diff --git a/.gitignore b/.gitignore index 217c2c0..bd6cfd5 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /.execute_query/ +/.spanner-docs/ diff --git a/CLAUDE.md b/CLAUDE.md index 76ba5d8..4e6182e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -61,6 +61,42 @@ Each `.metadata.json` sidecar (ours, not vendored) tracks: A `.test` file with no sidecar passes completely. +## Spanner Corpus + +`parser/testdata/spanner/` holds golden cases generated (not vendored) from the +GoogleSQL examples in Cloud Spanner's docs: the reference pages +(docs.cloud.google.com/spanner/docs/reference/standard-sql/) at the top level +and the guide pages under `guides/` (page list in `cmd/spanner-corpus/pages.go`). +The parser tests run them alongside the vendored files, as subtests named +`spanner/`, with the same `parse_todo` sidecars. + +- `.test` — examples the reference parser accepts (statement, script + or expression mode), with its output as the golden tree. +- `.spanner.sql` — Spanner-only syntax the reference parser rejects + (e.g. `GRANT ... TO ROLE`, change streams, `CREATE PROTO BUNDLE`, + `vector_length`). No golden output: this is the backlog of syntax that + needs a design decision before zetajones can parse it. +- `.rejected.sql` — rejected examples not yet triaged. Classify each by + id in `cmd/spanner-corpus/triage.txt` as `spanner`, `typo` (broken in the + docs) or `fragment`, then regenerate; this file should not exist after. + +Grammar templates, partial clauses, bare graph patterns and examples marked +`-- Error:` are dropped automatically (see `fragmentReason`; `-v` lists +them), as are PostgreSQL tabs and query-result comments. + +Regenerate (pages are cached in `.spanner-docs/`; `-refresh` re-downloads), +then re-record the todos, since regeneration renumbers cases and drops the +sidecars: + +```bash +go run ./cmd/spanner-corpus +go test ./parser -check-parse +``` + +`go run ./cmd/spanner-corpus -discover` lists guide pages with GoogleSQL +examples that `pages.go` does not include yet. Do not hand-edit generated +files; change `cmd/spanner-corpus` instead. + ## Alternation Groups Cases whose SQL or option lines contain `{{a|b|c}}` groups expand into the @@ -100,7 +136,7 @@ The grammar and AST are defined in - `googlesql/parser/*.tm` / grammar files — syntax reference For new test queries, golden output comes from the prebuilt reference binary -(pinned in `cmd/regenerate-parse/main.go`, must match the vendored testdata +(pinned in `internal/refparse/refparse.go`, must match the vendored testdata release): ```bash diff --git a/ast/ast.go b/ast/ast.go index ad567cd..f5e7bcb 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -2931,6 +2931,23 @@ func (n *DropRowAccessPolicyStatement) Children() []Node { return children(n.Name, n.Target) } +// DropIndexStatement is a "DROP {SEARCH|VECTOR} INDEX [IF EXISTS] name +// [ON table]" statement; see ASTDropSearchIndexStatement and +// ASTDropVectorIndexStatement in googlesql/parser/parse_tree.h. Vector selects +// the VECTOR form. +type DropIndexStatement struct { + Span + Vector bool `json:"vector,omitempty"` + IsIfExists bool `json:"is_if_exists,omitempty"` + Name *PathExpression `json:"name"` + Table *PathExpression `json:"table,omitempty"` +} + +func (n *DropIndexStatement) statementNode() {} +func (n *DropIndexStatement) Children() []Node { + return children(n.Name, n.Table) +} + // DropAllRowAccessPoliciesStatement is a // "DROP ALL ROW [ACCESS] POLICIES ON target" statement; see // ASTDropAllRowAccessPoliciesStatement in googlesql/parser/parse_tree.h. @@ -3941,6 +3958,15 @@ type NotNullColumnAttribute struct { func (n *NotNullColumnAttribute) Children() []Node { return nil } +// HiddenColumnAttribute is the "HIDDEN" column attribute; see +// ASTHiddenColumnAttribute in googlesql/parser/parse_tree.h. It has no +// children. +type HiddenColumnAttribute struct { + Span +} + +func (n *HiddenColumnAttribute) Children() []Node { return nil } + // PrimaryKeyColumnAttribute is the "PRIMARY KEY [ENFORCED|NOT ENFORCED]" column // attribute; see ASTPrimaryKeyColumnAttribute in // googlesql/parser/parse_tree.h. Enforced defaults to true. @@ -6002,3 +6028,200 @@ type GqlPageLimit struct { } func (n *GqlPageLimit) Children() []Node { return children(n.Value) } + +// CreatePropertyGraphStatement is a CREATE PROPERTY GRAPH statement; see +// ASTCreatePropertyGraphStatement in googlesql/parser/parse_tree.h. Scope is +// "", "TEMP", "PUBLIC", or "PRIVATE". EdgeTables and Options are optional. +type CreatePropertyGraphStatement struct { + Span + Scope string `json:"scope,omitempty"` + IsOrReplace bool `json:"is_or_replace,omitempty"` + IsIfNotExists bool `json:"is_if_not_exists,omitempty"` + Name *PathExpression `json:"name"` + NodeTables *GraphElementTableList `json:"node_tables"` + EdgeTables *GraphElementTableList `json:"edge_tables,omitempty"` + Options *OptionsList `json:"options,omitempty"` +} + +func (n *CreatePropertyGraphStatement) statementNode() {} +func (n *CreatePropertyGraphStatement) Children() []Node { + return children(n.Name, n.NodeTables, n.EdgeTables, n.Options) +} + +// GraphElementTableList is the parenthesized NODE TABLES or EDGE TABLES list +// of a property graph; see ASTGraphElementTableList. +type GraphElementTableList struct { + Span + Tables []*GraphElementTable `json:"tables"` +} + +func (n *GraphElementTableList) Children() []Node { + out := make([]Node, 0, len(n.Tables)) + for _, t := range n.Tables { + out = append(out, t) + } + return out +} + +// GraphElementTable is one node or edge table definition; see +// ASTGraphElementTable. Options holds the element-level default label +// OPTIONS list, which the debug string shows last. +type GraphElementTable struct { + Span + Name *PathExpression `json:"name"` + Alias *Alias `json:"alias,omitempty"` + Key *ColumnList `json:"key,omitempty"` + Source *GraphNodeTableReference `json:"source,omitempty"` + Destination *GraphNodeTableReference `json:"destination,omitempty"` + LabelProperties *GraphElementLabelAndPropertiesList `json:"label_properties"` + DynamicLabel *GraphDynamicLabel `json:"dynamic_label,omitempty"` + DynamicProperties *GraphDynamicProperties `json:"dynamic_properties,omitempty"` + Options *OptionsList `json:"options,omitempty"` +} + +func (n *GraphElementTable) Children() []Node { + return children(n.Name, n.Alias, n.Key, n.Source, n.Destination, n.LabelProperties, + n.DynamicLabel, n.DynamicProperties, n.Options) +} + +// GraphNodeTableReference is an edge table's "SOURCE KEY (...) REFERENCES +// node [(...)]" or "DESTINATION KEY ..." clause; see +// ASTGraphNodeTableReference. Type is "SOURCE" or "DESTINATION". +type GraphNodeTableReference struct { + Span + Type string `json:"type"` + NodeTable *Identifier `json:"node_table"` + EdgeColumns *ColumnList `json:"edge_columns"` + NodeColumns *ColumnList `json:"node_columns,omitempty"` +} + +func (n *GraphNodeTableReference) Children() []Node { + return children(n.NodeTable, n.EdgeColumns, n.NodeColumns) +} + +// GraphElementLabelAndPropertiesList is an element table's labels and their +// properties; see ASTGraphElementLabelAndPropertiesList. +type GraphElementLabelAndPropertiesList struct { + Span + Items []*GraphElementLabelAndProperties `json:"items"` +} + +func (n *GraphElementLabelAndPropertiesList) Children() []Node { + out := make([]Node, 0, len(n.Items)) + for _, it := range n.Items { + out = append(out, it) + } + return out +} + +// GraphElementLabelAndProperties is a "LABEL name [properties]" or "DEFAULT +// LABEL [OPTIONS(...)] [properties]" clause; see +// ASTGraphElementLabelAndProperties. Label is nil for the default label. +type GraphElementLabelAndProperties struct { + Span + Label *Identifier `json:"label,omitempty"` + Options *OptionsList `json:"options,omitempty"` + Properties *GraphProperties `json:"properties"` +} + +func (n *GraphElementLabelAndProperties) Children() []Node { + return children(n.Label, n.Options, n.Properties) +} + +// GraphProperties is a label's properties: NO PROPERTIES, PROPERTIES [ARE] +// ALL COLUMNS [EXCEPT (...)], or PROPERTIES (derived, ...); see +// ASTGraphProperties. An omitted clause means all columns. +type GraphProperties struct { + Span + NoProperties bool `json:"no_properties,omitempty"` + DerivedProperties *GraphDerivedPropertyList `json:"derived_properties,omitempty"` + AllExceptColumns *ColumnList `json:"all_except_columns,omitempty"` +} + +func (n *GraphProperties) Children() []Node { + return children(n.DerivedProperties, n.AllExceptColumns) +} + +// GraphDerivedPropertyList is the list inside PROPERTIES (...); see +// ASTGraphDerivedPropertyList. +type GraphDerivedPropertyList struct { + Span + Properties []*GraphDerivedProperty `json:"properties"` +} + +func (n *GraphDerivedPropertyList) Children() []Node { + out := make([]Node, 0, len(n.Properties)) + for _, d := range n.Properties { + out = append(out, d) + } + return out +} + +// GraphDerivedProperty is "expression [AS alias] [OPTIONS(...)]"; see +// ASTGraphDerivedProperty. +type GraphDerivedProperty struct { + Span + Expression Node `json:"expression"` + Alias *Alias `json:"alias,omitempty"` + Options *OptionsList `json:"options,omitempty"` +} + +func (n *GraphDerivedProperty) Children() []Node { + return children(n.Expression, n.Alias, n.Options) +} + +// GraphDynamicLabel is the expression of a DYNAMIC LABEL (...) clause; see +// ASTGraphDynamicLabel. +type GraphDynamicLabel struct { + Span + Expression Node `json:"expression"` +} + +func (n *GraphDynamicLabel) Children() []Node { return children(n.Expression) } + +// GraphDynamicProperties is the expression of a DYNAMIC PROPERTIES (...) +// clause; see ASTGraphDynamicProperties. +type GraphDynamicProperties struct { + Span + Expression Node `json:"expression"` +} + +func (n *GraphDynamicProperties) Children() []Node { return children(n.Expression) } + +// AlterIndexStatement is "ALTER {SEARCH|VECTOR} INDEX [IF EXISTS] name [ON +// table] actions"; see ASTAlterIndexStatement in +// googlesql/parser/parse_tree.h. Vector selects the VECTOR form (not shown in +// the debug string). +type AlterIndexStatement struct { + Span + Vector bool `json:"vector,omitempty"` + IsIfExists bool `json:"is_if_exists,omitempty"` + Name *PathExpression `json:"name"` + Table *PathExpression `json:"table,omitempty"` + Actions *AlterActionList `json:"actions"` +} + +func (n *AlterIndexStatement) statementNode() {} +func (n *AlterIndexStatement) Children() []Node { + return children(n.Name, n.Table, n.Actions) +} + +// AddColumnIdentifierAction is an ALTER INDEX "ADD COLUMN [IF NOT EXISTS] +// identifier [OPTIONS(...)]" action; see ASTAddColumnIdentifierAction. +type AddColumnIdentifierAction struct { + Span + IsIfNotExists bool `json:"is_if_not_exists,omitempty"` + Column *Identifier `json:"column"` + Options *OptionsList `json:"options,omitempty"` +} + +func (n *AddColumnIdentifierAction) Children() []Node { + return children(n.Column, n.Options) +} + +// RebuildAction is the ALTER INDEX "REBUILD" action; see ASTRebuildAction. +type RebuildAction struct { + Span +} + +func (n *RebuildAction) Children() []Node { return nil } diff --git a/cmd/next-test/main.go b/cmd/next-test/main.go index 2540905..0801b75 100644 --- a/cmd/next-test/main.go +++ b/cmd/next-test/main.go @@ -70,6 +70,12 @@ func resolve(cases []*testfile.Case, key string) (pendingItem, bool) { func main() { files, err := filepath.Glob(filepath.Join("parser", "testdata", "*.test")) + if err == nil { + // Generated Spanner corpus (see cmd/spanner-corpus). + var spanner []string + spanner, err = testfile.FindFiles(filepath.Join("parser", "testdata", "spanner")) + files = append(files, spanner...) + } if err != nil || len(files) == 0 { fmt.Fprintf(os.Stderr, "no .test files found (run from the repository root): %v\n", err) os.Exit(1) diff --git a/cmd/regenerate-parse/main.go b/cmd/regenerate-parse/main.go index 3b76a88..bf5081a 100644 --- a/cmd/regenerate-parse/main.go +++ b/cmd/regenerate-parse/main.go @@ -12,31 +12,22 @@ // // go run ./cmd/regenerate-parse -sql "select 1 + 2" // go run ./cmd/regenerate-parse -file query.sql +// go run ./cmd/regenerate-parse -features MAXIMUM,+SPANNER_LEGACY_DDL -sql "..." package main import ( - "bytes" "flag" "fmt" - "io" - "net/http" "os" - "os/exec" - "path/filepath" - "runtime" - "strings" -) - -// Required googlesql release for generating golden files. This must match the -// release the vendored parser/testdata files were taken from; update both -// together. -const requiredRelease = "2026.01.1" -const binDir = ".execute_query" + "github.com/sqlc-dev/zetajones/internal/refparse" +) func main() { sql := flag.String("sql", "", "SQL statement to parse") file := flag.String("file", "", "File containing the SQL statement to parse") + features := flag.String("features", "", "--enabled_language_features spec (e.g. MAXIMUM,+SPANNER_LEGACY_DDL)") + mode := flag.String("mode", "", "--sql_mode: query (default), script, or expression") flag.Parse() if (*sql == "") == (*file == "") { @@ -52,85 +43,16 @@ func main() { *sql = string(data) } - bin, err := ensureBinary() + bin, err := refparse.EnsureBinary() if err != nil { fmt.Fprintf(os.Stderr, "setting up execute_query: %v\n", err) os.Exit(1) } - out, err := runParse(bin, *sql) + out, err := refparse.Parse(bin, *sql, refparse.Options{LanguageFeatures: *features, Mode: *mode}) if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) os.Exit(1) } fmt.Println(out) } - -// runParse runs execute_query in parse mode and returns the parse tree debug -// string. -func runParse(bin, sql string) (string, error) { - cmd := exec.Command(bin, "--mode=parse", sql) - var stdout, stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr - if err := cmd.Run(); err != nil { - msg := strings.TrimSpace(stderr.String()) - if msg == "" { - msg = err.Error() - } - return "", fmt.Errorf("execute_query: %s", msg) - } - return strings.TrimSpace(stdout.String()), nil -} - -// ensureBinary downloads the pinned execute_query release binary if needed -// and returns its path. -func ensureBinary() (string, error) { - var asset string - switch runtime.GOOS { - case "linux": - asset = "execute_query_linux" - case "darwin": - asset = "execute_query_macos" - default: - return "", fmt.Errorf("no prebuilt execute_query binary for %s", runtime.GOOS) - } - - bin := filepath.Join(binDir, "execute_query-"+requiredRelease) - if _, err := os.Stat(bin); err == nil { - return bin, nil - } - - url := fmt.Sprintf("https://github.com/google/googlesql/releases/download/%s/%s", requiredRelease, asset) - fmt.Fprintf(os.Stderr, "Downloading %s...\n", url) - - if err := os.MkdirAll(binDir, 0o755); err != nil { - return "", err - } - resp, err := http.Get(url) - if err != nil { - return "", fmt.Errorf("downloading: %w", err) - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return "", fmt.Errorf("download failed with status %d", resp.StatusCode) - } - - tmp := bin + ".tmp" - out, err := os.OpenFile(tmp, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o755) - if err != nil { - return "", err - } - if _, err := io.Copy(out, resp.Body); err != nil { - out.Close() - os.Remove(tmp) - return "", fmt.Errorf("writing binary: %w", err) - } - out.Close() - if err := os.Rename(tmp, bin); err != nil { - os.Remove(tmp) - return "", err - } - fmt.Fprintf(os.Stderr, "Installed %s\n", bin) - return bin, nil -} diff --git a/cmd/spanner-corpus/extract.go b/cmd/spanner-corpus/extract.go new file mode 100644 index 0000000..e1cefb5 --- /dev/null +++ b/cmd/spanner-corpus/extract.go @@ -0,0 +1,277 @@ +package main + +import ( + "bytes" + "compress/gzip" + "fmt" + "html" + "io" + "net/http" + "os" + "path/filepath" + "regexp" + "strings" +) + +// fetch returns the page's HTML, downloading it into the gzipped cache if +// needed. +func fetch(p page, cacheDir string, refresh bool) (string, error) { + path := filepath.Join(cacheDir, strings.ReplaceAll(p.path, "/", ".")+".html.gz") + if !refresh { + if data, err := os.ReadFile(path); err == nil { + zr, err := gzip.NewReader(bytes.NewReader(data)) + if err == nil { + if doc, err := io.ReadAll(zr); err == nil { + return string(doc), nil + } + } + } + } + fmt.Fprintf(os.Stderr, "Downloading %s...\n", p.url()) + resp, err := http.Get(p.url()) + if err != nil { + return "", err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("GET %s: status %d", p.url(), resp.StatusCode) + } + doc, err := io.ReadAll(resp.Body) + if err != nil { + return "", err + } + var buf bytes.Buffer + zw := gzip.NewWriter(&buf) + zw.Write(doc) + zw.Close() + if err := os.MkdirAll(cacheDir, 0o755); err != nil { + return "", err + } + if err := os.WriteFile(path, buf.Bytes(), 0o644); err != nil { + return "", err + } + return string(doc), nil +} + +// navRE matches links to other Spanner docs pages. +var navRE = regexp.MustCompile(`href="/spanner/docs/([^"#?]+)"`) + +// navLinks returns the Spanner docs page paths linked from doc. +func navLinks(doc string) []string { + var out []string + seen := map[string]bool{} + for _, m := range navRE.FindAllStringSubmatch(doc, -1) { + if !seen[m[1]] { + seen[m[1]] = true + out = append(out, m[1]) + } + } + return out +} + +// block is one SQL example block on a page. +type block struct { + section string // id of the nearest preceding heading + sql string +} + +var ( + // tokenRE matches, in document order, the start and end of a tab set + // (devsite-selector), a heading, or a
 block.
+	tokenRE = regexp.MustCompile(`(?s)()|]*)>(.*?)|]*)>(.*?)
`) + idAttr = regexp.MustCompile(`\bid="([^"]*)"`) + syntax = regexp.MustCompile(`\bsyntax="([^"]*)"`) + tagRE = regexp.MustCompile(`<[^>]*>`) + + // resultTableRE matches the query-result tables the docs print as block + // comments after examples ("/*---+---+ ... +---*/"). Some are closed with + // a typo'd "/*" instead of "*/", which would swallow the rest of the + // block, so both endings are accepted. + resultTableRE = regexp.MustCompile(`(?m)^[ \t]*/\*[-+]+[^\n]*\n(?:[^\n]*\n)*?[^\n]*?(?:\*/|[-+]/\*)[ \t]*$`) +) + +// extractBlocks returns the page's GoogleSQL example blocks. Copyable blocks +// (class devsite-click-to-copy) marked as SQL are examples; the reference's +// grammar blocks are plain
s, and blocks with  placeholders are
+// templates, so both are skipped. Guide pages show each example in GoogleSQL
+// and PostgreSQL tabs; PostgreSQL ones are skipped.
+func extractBlocks(doc string) []block {
+	var blocks []block
+	section, sectionTitle := "", ""
+	var tabs []string // current tab title per open tab set
+	for _, m := range tokenRE.FindAllStringSubmatch(doc, -1) {
+		switch {
+		case m[1] != "":
+			tabs = append(tabs, "")
+		case m[2] != "":
+			if len(tabs) > 0 {
+				tabs = tabs[:len(tabs)-1]
+			}
+		case m[6] != "" || m[5] != "":
+			attrs, body := m[5], m[6]
+			if !strings.Contains(attrs, "devsite-click-to-copy") || strings.Contains(attrs, "devsite-disable-click-to-copy") {
+				continue
+			}
+			if strings.Contains(body, " 0 {
+				tab = tabs[len(tabs)-1]
+			}
+			if postgres(lang) || postgres(tab) || (tab == "" && postgres(sectionTitle)) {
+				continue
+			}
+			if lang != "SQL" && lang != "GoogleSQL" && !(lang == "" && strings.Contains(tab, "GoogleSQL")) {
+				continue
+			}
+			text := html.UnescapeString(tagRE.ReplaceAllString(body, ""))
+			text = strings.ReplaceAll(text, "\r\n", "\n")
+			text = strings.ReplaceAll(text, " ", " ")
+			text = resultTableRE.ReplaceAllString(text, "")
+			blocks = append(blocks, block{section: section, sql: text})
+		default: // heading
+			title := strings.TrimSpace(html.UnescapeString(tagRE.ReplaceAllString(m[4], "")))
+			if len(tabs) > 0 {
+				tabs[len(tabs)-1] = title
+				continue
+			}
+			if id := idAttr.FindStringSubmatch(m[3]); id != nil {
+				section, sectionTitle = id[1], title
+			}
+		}
+	}
+	return blocks
+}
+
+func postgres(s string) bool {
+	return strings.Contains(strings.ToLower(s), "postgres") && !strings.Contains(s, "GoogleSQL")
+}
+
+// scan walks sql, calling code for each byte outside comments and literals
+// and skip for each comment or quoted literal span [i, j). It returns nothing;
+// callers build what they need from the callbacks.
+func scan(sql string, code func(i int), literal func(i, j int), comment func(i, j int)) {
+	for i := 0; i < len(sql); {
+		c := sql[i]
+		switch {
+		case c == '-' && strings.HasPrefix(sql[i:], "--"), c == '#':
+			j := skipLine(sql, i)
+			comment(i, j)
+			i = j
+		case c == '/' && strings.HasPrefix(sql[i:], "/*"):
+			j := len(sql)
+			if end := strings.Index(sql[i+2:], "*/"); end >= 0 {
+				j = i + 2 + end + 2
+			}
+			comment(i, j)
+			i = j
+		case c == '\'' || c == '"' || c == '`':
+			j := skipQuoted(sql, i)
+			literal(i, j)
+			i = j
+		default:
+			code(i)
+			i++
+		}
+	}
+}
+
+func isSpace(c byte) bool { return c == ' ' || c == '\t' || c == '\n' || c == '\r' }
+
+// splitStatements splits sql on top-level ";" (outside string literals,
+// quoted identifiers and comments) and returns the trimmed statements that
+// contain more than comments.
+func splitStatements(sql string) []string {
+	var out []string
+	start, hasCode := 0, false
+	flush := func(end int) {
+		if s := strings.TrimSpace(sql[start:end]); hasCode && s != "" {
+			out = append(out, s)
+		}
+		hasCode = false
+	}
+	scan(sql,
+		func(i int) {
+			switch {
+			case sql[i] == ';':
+				flush(i)
+				start = i + 1
+			case !isSpace(sql[i]):
+				hasCode = true
+			}
+		},
+		func(i, j int) { hasCode = true },
+		func(i, j int) {})
+	flush(len(sql))
+	return out
+}
+
+// codeOnly returns sql with comments removed and literals replaced by a
+// placeholder, for pattern checks that must not look inside them.
+func codeOnly(sql string) string {
+	var b strings.Builder
+	scan(sql,
+		func(i int) { b.WriteByte(sql[i]) },
+		func(i, j int) { b.WriteString("''") },
+		func(i, j int) { b.WriteByte(' ') })
+	return b.String()
+}
+
+func skipLine(s string, i int) int {
+	if end := strings.IndexByte(s[i:], '\n'); end >= 0 {
+		return i + end + 1
+	}
+	return len(s)
+}
+
+// skipQuoted returns the index just past the quoted token starting at i,
+// handling triple-quoted strings and backslash escapes.
+func skipQuoted(s string, i int) int {
+	q := s[i : i+1]
+	if q != "`" && strings.HasPrefix(s[i:], q+q+q) {
+		q = q + q + q
+	}
+	for j := i + len(q); j < len(s); j++ {
+		if s[j] == '\\' {
+			j++
+			continue
+		}
+		if strings.HasPrefix(s[j:], q) {
+			return j + len(q)
+		}
+	}
+	return len(s)
+}
+
+// splitUnterminated splits a statement at each line that starts, in column
+// 0, with the same keyword as its first line (CREATE ... CREATE ..., GRAPH
+// ... GRAPH ...), for doc examples that list several statements without ";"
+// between them. It returns nil if there is no such line.
+func splitUnterminated(sql string) []string {
+	lines := strings.Split(sql, "\n")
+	first := firstWordRE.FindStringSubmatch(codeOnly(sql))
+	if first == nil || !statementStarts[strings.ToUpper(first[1])] {
+		return nil
+	}
+	var parts []string
+	start := 0
+	for i := 1; i < len(lines); i++ {
+		m := firstWordRE.FindStringSubmatch(lines[i])
+		if m == nil || isSpace(lines[i][0]) || !strings.EqualFold(m[1], first[1]) {
+			continue
+		}
+		if part := strings.TrimSpace(strings.Join(lines[start:i], "\n")); part != "" {
+			parts = append(parts, part)
+		}
+		start = i
+	}
+	if start == 0 {
+		return nil
+	}
+	return append(parts, strings.TrimSpace(strings.Join(lines[start:], "\n")))
+}
diff --git a/cmd/spanner-corpus/main.go b/cmd/spanner-corpus/main.go
new file mode 100644
index 0000000..4d879fc
--- /dev/null
+++ b/cmd/spanner-corpus/main.go
@@ -0,0 +1,494 @@
+// Command spanner-corpus builds golden parser tests from the SQL examples in
+// Cloud Spanner's documentation: the GoogleSQL reference
+// (https://docs.cloud.google.com/spanner/docs/reference/standard-sql/) and
+// the guide pages with GoogleSQL examples (see pages.go).
+//
+// For each page it downloads the HTML (cached under .spanner-docs/), extracts
+// the copyable GoogleSQL example blocks (grammar blocks, templates and
+// PostgreSQL tabs are skipped, as are query-result comments), splits them
+// into statements, and runs each through the reference parser (see
+// internal/refparse) with Spanner's language features enabled.
+//
+//   - Statements the reference accepts become cases in
+//     parser/testdata/spanner/.test, in the same format as the vendored
+//     googlesql testdata, so the parser tests run them (with parse_todo
+//     tracking in a .metadata.json sidecar like any other file).
+//   - Statements the reference rejects are triaged. Grammar fragments are
+//     recognized by heuristics (see fragmentReason) and dropped; the rest are
+//     looked up by id in triage.txt:
+//     "spanner" (valid Spanner syntax GoogleSQL's parser lacks) goes to
+//     .spanner.sql, the backlog of Spanner-only syntax; "typo" and
+//     "fragment" are dropped. Untriaged rejects are written to
+//     .rejected.sql, each with its id, for someone to classify in
+//     triage.txt.
+//
+// The generated files are regenerated wholesale; do not edit them by hand.
+// After regenerating, run
+//
+//	go test ./parser -check-parse
+//
+// to record cases the parser does not handle yet in the sidecars.
+//
+// The examples are taken from Google Cloud documentation; code samples there
+// are licensed under the Apache 2.0 License.
+//
+// Usage:
+//
+//	go run ./cmd/spanner-corpus                  # all pages
+//	go run ./cmd/spanner-corpus -pages reference/standard-sql/dml-syntax
+//	go run ./cmd/spanner-corpus -refresh         # re-download pages
+//	go run ./cmd/spanner-corpus -discover        # list guide pages not in pages.go
+package main
+
+import (
+	"flag"
+	"fmt"
+	"os"
+	"path/filepath"
+	"runtime"
+	"slices"
+	"sort"
+	"strings"
+	"sync"
+
+	"github.com/sqlc-dev/zetajones/internal/refparse"
+	"github.com/sqlc-dev/zetajones/internal/testfile"
+)
+
+const docsURL = "https://docs.cloud.google.com/spanner/docs/"
+
+// features are the language features Spanner's dialect needs from the
+// reference parser, including the SQL graph features.
+const features = "MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE"
+
+// page is a Spanner docs page, by its path under docsURL.
+type page struct {
+	path string
+}
+
+func (p page) url() string { return docsURL + p.path }
+
+// out is the page's output path (without extension) relative to the output
+// directory: reference pages by name, guides under guides/.
+func (p page) out() string {
+	if name, ok := strings.CutPrefix(p.path, "reference/standard-sql/"); ok {
+		return name
+	}
+	return filepath.Join("guides", strings.ReplaceAll(p.path, "/", "."))
+}
+
+func allPages() []page { return append(slices.Clone(referencePages), guidePages...) }
+
+func main() {
+	only := flag.String("pages", "", "comma-separated page paths to regenerate (default: all)")
+	refresh := flag.Bool("refresh", false, "re-download cached pages")
+	discover := flag.Bool("discover", false, "list guide pages with GoogleSQL examples that pages.go lacks")
+	cacheDir := flag.String("cache", ".spanner-docs", "directory for downloaded HTML")
+	outDir := flag.String("out", filepath.Join("parser", "testdata", "spanner"), "output directory")
+	verbose := flag.Bool("v", false, "print the rejected statements dropped as fragments")
+	triagePath := flag.String("triage", filepath.Join("cmd", "spanner-corpus", "triage.txt"), "triage file")
+	flag.Parse()
+
+	if *discover {
+		runDiscover(*cacheDir, *refresh)
+		return
+	}
+
+	pages := allPages()
+	selected := pages
+	if *only != "" {
+		names := strings.Split(*only, ",")
+		selected = nil
+		for _, p := range pages {
+			if slices.Contains(names, p.path) {
+				selected = append(selected, p)
+			}
+		}
+		if len(selected) != len(names) {
+			fatalf("unknown page in -pages %q", *only)
+		}
+	}
+
+	tri, err := loadTriage(*triagePath)
+	if err != nil {
+		fatalf("%v", err)
+	}
+	bin, err := refparse.EnsureBinary()
+	if err != nil {
+		fatalf("setting up execute_query: %v", err)
+	}
+
+	// A full run regenerates everything, so files for pages no longer listed
+	// do not linger.
+	if *only == "" {
+		if err := os.RemoveAll(*outDir); err != nil {
+			fatalf("%v", err)
+		}
+	}
+
+	// Statements are deduplicated across pages in page order, so a selected
+	// subset must still see the earlier pages' statements.
+	seen := map[string]bool{}
+	used := map[string]bool{}
+	var total counts
+	for _, p := range pages {
+		doc, err := fetch(p, *cacheDir, *refresh && slices.Contains(selected, p))
+		if err != nil {
+			fatalf("%s: %v", p.path, err)
+		}
+		blocks := extractBlocks(doc)
+		if !slices.Contains(selected, p) {
+			for _, b := range blocks {
+				seen[strings.TrimSpace(b.sql)] = true
+				for _, s := range splitStatements(b.sql) {
+					seen[s] = true
+				}
+			}
+			continue
+		}
+		accepted, rejected := runReference(bin, blocks, seen)
+
+		var c counts
+		c.accepted = len(accepted)
+		var spanner, untriaged []rejection
+		for _, r := range rejected {
+			r.id = stmtID(r.sql)
+			if v, ok := tri[r.id]; ok {
+				used[r.id] = true
+				switch v.kind {
+				case verdictSpanner:
+					r.note = v.note
+					spanner = append(spanner, r)
+				case verdictTypo:
+					c.typos++
+				default:
+					c.fragments++
+				}
+				continue
+			}
+			if reason := fragmentReason(r.sql); reason != "" {
+				if *verbose {
+					fmt.Printf("fragment (%s) %s#%s\n%s\n\n", reason, p.url(), r.section, r.sql)
+				}
+				c.fragments++
+				continue
+			}
+			untriaged = append(untriaged, r)
+		}
+		c.spanner, c.untriaged = len(spanner), len(untriaged)
+		total.add(c)
+
+		base := filepath.Join(*outDir, p.out())
+		if err := os.MkdirAll(filepath.Dir(base), 0o755); err != nil {
+			fatalf("%v", err)
+		}
+		if err := writeTests(base+".test", p, accepted); err != nil {
+			fatalf("%v", err)
+		}
+		if err := writeRejects(base+".spanner.sql", p, spanner, spannerHeader); err != nil {
+			fatalf("%v", err)
+		}
+		if err := writeRejects(base+".rejected.sql", p, untriaged, rejectedHeader); err != nil {
+			fatalf("%v", err)
+		}
+		fmt.Printf("%-50s %s\n", p.path, c)
+	}
+	fmt.Printf("%-50s %s\n", "TOTAL", total)
+
+	if *only == "" {
+		var stale []string
+		for id := range tri {
+			if !used[id] {
+				stale = append(stale, id)
+			}
+		}
+		sort.Strings(stale)
+		for _, id := range stale {
+			fmt.Fprintf(os.Stderr, "warning: triage.txt entry %s matches no rejected statement\n", id)
+		}
+	}
+	if total.untriaged > 0 {
+		fmt.Fprintf(os.Stderr, "%d rejected statements need triage; see %s/**/*.rejected.sql\n", total.untriaged, *outDir)
+	}
+}
+
+type counts struct {
+	accepted, spanner, typos, fragments, untriaged int
+}
+
+func (c *counts) add(o counts) {
+	c.accepted += o.accepted
+	c.spanner += o.spanner
+	c.typos += o.typos
+	c.fragments += o.fragments
+	c.untriaged += o.untriaged
+}
+
+func (c counts) String() string {
+	return fmt.Sprintf("%4d accepted %3d spanner-only %3d typos %3d fragments %3d untriaged",
+		c.accepted, c.spanner, c.typos, c.fragments, c.untriaged)
+}
+
+func fatalf(format string, args ...any) {
+	fmt.Fprintf(os.Stderr, "spanner-corpus: "+format+"\n", args...)
+	os.Exit(1)
+}
+
+// runDiscover crawls the Spanner docs navigation for guide pages not in
+// pages.go and prints those with GoogleSQL example statements.
+func runDiscover(cacheDir string, refresh bool) {
+	nav, err := fetch(page{"secondary-indexes"}, cacheDir, refresh)
+	if err != nil {
+		fatalf("%v", err)
+	}
+	known := map[string]bool{}
+	for _, p := range allPages() {
+		known[p.path] = true
+	}
+	var candidates []page
+	for _, path := range navLinks(nav) {
+		if known[path] || strings.HasPrefix(path, "reference/") || strings.Contains(path, "postgres") {
+			continue
+		}
+		candidates = append(candidates, page{path})
+	}
+	found := make([]int, len(candidates))
+	parallel(len(candidates), func(i int) {
+		doc, err := fetch(candidates[i], cacheDir, refresh)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "%s: %v\n", candidates[i].path, err)
+			return
+		}
+		for _, b := range extractBlocks(doc) {
+			found[i] += len(splitStatements(b.sql))
+		}
+	})
+	for i, p := range candidates {
+		if found[i] > 0 {
+			fmt.Printf("%s\t%d\n", p.path, found[i])
+		}
+	}
+}
+
+// testCase is an example the reference accepted.
+type testCase struct {
+	section string
+	sql     string
+	mode    string // "" (statement), "script" or "expression"
+	tree    string
+}
+
+// rejection is a statement the reference rejected.
+type rejection struct {
+	section string
+	sql     string
+	err     string
+	id      string
+	note    string
+}
+
+// result is one reference parse.
+type result struct {
+	tree string
+	err  error
+}
+
+type unit struct {
+	block   int
+	section string
+	sql     string
+}
+
+// runReference runs every new statement through the reference parser. A
+// block whose statements do not all parse individually is retried as one
+// script, which covers procedural blocks (BEGIN ... END, procedure bodies)
+// whose inner ";"s the naive splitter cuts. A statement that still fails is
+// retried as an expression, which covers the function pages' bare call and
+// literal examples.
+func runReference(bin string, blocks []block, seen map[string]bool) ([]testCase, []rejection) {
+	parse := func(sql, mode string) result {
+		tree, err := refparse.Parse(bin, sql+"\n", refparse.Options{LanguageFeatures: features, Mode: mode})
+		return result{tree, err}
+	}
+
+	var units []unit
+	for i, b := range blocks {
+		for _, s := range splitStatements(b.sql) {
+			units = append(units, unit{block: i, section: b.section, sql: s})
+		}
+	}
+	stmt := make([]result, len(units))
+	parallel(len(units), func(i int) { stmt[i] = parse(units[i].sql, "") })
+
+	var accepted []testCase
+	var rejected []rejection
+	for i := 0; i < len(units); {
+		j := i
+		ok := true
+		for j < len(units) && units[j].block == units[i].block {
+			ok = ok && stmt[j].err == nil
+			j++
+		}
+		if !ok && j-i > 1 {
+			script := strings.TrimSpace(blocks[units[i].block].sql)
+			if r := parse(script, "script"); r.err == nil {
+				if !seen[script] {
+					seen[script] = true
+					accepted = append(accepted, testCase{section: units[i].section, sql: script, mode: "script", tree: r.tree})
+				}
+				i = j
+				continue
+			}
+		}
+		for k := i; k < j; k++ {
+			u := units[k]
+			if seen[u.sql] {
+				continue
+			}
+			seen[u.sql] = true
+			if stmt[k].err == nil {
+				accepted = append(accepted, testCase{section: u.section, sql: u.sql, tree: stmt[k].tree})
+			} else if r := parse(u.sql, "expression"); r.err == nil {
+				accepted = append(accepted, testCase{section: u.section, sql: u.sql, mode: "expression", tree: r.tree})
+			} else if parts := splitUnterminated(u.sql); len(parts) > 1 && allParse(parts, parse) {
+				// The docs left out the ";"s between statements.
+				for _, part := range parts {
+					if !seen[part] {
+						seen[part] = true
+						accepted = append(accepted, testCase{section: u.section, sql: part, tree: parse(part, "").tree})
+					}
+				}
+			} else {
+				rejected = append(rejected, rejection{section: u.section, sql: u.sql, err: firstLine(stmt[k].err.Error())})
+			}
+		}
+		i = j
+	}
+	return accepted, rejected
+}
+
+// allParse reports whether every part parses as a statement.
+func allParse(parts []string, parse func(sql, mode string) result) bool {
+	for _, part := range parts {
+		if parse(part, "").err != nil {
+			return false
+		}
+	}
+	return true
+}
+
+func parallel(n int, f func(int)) {
+	var wg sync.WaitGroup
+	sem := make(chan struct{}, runtime.NumCPU())
+	for i := range n {
+		wg.Add(1)
+		sem <- struct{}{}
+		go func() {
+			defer wg.Done()
+			defer func() { <-sem }()
+			f(i)
+		}()
+	}
+	wg.Wait()
+}
+
+func firstLine(s string) string {
+	s, _, _ = strings.Cut(s, "\n")
+	return s
+}
+
+func header(p page) string {
+	return "# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.\n" +
+		"# Source: " + p.url() + "\n" +
+		"# Examples from Google Cloud documentation, licensed under Apache 2.0.\n"
+}
+
+// writeTests writes accepted cases in the vendored testdata format. Source
+// comments precede the option lines: comments after them would be part of
+// the input.
+//
+// Case numbering changes on regeneration, so the metadata sidecar is removed;
+// `go test ./parser -check-parse` re-creates it.
+func writeTests(path string, p page, cases []testCase) error {
+	for _, f := range []string{path, testfile.MetadataPath(path)} {
+		if err := os.Remove(f); err != nil && !os.IsNotExist(err) {
+			return err
+		}
+	}
+	if len(cases) == 0 {
+		return nil
+	}
+	var b strings.Builder
+	for i, c := range cases {
+		if i == 0 {
+			b.WriteString(header(p))
+		} else {
+			b.WriteString("==\n")
+		}
+		fmt.Fprintf(&b, "# %s#%s\n", p.url(), c.section)
+		if i == 0 {
+			fmt.Fprintf(&b, "[default language_features=%s]\n", features)
+			b.WriteString("[default no_show_parse_location_text]\n")
+			// execute_query always reserves the GRAPH_TABLE keyword.
+			b.WriteString("[default reserve_graph_table]\n")
+		}
+		if c.mode != "" {
+			fmt.Fprintf(&b, "[mode=%s]\n", c.mode)
+		}
+		writePayload(&b, c.sql)
+		b.WriteString("--\n")
+		writePayload(&b, c.tree)
+	}
+	return os.WriteFile(path, []byte(b.String()), 0o644)
+}
+
+// writePayload writes text, escaping lines the test format would otherwise
+// treat as comments, options or separators.
+func writePayload(b *strings.Builder, text string) {
+	for line := range strings.SplitSeq(text, "\n") {
+		t := strings.TrimSpace(line)
+		if line == "==" || line == "--" || strings.HasPrefix(t, "#") || strings.HasPrefix(t, "[") || strings.HasPrefix(line, `\`) {
+			b.WriteByte('\\')
+		}
+		b.WriteString(line)
+		b.WriteByte('\n')
+	}
+}
+
+const spannerHeader = `#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=` + features + `
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+`
+
+const rejectedHeader = `#
+# Example statements the reference parser rejects that are not yet triaged.
+# Classify each by id in cmd/spanner-corpus/triage.txt as "spanner"
+# (Spanner-only syntax), "typo" (broken in the docs) or "fragment" (not a
+# complete statement), then regenerate.
+`
+
+func writeRejects(path string, p page, rejects []rejection, preamble string) error {
+	if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
+		return err
+	}
+	if len(rejects) == 0 {
+		return nil
+	}
+	var b strings.Builder
+	b.WriteString(header(p))
+	b.WriteString(preamble)
+	for _, r := range rejects {
+		fmt.Fprintf(&b, "\n# %s#%s\n", p.url(), r.section)
+		if r.note != "" {
+			fmt.Fprintf(&b, "# id %s: %s\n", r.id, r.note)
+		} else {
+			fmt.Fprintf(&b, "# id %s\n", r.id)
+		}
+		fmt.Fprintf(&b, "# %s\n", r.err)
+		b.WriteString(r.sql)
+		b.WriteString(";\n")
+	}
+	return os.WriteFile(path, []byte(b.String()), 0o644)
+}
diff --git a/cmd/spanner-corpus/pages.go b/cmd/spanner-corpus/pages.go
new file mode 100644
index 0000000..b0836f0
--- /dev/null
+++ b/cmd/spanner-corpus/pages.go
@@ -0,0 +1,191 @@
+package main
+
+// referencePages are the GoogleSQL reference pages, in output order.
+// Statements that appear on more than one page are kept only on the first.
+var referencePages = []page{
+	{"reference/standard-sql/data-definition-language"},
+	{"reference/standard-sql/dml-syntax"},
+	{"reference/standard-sql/query-syntax"},
+	{"reference/standard-sql/pipe-syntax"},
+	{"reference/standard-sql/subqueries"},
+	{"reference/standard-sql/procedural-language"},
+	{"reference/standard-sql/stored-procedures"},
+	{"reference/standard-sql/graph-schema-statements"},
+	{"reference/standard-sql/graph-query-statements"},
+	{"reference/standard-sql/graph-sql-queries"},
+	{"reference/standard-sql/graph-patterns"},
+	{"reference/standard-sql/graph-subqueries"},
+	{"reference/standard-sql/aggregate_functions"},
+	{"reference/standard-sql/array_functions"},
+	{"reference/standard-sql/bit_functions"},
+	{"reference/standard-sql/compression-functions"},
+	{"reference/standard-sql/conditional_expressions"},
+	{"reference/standard-sql/conversion_functions"},
+	{"reference/standard-sql/date_functions"},
+	{"reference/standard-sql/debugging_functions"},
+	{"reference/standard-sql/graph-conditional-expressions"},
+	{"reference/standard-sql/graph-gql-functions"},
+	{"reference/standard-sql/graph-operators"},
+	{"reference/standard-sql/graph-sql-functions"},
+	{"reference/standard-sql/hash_functions"},
+	{"reference/standard-sql/interval_functions"},
+	{"reference/standard-sql/json_functions"},
+	{"reference/standard-sql/mathematical_functions"},
+	{"reference/standard-sql/ml-functions"},
+	{"reference/standard-sql/net_functions"},
+	{"reference/standard-sql/operators"},
+	{"reference/standard-sql/protocol_buffer_functions"},
+	{"reference/standard-sql/search_functions"},
+	{"reference/standard-sql/security_functions"},
+	{"reference/standard-sql/sequence_functions"},
+	{"reference/standard-sql/statistical_aggregate_functions"},
+	{"reference/standard-sql/string_functions"},
+	{"reference/standard-sql/timestamp_functions"},
+	{"reference/standard-sql/utility-functions"},
+	{"reference/standard-sql/aggregate-function-calls"},
+	{"reference/standard-sql/data-types"},
+	{"reference/standard-sql/lexical"},
+	{"reference/standard-sql/protocol-buffers"},
+	{"reference/standard-sql/collation-concepts"},
+	{"reference/standard-sql/format-elements"},
+	{"reference/standard-sql/conversion_rules"},
+}
+
+// guidePages are the Spanner guide pages with GoogleSQL examples, found with
+// -discover, minus pages whose examples are not GoogleSQL (PGAdapter,
+// openCypher, Cassandra CQL, PostgreSQL and Oracle migration, JSONB). They
+// follow the reference pages in output order.
+var guidePages = []page{
+	{"backfill-embeddings"},
+	{"best-practices-gaming-database"},
+	{"bulk-loading"},
+	{"change-streams/details"},
+	{"change-streams/manage"},
+	{"change-streams/use-dataflow"},
+	{"check-constraint/how-to"},
+	{"commit-timestamp"},
+	{"configure-columnar-engine"},
+	{"configure-fgac"},
+	{"create-manage-data-placements"},
+	{"create-manage-databases"},
+	{"create-manage-locality-groups"},
+	{"create-manage-split-points"},
+	{"create-manage-views"},
+	{"create-query-database-console"},
+	{"databoost/databoost-run-queries"},
+	{"dml-best-practices"},
+	{"dml-tasks"},
+	{"fgac-about"},
+	{"fgac-privileges"},
+	{"fgac-queues"},
+	{"fgac-system-roles"},
+	{"find-approximate-nearest-neighbors"},
+	{"find-k-nearest-neighbors"},
+	{"foreign-keys/how-to"},
+	{"foreign-keys/overview"},
+	{"full-text-search/combine-tokenlists"},
+	{"full-text-search/facets"},
+	{"full-text-search/fuzzy-search"},
+	{"full-text-search/mix-full-text-and-non-text-queries"},
+	{"full-text-search/numeric-indexes"},
+	{"full-text-search/paginate-search-results"},
+	{"full-text-search/partition-search-index"},
+	{"full-text-search/pattern-matching-function-acceleration"},
+	{"full-text-search/query-overview"},
+	{"full-text-search/ranked-search"},
+	{"full-text-search/search-indexes"},
+	{"full-text-search/search-multiple-columns"},
+	{"full-text-search/search-query-enhancement"},
+	{"full-text-search/substring-search"},
+	{"full-text-search/tokenization"},
+	{"full-text-search/top-k-pattern-matching"},
+	{"generated-column/how-to"},
+	{"getting-started/ado_net"},
+	{"getting-started/cpp"},
+	{"getting-started/csharp"},
+	{"getting-started/database_sql"},
+	{"getting-started/go"},
+	{"getting-started/java"},
+	{"getting-started/jdbc"},
+	{"getting-started/nodejs"},
+	{"getting-started/php"},
+	{"getting-started/python"},
+	{"getting-started/ruby"},
+	{"graph/algorithm-best-practices"},
+	{"graph/algorithm-schema-requirements-and-feature-compatibility"},
+	{"graph/algorithms"},
+	{"graph/best-practices-designing-schema"},
+	{"graph/best-practices-tuning-queries"},
+	{"graph/create-update-drop-schema"},
+	{"graph/full-text-search-and-graph"},
+	{"graph/graph-algorithms-overview"},
+	{"graph/graph-with-views-how-to"},
+	{"graph/graph-with-views-overview"},
+	{"graph/insert-update-delete-data"},
+	{"graph/manage-schemaless-data"},
+	{"graph/perform-vector-similarity-search"},
+	{"graph/queries-overview"},
+	{"graph/reverse-etl"},
+	{"graph/run-algorithms"},
+	{"graph/schema-overview"},
+	{"graph/set-up"},
+	{"graph/troubleshoot"},
+	{"graph/work-with-paths"},
+	{"graph/work-with-visualizations"},
+	{"hybrid-full-text-vector-search"},
+	{"implement-multi-tenancy"},
+	{"index-geo-partitioning"},
+	{"introspection/active-partitioned-dmls"},
+	{"introspection/column-operations-statistics"},
+	{"introspection/hot-split-statistics"},
+	{"introspection/investigate-cpu-utilization"},
+	{"introspection/lock-statistics"},
+	{"introspection/oldest-active-queries"},
+	{"introspection/query-statistics"},
+	{"introspection/read-statistics"},
+	{"introspection/table-operations-statistics"},
+	{"introspection/transaction-statistics"},
+	{"introspection/troubleshooting-with-tags"},
+	{"introspection/vector-index-statistics"},
+	{"isolation-levels"},
+	{"manual-data-compaction"},
+	{"migrating-primary-keys"},
+	{"ml-emulator"},
+	{"ml-tutorial"},
+	{"ml-tutorial-embeddings"},
+	{"ml-tutorial-generative-ai"},
+	{"named-schemas"},
+	{"pre-splitting-overview"},
+	{"primary-key-default-value"},
+	{"query-columnar-data"},
+	{"query-execution-plans"},
+	{"query-optimizer/manage-query-optimizer"},
+	{"query-optimizer/version-statistics"},
+	{"query-optimizer/versions"},
+	{"queues/queues-examples"},
+	{"queues/queues-using"},
+	{"schema-and-data-model"},
+	{"schema-design"},
+	{"schema-updates"},
+	{"schema-updates-best-practices"},
+	{"secondary-indexes"},
+	{"sequence-tasks"},
+	{"set-default-time-zone"},
+	{"sql-best-practices"},
+	{"storing-numeric-data"},
+	{"table-name-synonym"},
+	{"troubleshooting-performance-regressions"},
+	{"ttl/monitoring-and-metrics"},
+	{"ttl/working-with-ttl"},
+	{"use-pitr"},
+	{"use-select-for-update-repeatable-read"},
+	{"use-select-for-update-serializable"},
+	{"use-sqlalchemy"},
+	{"vector-index-best-practices"},
+	{"vector-indexes"},
+	{"vector-search-best-practices"},
+	{"vector-search-tutorial"},
+	{"views"},
+	{"working-with-json"},
+	{"write-sql-gemini"},
+}
diff --git a/cmd/spanner-corpus/triage.go b/cmd/spanner-corpus/triage.go
new file mode 100644
index 0000000..2e243f5
--- /dev/null
+++ b/cmd/spanner-corpus/triage.go
@@ -0,0 +1,135 @@
+package main
+
+import (
+	"bufio"
+	"crypto/sha256"
+	"encoding/hex"
+	"fmt"
+	"os"
+	"regexp"
+	"strings"
+)
+
+// Verdicts for statements the reference parser rejects, recorded in
+// triage.txt.
+const (
+	// verdictSpanner is valid Spanner syntax that GoogleSQL's parser does not
+	// accept. These are written to .spanner.sql as the backlog of
+	// Spanner-only syntax.
+	verdictSpanner = "spanner"
+	// verdictTypo is an example that is broken in the docs. Dropped.
+	verdictTypo = "typo"
+	// verdictFragment is a grammar template or partial statement the
+	// fragment heuristics miss. Dropped.
+	verdictFragment = "fragment"
+)
+
+type verdict struct {
+	kind string
+	note string
+}
+
+// stmtID is the stable id triage.txt uses for a statement.
+func stmtID(sql string) string {
+	sum := sha256.Sum256([]byte(sql))
+	return hex.EncodeToString(sum[:])[:12]
+}
+
+// loadTriage reads triage.txt: one "  " per line, with
+// "#" comments.
+func loadTriage(path string) (map[string]verdict, error) {
+	f, err := os.Open(path)
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	out := map[string]verdict{}
+	sc := bufio.NewScanner(f)
+	for n := 1; sc.Scan(); n++ {
+		line := strings.TrimSpace(sc.Text())
+		if line == "" || strings.HasPrefix(line, "#") {
+			continue
+		}
+		fields := strings.Fields(line)
+		if len(fields) < 2 {
+			return nil, fmt.Errorf("%s:%d: want \"  [note]\"", path, n)
+		}
+		switch fields[1] {
+		case verdictSpanner, verdictTypo, verdictFragment:
+		default:
+			return nil, fmt.Errorf("%s:%d: unknown verdict %q", path, n, fields[1])
+		}
+		out[fields[0]] = verdict{kind: fields[1], note: strings.Join(fields[2:], " ")}
+	}
+	return out, sc.Err()
+}
+
+// statementStarts are the keywords a complete statement or script can begin
+// with. A rejected example starting with anything else (an identifier, a
+// clause keyword like MATCH or OPTIONS, "|>") is a fragment.
+var statementStarts = map[string]bool{}
+
+func init() {
+	for _, kw := range strings.Fields(`
+		ABORT ALTER ANALYZE ASSERT BEGIN BREAK CALL CASE CLONE COMMIT CONTINUE
+		CREATE DECLARE DEFINE DELETE DESCRIBE DROP EXECUTE EXPLAIN EXPORT FOR
+		FROM GRANT GRAPH IF IMPORT INSERT ITERATE LEAVE LOAD LOOP MERGE RAISE
+		RENAME REPEAT RETURN REVOKE ROLLBACK RUN SELECT SET SHOW START TABLE
+		TRUNCATE UNDROP UPDATE VALUES WHILE WITH`) {
+		statementStarts[kw] = true
+	}
+}
+
+// grammarMarkers match reference-grammar notation in code (outside literals
+// and comments): ellipses, optional "[AS]"-style brackets, "{a | b}"
+// alternations, , and markdown fences leaked into a
+// code block.
+var grammarMarkers = []*regexp.Regexp{
+	regexp.MustCompile(`\.\.\.|…`),
+	regexp.MustCompile(`(?i)\[\s*(?:,|\[|AS\b|NOT\b|OR\b|IF\b|FROM\b|COLUMN\b|ONLY\b|RECURSIVE\b|DISTINCT\b|ALL\b|VECTOR\b|PARENT\b)`),
+	regexp.MustCompile(`\{[^{}]*\|[^{}]*\}`),
+	regexp.MustCompile(`<[a-z][a-z0-9]*[_ ][a-z0-9_ ]*>`),
+	regexp.MustCompile("```"),
+	regexp.MustCompile(`//`),                 // pseudo-comments like "// INVALID"
+	regexp.MustCompile(`\$[A-Za-z_]`),        // $placeholder macros
+	regexp.MustCompile(`^\s*@\{[^}]*\}\s*$`), // a hint on its own
+}
+
+// documentedErrorRE matches a leading comment that marks an example as
+// intentionally invalid.
+var documentedErrorRE = regexp.MustCompile(`(?i)^\s*(?:--|#)[^\n]*\b(?:error|unsupported|invalid|not supported)\b`)
+
+// graphPatternRE matches a bare graph pattern such as "(p:Person)",
+// "(a)-[e]->(b)" or "((a))(-[t]->(b))": parentheses, then an optional
+// variable and a label, property spec, WHERE, edge or closing paren.
+var graphPatternRE = regexp.MustCompile(`(?i)^\s*\(+\s*(?:-|\w*\s*(?::|\bIS\b|\{|\bWHERE\b|\)))`)
+
+var firstWordRE = regexp.MustCompile(`^\s*([A-Za-z_]+|\S)`)
+
+// fragmentReason reports why a rejected statement looks like a grammar
+// fragment rather than a complete statement, or "" if it does not.
+func fragmentReason(sql string) string {
+	if documentedErrorRE.MatchString(sql) {
+		return "documented error example"
+	}
+	code := codeOnly(sql)
+	if graphPatternRE.MatchString(code) {
+		return "bare graph pattern"
+	}
+	for _, re := range grammarMarkers {
+		if m := re.FindString(code); m != "" {
+			return fmt.Sprintf("grammar notation %q", m)
+		}
+	}
+	m := firstWordRE.FindStringSubmatch(code)
+	if m == nil {
+		return "no code"
+	}
+	switch w := m[1]; {
+	case w == "(" || w == "@":
+		return ""
+	case !statementStarts[strings.ToUpper(w)]:
+		return fmt.Sprintf("starts with %q", w)
+	}
+	return ""
+}
diff --git a/cmd/spanner-corpus/triage.txt b/cmd/spanner-corpus/triage.txt
new file mode 100644
index 0000000..a492bb8
--- /dev/null
+++ b/cmd/spanner-corpus/triage.txt
@@ -0,0 +1,207 @@
+# Triage verdicts for example statements the reference parser rejects.
+# See cmd/spanner-corpus/main.go. Format:   
+# Verdicts: spanner (Spanner-only syntax), typo (broken in the docs),
+# fragment (not a complete statement; missed by the heuristics).
+
+# Spanner-only syntax (written to .spanner.sql).
+22b0ba359fa8 spanner ALTER COLUMN to a non-stored generated column (generated-column.how-to)
+11fea51e4d41 spanner ALTER INDEX ADD/DROP STORED COLUMN (secondary-indexes)
+23425be4c4f6 spanner ALTER INDEX ADD/DROP STORED COLUMN (secondary-indexes)
+3127ba6ca90c spanner ARRAY(vector_length=>N) (data-definition-language)
+539ecbbae570 spanner ARRAY(vector_length=>N) (data-definition-language)
+747de5553744 spanner ARRAY(vector_length=>N) (graph.perform-vector-similarity-search)
+d8d4d1cfa615 spanner ARRAY(vector_length=>N) (graph.perform-vector-similarity-search)
+132bb7f629e6 spanner ARRAY(vector_length=>N) (vector-indexes)
+e2f603537017 spanner ARRAY(vector_length=>N) (vector-indexes)
+cbdc2fb90b11 spanner AUTO_INCREMENT columns (primary-key-default-value)
+83fdbb648537 spanner CREATE ROLE (fgac-about)
+1d9d096db2f8 spanner CREATE ROLE (fgac-queues)
+5b54ac5fbc37 spanner CREATE ROLE (fgac-queues)
+fa3fd655dd8d spanner CREATE ROLE (fgac-queues)
+bfb46504a7f2 spanner CREATE ROLE (graph.graph-with-views-how-to)
+5f1bb1f70bc6 spanner CREATE ROLE (named-schemas)
+d4c050bd9942 spanner DEFAULT after NOT NULL (timestamp_functions)
+43c581426688 spanner GRANT/REVOKE for fine-grained access control (configure-fgac)
+6d9c8bec9783 spanner GRANT/REVOKE for fine-grained access control (configure-fgac)
+aeacd0809f0e spanner GRANT/REVOKE for fine-grained access control (configure-fgac)
+6591668a948f spanner GRANT/REVOKE for fine-grained access control (create-manage-views)
+6ed82bd51f5f spanner GRANT/REVOKE for fine-grained access control (create-manage-views)
+2faa40dfa4bd spanner GRANT/REVOKE for fine-grained access control (data-definition-language)
+4f91c277724d spanner GRANT/REVOKE for fine-grained access control (data-definition-language)
+56ddc9700e16 spanner GRANT/REVOKE for fine-grained access control (data-definition-language)
+a7ac2297db8d spanner GRANT/REVOKE for fine-grained access control (data-definition-language)
+a859440344c5 spanner GRANT/REVOKE for fine-grained access control (data-definition-language)
+ac9569aebf1c spanner GRANT/REVOKE for fine-grained access control (data-definition-language)
+aecb32dadca9 spanner GRANT/REVOKE for fine-grained access control (data-definition-language)
+c10d4211df33 spanner GRANT/REVOKE for fine-grained access control (data-definition-language)
+2a3d1404fb33 spanner GRANT/REVOKE for fine-grained access control (fgac-about)
+029cabe80e58 spanner GRANT/REVOKE for fine-grained access control (fgac-privileges)
+1ea527a72cbb spanner GRANT/REVOKE for fine-grained access control (fgac-privileges)
+2b4080c56596 spanner GRANT/REVOKE for fine-grained access control (fgac-privileges)
+42e822b83cd2 spanner GRANT/REVOKE for fine-grained access control (fgac-privileges)
+4808623c5c48 spanner GRANT/REVOKE for fine-grained access control (fgac-privileges)
+81ae2c0b5695 spanner GRANT/REVOKE for fine-grained access control (fgac-privileges)
+871554001108 spanner GRANT/REVOKE for fine-grained access control (fgac-privileges)
+8f3a85a82a45 spanner GRANT/REVOKE for fine-grained access control (fgac-privileges)
+adfe06052399 spanner GRANT/REVOKE for fine-grained access control (fgac-privileges)
+d9c104a7af3a spanner GRANT/REVOKE for fine-grained access control (fgac-privileges)
+f0715dd33a8c spanner GRANT/REVOKE for fine-grained access control (fgac-privileges)
+f64849984457 spanner GRANT/REVOKE for fine-grained access control (fgac-privileges)
+fb67d4171d70 spanner GRANT/REVOKE for fine-grained access control (fgac-privileges)
+012cb76568a3 spanner GRANT/REVOKE for fine-grained access control (fgac-queues)
+2e55ab4a6d03 spanner GRANT/REVOKE for fine-grained access control (fgac-queues)
+3f61756abb45 spanner GRANT/REVOKE for fine-grained access control (fgac-queues)
+4836493a0b5e spanner GRANT/REVOKE for fine-grained access control (fgac-queues)
+4f7c0784c8e3 spanner GRANT/REVOKE for fine-grained access control (fgac-queues)
+69358adb0d47 spanner GRANT/REVOKE for fine-grained access control (fgac-system-roles)
+6f87e7b906f6 spanner GRANT/REVOKE for fine-grained access control (fgac-system-roles)
+aaf1ea5ec0ab spanner GRANT/REVOKE for fine-grained access control (fgac-system-roles)
+cf49207a4c42 spanner GRANT/REVOKE for fine-grained access control (fgac-system-roles)
+6d9fa1b755f8 spanner GRANT/REVOKE for fine-grained access control (graph.graph-with-views-how-to)
+16552ab9a0e5 spanner GRANT/REVOKE for fine-grained access control (named-schemas)
+2907f4ab8957 spanner GRANT/REVOKE for fine-grained access control (named-schemas)
+5101cbaad9f1 spanner GRANT/REVOKE for fine-grained access control (named-schemas)
+aaefbbaa683f spanner GRANT/REVOKE for fine-grained access control (named-schemas)
+d509cbcdf0c6 spanner GRANT/REVOKE for fine-grained access control (named-schemas)
+4c161277f464 spanner INTERLEAVE IN without PARENT (graph.reverse-etl)
+bda3df9837d9 spanner INTERLEAVE IN without PARENT (schema-and-data-model)
+0aa013a6dacc spanner RENAME TABLE with several renames (data-definition-language)
+560790b2e65e spanner RENAME TABLE with several renames (table-name-synonym)
+169335cabce7 spanner change streams (change-streams.manage)
+225eb11e770e spanner change streams (change-streams.manage)
+34c6b852464a spanner change streams (change-streams.manage)
+35d491ab634c spanner change streams (change-streams.manage)
+4a6707c3d12a spanner change streams (change-streams.manage)
+52f1211c24a1 spanner change streams (change-streams.manage)
+545827191d61 spanner change streams (change-streams.manage)
+5c4ce0908b11 spanner change streams (change-streams.manage)
+5c61bc693ebd spanner change streams (change-streams.manage)
+74879c0f988e spanner change streams (change-streams.manage)
+863484ba8dd9 spanner change streams (change-streams.manage)
+87211d59c264 spanner change streams (change-streams.manage)
+8e3dc42d0625 spanner change streams (change-streams.manage)
+9e2d2b822536 spanner change streams (change-streams.manage)
+ad1588a2a382 spanner change streams (change-streams.manage)
+bc4cac6f97b3 spanner change streams (change-streams.manage)
+d4439304f0c3 spanner change streams (change-streams.manage)
+d4f93cc03140 spanner change streams (change-streams.manage)
+dfdced3e930e spanner change streams (change-streams.manage)
+e68a813ec69e spanner change streams (change-streams.manage)
+e86c61083c22 spanner change streams (change-streams.manage)
+f5f4fc880a29 spanner change streams (change-streams.manage)
+f795e8dbf4ec spanner change streams (change-streams.manage)
+c0682732ef08 spanner column ON UPDATE expression (dml-syntax)
+49501c169a47 spanner data placement / geo-partitioning (create-manage-data-placements)
+4a7131371b56 spanner data placement / geo-partitioning (create-manage-data-placements)
+f751a8bfa73e spanner data placement / geo-partitioning (create-manage-data-placements)
+17b21dc96ab1 spanner data placement / geo-partitioning (index-geo-partitioning)
+2f15738e5e85 spanner data placement / geo-partitioning (index-geo-partitioning)
+cc13be61ce70 spanner data placement / geo-partitioning (index-geo-partitioning)
+cd34e8a576bb spanner data placement / geo-partitioning (index-geo-partitioning)
+4cb01623f288 spanner database ids with dashes (use-pitr)
+26a8b44f4976 spanner generated column after NOT NULL (generated-column.how-to)
+adb118abcad0 spanner generated column after NOT NULL (generated-column.how-to)
+e993c9dd5625 spanner generated column after NOT NULL (schema-design)
+1d6473a902cf spanner identity columns (migrating-primary-keys)
+370cc1df2396 spanner identity columns (migrating-primary-keys)
+6e4905147d32 spanner identity columns (migrating-primary-keys)
+a2561c1baafc spanner identity columns (migrating-primary-keys)
+2fad4777471c spanner identity columns (primary-key-default-value)
+57c70c72e944 spanner identity columns (primary-key-default-value)
+e19b178d0d2e spanner identity columns (primary-key-default-value)
+f19adcb13ef5 spanner identity columns (primary-key-default-value)
+01a0dff9e706 spanner locality groups (create-manage-locality-groups)
+285931116d26 spanner locality groups (create-manage-locality-groups)
+2a925ceede24 spanner locality groups (create-manage-locality-groups)
+32396e1bf247 spanner locality groups (create-manage-locality-groups)
+999a3d5dfef6 spanner locality groups (create-manage-locality-groups)
+b2707791e64d spanner locality groups (create-manage-locality-groups)
+bd3d36a38974 spanner locality groups (create-manage-locality-groups)
+c8f11334b5a8 spanner locality groups (create-manage-locality-groups)
+6b16dd2c79d0 spanner proto bundles (data-definition-language)
+50c66ca6ed0e spanner proto bundles (protocol-buffers)
+9879725f4dbc spanner proto bundles (protocol-buffers)
+b456ed9cdb3e spanner proto bundles (protocol-buffers)
+d6be672a4744 spanner proto bundles (protocol-buffers)
+f26727d5910e spanner proto bundles (protocol-buffers)
+41eab2a72585 spanner proto bundles (schema-updates)
+fe13330c8cf7 spanner proto bundles (secondary-indexes)
+23d917fa8b61 spanner queues (queues.queues-examples)
+8f0f50d8a999 spanner queues (queues.queues-examples)
+aef3cbd9ca49 spanner queues (queues.queues-using)
+9c7464fea28c spanner row deletion policy (TTL) (graph.best-practices-designing-schema)
+af7e3c0e2324 spanner row deletion policy (TTL) (graph.best-practices-designing-schema)
+27727b8671c2 spanner row deletion policy (TTL) (graph.reverse-etl)
+c2e5bc718928 spanner row deletion policy (TTL) (graph.reverse-etl)
+14c9930fca7c spanner row deletion policy (TTL) (ttl.working-with-ttl)
+1de829b56fcf spanner row deletion policy (TTL) (ttl.working-with-ttl)
+278715e1de63 spanner row deletion policy (TTL) (ttl.working-with-ttl)
+9fabc8cd139c spanner row deletion policy (TTL) (ttl.working-with-ttl)
+ebe181a95224 spanner row deletion policy (TTL) (ttl.working-with-ttl)
+4a2243fb3317 spanner search index clauses (ORDER BY, WHERE, INTERLEAVE IN) (full-text-search.facets)
+497af036fbe9 spanner search index clauses (ORDER BY, WHERE, INTERLEAVE IN) (full-text-search.search-indexes)
+866fcf6e1c19 spanner search index clauses (ORDER BY, WHERE, INTERLEAVE IN) (full-text-search.search-indexes)
+9897bb972a57 spanner search index clauses (ORDER BY, WHERE, INTERLEAVE IN) (full-text-search.search-indexes)
+eef67d96745a spanner search index clauses (ORDER BY, WHERE, INTERLEAVE IN) (full-text-search.search-indexes)
+f7df4248d043 spanner search index clauses (ORDER BY, WHERE, INTERLEAVE IN) (full-text-search.top-k-pattern-matching)
+51eb77fd9a06 spanner table OPTIONS after PRIMARY KEY (configure-columnar-engine)
+440625a3c44c spanner table OPTIONS after PRIMARY KEY (create-manage-locality-groups)
+d135863ff683 spanner table OPTIONS after PRIMARY KEY (create-manage-locality-groups)
+da7b47723b15 spanner table OPTIONS after PRIMARY KEY (create-manage-locality-groups)
+c77ee47eac7e spanner table OPTIONS after PRIMARY KEY (full-text-search.search-query-enhancement)
+d69ff196cee4 spanner table OPTIONS after PRIMARY KEY (full-text-search.search-query-enhancement)
+116da4526916 spanner table synonyms (table-name-synonym)
+47f072ed10b4 spanner table synonyms (table-name-synonym)
+7a6c31530ba7 spanner table synonyms (table-name-synonym)
+815ade90f45a spanner table synonyms (table-name-synonym)
+92ddbe4350a8 spanner table synonyms (table-name-synonym)
+b44f41011be9 spanner table synonyms (table-name-synonym)
+c388096005d9 spanner table synonyms (table-name-synonym)
+2797564c39f8 spanner trailing comma in OPTIONS (migrating-primary-keys)
+87cc4f015ea6 spanner vector index clauses (data-definition-language)
+988db338d407 spanner vector index clauses (data-definition-language)
+c17672af79d0 spanner vector index clauses (data-definition-language)
+31c46e52a9b5 spanner vector index clauses (graph.perform-vector-similarity-search)
+c2a8b55f1e16 spanner vector index clauses (vector-indexes)
+d2267b14470f spanner vector index clauses (vector-search-tutorial)
+
+# Examples broken in the docs.
+05bdd1196cbe typo "," before PRIMARY KEY (table-name-synonym)
+77a2c9b8cd37 typo "," before PRIMARY KEY (table-name-synonym)
+794497e99af5 typo "," before PRIMARY KEY (table-name-synonym)
+91b906573a7c typo "," before PRIMARY KEY (table-name-synonym)
+ad6c128bccac typo "," before PRIMARY KEY (table-name-synonym)
+d7a86fb426e3 typo ";" inside a CASE expression (views)
+b96f69b5c88d typo FROM ... WHERE without |> (secondary-indexes)
+92778e416f6e typo INSERT missing VALUES (dml-syntax)
+9ec9a91db88f typo INSERT missing VALUES (dml-syntax)
+d0cecd8d4edb typo INSERT missing VALUES (dml-syntax)
+086a4c790914 typo PROTO is a reserved keyword; alias must be quoted (protocol_buffer_functions)
+8cd8da550150 typo PROTO is a reserved keyword; alias must be quoted (protocol_buffer_functions)
+ef48d30d9f91 typo PROTO is a reserved keyword; alias must be quoted (protocol_buffer_functions)
+f219de5c4dda typo PROTO(MAX) is not a Spanner type (secondary-indexes)
+29ae3e974716 typo SET DATABASE OPTIONS; ALTER DATABASE ... SET OPTIONS is the DDL (foreign-keys.overview)
+1763636d315e typo YAML markup in the SQL block (secondary-indexes)
+b2fa9aec3a31 typo missing "," between columns (best-practices-gaming-database)
+e2189e230206 typo statements without ";" between them (create-query-database-console)
+61cae0506200 typo statements without ";" between them (full-text-search.facets)
+c25816574cd4 typo stray ">" after package_name (query-optimizer.manage-query-optimizer)
+a294f2aa74c2 typo unbalanced brackets (mathematical_functions)
+d2531672a2f3 typo unbalanced parentheses (json_functions)
+76817f07b20d typo unbalanced parentheses (sequence_functions)
+
+# Fragments and error examples the heuristics miss.
+b6f2a97e1700 fragment  in the query (create-query-database-console)
+10ef749a13df fragment  in the query (find-k-nearest-neighbors)
+ad3ada26181a fragment CAST to an unsupported type, an error example (json_functions)
+23504b9be43c fragment MySQL example (migrating-primary-keys)
+483a6db44983 fragment UPDATE SET clause fragment (dml-syntax)
+2e7ba4b5f5d4 fragment grammar (data-definition-language)
+0825d3025201 fragment grammar (query-syntax)
+02daacf275c8 fragment index name omitted (vector-search-best-practices)
+6ba570b23582 fragment index name omitted (vector-search-best-practices)
+db5390b41bb7 fragment index name omitted (vector-search-best-practices)
+d75ff8191719 fragment query placeholders (query-syntax)
+335fbff3b8da fragment shows where FOR UPDATE is not allowed (use-select-for-update-serializable)
+9e9a06bb6043 fragment troubleshooting example of an error (graph.troubleshoot)
+0dc5e580229d fragment two expressions without a separator (operators)
diff --git a/internal/dump/dump.go b/internal/dump/dump.go
index 9f208a8..2cef58a 100644
--- a/internal/dump/dump.go
+++ b/internal/dump/dump.go
@@ -354,6 +354,15 @@ func nodeString(n ast.Node) string {
 			return "DropRowAccessPolicyStatement(is_if_exists)"
 		}
 		return "DropRowAccessPolicyStatement"
+	case *ast.DropIndexStatement:
+		name := "DropSearchIndexStatement"
+		if t.Vector {
+			name = "DropVectorIndexStatement"
+		}
+		if t.IsIfExists {
+			return name + "(is_if_exists)"
+		}
+		return name
 	case *ast.DropAllRowAccessPoliciesStatement:
 		return "DropAllRowAccessPoliciesStatement"
 	case *ast.DescribeStatement:
@@ -389,6 +398,18 @@ func nodeString(n ast.Node) string {
 		return "SpannerTableOptions"
 	case *ast.SpannerInterleaveClause:
 		return "SpannerInterleaveClause"
+	case *ast.AlterIndexStatement:
+		if t.IsIfExists {
+			return "AlterIndexStatement(is_if_exists)"
+		}
+		return "AlterIndexStatement"
+	case *ast.AddColumnIdentifierAction:
+		if t.IsIfNotExists {
+			return "AddColumnIdentifierAction(is_if_not_exists)"
+		}
+		return "AddColumnIdentifierAction"
+	case *ast.RebuildAction:
+		return "RebuildAction"
 	case *ast.DropColumnAction:
 		if t.IsIfExists {
 			return "DropColumnAction(is_if_exists)"
@@ -667,6 +688,8 @@ func nodeString(n ast.Node) string {
 		return "ColumnAttributeList"
 	case *ast.NotNullColumnAttribute:
 		return "NotNullColumnAttribute"
+	case *ast.HiddenColumnAttribute:
+		return "HiddenColumnAttribute"
 	case *ast.PrimaryKeyColumnAttribute:
 		if t.Enforced {
 			return "PrimaryKeyColumnAttribute(ENFORCED)"
@@ -1102,6 +1125,48 @@ func nodeString(n ast.Node) string {
 		return fmt.Sprintf("CreateExternalSchemaStatement(%s)", strings.Join(mods, ", "))
 	case *ast.DefineTableStatement:
 		return "DefineTableStatement"
+	case *ast.CreatePropertyGraphStatement:
+		// Modifier order matches the base ASTCreateStatement modifiers:
+		// scope, is_or_replace, is_if_not_exists.
+		var mods []string
+		switch t.Scope {
+		case "PRIVATE":
+			mods = append(mods, "is_private")
+		case "PUBLIC":
+			mods = append(mods, "is_public")
+		case "TEMP":
+			mods = append(mods, "is_temp")
+		}
+		if t.IsOrReplace {
+			mods = append(mods, "is_or_replace")
+		}
+		if t.IsIfNotExists {
+			mods = append(mods, "is_if_not_exists")
+		}
+		if len(mods) == 0 {
+			return "CreatePropertyGraphStatement"
+		}
+		return fmt.Sprintf("CreatePropertyGraphStatement(%s)", strings.Join(mods, ", "))
+	case *ast.GraphElementTableList:
+		return "GraphElementTableList"
+	case *ast.GraphElementTable:
+		return "GraphElementTable"
+	case *ast.GraphNodeTableReference:
+		return "GraphNodeTableReference(" + t.Type + ")"
+	case *ast.GraphElementLabelAndPropertiesList:
+		return "GraphElementLabelAndPropertiesList"
+	case *ast.GraphElementLabelAndProperties:
+		return "GraphElementLabelAndProperties"
+	case *ast.GraphProperties:
+		return "GraphProperties"
+	case *ast.GraphDerivedPropertyList:
+		return "GraphDerivedPropertyList"
+	case *ast.GraphDerivedProperty:
+		return "GraphDerivedProperty"
+	case *ast.GraphDynamicLabel:
+		return "GraphDynamicLabel"
+	case *ast.GraphDynamicProperties:
+		return "GraphDynamicProperties"
 	case *ast.CreateConstantStatement:
 		// Modifier order matches the base ASTCreateStatement modifiers:
 		// scope, is_or_replace, is_if_not_exists.
diff --git a/internal/refparse/refparse.go b/internal/refparse/refparse.go
new file mode 100644
index 0000000..1246f2a
--- /dev/null
+++ b/internal/refparse/refparse.go
@@ -0,0 +1,126 @@
+// Package refparse runs GoogleSQL's reference parser, using the prebuilt
+// execute_query binary published with googlesql releases
+// (https://github.com/google/googlesql/releases), to mint golden parse trees
+// for queries that are not in the vendored testdata.
+package refparse
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"net/http"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"runtime"
+	"strings"
+)
+
+// Release is the googlesql release used to generate golden output. It must
+// match the release the vendored parser/testdata files were taken from;
+// update both together.
+const Release = "2026.01.1"
+
+// binDir is where the downloaded binary is cached, relative to the repository
+// root (gitignored).
+const binDir = ".execute_query"
+
+// Options configure one reference parse.
+type Options struct {
+	// LanguageFeatures is an --enabled_language_features spec such as
+	// "MAXIMUM,+SPANNER_LEGACY_DDL". Empty uses the binary's default.
+	LanguageFeatures string
+	// Mode is the --sql_mode: "" or "query" for a single statement,
+	// "script", or "expression".
+	Mode string
+}
+
+// Parse runs execute_query in parse mode and returns the parse tree debug
+// string (without location text). A parse failure is returned as an error
+// carrying the reference's message.
+func Parse(bin, sql string, opts Options) (string, error) {
+	// The parser test driver prints explicit ASC as "ASC EXPLICITLY";
+	// execute_query only does so with this flag.
+	args := []string{"--mode=parse", "--output_asc_explicitly"}
+	if opts.LanguageFeatures != "" {
+		args = append(args, "--enabled_language_features="+opts.LanguageFeatures)
+	}
+	if opts.Mode != "" {
+		args = append(args, "--sql_mode="+opts.Mode)
+	}
+	// "--" ends flag parsing, so SQL that starts with a "--" comment is not
+	// taken for a flag.
+	args = append(args, "--", sql)
+	cmd := exec.Command(bin, args...)
+	var stdout, stderr bytes.Buffer
+	cmd.Stdout = &stdout
+	cmd.Stderr = &stderr
+	if err := cmd.Run(); err != nil {
+		msg := strings.TrimSpace(stderr.String())
+		if msg == "" {
+			msg = strings.TrimSpace(stdout.String())
+		}
+		if msg == "" {
+			msg = err.Error()
+		}
+		return "", fmt.Errorf("execute_query: %s", msg)
+	}
+	// Parse errors are printed to stdout with a zero exit status.
+	out := strings.TrimSpace(stdout.String())
+	if msg, ok := strings.CutPrefix(out, "ERROR: "); ok {
+		return "", fmt.Errorf("execute_query: %s", msg)
+	}
+	return out, nil
+}
+
+// EnsureBinary downloads the pinned execute_query release binary if needed
+// and returns its path.
+func EnsureBinary() (string, error) {
+	var asset string
+	switch runtime.GOOS {
+	case "linux":
+		asset = "execute_query_linux"
+	case "darwin":
+		asset = "execute_query_macos"
+	default:
+		return "", fmt.Errorf("no prebuilt execute_query binary for %s", runtime.GOOS)
+	}
+
+	bin := filepath.Join(binDir, "execute_query-"+Release)
+	if _, err := os.Stat(bin); err == nil {
+		return bin, nil
+	}
+
+	url := fmt.Sprintf("https://github.com/google/googlesql/releases/download/%s/%s", Release, asset)
+	fmt.Fprintf(os.Stderr, "Downloading %s...\n", url)
+
+	if err := os.MkdirAll(binDir, 0o755); err != nil {
+		return "", err
+	}
+	resp, err := http.Get(url)
+	if err != nil {
+		return "", fmt.Errorf("downloading: %w", err)
+	}
+	defer resp.Body.Close()
+	if resp.StatusCode != http.StatusOK {
+		return "", fmt.Errorf("download failed with status %d", resp.StatusCode)
+	}
+
+	tmp := bin + ".tmp"
+	out, err := os.OpenFile(tmp, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o755)
+	if err != nil {
+		return "", err
+	}
+	if _, err := io.Copy(out, resp.Body); err != nil {
+		out.Close()
+		os.Remove(tmp)
+		return "", fmt.Errorf("writing binary: %w", err)
+	}
+	out.Close()
+	if err := os.Rename(tmp, bin); err != nil {
+		os.Remove(tmp)
+		return "", err
+	}
+	fmt.Fprintf(os.Stderr, "Installed %s\n", bin)
+	return bin, nil
+}
diff --git a/internal/testfile/metadata.go b/internal/testfile/metadata.go
index c2c912e..d980352 100644
--- a/internal/testfile/metadata.go
+++ b/internal/testfile/metadata.go
@@ -2,7 +2,9 @@ package testfile
 
 import (
 	"encoding/json"
+	"io/fs"
 	"os"
+	"path/filepath"
 	"strings"
 )
 
@@ -53,3 +55,22 @@ func SaveMetadata(testPath string, meta *Metadata) error {
 	}
 	return os.WriteFile(MetadataPath(testPath), append(data, '\n'), 0o644)
 }
+
+// FindFiles returns the .test files under dir, recursively, in lexical
+// order. A missing dir yields no files.
+func FindFiles(dir string) ([]string, error) {
+	var files []string
+	err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
+		if err != nil {
+			if os.IsNotExist(err) && path == dir {
+				return filepath.SkipDir
+			}
+			return err
+		}
+		if !d.IsDir() && strings.HasSuffix(path, ".test") {
+			files = append(files, path)
+		}
+		return nil
+	})
+	return files, err
+}
diff --git a/parser/graph_ddl.go b/parser/graph_ddl.go
new file mode 100644
index 0000000..f283528
--- /dev/null
+++ b/parser/graph_ddl.go
@@ -0,0 +1,380 @@
+package parser
+
+// Property graph DDL: CREATE PROPERTY GRAPH. It mirrors
+// create_property_graph_statement and the element_table_* / label / properties
+// productions in googlesql/parser/googlesql.tm. DROP PROPERTY GRAPH is a
+// generic DropStatement (see parseDropStatement).
+
+import (
+	"github.com/sqlc-dev/zetajones/ast"
+	"github.com/sqlc-dev/zetajones/token"
+)
+
+// parseCreatePropertyGraphStatement parses the tail of "CREATE [OR REPLACE]
+// [scope] PROPERTY GRAPH [IF NOT EXISTS] name NODE TABLES (...) [EDGE TABLES
+// (...)] [OPTIONS(...)]"; see create_property_graph_statement in
+// googlesql.tm. The parser is at PROPERTY.
+func (p *parser) parseCreatePropertyGraphStatement(createTok token.Token, scope string, isOrReplace bool) (ast.Statement, error) {
+	p.advance() // PROPERTY
+	if _, err := p.expectKeyword("GRAPH"); err != nil {
+		return nil, err
+	}
+	stmt := &ast.CreatePropertyGraphStatement{Scope: scope, IsOrReplace: isOrReplace}
+	ifNotExists, err := p.parseOptIfNotExists()
+	if err != nil {
+		return nil, err
+	}
+	stmt.IsIfNotExists = ifNotExists
+	if stmt.Name, err = p.parsePathExpression(); err != nil {
+		return nil, err
+	}
+	if !isKeyword(p.peek(), "NODE") {
+		return nil, p.errorf(p.peek().Pos, `Syntax error: Expected "." or keyword NODE but got %s`, describeToken(p.peek()))
+	}
+	p.advance() // NODE
+	if _, err := p.expectKeyword("TABLES"); err != nil {
+		return nil, err
+	}
+	if stmt.NodeTables, err = p.parseGraphElementTableList(); err != nil {
+		return nil, err
+	}
+	if isKeyword(p.peek(), "EDGE") {
+		p.advance() // EDGE
+		if _, err := p.expectKeyword("TABLES"); err != nil {
+			return nil, err
+		}
+		if stmt.EdgeTables, err = p.parseGraphElementTableList(); err != nil {
+			return nil, err
+		}
+	}
+	if isKeyword(p.peek(), "OPTIONS") {
+		p.advance() // OPTIONS
+		if stmt.Options, err = p.parseOptionsList(); err != nil {
+			return nil, err
+		}
+	}
+	stmt.Span = span(createTok.Pos, p.prevEnd())
+	return stmt, nil
+}
+
+// parseGraphElementTableList parses "( element_table_definition, ... [,] )";
+// see element_table_list in googlesql.tm. The list's location includes the
+// parentheses.
+func (p *parser) parseGraphElementTableList() (*ast.GraphElementTableList, error) {
+	lparen, err := p.expect(token.LPAREN, `"("`)
+	if err != nil {
+		return nil, err
+	}
+	list := &ast.GraphElementTableList{}
+	for {
+		table, err := p.parseGraphElementTable()
+		if err != nil {
+			return nil, err
+		}
+		list.Tables = append(list.Tables, table)
+		if p.peek().Kind != token.COMMA {
+			if p.peek().Kind != token.RPAREN {
+				return nil, p.errorf(p.peek().Pos, `Syntax error: Expected ")" or "," but got %s`, describeToken(p.peek()))
+			}
+			break
+		}
+		p.advance() // ,
+		if p.peek().Kind == token.RPAREN {
+			break // trailing comma
+		}
+	}
+	rparen, err := p.expect(token.RPAREN, `")"`)
+	if err != nil {
+		return nil, err
+	}
+	list.Span = span(lparen.Pos, rparen.End)
+	return list, nil
+}
+
+// parseGraphElementTable parses an element_table_definition: "path [AS alias]
+// [KEY (...)] [SOURCE KEY ...] [DESTINATION KEY ...] [OPTIONS(...)]
+// [labels and properties] [DYNAMIC LABEL (...)] [DYNAMIC PROPERTIES (...)]";
+// see element_table_definition in googlesql.tm.
+func (p *parser) parseGraphElementTable() (*ast.GraphElementTable, error) {
+	name, err := p.parsePathExpression()
+	if err != nil {
+		return nil, err
+	}
+	table := &ast.GraphElementTable{Name: name}
+	if isKeyword(p.peek(), "AS") {
+		if table.Alias, err = p.parseRequiredAsAlias(); err != nil {
+			return nil, err
+		}
+	}
+	if isKeyword(p.peek(), "KEY") {
+		p.advance() // KEY
+		if table.Key, err = p.parseColumnList(); err != nil {
+			return nil, err
+		}
+	}
+	if isKeyword(p.peek(), "SOURCE") {
+		if table.Source, err = p.parseGraphNodeTableReference("SOURCE"); err != nil {
+			return nil, err
+		}
+	}
+	if isKeyword(p.peek(), "DESTINATION") {
+		if table.Destination, err = p.parseGraphNodeTableReference("DESTINATION"); err != nil {
+			return nil, err
+		}
+	}
+	if isKeyword(p.peek(), "OPTIONS") {
+		p.advance() // OPTIONS
+		if table.Options, err = p.parseOptionsList(); err != nil {
+			return nil, err
+		}
+	}
+	if table.LabelProperties, err = p.parseGraphLabelAndPropertiesList(); err != nil {
+		return nil, err
+	}
+	if err := p.parseGraphDynamicLabelAndProperties(table); err != nil {
+		return nil, err
+	}
+	table.Span = span(name.Pos(), p.prevEnd())
+	return table, nil
+}
+
+// parseGraphNodeTableReference parses "{SOURCE|DESTINATION} KEY (...)
+// REFERENCES node [(...)]"; see source_node_table_clause and
+// dest_node_table_clause in googlesql.tm. refType is the leading keyword.
+func (p *parser) parseGraphNodeTableReference(refType string) (*ast.GraphNodeTableReference, error) {
+	startTok := p.advance() // SOURCE or DESTINATION
+	if _, err := p.expectKeyword("KEY"); err != nil {
+		return nil, err
+	}
+	ref := &ast.GraphNodeTableReference{Type: refType}
+	var err error
+	if ref.EdgeColumns, err = p.parseColumnList(); err != nil {
+		return nil, err
+	}
+	if _, err := p.expectKeyword("REFERENCES"); err != nil {
+		return nil, err
+	}
+	if ref.NodeTable, err = p.parseIdentifier(); err != nil {
+		return nil, err
+	}
+	if p.peek().Kind == token.LPAREN {
+		if ref.NodeColumns, err = p.parseColumnList(); err != nil {
+			return nil, err
+		}
+	}
+	ref.Span = span(startTok.Pos, p.prevEnd())
+	return ref, nil
+}
+
+// parseGraphLabelAndPropertiesList parses opt_label_and_properties_clause. A
+// bare properties clause or an empty clause is an implicit DEFAULT LABEL; an
+// empty clause additionally means PROPERTIES ARE ALL COLUMNS, located at the
+// end of the preceding token.
+func (p *parser) parseGraphLabelAndPropertiesList() (*ast.GraphElementLabelAndPropertiesList, error) {
+	implicit := func(props *ast.GraphProperties) *ast.GraphElementLabelAndPropertiesList {
+		item := &ast.GraphElementLabelAndProperties{Span: props.Span, Properties: props}
+		return &ast.GraphElementLabelAndPropertiesList{Span: props.Span, Items: []*ast.GraphElementLabelAndProperties{item}}
+	}
+	switch {
+	case isKeyword(p.peek(), "PROPERTIES") || isKeyword(p.peek(), "NO"):
+		props, err := p.parseGraphProperties()
+		if err != nil {
+			return nil, err
+		}
+		return implicit(props), nil
+	case !isKeyword(p.peek(), "LABEL") && !isKeyword(p.peek(), "DEFAULT"):
+		end := p.prevEnd()
+		return implicit(&ast.GraphProperties{Span: span(end, end)}), nil
+	}
+	list := &ast.GraphElementLabelAndPropertiesList{}
+	for isKeyword(p.peek(), "LABEL") || isKeyword(p.peek(), "DEFAULT") {
+		startTok := p.advance() // DEFAULT or LABEL
+		item := &ast.GraphElementLabelAndProperties{}
+		var err error
+		if isKeyword(startTok, "DEFAULT") {
+			if _, err := p.expectKeyword("LABEL"); err != nil {
+				return nil, err
+			}
+			if isKeyword(p.peek(), "OPTIONS") {
+				p.advance() // OPTIONS
+				if item.Options, err = p.parseOptionsList(); err != nil {
+					return nil, err
+				}
+			}
+		} else if item.Label, err = p.parseIdentifier(); err != nil {
+			return nil, err
+		}
+		if isKeyword(p.peek(), "PROPERTIES") || isKeyword(p.peek(), "NO") {
+			if item.Properties, err = p.parseGraphProperties(); err != nil {
+				return nil, err
+			}
+		} else {
+			end := p.prevEnd()
+			item.Properties = &ast.GraphProperties{Span: span(end, end)}
+		}
+		item.Span = span(startTok.Pos, p.prevEnd())
+		list.Items = append(list.Items, item)
+	}
+	list.Span = span(list.Items[0].Pos(), p.prevEnd())
+	return list, nil
+}
+
+// atGraphLabel reports whether a "LABEL" or "DEFAULT LABEL" keyword sequence
+// (static_label_or_properties_keywords) starts at the next token.
+func (p *parser) atGraphLabel() bool {
+	return isKeyword(p.peek(), "LABEL") ||
+		(isKeyword(p.peek(), "DEFAULT") && isKeyword(p.peekAt(1), "LABEL"))
+}
+
+// atGraphProperties reports whether a "PROPERTIES" or "NO PROPERTIES"
+// keyword sequence (static_label_or_properties_keywords) starts at the next
+// token.
+func (p *parser) atGraphProperties() bool {
+	return isKeyword(p.peek(), "PROPERTIES") ||
+		(isKeyword(p.peek(), "NO") && isKeyword(p.peekAt(1), "PROPERTIES"))
+}
+
+// parseGraphProperties parses a properties_clause: "NO PROPERTIES",
+// "PROPERTIES [ARE] ALL COLUMNS [EXCEPT (...)]" or "PROPERTIES (derived, ...)";
+// see properties_clause in googlesql.tm.
+func (p *parser) parseGraphProperties() (*ast.GraphProperties, error) {
+	startTok := p.advance() // NO or PROPERTIES
+	props := &ast.GraphProperties{}
+	switch {
+	case isKeyword(startTok, "NO"):
+		if _, err := p.expectKeyword("PROPERTIES"); err != nil {
+			return nil, err
+		}
+		props.NoProperties = true
+	case p.peek().Kind == token.LPAREN:
+		p.advance() // (
+		list := &ast.GraphDerivedPropertyList{}
+		for {
+			prop, err := p.parseGraphDerivedProperty()
+			if err != nil {
+				return nil, err
+			}
+			list.Properties = append(list.Properties, prop)
+			if p.peek().Kind != token.COMMA {
+				break
+			}
+			p.advance() // ,
+		}
+		list.Span = span(list.Properties[0].Pos(), p.prevEnd())
+		if _, err := p.expect(token.RPAREN, `")"`); err != nil {
+			return nil, err
+		}
+		props.DerivedProperties = list
+	default:
+		if isKeyword(p.peek(), "ARE") {
+			p.advance() // ARE
+			if _, err := p.expectKeyword("ALL"); err != nil {
+				return nil, err
+			}
+		} else if !isKeyword(p.peek(), "ALL") {
+			return nil, p.errorf(p.peek().Pos, `Syntax error: Expected "(" or keyword ALL or keyword ARE but got %s`, describeToken(p.peek()))
+		} else {
+			p.advance() // ALL
+		}
+		if _, err := p.expectKeyword("COLUMNS"); err != nil {
+			return nil, err
+		}
+		if isKeyword(p.peek(), "EXCEPT") {
+			// The lexer reports EXCEPT not followed by ALL, DISTINCT or "("
+			// before the grammar sees it, and lexes EXCEPT ALL/DISTINCT as a
+			// set operation, which ends the element table.
+			if err := p.exceptClashError(); err != nil {
+				return nil, err
+			}
+		}
+		if isKeyword(p.peek(), "EXCEPT") && p.peekAt(1).Kind == token.LPAREN {
+			p.advance() // EXCEPT
+			cols, err := p.parseColumnList()
+			if err != nil {
+				return nil, err
+			}
+			props.AllExceptColumns = cols
+		}
+	}
+	props.Span = span(startTok.Pos, p.prevEnd())
+	return props, nil
+}
+
+// parseGraphDerivedProperty parses "expression [AS alias] [OPTIONS(...)]";
+// see derived_property in googlesql.tm.
+func (p *parser) parseGraphDerivedProperty() (*ast.GraphDerivedProperty, error) {
+	expr, err := p.parseExpression()
+	if err != nil {
+		return nil, err
+	}
+	prop := &ast.GraphDerivedProperty{Expression: expr}
+	if isKeyword(p.peek(), "AS") {
+		if prop.Alias, err = p.parseRequiredAsAlias(); err != nil {
+			return nil, err
+		}
+	}
+	if isKeyword(p.peek(), "OPTIONS") {
+		p.advance() // OPTIONS
+		if prop.Options, err = p.parseOptionsList(); err != nil {
+			return nil, err
+		}
+	}
+	prop.Span = span(p.extStart(expr), p.prevEnd())
+	return prop, nil
+}
+
+// parseGraphDynamicLabelAndProperties parses the optional "DYNAMIC LABEL
+// (expr)" and "DYNAMIC PROPERTIES (expr)" clauses, in either order, each at
+// most once; see dynamic_label_and_properties in googlesql.tm.
+func (p *parser) parseGraphDynamicLabelAndProperties(table *ast.GraphElementTable) error {
+	sawDynamic := false
+	for isKeyword(p.peek(), "DYNAMIC") {
+		sawDynamic = true
+		dynamicTok := p.advance() // DYNAMIC
+		kindTok := p.peek()
+		isLabel := isKeyword(kindTok, "LABEL")
+		if !isLabel && !isKeyword(kindTok, "PROPERTIES") {
+			return p.errorf(kindTok.Pos, "Syntax error: Expected keyword LABEL or keyword PROPERTIES but got %s", describeToken(kindTok))
+		}
+		p.advance() // LABEL or PROPERTIES
+		if _, err := p.expect(token.LPAREN, `"("`); err != nil {
+			return err
+		}
+		expr, err := p.parseExpression()
+		if err != nil {
+			return err
+		}
+		if _, err := p.expect(token.RPAREN, `")"`); err != nil {
+			return err
+		}
+		exprSpan := span(p.extStart(expr), p.extEnd(expr))
+		if isLabel {
+			if table.DynamicLabel != nil {
+				return p.errorf(dynamicTok.Pos, "DYNAMIC LABEL cannot be used more than once in a single element table")
+			}
+			table.DynamicLabel = &ast.GraphDynamicLabel{Span: exprSpan, Expression: expr}
+		} else {
+			if table.DynamicProperties != nil {
+				return p.errorf(dynamicTok.Pos, "DYNAMIC PROPERTIES cannot be used more than once in a single element table")
+			}
+			table.DynamicProperties = &ast.GraphDynamicProperties{Span: exprSpan, Expression: expr}
+		}
+	}
+	if !sawDynamic {
+		return nil
+	}
+	// A static clause after a dynamic one; NO and DEFAULT commit to their
+	// two-keyword forms.
+	tok := p.peek()
+	switch {
+	case isKeyword(tok, "NO") && !isKeyword(p.peekAt(1), "PROPERTIES"):
+		p.advance()
+		return p.errorf(p.peek().Pos, "Syntax error: Expected keyword PROPERTIES but got %s", describeToken(p.peek()))
+	case isKeyword(tok, "DEFAULT") && !isKeyword(p.peekAt(1), "LABEL"):
+		p.advance()
+		return p.errorf(p.peek().Pos, "Syntax error: Expected keyword LABEL but got %s", describeToken(p.peek()))
+	case p.atGraphLabel() || p.atGraphProperties():
+		return p.errorf(tok.Pos, "Syntax error: LABEL or PROPERTIES clause should be moved before DYNAMIC LABEL/PROPERTIES clause")
+	}
+	return nil
+}
diff --git a/parser/parser.go b/parser/parser.go
index bb8934c..2e7ede8 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -2814,6 +2814,11 @@ func (p *parser) parseCreateStatement() (ast.Statement, error) {
 	if isKeyword(p.peek(), "MODEL") {
 		return p.parseCreateModelStatement(createTok, scope, isOrReplace)
 	}
+	// CREATE [OR REPLACE] [scope] PROPERTY GRAPH ...; see
+	// create_property_graph_statement in googlesql.tm.
+	if isKeyword(p.peek(), "PROPERTY") {
+		return p.parseCreatePropertyGraphStatement(createTok, scope, isOrReplace)
+	}
 	// CREATE [OR REPLACE] [scope] CONSTANT [IF NOT EXISTS]  = ; see
 	// create_constant_statement in googlesql.tm.
 	if isKeyword(p.peek(), "CONSTANT") {
@@ -3867,19 +3872,28 @@ func (p *parser) parseStructColumnField() (*ast.StructColumnField, error) {
 }
 
 // tryParseColumnAttributes parses a run of column attributes trailing a column
-// schema; see column_attributes in googlesql.tm. Only NOT NULL is implemented
-// (the sole attribute exercised by the external-table tests). It returns nil
-// when no attribute is present.
+// schema; see column_attributes in googlesql.tm. NOT NULL, PRIMARY KEY and
+// HIDDEN are implemented. It returns nil when no attribute is present.
 func (p *parser) tryParseColumnAttributes() *ast.ColumnAttributeList {
 	startsAttr := func() bool {
 		return (isKeyword(p.peek(), "NOT") && isKeyword(p.peekAt(1), "NULL")) ||
-			(isKeyword(p.peek(), "PRIMARY") && isKeyword(p.peekAt(1), "KEY"))
+			(isKeyword(p.peek(), "PRIMARY") && isKeyword(p.peekAt(1), "KEY")) ||
+			isKeyword(p.peek(), "HIDDEN")
 	}
 	if !startsAttr() {
 		return nil
 	}
 	list := &ast.ColumnAttributeList{}
 	for startsAttr() {
+		if isKeyword(p.peek(), "HIDDEN") {
+			hiddenTok := p.advance() // HIDDEN
+			if len(list.Attributes) == 0 {
+				list.Start = hiddenTok.Pos
+			}
+			list.Stop = hiddenTok.End
+			list.Attributes = append(list.Attributes, &ast.HiddenColumnAttribute{Span: span(hiddenTok.Pos, hiddenTok.End)})
+			continue
+		}
 		if isKeyword(p.peek(), "PRIMARY") {
 			primaryTok := p.advance() // PRIMARY
 			keyTok := p.advance()     // KEY
@@ -6322,6 +6336,15 @@ func (p *parser) parseDropStatement() (ast.Statement, error) {
 		// the unquoted keyword ROW triggers this; a quoted `row` object type
 		// falls through to the generic handling below.
 		return p.parseDropRowAccessPolicyStatement(dropTok)
+	case isKeyword(kindTok, "SEARCH"), isKeyword(kindTok, "VECTOR"):
+		return p.parseDropIndexStatement(dropTok)
+	case isKeyword(kindTok, "PROPERTY"):
+		// DROP PROPERTY GRAPH; see schema_object_kind in googlesql.tm.
+		p.advance() // PROPERTY
+		if _, err := p.expectKeyword("GRAPH"); err != nil {
+			return nil, err
+		}
+		objectKind = "PROPERTY GRAPH"
 	case isKeyword(kindTok, "ALL"):
 		// DROP ALL ROW [ACCESS] POLICIES ON ...; see
 		// drop_all_row_access_policies_statement in googlesql.tm.
@@ -6499,6 +6522,151 @@ func (p *parser) parseDropRowAccessPolicyStatement(dropTok token.Token) (ast.Sta
 	}, nil
 }
 
+// parseDropIndexStatement parses "DROP {SEARCH|VECTOR} INDEX [IF EXISTS]
+// path [ON path]"; see the "DROP" index_type "INDEX" production of
+// drop_statement in googlesql.tm. The DROP keyword is already consumed; the
+// parser is at SEARCH or VECTOR.
+func (p *parser) parseDropIndexStatement(dropTok token.Token) (ast.Statement, error) {
+	kindTok := p.advance() // SEARCH or VECTOR
+	if _, err := p.expectKeyword("INDEX"); err != nil {
+		return nil, err
+	}
+	stmt := &ast.DropIndexStatement{Vector: isKeyword(kindTok, "VECTOR"), IsIfExists: p.tryParseIfExists()}
+	name, err := p.parsePathExpression()
+	if err != nil {
+		return nil, err
+	}
+	stmt.Name = name
+	end := name.End()
+	if isKeyword(p.peek(), "ON") {
+		p.advance() // ON
+		table, err := p.parsePathExpression()
+		if err != nil {
+			return nil, err
+		}
+		stmt.Table = table
+		end = table.End()
+	}
+	stmt.Span = span(dropTok.Pos, end)
+	return stmt, nil
+}
+
+// parseAlterIndexStatement parses "ALTER {SEARCH|VECTOR} INDEX [IF EXISTS]
+// path [ON path] alter_index_action, ..."; see the "ALTER" alter_index_type
+// "INDEX" production of alter_statement and alter_index_action in
+// googlesql.tm. The ALTER keyword is already consumed; the parser is at
+// SEARCH or VECTOR.
+func (p *parser) parseAlterIndexStatement(alterTok token.Token) (ast.Statement, error) {
+	kindTok := p.advance() // SEARCH or VECTOR
+	if _, err := p.expectKeyword("INDEX"); err != nil {
+		return nil, err
+	}
+	stmt := &ast.AlterIndexStatement{Vector: isKeyword(kindTok, "VECTOR"), IsIfExists: p.tryParseIfExists()}
+	var err error
+	if stmt.Name, err = p.parsePathExpression(); err != nil {
+		return nil, err
+	}
+	if isKeyword(p.peek(), "ON") {
+		p.advance() // ON
+		if stmt.Table, err = p.parsePathExpression(); err != nil {
+			return nil, err
+		}
+	}
+	list := &ast.AlterActionList{}
+	for {
+		action, err := p.parseAlterIndexAction()
+		if err != nil {
+			return nil, err
+		}
+		list.Actions = append(list.Actions, action)
+		if p.peek().Kind != token.COMMA {
+			break
+		}
+		p.advance() // ,
+	}
+	list.Span = span(list.Actions[0].Pos(), p.prevEnd())
+	stmt.Actions = list
+	stmt.Span = span(alterTok.Pos, p.prevEnd())
+	return stmt, nil
+}
+
+// parseAlterIndexAction parses one alter_index_action: SET OPTIONS (...),
+// ADD COLUMN [IF NOT EXISTS] c [OPTIONS(...)], DROP COLUMN [IF EXISTS] c,
+// ALTER COLUMN [IF EXISTS] c SET OPTIONS (...), or REBUILD.
+func (p *parser) parseAlterIndexAction() (ast.Node, error) {
+	tok := p.peek()
+	switch {
+	case isKeyword(tok, "REBUILD"):
+		p.advance()
+		return &ast.RebuildAction{Span: span(tok.Pos, tok.End)}, nil
+	case isKeyword(tok, "SET"):
+		p.advance() // SET
+		if _, err := p.expectKeyword("OPTIONS"); err != nil {
+			return nil, err
+		}
+		opts, err := p.parseOptionsList()
+		if err != nil {
+			return nil, err
+		}
+		return &ast.SetOptionsAction{Span: span(tok.Pos, opts.End()), Options: opts}, nil
+	case isKeyword(tok, "ADD"):
+		p.advance() // ADD
+		if _, err := p.expectKeyword("COLUMN"); err != nil {
+			return nil, err
+		}
+		ifNotExists, err := p.parseOptIfNotExists()
+		if err != nil {
+			return nil, err
+		}
+		col, err := p.parseIdentifier()
+		if err != nil {
+			return nil, err
+		}
+		action := &ast.AddColumnIdentifierAction{IsIfNotExists: ifNotExists, Column: col}
+		if isKeyword(p.peek(), "OPTIONS") {
+			p.advance() // OPTIONS
+			if action.Options, err = p.parseOptionsList(); err != nil {
+				return nil, err
+			}
+		}
+		action.Span = span(tok.Pos, p.prevEnd())
+		return action, nil
+	case isKeyword(tok, "DROP"):
+		p.advance() // DROP
+		if _, err := p.expectKeyword("COLUMN"); err != nil {
+			return nil, err
+		}
+		ifExists := p.tryParseIfExists()
+		col, err := p.parseIdentifier()
+		if err != nil {
+			return nil, err
+		}
+		return &ast.DropColumnAction{Span: span(tok.Pos, col.End()), IsIfExists: ifExists, Name: col}, nil
+	case isKeyword(tok, "ALTER"):
+		p.advance() // ALTER
+		if _, err := p.expectKeyword("COLUMN"); err != nil {
+			return nil, err
+		}
+		ifExists := p.tryParseIfExists()
+		col, err := p.parseIdentifier()
+		if err != nil {
+			return nil, err
+		}
+		if _, err := p.expectKeyword("SET"); err != nil {
+			return nil, err
+		}
+		if _, err := p.expectKeyword("OPTIONS"); err != nil {
+			return nil, err
+		}
+		opts, err := p.parseOptionsList()
+		if err != nil {
+			return nil, err
+		}
+		return &ast.AlterColumnOptionsAction{Span: span(tok.Pos, opts.End()), IsIfExists: ifExists, Column: col, Options: opts}, nil
+	}
+	return nil, p.errorf(tok.Pos, "Syntax error: Unexpected %s", describeToken(tok))
+}
+
 // parseDropAllRowAccessPoliciesStatement parses
 // "DROP ALL ROW [ACCESS] POLICIES ON path"; see
 // drop_all_row_access_policies_statement in googlesql.tm. The DROP keyword is
@@ -6827,6 +6995,8 @@ func (p *parser) parseAlterStatement() (ast.Statement, error) {
 	case isKeyword(kindTok, "FUNCTION"):
 		p.advance()
 		unsupported = "FUNCTION"
+	case isKeyword(kindTok, "SEARCH"), isKeyword(kindTok, "VECTOR"):
+		return p.parseAlterIndexStatement(alterTok)
 	case isKeyword(kindTok, "INDEX"):
 		p.advance()
 		unsupported = "INDEX"
diff --git a/parser/parser_test.go b/parser/parser_test.go
index dd12140..683d1f1 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -171,9 +171,17 @@ func TestParser(t *testing.T) {
 	if len(files) == 0 {
 		t.Fatal("no .test files found in testdata")
 	}
+	// Golden cases generated from Spanner's docs (see cmd/spanner-corpus),
+	// run as subtests named "spanner/" and "spanner/guides/".
+	spanner, err := testfile.FindFiles(filepath.Join("testdata", "spanner"))
+	if err != nil {
+		t.Fatal(err)
+	}
+	files = append(files, spanner...)
 
 	for _, path := range files {
-		t.Run(strings.TrimSuffix(filepath.Base(path), ".test"), func(t *testing.T) {
+		name, _ := filepath.Rel("testdata", path)
+		t.Run(filepath.ToSlash(strings.TrimSuffix(name, ".test")), func(t *testing.T) {
 			t.Parallel()
 			testFile(t, path)
 		})
diff --git a/parser/testdata/spanner/aggregate-function-calls.test b/parser/testdata/spanner/aggregate-function-calls.test
new file mode 100644
index 0000000..f59de0c
--- /dev/null
+++ b/parser/testdata/spanner/aggregate-function-calls.test
@@ -0,0 +1,595 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate-function-calls
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate-function-calls#having_max
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+WITH
+  Precipitation AS (
+    SELECT 2009 AS year, 'spring' AS season, 3 AS inches
+    UNION ALL
+    SELECT 2001, 'winter', 4
+    UNION ALL
+    SELECT 2003, 'fall', 1
+    UNION ALL
+    SELECT 2002, 'spring', 4
+    UNION ALL
+    SELECT 2005, 'summer', 1
+  )
+SELECT ANY_VALUE(year HAVING MAX inches) AS any_year_with_max_inches FROM Precipitation
+--
+QueryStatement [0-344]
+  Query [0-344]
+    WithClause [0-256]
+      WithClauseEntry [7-256]
+        AliasedQuery [7-256]
+          Identifier(Precipitation) [7-20]
+          Query [24-256]
+            SetOperation(UNION ALL) [30-252]
+              SetOperationMetadataList [87-223]
+                SetOperationMetadata [87-96]
+                  SetOperationType [87-92]
+                  SetOperationAllOrDistinct [93-96]
+                SetOperationMetadata [130-139]
+                  SetOperationType [130-135]
+                  SetOperationAllOrDistinct [136-139]
+                SetOperationMetadata [171-180]
+                  SetOperationType [171-176]
+                  SetOperationAllOrDistinct [177-180]
+                SetOperationMetadata [214-223]
+                  SetOperationType [214-219]
+                  SetOperationAllOrDistinct [220-223]
+              Select [30-82]
+                SelectList [37-82]
+                  SelectColumn [37-49]
+                    IntLiteral(2009) [37-41]
+                    Alias [42-49]
+                      Identifier(year) [45-49]
+                  SelectColumn [51-69]
+                    StringLiteral [51-59]
+                      StringLiteralComponent('spring') [51-59]
+                    Alias [60-69]
+                      Identifier(season) [63-69]
+                  SelectColumn [71-82]
+                    IntLiteral(3) [71-72]
+                    Alias [73-82]
+                      Identifier(inches) [76-82]
+              Select [101-125]
+                SelectList [108-125]
+                  SelectColumn [108-112]
+                    IntLiteral(2001) [108-112]
+                  SelectColumn [114-122]
+                    StringLiteral [114-122]
+                      StringLiteralComponent('winter') [114-122]
+                  SelectColumn [124-125]
+                    IntLiteral(4) [124-125]
+              Select [144-166]
+                SelectList [151-166]
+                  SelectColumn [151-155]
+                    IntLiteral(2003) [151-155]
+                  SelectColumn [157-163]
+                    StringLiteral [157-163]
+                      StringLiteralComponent('fall') [157-163]
+                  SelectColumn [165-166]
+                    IntLiteral(1) [165-166]
+              Select [185-209]
+                SelectList [192-209]
+                  SelectColumn [192-196]
+                    IntLiteral(2002) [192-196]
+                  SelectColumn [198-206]
+                    StringLiteral [198-206]
+                      StringLiteralComponent('spring') [198-206]
+                  SelectColumn [208-209]
+                    IntLiteral(4) [208-209]
+              Select [228-252]
+                SelectList [235-252]
+                  SelectColumn [235-239]
+                    IntLiteral(2005) [235-239]
+                  SelectColumn [241-249]
+                    StringLiteral [241-249]
+                      StringLiteralComponent('summer') [241-249]
+                  SelectColumn [251-252]
+                    IntLiteral(1) [251-252]
+    Select [257-344]
+      SelectList [264-325]
+        SelectColumn [264-325]
+          FunctionCall [264-297]
+            PathExpression [264-273]
+              Identifier(ANY_VALUE) [264-273]
+            PathExpression [274-278]
+              Identifier(year) [274-278]
+            HavingModifier [279-296]
+              PathExpression [290-296]
+                Identifier(inches) [290-296]
+          Alias [298-325]
+            Identifier(any_year_with_max_inches) [301-325]
+      FromClause [326-344]
+        TablePathExpression [331-344]
+          PathExpression [331-344]
+            Identifier(Precipitation) [331-344]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate-function-calls#having_max
+WITH
+  Precipitation AS (
+    SELECT 2001 AS year, 'spring' AS season, 9 AS inches
+    UNION ALL
+    SELECT 2001, 'winter', 1
+    UNION ALL
+    SELECT 2000, 'fall', 3
+    UNION ALL
+    SELECT 2000, 'summer', 5
+    UNION ALL
+    SELECT 2000, 'spring', 7
+    UNION ALL
+    SELECT 2000, 'winter', 2
+  )
+SELECT AVG(inches HAVING MAX year) AS average FROM Precipitation
+--
+QueryStatement [0-364]
+  Query [0-364]
+    WithClause [0-299]
+      WithClauseEntry [7-299]
+        AliasedQuery [7-299]
+          Identifier(Precipitation) [7-20]
+          Query [24-299]
+            SetOperation(UNION ALL) [30-295]
+              SetOperationMetadataList [87-266]
+                SetOperationMetadata [87-96]
+                  SetOperationType [87-92]
+                  SetOperationAllOrDistinct [93-96]
+                SetOperationMetadata [130-139]
+                  SetOperationType [130-135]
+                  SetOperationAllOrDistinct [136-139]
+                SetOperationMetadata [171-180]
+                  SetOperationType [171-176]
+                  SetOperationAllOrDistinct [177-180]
+                SetOperationMetadata [214-223]
+                  SetOperationType [214-219]
+                  SetOperationAllOrDistinct [220-223]
+                SetOperationMetadata [257-266]
+                  SetOperationType [257-262]
+                  SetOperationAllOrDistinct [263-266]
+              Select [30-82]
+                SelectList [37-82]
+                  SelectColumn [37-49]
+                    IntLiteral(2001) [37-41]
+                    Alias [42-49]
+                      Identifier(year) [45-49]
+                  SelectColumn [51-69]
+                    StringLiteral [51-59]
+                      StringLiteralComponent('spring') [51-59]
+                    Alias [60-69]
+                      Identifier(season) [63-69]
+                  SelectColumn [71-82]
+                    IntLiteral(9) [71-72]
+                    Alias [73-82]
+                      Identifier(inches) [76-82]
+              Select [101-125]
+                SelectList [108-125]
+                  SelectColumn [108-112]
+                    IntLiteral(2001) [108-112]
+                  SelectColumn [114-122]
+                    StringLiteral [114-122]
+                      StringLiteralComponent('winter') [114-122]
+                  SelectColumn [124-125]
+                    IntLiteral(1) [124-125]
+              Select [144-166]
+                SelectList [151-166]
+                  SelectColumn [151-155]
+                    IntLiteral(2000) [151-155]
+                  SelectColumn [157-163]
+                    StringLiteral [157-163]
+                      StringLiteralComponent('fall') [157-163]
+                  SelectColumn [165-166]
+                    IntLiteral(3) [165-166]
+              Select [185-209]
+                SelectList [192-209]
+                  SelectColumn [192-196]
+                    IntLiteral(2000) [192-196]
+                  SelectColumn [198-206]
+                    StringLiteral [198-206]
+                      StringLiteralComponent('summer') [198-206]
+                  SelectColumn [208-209]
+                    IntLiteral(5) [208-209]
+              Select [228-252]
+                SelectList [235-252]
+                  SelectColumn [235-239]
+                    IntLiteral(2000) [235-239]
+                  SelectColumn [241-249]
+                    StringLiteral [241-249]
+                      StringLiteralComponent('spring') [241-249]
+                  SelectColumn [251-252]
+                    IntLiteral(7) [251-252]
+              Select [271-295]
+                SelectList [278-295]
+                  SelectColumn [278-282]
+                    IntLiteral(2000) [278-282]
+                  SelectColumn [284-292]
+                    StringLiteral [284-292]
+                      StringLiteralComponent('winter') [284-292]
+                  SelectColumn [294-295]
+                    IntLiteral(2) [294-295]
+    Select [300-364]
+      SelectList [307-345]
+        SelectColumn [307-345]
+          FunctionCall [307-334]
+            PathExpression [307-310]
+              Identifier(AVG) [307-310]
+            PathExpression [311-317]
+              Identifier(inches) [311-317]
+            HavingModifier [318-333]
+              PathExpression [329-333]
+                Identifier(year) [329-333]
+          Alias [335-345]
+            Identifier(average) [338-345]
+      FromClause [346-364]
+        TablePathExpression [351-364]
+          PathExpression [351-364]
+            Identifier(Precipitation) [351-364]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate-function-calls#having_min
+WITH
+  Precipitation AS (
+    SELECT 2009 AS year, 'spring' AS season, 3 AS inches
+    UNION ALL
+    SELECT 2001, 'winter', 4
+    UNION ALL
+    SELECT 2003, 'fall', 1
+    UNION ALL
+    SELECT 2002, 'spring', 4
+    UNION ALL
+    SELECT 2005, 'summer', 1
+  )
+SELECT ANY_VALUE(year HAVING MIN inches) AS any_year_with_min_inches FROM Precipitation
+--
+QueryStatement [0-344]
+  Query [0-344]
+    WithClause [0-256]
+      WithClauseEntry [7-256]
+        AliasedQuery [7-256]
+          Identifier(Precipitation) [7-20]
+          Query [24-256]
+            SetOperation(UNION ALL) [30-252]
+              SetOperationMetadataList [87-223]
+                SetOperationMetadata [87-96]
+                  SetOperationType [87-92]
+                  SetOperationAllOrDistinct [93-96]
+                SetOperationMetadata [130-139]
+                  SetOperationType [130-135]
+                  SetOperationAllOrDistinct [136-139]
+                SetOperationMetadata [171-180]
+                  SetOperationType [171-176]
+                  SetOperationAllOrDistinct [177-180]
+                SetOperationMetadata [214-223]
+                  SetOperationType [214-219]
+                  SetOperationAllOrDistinct [220-223]
+              Select [30-82]
+                SelectList [37-82]
+                  SelectColumn [37-49]
+                    IntLiteral(2009) [37-41]
+                    Alias [42-49]
+                      Identifier(year) [45-49]
+                  SelectColumn [51-69]
+                    StringLiteral [51-59]
+                      StringLiteralComponent('spring') [51-59]
+                    Alias [60-69]
+                      Identifier(season) [63-69]
+                  SelectColumn [71-82]
+                    IntLiteral(3) [71-72]
+                    Alias [73-82]
+                      Identifier(inches) [76-82]
+              Select [101-125]
+                SelectList [108-125]
+                  SelectColumn [108-112]
+                    IntLiteral(2001) [108-112]
+                  SelectColumn [114-122]
+                    StringLiteral [114-122]
+                      StringLiteralComponent('winter') [114-122]
+                  SelectColumn [124-125]
+                    IntLiteral(4) [124-125]
+              Select [144-166]
+                SelectList [151-166]
+                  SelectColumn [151-155]
+                    IntLiteral(2003) [151-155]
+                  SelectColumn [157-163]
+                    StringLiteral [157-163]
+                      StringLiteralComponent('fall') [157-163]
+                  SelectColumn [165-166]
+                    IntLiteral(1) [165-166]
+              Select [185-209]
+                SelectList [192-209]
+                  SelectColumn [192-196]
+                    IntLiteral(2002) [192-196]
+                  SelectColumn [198-206]
+                    StringLiteral [198-206]
+                      StringLiteralComponent('spring') [198-206]
+                  SelectColumn [208-209]
+                    IntLiteral(4) [208-209]
+              Select [228-252]
+                SelectList [235-252]
+                  SelectColumn [235-239]
+                    IntLiteral(2005) [235-239]
+                  SelectColumn [241-249]
+                    StringLiteral [241-249]
+                      StringLiteralComponent('summer') [241-249]
+                  SelectColumn [251-252]
+                    IntLiteral(1) [251-252]
+    Select [257-344]
+      SelectList [264-325]
+        SelectColumn [264-325]
+          FunctionCall [264-297]
+            PathExpression [264-273]
+              Identifier(ANY_VALUE) [264-273]
+            PathExpression [274-278]
+              Identifier(year) [274-278]
+            HavingModifier [279-296]
+              PathExpression [290-296]
+                Identifier(inches) [290-296]
+          Alias [298-325]
+            Identifier(any_year_with_min_inches) [301-325]
+      FromClause [326-344]
+        TablePathExpression [331-344]
+          PathExpression [331-344]
+            Identifier(Precipitation) [331-344]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate-function-calls#having_min
+WITH
+  Precipitation AS (
+    SELECT 2001 AS year, 'spring' AS season, 9 AS inches
+    UNION ALL
+    SELECT 2001, 'winter', 1
+    UNION ALL
+    SELECT 2000, 'fall', 3
+    UNION ALL
+    SELECT 2000, 'summer', 5
+    UNION ALL
+    SELECT 2000, 'spring', 7
+    UNION ALL
+    SELECT 2000, 'winter', 2
+  )
+SELECT AVG(inches HAVING MIN year) AS average FROM Precipitation
+--
+QueryStatement [0-364]
+  Query [0-364]
+    WithClause [0-299]
+      WithClauseEntry [7-299]
+        AliasedQuery [7-299]
+          Identifier(Precipitation) [7-20]
+          Query [24-299]
+            SetOperation(UNION ALL) [30-295]
+              SetOperationMetadataList [87-266]
+                SetOperationMetadata [87-96]
+                  SetOperationType [87-92]
+                  SetOperationAllOrDistinct [93-96]
+                SetOperationMetadata [130-139]
+                  SetOperationType [130-135]
+                  SetOperationAllOrDistinct [136-139]
+                SetOperationMetadata [171-180]
+                  SetOperationType [171-176]
+                  SetOperationAllOrDistinct [177-180]
+                SetOperationMetadata [214-223]
+                  SetOperationType [214-219]
+                  SetOperationAllOrDistinct [220-223]
+                SetOperationMetadata [257-266]
+                  SetOperationType [257-262]
+                  SetOperationAllOrDistinct [263-266]
+              Select [30-82]
+                SelectList [37-82]
+                  SelectColumn [37-49]
+                    IntLiteral(2001) [37-41]
+                    Alias [42-49]
+                      Identifier(year) [45-49]
+                  SelectColumn [51-69]
+                    StringLiteral [51-59]
+                      StringLiteralComponent('spring') [51-59]
+                    Alias [60-69]
+                      Identifier(season) [63-69]
+                  SelectColumn [71-82]
+                    IntLiteral(9) [71-72]
+                    Alias [73-82]
+                      Identifier(inches) [76-82]
+              Select [101-125]
+                SelectList [108-125]
+                  SelectColumn [108-112]
+                    IntLiteral(2001) [108-112]
+                  SelectColumn [114-122]
+                    StringLiteral [114-122]
+                      StringLiteralComponent('winter') [114-122]
+                  SelectColumn [124-125]
+                    IntLiteral(1) [124-125]
+              Select [144-166]
+                SelectList [151-166]
+                  SelectColumn [151-155]
+                    IntLiteral(2000) [151-155]
+                  SelectColumn [157-163]
+                    StringLiteral [157-163]
+                      StringLiteralComponent('fall') [157-163]
+                  SelectColumn [165-166]
+                    IntLiteral(3) [165-166]
+              Select [185-209]
+                SelectList [192-209]
+                  SelectColumn [192-196]
+                    IntLiteral(2000) [192-196]
+                  SelectColumn [198-206]
+                    StringLiteral [198-206]
+                      StringLiteralComponent('summer') [198-206]
+                  SelectColumn [208-209]
+                    IntLiteral(5) [208-209]
+              Select [228-252]
+                SelectList [235-252]
+                  SelectColumn [235-239]
+                    IntLiteral(2000) [235-239]
+                  SelectColumn [241-249]
+                    StringLiteral [241-249]
+                      StringLiteralComponent('spring') [241-249]
+                  SelectColumn [251-252]
+                    IntLiteral(7) [251-252]
+              Select [271-295]
+                SelectList [278-295]
+                  SelectColumn [278-282]
+                    IntLiteral(2000) [278-282]
+                  SelectColumn [284-292]
+                    StringLiteral [284-292]
+                      StringLiteralComponent('winter') [284-292]
+                  SelectColumn [294-295]
+                    IntLiteral(2) [294-295]
+    Select [300-364]
+      SelectList [307-345]
+        SelectColumn [307-345]
+          FunctionCall [307-334]
+            PathExpression [307-310]
+              Identifier(AVG) [307-310]
+            PathExpression [311-317]
+              Identifier(inches) [311-317]
+            HavingModifier [318-333]
+              PathExpression [329-333]
+                Identifier(year) [329-333]
+          Alias [335-345]
+            Identifier(average) [338-345]
+      FromClause [346-364]
+        TablePathExpression [351-364]
+          PathExpression [351-364]
+            Identifier(Precipitation) [351-364]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate-function-calls#remove_nulls
+SELECT
+  COUNT(*) AS row_count,
+  COUNT(x) AS non_null_rows,
+  AVG(x) AS avg_x
+FROM UNNEST([1, 2, 3, 4, 5, NULL]) AS x
+--
+QueryStatement [0-118]
+  Query [0-118]
+    Select [0-118]
+      SelectList [9-78]
+        SelectColumn [9-30]
+          FunctionCall [9-17]
+            PathExpression [9-14]
+              Identifier(COUNT) [9-14]
+            Star(*) [15-16]
+          Alias [18-30]
+            Identifier(row_count) [21-30]
+        SelectColumn [34-59]
+          FunctionCall [34-42]
+            PathExpression [34-39]
+              Identifier(COUNT) [34-39]
+            PathExpression [40-41]
+              Identifier(x) [40-41]
+          Alias [43-59]
+            Identifier(non_null_rows) [46-59]
+        SelectColumn [63-78]
+          FunctionCall [63-69]
+            PathExpression [63-66]
+              Identifier(AVG) [63-66]
+            PathExpression [67-68]
+              Identifier(x) [67-68]
+          Alias [70-78]
+            Identifier(avg_x) [73-78]
+      FromClause [79-118]
+        TablePathExpression [84-118]
+          UnnestExpression [84-113]
+            ExpressionWithOptAlias [91-112]
+              ArrayConstructor [91-112]
+                IntLiteral(1) [92-93]
+                IntLiteral(2) [95-96]
+                IntLiteral(3) [98-99]
+                IntLiteral(4) [101-102]
+                IntLiteral(5) [104-105]
+                NullLiteral(NULL) [107-111]
+          Alias [114-118]
+            Identifier(x) [117-118]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate-function-calls#aggregate_function_examples
+SELECT
+  COUNT(*) AS total_count,
+  COUNT(fruit) AS non_null_count,
+  MIN(fruit) AS min,
+  MAX(fruit) AS max
+FROM
+  (
+    SELECT NULL AS fruit
+    UNION ALL
+    SELECT 'apple' AS fruit
+    UNION ALL
+    SELECT 'pear' AS fruit
+    UNION ALL
+    SELECT 'orange' AS fruit
+  )
+--
+QueryStatement [0-272]
+  Query [0-272]
+    Select [0-272]
+      SelectList [9-108]
+        SelectColumn [9-32]
+          FunctionCall [9-17]
+            PathExpression [9-14]
+              Identifier(COUNT) [9-14]
+            Star(*) [15-16]
+          Alias [18-32]
+            Identifier(total_count) [21-32]
+        SelectColumn [36-66]
+          FunctionCall [36-48]
+            PathExpression [36-41]
+              Identifier(COUNT) [36-41]
+            PathExpression [42-47]
+              Identifier(fruit) [42-47]
+          Alias [49-66]
+            Identifier(non_null_count) [52-66]
+        SelectColumn [70-87]
+          FunctionCall [70-80]
+            PathExpression [70-73]
+              Identifier(MIN) [70-73]
+            PathExpression [74-79]
+              Identifier(fruit) [74-79]
+          Alias [81-87]
+            Identifier(min) [84-87]
+        SelectColumn [91-108]
+          FunctionCall [91-101]
+            PathExpression [91-94]
+              Identifier(MAX) [91-94]
+            PathExpression [95-100]
+              Identifier(fruit) [95-100]
+          Alias [102-108]
+            Identifier(max) [105-108]
+      FromClause [109-272]
+        TableSubquery [116-272]
+          Query [122-268]
+            SetOperation(UNION ALL) [122-268]
+              SetOperationMetadataList [147-239]
+                SetOperationMetadata [147-156]
+                  SetOperationType [147-152]
+                  SetOperationAllOrDistinct [153-156]
+                SetOperationMetadata [189-198]
+                  SetOperationType [189-194]
+                  SetOperationAllOrDistinct [195-198]
+                SetOperationMetadata [230-239]
+                  SetOperationType [230-235]
+                  SetOperationAllOrDistinct [236-239]
+              Select [122-142]
+                SelectList [129-142]
+                  SelectColumn [129-142]
+                    NullLiteral(NULL) [129-133]
+                    Alias [134-142]
+                      Identifier(fruit) [137-142]
+              Select [161-184]
+                SelectList [168-184]
+                  SelectColumn [168-184]
+                    StringLiteral [168-175]
+                      StringLiteralComponent('apple') [168-175]
+                    Alias [176-184]
+                      Identifier(fruit) [179-184]
+              Select [203-225]
+                SelectList [210-225]
+                  SelectColumn [210-225]
+                    StringLiteral [210-216]
+                      StringLiteralComponent('pear') [210-216]
+                    Alias [217-225]
+                      Identifier(fruit) [220-225]
+              Select [244-268]
+                SelectList [251-268]
+                  SelectColumn [251-268]
+                    StringLiteral [251-259]
+                      StringLiteralComponent('orange') [251-259]
+                    Alias [260-268]
+                      Identifier(fruit) [263-268]
diff --git a/parser/testdata/spanner/aggregate_functions.test b/parser/testdata/spanner/aggregate_functions.test
new file mode 100644
index 0000000..dc81c8d
--- /dev/null
+++ b/parser/testdata/spanner/aggregate_functions.test
@@ -0,0 +1,1886 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#any_value
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT ANY_VALUE(fruit) as any_value
+FROM UNNEST(["apple", "banana", "pear"]) as fruit
+--
+QueryStatement [0-86]
+  Query [0-86]
+    Select [0-86]
+      SelectList [7-36]
+        SelectColumn [7-36]
+          FunctionCall [7-23]
+            PathExpression [7-16]
+              Identifier(ANY_VALUE) [7-16]
+            PathExpression [17-22]
+              Identifier(fruit) [17-22]
+          Alias [24-36]
+            Identifier(any_value) [27-36]
+      FromClause [37-86]
+        TablePathExpression [42-86]
+          UnnestExpression [42-77]
+            ExpressionWithOptAlias [49-76]
+              ArrayConstructor [49-76]
+                StringLiteral [50-57]
+                  StringLiteralComponent("apple") [50-57]
+                StringLiteral [59-67]
+                  StringLiteralComponent("banana") [59-67]
+                StringLiteral [69-75]
+                  StringLiteralComponent("pear") [69-75]
+          Alias [78-86]
+            Identifier(fruit) [81-86]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#any_value
+WITH
+  Store AS (
+    SELECT 20 AS sold, "apples" AS fruit
+    UNION ALL
+    SELECT 30 AS sold, "pears" AS fruit
+    UNION ALL
+    SELECT 30 AS sold, "bananas" AS fruit
+    UNION ALL
+    SELECT 10 AS sold, "oranges" AS fruit
+  )
+SELECT ANY_VALUE(fruit HAVING MAX sold) AS a_highest_selling_fruit FROM Store
+--
+QueryStatement [0-306]
+  Query [0-306]
+    WithClause [0-228]
+      WithClauseEntry [7-228]
+        AliasedQuery [7-228]
+          Identifier(Store) [7-12]
+          Query [16-228]
+            SetOperation(UNION ALL) [22-224]
+              SetOperationMetadataList [63-182]
+                SetOperationMetadata [63-72]
+                  SetOperationType [63-68]
+                  SetOperationAllOrDistinct [69-72]
+                SetOperationMetadata [117-126]
+                  SetOperationType [117-122]
+                  SetOperationAllOrDistinct [123-126]
+                SetOperationMetadata [173-182]
+                  SetOperationType [173-178]
+                  SetOperationAllOrDistinct [179-182]
+              Select [22-58]
+                SelectList [29-58]
+                  SelectColumn [29-39]
+                    IntLiteral(20) [29-31]
+                    Alias [32-39]
+                      Identifier(sold) [35-39]
+                  SelectColumn [41-58]
+                    StringLiteral [41-49]
+                      StringLiteralComponent("apples") [41-49]
+                    Alias [50-58]
+                      Identifier(fruit) [53-58]
+              Select [77-112]
+                SelectList [84-112]
+                  SelectColumn [84-94]
+                    IntLiteral(30) [84-86]
+                    Alias [87-94]
+                      Identifier(sold) [90-94]
+                  SelectColumn [96-112]
+                    StringLiteral [96-103]
+                      StringLiteralComponent("pears") [96-103]
+                    Alias [104-112]
+                      Identifier(fruit) [107-112]
+              Select [131-168]
+                SelectList [138-168]
+                  SelectColumn [138-148]
+                    IntLiteral(30) [138-140]
+                    Alias [141-148]
+                      Identifier(sold) [144-148]
+                  SelectColumn [150-168]
+                    StringLiteral [150-159]
+                      StringLiteralComponent("bananas") [150-159]
+                    Alias [160-168]
+                      Identifier(fruit) [163-168]
+              Select [187-224]
+                SelectList [194-224]
+                  SelectColumn [194-204]
+                    IntLiteral(10) [194-196]
+                    Alias [197-204]
+                      Identifier(sold) [200-204]
+                  SelectColumn [206-224]
+                    StringLiteral [206-215]
+                      StringLiteralComponent("oranges") [206-215]
+                    Alias [216-224]
+                      Identifier(fruit) [219-224]
+    Select [229-306]
+      SelectList [236-295]
+        SelectColumn [236-295]
+          FunctionCall [236-268]
+            PathExpression [236-245]
+              Identifier(ANY_VALUE) [236-245]
+            PathExpression [246-251]
+              Identifier(fruit) [246-251]
+            HavingModifier [252-267]
+              PathExpression [263-267]
+                Identifier(sold) [263-267]
+          Alias [269-295]
+            Identifier(a_highest_selling_fruit) [272-295]
+      FromClause [296-306]
+        TablePathExpression [301-306]
+          PathExpression [301-306]
+            Identifier(Store) [301-306]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#any_value
+WITH
+  Store AS (
+    SELECT 20 AS sold, "apples" AS fruit
+    UNION ALL
+    SELECT 30 AS sold, "pears" AS fruit
+    UNION ALL
+    SELECT 30 AS sold, "bananas" AS fruit
+    UNION ALL
+    SELECT 10 AS sold, "oranges" AS fruit
+  )
+SELECT ANY_VALUE(fruit HAVING MIN sold) AS a_lowest_selling_fruit FROM Store
+--
+QueryStatement [0-305]
+  Query [0-305]
+    WithClause [0-228]
+      WithClauseEntry [7-228]
+        AliasedQuery [7-228]
+          Identifier(Store) [7-12]
+          Query [16-228]
+            SetOperation(UNION ALL) [22-224]
+              SetOperationMetadataList [63-182]
+                SetOperationMetadata [63-72]
+                  SetOperationType [63-68]
+                  SetOperationAllOrDistinct [69-72]
+                SetOperationMetadata [117-126]
+                  SetOperationType [117-122]
+                  SetOperationAllOrDistinct [123-126]
+                SetOperationMetadata [173-182]
+                  SetOperationType [173-178]
+                  SetOperationAllOrDistinct [179-182]
+              Select [22-58]
+                SelectList [29-58]
+                  SelectColumn [29-39]
+                    IntLiteral(20) [29-31]
+                    Alias [32-39]
+                      Identifier(sold) [35-39]
+                  SelectColumn [41-58]
+                    StringLiteral [41-49]
+                      StringLiteralComponent("apples") [41-49]
+                    Alias [50-58]
+                      Identifier(fruit) [53-58]
+              Select [77-112]
+                SelectList [84-112]
+                  SelectColumn [84-94]
+                    IntLiteral(30) [84-86]
+                    Alias [87-94]
+                      Identifier(sold) [90-94]
+                  SelectColumn [96-112]
+                    StringLiteral [96-103]
+                      StringLiteralComponent("pears") [96-103]
+                    Alias [104-112]
+                      Identifier(fruit) [107-112]
+              Select [131-168]
+                SelectList [138-168]
+                  SelectColumn [138-148]
+                    IntLiteral(30) [138-140]
+                    Alias [141-148]
+                      Identifier(sold) [144-148]
+                  SelectColumn [150-168]
+                    StringLiteral [150-159]
+                      StringLiteralComponent("bananas") [150-159]
+                    Alias [160-168]
+                      Identifier(fruit) [163-168]
+              Select [187-224]
+                SelectList [194-224]
+                  SelectColumn [194-204]
+                    IntLiteral(10) [194-196]
+                    Alias [197-204]
+                      Identifier(sold) [200-204]
+                  SelectColumn [206-224]
+                    StringLiteral [206-215]
+                      StringLiteralComponent("oranges") [206-215]
+                    Alias [216-224]
+                      Identifier(fruit) [219-224]
+    Select [229-305]
+      SelectList [236-294]
+        SelectColumn [236-294]
+          FunctionCall [236-268]
+            PathExpression [236-245]
+              Identifier(ANY_VALUE) [236-245]
+            PathExpression [246-251]
+              Identifier(fruit) [246-251]
+            HavingModifier [252-267]
+              PathExpression [263-267]
+                Identifier(sold) [263-267]
+          Alias [269-294]
+            Identifier(a_lowest_selling_fruit) [272-294]
+      FromClause [295-305]
+        TablePathExpression [300-305]
+          PathExpression [300-305]
+            Identifier(Store) [300-305]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#array_agg
+SELECT ARRAY_AGG(x) AS array_agg FROM UNNEST([2, 1,-2, 3, -2, 1, 2]) AS x
+--
+QueryStatement [0-73]
+  Query [0-73]
+    Select [0-73]
+      SelectList [7-32]
+        SelectColumn [7-32]
+          FunctionCall [7-19]
+            PathExpression [7-16]
+              Identifier(ARRAY_AGG) [7-16]
+            PathExpression [17-18]
+              Identifier(x) [17-18]
+          Alias [20-32]
+            Identifier(array_agg) [23-32]
+      FromClause [33-73]
+        TablePathExpression [38-73]
+          UnnestExpression [38-68]
+            ExpressionWithOptAlias [45-67]
+              ArrayConstructor [45-67]
+                IntLiteral(2) [46-47]
+                IntLiteral(1) [49-50]
+                UnaryExpression(-) [51-53]
+                  IntLiteral(2) [52-53]
+                IntLiteral(3) [55-56]
+                UnaryExpression(-) [58-60]
+                  IntLiteral(2) [59-60]
+                IntLiteral(1) [62-63]
+                IntLiteral(2) [65-66]
+          Alias [69-73]
+            Identifier(x) [72-73]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#array_agg
+SELECT ARRAY_AGG(DISTINCT x) AS array_agg
+FROM UNNEST([2, 1, -2, 3, -2, 1, 2]) AS x
+--
+QueryStatement [0-83]
+  Query [0-83]
+    Select [0-83]
+      SelectList [7-41]
+        SelectColumn [7-41]
+          FunctionCall(distinct=true) [7-28]
+            PathExpression [7-16]
+              Identifier(ARRAY_AGG) [7-16]
+            PathExpression [26-27]
+              Identifier(x) [26-27]
+          Alias [29-41]
+            Identifier(array_agg) [32-41]
+      FromClause [42-83]
+        TablePathExpression [47-83]
+          UnnestExpression [47-78]
+            ExpressionWithOptAlias [54-77]
+              ArrayConstructor [54-77]
+                IntLiteral(2) [55-56]
+                IntLiteral(1) [58-59]
+                UnaryExpression(-) [61-63]
+                  IntLiteral(2) [62-63]
+                IntLiteral(3) [65-66]
+                UnaryExpression(-) [68-70]
+                  IntLiteral(2) [69-70]
+                IntLiteral(1) [72-73]
+                IntLiteral(2) [75-76]
+          Alias [79-83]
+            Identifier(x) [82-83]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#array_agg
+SELECT ARRAY_AGG(x IGNORE NULLS) AS array_agg
+FROM UNNEST([NULL, 1, -2, 3, -2, 1, NULL]) AS x
+--
+QueryStatement [0-93]
+  Query [0-93]
+    Select [0-93]
+      SelectList [7-45]
+        SelectColumn [7-45]
+          FunctionCall [7-32]
+            PathExpression [7-16]
+              Identifier(ARRAY_AGG) [7-16]
+            PathExpression [17-18]
+              Identifier(x) [17-18]
+          Alias [33-45]
+            Identifier(array_agg) [36-45]
+      FromClause [46-93]
+        TablePathExpression [51-93]
+          UnnestExpression [51-88]
+            ExpressionWithOptAlias [58-87]
+              ArrayConstructor [58-87]
+                NullLiteral(NULL) [59-63]
+                IntLiteral(1) [65-66]
+                UnaryExpression(-) [68-70]
+                  IntLiteral(2) [69-70]
+                IntLiteral(3) [72-73]
+                UnaryExpression(-) [75-77]
+                  IntLiteral(2) [76-77]
+                IntLiteral(1) [79-80]
+                NullLiteral(NULL) [82-86]
+          Alias [89-93]
+            Identifier(x) [92-93]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#array_agg
+SELECT ARRAY_AGG(x ORDER BY ABS(x)) AS array_agg
+FROM UNNEST([2, 1, -2, 3, -2, 1, 2]) AS x
+--
+QueryStatement [0-90]
+  Query [0-90]
+    Select [0-90]
+      SelectList [7-48]
+        SelectColumn [7-48]
+          FunctionCall [7-35]
+            PathExpression [7-16]
+              Identifier(ARRAY_AGG) [7-16]
+            PathExpression [17-18]
+              Identifier(x) [17-18]
+            OrderBy [19-34]
+              OrderingExpression(ASC) [28-34]
+                FunctionCall [28-34]
+                  PathExpression [28-31]
+                    Identifier(ABS) [28-31]
+                  PathExpression [32-33]
+                    Identifier(x) [32-33]
+          Alias [36-48]
+            Identifier(array_agg) [39-48]
+      FromClause [49-90]
+        TablePathExpression [54-90]
+          UnnestExpression [54-85]
+            ExpressionWithOptAlias [61-84]
+              ArrayConstructor [61-84]
+                IntLiteral(2) [62-63]
+                IntLiteral(1) [65-66]
+                UnaryExpression(-) [68-70]
+                  IntLiteral(2) [69-70]
+                IntLiteral(3) [72-73]
+                UnaryExpression(-) [75-77]
+                  IntLiteral(2) [76-77]
+                IntLiteral(1) [79-80]
+                IntLiteral(2) [82-83]
+          Alias [86-90]
+            Identifier(x) [89-90]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#array_agg
+SELECT ARRAY_AGG(x LIMIT 5) AS array_agg
+FROM UNNEST([2, 1, -2, 3, -2, 1, 2]) AS x
+--
+QueryStatement [0-82]
+  Query [0-82]
+    Select [0-82]
+      SelectList [7-40]
+        SelectColumn [7-40]
+          FunctionCall [7-27]
+            PathExpression [7-16]
+              Identifier(ARRAY_AGG) [7-16]
+            PathExpression [17-18]
+              Identifier(x) [17-18]
+            LimitOffset [19-26]
+              Limit [19-26]
+                IntLiteral(5) [25-26]
+          Alias [28-40]
+            Identifier(array_agg) [31-40]
+      FromClause [41-82]
+        TablePathExpression [46-82]
+          UnnestExpression [46-77]
+            ExpressionWithOptAlias [53-76]
+              ArrayConstructor [53-76]
+                IntLiteral(2) [54-55]
+                IntLiteral(1) [57-58]
+                UnaryExpression(-) [60-62]
+                  IntLiteral(2) [61-62]
+                IntLiteral(3) [64-65]
+                UnaryExpression(-) [67-69]
+                  IntLiteral(2) [68-69]
+                IntLiteral(1) [71-72]
+                IntLiteral(2) [74-75]
+          Alias [78-82]
+            Identifier(x) [81-82]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#array_agg
+WITH vals AS
+  (
+    SELECT 1 x UNION ALL
+    SELECT -2 x UNION ALL
+    SELECT 3 x UNION ALL
+    SELECT -2 x UNION ALL
+    SELECT 1 x
+  )
+SELECT ARRAY_AGG(DISTINCT x ORDER BY x) as array_agg
+FROM vals
+--
+QueryStatement [0-200]
+  Query [0-200]
+    WithClause [0-137]
+      WithClauseEntry [5-137]
+        AliasedQuery [5-137]
+          Identifier(vals) [5-9]
+          Query [15-137]
+            SetOperation(UNION ALL) [21-133]
+              SetOperationMetadataList [32-118]
+                SetOperationMetadata [32-41]
+                  SetOperationType [32-37]
+                  SetOperationAllOrDistinct [38-41]
+                SetOperationMetadata [58-67]
+                  SetOperationType [58-63]
+                  SetOperationAllOrDistinct [64-67]
+                SetOperationMetadata [83-92]
+                  SetOperationType [83-88]
+                  SetOperationAllOrDistinct [89-92]
+                SetOperationMetadata [109-118]
+                  SetOperationType [109-114]
+                  SetOperationAllOrDistinct [115-118]
+              Select [21-31]
+                SelectList [28-31]
+                  SelectColumn [28-31]
+                    IntLiteral(1) [28-29]
+                    Alias [30-31]
+                      Identifier(x) [30-31]
+              Select [46-57]
+                SelectList [53-57]
+                  SelectColumn [53-57]
+                    UnaryExpression(-) [53-55]
+                      IntLiteral(2) [54-55]
+                    Alias [56-57]
+                      Identifier(x) [56-57]
+              Select [72-82]
+                SelectList [79-82]
+                  SelectColumn [79-82]
+                    IntLiteral(3) [79-80]
+                    Alias [81-82]
+                      Identifier(x) [81-82]
+              Select [97-108]
+                SelectList [104-108]
+                  SelectColumn [104-108]
+                    UnaryExpression(-) [104-106]
+                      IntLiteral(2) [105-106]
+                    Alias [107-108]
+                      Identifier(x) [107-108]
+              Select [123-133]
+                SelectList [130-133]
+                  SelectColumn [130-133]
+                    IntLiteral(1) [130-131]
+                    Alias [132-133]
+                      Identifier(x) [132-133]
+    Select [138-200]
+      SelectList [145-190]
+        SelectColumn [145-190]
+          FunctionCall(distinct=true) [145-177]
+            PathExpression [145-154]
+              Identifier(ARRAY_AGG) [145-154]
+            PathExpression [164-165]
+              Identifier(x) [164-165]
+            OrderBy [166-176]
+              OrderingExpression(ASC) [175-176]
+                PathExpression [175-176]
+                  Identifier(x) [175-176]
+          Alias [178-190]
+            Identifier(array_agg) [181-190]
+      FromClause [191-200]
+        TablePathExpression [196-200]
+          PathExpression [196-200]
+            Identifier(vals) [196-200]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#array_agg
+WITH vals AS
+  (
+    SELECT 1 x, 'a' y UNION ALL
+    SELECT 1 x, 'b' y UNION ALL
+    SELECT 2 x, 'a' y UNION ALL
+    SELECT 2 x, 'c' y
+  )
+SELECT x, ARRAY_AGG(y) as array_agg
+FROM vals
+GROUP BY x
+--
+QueryStatement [0-195]
+  Query [0-195]
+    WithClause [0-138]
+      WithClauseEntry [5-138]
+        AliasedQuery [5-138]
+          Identifier(vals) [5-9]
+          Query [15-138]
+            SetOperation(UNION ALL) [21-134]
+              SetOperationMetadataList [39-112]
+                SetOperationMetadata [39-48]
+                  SetOperationType [39-44]
+                  SetOperationAllOrDistinct [45-48]
+                SetOperationMetadata [71-80]
+                  SetOperationType [71-76]
+                  SetOperationAllOrDistinct [77-80]
+                SetOperationMetadata [103-112]
+                  SetOperationType [103-108]
+                  SetOperationAllOrDistinct [109-112]
+              Select [21-38]
+                SelectList [28-38]
+                  SelectColumn [28-31]
+                    IntLiteral(1) [28-29]
+                    Alias [30-31]
+                      Identifier(x) [30-31]
+                  SelectColumn [33-38]
+                    StringLiteral [33-36]
+                      StringLiteralComponent('a') [33-36]
+                    Alias [37-38]
+                      Identifier(y) [37-38]
+              Select [53-70]
+                SelectList [60-70]
+                  SelectColumn [60-63]
+                    IntLiteral(1) [60-61]
+                    Alias [62-63]
+                      Identifier(x) [62-63]
+                  SelectColumn [65-70]
+                    StringLiteral [65-68]
+                      StringLiteralComponent('b') [65-68]
+                    Alias [69-70]
+                      Identifier(y) [69-70]
+              Select [85-102]
+                SelectList [92-102]
+                  SelectColumn [92-95]
+                    IntLiteral(2) [92-93]
+                    Alias [94-95]
+                      Identifier(x) [94-95]
+                  SelectColumn [97-102]
+                    StringLiteral [97-100]
+                      StringLiteralComponent('a') [97-100]
+                    Alias [101-102]
+                      Identifier(y) [101-102]
+              Select [117-134]
+                SelectList [124-134]
+                  SelectColumn [124-127]
+                    IntLiteral(2) [124-125]
+                    Alias [126-127]
+                      Identifier(x) [126-127]
+                  SelectColumn [129-134]
+                    StringLiteral [129-132]
+                      StringLiteralComponent('c') [129-132]
+                    Alias [133-134]
+                      Identifier(y) [133-134]
+    Select [139-195]
+      SelectList [146-174]
+        SelectColumn [146-147]
+          PathExpression [146-147]
+            Identifier(x) [146-147]
+        SelectColumn [149-174]
+          FunctionCall [149-161]
+            PathExpression [149-158]
+              Identifier(ARRAY_AGG) [149-158]
+            PathExpression [159-160]
+              Identifier(y) [159-160]
+          Alias [162-174]
+            Identifier(array_agg) [165-174]
+      FromClause [175-184]
+        TablePathExpression [180-184]
+          PathExpression [180-184]
+            Identifier(vals) [180-184]
+      GroupBy [185-195]
+        GroupingItem [194-195]
+          PathExpression [194-195]
+            Identifier(x) [194-195]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#array_concat_agg
+SELECT ARRAY_CONCAT_AGG(x) AS array_concat_agg FROM (
+  SELECT [NULL, 1, 2, 3, 4] AS x
+  UNION ALL SELECT NULL
+  UNION ALL SELECT [5, 6]
+  UNION ALL SELECT [7, 8, 9]
+)
+--
+QueryStatement [0-167]
+  Query [0-167]
+    Select [0-167]
+      SelectList [7-46]
+        SelectColumn [7-46]
+          FunctionCall [7-26]
+            PathExpression [7-23]
+              Identifier(ARRAY_CONCAT_AGG) [7-23]
+            PathExpression [24-25]
+              Identifier(x) [24-25]
+          Alias [27-46]
+            Identifier(array_concat_agg) [30-46]
+      FromClause [47-167]
+        TableSubquery [52-167]
+          Query [56-165]
+            SetOperation(UNION ALL) [56-165]
+              SetOperationMetadataList [89-148]
+                SetOperationMetadata [89-98]
+                  SetOperationType [89-94]
+                  SetOperationAllOrDistinct [95-98]
+                SetOperationMetadata [113-122]
+                  SetOperationType [113-118]
+                  SetOperationAllOrDistinct [119-122]
+                SetOperationMetadata [139-148]
+                  SetOperationType [139-144]
+                  SetOperationAllOrDistinct [145-148]
+              Select [56-86]
+                SelectList [63-86]
+                  SelectColumn [63-86]
+                    ArrayConstructor [63-81]
+                      NullLiteral(NULL) [64-68]
+                      IntLiteral(1) [70-71]
+                      IntLiteral(2) [73-74]
+                      IntLiteral(3) [76-77]
+                      IntLiteral(4) [79-80]
+                    Alias [82-86]
+                      Identifier(x) [85-86]
+              Select [99-110]
+                SelectList [106-110]
+                  SelectColumn [106-110]
+                    NullLiteral(NULL) [106-110]
+              Select [123-136]
+                SelectList [130-136]
+                  SelectColumn [130-136]
+                    ArrayConstructor [130-136]
+                      IntLiteral(5) [131-132]
+                      IntLiteral(6) [134-135]
+              Select [149-165]
+                SelectList [156-165]
+                  SelectColumn [156-165]
+                    ArrayConstructor [156-165]
+                      IntLiteral(7) [157-158]
+                      IntLiteral(8) [160-161]
+                      IntLiteral(9) [163-164]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#array_concat_agg
+SELECT ARRAY_CONCAT_AGG(x ORDER BY ARRAY_LENGTH(x)) AS array_concat_agg FROM (
+  SELECT [1, 2, 3, 4] AS x
+  UNION ALL SELECT [5, 6]
+  UNION ALL SELECT [7, 8, 9]
+)
+--
+QueryStatement [0-162]
+  Query [0-162]
+    Select [0-162]
+      SelectList [7-71]
+        SelectColumn [7-71]
+          FunctionCall [7-51]
+            PathExpression [7-23]
+              Identifier(ARRAY_CONCAT_AGG) [7-23]
+            PathExpression [24-25]
+              Identifier(x) [24-25]
+            OrderBy [26-50]
+              OrderingExpression(ASC) [35-50]
+                FunctionCall [35-50]
+                  PathExpression [35-47]
+                    Identifier(ARRAY_LENGTH) [35-47]
+                  PathExpression [48-49]
+                    Identifier(x) [48-49]
+          Alias [52-71]
+            Identifier(array_concat_agg) [55-71]
+      FromClause [72-162]
+        TableSubquery [77-162]
+          Query [81-160]
+            SetOperation(UNION ALL) [81-160]
+              SetOperationMetadataList [108-143]
+                SetOperationMetadata [108-117]
+                  SetOperationType [108-113]
+                  SetOperationAllOrDistinct [114-117]
+                SetOperationMetadata [134-143]
+                  SetOperationType [134-139]
+                  SetOperationAllOrDistinct [140-143]
+              Select [81-105]
+                SelectList [88-105]
+                  SelectColumn [88-105]
+                    ArrayConstructor [88-100]
+                      IntLiteral(1) [89-90]
+                      IntLiteral(2) [92-93]
+                      IntLiteral(3) [95-96]
+                      IntLiteral(4) [98-99]
+                    Alias [101-105]
+                      Identifier(x) [104-105]
+              Select [118-131]
+                SelectList [125-131]
+                  SelectColumn [125-131]
+                    ArrayConstructor [125-131]
+                      IntLiteral(5) [126-127]
+                      IntLiteral(6) [129-130]
+              Select [144-160]
+                SelectList [151-160]
+                  SelectColumn [151-160]
+                    ArrayConstructor [151-160]
+                      IntLiteral(7) [152-153]
+                      IntLiteral(8) [155-156]
+                      IntLiteral(9) [158-159]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#array_concat_agg
+SELECT ARRAY_CONCAT_AGG(x LIMIT 2) AS array_concat_agg FROM (
+  SELECT [1, 2, 3, 4] AS x
+  UNION ALL SELECT [5, 6]
+  UNION ALL SELECT [7, 8, 9]
+)
+--
+QueryStatement [0-145]
+  Query [0-145]
+    Select [0-145]
+      SelectList [7-54]
+        SelectColumn [7-54]
+          FunctionCall [7-34]
+            PathExpression [7-23]
+              Identifier(ARRAY_CONCAT_AGG) [7-23]
+            PathExpression [24-25]
+              Identifier(x) [24-25]
+            LimitOffset [26-33]
+              Limit [26-33]
+                IntLiteral(2) [32-33]
+          Alias [35-54]
+            Identifier(array_concat_agg) [38-54]
+      FromClause [55-145]
+        TableSubquery [60-145]
+          Query [64-143]
+            SetOperation(UNION ALL) [64-143]
+              SetOperationMetadataList [91-126]
+                SetOperationMetadata [91-100]
+                  SetOperationType [91-96]
+                  SetOperationAllOrDistinct [97-100]
+                SetOperationMetadata [117-126]
+                  SetOperationType [117-122]
+                  SetOperationAllOrDistinct [123-126]
+              Select [64-88]
+                SelectList [71-88]
+                  SelectColumn [71-88]
+                    ArrayConstructor [71-83]
+                      IntLiteral(1) [72-73]
+                      IntLiteral(2) [75-76]
+                      IntLiteral(3) [78-79]
+                      IntLiteral(4) [81-82]
+                    Alias [84-88]
+                      Identifier(x) [87-88]
+              Select [101-114]
+                SelectList [108-114]
+                  SelectColumn [108-114]
+                    ArrayConstructor [108-114]
+                      IntLiteral(5) [109-110]
+                      IntLiteral(6) [112-113]
+              Select [127-143]
+                SelectList [134-143]
+                  SelectColumn [134-143]
+                    ArrayConstructor [134-143]
+                      IntLiteral(7) [135-136]
+                      IntLiteral(8) [138-139]
+                      IntLiteral(9) [141-142]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#array_concat_agg
+SELECT ARRAY_CONCAT_AGG(x ORDER BY ARRAY_LENGTH(x) LIMIT 2) AS array_concat_agg FROM (
+  SELECT [1, 2, 3, 4] AS x
+  UNION ALL SELECT [5, 6]
+  UNION ALL SELECT [7, 8, 9]
+)
+--
+QueryStatement [0-170]
+  Query [0-170]
+    Select [0-170]
+      SelectList [7-79]
+        SelectColumn [7-79]
+          FunctionCall [7-59]
+            PathExpression [7-23]
+              Identifier(ARRAY_CONCAT_AGG) [7-23]
+            PathExpression [24-25]
+              Identifier(x) [24-25]
+            OrderBy [26-50]
+              OrderingExpression(ASC) [35-50]
+                FunctionCall [35-50]
+                  PathExpression [35-47]
+                    Identifier(ARRAY_LENGTH) [35-47]
+                  PathExpression [48-49]
+                    Identifier(x) [48-49]
+            LimitOffset [51-58]
+              Limit [51-58]
+                IntLiteral(2) [57-58]
+          Alias [60-79]
+            Identifier(array_concat_agg) [63-79]
+      FromClause [80-170]
+        TableSubquery [85-170]
+          Query [89-168]
+            SetOperation(UNION ALL) [89-168]
+              SetOperationMetadataList [116-151]
+                SetOperationMetadata [116-125]
+                  SetOperationType [116-121]
+                  SetOperationAllOrDistinct [122-125]
+                SetOperationMetadata [142-151]
+                  SetOperationType [142-147]
+                  SetOperationAllOrDistinct [148-151]
+              Select [89-113]
+                SelectList [96-113]
+                  SelectColumn [96-113]
+                    ArrayConstructor [96-108]
+                      IntLiteral(1) [97-98]
+                      IntLiteral(2) [100-101]
+                      IntLiteral(3) [103-104]
+                      IntLiteral(4) [106-107]
+                    Alias [109-113]
+                      Identifier(x) [112-113]
+              Select [126-139]
+                SelectList [133-139]
+                  SelectColumn [133-139]
+                    ArrayConstructor [133-139]
+                      IntLiteral(5) [134-135]
+                      IntLiteral(6) [137-138]
+              Select [152-168]
+                SelectList [159-168]
+                  SelectColumn [159-168]
+                    ArrayConstructor [159-168]
+                      IntLiteral(7) [160-161]
+                      IntLiteral(8) [163-164]
+                      IntLiteral(9) [166-167]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#avg
+SELECT AVG(x) as avg
+FROM UNNEST([0, 2, 4, 4, 5]) as x
+--
+QueryStatement [0-54]
+  Query [0-54]
+    Select [0-54]
+      SelectList [7-20]
+        SelectColumn [7-20]
+          FunctionCall [7-13]
+            PathExpression [7-10]
+              Identifier(AVG) [7-10]
+            PathExpression [11-12]
+              Identifier(x) [11-12]
+          Alias [14-20]
+            Identifier(avg) [17-20]
+      FromClause [21-54]
+        TablePathExpression [26-54]
+          UnnestExpression [26-49]
+            ExpressionWithOptAlias [33-48]
+              ArrayConstructor [33-48]
+                IntLiteral(0) [34-35]
+                IntLiteral(2) [37-38]
+                IntLiteral(4) [40-41]
+                IntLiteral(4) [43-44]
+                IntLiteral(5) [46-47]
+          Alias [50-54]
+            Identifier(x) [53-54]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#avg
+SELECT AVG(DISTINCT x) AS avg
+FROM UNNEST([0, 2, 4, 4, 5]) AS x
+--
+QueryStatement [0-63]
+  Query [0-63]
+    Select [0-63]
+      SelectList [7-29]
+        SelectColumn [7-29]
+          FunctionCall(distinct=true) [7-22]
+            PathExpression [7-10]
+              Identifier(AVG) [7-10]
+            PathExpression [20-21]
+              Identifier(x) [20-21]
+          Alias [23-29]
+            Identifier(avg) [26-29]
+      FromClause [30-63]
+        TablePathExpression [35-63]
+          UnnestExpression [35-58]
+            ExpressionWithOptAlias [42-57]
+              ArrayConstructor [42-57]
+                IntLiteral(0) [43-44]
+                IntLiteral(2) [46-47]
+                IntLiteral(4) [49-50]
+                IntLiteral(4) [52-53]
+                IntLiteral(5) [55-56]
+          Alias [59-63]
+            Identifier(x) [62-63]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#bit_and
+SELECT BIT_AND(x) as bit_and FROM UNNEST([0xF001, 0x00A1]) as x
+--
+QueryStatement [0-63]
+  Query [0-63]
+    Select [0-63]
+      SelectList [7-28]
+        SelectColumn [7-28]
+          FunctionCall [7-17]
+            PathExpression [7-14]
+              Identifier(BIT_AND) [7-14]
+            PathExpression [15-16]
+              Identifier(x) [15-16]
+          Alias [18-28]
+            Identifier(bit_and) [21-28]
+      FromClause [29-63]
+        TablePathExpression [34-63]
+          UnnestExpression [34-58]
+            ExpressionWithOptAlias [41-57]
+              ArrayConstructor [41-57]
+                IntLiteral(0xF001) [42-48]
+                IntLiteral(0x00A1) [50-56]
+          Alias [59-63]
+            Identifier(x) [62-63]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#bit_or
+SELECT BIT_OR(x) as bit_or FROM UNNEST([0xF001, 0x00A1]) as x
+--
+QueryStatement [0-61]
+  Query [0-61]
+    Select [0-61]
+      SelectList [7-26]
+        SelectColumn [7-26]
+          FunctionCall [7-16]
+            PathExpression [7-13]
+              Identifier(BIT_OR) [7-13]
+            PathExpression [14-15]
+              Identifier(x) [14-15]
+          Alias [17-26]
+            Identifier(bit_or) [20-26]
+      FromClause [27-61]
+        TablePathExpression [32-61]
+          UnnestExpression [32-56]
+            ExpressionWithOptAlias [39-55]
+              ArrayConstructor [39-55]
+                IntLiteral(0xF001) [40-46]
+                IntLiteral(0x00A1) [48-54]
+          Alias [57-61]
+            Identifier(x) [60-61]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#bit_xor
+SELECT BIT_XOR(x) AS bit_xor FROM UNNEST([5678, 1234]) AS x
+--
+QueryStatement [0-59]
+  Query [0-59]
+    Select [0-59]
+      SelectList [7-28]
+        SelectColumn [7-28]
+          FunctionCall [7-17]
+            PathExpression [7-14]
+              Identifier(BIT_XOR) [7-14]
+            PathExpression [15-16]
+              Identifier(x) [15-16]
+          Alias [18-28]
+            Identifier(bit_xor) [21-28]
+      FromClause [29-59]
+        TablePathExpression [34-59]
+          UnnestExpression [34-54]
+            ExpressionWithOptAlias [41-53]
+              ArrayConstructor [41-53]
+                IntLiteral(5678) [42-46]
+                IntLiteral(1234) [48-52]
+          Alias [55-59]
+            Identifier(x) [58-59]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#bit_xor
+SELECT BIT_XOR(x) AS bit_xor FROM UNNEST([1234, 5678, 1234]) AS x
+--
+QueryStatement [0-65]
+  Query [0-65]
+    Select [0-65]
+      SelectList [7-28]
+        SelectColumn [7-28]
+          FunctionCall [7-17]
+            PathExpression [7-14]
+              Identifier(BIT_XOR) [7-14]
+            PathExpression [15-16]
+              Identifier(x) [15-16]
+          Alias [18-28]
+            Identifier(bit_xor) [21-28]
+      FromClause [29-65]
+        TablePathExpression [34-65]
+          UnnestExpression [34-60]
+            ExpressionWithOptAlias [41-59]
+              ArrayConstructor [41-59]
+                IntLiteral(1234) [42-46]
+                IntLiteral(5678) [48-52]
+                IntLiteral(1234) [54-58]
+          Alias [61-65]
+            Identifier(x) [64-65]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#bit_xor
+SELECT BIT_XOR(DISTINCT x) AS bit_xor FROM UNNEST([1234, 5678, 1234]) AS x
+--
+QueryStatement [0-74]
+  Query [0-74]
+    Select [0-74]
+      SelectList [7-37]
+        SelectColumn [7-37]
+          FunctionCall(distinct=true) [7-26]
+            PathExpression [7-14]
+              Identifier(BIT_XOR) [7-14]
+            PathExpression [24-25]
+              Identifier(x) [24-25]
+          Alias [27-37]
+            Identifier(bit_xor) [30-37]
+      FromClause [38-74]
+        TablePathExpression [43-74]
+          UnnestExpression [43-69]
+            ExpressionWithOptAlias [50-68]
+              ArrayConstructor [50-68]
+                IntLiteral(1234) [51-55]
+                IntLiteral(5678) [57-61]
+                IntLiteral(1234) [63-67]
+          Alias [70-74]
+            Identifier(x) [73-74]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#count
+[mode=expression]
+COUNT(*)
+--
+FunctionCall [0-8]
+  PathExpression [0-5]
+    Identifier(COUNT) [0-5]
+  Star(*) [6-7]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#count
+[mode=expression]
+COUNT(DISTINCT IF(condition, expression, NULL))
+--
+FunctionCall(distinct=true) [0-47]
+  PathExpression [0-5]
+    Identifier(COUNT) [0-5]
+  FunctionCall [15-46]
+    PathExpression [15-17]
+      Identifier(`IF`) [15-17]
+    PathExpression [18-27]
+      Identifier(condition) [18-27]
+    PathExpression [29-39]
+      Identifier(expression) [29-39]
+    NullLiteral(NULL) [41-45]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#count
+SELECT
+  COUNT(*) AS count_star,
+  COUNT(DISTINCT x) AS count_dist_x
+FROM UNNEST([1, 4, 4, 5]) AS x
+--
+QueryStatement [0-99]
+  Query [0-99]
+    Select [0-99]
+      SelectList [9-68]
+        SelectColumn [9-31]
+          FunctionCall [9-17]
+            PathExpression [9-14]
+              Identifier(COUNT) [9-14]
+            Star(*) [15-16]
+          Alias [18-31]
+            Identifier(count_star) [21-31]
+        SelectColumn [35-68]
+          FunctionCall(distinct=true) [35-52]
+            PathExpression [35-40]
+              Identifier(COUNT) [35-40]
+            PathExpression [50-51]
+              Identifier(x) [50-51]
+          Alias [53-68]
+            Identifier(count_dist_x) [56-68]
+      FromClause [69-99]
+        TablePathExpression [74-99]
+          UnnestExpression [74-94]
+            ExpressionWithOptAlias [81-93]
+              ArrayConstructor [81-93]
+                IntLiteral(1) [82-83]
+                IntLiteral(4) [85-86]
+                IntLiteral(4) [88-89]
+                IntLiteral(5) [91-92]
+          Alias [95-99]
+            Identifier(x) [98-99]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#count
+SELECT COUNT(*) AS count_star, COUNT(x) AS count_x
+FROM UNNEST([1, 4, NULL, 4, 5]) AS x
+--
+QueryStatement [0-87]
+  Query [0-87]
+    Select [0-87]
+      SelectList [7-50]
+        SelectColumn [7-29]
+          FunctionCall [7-15]
+            PathExpression [7-12]
+              Identifier(COUNT) [7-12]
+            Star(*) [13-14]
+          Alias [16-29]
+            Identifier(count_star) [19-29]
+        SelectColumn [31-50]
+          FunctionCall [31-39]
+            PathExpression [31-36]
+              Identifier(COUNT) [31-36]
+            PathExpression [37-38]
+              Identifier(x) [37-38]
+          Alias [40-50]
+            Identifier(count_x) [43-50]
+      FromClause [51-87]
+        TablePathExpression [56-87]
+          UnnestExpression [56-82]
+            ExpressionWithOptAlias [63-81]
+              ArrayConstructor [63-81]
+                IntLiteral(1) [64-65]
+                IntLiteral(4) [67-68]
+                NullLiteral(NULL) [70-74]
+                IntLiteral(4) [76-77]
+                IntLiteral(5) [79-80]
+          Alias [83-87]
+            Identifier(x) [86-87]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#count
+SELECT COUNT(DISTINCT IF(x > 0, x, NULL)) AS distinct_positive
+FROM UNNEST([1, -2, 4, 1, -5, 4, 1, 3, -6, 1]) AS x
+--
+QueryStatement [0-114]
+  Query [0-114]
+    Select [0-114]
+      SelectList [7-62]
+        SelectColumn [7-62]
+          FunctionCall(distinct=true) [7-41]
+            PathExpression [7-12]
+              Identifier(COUNT) [7-12]
+            FunctionCall [22-40]
+              PathExpression [22-24]
+                Identifier(`IF`) [22-24]
+              BinaryExpression(>) [25-30]
+                PathExpression [25-26]
+                  Identifier(x) [25-26]
+                IntLiteral(0) [29-30]
+              PathExpression [32-33]
+                Identifier(x) [32-33]
+              NullLiteral(NULL) [35-39]
+          Alias [42-62]
+            Identifier(distinct_positive) [45-62]
+      FromClause [63-114]
+        TablePathExpression [68-114]
+          UnnestExpression [68-109]
+            ExpressionWithOptAlias [75-108]
+              ArrayConstructor [75-108]
+                IntLiteral(1) [76-77]
+                UnaryExpression(-) [79-81]
+                  IntLiteral(2) [80-81]
+                IntLiteral(4) [83-84]
+                IntLiteral(1) [86-87]
+                UnaryExpression(-) [89-91]
+                  IntLiteral(5) [90-91]
+                IntLiteral(4) [93-94]
+                IntLiteral(1) [96-97]
+                IntLiteral(3) [99-100]
+                UnaryExpression(-) [102-104]
+                  IntLiteral(6) [103-104]
+                IntLiteral(1) [106-107]
+          Alias [110-114]
+            Identifier(x) [113-114]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#count
+WITH Events AS (
+  SELECT DATE '2021-01-01' AS event_date, 'SUCCESS' AS event_type
+  UNION ALL
+  SELECT DATE '2021-01-02' AS event_date, 'SUCCESS' AS event_type
+  UNION ALL
+  SELECT DATE '2021-01-02' AS event_date, 'FAILURE' AS event_type
+  UNION ALL
+  SELECT DATE '2021-01-03' AS event_date, 'SUCCESS' AS event_type
+  UNION ALL
+  SELECT DATE '2021-01-04' AS event_date, 'FAILURE' AS event_type
+  UNION ALL
+  SELECT DATE '2021-01-04' AS event_date, 'FAILURE' AS event_type
+)
+SELECT
+  COUNT(DISTINCT IF(event_type = 'FAILURE', event_date, NULL))
+    AS distinct_dates_with_failures
+FROM Events
+--
+QueryStatement [0-592]
+  Query [0-592]
+    WithClause [0-474]
+      WithClauseEntry [5-474]
+        AliasedQuery [5-474]
+          Identifier(Events) [5-11]
+          Query [15-474]
+            SetOperation(UNION ALL) [19-472]
+              SetOperationMetadataList [85-406]
+                SetOperationMetadata [85-94]
+                  SetOperationType [85-90]
+                  SetOperationAllOrDistinct [91-94]
+                SetOperationMetadata [163-172]
+                  SetOperationType [163-168]
+                  SetOperationAllOrDistinct [169-172]
+                SetOperationMetadata [241-250]
+                  SetOperationType [241-246]
+                  SetOperationAllOrDistinct [247-250]
+                SetOperationMetadata [319-328]
+                  SetOperationType [319-324]
+                  SetOperationAllOrDistinct [325-328]
+                SetOperationMetadata [397-406]
+                  SetOperationType [397-402]
+                  SetOperationAllOrDistinct [403-406]
+              Select [19-82]
+                SelectList [26-82]
+                  SelectColumn [26-57]
+                    DateOrTimeLiteral(TYPE_DATE) [26-43]
+                      StringLiteral [31-43]
+                        StringLiteralComponent('2021-01-01') [31-43]
+                    Alias [44-57]
+                      Identifier(event_date) [47-57]
+                  SelectColumn [59-82]
+                    StringLiteral [59-68]
+                      StringLiteralComponent('SUCCESS') [59-68]
+                    Alias [69-82]
+                      Identifier(event_type) [72-82]
+              Select [97-160]
+                SelectList [104-160]
+                  SelectColumn [104-135]
+                    DateOrTimeLiteral(TYPE_DATE) [104-121]
+                      StringLiteral [109-121]
+                        StringLiteralComponent('2021-01-02') [109-121]
+                    Alias [122-135]
+                      Identifier(event_date) [125-135]
+                  SelectColumn [137-160]
+                    StringLiteral [137-146]
+                      StringLiteralComponent('SUCCESS') [137-146]
+                    Alias [147-160]
+                      Identifier(event_type) [150-160]
+              Select [175-238]
+                SelectList [182-238]
+                  SelectColumn [182-213]
+                    DateOrTimeLiteral(TYPE_DATE) [182-199]
+                      StringLiteral [187-199]
+                        StringLiteralComponent('2021-01-02') [187-199]
+                    Alias [200-213]
+                      Identifier(event_date) [203-213]
+                  SelectColumn [215-238]
+                    StringLiteral [215-224]
+                      StringLiteralComponent('FAILURE') [215-224]
+                    Alias [225-238]
+                      Identifier(event_type) [228-238]
+              Select [253-316]
+                SelectList [260-316]
+                  SelectColumn [260-291]
+                    DateOrTimeLiteral(TYPE_DATE) [260-277]
+                      StringLiteral [265-277]
+                        StringLiteralComponent('2021-01-03') [265-277]
+                    Alias [278-291]
+                      Identifier(event_date) [281-291]
+                  SelectColumn [293-316]
+                    StringLiteral [293-302]
+                      StringLiteralComponent('SUCCESS') [293-302]
+                    Alias [303-316]
+                      Identifier(event_type) [306-316]
+              Select [331-394]
+                SelectList [338-394]
+                  SelectColumn [338-369]
+                    DateOrTimeLiteral(TYPE_DATE) [338-355]
+                      StringLiteral [343-355]
+                        StringLiteralComponent('2021-01-04') [343-355]
+                    Alias [356-369]
+                      Identifier(event_date) [359-369]
+                  SelectColumn [371-394]
+                    StringLiteral [371-380]
+                      StringLiteralComponent('FAILURE') [371-380]
+                    Alias [381-394]
+                      Identifier(event_type) [384-394]
+              Select [409-472]
+                SelectList [416-472]
+                  SelectColumn [416-447]
+                    DateOrTimeLiteral(TYPE_DATE) [416-433]
+                      StringLiteral [421-433]
+                        StringLiteralComponent('2021-01-04') [421-433]
+                    Alias [434-447]
+                      Identifier(event_date) [437-447]
+                  SelectColumn [449-472]
+                    StringLiteral [449-458]
+                      StringLiteralComponent('FAILURE') [449-458]
+                    Alias [459-472]
+                      Identifier(event_type) [462-472]
+    Select [475-592]
+      SelectList [484-580]
+        SelectColumn [484-580]
+          FunctionCall(distinct=true) [484-544]
+            PathExpression [484-489]
+              Identifier(COUNT) [484-489]
+            FunctionCall [499-543]
+              PathExpression [499-501]
+                Identifier(`IF`) [499-501]
+              BinaryExpression(=) [502-524]
+                PathExpression [502-512]
+                  Identifier(event_type) [502-512]
+                StringLiteral [515-524]
+                  StringLiteralComponent('FAILURE') [515-524]
+              PathExpression [526-536]
+                Identifier(event_date) [526-536]
+              NullLiteral(NULL) [538-542]
+          Alias [549-580]
+            Identifier(distinct_dates_with_failures) [552-580]
+      FromClause [581-592]
+        TablePathExpression [586-592]
+          PathExpression [586-592]
+            Identifier(Events) [586-592]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#count
+WITH
+  customers AS (
+    SELECT 1934 AS id, 'a' AS team UNION ALL
+    SELECT 2991, 'b' UNION ALL
+    SELECT 3988, 'c'),
+  vendors AS (
+    SELECT 1934 AS id, 'd' AS team UNION ALL
+    SELECT 2991, 'e' UNION ALL
+    SELECT 4366, 'f')
+SELECT
+  COUNT(DISTINCT IF(id IN (SELECT id FROM customers), id, NULL)) AS result
+FROM vendors
+--
+QueryStatement [0-328]
+  Query [0-328]
+    WithClause [0-233]
+      WithClauseEntry [7-119]
+        AliasedQuery [7-119]
+          Identifier(customers) [7-16]
+          Query [20-119]
+            SetOperation(UNION ALL) [26-118]
+              SetOperationMetadataList [57-97]
+                SetOperationMetadata [57-66]
+                  SetOperationType [57-62]
+                  SetOperationAllOrDistinct [63-66]
+                SetOperationMetadata [88-97]
+                  SetOperationType [88-93]
+                  SetOperationAllOrDistinct [94-97]
+              Select [26-56]
+                SelectList [33-56]
+                  SelectColumn [33-43]
+                    IntLiteral(1934) [33-37]
+                    Alias [38-43]
+                      Identifier(id) [41-43]
+                  SelectColumn [45-56]
+                    StringLiteral [45-48]
+                      StringLiteralComponent('a') [45-48]
+                    Alias [49-56]
+                      Identifier(team) [52-56]
+              Select [71-87]
+                SelectList [78-87]
+                  SelectColumn [78-82]
+                    IntLiteral(2991) [78-82]
+                  SelectColumn [84-87]
+                    StringLiteral [84-87]
+                      StringLiteralComponent('b') [84-87]
+              Select [102-118]
+                SelectList [109-118]
+                  SelectColumn [109-113]
+                    IntLiteral(3988) [109-113]
+                  SelectColumn [115-118]
+                    StringLiteral [115-118]
+                      StringLiteralComponent('c') [115-118]
+      WithClauseEntry [123-233]
+        AliasedQuery [123-233]
+          Identifier(vendors) [123-130]
+          Query [134-233]
+            SetOperation(UNION ALL) [140-232]
+              SetOperationMetadataList [171-211]
+                SetOperationMetadata [171-180]
+                  SetOperationType [171-176]
+                  SetOperationAllOrDistinct [177-180]
+                SetOperationMetadata [202-211]
+                  SetOperationType [202-207]
+                  SetOperationAllOrDistinct [208-211]
+              Select [140-170]
+                SelectList [147-170]
+                  SelectColumn [147-157]
+                    IntLiteral(1934) [147-151]
+                    Alias [152-157]
+                      Identifier(id) [155-157]
+                  SelectColumn [159-170]
+                    StringLiteral [159-162]
+                      StringLiteralComponent('d') [159-162]
+                    Alias [163-170]
+                      Identifier(team) [166-170]
+              Select [185-201]
+                SelectList [192-201]
+                  SelectColumn [192-196]
+                    IntLiteral(2991) [192-196]
+                  SelectColumn [198-201]
+                    StringLiteral [198-201]
+                      StringLiteralComponent('e') [198-201]
+              Select [216-232]
+                SelectList [223-232]
+                  SelectColumn [223-227]
+                    IntLiteral(4366) [223-227]
+                  SelectColumn [229-232]
+                    StringLiteral [229-232]
+                      StringLiteralComponent('f') [229-232]
+    Select [234-328]
+      SelectList [243-315]
+        SelectColumn [243-315]
+          FunctionCall(distinct=true) [243-305]
+            PathExpression [243-248]
+              Identifier(COUNT) [243-248]
+            FunctionCall [258-304]
+              PathExpression [258-260]
+                Identifier(`IF`) [258-260]
+              InExpression(IN) [261-293]
+                PathExpression [261-263]
+                  Identifier(id) [261-263]
+                Location [264-266]
+                Query [268-292]
+                  Select [268-292]
+                    SelectList [275-277]
+                      SelectColumn [275-277]
+                        PathExpression [275-277]
+                          Identifier(id) [275-277]
+                    FromClause [278-292]
+                      TablePathExpression [283-292]
+                        PathExpression [283-292]
+                          Identifier(customers) [283-292]
+              PathExpression [295-297]
+                Identifier(id) [295-297]
+              NullLiteral(NULL) [299-303]
+          Alias [306-315]
+            Identifier(result) [309-315]
+      FromClause [316-328]
+        TablePathExpression [321-328]
+          PathExpression [321-328]
+            Identifier(vendors) [321-328]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#countif
+SELECT COUNTIF(x<0) AS num_negative, COUNTIF(x>0) AS num_positive
+FROM UNNEST([5, -2, 3, 6, -10, -7, 4, 0]) AS x
+--
+QueryStatement [0-112]
+  Query [0-112]
+    Select [0-112]
+      SelectList [7-65]
+        SelectColumn [7-35]
+          FunctionCall [7-19]
+            PathExpression [7-14]
+              Identifier(COUNTIF) [7-14]
+            BinaryExpression(<) [15-18]
+              PathExpression [15-16]
+                Identifier(x) [15-16]
+              IntLiteral(0) [17-18]
+          Alias [20-35]
+            Identifier(num_negative) [23-35]
+        SelectColumn [37-65]
+          FunctionCall [37-49]
+            PathExpression [37-44]
+              Identifier(COUNTIF) [37-44]
+            BinaryExpression(>) [45-48]
+              PathExpression [45-46]
+                Identifier(x) [45-46]
+              IntLiteral(0) [47-48]
+          Alias [50-65]
+            Identifier(num_positive) [53-65]
+      FromClause [66-112]
+        TablePathExpression [71-112]
+          UnnestExpression [71-107]
+            ExpressionWithOptAlias [78-106]
+              ArrayConstructor [78-106]
+                IntLiteral(5) [79-80]
+                UnaryExpression(-) [82-84]
+                  IntLiteral(2) [83-84]
+                IntLiteral(3) [86-87]
+                IntLiteral(6) [89-90]
+                UnaryExpression(-) [92-95]
+                  IntLiteral(10) [93-95]
+                UnaryExpression(-) [97-99]
+                  IntLiteral(7) [98-99]
+                IntLiteral(4) [101-102]
+                IntLiteral(0) [104-105]
+          Alias [108-112]
+            Identifier(x) [111-112]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#logical_and
+SELECT LOGICAL_AND(x < 3) AS logical_and FROM UNNEST([1, 2, 4]) AS x
+--
+QueryStatement [0-68]
+  Query [0-68]
+    Select [0-68]
+      SelectList [7-40]
+        SelectColumn [7-40]
+          FunctionCall [7-25]
+            PathExpression [7-18]
+              Identifier(LOGICAL_AND) [7-18]
+            BinaryExpression(<) [19-24]
+              PathExpression [19-20]
+                Identifier(x) [19-20]
+              IntLiteral(3) [23-24]
+          Alias [26-40]
+            Identifier(logical_and) [29-40]
+      FromClause [41-68]
+        TablePathExpression [46-68]
+          UnnestExpression [46-63]
+            ExpressionWithOptAlias [53-62]
+              ArrayConstructor [53-62]
+                IntLiteral(1) [54-55]
+                IntLiteral(2) [57-58]
+                IntLiteral(4) [60-61]
+          Alias [64-68]
+            Identifier(x) [67-68]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#logical_or
+SELECT LOGICAL_OR(x < 3) AS logical_or FROM UNNEST([1, 2, 4]) AS x
+--
+QueryStatement [0-66]
+  Query [0-66]
+    Select [0-66]
+      SelectList [7-38]
+        SelectColumn [7-38]
+          FunctionCall [7-24]
+            PathExpression [7-17]
+              Identifier(LOGICAL_OR) [7-17]
+            BinaryExpression(<) [18-23]
+              PathExpression [18-19]
+                Identifier(x) [18-19]
+              IntLiteral(3) [22-23]
+          Alias [25-38]
+            Identifier(logical_or) [28-38]
+      FromClause [39-66]
+        TablePathExpression [44-66]
+          UnnestExpression [44-61]
+            ExpressionWithOptAlias [51-60]
+              ArrayConstructor [51-60]
+                IntLiteral(1) [52-53]
+                IntLiteral(2) [55-56]
+                IntLiteral(4) [58-59]
+          Alias [62-66]
+            Identifier(x) [65-66]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#max
+SELECT MAX(x) AS max
+FROM UNNEST([8, 37, 55, 4]) AS x
+--
+QueryStatement [0-53]
+  Query [0-53]
+    Select [0-53]
+      SelectList [7-20]
+        SelectColumn [7-20]
+          FunctionCall [7-13]
+            PathExpression [7-10]
+              Identifier(MAX) [7-10]
+            PathExpression [11-12]
+              Identifier(x) [11-12]
+          Alias [14-20]
+            Identifier(max) [17-20]
+      FromClause [21-53]
+        TablePathExpression [26-53]
+          UnnestExpression [26-48]
+            ExpressionWithOptAlias [33-47]
+              ArrayConstructor [33-47]
+                IntLiteral(8) [34-35]
+                IntLiteral(37) [37-39]
+                IntLiteral(55) [41-43]
+                IntLiteral(4) [45-46]
+          Alias [49-53]
+            Identifier(x) [52-53]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#min
+SELECT MIN(x) AS min
+FROM UNNEST([8, 37, 4, 55]) AS x
+--
+QueryStatement [0-53]
+  Query [0-53]
+    Select [0-53]
+      SelectList [7-20]
+        SelectColumn [7-20]
+          FunctionCall [7-13]
+            PathExpression [7-10]
+              Identifier(MIN) [7-10]
+            PathExpression [11-12]
+              Identifier(x) [11-12]
+          Alias [14-20]
+            Identifier(min) [17-20]
+      FromClause [21-53]
+        TablePathExpression [26-53]
+          UnnestExpression [26-48]
+            ExpressionWithOptAlias [33-47]
+              ArrayConstructor [33-47]
+                IntLiteral(8) [34-35]
+                IntLiteral(37) [37-39]
+                IntLiteral(4) [41-42]
+                IntLiteral(55) [44-46]
+          Alias [49-53]
+            Identifier(x) [52-53]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#string_agg
+SELECT STRING_AGG(fruit) AS string_agg
+FROM UNNEST(["apple", NULL, "pear", "banana", "pear"]) AS fruit
+--
+QueryStatement [0-102]
+  Query [0-102]
+    Select [0-102]
+      SelectList [7-38]
+        SelectColumn [7-38]
+          FunctionCall [7-24]
+            PathExpression [7-17]
+              Identifier(STRING_AGG) [7-17]
+            PathExpression [18-23]
+              Identifier(fruit) [18-23]
+          Alias [25-38]
+            Identifier(string_agg) [28-38]
+      FromClause [39-102]
+        TablePathExpression [44-102]
+          UnnestExpression [44-93]
+            ExpressionWithOptAlias [51-92]
+              ArrayConstructor [51-92]
+                StringLiteral [52-59]
+                  StringLiteralComponent("apple") [52-59]
+                NullLiteral(NULL) [61-65]
+                StringLiteral [67-73]
+                  StringLiteralComponent("pear") [67-73]
+                StringLiteral [75-83]
+                  StringLiteralComponent("banana") [75-83]
+                StringLiteral [85-91]
+                  StringLiteralComponent("pear") [85-91]
+          Alias [94-102]
+            Identifier(fruit) [97-102]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#string_agg
+SELECT STRING_AGG(fruit, " & ") AS string_agg
+FROM UNNEST(["apple", "pear", "banana", "pear"]) AS fruit
+--
+QueryStatement [0-103]
+  Query [0-103]
+    Select [0-103]
+      SelectList [7-45]
+        SelectColumn [7-45]
+          FunctionCall [7-31]
+            PathExpression [7-17]
+              Identifier(STRING_AGG) [7-17]
+            PathExpression [18-23]
+              Identifier(fruit) [18-23]
+            StringLiteral [25-30]
+              StringLiteralComponent(" & ") [25-30]
+          Alias [32-45]
+            Identifier(string_agg) [35-45]
+      FromClause [46-103]
+        TablePathExpression [51-103]
+          UnnestExpression [51-94]
+            ExpressionWithOptAlias [58-93]
+              ArrayConstructor [58-93]
+                StringLiteral [59-66]
+                  StringLiteralComponent("apple") [59-66]
+                StringLiteral [68-74]
+                  StringLiteralComponent("pear") [68-74]
+                StringLiteral [76-84]
+                  StringLiteralComponent("banana") [76-84]
+                StringLiteral [86-92]
+                  StringLiteralComponent("pear") [86-92]
+          Alias [95-103]
+            Identifier(fruit) [98-103]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#string_agg
+SELECT STRING_AGG(DISTINCT fruit, " & ") AS string_agg
+FROM UNNEST(["apple", "pear", "banana", "pear"]) AS fruit
+--
+QueryStatement [0-112]
+  Query [0-112]
+    Select [0-112]
+      SelectList [7-54]
+        SelectColumn [7-54]
+          FunctionCall(distinct=true) [7-40]
+            PathExpression [7-17]
+              Identifier(STRING_AGG) [7-17]
+            PathExpression [27-32]
+              Identifier(fruit) [27-32]
+            StringLiteral [34-39]
+              StringLiteralComponent(" & ") [34-39]
+          Alias [41-54]
+            Identifier(string_agg) [44-54]
+      FromClause [55-112]
+        TablePathExpression [60-112]
+          UnnestExpression [60-103]
+            ExpressionWithOptAlias [67-102]
+              ArrayConstructor [67-102]
+                StringLiteral [68-75]
+                  StringLiteralComponent("apple") [68-75]
+                StringLiteral [77-83]
+                  StringLiteralComponent("pear") [77-83]
+                StringLiteral [85-93]
+                  StringLiteralComponent("banana") [85-93]
+                StringLiteral [95-101]
+                  StringLiteralComponent("pear") [95-101]
+          Alias [104-112]
+            Identifier(fruit) [107-112]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#string_agg
+SELECT STRING_AGG(fruit, " & " ORDER BY LENGTH(fruit)) AS string_agg
+FROM UNNEST(["apple", "pear", "banana", "pear"]) AS fruit
+--
+QueryStatement [0-126]
+  Query [0-126]
+    Select [0-126]
+      SelectList [7-68]
+        SelectColumn [7-68]
+          FunctionCall [7-54]
+            PathExpression [7-17]
+              Identifier(STRING_AGG) [7-17]
+            PathExpression [18-23]
+              Identifier(fruit) [18-23]
+            StringLiteral [25-30]
+              StringLiteralComponent(" & ") [25-30]
+            OrderBy [31-53]
+              OrderingExpression(ASC) [40-53]
+                FunctionCall [40-53]
+                  PathExpression [40-46]
+                    Identifier(LENGTH) [40-46]
+                  PathExpression [47-52]
+                    Identifier(fruit) [47-52]
+          Alias [55-68]
+            Identifier(string_agg) [58-68]
+      FromClause [69-126]
+        TablePathExpression [74-126]
+          UnnestExpression [74-117]
+            ExpressionWithOptAlias [81-116]
+              ArrayConstructor [81-116]
+                StringLiteral [82-89]
+                  StringLiteralComponent("apple") [82-89]
+                StringLiteral [91-97]
+                  StringLiteralComponent("pear") [91-97]
+                StringLiteral [99-107]
+                  StringLiteralComponent("banana") [99-107]
+                StringLiteral [109-115]
+                  StringLiteralComponent("pear") [109-115]
+          Alias [118-126]
+            Identifier(fruit) [121-126]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#string_agg
+SELECT STRING_AGG(fruit, " & " LIMIT 2) AS string_agg
+FROM UNNEST(["apple", "pear", "banana", "pear"]) AS fruit
+--
+QueryStatement [0-111]
+  Query [0-111]
+    Select [0-111]
+      SelectList [7-53]
+        SelectColumn [7-53]
+          FunctionCall [7-39]
+            PathExpression [7-17]
+              Identifier(STRING_AGG) [7-17]
+            PathExpression [18-23]
+              Identifier(fruit) [18-23]
+            StringLiteral [25-30]
+              StringLiteralComponent(" & ") [25-30]
+            LimitOffset [31-38]
+              Limit [31-38]
+                IntLiteral(2) [37-38]
+          Alias [40-53]
+            Identifier(string_agg) [43-53]
+      FromClause [54-111]
+        TablePathExpression [59-111]
+          UnnestExpression [59-102]
+            ExpressionWithOptAlias [66-101]
+              ArrayConstructor [66-101]
+                StringLiteral [67-74]
+                  StringLiteralComponent("apple") [67-74]
+                StringLiteral [76-82]
+                  StringLiteralComponent("pear") [76-82]
+                StringLiteral [84-92]
+                  StringLiteralComponent("banana") [84-92]
+                StringLiteral [94-100]
+                  StringLiteralComponent("pear") [94-100]
+          Alias [103-111]
+            Identifier(fruit) [106-111]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#string_agg
+SELECT STRING_AGG(DISTINCT fruit, " & " ORDER BY fruit DESC LIMIT 2) AS string_agg
+FROM UNNEST(["apple", "pear", "banana", "pear"]) AS fruit
+--
+QueryStatement [0-140]
+  Query [0-140]
+    Select [0-140]
+      SelectList [7-82]
+        SelectColumn [7-82]
+          FunctionCall(distinct=true) [7-68]
+            PathExpression [7-17]
+              Identifier(STRING_AGG) [7-17]
+            PathExpression [27-32]
+              Identifier(fruit) [27-32]
+            StringLiteral [34-39]
+              StringLiteralComponent(" & ") [34-39]
+            OrderBy [40-59]
+              OrderingExpression(DESC) [49-59]
+                PathExpression [49-54]
+                  Identifier(fruit) [49-54]
+            LimitOffset [60-67]
+              Limit [60-67]
+                IntLiteral(2) [66-67]
+          Alias [69-82]
+            Identifier(string_agg) [72-82]
+      FromClause [83-140]
+        TablePathExpression [88-140]
+          UnnestExpression [88-131]
+            ExpressionWithOptAlias [95-130]
+              ArrayConstructor [95-130]
+                StringLiteral [96-103]
+                  StringLiteralComponent("apple") [96-103]
+                StringLiteral [105-111]
+                  StringLiteralComponent("pear") [105-111]
+                StringLiteral [113-121]
+                  StringLiteralComponent("banana") [113-121]
+                StringLiteral [123-129]
+                  StringLiteralComponent("pear") [123-129]
+          Alias [132-140]
+            Identifier(fruit) [135-140]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#sum
+SELECT SUM(x) AS sum
+FROM UNNEST([1, 2, 3, 4, 5, 4, 3, 2, 1]) AS x
+--
+QueryStatement [0-66]
+  Query [0-66]
+    Select [0-66]
+      SelectList [7-20]
+        SelectColumn [7-20]
+          FunctionCall [7-13]
+            PathExpression [7-10]
+              Identifier(SUM) [7-10]
+            PathExpression [11-12]
+              Identifier(x) [11-12]
+          Alias [14-20]
+            Identifier(sum) [17-20]
+      FromClause [21-66]
+        TablePathExpression [26-66]
+          UnnestExpression [26-61]
+            ExpressionWithOptAlias [33-60]
+              ArrayConstructor [33-60]
+                IntLiteral(1) [34-35]
+                IntLiteral(2) [37-38]
+                IntLiteral(3) [40-41]
+                IntLiteral(4) [43-44]
+                IntLiteral(5) [46-47]
+                IntLiteral(4) [49-50]
+                IntLiteral(3) [52-53]
+                IntLiteral(2) [55-56]
+                IntLiteral(1) [58-59]
+          Alias [62-66]
+            Identifier(x) [65-66]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#sum
+SELECT SUM(DISTINCT x) AS sum
+FROM UNNEST([1, 2, 3, 4, 5, 4, 3, 2, 1]) AS x
+--
+QueryStatement [0-75]
+  Query [0-75]
+    Select [0-75]
+      SelectList [7-29]
+        SelectColumn [7-29]
+          FunctionCall(distinct=true) [7-22]
+            PathExpression [7-10]
+              Identifier(SUM) [7-10]
+            PathExpression [20-21]
+              Identifier(x) [20-21]
+          Alias [23-29]
+            Identifier(sum) [26-29]
+      FromClause [30-75]
+        TablePathExpression [35-75]
+          UnnestExpression [35-70]
+            ExpressionWithOptAlias [42-69]
+              ArrayConstructor [42-69]
+                IntLiteral(1) [43-44]
+                IntLiteral(2) [46-47]
+                IntLiteral(3) [49-50]
+                IntLiteral(4) [52-53]
+                IntLiteral(5) [55-56]
+                IntLiteral(4) [58-59]
+                IntLiteral(3) [61-62]
+                IntLiteral(2) [64-65]
+                IntLiteral(1) [67-68]
+          Alias [71-75]
+            Identifier(x) [74-75]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/aggregate_functions#sum
+SELECT SUM(x) AS sum
+FROM UNNEST([]) AS x
+--
+QueryStatement [0-41]
+  Query [0-41]
+    Select [0-41]
+      SelectList [7-20]
+        SelectColumn [7-20]
+          FunctionCall [7-13]
+            PathExpression [7-10]
+              Identifier(SUM) [7-10]
+            PathExpression [11-12]
+              Identifier(x) [11-12]
+          Alias [14-20]
+            Identifier(sum) [17-20]
+      FromClause [21-41]
+        TablePathExpression [26-41]
+          UnnestExpression [26-36]
+            ExpressionWithOptAlias [33-35]
+              ArrayConstructor [33-35]
+          Alias [37-41]
+            Identifier(x) [40-41]
diff --git a/parser/testdata/spanner/array_functions.test b/parser/testdata/spanner/array_functions.test
new file mode 100644
index 0000000..6097004
--- /dev/null
+++ b/parser/testdata/spanner/array_functions.test
@@ -0,0 +1,1595 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT ARRAY
+  (SELECT 1 UNION ALL
+   SELECT 2 UNION ALL
+   SELECT 3) AS new_array
+--
+QueryStatement [0-82]
+  Query [0-82]
+    Select [0-82]
+      SelectList [7-82]
+        SelectColumn [7-82]
+          ExpressionSubquery(modifier=ARRAY) [7-69]
+            Query [16-68]
+              SetOperation(UNION ALL) [16-68]
+                SetOperationMetadataList [25-56]
+                  SetOperationMetadata [25-34]
+                    SetOperationType [25-30]
+                    SetOperationAllOrDistinct [31-34]
+                  SetOperationMetadata [47-56]
+                    SetOperationType [47-52]
+                    SetOperationAllOrDistinct [53-56]
+                Select [16-24]
+                  SelectList [23-24]
+                    SelectColumn [23-24]
+                      IntLiteral(1) [23-24]
+                Select [38-46]
+                  SelectList [45-46]
+                    SelectColumn [45-46]
+                      IntLiteral(2) [45-46]
+                Select [60-68]
+                  SelectList [67-68]
+                    SelectColumn [67-68]
+                      IntLiteral(3) [67-68]
+          Alias [70-82]
+            Identifier(new_array) [73-82]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array
+SELECT
+  ARRAY
+    (SELECT AS STRUCT 1, 2, 3
+     UNION ALL SELECT AS STRUCT 4, 5, 6) AS new_array
+--
+QueryStatement [0-98]
+  Query [0-98]
+    Select [0-98]
+      SelectList [9-98]
+        SelectColumn [9-98]
+          ExpressionSubquery(modifier=ARRAY) [9-85]
+            Query [20-84]
+              SetOperation(UNION ALL) [20-84]
+                SetOperationMetadataList [50-59]
+                  SetOperationMetadata [50-59]
+                    SetOperationType [50-55]
+                    SetOperationAllOrDistinct [56-59]
+                Select [20-44]
+                  SelectAs(as_mode=STRUCT) [27-36]
+                  SelectList [37-44]
+                    SelectColumn [37-38]
+                      IntLiteral(1) [37-38]
+                    SelectColumn [40-41]
+                      IntLiteral(2) [40-41]
+                    SelectColumn [43-44]
+                      IntLiteral(3) [43-44]
+                Select [60-84]
+                  SelectAs(as_mode=STRUCT) [67-76]
+                  SelectList [77-84]
+                    SelectColumn [77-78]
+                      IntLiteral(4) [77-78]
+                    SelectColumn [80-81]
+                      IntLiteral(5) [80-81]
+                    SelectColumn [83-84]
+                      IntLiteral(6) [83-84]
+          Alias [86-98]
+            Identifier(new_array) [89-98]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array
+SELECT ARRAY
+  (SELECT AS STRUCT [1, 2, 3] UNION ALL
+   SELECT AS STRUCT [4, 5, 6]) AS new_array
+--
+QueryStatement [0-96]
+  Query [0-96]
+    Select [0-96]
+      SelectList [7-96]
+        SelectColumn [7-96]
+          ExpressionSubquery(modifier=ARRAY) [7-83]
+            Query [16-82]
+              SetOperation(UNION ALL) [16-82]
+                SetOperationMetadataList [43-52]
+                  SetOperationMetadata [43-52]
+                    SetOperationType [43-48]
+                    SetOperationAllOrDistinct [49-52]
+                Select [16-42]
+                  SelectAs(as_mode=STRUCT) [23-32]
+                  SelectList [33-42]
+                    SelectColumn [33-42]
+                      ArrayConstructor [33-42]
+                        IntLiteral(1) [34-35]
+                        IntLiteral(2) [37-38]
+                        IntLiteral(3) [40-41]
+                Select [56-82]
+                  SelectAs(as_mode=STRUCT) [63-72]
+                  SelectList [73-82]
+                    SelectColumn [73-82]
+                      ArrayConstructor [73-82]
+                        IntLiteral(4) [74-75]
+                        IntLiteral(5) [77-78]
+                        IntLiteral(6) [80-81]
+          Alias [84-96]
+            Identifier(new_array) [87-96]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_concat
+SELECT ARRAY_CONCAT([1, 2], [3, 4], [5, 6]) as count_to_six
+--
+QueryStatement [0-59]
+  Query [0-59]
+    Select [0-59]
+      SelectList [7-59]
+        SelectColumn [7-59]
+          FunctionCall [7-43]
+            PathExpression [7-19]
+              Identifier(ARRAY_CONCAT) [7-19]
+            ArrayConstructor [20-26]
+              IntLiteral(1) [21-22]
+              IntLiteral(2) [24-25]
+            ArrayConstructor [28-34]
+              IntLiteral(3) [29-30]
+              IntLiteral(4) [32-33]
+            ArrayConstructor [36-42]
+              IntLiteral(5) [37-38]
+              IntLiteral(6) [40-41]
+          Alias [44-59]
+            Identifier(count_to_six) [47-59]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_filter
+SELECT
+  ARRAY_FILTER([1 ,2, 3], e -> e > 1) AS a1,
+  ARRAY_FILTER([0, 2, 3], (e, i) -> e > i) AS a2
+--
+QueryStatement [0-100]
+  Query [0-100]
+    Select [0-100]
+      SelectList [9-100]
+        SelectColumn [9-50]
+          FunctionCall [9-44]
+            PathExpression [9-21]
+              Identifier(ARRAY_FILTER) [9-21]
+            ArrayConstructor [22-31]
+              IntLiteral(1) [23-24]
+              IntLiteral(2) [26-27]
+              IntLiteral(3) [29-30]
+            Lambda [33-43]
+              PathExpression [33-34]
+                Identifier(e) [33-34]
+              BinaryExpression(>) [38-43]
+                PathExpression [38-39]
+                  Identifier(e) [38-39]
+                IntLiteral(1) [42-43]
+          Alias [45-50]
+            Identifier(a1) [48-50]
+        SelectColumn [54-100]
+          FunctionCall [54-94]
+            PathExpression [54-66]
+              Identifier(ARRAY_FILTER) [54-66]
+            ArrayConstructor [67-76]
+              IntLiteral(0) [68-69]
+              IntLiteral(2) [71-72]
+              IntLiteral(3) [74-75]
+            Lambda [78-93]
+              StructConstructorWithParens [78-84]
+                PathExpression [79-80]
+                  Identifier(e) [79-80]
+                PathExpression [82-83]
+                  Identifier(i) [82-83]
+              BinaryExpression(>) [88-93]
+                PathExpression [88-89]
+                  Identifier(e) [88-89]
+                PathExpression [92-93]
+                  Identifier(i) [92-93]
+          Alias [95-100]
+            Identifier(a2) [98-100]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_first
+[mode=expression]
+ARRAY_FIRST(array_expression)
+--
+FunctionCall [0-29]
+  PathExpression [0-11]
+    Identifier(ARRAY_FIRST) [0-11]
+  PathExpression [12-28]
+    Identifier(array_expression) [12-28]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_first
+SELECT ARRAY_FIRST(['a','b','c','d']) as first_element
+--
+QueryStatement [0-54]
+  Query [0-54]
+    Select [0-54]
+      SelectList [7-54]
+        SelectColumn [7-54]
+          FunctionCall [7-37]
+            PathExpression [7-18]
+              Identifier(ARRAY_FIRST) [7-18]
+            ArrayConstructor [19-36]
+              StringLiteral [20-23]
+                StringLiteralComponent('a') [20-23]
+              StringLiteral [24-27]
+                StringLiteralComponent('b') [24-27]
+              StringLiteral [28-31]
+                StringLiteralComponent('c') [28-31]
+              StringLiteral [32-35]
+                StringLiteralComponent('d') [32-35]
+          Alias [38-54]
+            Identifier(first_element) [41-54]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_includes_signature1
+[mode=expression]
+ARRAY_INCLUDES(array_to_search, search_value)
+--
+FunctionCall [0-45]
+  PathExpression [0-14]
+    Identifier(ARRAY_INCLUDES) [0-14]
+  PathExpression [15-30]
+    Identifier(array_to_search) [15-30]
+  PathExpression [32-44]
+    Identifier(search_value) [32-44]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_includes_signature1
+SELECT
+  ARRAY_INCLUDES([1, 2, 3], 0) AS a1,
+  ARRAY_INCLUDES([1, 2, 3], 1) AS a2
+--
+QueryStatement [0-81]
+  Query [0-81]
+    Select [0-81]
+      SelectList [9-81]
+        SelectColumn [9-43]
+          FunctionCall [9-37]
+            PathExpression [9-23]
+              Identifier(ARRAY_INCLUDES) [9-23]
+            ArrayConstructor [24-33]
+              IntLiteral(1) [25-26]
+              IntLiteral(2) [28-29]
+              IntLiteral(3) [31-32]
+            IntLiteral(0) [35-36]
+          Alias [38-43]
+            Identifier(a1) [41-43]
+        SelectColumn [47-81]
+          FunctionCall [47-75]
+            PathExpression [47-61]
+              Identifier(ARRAY_INCLUDES) [47-61]
+            ArrayConstructor [62-71]
+              IntLiteral(1) [63-64]
+              IntLiteral(2) [66-67]
+              IntLiteral(3) [69-70]
+            IntLiteral(1) [73-74]
+          Alias [76-81]
+            Identifier(a2) [79-81]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_includes_signature2
+SELECT
+  ARRAY_INCLUDES([1, 2, 3], e -> e > 3) AS a1,
+  ARRAY_INCLUDES([1, 2, 3], e -> e > 0) AS a2
+--
+QueryStatement [0-99]
+  Query [0-99]
+    Select [0-99]
+      SelectList [9-99]
+        SelectColumn [9-52]
+          FunctionCall [9-46]
+            PathExpression [9-23]
+              Identifier(ARRAY_INCLUDES) [9-23]
+            ArrayConstructor [24-33]
+              IntLiteral(1) [25-26]
+              IntLiteral(2) [28-29]
+              IntLiteral(3) [31-32]
+            Lambda [35-45]
+              PathExpression [35-36]
+                Identifier(e) [35-36]
+              BinaryExpression(>) [40-45]
+                PathExpression [40-41]
+                  Identifier(e) [40-41]
+                IntLiteral(3) [44-45]
+          Alias [47-52]
+            Identifier(a1) [50-52]
+        SelectColumn [56-99]
+          FunctionCall [56-93]
+            PathExpression [56-70]
+              Identifier(ARRAY_INCLUDES) [56-70]
+            ArrayConstructor [71-80]
+              IntLiteral(1) [72-73]
+              IntLiteral(2) [75-76]
+              IntLiteral(3) [78-79]
+            Lambda [82-92]
+              PathExpression [82-83]
+                Identifier(e) [82-83]
+              BinaryExpression(>) [87-92]
+                PathExpression [87-88]
+                  Identifier(e) [87-88]
+                IntLiteral(0) [91-92]
+          Alias [94-99]
+            Identifier(a2) [97-99]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_includes_all
+[mode=expression]
+ARRAY_INCLUDES_ALL(array_to_search, search_values)
+--
+FunctionCall [0-50]
+  PathExpression [0-18]
+    Identifier(ARRAY_INCLUDES_ALL) [0-18]
+  PathExpression [19-34]
+    Identifier(array_to_search) [19-34]
+  PathExpression [36-49]
+    Identifier(search_values) [36-49]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_includes_all
+SELECT
+  ARRAY_INCLUDES_ALL([1,2,3,4,5], [3,4,5]) AS a1,
+  ARRAY_INCLUDES_ALL([1,2,3,4,5], [4,5,6]) AS a2
+--
+QueryStatement [0-105]
+  Query [0-105]
+    Select [0-105]
+      SelectList [9-105]
+        SelectColumn [9-55]
+          FunctionCall [9-49]
+            PathExpression [9-27]
+              Identifier(ARRAY_INCLUDES_ALL) [9-27]
+            ArrayConstructor [28-39]
+              IntLiteral(1) [29-30]
+              IntLiteral(2) [31-32]
+              IntLiteral(3) [33-34]
+              IntLiteral(4) [35-36]
+              IntLiteral(5) [37-38]
+            ArrayConstructor [41-48]
+              IntLiteral(3) [42-43]
+              IntLiteral(4) [44-45]
+              IntLiteral(5) [46-47]
+          Alias [50-55]
+            Identifier(a1) [53-55]
+        SelectColumn [59-105]
+          FunctionCall [59-99]
+            PathExpression [59-77]
+              Identifier(ARRAY_INCLUDES_ALL) [59-77]
+            ArrayConstructor [78-89]
+              IntLiteral(1) [79-80]
+              IntLiteral(2) [81-82]
+              IntLiteral(3) [83-84]
+              IntLiteral(4) [85-86]
+              IntLiteral(5) [87-88]
+            ArrayConstructor [91-98]
+              IntLiteral(4) [92-93]
+              IntLiteral(5) [94-95]
+              IntLiteral(6) [96-97]
+          Alias [100-105]
+            Identifier(a2) [103-105]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_includes_any
+[mode=expression]
+ARRAY_INCLUDES_ANY(array_to_search, search_values)
+--
+FunctionCall [0-50]
+  PathExpression [0-18]
+    Identifier(ARRAY_INCLUDES_ANY) [0-18]
+  PathExpression [19-34]
+    Identifier(array_to_search) [19-34]
+  PathExpression [36-49]
+    Identifier(search_values) [36-49]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_includes_any
+SELECT
+  ARRAY_INCLUDES_ANY([1,2,3], [3,4,5]) AS a1,
+  ARRAY_INCLUDES_ANY([1,2,3], [4,5,6]) AS a2
+--
+QueryStatement [0-97]
+  Query [0-97]
+    Select [0-97]
+      SelectList [9-97]
+        SelectColumn [9-51]
+          FunctionCall [9-45]
+            PathExpression [9-27]
+              Identifier(ARRAY_INCLUDES_ANY) [9-27]
+            ArrayConstructor [28-35]
+              IntLiteral(1) [29-30]
+              IntLiteral(2) [31-32]
+              IntLiteral(3) [33-34]
+            ArrayConstructor [37-44]
+              IntLiteral(3) [38-39]
+              IntLiteral(4) [40-41]
+              IntLiteral(5) [42-43]
+          Alias [46-51]
+            Identifier(a1) [49-51]
+        SelectColumn [55-97]
+          FunctionCall [55-91]
+            PathExpression [55-73]
+              Identifier(ARRAY_INCLUDES_ANY) [55-73]
+            ArrayConstructor [74-81]
+              IntLiteral(1) [75-76]
+              IntLiteral(2) [77-78]
+              IntLiteral(3) [79-80]
+            ArrayConstructor [83-90]
+              IntLiteral(4) [84-85]
+              IntLiteral(5) [86-87]
+              IntLiteral(6) [88-89]
+          Alias [92-97]
+            Identifier(a2) [95-97]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_is_distinct
+[mode=expression]
+ARRAY_IS_DISTINCT(value)
+--
+FunctionCall [0-24]
+  PathExpression [0-17]
+    Identifier(ARRAY_IS_DISTINCT) [0-17]
+  PathExpression [18-23]
+    Identifier(value) [18-23]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_is_distinct
+SELECT ARRAY_IS_DISTINCT([1, 2, 3]) AS is_distinct
+--
+QueryStatement [0-50]
+  Query [0-50]
+    Select [0-50]
+      SelectList [7-50]
+        SelectColumn [7-50]
+          FunctionCall [7-35]
+            PathExpression [7-24]
+              Identifier(ARRAY_IS_DISTINCT) [7-24]
+            ArrayConstructor [25-34]
+              IntLiteral(1) [26-27]
+              IntLiteral(2) [29-30]
+              IntLiteral(3) [32-33]
+          Alias [36-50]
+            Identifier(is_distinct) [39-50]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_is_distinct
+SELECT ARRAY_IS_DISTINCT([1, 1, 1]) AS is_distinct
+--
+QueryStatement [0-50]
+  Query [0-50]
+    Select [0-50]
+      SelectList [7-50]
+        SelectColumn [7-50]
+          FunctionCall [7-35]
+            PathExpression [7-24]
+              Identifier(ARRAY_IS_DISTINCT) [7-24]
+            ArrayConstructor [25-34]
+              IntLiteral(1) [26-27]
+              IntLiteral(1) [29-30]
+              IntLiteral(1) [32-33]
+          Alias [36-50]
+            Identifier(is_distinct) [39-50]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_is_distinct
+SELECT ARRAY_IS_DISTINCT([1, 2, NULL]) AS is_distinct
+--
+QueryStatement [0-53]
+  Query [0-53]
+    Select [0-53]
+      SelectList [7-53]
+        SelectColumn [7-53]
+          FunctionCall [7-38]
+            PathExpression [7-24]
+              Identifier(ARRAY_IS_DISTINCT) [7-24]
+            ArrayConstructor [25-37]
+              IntLiteral(1) [26-27]
+              IntLiteral(2) [29-30]
+              NullLiteral(NULL) [32-36]
+          Alias [39-53]
+            Identifier(is_distinct) [42-53]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_is_distinct
+SELECT ARRAY_IS_DISTINCT([1, 1, NULL]) AS is_distinct
+--
+QueryStatement [0-53]
+  Query [0-53]
+    Select [0-53]
+      SelectList [7-53]
+        SelectColumn [7-53]
+          FunctionCall [7-38]
+            PathExpression [7-24]
+              Identifier(ARRAY_IS_DISTINCT) [7-24]
+            ArrayConstructor [25-37]
+              IntLiteral(1) [26-27]
+              IntLiteral(1) [29-30]
+              NullLiteral(NULL) [32-36]
+          Alias [39-53]
+            Identifier(is_distinct) [42-53]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_is_distinct
+SELECT ARRAY_IS_DISTINCT([1, NULL, NULL]) AS is_distinct
+--
+QueryStatement [0-56]
+  Query [0-56]
+    Select [0-56]
+      SelectList [7-56]
+        SelectColumn [7-56]
+          FunctionCall [7-41]
+            PathExpression [7-24]
+              Identifier(ARRAY_IS_DISTINCT) [7-24]
+            ArrayConstructor [25-40]
+              IntLiteral(1) [26-27]
+              NullLiteral(NULL) [29-33]
+              NullLiteral(NULL) [35-39]
+          Alias [42-56]
+            Identifier(is_distinct) [45-56]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_is_distinct
+SELECT ARRAY_IS_DISTINCT([]) AS is_distinct
+--
+QueryStatement [0-43]
+  Query [0-43]
+    Select [0-43]
+      SelectList [7-43]
+        SelectColumn [7-43]
+          FunctionCall [7-28]
+            PathExpression [7-24]
+              Identifier(ARRAY_IS_DISTINCT) [7-24]
+            ArrayConstructor [25-27]
+          Alias [29-43]
+            Identifier(is_distinct) [32-43]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_is_distinct
+SELECT ARRAY_IS_DISTINCT(NULL) AS is_distinct
+--
+QueryStatement [0-45]
+  Query [0-45]
+    Select [0-45]
+      SelectList [7-45]
+        SelectColumn [7-45]
+          FunctionCall [7-30]
+            PathExpression [7-24]
+              Identifier(ARRAY_IS_DISTINCT) [7-24]
+            NullLiteral(NULL) [25-29]
+          Alias [31-45]
+            Identifier(is_distinct) [34-45]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_last
+[mode=expression]
+ARRAY_LAST(array_expression)
+--
+FunctionCall [0-28]
+  PathExpression [0-10]
+    Identifier(ARRAY_LAST) [0-10]
+  PathExpression [11-27]
+    Identifier(array_expression) [11-27]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_last
+SELECT ARRAY_LAST(['a','b','c','d']) as last_element
+--
+QueryStatement [0-52]
+  Query [0-52]
+    Select [0-52]
+      SelectList [7-52]
+        SelectColumn [7-52]
+          FunctionCall [7-36]
+            PathExpression [7-17]
+              Identifier(ARRAY_LAST) [7-17]
+            ArrayConstructor [18-35]
+              StringLiteral [19-22]
+                StringLiteralComponent('a') [19-22]
+              StringLiteral [23-26]
+                StringLiteralComponent('b') [23-26]
+              StringLiteral [27-30]
+                StringLiteralComponent('c') [27-30]
+              StringLiteral [31-34]
+                StringLiteralComponent('d') [31-34]
+          Alias [37-52]
+            Identifier(last_element) [40-52]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_length
+[mode=expression]
+ARRAY_LENGTH(array_expression)
+--
+FunctionCall [0-30]
+  PathExpression [0-12]
+    Identifier(ARRAY_LENGTH) [0-12]
+  PathExpression [13-29]
+    Identifier(array_expression) [13-29]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_length
+SELECT
+  ARRAY_LENGTH(["coffee", NULL, "milk" ]) AS size_a,
+  ARRAY_LENGTH(["cake", "pie"]) AS size_b
+--
+QueryStatement [0-101]
+  Query [0-101]
+    Select [0-101]
+      SelectList [9-101]
+        SelectColumn [9-58]
+          FunctionCall [9-48]
+            PathExpression [9-21]
+              Identifier(ARRAY_LENGTH) [9-21]
+            ArrayConstructor [22-47]
+              StringLiteral [23-31]
+                StringLiteralComponent("coffee") [23-31]
+              NullLiteral(NULL) [33-37]
+              StringLiteral [39-45]
+                StringLiteralComponent("milk") [39-45]
+          Alias [49-58]
+            Identifier(size_a) [52-58]
+        SelectColumn [62-101]
+          FunctionCall [62-91]
+            PathExpression [62-74]
+              Identifier(ARRAY_LENGTH) [62-74]
+            ArrayConstructor [75-90]
+              StringLiteral [76-82]
+                StringLiteralComponent("cake") [76-82]
+              StringLiteral [84-89]
+                StringLiteralComponent("pie") [84-89]
+          Alias [92-101]
+            Identifier(size_b) [95-101]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_max
+[mode=expression]
+ARRAY_MAX(input_array)
+--
+FunctionCall [0-22]
+  PathExpression [0-9]
+    Identifier(ARRAY_MAX) [0-9]
+  PathExpression [10-21]
+    Identifier(input_array) [10-21]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_max
+SELECT ARRAY_MAX([8, 37, NULL, 55, 4]) as max
+--
+QueryStatement [0-45]
+  Query [0-45]
+    Select [0-45]
+      SelectList [7-45]
+        SelectColumn [7-45]
+          FunctionCall [7-38]
+            PathExpression [7-16]
+              Identifier(ARRAY_MAX) [7-16]
+            ArrayConstructor [17-37]
+              IntLiteral(8) [18-19]
+              IntLiteral(37) [21-23]
+              NullLiteral(NULL) [25-29]
+              IntLiteral(55) [31-33]
+              IntLiteral(4) [35-36]
+          Alias [39-45]
+            Identifier(max) [42-45]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_min
+[mode=expression]
+ARRAY_MIN(input_array)
+--
+FunctionCall [0-22]
+  PathExpression [0-9]
+    Identifier(ARRAY_MIN) [0-9]
+  PathExpression [10-21]
+    Identifier(input_array) [10-21]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_min
+SELECT ARRAY_MIN([8, 37, NULL, 4, 55]) as min
+--
+QueryStatement [0-45]
+  Query [0-45]
+    Select [0-45]
+      SelectList [7-45]
+        SelectColumn [7-45]
+          FunctionCall [7-38]
+            PathExpression [7-16]
+              Identifier(ARRAY_MIN) [7-16]
+            ArrayConstructor [17-37]
+              IntLiteral(8) [18-19]
+              IntLiteral(37) [21-23]
+              NullLiteral(NULL) [25-29]
+              IntLiteral(4) [31-32]
+              IntLiteral(55) [34-36]
+          Alias [39-45]
+            Identifier(min) [42-45]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_reverse
+[mode=expression]
+ARRAY_REVERSE(value)
+--
+FunctionCall [0-20]
+  PathExpression [0-13]
+    Identifier(ARRAY_REVERSE) [0-13]
+  PathExpression [14-19]
+    Identifier(value) [14-19]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_reverse
+SELECT ARRAY_REVERSE([1, 2, 3]) AS reverse_arr
+--
+QueryStatement [0-46]
+  Query [0-46]
+    Select [0-46]
+      SelectList [7-46]
+        SelectColumn [7-46]
+          FunctionCall [7-31]
+            PathExpression [7-20]
+              Identifier(ARRAY_REVERSE) [7-20]
+            ArrayConstructor [21-30]
+              IntLiteral(1) [22-23]
+              IntLiteral(2) [25-26]
+              IntLiteral(3) [28-29]
+          Alias [32-46]
+            Identifier(reverse_arr) [35-46]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_slice
+[mode=expression]
+ARRAY_SLICE(array_to_slice, start_offset, end_offset)
+--
+FunctionCall [0-53]
+  PathExpression [0-11]
+    Identifier(ARRAY_SLICE) [0-11]
+  PathExpression [12-26]
+    Identifier(array_to_slice) [12-26]
+  PathExpression [28-40]
+    Identifier(start_offset) [28-40]
+  PathExpression [42-52]
+    Identifier(end_offset) [42-52]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_slice
+SELECT ARRAY_SLICE(['a', 'b', 'c', 'd', 'e'], 1, 3) AS result
+--
+QueryStatement [0-61]
+  Query [0-61]
+    Select [0-61]
+      SelectList [7-61]
+        SelectColumn [7-61]
+          FunctionCall [7-51]
+            PathExpression [7-18]
+              Identifier(ARRAY_SLICE) [7-18]
+            ArrayConstructor [19-44]
+              StringLiteral [20-23]
+                StringLiteralComponent('a') [20-23]
+              StringLiteral [25-28]
+                StringLiteralComponent('b') [25-28]
+              StringLiteral [30-33]
+                StringLiteralComponent('c') [30-33]
+              StringLiteral [35-38]
+                StringLiteralComponent('d') [35-38]
+              StringLiteral [40-43]
+                StringLiteralComponent('e') [40-43]
+            IntLiteral(1) [46-47]
+            IntLiteral(3) [49-50]
+          Alias [52-61]
+            Identifier(result) [55-61]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_slice
+SELECT ARRAY_SLICE(['a', 'b', 'c', 'd', 'e'], -1, 3) AS result
+--
+QueryStatement [0-62]
+  Query [0-62]
+    Select [0-62]
+      SelectList [7-62]
+        SelectColumn [7-62]
+          FunctionCall [7-52]
+            PathExpression [7-18]
+              Identifier(ARRAY_SLICE) [7-18]
+            ArrayConstructor [19-44]
+              StringLiteral [20-23]
+                StringLiteralComponent('a') [20-23]
+              StringLiteral [25-28]
+                StringLiteralComponent('b') [25-28]
+              StringLiteral [30-33]
+                StringLiteralComponent('c') [30-33]
+              StringLiteral [35-38]
+                StringLiteralComponent('d') [35-38]
+              StringLiteral [40-43]
+                StringLiteralComponent('e') [40-43]
+            UnaryExpression(-) [46-48]
+              IntLiteral(1) [47-48]
+            IntLiteral(3) [50-51]
+          Alias [53-62]
+            Identifier(result) [56-62]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_slice
+SELECT ARRAY_SLICE(['a', 'b', 'c', 'd', 'e'], 1, -3) AS result
+--
+QueryStatement [0-62]
+  Query [0-62]
+    Select [0-62]
+      SelectList [7-62]
+        SelectColumn [7-62]
+          FunctionCall [7-52]
+            PathExpression [7-18]
+              Identifier(ARRAY_SLICE) [7-18]
+            ArrayConstructor [19-44]
+              StringLiteral [20-23]
+                StringLiteralComponent('a') [20-23]
+              StringLiteral [25-28]
+                StringLiteralComponent('b') [25-28]
+              StringLiteral [30-33]
+                StringLiteralComponent('c') [30-33]
+              StringLiteral [35-38]
+                StringLiteralComponent('d') [35-38]
+              StringLiteral [40-43]
+                StringLiteralComponent('e') [40-43]
+            IntLiteral(1) [46-47]
+            UnaryExpression(-) [49-51]
+              IntLiteral(3) [50-51]
+          Alias [53-62]
+            Identifier(result) [56-62]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_slice
+SELECT ARRAY_SLICE(['a', 'b', 'c', 'd', 'e'], -1, -3) AS result
+--
+QueryStatement [0-63]
+  Query [0-63]
+    Select [0-63]
+      SelectList [7-63]
+        SelectColumn [7-63]
+          FunctionCall [7-53]
+            PathExpression [7-18]
+              Identifier(ARRAY_SLICE) [7-18]
+            ArrayConstructor [19-44]
+              StringLiteral [20-23]
+                StringLiteralComponent('a') [20-23]
+              StringLiteral [25-28]
+                StringLiteralComponent('b') [25-28]
+              StringLiteral [30-33]
+                StringLiteralComponent('c') [30-33]
+              StringLiteral [35-38]
+                StringLiteralComponent('d') [35-38]
+              StringLiteral [40-43]
+                StringLiteralComponent('e') [40-43]
+            UnaryExpression(-) [46-48]
+              IntLiteral(1) [47-48]
+            UnaryExpression(-) [50-52]
+              IntLiteral(3) [51-52]
+          Alias [54-63]
+            Identifier(result) [57-63]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_slice
+SELECT ARRAY_SLICE(['a', 'b', 'c', 'd', 'e'], -3, -1) AS result
+--
+QueryStatement [0-63]
+  Query [0-63]
+    Select [0-63]
+      SelectList [7-63]
+        SelectColumn [7-63]
+          FunctionCall [7-53]
+            PathExpression [7-18]
+              Identifier(ARRAY_SLICE) [7-18]
+            ArrayConstructor [19-44]
+              StringLiteral [20-23]
+                StringLiteralComponent('a') [20-23]
+              StringLiteral [25-28]
+                StringLiteralComponent('b') [25-28]
+              StringLiteral [30-33]
+                StringLiteralComponent('c') [30-33]
+              StringLiteral [35-38]
+                StringLiteralComponent('d') [35-38]
+              StringLiteral [40-43]
+                StringLiteralComponent('e') [40-43]
+            UnaryExpression(-) [46-48]
+              IntLiteral(3) [47-48]
+            UnaryExpression(-) [50-52]
+              IntLiteral(1) [51-52]
+          Alias [54-63]
+            Identifier(result) [57-63]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_slice
+SELECT ARRAY_SLICE(['a', 'b', 'c', 'd', 'e'], 3, 3) AS result
+--
+QueryStatement [0-61]
+  Query [0-61]
+    Select [0-61]
+      SelectList [7-61]
+        SelectColumn [7-61]
+          FunctionCall [7-51]
+            PathExpression [7-18]
+              Identifier(ARRAY_SLICE) [7-18]
+            ArrayConstructor [19-44]
+              StringLiteral [20-23]
+                StringLiteralComponent('a') [20-23]
+              StringLiteral [25-28]
+                StringLiteralComponent('b') [25-28]
+              StringLiteral [30-33]
+                StringLiteralComponent('c') [30-33]
+              StringLiteral [35-38]
+                StringLiteralComponent('d') [35-38]
+              StringLiteral [40-43]
+                StringLiteralComponent('e') [40-43]
+            IntLiteral(3) [46-47]
+            IntLiteral(3) [49-50]
+          Alias [52-61]
+            Identifier(result) [55-61]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_slice
+SELECT ARRAY_SLICE(['a', 'b', 'c', 'd', 'e'], -3, -3) AS result
+--
+QueryStatement [0-63]
+  Query [0-63]
+    Select [0-63]
+      SelectList [7-63]
+        SelectColumn [7-63]
+          FunctionCall [7-53]
+            PathExpression [7-18]
+              Identifier(ARRAY_SLICE) [7-18]
+            ArrayConstructor [19-44]
+              StringLiteral [20-23]
+                StringLiteralComponent('a') [20-23]
+              StringLiteral [25-28]
+                StringLiteralComponent('b') [25-28]
+              StringLiteral [30-33]
+                StringLiteralComponent('c') [30-33]
+              StringLiteral [35-38]
+                StringLiteralComponent('d') [35-38]
+              StringLiteral [40-43]
+                StringLiteralComponent('e') [40-43]
+            UnaryExpression(-) [46-48]
+              IntLiteral(3) [47-48]
+            UnaryExpression(-) [50-52]
+              IntLiteral(3) [51-52]
+          Alias [54-63]
+            Identifier(result) [57-63]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_slice
+SELECT ARRAY_SLICE(['a', 'b', 'c', 'd', 'e'], 1, 30) AS result
+--
+QueryStatement [0-62]
+  Query [0-62]
+    Select [0-62]
+      SelectList [7-62]
+        SelectColumn [7-62]
+          FunctionCall [7-52]
+            PathExpression [7-18]
+              Identifier(ARRAY_SLICE) [7-18]
+            ArrayConstructor [19-44]
+              StringLiteral [20-23]
+                StringLiteralComponent('a') [20-23]
+              StringLiteral [25-28]
+                StringLiteralComponent('b') [25-28]
+              StringLiteral [30-33]
+                StringLiteralComponent('c') [30-33]
+              StringLiteral [35-38]
+                StringLiteralComponent('d') [35-38]
+              StringLiteral [40-43]
+                StringLiteralComponent('e') [40-43]
+            IntLiteral(1) [46-47]
+            IntLiteral(30) [49-51]
+          Alias [53-62]
+            Identifier(result) [56-62]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_slice
+SELECT ARRAY_SLICE(['a', 'b', 'c', 'd', 'e'], 1, -30) AS result
+--
+QueryStatement [0-63]
+  Query [0-63]
+    Select [0-63]
+      SelectList [7-63]
+        SelectColumn [7-63]
+          FunctionCall [7-53]
+            PathExpression [7-18]
+              Identifier(ARRAY_SLICE) [7-18]
+            ArrayConstructor [19-44]
+              StringLiteral [20-23]
+                StringLiteralComponent('a') [20-23]
+              StringLiteral [25-28]
+                StringLiteralComponent('b') [25-28]
+              StringLiteral [30-33]
+                StringLiteralComponent('c') [30-33]
+              StringLiteral [35-38]
+                StringLiteralComponent('d') [35-38]
+              StringLiteral [40-43]
+                StringLiteralComponent('e') [40-43]
+            IntLiteral(1) [46-47]
+            UnaryExpression(-) [49-52]
+              IntLiteral(30) [50-52]
+          Alias [54-63]
+            Identifier(result) [57-63]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_slice
+SELECT ARRAY_SLICE(['a', 'b', 'c', 'd', 'e'], -30, 30) AS result
+--
+QueryStatement [0-64]
+  Query [0-64]
+    Select [0-64]
+      SelectList [7-64]
+        SelectColumn [7-64]
+          FunctionCall [7-54]
+            PathExpression [7-18]
+              Identifier(ARRAY_SLICE) [7-18]
+            ArrayConstructor [19-44]
+              StringLiteral [20-23]
+                StringLiteralComponent('a') [20-23]
+              StringLiteral [25-28]
+                StringLiteralComponent('b') [25-28]
+              StringLiteral [30-33]
+                StringLiteralComponent('c') [30-33]
+              StringLiteral [35-38]
+                StringLiteralComponent('d') [35-38]
+              StringLiteral [40-43]
+                StringLiteralComponent('e') [40-43]
+            UnaryExpression(-) [46-49]
+              IntLiteral(30) [47-49]
+            IntLiteral(30) [51-53]
+          Alias [55-64]
+            Identifier(result) [58-64]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_slice
+SELECT ARRAY_SLICE(['a', 'b', 'c', 'd', 'e'], -30, -5) AS result
+--
+QueryStatement [0-64]
+  Query [0-64]
+    Select [0-64]
+      SelectList [7-64]
+        SelectColumn [7-64]
+          FunctionCall [7-54]
+            PathExpression [7-18]
+              Identifier(ARRAY_SLICE) [7-18]
+            ArrayConstructor [19-44]
+              StringLiteral [20-23]
+                StringLiteralComponent('a') [20-23]
+              StringLiteral [25-28]
+                StringLiteralComponent('b') [25-28]
+              StringLiteral [30-33]
+                StringLiteralComponent('c') [30-33]
+              StringLiteral [35-38]
+                StringLiteralComponent('d') [35-38]
+              StringLiteral [40-43]
+                StringLiteralComponent('e') [40-43]
+            UnaryExpression(-) [46-49]
+              IntLiteral(30) [47-49]
+            UnaryExpression(-) [51-53]
+              IntLiteral(5) [52-53]
+          Alias [55-64]
+            Identifier(result) [58-64]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_slice
+SELECT ARRAY_SLICE(['a', 'b', 'c', 'd', 'e'], 5, 30) AS result
+--
+QueryStatement [0-62]
+  Query [0-62]
+    Select [0-62]
+      SelectList [7-62]
+        SelectColumn [7-62]
+          FunctionCall [7-52]
+            PathExpression [7-18]
+              Identifier(ARRAY_SLICE) [7-18]
+            ArrayConstructor [19-44]
+              StringLiteral [20-23]
+                StringLiteralComponent('a') [20-23]
+              StringLiteral [25-28]
+                StringLiteralComponent('b') [25-28]
+              StringLiteral [30-33]
+                StringLiteralComponent('c') [30-33]
+              StringLiteral [35-38]
+                StringLiteralComponent('d') [35-38]
+              StringLiteral [40-43]
+                StringLiteralComponent('e') [40-43]
+            IntLiteral(5) [46-47]
+            IntLiteral(30) [49-51]
+          Alias [53-62]
+            Identifier(result) [56-62]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_slice
+SELECT ARRAY_SLICE(['a', 'b', 'c', 'd', 'e'], 1, NULL) AS result
+--
+QueryStatement [0-64]
+  Query [0-64]
+    Select [0-64]
+      SelectList [7-64]
+        SelectColumn [7-64]
+          FunctionCall [7-54]
+            PathExpression [7-18]
+              Identifier(ARRAY_SLICE) [7-18]
+            ArrayConstructor [19-44]
+              StringLiteral [20-23]
+                StringLiteralComponent('a') [20-23]
+              StringLiteral [25-28]
+                StringLiteralComponent('b') [25-28]
+              StringLiteral [30-33]
+                StringLiteralComponent('c') [30-33]
+              StringLiteral [35-38]
+                StringLiteralComponent('d') [35-38]
+              StringLiteral [40-43]
+                StringLiteralComponent('e') [40-43]
+            IntLiteral(1) [46-47]
+            NullLiteral(NULL) [49-53]
+          Alias [55-64]
+            Identifier(result) [58-64]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_slice
+SELECT ARRAY_SLICE(['a', 'b', NULL, 'd', 'e'], 1, 3) AS result
+--
+QueryStatement [0-62]
+  Query [0-62]
+    Select [0-62]
+      SelectList [7-62]
+        SelectColumn [7-62]
+          FunctionCall [7-52]
+            PathExpression [7-18]
+              Identifier(ARRAY_SLICE) [7-18]
+            ArrayConstructor [19-45]
+              StringLiteral [20-23]
+                StringLiteralComponent('a') [20-23]
+              StringLiteral [25-28]
+                StringLiteralComponent('b') [25-28]
+              NullLiteral(NULL) [30-34]
+              StringLiteral [36-39]
+                StringLiteralComponent('d') [36-39]
+              StringLiteral [41-44]
+                StringLiteralComponent('e') [41-44]
+            IntLiteral(1) [47-48]
+            IntLiteral(3) [50-51]
+          Alias [53-62]
+            Identifier(result) [56-62]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_to_string
+SELECT ARRAY_TO_STRING(['coffee', 'tea', 'milk', NULL], '--', 'MISSING') AS text
+--
+QueryStatement [0-80]
+  Query [0-80]
+    Select [0-80]
+      SelectList [7-80]
+        SelectColumn [7-80]
+          FunctionCall [7-72]
+            PathExpression [7-22]
+              Identifier(ARRAY_TO_STRING) [7-22]
+            ArrayConstructor [23-54]
+              StringLiteral [24-32]
+                StringLiteralComponent('coffee') [24-32]
+              StringLiteral [34-39]
+                StringLiteralComponent('tea') [34-39]
+              StringLiteral [41-47]
+                StringLiteralComponent('milk') [41-47]
+              NullLiteral(NULL) [49-53]
+            StringLiteral [56-60]
+              StringLiteralComponent('--') [56-60]
+            StringLiteral [62-71]
+              StringLiteralComponent('MISSING') [62-71]
+          Alias [73-80]
+            Identifier(text) [76-80]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_to_string
+SELECT ARRAY_TO_STRING(['cake', 'pie', NULL], '--', 'MISSING') AS text
+--
+QueryStatement [0-70]
+  Query [0-70]
+    Select [0-70]
+      SelectList [7-70]
+        SelectColumn [7-70]
+          FunctionCall [7-62]
+            PathExpression [7-22]
+              Identifier(ARRAY_TO_STRING) [7-22]
+            ArrayConstructor [23-44]
+              StringLiteral [24-30]
+                StringLiteralComponent('cake') [24-30]
+              StringLiteral [32-37]
+                StringLiteralComponent('pie') [32-37]
+              NullLiteral(NULL) [39-43]
+            StringLiteral [46-50]
+              StringLiteralComponent('--') [46-50]
+            StringLiteral [52-61]
+              StringLiteralComponent('MISSING') [52-61]
+          Alias [63-70]
+            Identifier(text) [66-70]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_to_string
+SELECT ARRAY_TO_STRING([b'prefix', b'middle', b'suffix', b'\x00'], b'--') AS data
+--
+QueryStatement [0-81]
+  Query [0-81]
+    Select [0-81]
+      SelectList [7-81]
+        SelectColumn [7-81]
+          FunctionCall [7-73]
+            PathExpression [7-22]
+              Identifier(ARRAY_TO_STRING) [7-22]
+            ArrayConstructor [23-65]
+              BytesLiteral [24-33]
+                BytesLiteralComponent(b'prefix') [24-33]
+              BytesLiteral [35-44]
+                BytesLiteralComponent(b'middle') [35-44]
+              BytesLiteral [46-55]
+                BytesLiteralComponent(b'suffix') [46-55]
+              BytesLiteral [57-64]
+                BytesLiteralComponent(b'\x00') [57-64]
+            BytesLiteral [67-72]
+              BytesLiteralComponent(b'--') [67-72]
+          Alias [74-81]
+            Identifier(data) [77-81]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#array_transform
+SELECT
+  ARRAY_TRANSFORM([1, 4, 3], e -> e + 1) AS a1,
+  ARRAY_TRANSFORM([1, 4, 3], (e, i) -> e + i) AS a2
+--
+QueryStatement [0-106]
+  Query [0-106]
+    Select [0-106]
+      SelectList [9-106]
+        SelectColumn [9-53]
+          FunctionCall [9-47]
+            PathExpression [9-24]
+              Identifier(ARRAY_TRANSFORM) [9-24]
+            ArrayConstructor [25-34]
+              IntLiteral(1) [26-27]
+              IntLiteral(4) [29-30]
+              IntLiteral(3) [32-33]
+            Lambda [36-46]
+              PathExpression [36-37]
+                Identifier(e) [36-37]
+              BinaryExpression(+) [41-46]
+                PathExpression [41-42]
+                  Identifier(e) [41-42]
+                IntLiteral(1) [45-46]
+          Alias [48-53]
+            Identifier(a1) [51-53]
+        SelectColumn [57-106]
+          FunctionCall [57-100]
+            PathExpression [57-72]
+              Identifier(ARRAY_TRANSFORM) [57-72]
+            ArrayConstructor [73-82]
+              IntLiteral(1) [74-75]
+              IntLiteral(4) [77-78]
+              IntLiteral(3) [80-81]
+            Lambda [84-99]
+              StructConstructorWithParens [84-90]
+                PathExpression [85-86]
+                  Identifier(e) [85-86]
+                PathExpression [88-89]
+                  Identifier(i) [88-89]
+              BinaryExpression(+) [94-99]
+                PathExpression [94-95]
+                  Identifier(e) [94-95]
+                PathExpression [98-99]
+                  Identifier(i) [98-99]
+          Alias [101-106]
+            Identifier(a2) [104-106]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#generate_array
+SELECT GENERATE_ARRAY(1, 5) AS example_array
+--
+QueryStatement [0-44]
+  Query [0-44]
+    Select [0-44]
+      SelectList [7-44]
+        SelectColumn [7-44]
+          FunctionCall [7-27]
+            PathExpression [7-21]
+              Identifier(GENERATE_ARRAY) [7-21]
+            IntLiteral(1) [22-23]
+            IntLiteral(5) [25-26]
+          Alias [28-44]
+            Identifier(example_array) [31-44]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#generate_array
+SELECT GENERATE_ARRAY(0, 10, 3) AS example_array
+--
+QueryStatement [0-48]
+  Query [0-48]
+    Select [0-48]
+      SelectList [7-48]
+        SelectColumn [7-48]
+          FunctionCall [7-31]
+            PathExpression [7-21]
+              Identifier(GENERATE_ARRAY) [7-21]
+            IntLiteral(0) [22-23]
+            IntLiteral(10) [25-27]
+            IntLiteral(3) [29-30]
+          Alias [32-48]
+            Identifier(example_array) [35-48]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#generate_array
+SELECT GENERATE_ARRAY(10, 0, -3) AS example_array
+--
+QueryStatement [0-49]
+  Query [0-49]
+    Select [0-49]
+      SelectList [7-49]
+        SelectColumn [7-49]
+          FunctionCall [7-32]
+            PathExpression [7-21]
+              Identifier(GENERATE_ARRAY) [7-21]
+            IntLiteral(10) [22-24]
+            IntLiteral(0) [26-27]
+            UnaryExpression(-) [29-31]
+              IntLiteral(3) [30-31]
+          Alias [33-49]
+            Identifier(example_array) [36-49]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#generate_array
+SELECT GENERATE_ARRAY(4, 4, 10) AS example_array
+--
+QueryStatement [0-48]
+  Query [0-48]
+    Select [0-48]
+      SelectList [7-48]
+        SelectColumn [7-48]
+          FunctionCall [7-31]
+            PathExpression [7-21]
+              Identifier(GENERATE_ARRAY) [7-21]
+            IntLiteral(4) [22-23]
+            IntLiteral(4) [25-26]
+            IntLiteral(10) [28-30]
+          Alias [32-48]
+            Identifier(example_array) [35-48]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#generate_array
+SELECT GENERATE_ARRAY(10, 0, 3) AS example_array
+--
+QueryStatement [0-48]
+  Query [0-48]
+    Select [0-48]
+      SelectList [7-48]
+        SelectColumn [7-48]
+          FunctionCall [7-31]
+            PathExpression [7-21]
+              Identifier(GENERATE_ARRAY) [7-21]
+            IntLiteral(10) [22-24]
+            IntLiteral(0) [26-27]
+            IntLiteral(3) [29-30]
+          Alias [32-48]
+            Identifier(example_array) [35-48]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#generate_array
+SELECT GENERATE_ARRAY(5, NULL, 1) AS example_array
+--
+QueryStatement [0-50]
+  Query [0-50]
+    Select [0-50]
+      SelectList [7-50]
+        SelectColumn [7-50]
+          FunctionCall [7-33]
+            PathExpression [7-21]
+              Identifier(GENERATE_ARRAY) [7-21]
+            IntLiteral(5) [22-23]
+            NullLiteral(NULL) [25-29]
+            IntLiteral(1) [31-32]
+          Alias [34-50]
+            Identifier(example_array) [37-50]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#generate_array
+SELECT GENERATE_ARRAY(start, 5) AS example_array
+FROM UNNEST([3, 4, 5]) AS start
+--
+QueryStatement [0-80]
+  Query [0-80]
+    Select [0-80]
+      SelectList [7-48]
+        SelectColumn [7-48]
+          FunctionCall [7-31]
+            PathExpression [7-21]
+              Identifier(GENERATE_ARRAY) [7-21]
+            PathExpression [22-27]
+              Identifier(start) [22-27]
+            IntLiteral(5) [29-30]
+          Alias [32-48]
+            Identifier(example_array) [35-48]
+      FromClause [49-80]
+        TablePathExpression [54-80]
+          UnnestExpression [54-71]
+            ExpressionWithOptAlias [61-70]
+              ArrayConstructor [61-70]
+                IntLiteral(3) [62-63]
+                IntLiteral(4) [65-66]
+                IntLiteral(5) [68-69]
+          Alias [72-80]
+            Identifier(start) [75-80]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#generate_date_array
+SELECT GENERATE_DATE_ARRAY('2016-10-05', '2016-10-08') AS example
+--
+QueryStatement [0-65]
+  Query [0-65]
+    Select [0-65]
+      SelectList [7-65]
+        SelectColumn [7-65]
+          FunctionCall [7-54]
+            PathExpression [7-26]
+              Identifier(GENERATE_DATE_ARRAY) [7-26]
+            StringLiteral [27-39]
+              StringLiteralComponent('2016-10-05') [27-39]
+            StringLiteral [41-53]
+              StringLiteralComponent('2016-10-08') [41-53]
+          Alias [55-65]
+            Identifier(example) [58-65]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#generate_date_array
+SELECT GENERATE_DATE_ARRAY(
+ '2016-10-05', '2016-10-09', INTERVAL 2 DAY) AS example
+--
+QueryStatement [0-83]
+  Query [0-83]
+    Select [0-83]
+      SelectList [7-83]
+        SelectColumn [7-83]
+          FunctionCall [7-72]
+            PathExpression [7-26]
+              Identifier(GENERATE_DATE_ARRAY) [7-26]
+            StringLiteral [29-41]
+              StringLiteralComponent('2016-10-05') [29-41]
+            StringLiteral [43-55]
+              StringLiteralComponent('2016-10-09') [43-55]
+            IntervalExpr [57-71]
+              IntLiteral(2) [66-67]
+              Identifier(DAY) [68-71]
+          Alias [73-83]
+            Identifier(example) [76-83]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#generate_date_array
+SELECT GENERATE_DATE_ARRAY('2016-10-05',
+  '2016-10-01', INTERVAL -3 DAY) AS example
+--
+QueryStatement [0-84]
+  Query [0-84]
+    Select [0-84]
+      SelectList [7-84]
+        SelectColumn [7-84]
+          FunctionCall [7-73]
+            PathExpression [7-26]
+              Identifier(GENERATE_DATE_ARRAY) [7-26]
+            StringLiteral [27-39]
+              StringLiteralComponent('2016-10-05') [27-39]
+            StringLiteral [43-55]
+              StringLiteralComponent('2016-10-01') [43-55]
+            IntervalExpr [57-72]
+              UnaryExpression(-) [66-68]
+                IntLiteral(3) [67-68]
+              Identifier(DAY) [69-72]
+          Alias [74-84]
+            Identifier(example) [77-84]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#generate_date_array
+SELECT GENERATE_DATE_ARRAY('2016-10-05',
+  '2016-10-05', INTERVAL 8 DAY) AS example
+--
+QueryStatement [0-83]
+  Query [0-83]
+    Select [0-83]
+      SelectList [7-83]
+        SelectColumn [7-83]
+          FunctionCall [7-72]
+            PathExpression [7-26]
+              Identifier(GENERATE_DATE_ARRAY) [7-26]
+            StringLiteral [27-39]
+              StringLiteralComponent('2016-10-05') [27-39]
+            StringLiteral [43-55]
+              StringLiteralComponent('2016-10-05') [43-55]
+            IntervalExpr [57-71]
+              IntLiteral(8) [66-67]
+              Identifier(DAY) [68-71]
+          Alias [73-83]
+            Identifier(example) [76-83]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#generate_date_array
+SELECT GENERATE_DATE_ARRAY('2016-10-05',
+  '2016-10-01', INTERVAL 1 DAY) AS example
+--
+QueryStatement [0-83]
+  Query [0-83]
+    Select [0-83]
+      SelectList [7-83]
+        SelectColumn [7-83]
+          FunctionCall [7-72]
+            PathExpression [7-26]
+              Identifier(GENERATE_DATE_ARRAY) [7-26]
+            StringLiteral [27-39]
+              StringLiteralComponent('2016-10-05') [27-39]
+            StringLiteral [43-55]
+              StringLiteralComponent('2016-10-01') [43-55]
+            IntervalExpr [57-71]
+              IntLiteral(1) [66-67]
+              Identifier(DAY) [68-71]
+          Alias [73-83]
+            Identifier(example) [76-83]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#generate_date_array
+SELECT GENERATE_DATE_ARRAY('2016-10-05', NULL) AS example
+--
+QueryStatement [0-57]
+  Query [0-57]
+    Select [0-57]
+      SelectList [7-57]
+        SelectColumn [7-57]
+          FunctionCall [7-46]
+            PathExpression [7-26]
+              Identifier(GENERATE_DATE_ARRAY) [7-26]
+            StringLiteral [27-39]
+              StringLiteralComponent('2016-10-05') [27-39]
+            NullLiteral(NULL) [41-45]
+          Alias [47-57]
+            Identifier(example) [50-57]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#generate_date_array
+SELECT GENERATE_DATE_ARRAY('2016-01-01',
+  '2016-12-31', INTERVAL 2 MONTH) AS example
+--
+QueryStatement [0-85]
+  Query [0-85]
+    Select [0-85]
+      SelectList [7-85]
+        SelectColumn [7-85]
+          FunctionCall [7-74]
+            PathExpression [7-26]
+              Identifier(GENERATE_DATE_ARRAY) [7-26]
+            StringLiteral [27-39]
+              StringLiteralComponent('2016-01-01') [27-39]
+            StringLiteral [43-55]
+              StringLiteralComponent('2016-12-31') [43-55]
+            IntervalExpr [57-73]
+              IntLiteral(2) [66-67]
+              Identifier(MONTH) [68-73]
+          Alias [75-85]
+            Identifier(example) [78-85]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/array_functions#generate_date_array
+SELECT GENERATE_DATE_ARRAY(date_start, date_end, INTERVAL 1 WEEK) AS date_range
+FROM (
+  SELECT DATE '2016-01-01' AS date_start, DATE '2016-01-31' AS date_end
+  UNION ALL SELECT DATE "2016-04-01", DATE "2016-04-30"
+  UNION ALL SELECT DATE "2016-07-01", DATE "2016-07-31"
+  UNION ALL SELECT DATE "2016-10-01", DATE "2016-10-31"
+) AS items
+--
+QueryStatement [0-337]
+  Query [0-337]
+    Select [0-337]
+      SelectList [7-79]
+        SelectColumn [7-79]
+          FunctionCall [7-65]
+            PathExpression [7-26]
+              Identifier(GENERATE_DATE_ARRAY) [7-26]
+            PathExpression [27-37]
+              Identifier(date_start) [27-37]
+            PathExpression [39-47]
+              Identifier(date_end) [39-47]
+            IntervalExpr [49-64]
+              IntLiteral(1) [58-59]
+              Identifier(WEEK) [60-64]
+          Alias [66-79]
+            Identifier(date_range) [69-79]
+      FromClause [80-337]
+        TableSubquery [85-337]
+          Query [89-326]
+            SetOperation(UNION ALL) [89-326]
+              SetOperationMetadataList [161-282]
+                SetOperationMetadata [161-170]
+                  SetOperationType [161-166]
+                  SetOperationAllOrDistinct [167-170]
+                SetOperationMetadata [217-226]
+                  SetOperationType [217-222]
+                  SetOperationAllOrDistinct [223-226]
+                SetOperationMetadata [273-282]
+                  SetOperationType [273-278]
+                  SetOperationAllOrDistinct [279-282]
+              Select [89-158]
+                SelectList [96-158]
+                  SelectColumn [96-127]
+                    DateOrTimeLiteral(TYPE_DATE) [96-113]
+                      StringLiteral [101-113]
+                        StringLiteralComponent('2016-01-01') [101-113]
+                    Alias [114-127]
+                      Identifier(date_start) [117-127]
+                  SelectColumn [129-158]
+                    DateOrTimeLiteral(TYPE_DATE) [129-146]
+                      StringLiteral [134-146]
+                        StringLiteralComponent('2016-01-31') [134-146]
+                    Alias [147-158]
+                      Identifier(date_end) [150-158]
+              Select [171-214]
+                SelectList [178-214]
+                  SelectColumn [178-195]
+                    DateOrTimeLiteral(TYPE_DATE) [178-195]
+                      StringLiteral [183-195]
+                        StringLiteralComponent("2016-04-01") [183-195]
+                  SelectColumn [197-214]
+                    DateOrTimeLiteral(TYPE_DATE) [197-214]
+                      StringLiteral [202-214]
+                        StringLiteralComponent("2016-04-30") [202-214]
+              Select [227-270]
+                SelectList [234-270]
+                  SelectColumn [234-251]
+                    DateOrTimeLiteral(TYPE_DATE) [234-251]
+                      StringLiteral [239-251]
+                        StringLiteralComponent("2016-07-01") [239-251]
+                  SelectColumn [253-270]
+                    DateOrTimeLiteral(TYPE_DATE) [253-270]
+                      StringLiteral [258-270]
+                        StringLiteralComponent("2016-07-31") [258-270]
+              Select [283-326]
+                SelectList [290-326]
+                  SelectColumn [290-307]
+                    DateOrTimeLiteral(TYPE_DATE) [290-307]
+                      StringLiteral [295-307]
+                        StringLiteralComponent("2016-10-01") [295-307]
+                  SelectColumn [309-326]
+                    DateOrTimeLiteral(TYPE_DATE) [309-326]
+                      StringLiteral [314-326]
+                        StringLiteralComponent("2016-10-31") [314-326]
+          Alias [329-337]
+            Identifier(items) [332-337]
diff --git a/parser/testdata/spanner/bit_functions.test b/parser/testdata/spanner/bit_functions.test
new file mode 100644
index 0000000..869e4de
--- /dev/null
+++ b/parser/testdata/spanner/bit_functions.test
@@ -0,0 +1,183 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/bit_functions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/bit_functions#bit_count
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+BIT_COUNT(expression)
+--
+FunctionCall [0-21]
+  PathExpression [0-9]
+    Identifier(BIT_COUNT) [0-9]
+  PathExpression [10-20]
+    Identifier(expression) [10-20]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/bit_functions#bit_count
+SELECT a, BIT_COUNT(a) AS a_bits, FORMAT("%T", b) as b, BIT_COUNT(b) AS b_bits
+FROM UNNEST([
+  STRUCT(0 AS a, b'' AS b), (0, b'\x00'), (5, b'\x05'), (8, b'\x00\x08'),
+  (0xFFFF, b'\xFF\xFF'), (-2, b'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE'),
+  (-1, b'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF'),
+  (NULL, b'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF')
+]) AS x
+--
+QueryStatement [0-341]
+  Query [0-341]
+    Select [0-341]
+      SelectList [7-78]
+        SelectColumn [7-8]
+          PathExpression [7-8]
+            Identifier(a) [7-8]
+        SelectColumn [10-32]
+          FunctionCall [10-22]
+            PathExpression [10-19]
+              Identifier(BIT_COUNT) [10-19]
+            PathExpression [20-21]
+              Identifier(a) [20-21]
+          Alias [23-32]
+            Identifier(a_bits) [26-32]
+        SelectColumn [34-54]
+          FunctionCall [34-49]
+            PathExpression [34-40]
+              Identifier(FORMAT) [34-40]
+            StringLiteral [41-45]
+              StringLiteralComponent("%T") [41-45]
+            PathExpression [47-48]
+              Identifier(b) [47-48]
+          Alias [50-54]
+            Identifier(b) [53-54]
+        SelectColumn [56-78]
+          FunctionCall [56-68]
+            PathExpression [56-65]
+              Identifier(BIT_COUNT) [56-65]
+            PathExpression [66-67]
+              Identifier(b) [66-67]
+          Alias [69-78]
+            Identifier(b_bits) [72-78]
+      FromClause [79-341]
+        TablePathExpression [84-341]
+          UnnestExpression [84-336]
+            ExpressionWithOptAlias [91-335]
+              ArrayConstructor [91-335]
+                StructConstructorWithKeyword [95-119]
+                  StructConstructorArg [102-108]
+                    IntLiteral(0) [102-103]
+                    Alias [104-108]
+                      Identifier(a) [107-108]
+                  StructConstructorArg [110-118]
+                    BytesLiteral [110-113]
+                      BytesLiteralComponent(b'') [110-113]
+                    Alias [114-118]
+                      Identifier(b) [117-118]
+                StructConstructorWithParens [121-133]
+                  IntLiteral(0) [122-123]
+                  BytesLiteral [125-132]
+                    BytesLiteralComponent(b'\x00') [125-132]
+                StructConstructorWithParens [135-147]
+                  IntLiteral(5) [136-137]
+                  BytesLiteral [139-146]
+                    BytesLiteralComponent(b'\x05') [139-146]
+                StructConstructorWithParens [149-165]
+                  IntLiteral(8) [150-151]
+                  BytesLiteral [153-164]
+                    BytesLiteralComponent(b'\x00\x08') [153-164]
+                StructConstructorWithParens [169-190]
+                  IntLiteral(0xFFFF) [170-176]
+                  BytesLiteral [178-189]
+                    BytesLiteralComponent(b'\xFF\xFF') [178-189]
+                StructConstructorWithParens [192-233]
+                  UnaryExpression(-) [193-195]
+                    IntLiteral(2) [194-195]
+                  BytesLiteral [197-232]
+                    BytesLiteralComponent(b'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE') [197-232]
+                StructConstructorWithParens [237-278]
+                  UnaryExpression(-) [238-240]
+                    IntLiteral(1) [239-240]
+                  BytesLiteral [242-277]
+                    BytesLiteralComponent(b'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF') [242-277]
+                StructConstructorWithParens [282-333]
+                  NullLiteral(NULL) [283-287]
+                  BytesLiteral [289-332]
+                    BytesLiteralComponent(b'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF') [289-332]
+          Alias [337-341]
+            Identifier(x) [340-341]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/bit_functions#bit_reverse
+[mode=expression]
+BIT_REVERSE(value, preserve_sign)
+--
+FunctionCall [0-33]
+  PathExpression [0-11]
+    Identifier(BIT_REVERSE) [0-11]
+  PathExpression [12-17]
+    Identifier(value) [12-17]
+  PathExpression [19-32]
+    Identifier(preserve_sign) [19-32]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/bit_functions#bit_reverse
+SELECT BIT_REVERSE(100, true) AS results
+--
+QueryStatement [0-40]
+  Query [0-40]
+    Select [0-40]
+      SelectList [7-40]
+        SelectColumn [7-40]
+          FunctionCall [7-29]
+            PathExpression [7-18]
+              Identifier(BIT_REVERSE) [7-18]
+            IntLiteral(100) [19-22]
+            BooleanLiteral(true) [24-28]
+          Alias [30-40]
+            Identifier(results) [33-40]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/bit_functions#bit_reverse
+SELECT BIT_REVERSE(100, false) AS results
+--
+QueryStatement [0-41]
+  Query [0-41]
+    Select [0-41]
+      SelectList [7-41]
+        SelectColumn [7-41]
+          FunctionCall [7-30]
+            PathExpression [7-18]
+              Identifier(BIT_REVERSE) [7-18]
+            IntLiteral(100) [19-22]
+            BooleanLiteral(false) [24-29]
+          Alias [31-41]
+            Identifier(results) [34-41]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/bit_functions#bit_reverse
+SELECT BIT_REVERSE(-100, true) AS results
+--
+QueryStatement [0-41]
+  Query [0-41]
+    Select [0-41]
+      SelectList [7-41]
+        SelectColumn [7-41]
+          FunctionCall [7-30]
+            PathExpression [7-18]
+              Identifier(BIT_REVERSE) [7-18]
+            UnaryExpression(-) [19-23]
+              IntLiteral(100) [20-23]
+            BooleanLiteral(true) [25-29]
+          Alias [31-41]
+            Identifier(results) [34-41]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/bit_functions#bit_reverse
+SELECT BIT_REVERSE(-100, false) AS results
+--
+QueryStatement [0-42]
+  Query [0-42]
+    Select [0-42]
+      SelectList [7-42]
+        SelectColumn [7-42]
+          FunctionCall [7-31]
+            PathExpression [7-18]
+              Identifier(BIT_REVERSE) [7-18]
+            UnaryExpression(-) [19-23]
+              IntLiteral(100) [20-23]
+            BooleanLiteral(false) [25-30]
+          Alias [32-42]
+            Identifier(results) [35-42]
diff --git a/parser/testdata/spanner/collation-concepts.test b/parser/testdata/spanner/collation-concepts.test
new file mode 100644
index 0000000..7d8c052
--- /dev/null
+++ b/parser/testdata/spanner/collation-concepts.test
@@ -0,0 +1,65 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/collation-concepts
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/collation-concepts#collation_specification_example
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT Place
+FROM Locations
+ORDER BY Place COLLATE 'und:ci'
+--
+QueryStatement [0-59]
+  Query [0-59]
+    Select [0-27]
+      SelectList [7-12]
+        SelectColumn [7-12]
+          PathExpression [7-12]
+            Identifier(Place) [7-12]
+      FromClause [13-27]
+        TablePathExpression [18-27]
+          PathExpression [18-27]
+            Identifier(Locations) [18-27]
+    OrderBy [28-59]
+      OrderingExpression(ASC) [37-59]
+        PathExpression [37-42]
+          Identifier(Place) [37-42]
+        Collate [43-59]
+          StringLiteral [51-59]
+            StringLiteralComponent('und:ci') [51-59]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/collation-concepts#caveats
+SELECT *
+FROM UNNEST([
+  'oran\u2060ge1',
+  '\u2060orange2',
+  'orange3'
+]) AS fruit
+ORDER BY fruit COLLATE 'und'
+--
+QueryStatement [0-113]
+  Query [0-113]
+    Select [0-84]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-84]
+        TablePathExpression [14-84]
+          UnnestExpression [14-75]
+            ExpressionWithOptAlias [21-74]
+              ArrayConstructor [21-74]
+                StringLiteral [25-40]
+                  StringLiteralComponent('oran\u2060ge1') [25-40]
+                StringLiteral [44-59]
+                  StringLiteralComponent('\u2060orange2') [44-59]
+                StringLiteral [63-72]
+                  StringLiteralComponent('orange3') [63-72]
+          Alias [76-84]
+            Identifier(fruit) [79-84]
+    OrderBy [85-113]
+      OrderingExpression(ASC) [94-113]
+        PathExpression [94-99]
+          Identifier(fruit) [94-99]
+        Collate [100-113]
+          StringLiteral [108-113]
+            StringLiteralComponent('und') [108-113]
diff --git a/parser/testdata/spanner/compression-functions.test b/parser/testdata/spanner/compression-functions.test
new file mode 100644
index 0000000..d1e69c9
--- /dev/null
+++ b/parser/testdata/spanner/compression-functions.test
@@ -0,0 +1,212 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/compression-functions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/compression-functions#zstd_compress
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+ZSTD_COMPRESS(string_or_bytes_value, level => 3)
+--
+FunctionCall [0-48]
+  PathExpression [0-13]
+    Identifier(ZSTD_COMPRESS) [0-13]
+  PathExpression [14-35]
+    Identifier(string_or_bytes_value) [14-35]
+  NamedArgument [37-47]
+    Identifier(level) [37-42]
+    IntLiteral(3) [46-47]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/compression-functions#zstd_compress
+SELECT ZSTD_COMPRESS('string_value') AS result
+--
+QueryStatement [0-46]
+  Query [0-46]
+    Select [0-46]
+      SelectList [7-46]
+        SelectColumn [7-46]
+          FunctionCall [7-36]
+            PathExpression [7-20]
+              Identifier(ZSTD_COMPRESS) [7-20]
+            StringLiteral [21-35]
+              StringLiteralComponent('string_value') [21-35]
+          Alias [37-46]
+            Identifier(result) [40-46]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/compression-functions#zstd_compress
+SELECT ZSTD_COMPRESS(b'bytes_value', level => 1)
+--
+QueryStatement [0-48]
+  Query [0-48]
+    Select [0-48]
+      SelectList [7-48]
+        SelectColumn [7-48]
+          FunctionCall [7-48]
+            PathExpression [7-20]
+              Identifier(ZSTD_COMPRESS) [7-20]
+            BytesLiteral [21-35]
+              BytesLiteralComponent(b'bytes_value') [21-35]
+            NamedArgument [37-47]
+              Identifier(level) [37-42]
+              IntLiteral(1) [46-47]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/compression-functions#zstd_compress
+SELECT ZSTD_COMPRESS(NULL) AS result
+--
+QueryStatement [0-36]
+  Query [0-36]
+    Select [0-36]
+      SelectList [7-36]
+        SelectColumn [7-36]
+          FunctionCall [7-26]
+            PathExpression [7-20]
+              Identifier(ZSTD_COMPRESS) [7-20]
+            NullLiteral(NULL) [21-25]
+          Alias [27-36]
+            Identifier(result) [30-36]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/compression-functions#zstd_decompress_to_bytes
+[mode=expression]
+ZSTD_DECOMPRESS_TO_BYTES(bytes_value, size_limit => 1024 * 1024 * 1024)
+--
+FunctionCall [0-71]
+  PathExpression [0-24]
+    Identifier(ZSTD_DECOMPRESS_TO_BYTES) [0-24]
+  PathExpression [25-36]
+    Identifier(bytes_value) [25-36]
+  NamedArgument [38-70]
+    Identifier(size_limit) [38-48]
+    BinaryExpression(*) [52-70]
+      BinaryExpression(*) [52-63]
+        IntLiteral(1024) [52-56]
+        IntLiteral(1024) [59-63]
+      IntLiteral(1024) [66-70]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/compression-functions#zstd_decompress_to_bytes
+SELECT ZSTD_DECOMPRESS_TO_BYTES(ZSTD_COMPRESS(b'bytes')) AS result
+--
+QueryStatement [0-66]
+  Query [0-66]
+    Select [0-66]
+      SelectList [7-66]
+        SelectColumn [7-66]
+          FunctionCall [7-56]
+            PathExpression [7-31]
+              Identifier(ZSTD_DECOMPRESS_TO_BYTES) [7-31]
+            FunctionCall [32-55]
+              PathExpression [32-45]
+                Identifier(ZSTD_COMPRESS) [32-45]
+              BytesLiteral [46-54]
+                BytesLiteralComponent(b'bytes') [46-54]
+          Alias [57-66]
+            Identifier(result) [60-66]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/compression-functions#zstd_decompress_to_bytes
+SELECT ZSTD_DECOMPRESS_TO_BYTES(ZSTD_COMPRESS(b'bytes'), size_limit => 1) AS result
+--
+QueryStatement [0-83]
+  Query [0-83]
+    Select [0-83]
+      SelectList [7-83]
+        SelectColumn [7-83]
+          FunctionCall [7-73]
+            PathExpression [7-31]
+              Identifier(ZSTD_DECOMPRESS_TO_BYTES) [7-31]
+            FunctionCall [32-55]
+              PathExpression [32-45]
+                Identifier(ZSTD_COMPRESS) [32-45]
+              BytesLiteral [46-54]
+                BytesLiteralComponent(b'bytes') [46-54]
+            NamedArgument [57-72]
+              Identifier(size_limit) [57-67]
+              IntLiteral(1) [71-72]
+          Alias [74-83]
+            Identifier(result) [77-83]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/compression-functions#zstd_decompress_to_bytes
+SELECT ZSTD_DECOMPRESS_TO_BYTES(NULL) AS result
+--
+QueryStatement [0-47]
+  Query [0-47]
+    Select [0-47]
+      SelectList [7-47]
+        SelectColumn [7-47]
+          FunctionCall [7-37]
+            PathExpression [7-31]
+              Identifier(ZSTD_DECOMPRESS_TO_BYTES) [7-31]
+            NullLiteral(NULL) [32-36]
+          Alias [38-47]
+            Identifier(result) [41-47]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/compression-functions#zstd_decompress_to_string
+[mode=expression]
+ZSTD_DECOMPRESS_TO_STRING(bytes_value, size_limit => 1024 * 1024 * 1024)
+--
+FunctionCall [0-72]
+  PathExpression [0-25]
+    Identifier(ZSTD_DECOMPRESS_TO_STRING) [0-25]
+  PathExpression [26-37]
+    Identifier(bytes_value) [26-37]
+  NamedArgument [39-71]
+    Identifier(size_limit) [39-49]
+    BinaryExpression(*) [53-71]
+      BinaryExpression(*) [53-64]
+        IntLiteral(1024) [53-57]
+        IntLiteral(1024) [60-64]
+      IntLiteral(1024) [67-71]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/compression-functions#zstd_decompress_to_string
+SELECT ZSTD_DECOMPRESS_TO_STRING(ZSTD_COMPRESS('zstd')) AS result
+--
+QueryStatement [0-65]
+  Query [0-65]
+    Select [0-65]
+      SelectList [7-65]
+        SelectColumn [7-65]
+          FunctionCall [7-55]
+            PathExpression [7-32]
+              Identifier(ZSTD_DECOMPRESS_TO_STRING) [7-32]
+            FunctionCall [33-54]
+              PathExpression [33-46]
+                Identifier(ZSTD_COMPRESS) [33-46]
+              StringLiteral [47-53]
+                StringLiteralComponent('zstd') [47-53]
+          Alias [56-65]
+            Identifier(result) [59-65]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/compression-functions#zstd_decompress_to_string
+SELECT ZSTD_DECOMPRESS_TO_STRING(ZSTD_COMPRESS('zstd'), size_limit => 1) AS result
+--
+QueryStatement [0-82]
+  Query [0-82]
+    Select [0-82]
+      SelectList [7-82]
+        SelectColumn [7-82]
+          FunctionCall [7-72]
+            PathExpression [7-32]
+              Identifier(ZSTD_DECOMPRESS_TO_STRING) [7-32]
+            FunctionCall [33-54]
+              PathExpression [33-46]
+                Identifier(ZSTD_COMPRESS) [33-46]
+              StringLiteral [47-53]
+                StringLiteralComponent('zstd') [47-53]
+            NamedArgument [56-71]
+              Identifier(size_limit) [56-66]
+              IntLiteral(1) [70-71]
+          Alias [73-82]
+            Identifier(result) [76-82]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/compression-functions#zstd_decompress_to_string
+SELECT ZSTD_DECOMPRESS_TO_STRING(NULL) AS result
+--
+QueryStatement [0-48]
+  Query [0-48]
+    Select [0-48]
+      SelectList [7-48]
+        SelectColumn [7-48]
+          FunctionCall [7-38]
+            PathExpression [7-32]
+              Identifier(ZSTD_DECOMPRESS_TO_STRING) [7-32]
+            NullLiteral(NULL) [33-37]
+          Alias [39-48]
+            Identifier(result) [42-48]
diff --git a/parser/testdata/spanner/conditional_expressions.test b/parser/testdata/spanner/conditional_expressions.test
new file mode 100644
index 0000000..041e57c
--- /dev/null
+++ b/parser/testdata/spanner/conditional_expressions.test
@@ -0,0 +1,484 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conditional_expressions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conditional_expressions#case_expr
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+WITH Numbers AS (
+  SELECT 90 as A, 2 as B UNION ALL
+  SELECT 50, 8 UNION ALL
+  SELECT 60, 6 UNION ALL
+  SELECT 50, 10
+)
+SELECT
+  A,
+  B,
+  CASE A
+    WHEN 90 THEN 'red'
+    WHEN 50 THEN 'blue'
+    ELSE 'green'
+    END
+    AS result
+FROM Numbers
+--
+QueryStatement [0-245]
+  Query [0-245]
+    WithClause [0-120]
+      WithClauseEntry [5-120]
+        AliasedQuery [5-120]
+          Identifier(Numbers) [5-12]
+          Query [16-120]
+            SetOperation(UNION ALL) [20-118]
+              SetOperationMetadataList [43-102]
+                SetOperationMetadata [43-52]
+                  SetOperationType [43-48]
+                  SetOperationAllOrDistinct [49-52]
+                SetOperationMetadata [68-77]
+                  SetOperationType [68-73]
+                  SetOperationAllOrDistinct [74-77]
+                SetOperationMetadata [93-102]
+                  SetOperationType [93-98]
+                  SetOperationAllOrDistinct [99-102]
+              Select [20-42]
+                SelectList [27-42]
+                  SelectColumn [27-34]
+                    IntLiteral(90) [27-29]
+                    Alias [30-34]
+                      Identifier(A) [33-34]
+                  SelectColumn [36-42]
+                    IntLiteral(2) [36-37]
+                    Alias [38-42]
+                      Identifier(B) [41-42]
+              Select [55-67]
+                SelectList [62-67]
+                  SelectColumn [62-64]
+                    IntLiteral(50) [62-64]
+                  SelectColumn [66-67]
+                    IntLiteral(8) [66-67]
+              Select [80-92]
+                SelectList [87-92]
+                  SelectColumn [87-89]
+                    IntLiteral(60) [87-89]
+                  SelectColumn [91-92]
+                    IntLiteral(6) [91-92]
+              Select [105-118]
+                SelectList [112-118]
+                  SelectColumn [112-114]
+                    IntLiteral(50) [112-114]
+                  SelectColumn [116-118]
+                    IntLiteral(10) [116-118]
+    Select [121-245]
+      SelectList [130-232]
+        SelectColumn [130-131]
+          PathExpression [130-131]
+            Identifier(A) [130-131]
+        SelectColumn [135-136]
+          PathExpression [135-136]
+            Identifier(B) [135-136]
+        SelectColumn [140-232]
+          CaseValueExpression [140-218]
+            PathExpression [145-146]
+              Identifier(A) [145-146]
+            IntLiteral(90) [156-158]
+            StringLiteral [164-169]
+              StringLiteralComponent('red') [164-169]
+            IntLiteral(50) [179-181]
+            StringLiteral [187-193]
+              StringLiteralComponent('blue') [187-193]
+            StringLiteral [203-210]
+              StringLiteralComponent('green') [203-210]
+          Alias [223-232]
+            Identifier(result) [226-232]
+      FromClause [233-245]
+        TablePathExpression [238-245]
+          PathExpression [238-245]
+            Identifier(Numbers) [238-245]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conditional_expressions#case
+WITH Numbers AS (
+  SELECT 90 as A, 2 as B UNION ALL
+  SELECT 50, 6 UNION ALL
+  SELECT 20, 10
+)
+SELECT
+  A,
+  B,
+  CASE
+    WHEN A > 60 THEN 'red'
+    WHEN B = 6 THEN 'blue'
+    ELSE 'green'
+    END
+    AS result
+FROM Numbers
+--
+QueryStatement [0-225]
+  Query [0-225]
+    WithClause [0-95]
+      WithClauseEntry [5-95]
+        AliasedQuery [5-95]
+          Identifier(Numbers) [5-12]
+          Query [16-95]
+            SetOperation(UNION ALL) [20-93]
+              SetOperationMetadataList [43-77]
+                SetOperationMetadata [43-52]
+                  SetOperationType [43-48]
+                  SetOperationAllOrDistinct [49-52]
+                SetOperationMetadata [68-77]
+                  SetOperationType [68-73]
+                  SetOperationAllOrDistinct [74-77]
+              Select [20-42]
+                SelectList [27-42]
+                  SelectColumn [27-34]
+                    IntLiteral(90) [27-29]
+                    Alias [30-34]
+                      Identifier(A) [33-34]
+                  SelectColumn [36-42]
+                    IntLiteral(2) [36-37]
+                    Alias [38-42]
+                      Identifier(B) [41-42]
+              Select [55-67]
+                SelectList [62-67]
+                  SelectColumn [62-64]
+                    IntLiteral(50) [62-64]
+                  SelectColumn [66-67]
+                    IntLiteral(6) [66-67]
+              Select [80-93]
+                SelectList [87-93]
+                  SelectColumn [87-89]
+                    IntLiteral(20) [87-89]
+                  SelectColumn [91-93]
+                    IntLiteral(10) [91-93]
+    Select [96-225]
+      SelectList [105-212]
+        SelectColumn [105-106]
+          PathExpression [105-106]
+            Identifier(A) [105-106]
+        SelectColumn [110-111]
+          PathExpression [110-111]
+            Identifier(B) [110-111]
+        SelectColumn [115-212]
+          CaseNoValueExpression [115-198]
+            BinaryExpression(>) [129-135]
+              PathExpression [129-130]
+                Identifier(A) [129-130]
+              IntLiteral(60) [133-135]
+            StringLiteral [141-146]
+              StringLiteralComponent('red') [141-146]
+            BinaryExpression(=) [156-161]
+              PathExpression [156-157]
+                Identifier(B) [156-157]
+              IntLiteral(6) [160-161]
+            StringLiteral [167-173]
+              StringLiteralComponent('blue') [167-173]
+            StringLiteral [183-190]
+              StringLiteralComponent('green') [183-190]
+          Alias [203-212]
+            Identifier(result) [206-212]
+      FromClause [213-225]
+        TablePathExpression [218-225]
+          PathExpression [218-225]
+            Identifier(Numbers) [218-225]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conditional_expressions#coalesce
+SELECT COALESCE('A', 'B', 'C') as result
+--
+QueryStatement [0-40]
+  Query [0-40]
+    Select [0-40]
+      SelectList [7-40]
+        SelectColumn [7-40]
+          FunctionCall [7-30]
+            PathExpression [7-15]
+              Identifier(COALESCE) [7-15]
+            StringLiteral [16-19]
+              StringLiteralComponent('A') [16-19]
+            StringLiteral [21-24]
+              StringLiteralComponent('B') [21-24]
+            StringLiteral [26-29]
+              StringLiteralComponent('C') [26-29]
+          Alias [31-40]
+            Identifier(result) [34-40]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conditional_expressions#coalesce
+SELECT COALESCE(NULL, 'B', 'C') as result
+--
+QueryStatement [0-41]
+  Query [0-41]
+    Select [0-41]
+      SelectList [7-41]
+        SelectColumn [7-41]
+          FunctionCall [7-31]
+            PathExpression [7-15]
+              Identifier(COALESCE) [7-15]
+            NullLiteral(NULL) [16-20]
+            StringLiteral [22-25]
+              StringLiteralComponent('B') [22-25]
+            StringLiteral [27-30]
+              StringLiteralComponent('C') [27-30]
+          Alias [32-41]
+            Identifier(result) [35-41]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conditional_expressions#coalesce
+-- The first expression is non-NULL, so it's used. The division-by-zero error
+-- from the second expression isn't produced.
+SELECT COALESCE(10, 1 / 0) as result
+--
+QueryStatement [124-160]
+  Query [124-160]
+    Select [124-160]
+      SelectList [131-160]
+        SelectColumn [131-160]
+          FunctionCall [131-150]
+            PathExpression [131-139]
+              Identifier(COALESCE) [131-139]
+            IntLiteral(10) [140-142]
+            BinaryExpression(/) [144-149]
+              IntLiteral(1) [144-145]
+              IntLiteral(0) [148-149]
+          Alias [151-160]
+            Identifier(result) [154-160]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conditional_expressions#coalesce
+SELECT COALESCE(
+  (SELECT event_date FROM recent_events LIMIT 1),
+  (SELECT MAX(event_date) FROM weekly_events),
+  (SELECT MAX(event_date) FROM all_events)
+)
+--
+QueryStatement [0-158]
+  Query [0-158]
+    Select [0-158]
+      SelectList [7-158]
+        SelectColumn [7-158]
+          FunctionCall [7-158]
+            PathExpression [7-15]
+              Identifier(COALESCE) [7-15]
+            ExpressionSubquery [19-65]
+              Query [20-64]
+                Select [20-56]
+                  SelectList [27-37]
+                    SelectColumn [27-37]
+                      PathExpression [27-37]
+                        Identifier(event_date) [27-37]
+                  FromClause [38-56]
+                    TablePathExpression [43-56]
+                      PathExpression [43-56]
+                        Identifier(recent_events) [43-56]
+                LimitOffset [57-64]
+                  Limit [57-64]
+                    IntLiteral(1) [63-64]
+            ExpressionSubquery [69-112]
+              Query [70-111]
+                Select [70-111]
+                  SelectList [77-92]
+                    SelectColumn [77-92]
+                      FunctionCall [77-92]
+                        PathExpression [77-80]
+                          Identifier(MAX) [77-80]
+                        PathExpression [81-91]
+                          Identifier(event_date) [81-91]
+                  FromClause [93-111]
+                    TablePathExpression [98-111]
+                      PathExpression [98-111]
+                        Identifier(weekly_events) [98-111]
+            ExpressionSubquery [116-156]
+              Query [117-155]
+                Select [117-155]
+                  SelectList [124-139]
+                    SelectColumn [124-139]
+                      FunctionCall [124-139]
+                        PathExpression [124-127]
+                          Identifier(MAX) [124-127]
+                        PathExpression [128-138]
+                          Identifier(event_date) [128-138]
+                  FromClause [140-155]
+                    TablePathExpression [145-155]
+                      PathExpression [145-155]
+                        Identifier(all_events) [145-155]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conditional_expressions#if
+[mode=expression]
+IF(expr, true_result, else_result)
+--
+FunctionCall [0-34]
+  PathExpression [0-2]
+    Identifier(`IF`) [0-2]
+  PathExpression [3-7]
+    Identifier(expr) [3-7]
+  PathExpression [9-20]
+    Identifier(true_result) [9-20]
+  PathExpression [22-33]
+    Identifier(else_result) [22-33]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conditional_expressions#if
+SELECT
+  10 AS A,
+  20 AS B,
+  IF(10 < 20, 'true', 'false') AS result
+--
+QueryStatement [0-69]
+  Query [0-69]
+    Select [0-69]
+      SelectList [9-69]
+        SelectColumn [9-16]
+          IntLiteral(10) [9-11]
+          Alias [12-16]
+            Identifier(A) [15-16]
+        SelectColumn [20-27]
+          IntLiteral(20) [20-22]
+          Alias [23-27]
+            Identifier(B) [26-27]
+        SelectColumn [31-69]
+          FunctionCall [31-59]
+            PathExpression [31-33]
+              Identifier(`IF`) [31-33]
+            BinaryExpression(<) [34-41]
+              IntLiteral(10) [34-36]
+              IntLiteral(20) [39-41]
+            StringLiteral [43-49]
+              StringLiteralComponent('true') [43-49]
+            StringLiteral [51-58]
+              StringLiteralComponent('false') [51-58]
+          Alias [60-69]
+            Identifier(result) [63-69]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conditional_expressions#if
+SELECT
+  30 AS A,
+  20 AS B,
+  IF(30 < 20, 'true', 'false') AS result
+--
+QueryStatement [0-69]
+  Query [0-69]
+    Select [0-69]
+      SelectList [9-69]
+        SelectColumn [9-16]
+          IntLiteral(30) [9-11]
+          Alias [12-16]
+            Identifier(A) [15-16]
+        SelectColumn [20-27]
+          IntLiteral(20) [20-22]
+          Alias [23-27]
+            Identifier(B) [26-27]
+        SelectColumn [31-69]
+          FunctionCall [31-59]
+            PathExpression [31-33]
+              Identifier(`IF`) [31-33]
+            BinaryExpression(<) [34-41]
+              IntLiteral(30) [34-36]
+              IntLiteral(20) [39-41]
+            StringLiteral [43-49]
+              StringLiteralComponent('true') [43-49]
+            StringLiteral [51-58]
+              StringLiteralComponent('false') [51-58]
+          Alias [60-69]
+            Identifier(result) [63-69]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conditional_expressions#ifnull
+[mode=expression]
+IFNULL(expr, null_result)
+--
+FunctionCall [0-25]
+  PathExpression [0-6]
+    Identifier(IFNULL) [0-6]
+  PathExpression [7-11]
+    Identifier(expr) [7-11]
+  PathExpression [13-24]
+    Identifier(null_result) [13-24]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conditional_expressions#ifnull
+SELECT IFNULL(NULL, 0) as result
+--
+QueryStatement [0-32]
+  Query [0-32]
+    Select [0-32]
+      SelectList [7-32]
+        SelectColumn [7-32]
+          FunctionCall [7-22]
+            PathExpression [7-13]
+              Identifier(IFNULL) [7-13]
+            NullLiteral(NULL) [14-18]
+            IntLiteral(0) [20-21]
+          Alias [23-32]
+            Identifier(result) [26-32]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conditional_expressions#ifnull
+SELECT IFNULL(10, 0) as result
+--
+QueryStatement [0-30]
+  Query [0-30]
+    Select [0-30]
+      SelectList [7-30]
+        SelectColumn [7-30]
+          FunctionCall [7-20]
+            PathExpression [7-13]
+              Identifier(IFNULL) [7-13]
+            IntLiteral(10) [14-16]
+            IntLiteral(0) [18-19]
+          Alias [21-30]
+            Identifier(result) [24-30]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conditional_expressions#ifnull
+-- The first expression is non-NULL, so it's used. The division-by-zero error
+-- from the second expression isn't produced.
+SELECT IFNULL(10, 1 / 0) as result
+--
+QueryStatement [124-158]
+  Query [124-158]
+    Select [124-158]
+      SelectList [131-158]
+        SelectColumn [131-158]
+          FunctionCall [131-148]
+            PathExpression [131-137]
+              Identifier(IFNULL) [131-137]
+            IntLiteral(10) [138-140]
+            BinaryExpression(/) [142-147]
+              IntLiteral(1) [142-143]
+              IntLiteral(0) [146-147]
+          Alias [149-158]
+            Identifier(result) [152-158]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conditional_expressions#nullif
+[mode=expression]
+NULLIF(expr, expr_to_match)
+--
+FunctionCall [0-27]
+  PathExpression [0-6]
+    Identifier(NULLIF) [0-6]
+  PathExpression [7-11]
+    Identifier(expr) [7-11]
+  PathExpression [13-26]
+    Identifier(expr_to_match) [13-26]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conditional_expressions#nullif
+SELECT NULLIF(0, 0) as result
+--
+QueryStatement [0-29]
+  Query [0-29]
+    Select [0-29]
+      SelectList [7-29]
+        SelectColumn [7-29]
+          FunctionCall [7-19]
+            PathExpression [7-13]
+              Identifier(NULLIF) [7-13]
+            IntLiteral(0) [14-15]
+            IntLiteral(0) [17-18]
+          Alias [20-29]
+            Identifier(result) [23-29]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conditional_expressions#nullif
+SELECT NULLIF(10, 0) as result
+--
+QueryStatement [0-30]
+  Query [0-30]
+    Select [0-30]
+      SelectList [7-30]
+        SelectColumn [7-30]
+          FunctionCall [7-20]
+            PathExpression [7-13]
+              Identifier(NULLIF) [7-13]
+            IntLiteral(10) [14-16]
+            IntLiteral(0) [18-19]
+          Alias [21-30]
+            Identifier(result) [24-30]
diff --git a/parser/testdata/spanner/conversion_functions.test b/parser/testdata/spanner/conversion_functions.test
new file mode 100644
index 0000000..efade2b
--- /dev/null
+++ b/parser/testdata/spanner/conversion_functions.test
@@ -0,0 +1,518 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+CAST(expression AS typename)
+--
+CastExpression [0-28]
+  PathExpression [5-15]
+    Identifier(expression) [5-15]
+  SimpleType [19-27]
+    PathExpression [19-27]
+      Identifier(typename) [19-27]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast
+[mode=expression]
+CAST(x=1 AS STRING)
+--
+CastExpression [0-19]
+  BinaryExpression(=) [5-8]
+    PathExpression [5-6]
+      Identifier(x) [5-6]
+    IntLiteral(1) [7-8]
+  SimpleType [12-18]
+    PathExpression [12-18]
+      Identifier(STRING) [12-18]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast_as_array
+[mode=expression]
+CAST(expression AS ARRAY)
+--
+CastExpression [0-39]
+  PathExpression [5-15]
+    Identifier(expression) [5-15]
+  ArrayType [19-38]
+    SimpleType [25-37]
+      PathExpression [25-37]
+        Identifier(element_type) [25-37]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast_as_bool
+[mode=expression]
+CAST(expression AS BOOL)
+--
+CastExpression [0-24]
+  PathExpression [5-15]
+    Identifier(expression) [5-15]
+  SimpleType [19-23]
+    PathExpression [19-23]
+      Identifier(BOOL) [19-23]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast_as_bytes
+[mode=expression]
+CAST(expression AS BYTES)
+--
+CastExpression [0-25]
+  PathExpression [5-15]
+    Identifier(expression) [5-15]
+  SimpleType [19-24]
+    PathExpression [19-24]
+      Identifier(BYTES) [19-24]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast_as_date
+[mode=expression]
+CAST(expression AS DATE)
+--
+CastExpression [0-24]
+  PathExpression [5-15]
+    Identifier(expression) [5-15]
+  SimpleType [19-23]
+    PathExpression [19-23]
+      Identifier(DATE) [19-23]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast_as_floating_point
+[mode=expression]
+CAST(expression AS FLOAT64)
+--
+CastExpression [0-27]
+  PathExpression [5-15]
+    Identifier(expression) [5-15]
+  SimpleType [19-26]
+    PathExpression [19-26]
+      Identifier(FLOAT64) [19-26]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast_as_floating_point
+[mode=expression]
+CAST(expression AS FLOAT32)
+--
+CastExpression [0-27]
+  PathExpression [5-15]
+    Identifier(expression) [5-15]
+  SimpleType [19-26]
+    PathExpression [19-26]
+      Identifier(FLOAT32) [19-26]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast_as_integer
+[mode=expression]
+CAST(expression AS INT64)
+--
+CastExpression [0-25]
+  PathExpression [5-15]
+    Identifier(expression) [5-15]
+  SimpleType [19-24]
+    PathExpression [19-24]
+      Identifier(INT64) [19-24]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast_as_integer
+SELECT '0x123' as hex_value, CAST('0x123' as INT64) as hex_to_int
+--
+QueryStatement [0-65]
+  Query [0-65]
+    Select [0-65]
+      SelectList [7-65]
+        SelectColumn [7-27]
+          StringLiteral [7-14]
+            StringLiteralComponent('0x123') [7-14]
+          Alias [15-27]
+            Identifier(hex_value) [18-27]
+        SelectColumn [29-65]
+          CastExpression [29-51]
+            StringLiteral [34-41]
+              StringLiteralComponent('0x123') [34-41]
+            SimpleType [45-50]
+              PathExpression [45-50]
+                Identifier(INT64) [45-50]
+          Alias [52-65]
+            Identifier(hex_to_int) [55-65]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast_as_integer
+SELECT '-0x123' as hex_value, CAST('-0x123' as INT64) as hex_to_int
+--
+QueryStatement [0-67]
+  Query [0-67]
+    Select [0-67]
+      SelectList [7-67]
+        SelectColumn [7-28]
+          StringLiteral [7-15]
+            StringLiteralComponent('-0x123') [7-15]
+          Alias [16-28]
+            Identifier(hex_value) [19-28]
+        SelectColumn [30-67]
+          CastExpression [30-53]
+            StringLiteral [35-43]
+              StringLiteralComponent('-0x123') [35-43]
+            SimpleType [47-52]
+              PathExpression [47-52]
+                Identifier(INT64) [47-52]
+          Alias [54-67]
+            Identifier(hex_to_int) [57-67]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast_as_json
+[mode=expression]
+CAST(expression AS JSON)
+--
+CastExpression [0-24]
+  PathExpression [5-15]
+    Identifier(expression) [5-15]
+  SimpleType [19-23]
+    PathExpression [19-23]
+      Identifier(JSON) [19-23]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast_as_json
+SELECT
+  CAST(1 AS JSON) AS json_number,
+  CAST('foo' AS JSON) AS json_string,
+  CAST(TRUE AS JSON) AS json_boolean,
+  CAST([1, 2, 3] AS JSON) AS json_array,
+  CAST(STRUCT('foo' AS a, TRUE AS b) AS JSON) AS json_object
+--
+QueryStatement [0-218]
+  Query [0-218]
+    Select [0-218]
+      SelectList [9-218]
+        SelectColumn [9-39]
+          CastExpression [9-24]
+            IntLiteral(1) [14-15]
+            SimpleType [19-23]
+              PathExpression [19-23]
+                Identifier(JSON) [19-23]
+          Alias [25-39]
+            Identifier(json_number) [28-39]
+        SelectColumn [43-77]
+          CastExpression [43-62]
+            StringLiteral [48-53]
+              StringLiteralComponent('foo') [48-53]
+            SimpleType [57-61]
+              PathExpression [57-61]
+                Identifier(JSON) [57-61]
+          Alias [63-77]
+            Identifier(json_string) [66-77]
+        SelectColumn [81-115]
+          CastExpression [81-99]
+            BooleanLiteral(TRUE) [86-90]
+            SimpleType [94-98]
+              PathExpression [94-98]
+                Identifier(JSON) [94-98]
+          Alias [100-115]
+            Identifier(json_boolean) [103-115]
+        SelectColumn [119-156]
+          CastExpression [119-142]
+            ArrayConstructor [124-133]
+              IntLiteral(1) [125-126]
+              IntLiteral(2) [128-129]
+              IntLiteral(3) [131-132]
+            SimpleType [137-141]
+              PathExpression [137-141]
+                Identifier(JSON) [137-141]
+          Alias [143-156]
+            Identifier(json_array) [146-156]
+        SelectColumn [160-218]
+          CastExpression [160-203]
+            StructConstructorWithKeyword [165-194]
+              StructConstructorArg [172-182]
+                StringLiteral [172-177]
+                  StringLiteralComponent('foo') [172-177]
+                Alias [178-182]
+                  Identifier(a) [181-182]
+              StructConstructorArg [184-193]
+                BooleanLiteral(TRUE) [184-188]
+                Alias [189-193]
+                  Identifier(b) [192-193]
+            SimpleType [198-202]
+              PathExpression [198-202]
+                Identifier(JSON) [198-202]
+          Alias [204-218]
+            Identifier(json_object) [207-218]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast_as_json
+SELECT
+  CAST(NULL AS JSON) AS json_null,
+  CAST(STRUCT(1 AS a, NULL AS b) AS JSON) AS json_object_with_null,
+  CAST([1, NULL, 3] AS JSON) AS json_array_with_null
+--
+QueryStatement [0-162]
+  Query [0-162]
+    Select [0-162]
+      SelectList [9-162]
+        SelectColumn [9-40]
+          CastExpression [9-27]
+            NullLiteral(NULL) [14-18]
+            SimpleType [22-26]
+              PathExpression [22-26]
+                Identifier(JSON) [22-26]
+          Alias [28-40]
+            Identifier(json_null) [31-40]
+        SelectColumn [44-108]
+          CastExpression [44-83]
+            StructConstructorWithKeyword [49-74]
+              StructConstructorArg [56-62]
+                IntLiteral(1) [56-57]
+                Alias [58-62]
+                  Identifier(a) [61-62]
+              StructConstructorArg [64-73]
+                NullLiteral(NULL) [64-68]
+                Alias [69-73]
+                  Identifier(b) [72-73]
+            SimpleType [78-82]
+              PathExpression [78-82]
+                Identifier(JSON) [78-82]
+          Alias [84-108]
+            Identifier(json_object_with_null) [87-108]
+        SelectColumn [112-162]
+          CastExpression [112-138]
+            ArrayConstructor [117-129]
+              IntLiteral(1) [118-119]
+              NullLiteral(NULL) [121-125]
+              IntLiteral(3) [127-128]
+            SimpleType [133-137]
+              PathExpression [133-137]
+                Identifier(JSON) [133-137]
+          Alias [139-162]
+            Identifier(json_array_with_null) [142-162]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast_as_interval
+[mode=expression]
+CAST(expression AS INTERVAL)
+--
+CastExpression [0-28]
+  PathExpression [5-15]
+    Identifier(expression) [5-15]
+  SimpleType [19-27]
+    PathExpression [19-27]
+      Identifier(`INTERVAL`) [19-27]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast_as_interval
+SELECT input, CAST(input AS INTERVAL) AS output
+FROM UNNEST([
+  '1-2 3 10:20:30.456',
+  '1-2',
+  '10:20:30',
+  'P1Y2M3D',
+  'PT10H20M30,456S'
+]) input
+--
+QueryStatement [0-150]
+  Query [0-150]
+    Select [0-150]
+      SelectList [7-47]
+        SelectColumn [7-12]
+          PathExpression [7-12]
+            Identifier(input) [7-12]
+        SelectColumn [14-47]
+          CastExpression [14-37]
+            PathExpression [19-24]
+              Identifier(input) [19-24]
+            SimpleType [28-36]
+              PathExpression [28-36]
+                Identifier(`INTERVAL`) [28-36]
+          Alias [38-47]
+            Identifier(output) [41-47]
+      FromClause [48-150]
+        TablePathExpression [53-150]
+          UnnestExpression [53-144]
+            ExpressionWithOptAlias [60-143]
+              ArrayConstructor [60-143]
+                StringLiteral [64-84]
+                  StringLiteralComponent('1-2 3 10:20:30.456') [64-84]
+                StringLiteral [88-93]
+                  StringLiteralComponent('1-2') [88-93]
+                StringLiteral [97-107]
+                  StringLiteralComponent('10:20:30') [97-107]
+                StringLiteral [111-120]
+                  StringLiteralComponent('P1Y2M3D') [111-120]
+                StringLiteral [124-141]
+                  StringLiteralComponent('PT10H20M30,456S') [124-141]
+          Alias [145-150]
+            Identifier(input) [145-150]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast_numeric
+[mode=expression]
+CAST(expression AS NUMERIC)
+--
+CastExpression [0-27]
+  PathExpression [5-15]
+    Identifier(expression) [5-15]
+  SimpleType [19-26]
+    PathExpression [19-26]
+      Identifier(NUMERIC) [19-26]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast_as_proto
+SELECT
+  CAST(
+    '''
+    year: 2001
+    month: 9
+    type { award_name: 'Best Artist' category: 'Artist' }
+    type { award_name: 'Best Album' category: 'Album' }
+    '''
+    AS googlesql.examples.music.Award)
+  AS award_col
+--
+QueryStatement [0-226]
+  Query [0-226]
+    Select [0-226]
+      SelectList [9-226]
+        SelectColumn [9-226]
+          CastExpression [9-211]
+            StringLiteral [19-172]
+              StringLiteralComponent('''
+    year: 2001
+    month: 9
+    type { award_name: 'Best Artist' category: 'Artist' }
+    type { award_name: 'Best Album' category: 'Album' }
+    ''') [19-172]
+            SimpleType [180-210]
+              PathExpression [180-210]
+                Identifier(googlesql) [180-189]
+                Identifier(examples) [190-198]
+                Identifier(music) [199-204]
+                Identifier(Award) [205-210]
+          Alias [214-226]
+            Identifier(award_col) [217-226]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast_as_string
+[mode=expression]
+CAST(expression AS STRING)
+--
+CastExpression [0-26]
+  PathExpression [5-15]
+    Identifier(expression) [5-15]
+  SimpleType [19-25]
+    PathExpression [19-25]
+      Identifier(STRING) [19-25]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast_as_string
+SELECT CAST(CURRENT_DATE() AS STRING) AS current_date
+--
+QueryStatement [0-53]
+  Query [0-53]
+    Select [0-53]
+      SelectList [7-53]
+        SelectColumn [7-53]
+          CastExpression [7-37]
+            FunctionCall [12-26]
+              PathExpression [12-24]
+                Identifier(`CURRENT_DATE`) [12-24]
+            SimpleType [30-36]
+              PathExpression [30-36]
+                Identifier(STRING) [30-36]
+          Alias [38-53]
+            Identifier(`current_date`) [41-53]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast_as_string
+SELECT CAST(INTERVAL 3 DAY AS STRING) AS interval_to_string
+--
+QueryStatement [0-59]
+  Query [0-59]
+    Select [0-59]
+      SelectList [7-59]
+        SelectColumn [7-59]
+          CastExpression [7-37]
+            IntervalExpr [12-26]
+              IntLiteral(3) [21-22]
+              Identifier(DAY) [23-26]
+            SimpleType [30-36]
+              PathExpression [30-36]
+                Identifier(STRING) [30-36]
+          Alias [38-59]
+            Identifier(interval_to_string) [41-59]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast_as_string
+SELECT CAST(
+  INTERVAL "1-2 3 4:5:6.789" YEAR TO SECOND
+  AS STRING) AS interval_to_string
+--
+QueryStatement [0-91]
+  Query [0-91]
+    Select [0-91]
+      SelectList [7-91]
+        SelectColumn [7-91]
+          CastExpression [7-69]
+            IntervalExpr [15-56]
+              StringLiteral [24-41]
+                StringLiteralComponent("1-2 3 4:5:6.789") [24-41]
+              Identifier(YEAR) [42-46]
+              Identifier(SECOND) [50-56]
+            SimpleType [62-68]
+              PathExpression [62-68]
+                Identifier(STRING) [62-68]
+          Alias [70-91]
+            Identifier(interval_to_string) [73-91]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast_as_timestamp
+[mode=expression]
+CAST(expression AS TIMESTAMP)
+--
+CastExpression [0-29]
+  PathExpression [5-15]
+    Identifier(expression) [5-15]
+  SimpleType [19-28]
+    PathExpression [19-28]
+      Identifier(TIMESTAMP) [19-28]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#cast_as_timestamp
+SELECT CAST("2020-06-02 17:00:53.110+00:00" AS TIMESTAMP) AS as_timestamp
+
+-- Results depend upon where this query was executed.
+--
+QueryStatement [0-73]
+  Query [0-73]
+    Select [0-73]
+      SelectList [7-73]
+        SelectColumn [7-73]
+          CastExpression [7-57]
+            StringLiteral [12-43]
+              StringLiteralComponent("2020-06-02 17:00:53.110+00:00") [12-43]
+            SimpleType [47-56]
+              PathExpression [47-56]
+                Identifier(TIMESTAMP) [47-56]
+          Alias [58-73]
+            Identifier(as_timestamp) [61-73]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#safe_casting
+[mode=expression]
+SAFE_CAST(expression AS typename)
+--
+CastExpression(return_null_on_error=true) [0-33]
+  PathExpression [10-20]
+    Identifier(expression) [10-20]
+  SimpleType [24-32]
+    PathExpression [24-32]
+      Identifier(typename) [24-32]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#safe_casting
+SELECT CAST("apple" AS INT64) AS not_a_number
+--
+QueryStatement [0-45]
+  Query [0-45]
+    Select [0-45]
+      SelectList [7-45]
+        SelectColumn [7-45]
+          CastExpression [7-29]
+            StringLiteral [12-19]
+              StringLiteralComponent("apple") [12-19]
+            SimpleType [23-28]
+              PathExpression [23-28]
+                Identifier(INT64) [23-28]
+          Alias [30-45]
+            Identifier(not_a_number) [33-45]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/conversion_functions#safe_casting
+SELECT SAFE_CAST("apple" AS INT64) AS not_a_number
+--
+QueryStatement [0-50]
+  Query [0-50]
+    Select [0-50]
+      SelectList [7-50]
+        SelectColumn [7-50]
+          CastExpression(return_null_on_error=true) [7-34]
+            StringLiteral [17-24]
+              StringLiteralComponent("apple") [17-24]
+            SimpleType [28-33]
+              PathExpression [28-33]
+                Identifier(INT64) [28-33]
+          Alias [35-50]
+            Identifier(not_a_number) [38-50]
diff --git a/parser/testdata/spanner/data-definition-language.metadata.json b/parser/testdata/spanner/data-definition-language.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/data-definition-language.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/data-definition-language.spanner.sql b/parser/testdata/spanner/data-definition-language.spanner.sql
new file mode 100644
index 0000000..f353b16
--- /dev/null
+++ b/parser/testdata/spanner/data-definition-language.spanner.sql
@@ -0,0 +1,92 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#syntax_9
+# id 6b16dd2c79d0: proto bundles (data-definition-language)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected keyword PROTO [at 1:6]
+DROP PROTO BUNDLE;
+
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#example
+# id 0aa013a6dacc: RENAME TABLE with several renames (data-definition-language)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ";" or end of input but got "," [at 1:32]
+RENAME TABLE Singers TO Artists, Albums TO Recordings;
+
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#examples
+# id aecb32dadca9: GRANT/REVOKE for fine-grained access control (data-definition-language)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:36]
+GRANT SELECT ON TABLE employees TO ROLE hr_rep;
+
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#examples
+# id c10d4211df33: GRANT/REVOKE for fine-grained access control (data-definition-language)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:60]
+GRANT SELECT(name, address, phone) ON TABLE contractors TO ROLE hr_rep;
+
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#examples
+# id 2faa40dfa4bd: GRANT/REVOKE for fine-grained access control (data-definition-language)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:54]
+GRANT SELECT, UPDATE(location) ON TABLE employees TO ROLE hr_manager;
+
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#examples
+# id ac9569aebf1c: GRANT/REVOKE for fine-grained access control (data-definition-language)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "." or keyword TO but got "," [at 1:73]
+GRANT SELECT(name, level, location), UPDATE(location) ON TABLE employees, contractors TO ROLE hr_manager;
+
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#examples
+# id a859440344c5: GRANT/REVOKE for fine-grained access control (data-definition-language)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:74]
+GRANT INSERT(name, cost_center, location, manager) ON TABLE employees TO ROLE hr_manager;
+
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#examples
+# id 56ddc9700e16: GRANT/REVOKE for fine-grained access control (data-definition-language)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected keyword ON but got identifier "pii_access" [at 1:12]
+GRANT ROLE pii_access TO ROLE hr_manager, hr_director;
+
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#examples
+# id 4f91c277724d: GRANT/REVOKE for fine-grained access control (data-definition-language)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:53]
+GRANT SELECT, UPDATE ON SEQUENCE sequence_object TO ROLE app_developer;
+
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#examples
+# id a7ac2297db8d: GRANT/REVOKE for fine-grained access control (data-definition-language)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected keyword ALL [at 1:17]
+GRANT SELECT ON ALL SEQUENCES IN SCHEMA default TO ROLE data_analyst;
+
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#examples_5
+# id 3127ba6ca90c: ARRAY(vector_length=>N) (data-definition-language)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected identifier "vector_length" [at 1:71]
+CREATE TABLE Singers(id INT64, genre STRING, embedding ARRAY(vector_length=>128))
+PRIMARY KEY(id);
+
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#examples_5
+# id c17672af79d0: vector index clauses (data-definition-language)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ";" or end of input but got keyword WHERE [at 3:1]
+CREATE VECTOR INDEX Singer_vector_index ON Singers(embedding)
+STORING (genre)
+WHERE embedding IS NOT NULL
+OPTIONS(distance_type = 'COSINE');
+
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#examples_5
+# id 539ecbbae570: ARRAY(vector_length=>N) (data-definition-language)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected identifier "vector_length" [at 1:57]
+CREATE TABLE Singers(id INT64, embedding ARRAY(vector_length=>128))
+PRIMARY KEY(id);
+
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#examples_5
+# id 988db338d407: vector index clauses (data-definition-language)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ";" or end of input but got keyword WHERE [at 3:1]
+CREATE VECTOR INDEX Singer_vector_index ON Singers(embedding)
+STORING (genre)
+WHERE embedding IS NOT NULL
+OPTIONS(distance_type = 'COSINE', tree_depth = 3, num_branches = 1000, num_leaves = 1000000);
+
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#examples_6
+# id 87cc4f015ea6: vector index clauses (data-definition-language)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected keyword COLUMN but got keyword STORED [at 2:6]
+ALTER VECTOR INDEX Singer_vector_index
+DROP STORED COLUMN genre;
diff --git a/parser/testdata/spanner/data-definition-language.test b/parser/testdata/spanner/data-definition-language.test
new file mode 100644
index 0000000..a86f0fd
--- /dev/null
+++ b/parser/testdata/spanner/data-definition-language.test
@@ -0,0 +1,528 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#reserved_keywords
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE MyTable (
+  RowId INT64 NOT NULL PRIMARY KEY,
+  `Order` INT64
+)
+--
+CreateTableStatement [0-76]
+  PathExpression [13-20]
+    Identifier(MyTable) [13-20]
+  TableElementList [21-76]
+    ColumnDefinition [25-57]
+      Identifier(RowId) [25-30]
+      SimpleColumnSchema [31-57]
+        PathExpression [31-36]
+          Identifier(INT64) [31-36]
+        ColumnAttributeList [37-57]
+          NotNullColumnAttribute [37-45]
+          PrimaryKeyColumnAttribute(ENFORCED) [46-57]
+    ColumnDefinition [61-74]
+      Identifier(`Order`) [61-68]
+      SimpleColumnSchema [69-74]
+        PathExpression [69-74]
+          Identifier(INT64) [69-74]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#names
+CREATE TABLE MyTable (col1 INT64 PRIMARY KEY)
+--
+CreateTableStatement [0-45]
+  PathExpression [13-20]
+    Identifier(MyTable) [13-20]
+  TableElementList [21-45]
+    ColumnDefinition [22-44]
+      Identifier(col1) [22-26]
+      SimpleColumnSchema [27-44]
+        PathExpression [27-32]
+          Identifier(INT64) [27-32]
+        ColumnAttributeList [33-44]
+          PrimaryKeyColumnAttribute(ENFORCED) [33-44]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#names
+CREATE TABLE MYTABLE (col1 INT64 PRIMARY KEY)
+--
+CreateTableStatement [0-45]
+  PathExpression [13-20]
+    Identifier(MYTABLE) [13-20]
+  TableElementList [21-45]
+    ColumnDefinition [22-44]
+      Identifier(col1) [22-26]
+      SimpleColumnSchema [27-44]
+        PathExpression [27-32]
+          Identifier(INT64) [27-32]
+        ColumnAttributeList [33-44]
+          PrimaryKeyColumnAttribute(ENFORCED) [33-44]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#names
+CREATE SCHEMA MyTable
+--
+CreateSchemaStatement [0-21]
+  PathExpression [14-21]
+    Identifier(MyTable) [14-21]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#names
+CREATE TABLE Singers (
+  SingerId   INT64 NOT NULL PRIMARY KEY,
+  FirstName  STRING(1024),
+  LastName   STRING(1024),
+  SingerInfo BYTES(MAX),
+  BirthDate  DATE
+)
+--
+CreateTableStatement [0-162]
+  PathExpression [13-20]
+    Identifier(Singers) [13-20]
+  TableElementList [21-162]
+    ColumnDefinition [25-62]
+      Identifier(SingerId) [25-33]
+      SimpleColumnSchema [36-62]
+        PathExpression [36-41]
+          Identifier(INT64) [36-41]
+        ColumnAttributeList [42-62]
+          NotNullColumnAttribute [42-50]
+          PrimaryKeyColumnAttribute(ENFORCED) [51-62]
+    ColumnDefinition [66-89]
+      Identifier(FirstName) [66-75]
+      SimpleColumnSchema [77-89]
+        PathExpression [77-83]
+          Identifier(STRING) [77-83]
+        TypeParameterList [83-88]
+          IntLiteral(1024) [84-88]
+    ColumnDefinition [93-116]
+      Identifier(LastName) [93-101]
+      SimpleColumnSchema [104-116]
+        PathExpression [104-110]
+          Identifier(STRING) [104-110]
+        TypeParameterList [110-115]
+          IntLiteral(1024) [111-115]
+    ColumnDefinition [120-141]
+      Identifier(SingerInfo) [120-130]
+      SimpleColumnSchema [131-141]
+        PathExpression [131-136]
+          Identifier(BYTES) [131-136]
+        TypeParameterList [136-140]
+          MaxLiteral() [137-140]
+    ColumnDefinition [145-160]
+      Identifier(BirthDate) [145-154]
+      SimpleColumnSchema [156-160]
+        PathExpression [156-160]
+          Identifier(DATE) [156-160]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#names
+CREATE INDEX SingersByFirstLastName ON singers(FirstName, LastName)
+--
+CreateIndexStatement [0-67]
+  PathExpression [13-35]
+    Identifier(SingersByFirstLastName) [13-35]
+  PathExpression [39-46]
+    Identifier(singers) [39-46]
+  IndexItemList [46-67]
+    OrderingExpression(ASC) [47-56]
+      PathExpression [47-56]
+        Identifier(FirstName) [47-56]
+    OrderingExpression(ASC) [58-66]
+      PathExpression [58-66]
+        Identifier(LastName) [58-66]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#names
+CREATE TABLE MyTable2 (col1 INT64 PRIMARY KEY)
+--
+CreateTableStatement [0-46]
+  PathExpression [13-21]
+    Identifier(MyTable2) [13-21]
+  TableElementList [22-46]
+    ColumnDefinition [23-45]
+      Identifier(col1) [23-27]
+      SimpleColumnSchema [28-45]
+        PathExpression [28-33]
+          Identifier(INT64) [28-33]
+        ColumnAttributeList [34-45]
+          PrimaryKeyColumnAttribute(ENFORCED) [34-45]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#names
+SELECT col1 FROM MyTable2 LIMIT 1
+--
+QueryStatement [0-33]
+  Query [0-33]
+    Select [0-25]
+      SelectList [7-11]
+        SelectColumn [7-11]
+          PathExpression [7-11]
+            Identifier(col1) [7-11]
+      FromClause [12-25]
+        TablePathExpression [17-25]
+          PathExpression [17-25]
+            Identifier(MyTable2) [17-25]
+    LimitOffset [26-33]
+      Limit [26-33]
+        IntLiteral(1) [32-33]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#names
+SELECT COL1 FROM MYTABLE2 LIMIT 1
+--
+QueryStatement [0-33]
+  Query [0-33]
+    Select [0-25]
+      SelectList [7-11]
+        SelectColumn [7-11]
+          PathExpression [7-11]
+            Identifier(COL1) [7-11]
+      FromClause [12-25]
+        TablePathExpression [17-25]
+          PathExpression [17-25]
+            Identifier(MYTABLE2) [17-25]
+    LimitOffset [26-33]
+      Limit [26-33]
+        IntLiteral(1) [32-33]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#names
+SELECT COL1 FROM mytable2 LIMIT 1
+--
+QueryStatement [0-33]
+  Query [0-33]
+    Select [0-25]
+      SelectList [7-11]
+        SelectColumn [7-11]
+          PathExpression [7-11]
+            Identifier(COL1) [7-11]
+      FromClause [12-25]
+        TablePathExpression [17-25]
+          PathExpression [17-25]
+            Identifier(mytable2) [17-25]
+    LimitOffset [26-33]
+      Limit [26-33]
+        IntLiteral(1) [32-33]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#names
+INSERT INTO MYTABLE2 (col1) VALUES(1)
+--
+InsertStatement [0-37]
+  PathExpression [12-20]
+    Identifier(MYTABLE2) [12-20]
+  ColumnList [21-27]
+    Identifier(col1) [22-26]
+  InsertValuesRowList [28-37]
+    InsertValuesRow [34-37]
+      IntLiteral(1) [35-36]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#names
+CREATE TABLE Singer (
+  Singer     INT64 NOT NULL PRIMARY KEY,
+  FirstName  STRING(1024),
+  LastName   STRING(1024),
+  BirthDate  DATE
+)
+--
+CreateTableStatement [0-136]
+  PathExpression [13-19]
+    Identifier(Singer) [13-19]
+  TableElementList [20-136]
+    ColumnDefinition [24-61]
+      Identifier(Singer) [24-30]
+      SimpleColumnSchema [35-61]
+        PathExpression [35-40]
+          Identifier(INT64) [35-40]
+        ColumnAttributeList [41-61]
+          NotNullColumnAttribute [41-49]
+          PrimaryKeyColumnAttribute(ENFORCED) [50-61]
+    ColumnDefinition [65-88]
+      Identifier(FirstName) [65-74]
+      SimpleColumnSchema [76-88]
+        PathExpression [76-82]
+          Identifier(STRING) [76-82]
+        TypeParameterList [82-87]
+          IntLiteral(1024) [83-87]
+    ColumnDefinition [92-115]
+      Identifier(LastName) [92-100]
+      SimpleColumnSchema [103-115]
+        PathExpression [103-109]
+          Identifier(STRING) [103-109]
+        TypeParameterList [109-114]
+          IntLiteral(1024) [110-114]
+    ColumnDefinition [119-134]
+      Identifier(BirthDate) [119-128]
+      SimpleColumnSchema [130-134]
+        PathExpression [130-134]
+          Identifier(DATE) [130-134]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#names
+SELECT S.FirstName, S.Singer FROM Singer S
+--
+QueryStatement [0-42]
+  Query [0-42]
+    Select [0-42]
+      SelectList [7-28]
+        SelectColumn [7-18]
+          PathExpression [7-18]
+            Identifier(S) [7-8]
+            Identifier(FirstName) [9-18]
+        SelectColumn [20-28]
+          PathExpression [20-28]
+            Identifier(S) [20-21]
+            Identifier(Singer) [22-28]
+      FromClause [29-42]
+        TablePathExpression [34-42]
+          PathExpression [34-40]
+            Identifier(Singer) [34-40]
+          Alias [41-42]
+            Identifier(S) [41-42]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#protocol-buffers
+CREATE TABLE Singers (
+ SingerId   INT64 NOT NULL PRIMARY KEY,
+ FirstName  STRING(1024),
+ LastName   STRING(1024),
+ SingerInfo googlesql.example.SingerInfo,
+ SingerInfoArray ARRAY,
+)
+--
+CreateTableStatement [0-212]
+  PathExpression [13-20]
+    Identifier(Singers) [13-20]
+  TableElementList [21-212]
+    ColumnDefinition [24-61]
+      Identifier(SingerId) [24-32]
+      SimpleColumnSchema [35-61]
+        PathExpression [35-40]
+          Identifier(INT64) [35-40]
+        ColumnAttributeList [41-61]
+          NotNullColumnAttribute [41-49]
+          PrimaryKeyColumnAttribute(ENFORCED) [50-61]
+    ColumnDefinition [64-87]
+      Identifier(FirstName) [64-73]
+      SimpleColumnSchema [75-87]
+        PathExpression [75-81]
+          Identifier(STRING) [75-81]
+        TypeParameterList [81-86]
+          IntLiteral(1024) [82-86]
+    ColumnDefinition [90-113]
+      Identifier(LastName) [90-98]
+      SimpleColumnSchema [101-113]
+        PathExpression [101-107]
+          Identifier(STRING) [101-107]
+        TypeParameterList [107-112]
+          IntLiteral(1024) [108-112]
+    ColumnDefinition [116-155]
+      Identifier(SingerInfo) [116-126]
+      SimpleColumnSchema [127-155]
+        PathExpression [127-155]
+          Identifier(googlesql) [127-136]
+          Identifier(example) [137-144]
+          Identifier(SingerInfo) [145-155]
+    ColumnDefinition [158-209]
+      Identifier(SingerInfoArray) [158-173]
+      ArrayColumnSchema [174-209]
+        SimpleColumnSchema [180-208]
+          PathExpression [180-208]
+            Identifier(googlesql) [180-189]
+            Identifier(example) [190-197]
+            Identifier(SingerInfo) [198-208]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#description_11
+SELECT pk1, pk2 FROM child
+    EXCEPT DISTINCT
+    SELECT pk1, pk2 FROM parent
+--
+QueryStatement [0-78]
+  Query [0-78]
+    SetOperation(EXCEPT DISTINCT) [0-78]
+      SetOperationMetadataList [31-46]
+        SetOperationMetadata [31-46]
+          SetOperationType [31-37]
+          SetOperationAllOrDistinct [38-46]
+      Select [0-26]
+        SelectList [7-15]
+          SelectColumn [7-10]
+            PathExpression [7-10]
+              Identifier(pk1) [7-10]
+          SelectColumn [12-15]
+            PathExpression [12-15]
+              Identifier(pk2) [12-15]
+        FromClause [16-26]
+          TablePathExpression [21-26]
+            PathExpression [21-26]
+              Identifier(child) [21-26]
+      Select [51-78]
+        SelectList [58-66]
+          SelectColumn [58-61]
+            PathExpression [58-61]
+              Identifier(pk1) [58-61]
+          SelectColumn [63-66]
+            PathExpression [63-66]
+              Identifier(pk2) [63-66]
+        FromClause [67-78]
+          TablePathExpression [72-78]
+            PathExpression [72-78]
+              Identifier(parent) [72-78]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#examples_3
+\# Create a positive bit-reversed sequence to use in a primary key.
+
+CREATE SEQUENCE MySequence OPTIONS (
+    sequence_kind='bit_reversed_positive',
+    skip_range_min = 1,
+    skip_range_max = 1000,
+    start_with_counter = 50)
+--
+CreateSequenceStatement [68-227]
+  PathExpression [84-94]
+    Identifier(MySequence) [84-94]
+  OptionsList [103-227]
+    OptionsEntry [109-146]
+      Identifier(sequence_kind) [109-122]
+      StringLiteral [123-146]
+        StringLiteralComponent('bit_reversed_positive') [123-146]
+    OptionsEntry [152-170]
+      Identifier(skip_range_min) [152-166]
+      IntLiteral(1) [169-170]
+    OptionsEntry [176-197]
+      Identifier(skip_range_max) [176-190]
+      IntLiteral(1000) [193-197]
+    OptionsEntry [203-226]
+      Identifier(start_with_counter) [203-221]
+      IntLiteral(50) [224-226]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#examples_3
+\# Create a table that uses the sequence for a key column.
+CREATE TABLE Singers (
+  SingerId INT64 DEFAULT (GET_NEXT_SEQUENCE_VALUE(SEQUENCE MySequence)),
+  FirstName  STRING(1024),
+  LastName   STRING(1024),
+  SingerInfo googlesql.example.SingerInfo,
+  BirthDate  DATE
+) PRIMARY KEY (SingerId)
+--
+CreateTableStatement [58-293]
+  PathExpression [71-78]
+    Identifier(Singers) [71-78]
+  TableElementList [79-270]
+    ColumnDefinition [83-152]
+      Identifier(SingerId) [83-91]
+      SimpleColumnSchema [92-152]
+        PathExpression [92-97]
+          Identifier(INT64) [92-97]
+        FunctionCall [107-151]
+          PathExpression [107-130]
+            Identifier(GET_NEXT_SEQUENCE_VALUE) [107-130]
+          SequenceArg [131-150]
+            PathExpression [140-150]
+              Identifier(MySequence) [140-150]
+    ColumnDefinition [156-179]
+      Identifier(FirstName) [156-165]
+      SimpleColumnSchema [167-179]
+        PathExpression [167-173]
+          Identifier(STRING) [167-173]
+        TypeParameterList [173-178]
+          IntLiteral(1024) [174-178]
+    ColumnDefinition [183-206]
+      Identifier(LastName) [183-191]
+      SimpleColumnSchema [194-206]
+        PathExpression [194-200]
+          Identifier(STRING) [194-200]
+        TypeParameterList [200-205]
+          IntLiteral(1024) [201-205]
+    ColumnDefinition [210-249]
+      Identifier(SingerInfo) [210-220]
+      SimpleColumnSchema [221-249]
+        PathExpression [221-249]
+          Identifier(googlesql) [221-230]
+          Identifier(example) [231-238]
+          Identifier(SingerInfo) [239-249]
+    ColumnDefinition [253-268]
+      Identifier(BirthDate) [253-262]
+      SimpleColumnSchema [264-268]
+        PathExpression [264-268]
+          Identifier(DATE) [264-268]
+  SpannerTableOptions [271-293]
+    PrimaryKey(ENFORCED) [271-293]
+      PrimaryKeyElementList [283-293]
+        PrimaryKeyElement(ASC) [284-292]
+          Identifier(SingerId) [284-292]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#examples_3
+SELECT * FROM information_schema.sequences
+--
+QueryStatement [0-42]
+  Query [0-42]
+    Select [0-42]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-42]
+        TablePathExpression [14-42]
+          PathExpression [14-42]
+            Identifier(information_schema) [14-32]
+            Identifier(sequences) [33-42]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#examples_3
+SELECT * FROM information_schema.sequence_options
+--
+QueryStatement [0-49]
+  Query [0-49]
+    Select [0-49]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-49]
+        TablePathExpression [14-49]
+          PathExpression [14-49]
+            Identifier(information_schema) [14-32]
+            Identifier(sequence_options) [33-49]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#examples_4
+\# Alter the sequence to include a skipped range. This is useful when you are
+\# migrating from a regular sequence with sequential data
+ALTER SEQUENCE MySequence
+SET OPTIONS (skip_range_min=1, skip_range_max=1234567)
+--
+AlterSequenceStatement [134-214]
+  PathExpression [149-159]
+    Identifier(MySequence) [149-159]
+  AlterActionList [160-214]
+    SetOptionsAction [160-214]
+      OptionsList [172-214]
+        OptionsEntry [173-189]
+          Identifier(skip_range_min) [173-187]
+          IntLiteral(1) [188-189]
+        OptionsEntry [191-213]
+          Identifier(skip_range_max) [191-205]
+          IntLiteral(1234567) [206-213]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#parameters_37
+SELECT s.package_name AS package_name, s.allow_gc AS allow_gc FROM INFORMATION_SCHEMA.SPANNER_STATISTICS s
+--
+QueryStatement [0-106]
+  Query [0-106]
+    Select [0-106]
+      SelectList [7-61]
+        SelectColumn [7-37]
+          PathExpression [7-21]
+            Identifier(s) [7-8]
+            Identifier(package_name) [9-21]
+          Alias [22-37]
+            Identifier(package_name) [25-37]
+        SelectColumn [39-61]
+          PathExpression [39-49]
+            Identifier(s) [39-40]
+            Identifier(allow_gc) [41-49]
+          Alias [50-61]
+            Identifier(allow_gc) [53-61]
+      FromClause [62-106]
+        TablePathExpression [67-106]
+          PathExpression [67-104]
+            Identifier(INFORMATION_SCHEMA) [67-85]
+            Identifier(SPANNER_STATISTICS) [86-104]
+          Alias [105-106]
+            Identifier(s) [105-106]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#example_4
+DROP VECTOR INDEX Singer_vector_index
+--
+DropVectorIndexStatement [0-37]
+  PathExpression [18-37]
+    Identifier(Singer_vector_index) [18-37]
diff --git a/parser/testdata/spanner/data-types.test b/parser/testdata/spanner/data-types.test
new file mode 100644
index 0000000..bc53f4f
--- /dev/null
+++ b/parser/testdata/spanner/data-types.test
@@ -0,0 +1,1073 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#using_array_literals
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT [1, 2, 3] AS numbers
+--
+QueryStatement [0-27]
+  Query [0-27]
+    Select [0-27]
+      SelectList [7-27]
+        SelectColumn [7-27]
+          ArrayConstructor [7-16]
+            IntLiteral(1) [8-9]
+            IntLiteral(2) [11-12]
+            IntLiteral(3) [14-15]
+          Alias [17-27]
+            Identifier(numbers) [20-27]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#using_array_literals
+SELECT ["apple", "pear", "orange"] AS fruit
+--
+QueryStatement [0-43]
+  Query [0-43]
+    Select [0-43]
+      SelectList [7-43]
+        SelectColumn [7-43]
+          ArrayConstructor [7-34]
+            StringLiteral [8-15]
+              StringLiteralComponent("apple") [8-15]
+            StringLiteral [17-23]
+              StringLiteralComponent("pear") [17-23]
+            StringLiteral [25-33]
+              StringLiteralComponent("orange") [25-33]
+          Alias [35-43]
+            Identifier(fruit) [38-43]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#using_array_literals
+SELECT [true, false, true] AS booleans
+--
+QueryStatement [0-38]
+  Query [0-38]
+    Select [0-38]
+      SelectList [7-38]
+        SelectColumn [7-38]
+          ArrayConstructor [7-26]
+            BooleanLiteral(true) [8-12]
+            BooleanLiteral(false) [14-19]
+            BooleanLiteral(true) [21-25]
+          Alias [27-38]
+            Identifier(booleans) [30-38]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#using_array_literals
+SELECT [a, b, c]
+FROM
+  (SELECT 5 AS a,
+          37 AS b,
+          406 AS c)
+--
+QueryStatement [0-78]
+  Query [0-78]
+    Select [0-78]
+      SelectList [7-16]
+        SelectColumn [7-16]
+          ArrayConstructor [7-16]
+            PathExpression [8-9]
+              Identifier(a) [8-9]
+            PathExpression [11-12]
+              Identifier(b) [11-12]
+            PathExpression [14-15]
+              Identifier(c) [14-15]
+      FromClause [17-78]
+        TableSubquery [24-78]
+          Query [25-77]
+            Select [25-77]
+              SelectList [32-77]
+                SelectColumn [32-38]
+                  IntLiteral(5) [32-33]
+                  Alias [34-38]
+                    Identifier(a) [37-38]
+                SelectColumn [50-57]
+                  IntLiteral(37) [50-52]
+                  Alias [53-57]
+                    Identifier(b) [56-57]
+                SelectColumn [69-77]
+                  IntLiteral(406) [69-72]
+                  Alias [73-77]
+                    Identifier(c) [76-77]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#using_array_literals
+SELECT [a, b, c]
+FROM
+  (SELECT CAST(5 AS INT64) AS a,
+          CAST(37 AS FLOAT64) AS b,
+          406 AS c)
+--
+QueryStatement [0-110]
+  Query [0-110]
+    Select [0-110]
+      SelectList [7-16]
+        SelectColumn [7-16]
+          ArrayConstructor [7-16]
+            PathExpression [8-9]
+              Identifier(a) [8-9]
+            PathExpression [11-12]
+              Identifier(b) [11-12]
+            PathExpression [14-15]
+              Identifier(c) [14-15]
+      FromClause [17-110]
+        TableSubquery [24-110]
+          Query [25-109]
+            Select [25-109]
+              SelectList [32-109]
+                SelectColumn [32-53]
+                  CastExpression [32-48]
+                    IntLiteral(5) [37-38]
+                    SimpleType [42-47]
+                      PathExpression [42-47]
+                        Identifier(INT64) [42-47]
+                  Alias [49-53]
+                    Identifier(a) [52-53]
+                SelectColumn [65-89]
+                  CastExpression [65-84]
+                    IntLiteral(37) [70-72]
+                    SimpleType [76-83]
+                      PathExpression [76-83]
+                        Identifier(FLOAT64) [76-83]
+                  Alias [85-89]
+                    Identifier(b) [88-89]
+                SelectColumn [101-109]
+                  IntLiteral(406) [101-104]
+                  Alias [105-109]
+                    Identifier(c) [108-109]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#using_array_literals
+SELECT ARRAY[1, 2, 3] AS floats
+--
+QueryStatement [0-40]
+  Query [0-40]
+    Select [0-40]
+      SelectList [7-40]
+        SelectColumn [7-40]
+          ArrayConstructor [7-30]
+            ArrayType [7-21]
+              SimpleType [13-20]
+                PathExpression [13-20]
+                  Identifier(FLOAT64) [13-20]
+            IntLiteral(1) [22-23]
+            IntLiteral(2) [25-26]
+            IntLiteral(3) [28-29]
+          Alias [31-40]
+            Identifier(floats) [34-40]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#generating_arrays_of_integers
+SELECT GENERATE_ARRAY(11, 33, 2) AS odds
+--
+QueryStatement [0-40]
+  Query [0-40]
+    Select [0-40]
+      SelectList [7-40]
+        SelectColumn [7-40]
+          FunctionCall [7-32]
+            PathExpression [7-21]
+              Identifier(GENERATE_ARRAY) [7-21]
+            IntLiteral(11) [22-24]
+            IntLiteral(33) [26-28]
+            IntLiteral(2) [30-31]
+          Alias [33-40]
+            Identifier(odds) [36-40]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#generating_arrays_of_integers
+SELECT GENERATE_ARRAY(21, 14, -1) AS countdown
+--
+QueryStatement [0-46]
+  Query [0-46]
+    Select [0-46]
+      SelectList [7-46]
+        SelectColumn [7-46]
+          FunctionCall [7-33]
+            PathExpression [7-21]
+              Identifier(GENERATE_ARRAY) [7-21]
+            IntLiteral(21) [22-24]
+            IntLiteral(14) [26-28]
+            UnaryExpression(-) [30-32]
+              IntLiteral(1) [31-32]
+          Alias [34-46]
+            Identifier(countdown) [37-46]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#generating_arrays_of_dates
+SELECT
+  GENERATE_DATE_ARRAY('2017-11-21', '2017-12-31', INTERVAL 1 WEEK)
+    AS date_array
+--
+QueryStatement [0-91]
+  Query [0-91]
+    Select [0-91]
+      SelectList [9-91]
+        SelectColumn [9-91]
+          FunctionCall [9-73]
+            PathExpression [9-28]
+              Identifier(GENERATE_DATE_ARRAY) [9-28]
+            StringLiteral [29-41]
+              StringLiteralComponent('2017-11-21') [29-41]
+            StringLiteral [43-55]
+              StringLiteralComponent('2017-12-31') [43-55]
+            IntervalExpr [57-72]
+              IntLiteral(1) [66-67]
+              Identifier(WEEK) [68-72]
+          Alias [78-91]
+            Identifier(date_array) [81-91]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#single_datetime_part_interval
+[mode=expression]
+INTERVAL step_size step_unit
+--
+IntervalExpr [0-28]
+  PathExpression [9-18]
+    Identifier(step_size) [9-18]
+  Identifier(step_unit) [19-28]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#range_datetime_part_interval
+[mode=expression]
+INTERVAL datetime_parts_string starting_datetime_part TO ending_datetime_part
+--
+IntervalExpr [0-77]
+  PathExpression [9-30]
+    Identifier(datetime_parts_string) [9-30]
+  Identifier(starting_datetime_part) [31-53]
+  Identifier(ending_datetime_part) [57-77]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#using_new_map_constructor
+[mode=expression]
+NEW googlesql.examples.astronomy.Planet {
+  planet_name: 'Jupiter'
+  facts: {
+    length_of_day: 9.93
+    distance_to_sun: 5.2 * ASTRONOMICAL_UNIT
+    has_rings: TRUE
+  }
+  major_moons: [
+    { moon_name: 'Io' },
+    { moon_name: 'Europa' },
+    { moon_name: 'Ganymede' },
+    { moon_name: 'Callisto'}
+  ]
+  minor_moons: (
+    SELECT ARRAY_AGG(moon_name)
+    FROM SolarSystemMoons
+    WHERE
+      planet_name = 'Jupiter'
+      AND circumference < 3121
+  )
+  count_of_space_probe_photos: (
+    GALILEO_PHOTOS
+    + JUNO_PHOTOS
+    + NEW_HORIZONS_PHOTOS
+    + CASSINI_PHOTOS
+    + ULYSSES_PHOTOS
+    + VOYAGER_1_PHOTOS
+    + VOYAGER_2_PHOTOS
+    + PIONEER_10_PHOTOS
+    + PIONEER_11_PHOTOS
+  )
+}
+--
+BracedNewConstructor [0-693]
+  SimpleType [4-39]
+    PathExpression [4-39]
+      Identifier(googlesql) [4-13]
+      Identifier(examples) [14-22]
+      Identifier(astronomy) [23-32]
+      Identifier(Planet) [33-39]
+  BracedConstructor [40-693]
+    BracedConstructorField [44-66]
+      BracedConstructorLhs [44-55]
+        PathExpression [44-55]
+          Identifier(planet_name) [44-55]
+      BracedConstructorFieldValue [57-66]
+        StringLiteral [57-66]
+          StringLiteralComponent('Jupiter') [57-66]
+    BracedConstructorField [69-170]
+      BracedConstructorLhs [69-74]
+        PathExpression [69-74]
+          Identifier(facts) [69-74]
+      BracedConstructorFieldValue [76-170]
+        BracedConstructor [76-170]
+          BracedConstructorField [82-101]
+            BracedConstructorLhs [82-95]
+              PathExpression [82-95]
+                Identifier(length_of_day) [82-95]
+            BracedConstructorFieldValue [97-101]
+              FloatLiteral(9.93) [97-101]
+          BracedConstructorField [106-146]
+            BracedConstructorLhs [106-121]
+              PathExpression [106-121]
+                Identifier(distance_to_sun) [106-121]
+            BracedConstructorFieldValue [123-146]
+              BinaryExpression(*) [123-146]
+                FloatLiteral(5.2) [123-126]
+                PathExpression [129-146]
+                  Identifier(ASTRONOMICAL_UNIT) [129-146]
+          BracedConstructorField [151-166]
+            BracedConstructorLhs [151-160]
+              PathExpression [151-160]
+                Identifier(has_rings) [151-160]
+            BracedConstructorFieldValue [162-166]
+              BooleanLiteral(TRUE) [162-166]
+    BracedConstructorField [173-305]
+      BracedConstructorLhs [173-184]
+        PathExpression [173-184]
+          Identifier(major_moons) [173-184]
+      BracedConstructorFieldValue [186-305]
+        ArrayConstructor [186-305]
+          BracedConstructor [192-211]
+            BracedConstructorField [194-209]
+              BracedConstructorLhs [194-203]
+                PathExpression [194-203]
+                  Identifier(moon_name) [194-203]
+              BracedConstructorFieldValue [205-209]
+                StringLiteral [205-209]
+                  StringLiteralComponent('Io') [205-209]
+          BracedConstructor [217-240]
+            BracedConstructorField [219-238]
+              BracedConstructorLhs [219-228]
+                PathExpression [219-228]
+                  Identifier(moon_name) [219-228]
+              BracedConstructorFieldValue [230-238]
+                StringLiteral [230-238]
+                  StringLiteralComponent('Europa') [230-238]
+          BracedConstructor [246-271]
+            BracedConstructorField [248-269]
+              BracedConstructorLhs [248-257]
+                PathExpression [248-257]
+                  Identifier(moon_name) [248-257]
+              BracedConstructorFieldValue [259-269]
+                StringLiteral [259-269]
+                  StringLiteralComponent('Ganymede') [259-269]
+          BracedConstructor [277-301]
+            BracedConstructorField [279-300]
+              BracedConstructorLhs [279-288]
+                PathExpression [279-288]
+                  Identifier(moon_name) [279-288]
+              BracedConstructorFieldValue [290-300]
+                StringLiteral [290-300]
+                  StringLiteralComponent('Callisto') [290-300]
+    BracedConstructorField [308-455]
+      BracedConstructorLhs [308-319]
+        PathExpression [308-319]
+          Identifier(minor_moons) [308-319]
+      BracedConstructorFieldValue [321-455]
+        ExpressionSubquery [321-455]
+          Query [327-451]
+            Select [327-451]
+              SelectList [334-354]
+                SelectColumn [334-354]
+                  FunctionCall [334-354]
+                    PathExpression [334-343]
+                      Identifier(ARRAY_AGG) [334-343]
+                    PathExpression [344-353]
+                      Identifier(moon_name) [344-353]
+              FromClause [359-380]
+                TablePathExpression [364-380]
+                  PathExpression [364-380]
+                    Identifier(SolarSystemMoons) [364-380]
+              WhereClause [385-451]
+                AndExpr [397-451]
+                  BinaryExpression(=) [397-420]
+                    PathExpression [397-408]
+                      Identifier(planet_name) [397-408]
+                    StringLiteral [411-420]
+                      StringLiteralComponent('Jupiter') [411-420]
+                  BinaryExpression(<) [431-451]
+                    PathExpression [431-444]
+                      Identifier(circumference) [431-444]
+                    IntLiteral(3121) [447-451]
+    BracedConstructorField [458-691]
+      BracedConstructorLhs [458-485]
+        PathExpression [458-485]
+          Identifier(count_of_space_probe_photos) [458-485]
+      BracedConstructorFieldValue [487-691]
+        BinaryExpression(+) [493-687]
+          BinaryExpression(+) [493-663]
+            BinaryExpression(+) [493-639]
+              BinaryExpression(+) [493-616]
+                BinaryExpression(+) [493-593]
+                  BinaryExpression(+) [493-572]
+                    BinaryExpression(+) [493-551]
+                      BinaryExpression(+) [493-525]
+                        PathExpression [493-507]
+                          Identifier(GALILEO_PHOTOS) [493-507]
+                        PathExpression [514-525]
+                          Identifier(JUNO_PHOTOS) [514-525]
+                      PathExpression [532-551]
+                        Identifier(NEW_HORIZONS_PHOTOS) [532-551]
+                    PathExpression [558-572]
+                      Identifier(CASSINI_PHOTOS) [558-572]
+                  PathExpression [579-593]
+                    Identifier(ULYSSES_PHOTOS) [579-593]
+                PathExpression [600-616]
+                  Identifier(VOYAGER_1_PHOTOS) [600-616]
+              PathExpression [623-639]
+                Identifier(VOYAGER_2_PHOTOS) [623-639]
+            PathExpression [646-663]
+              Identifier(PIONEER_10_PHOTOS) [646-663]
+          PathExpression [670-687]
+            Identifier(PIONEER_11_PHOTOS) [670-687]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#using_new_map_constructor
+SELECT
+  key,
+  name,
+  NEW googlesql.examples.music.Chart { rank: 1 chart_name: '2' }
+--
+QueryStatement [0-86]
+  Query [0-86]
+    Select [0-86]
+      SelectList [9-86]
+        SelectColumn [9-12]
+          PathExpression [9-12]
+            Identifier(key) [9-12]
+        SelectColumn [16-20]
+          PathExpression [16-20]
+            Identifier(name) [16-20]
+        SelectColumn [24-86]
+          BracedNewConstructor [24-86]
+            SimpleType [28-58]
+              PathExpression [28-58]
+                Identifier(googlesql) [28-37]
+                Identifier(examples) [38-46]
+                Identifier(music) [47-52]
+                Identifier(Chart) [53-58]
+            BracedConstructor [59-86]
+              BracedConstructorField [61-68]
+                BracedConstructorLhs [61-65]
+                  PathExpression [61-65]
+                    Identifier(rank) [61-65]
+                BracedConstructorFieldValue [67-68]
+                  IntLiteral(1) [67-68]
+              BracedConstructorField [69-84]
+                BracedConstructorLhs [69-79]
+                  PathExpression [69-79]
+                    Identifier(chart_name) [69-79]
+                BracedConstructorFieldValue [81-84]
+                  StringLiteral [81-84]
+                    StringLiteralComponent('2') [81-84]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#using_new_map_constructor
+SELECT
+  NEW googlesql.examples.music.Album {
+    album_name: 'New Moon'
+    singer {
+      nationality: 'Canadian'
+      residence: [ { city: 'Victoria' }, { city: 'Toronto' } ]
+    }
+    song: ['Sandstorm', 'Wait']
+  }
+--
+QueryStatement [0-220]
+  Query [0-220]
+    Select [0-220]
+      SelectList [9-220]
+        SelectColumn [9-220]
+          BracedNewConstructor [9-220]
+            SimpleType [13-43]
+              PathExpression [13-43]
+                Identifier(googlesql) [13-22]
+                Identifier(examples) [23-31]
+                Identifier(music) [32-37]
+                Identifier(Album) [38-43]
+            BracedConstructor [44-220]
+              BracedConstructorField [50-72]
+                BracedConstructorLhs [50-60]
+                  PathExpression [50-60]
+                    Identifier(album_name) [50-60]
+                BracedConstructorFieldValue [62-72]
+                  StringLiteral [62-72]
+                    StringLiteralComponent('New Moon') [62-72]
+              BracedConstructorField [77-184]
+                BracedConstructorLhs [77-83]
+                  PathExpression [77-83]
+                    Identifier(singer) [77-83]
+                BracedConstructorFieldValue [84-184]
+                  BracedConstructor [84-184]
+                    BracedConstructorField [92-115]
+                      BracedConstructorLhs [92-103]
+                        PathExpression [92-103]
+                          Identifier(nationality) [92-103]
+                      BracedConstructorFieldValue [105-115]
+                        StringLiteral [105-115]
+                          StringLiteralComponent('Canadian') [105-115]
+                    BracedConstructorField [122-178]
+                      BracedConstructorLhs [122-131]
+                        PathExpression [122-131]
+                          Identifier(residence) [122-131]
+                      BracedConstructorFieldValue [133-178]
+                        ArrayConstructor [133-178]
+                          BracedConstructor [135-155]
+                            BracedConstructorField [137-153]
+                              BracedConstructorLhs [137-141]
+                                PathExpression [137-141]
+                                  Identifier(city) [137-141]
+                              BracedConstructorFieldValue [143-153]
+                                StringLiteral [143-153]
+                                  StringLiteralComponent('Victoria') [143-153]
+                          BracedConstructor [157-176]
+                            BracedConstructorField [159-174]
+                              BracedConstructorLhs [159-163]
+                                PathExpression [159-163]
+                                  Identifier(city) [159-163]
+                              BracedConstructorFieldValue [165-174]
+                                StringLiteral [165-174]
+                                  StringLiteralComponent('Toronto') [165-174]
+              BracedConstructorField [189-216]
+                BracedConstructorLhs [189-193]
+                  PathExpression [189-193]
+                    Identifier(song) [189-193]
+                BracedConstructorFieldValue [195-216]
+                  ArrayConstructor [195-216]
+                    StringLiteral [196-207]
+                      StringLiteralComponent('Sandstorm') [196-207]
+                    StringLiteral [209-215]
+                      StringLiteralComponent('Wait') [209-215]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#using_new_map_constructor
+SELECT
+  NEW googlesql.examples.music.Chart {
+    rank: (SELECT COUNT(*) FROM TableName WHERE foo = 'bar')
+    chart_name: CONCAT('best', 'hits')
+  }
+--
+QueryStatement [0-149]
+  Query [0-149]
+    Select [0-149]
+      SelectList [9-149]
+        SelectColumn [9-149]
+          BracedNewConstructor [9-149]
+            SimpleType [13-43]
+              PathExpression [13-43]
+                Identifier(googlesql) [13-22]
+                Identifier(examples) [23-31]
+                Identifier(music) [32-37]
+                Identifier(Chart) [38-43]
+            BracedConstructor [44-149]
+              BracedConstructorField [50-106]
+                BracedConstructorLhs [50-54]
+                  PathExpression [50-54]
+                    Identifier(rank) [50-54]
+                BracedConstructorFieldValue [56-106]
+                  ExpressionSubquery [56-106]
+                    Query [57-105]
+                      Select [57-105]
+                        SelectList [64-72]
+                          SelectColumn [64-72]
+                            FunctionCall [64-72]
+                              PathExpression [64-69]
+                                Identifier(COUNT) [64-69]
+                              Star(*) [70-71]
+                        FromClause [73-87]
+                          TablePathExpression [78-87]
+                            PathExpression [78-87]
+                              Identifier(TableName) [78-87]
+                        WhereClause [88-105]
+                          BinaryExpression(=) [94-105]
+                            PathExpression [94-97]
+                              Identifier(foo) [94-97]
+                            StringLiteral [100-105]
+                              StringLiteralComponent('bar') [100-105]
+              BracedConstructorField [111-145]
+                BracedConstructorLhs [111-121]
+                  PathExpression [111-121]
+                    Identifier(chart_name) [111-121]
+                BracedConstructorFieldValue [123-145]
+                  FunctionCall [123-145]
+                    PathExpression [123-129]
+                      Identifier(CONCAT) [123-129]
+                    StringLiteral [130-136]
+                      StringLiteralComponent('best') [130-136]
+                    StringLiteral [138-144]
+                      StringLiteralComponent('hits') [138-144]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#using_new_map_constructor
+SELECT
+  ARRAY[
+    { rank: 1 chart_name: '2' },
+    { rank: 2 chart_name: '3' }]
+--
+QueryStatement [0-113]
+  Query [0-113]
+    Select [0-113]
+      SelectList [9-113]
+        SelectColumn [9-113]
+          ArrayConstructor [9-113]
+            ArrayType [9-46]
+              SimpleType [15-45]
+                PathExpression [15-45]
+                  Identifier(googlesql) [15-24]
+                  Identifier(examples) [25-33]
+                  Identifier(music) [34-39]
+                  Identifier(Chart) [40-45]
+            BracedConstructor [52-79]
+              BracedConstructorField [54-61]
+                BracedConstructorLhs [54-58]
+                  PathExpression [54-58]
+                    Identifier(rank) [54-58]
+                BracedConstructorFieldValue [60-61]
+                  IntLiteral(1) [60-61]
+              BracedConstructorField [62-77]
+                BracedConstructorLhs [62-72]
+                  PathExpression [62-72]
+                    Identifier(chart_name) [62-72]
+                BracedConstructorFieldValue [74-77]
+                  StringLiteral [74-77]
+                    StringLiteralComponent('2') [74-77]
+            BracedConstructor [85-112]
+              BracedConstructorField [87-94]
+                BracedConstructorLhs [87-91]
+                  PathExpression [87-91]
+                    Identifier(rank) [87-91]
+                BracedConstructorFieldValue [93-94]
+                  IntLiteral(2) [93-94]
+              BracedConstructorField [95-110]
+                BracedConstructorLhs [95-105]
+                  PathExpression [95-105]
+                    Identifier(chart_name) [95-105]
+                BracedConstructorFieldValue [107-110]
+                  StringLiteral [107-110]
+                    StringLiteralComponent('3') [107-110]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#using_new_map_constructor
+SELECT
+  STRUCT(
+    'foo', { rank: 1 chart_name: '2' }, 7)[1]
+--
+QueryStatement [0-109]
+  Query [0-109]
+    Select [0-109]
+      SelectList [9-109]
+        SelectColumn [9-109]
+          ArrayElement [9-109]
+            StructConstructorWithKeyword [9-106]
+              StructType [9-62]
+                StructField [16-22]
+                  SimpleType [16-22]
+                    PathExpression [16-22]
+                      Identifier(STRING) [16-22]
+                StructField [24-54]
+                  SimpleType [24-54]
+                    PathExpression [24-54]
+                      Identifier(googlesql) [24-33]
+                      Identifier(examples) [34-42]
+                      Identifier(music) [43-48]
+                      Identifier(Chart) [49-54]
+                StructField [56-61]
+                  SimpleType [56-61]
+                    PathExpression [56-61]
+                      Identifier(INT64) [56-61]
+              StructConstructorArg [68-73]
+                StringLiteral [68-73]
+                  StringLiteralComponent('foo') [68-73]
+              StructConstructorArg [75-102]
+                BracedConstructor [75-102]
+                  BracedConstructorField [77-84]
+                    BracedConstructorLhs [77-81]
+                      PathExpression [77-81]
+                        Identifier(rank) [77-81]
+                    BracedConstructorFieldValue [83-84]
+                      IntLiteral(1) [83-84]
+                  BracedConstructorField [85-100]
+                    BracedConstructorLhs [85-95]
+                      PathExpression [85-95]
+                        Identifier(chart_name) [85-95]
+                    BracedConstructorFieldValue [97-100]
+                      StringLiteral [97-100]
+                        StringLiteralComponent('2') [97-100]
+              StructConstructorArg [104-105]
+                IntLiteral(7) [104-105]
+            Location [106-107]
+            IntLiteral(1) [107-108]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#using_new_map_constructor
+UPDATE TableName SET proto_column = { rank: 1 chart_name: '2' }
+--
+UpdateStatement [0-63]
+  PathExpression [7-16]
+    Identifier(TableName) [7-16]
+  UpdateItemList [21-63]
+    UpdateItem [21-63]
+      UpdateSetValue [21-63]
+        PathExpression [21-33]
+          Identifier(proto_column) [21-33]
+        BracedConstructor [36-63]
+          BracedConstructorField [38-45]
+            BracedConstructorLhs [38-42]
+              PathExpression [38-42]
+                Identifier(rank) [38-42]
+            BracedConstructorFieldValue [44-45]
+              IntLiteral(1) [44-45]
+          BracedConstructorField [46-61]
+            BracedConstructorLhs [46-56]
+              PathExpression [46-56]
+                Identifier(chart_name) [46-56]
+            BracedConstructorFieldValue [58-61]
+              StringLiteral [58-61]
+                StringLiteralComponent('2') [58-61]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#using_new_map_constructor
+UPDATE TableName
+SET proto_array_column = [
+  { rank: 1 chart_name: '2' }, { rank: 2 chart_name: '3' }]
+--
+UpdateStatement [0-103]
+  PathExpression [7-16]
+    Identifier(TableName) [7-16]
+  UpdateItemList [21-103]
+    UpdateItem [21-103]
+      UpdateSetValue [21-103]
+        PathExpression [21-39]
+          Identifier(proto_array_column) [21-39]
+        ArrayConstructor [42-103]
+          BracedConstructor [46-73]
+            BracedConstructorField [48-55]
+              BracedConstructorLhs [48-52]
+                PathExpression [48-52]
+                  Identifier(rank) [48-52]
+              BracedConstructorFieldValue [54-55]
+                IntLiteral(1) [54-55]
+            BracedConstructorField [56-71]
+              BracedConstructorLhs [56-66]
+                PathExpression [56-66]
+                  Identifier(chart_name) [56-66]
+              BracedConstructorFieldValue [68-71]
+                StringLiteral [68-71]
+                  StringLiteralComponent('2') [68-71]
+          BracedConstructor [75-102]
+            BracedConstructorField [77-84]
+              BracedConstructorLhs [77-81]
+                PathExpression [77-81]
+                  Identifier(rank) [77-81]
+              BracedConstructorFieldValue [83-84]
+                IntLiteral(2) [83-84]
+            BracedConstructorField [85-100]
+              BracedConstructorLhs [85-95]
+                PathExpression [85-95]
+                  Identifier(chart_name) [85-95]
+              BracedConstructorFieldValue [97-100]
+                StringLiteral [97-100]
+                  StringLiteralComponent('3') [97-100]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#using_new_map_constructor
+CREATE TABLE TableName (
+  proto_column googlesql.examples.music.Chart  AS (
+    { rank: 1 chart_name: '2' }))
+--
+CreateTableStatement [0-110]
+  PathExpression [13-22]
+    Identifier(TableName) [13-22]
+  TableElementList [23-110]
+    ColumnDefinition [27-109]
+      Identifier(proto_column) [27-39]
+      SimpleColumnSchema [40-109]
+        PathExpression [40-70]
+          Identifier(googlesql) [40-49]
+          Identifier(examples) [50-58]
+          Identifier(music) [59-64]
+          Identifier(Chart) [65-70]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [72-109]
+          BracedConstructor [81-108]
+            BracedConstructorField [83-90]
+              BracedConstructorLhs [83-87]
+                PathExpression [83-87]
+                  Identifier(rank) [83-87]
+              BracedConstructorFieldValue [89-90]
+                IntLiteral(1) [89-90]
+            BracedConstructorField [91-106]
+              BracedConstructorLhs [91-101]
+                PathExpression [91-101]
+                  Identifier(chart_name) [91-101]
+              BracedConstructorFieldValue [103-106]
+                StringLiteral [103-106]
+                  StringLiteralComponent('2') [103-106]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#using_new_map_constructor
+CREATE TABLE TableName(
+  proto_column googlesql.examples.music.Chart DEFAULT (
+    { rank: 1 chart_name: '2' }))
+--
+CreateTableStatement [0-113]
+  PathExpression [13-22]
+    Identifier(TableName) [13-22]
+  TableElementList [22-113]
+    ColumnDefinition [26-112]
+      Identifier(proto_column) [26-38]
+      SimpleColumnSchema [39-112]
+        PathExpression [39-69]
+          Identifier(googlesql) [39-48]
+          Identifier(examples) [49-57]
+          Identifier(music) [58-63]
+          Identifier(Chart) [64-69]
+        BracedConstructor [84-111]
+          BracedConstructorField [86-93]
+            BracedConstructorLhs [86-90]
+              PathExpression [86-90]
+                Identifier(rank) [86-90]
+            BracedConstructorFieldValue [92-93]
+              IntLiteral(1) [92-93]
+          BracedConstructorField [94-109]
+            BracedConstructorLhs [94-104]
+              PathExpression [94-104]
+                Identifier(chart_name) [94-104]
+            BracedConstructorFieldValue [106-109]
+              StringLiteral [106-109]
+                StringLiteralComponent('2') [106-109]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#time_zones
+SELECT UNIX_MILLIS(TIMESTAMP '2008-12-25 15:30:00 America/Los_Angeles') AS millis
+--
+QueryStatement [0-81]
+  Query [0-81]
+    Select [0-81]
+      SelectList [7-81]
+        SelectColumn [7-81]
+          FunctionCall [7-71]
+            PathExpression [7-18]
+              Identifier(UNIX_MILLIS) [7-18]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [19-70]
+              StringLiteral [29-70]
+                StringLiteralComponent('2008-12-25 15:30:00 America/Los_Angeles') [29-70]
+          Alias [72-81]
+            Identifier(millis) [75-81]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#time_zones
+SELECT UNIX_MILLIS(TIMESTAMP '2008-12-25 15:30:00-08:00') AS millis
+--
+QueryStatement [0-67]
+  Query [0-67]
+    Select [0-67]
+      SelectList [7-67]
+        SelectColumn [7-67]
+          FunctionCall [7-57]
+            PathExpression [7-18]
+              Identifier(UNIX_MILLIS) [7-18]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [19-56]
+              StringLiteral [29-56]
+                StringLiteralComponent('2008-12-25 15:30:00-08:00') [29-56]
+          Alias [58-67]
+            Identifier(millis) [61-67]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#utc
+SELECT TIMESTAMP '2014-09-27T12:30:00.45Z'
+--
+QueryStatement [0-42]
+  Query [0-42]
+    Select [0-42]
+      SelectList [7-42]
+        SelectColumn [7-42]
+          DateOrTimeLiteral(TYPE_TIMESTAMP) [7-42]
+            StringLiteral [17-42]
+              StringLiteralComponent('2014-09-27T12:30:00.45Z') [17-42]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#utc
+SELECT TIMESTAMP '2014-09-27 12:30:00.45z'
+--
+QueryStatement [0-42]
+  Query [0-42]
+    Select [0-42]
+      SelectList [7-42]
+        SelectColumn [7-42]
+          DateOrTimeLiteral(TYPE_TIMESTAMP) [7-42]
+            StringLiteral [17-42]
+              StringLiteralComponent('2014-09-27 12:30:00.45z') [17-42]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#utc
+SELECT TIMESTAMP '2014-09-27T12:30:00.45 Etc/UTC'
+--
+QueryStatement [0-49]
+  Query [0-49]
+    Select [0-49]
+      SelectList [7-49]
+        SelectColumn [7-49]
+          DateOrTimeLiteral(TYPE_TIMESTAMP) [7-49]
+            StringLiteral [17-49]
+              StringLiteralComponent('2014-09-27T12:30:00.45 Etc/UTC') [17-49]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#time_zone_name
+[mode=expression]
+tz_identifier
+--
+PathExpression [0-13]
+  Identifier(tz_identifier) [0-13]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#daylight_saving_time
+SELECT
+FORMAT_TIMESTAMP("%c %Z", "2024-03-10 02:30:00 America/Los_Angeles", "UTC") AS two_thirty,
+FORMAT_TIMESTAMP("%c %Z", "2024-03-10 03:30:00 America/Los_Angeles", "UTC") AS three_thirty
+--
+QueryStatement [0-189]
+  Query [0-189]
+    Select [0-189]
+      SelectList [7-189]
+        SelectColumn [7-96]
+          FunctionCall [7-82]
+            PathExpression [7-23]
+              Identifier(FORMAT_TIMESTAMP) [7-23]
+            StringLiteral [24-31]
+              StringLiteralComponent("%c %Z") [24-31]
+            StringLiteral [33-74]
+              StringLiteralComponent("2024-03-10 02:30:00 America/Los_Angeles") [33-74]
+            StringLiteral [76-81]
+              StringLiteralComponent("UTC") [76-81]
+          Alias [83-96]
+            Identifier(two_thirty) [86-96]
+        SelectColumn [98-189]
+          FunctionCall [98-173]
+            PathExpression [98-114]
+              Identifier(FORMAT_TIMESTAMP) [98-114]
+            StringLiteral [115-122]
+              StringLiteralComponent("%c %Z") [115-122]
+            StringLiteral [124-165]
+              StringLiteralComponent("2024-03-10 03:30:00 America/Los_Angeles") [124-165]
+            StringLiteral [167-172]
+              StringLiteralComponent("UTC") [167-172]
+          Alias [174-189]
+            Identifier(three_thirty) [177-189]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#daylight_saving_time
+SELECT
+FORMAT_TIMESTAMP("%c %Z", "2024-03-10 10:30:00 UTC", "America/Los_Angeles") as ten_thirty
+--
+QueryStatement [0-96]
+  Query [0-96]
+    Select [0-96]
+      SelectList [7-96]
+        SelectColumn [7-96]
+          FunctionCall [7-82]
+            PathExpression [7-23]
+              Identifier(FORMAT_TIMESTAMP) [7-23]
+            StringLiteral [24-31]
+              StringLiteralComponent("%c %Z") [24-31]
+            StringLiteral [33-58]
+              StringLiteralComponent("2024-03-10 10:30:00 UTC") [33-58]
+            StringLiteral [60-81]
+              StringLiteralComponent("America/Los_Angeles") [60-81]
+          Alias [83-96]
+            Identifier(ten_thirty) [86-96]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#daylight_saving_time
+SELECT
+FORMAT_TIMESTAMP("%c %Z", "2024-11-03 01:30:00 America/Los_Angeles", "UTC") as one_thirty,
+FORMAT_TIMESTAMP("%c %Z", "2024-11-03 02:30:00 America/Los_Angeles", "UTC") as two_thirty
+--
+QueryStatement [0-187]
+  Query [0-187]
+    Select [0-187]
+      SelectList [7-187]
+        SelectColumn [7-96]
+          FunctionCall [7-82]
+            PathExpression [7-23]
+              Identifier(FORMAT_TIMESTAMP) [7-23]
+            StringLiteral [24-31]
+              StringLiteralComponent("%c %Z") [24-31]
+            StringLiteral [33-74]
+              StringLiteralComponent("2024-11-03 01:30:00 America/Los_Angeles") [33-74]
+            StringLiteral [76-81]
+              StringLiteralComponent("UTC") [76-81]
+          Alias [83-96]
+            Identifier(one_thirty) [86-96]
+        SelectColumn [98-187]
+          FunctionCall [98-173]
+            PathExpression [98-114]
+              Identifier(FORMAT_TIMESTAMP) [98-114]
+            StringLiteral [115-122]
+              StringLiteralComponent("%c %Z") [115-122]
+            StringLiteral [124-165]
+              StringLiteralComponent("2024-11-03 02:30:00 America/Los_Angeles") [124-165]
+            StringLiteral [167-172]
+              StringLiteralComponent("UTC") [167-172]
+          Alias [174-187]
+            Identifier(two_thirty) [177-187]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#cast_a_uuid_to_a_string
+SELECT CAST(NEW_UUID() AS STRING) AS UUID_STR
+--
+QueryStatement [0-45]
+  Query [0-45]
+    Select [0-45]
+      SelectList [7-45]
+        SelectColumn [7-45]
+          CastExpression [7-33]
+            FunctionCall [12-22]
+              PathExpression [12-20]
+                Identifier(NEW_UUID) [12-20]
+            SimpleType [26-32]
+              PathExpression [26-32]
+                Identifier(STRING) [26-32]
+          Alias [34-45]
+            Identifier(UUID_STR) [37-45]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#cast_a_uuid_to_a_string
+SELECT UUID_id >= CAST("00000000-0000-0000-0000-000000000000" AS UUID) FROM T1
+--
+QueryStatement [0-78]
+  Query [0-78]
+    Select [0-78]
+      SelectList [7-70]
+        SelectColumn [7-70]
+          BinaryExpression(>=) [7-70]
+            PathExpression [7-14]
+              Identifier(UUID_id) [7-14]
+            CastExpression [18-70]
+              StringLiteral [23-61]
+                StringLiteralComponent("00000000-0000-0000-0000-000000000000") [23-61]
+              SimpleType [65-69]
+                PathExpression [65-69]
+                  Identifier(UUID) [65-69]
+      FromClause [71-78]
+        TablePathExpression [76-78]
+          PathExpression [76-78]
+            Identifier(T1) [76-78]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#cast_a_uuid_to_a_string
+SELECT UUID_id >= "00000000-0000-0000-0000-000000000000" FROM T1
+--
+QueryStatement [0-64]
+  Query [0-64]
+    Select [0-64]
+      SelectList [7-56]
+        SelectColumn [7-56]
+          BinaryExpression(>=) [7-56]
+            PathExpression [7-14]
+              Identifier(UUID_id) [7-14]
+            StringLiteral [18-56]
+              StringLiteralComponent("00000000-0000-0000-0000-000000000000") [18-56]
+      FromClause [57-64]
+        TablePathExpression [62-64]
+          PathExpression [62-64]
+            Identifier(T1) [62-64]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#cast_a_uuid_to_bytes
+SELECT CAST(NEW_UUID() AS BYTES) AS UUID_BYTES
+--
+QueryStatement [0-46]
+  Query [0-46]
+    Select [0-46]
+      SelectList [7-46]
+        SelectColumn [7-46]
+          CastExpression [7-32]
+            FunctionCall [12-22]
+              PathExpression [12-20]
+                Identifier(NEW_UUID) [12-20]
+            SimpleType [26-31]
+              PathExpression [26-31]
+                Identifier(BYTES) [26-31]
+          Alias [33-46]
+            Identifier(UUID_BYTES) [36-46]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/data-types#comparison_operator_examples_2
+SELECT NEW_UUID() >= "00000000-0000-0000-0000-000000000000" AS Is_GE
+--
+QueryStatement [0-68]
+  Query [0-68]
+    Select [0-68]
+      SelectList [7-68]
+        SelectColumn [7-68]
+          BinaryExpression(>=) [7-59]
+            FunctionCall [7-17]
+              PathExpression [7-15]
+                Identifier(NEW_UUID) [7-15]
+            StringLiteral [21-59]
+              StringLiteralComponent("00000000-0000-0000-0000-000000000000") [21-59]
+          Alias [60-68]
+            Identifier(Is_GE) [63-68]
diff --git a/parser/testdata/spanner/date_functions.test b/parser/testdata/spanner/date_functions.test
new file mode 100644
index 0000000..d5cefa3
--- /dev/null
+++ b/parser/testdata/spanner/date_functions.test
@@ -0,0 +1,854 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#adddate
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+ADDDATE(date_expression, INTERVAL step_size step_unit)
+--
+FunctionCall [0-54]
+  PathExpression [0-7]
+    Identifier(ADDDATE) [0-7]
+  PathExpression [8-23]
+    Identifier(date_expression) [8-23]
+  IntervalExpr [25-53]
+    PathExpression [34-43]
+      Identifier(step_size) [34-43]
+    Identifier(step_unit) [44-53]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#current_date
+[mode=expression]
+CURRENT_DATE()
+--
+FunctionCall [0-14]
+  PathExpression [0-12]
+    Identifier(`CURRENT_DATE`) [0-12]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#current_date
+[mode=expression]
+CURRENT_DATE(time_zone_expression)
+--
+FunctionCall [0-34]
+  PathExpression [0-12]
+    Identifier(`CURRENT_DATE`) [0-12]
+  PathExpression [13-33]
+    Identifier(time_zone_expression) [13-33]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#current_date
+[mode=expression]
+CURRENT_DATE
+--
+FunctionCall [0-12]
+  PathExpression [0-12]
+    Identifier(`CURRENT_DATE`) [0-12]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#current_date
+SELECT CURRENT_DATE() AS the_date
+--
+QueryStatement [0-33]
+  Query [0-33]
+    Select [0-33]
+      SelectList [7-33]
+        SelectColumn [7-33]
+          FunctionCall [7-21]
+            PathExpression [7-19]
+              Identifier(`CURRENT_DATE`) [7-19]
+          Alias [22-33]
+            Identifier(the_date) [25-33]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#current_date
+SELECT CURRENT_DATE('America/Los_Angeles') AS the_date
+--
+QueryStatement [0-54]
+  Query [0-54]
+    Select [0-54]
+      SelectList [7-54]
+        SelectColumn [7-54]
+          FunctionCall [7-42]
+            PathExpression [7-19]
+              Identifier(`CURRENT_DATE`) [7-19]
+            StringLiteral [20-41]
+              StringLiteralComponent('America/Los_Angeles') [20-41]
+          Alias [43-54]
+            Identifier(the_date) [46-54]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#current_date
+SELECT CURRENT_DATE('-08') AS the_date
+--
+QueryStatement [0-38]
+  Query [0-38]
+    Select [0-38]
+      SelectList [7-38]
+        SelectColumn [7-38]
+          FunctionCall [7-26]
+            PathExpression [7-19]
+              Identifier(`CURRENT_DATE`) [7-19]
+            StringLiteral [20-25]
+              StringLiteralComponent('-08') [20-25]
+          Alias [27-38]
+            Identifier(the_date) [30-38]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#current_date
+SELECT CURRENT_DATE AS the_date
+--
+QueryStatement [0-31]
+  Query [0-31]
+    Select [0-31]
+      SelectList [7-31]
+        SelectColumn [7-31]
+          FunctionCall [7-19]
+            PathExpression [7-19]
+              Identifier(`CURRENT_DATE`) [7-19]
+          Alias [20-31]
+            Identifier(the_date) [23-31]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#date
+[mode=expression]
+DATE(year, month, day)
+--
+FunctionCall [0-22]
+  PathExpression [0-4]
+    Identifier(DATE) [0-4]
+  PathExpression [5-9]
+    Identifier(year) [5-9]
+  PathExpression [11-16]
+    Identifier(month) [11-16]
+  PathExpression [18-21]
+    Identifier(day) [18-21]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#date
+[mode=expression]
+DATE(timestamp_expression)
+--
+FunctionCall [0-26]
+  PathExpression [0-4]
+    Identifier(DATE) [0-4]
+  PathExpression [5-25]
+    Identifier(timestamp_expression) [5-25]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#date
+[mode=expression]
+DATE(timestamp_expression, time_zone_expression)
+--
+FunctionCall [0-48]
+  PathExpression [0-4]
+    Identifier(DATE) [0-4]
+  PathExpression [5-25]
+    Identifier(timestamp_expression) [5-25]
+  PathExpression [27-47]
+    Identifier(time_zone_expression) [27-47]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#date
+SELECT
+  DATE(2016, 12, 25) AS date_ymd,
+  DATE(TIMESTAMP '2016-12-25 05:30:00+07', 'America/Los_Angeles') AS date_tstz
+--
+QueryStatement [0-119]
+  Query [0-119]
+    Select [0-119]
+      SelectList [9-119]
+        SelectColumn [9-39]
+          FunctionCall [9-27]
+            PathExpression [9-13]
+              Identifier(DATE) [9-13]
+            IntLiteral(2016) [14-18]
+            IntLiteral(12) [20-22]
+            IntLiteral(25) [24-26]
+          Alias [28-39]
+            Identifier(date_ymd) [31-39]
+        SelectColumn [43-119]
+          FunctionCall [43-106]
+            PathExpression [43-47]
+              Identifier(DATE) [43-47]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [48-82]
+              StringLiteral [58-82]
+                StringLiteralComponent('2016-12-25 05:30:00+07') [58-82]
+            StringLiteral [84-105]
+              StringLiteralComponent('America/Los_Angeles') [84-105]
+          Alias [107-119]
+            Identifier(date_tstz) [110-119]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#date_add
+[mode=expression]
+DATE_ADD(date_expression, INTERVAL step_size step_unit)
+--
+FunctionCall [0-55]
+  PathExpression [0-8]
+    Identifier(DATE_ADD) [0-8]
+  PathExpression [9-24]
+    Identifier(date_expression) [9-24]
+  IntervalExpr [26-54]
+    PathExpression [35-44]
+      Identifier(step_size) [35-44]
+    Identifier(step_unit) [45-54]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#date_add
+SELECT DATE_ADD(DATE '2008-12-25', INTERVAL 5 DAY) AS five_days_later
+--
+QueryStatement [0-69]
+  Query [0-69]
+    Select [0-69]
+      SelectList [7-69]
+        SelectColumn [7-69]
+          FunctionCall [7-50]
+            PathExpression [7-15]
+              Identifier(DATE_ADD) [7-15]
+            DateOrTimeLiteral(TYPE_DATE) [16-33]
+              StringLiteral [21-33]
+                StringLiteralComponent('2008-12-25') [21-33]
+            IntervalExpr [35-49]
+              IntLiteral(5) [44-45]
+              Identifier(DAY) [46-49]
+          Alias [51-69]
+            Identifier(five_days_later) [54-69]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#date_diff
+[mode=expression]
+DATE_DIFF(end_date, start_date, granularity)
+--
+FunctionCall [0-44]
+  PathExpression [0-9]
+    Identifier(DATE_DIFF) [0-9]
+  PathExpression [10-18]
+    Identifier(end_date) [10-18]
+  PathExpression [20-30]
+    Identifier(start_date) [20-30]
+  PathExpression [32-43]
+    Identifier(granularity) [32-43]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#date_diff
+SELECT DATE_DIFF(DATE '2010-07-07', DATE '2008-12-25', DAY) AS days_diff
+--
+QueryStatement [0-72]
+  Query [0-72]
+    Select [0-72]
+      SelectList [7-72]
+        SelectColumn [7-72]
+          FunctionCall [7-59]
+            PathExpression [7-16]
+              Identifier(DATE_DIFF) [7-16]
+            DateOrTimeLiteral(TYPE_DATE) [17-34]
+              StringLiteral [22-34]
+                StringLiteralComponent('2010-07-07') [22-34]
+            DateOrTimeLiteral(TYPE_DATE) [36-53]
+              StringLiteral [41-53]
+                StringLiteralComponent('2008-12-25') [41-53]
+            PathExpression [55-58]
+              Identifier(DAY) [55-58]
+          Alias [60-72]
+            Identifier(days_diff) [63-72]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#date_diff
+SELECT
+  DATE_DIFF(DATE '2023-11-30', DATE '2020-01-01', YEAR) AS diff_3_9,
+  DATE_DIFF(DATE '2023-02-01', DATE '2020-01-01', YEAR) AS diff_3_1,
+  DATE_DIFF(DATE '2020-01-01', DATE '2023-11-30', YEAR) AS diff_negative_3_9,
+  DATE_DIFF(DATE '2020-01-01', DATE '2023-02-01', YEAR) AS diff_negative_3_1
+--
+QueryStatement [0-299]
+  Query [0-299]
+    Select [0-299]
+      SelectList [9-299]
+        SelectColumn [9-74]
+          FunctionCall [9-62]
+            PathExpression [9-18]
+              Identifier(DATE_DIFF) [9-18]
+            DateOrTimeLiteral(TYPE_DATE) [19-36]
+              StringLiteral [24-36]
+                StringLiteralComponent('2023-11-30') [24-36]
+            DateOrTimeLiteral(TYPE_DATE) [38-55]
+              StringLiteral [43-55]
+                StringLiteralComponent('2020-01-01') [43-55]
+            PathExpression [57-61]
+              Identifier(YEAR) [57-61]
+          Alias [63-74]
+            Identifier(diff_3_9) [66-74]
+        SelectColumn [78-143]
+          FunctionCall [78-131]
+            PathExpression [78-87]
+              Identifier(DATE_DIFF) [78-87]
+            DateOrTimeLiteral(TYPE_DATE) [88-105]
+              StringLiteral [93-105]
+                StringLiteralComponent('2023-02-01') [93-105]
+            DateOrTimeLiteral(TYPE_DATE) [107-124]
+              StringLiteral [112-124]
+                StringLiteralComponent('2020-01-01') [112-124]
+            PathExpression [126-130]
+              Identifier(YEAR) [126-130]
+          Alias [132-143]
+            Identifier(diff_3_1) [135-143]
+        SelectColumn [147-221]
+          FunctionCall [147-200]
+            PathExpression [147-156]
+              Identifier(DATE_DIFF) [147-156]
+            DateOrTimeLiteral(TYPE_DATE) [157-174]
+              StringLiteral [162-174]
+                StringLiteralComponent('2020-01-01') [162-174]
+            DateOrTimeLiteral(TYPE_DATE) [176-193]
+              StringLiteral [181-193]
+                StringLiteralComponent('2023-11-30') [181-193]
+            PathExpression [195-199]
+              Identifier(YEAR) [195-199]
+          Alias [201-221]
+            Identifier(diff_negative_3_9) [204-221]
+        SelectColumn [225-299]
+          FunctionCall [225-278]
+            PathExpression [225-234]
+              Identifier(DATE_DIFF) [225-234]
+            DateOrTimeLiteral(TYPE_DATE) [235-252]
+              StringLiteral [240-252]
+                StringLiteralComponent('2020-01-01') [240-252]
+            DateOrTimeLiteral(TYPE_DATE) [254-271]
+              StringLiteral [259-271]
+                StringLiteralComponent('2023-02-01') [259-271]
+            PathExpression [273-277]
+              Identifier(YEAR) [273-277]
+          Alias [279-299]
+            Identifier(diff_negative_3_1) [282-299]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#date_diff
+SELECT
+  DATE_DIFF(DATE '2017-10-15', DATE '2017-10-14', DAY) AS days_diff,
+  DATE_DIFF(DATE '2017-10-15', DATE '2017-10-14', WEEK) AS weeks_diff
+--
+QueryStatement [0-145]
+  Query [0-145]
+    Select [0-145]
+      SelectList [9-145]
+        SelectColumn [9-74]
+          FunctionCall [9-61]
+            PathExpression [9-18]
+              Identifier(DATE_DIFF) [9-18]
+            DateOrTimeLiteral(TYPE_DATE) [19-36]
+              StringLiteral [24-36]
+                StringLiteralComponent('2017-10-15') [24-36]
+            DateOrTimeLiteral(TYPE_DATE) [38-55]
+              StringLiteral [43-55]
+                StringLiteralComponent('2017-10-14') [43-55]
+            PathExpression [57-60]
+              Identifier(DAY) [57-60]
+          Alias [62-74]
+            Identifier(days_diff) [65-74]
+        SelectColumn [78-145]
+          FunctionCall [78-131]
+            PathExpression [78-87]
+              Identifier(DATE_DIFF) [78-87]
+            DateOrTimeLiteral(TYPE_DATE) [88-105]
+              StringLiteral [93-105]
+                StringLiteralComponent('2017-10-15') [93-105]
+            DateOrTimeLiteral(TYPE_DATE) [107-124]
+              StringLiteral [112-124]
+                StringLiteralComponent('2017-10-14') [112-124]
+            PathExpression [126-130]
+              Identifier(WEEK) [126-130]
+          Alias [132-145]
+            Identifier(weeks_diff) [135-145]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#date_diff
+SELECT
+  DATE_DIFF('2017-12-30', '2014-12-30', YEAR) AS year_diff,
+  DATE_DIFF('2017-12-30', '2014-12-30', ISOYEAR) AS isoyear_diff
+--
+QueryStatement [0-131]
+  Query [0-131]
+    Select [0-131]
+      SelectList [9-131]
+        SelectColumn [9-65]
+          FunctionCall [9-52]
+            PathExpression [9-18]
+              Identifier(DATE_DIFF) [9-18]
+            StringLiteral [19-31]
+              StringLiteralComponent('2017-12-30') [19-31]
+            StringLiteral [33-45]
+              StringLiteralComponent('2014-12-30') [33-45]
+            PathExpression [47-51]
+              Identifier(YEAR) [47-51]
+          Alias [53-65]
+            Identifier(year_diff) [56-65]
+        SelectColumn [69-131]
+          FunctionCall [69-115]
+            PathExpression [69-78]
+              Identifier(DATE_DIFF) [69-78]
+            StringLiteral [79-91]
+              StringLiteralComponent('2017-12-30') [79-91]
+            StringLiteral [93-105]
+              StringLiteralComponent('2014-12-30') [93-105]
+            PathExpression [107-114]
+              Identifier(ISOYEAR) [107-114]
+          Alias [116-131]
+            Identifier(isoyear_diff) [119-131]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#date_diff
+SELECT
+  DATE_DIFF('2017-12-18', '2017-12-17', WEEK) AS week_diff,
+  DATE_DIFF('2017-12-18', '2017-12-17', ISOWEEK) AS isoweek_diff
+--
+QueryStatement [0-131]
+  Query [0-131]
+    Select [0-131]
+      SelectList [9-131]
+        SelectColumn [9-65]
+          FunctionCall [9-52]
+            PathExpression [9-18]
+              Identifier(DATE_DIFF) [9-18]
+            StringLiteral [19-31]
+              StringLiteralComponent('2017-12-18') [19-31]
+            StringLiteral [33-45]
+              StringLiteralComponent('2017-12-17') [33-45]
+            PathExpression [47-51]
+              Identifier(WEEK) [47-51]
+          Alias [53-65]
+            Identifier(week_diff) [56-65]
+        SelectColumn [69-131]
+          FunctionCall [69-115]
+            PathExpression [69-78]
+              Identifier(DATE_DIFF) [69-78]
+            StringLiteral [79-91]
+              StringLiteralComponent('2017-12-18') [79-91]
+            StringLiteral [93-105]
+              StringLiteralComponent('2017-12-17') [93-105]
+            PathExpression [107-114]
+              Identifier(ISOWEEK) [107-114]
+          Alias [116-131]
+            Identifier(isoweek_diff) [119-131]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#date_from_unix_date
+[mode=expression]
+DATE_FROM_UNIX_DATE(int64_expression)
+--
+FunctionCall [0-37]
+  PathExpression [0-19]
+    Identifier(DATE_FROM_UNIX_DATE) [0-19]
+  PathExpression [20-36]
+    Identifier(int64_expression) [20-36]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#date_from_unix_date
+SELECT DATE_FROM_UNIX_DATE(14238) AS date_from_epoch
+--
+QueryStatement [0-52]
+  Query [0-52]
+    Select [0-52]
+      SelectList [7-52]
+        SelectColumn [7-52]
+          FunctionCall [7-33]
+            PathExpression [7-26]
+              Identifier(DATE_FROM_UNIX_DATE) [7-26]
+            IntLiteral(14238) [27-32]
+          Alias [34-52]
+            Identifier(date_from_epoch) [37-52]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#date_sub
+[mode=expression]
+DATE_SUB(date_expression, INTERVAL step_size step_unit)
+--
+FunctionCall [0-55]
+  PathExpression [0-8]
+    Identifier(DATE_SUB) [0-8]
+  PathExpression [9-24]
+    Identifier(date_expression) [9-24]
+  IntervalExpr [26-54]
+    PathExpression [35-44]
+      Identifier(step_size) [35-44]
+    Identifier(step_unit) [45-54]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#date_sub
+SELECT DATE_SUB(DATE '2008-12-25', INTERVAL 5 DAY) AS five_days_ago
+--
+QueryStatement [0-67]
+  Query [0-67]
+    Select [0-67]
+      SelectList [7-67]
+        SelectColumn [7-67]
+          FunctionCall [7-50]
+            PathExpression [7-15]
+              Identifier(DATE_SUB) [7-15]
+            DateOrTimeLiteral(TYPE_DATE) [16-33]
+              StringLiteral [21-33]
+                StringLiteralComponent('2008-12-25') [21-33]
+            IntervalExpr [35-49]
+              IntLiteral(5) [44-45]
+              Identifier(DAY) [46-49]
+          Alias [51-67]
+            Identifier(five_days_ago) [54-67]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#date_trunc
+[mode=expression]
+DATE_TRUNC(date_value, date_granularity)
+--
+FunctionCall [0-40]
+  PathExpression [0-10]
+    Identifier(DATE_TRUNC) [0-10]
+  PathExpression [11-21]
+    Identifier(date_value) [11-21]
+  PathExpression [23-39]
+    Identifier(date_granularity) [23-39]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#date_trunc
+SELECT DATE_TRUNC(DATE '2008-12-25', MONTH) AS month
+--
+QueryStatement [0-52]
+  Query [0-52]
+    Select [0-52]
+      SelectList [7-52]
+        SelectColumn [7-52]
+          FunctionCall [7-43]
+            PathExpression [7-17]
+              Identifier(DATE_TRUNC) [7-17]
+            DateOrTimeLiteral(TYPE_DATE) [18-35]
+              StringLiteral [23-35]
+                StringLiteralComponent('2008-12-25') [23-35]
+            PathExpression [37-42]
+              Identifier(MONTH) [37-42]
+          Alias [44-52]
+            Identifier(month) [47-52]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#date_trunc
+SELECT
+  DATE_TRUNC('2015-06-15', ISOYEAR) AS isoyear_boundary,
+  EXTRACT(ISOYEAR FROM DATE '2015-06-15') AS isoyear_number
+--
+QueryStatement [0-123]
+  Query [0-123]
+    Select [0-123]
+      SelectList [9-123]
+        SelectColumn [9-62]
+          FunctionCall [9-42]
+            PathExpression [9-19]
+              Identifier(DATE_TRUNC) [9-19]
+            StringLiteral [20-32]
+              StringLiteralComponent('2015-06-15') [20-32]
+            PathExpression [34-41]
+              Identifier(ISOYEAR) [34-41]
+          Alias [43-62]
+            Identifier(isoyear_boundary) [46-62]
+        SelectColumn [66-123]
+          ExtractExpression [66-105]
+            PathExpression [74-81]
+              Identifier(ISOYEAR) [74-81]
+            DateOrTimeLiteral(TYPE_DATE) [87-104]
+              StringLiteral [92-104]
+                StringLiteralComponent('2015-06-15') [92-104]
+          Alias [106-123]
+            Identifier(isoyear_number) [109-123]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#extract
+[mode=expression]
+EXTRACT(part FROM date_expression)
+--
+ExtractExpression [0-34]
+  PathExpression [8-12]
+    Identifier(part) [8-12]
+  PathExpression [18-33]
+    Identifier(date_expression) [18-33]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#extract
+SELECT EXTRACT(DAY FROM DATE '2013-12-25') AS the_day
+--
+QueryStatement [0-53]
+  Query [0-53]
+    Select [0-53]
+      SelectList [7-53]
+        SelectColumn [7-53]
+          ExtractExpression [7-42]
+            PathExpression [15-18]
+              Identifier(DAY) [15-18]
+            DateOrTimeLiteral(TYPE_DATE) [24-41]
+              StringLiteral [29-41]
+                StringLiteralComponent('2013-12-25') [29-41]
+          Alias [43-53]
+            Identifier(the_day) [46-53]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#extract
+SELECT
+  date,
+  EXTRACT(ISOYEAR FROM date) AS isoyear,
+  EXTRACT(ISOWEEK FROM date) AS isoweek,
+  EXTRACT(YEAR FROM date) AS year,
+  EXTRACT(WEEK FROM date) AS week
+FROM UNNEST(GENERATE_DATE_ARRAY('2015-12-23', '2016-01-09')) AS date
+ORDER BY date
+--
+QueryStatement [0-248]
+  Query [0-248]
+    Select [0-234]
+      SelectList [9-165]
+        SelectColumn [9-13]
+          PathExpression [9-13]
+            Identifier(date) [9-13]
+        SelectColumn [17-54]
+          ExtractExpression [17-43]
+            PathExpression [25-32]
+              Identifier(ISOYEAR) [25-32]
+            PathExpression [38-42]
+              Identifier(date) [38-42]
+          Alias [44-54]
+            Identifier(isoyear) [47-54]
+        SelectColumn [58-95]
+          ExtractExpression [58-84]
+            PathExpression [66-73]
+              Identifier(ISOWEEK) [66-73]
+            PathExpression [79-83]
+              Identifier(date) [79-83]
+          Alias [85-95]
+            Identifier(isoweek) [88-95]
+        SelectColumn [99-130]
+          ExtractExpression [99-122]
+            PathExpression [107-111]
+              Identifier(YEAR) [107-111]
+            PathExpression [117-121]
+              Identifier(date) [117-121]
+          Alias [123-130]
+            Identifier(year) [126-130]
+        SelectColumn [134-165]
+          ExtractExpression [134-157]
+            PathExpression [142-146]
+              Identifier(WEEK) [142-146]
+            PathExpression [152-156]
+              Identifier(date) [152-156]
+          Alias [158-165]
+            Identifier(week) [161-165]
+      FromClause [166-234]
+        TablePathExpression [171-234]
+          UnnestExpression [171-226]
+            ExpressionWithOptAlias [178-225]
+              FunctionCall [178-225]
+                PathExpression [178-197]
+                  Identifier(GENERATE_DATE_ARRAY) [178-197]
+                StringLiteral [198-210]
+                  StringLiteralComponent('2015-12-23') [198-210]
+                StringLiteral [212-224]
+                  StringLiteralComponent('2016-01-09') [212-224]
+          Alias [227-234]
+            Identifier(date) [230-234]
+    OrderBy [235-248]
+      OrderingExpression(ASC) [244-248]
+        PathExpression [244-248]
+          Identifier(date) [244-248]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#format_date
+[mode=expression]
+FORMAT_DATE(format_string, date_expr)
+--
+FunctionCall [0-37]
+  PathExpression [0-11]
+    Identifier(FORMAT_DATE) [0-11]
+  PathExpression [12-25]
+    Identifier(format_string) [12-25]
+  PathExpression [27-36]
+    Identifier(date_expr) [27-36]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#format_date
+SELECT FORMAT_DATE('%x', DATE '2008-12-25') AS US_format
+--
+QueryStatement [0-56]
+  Query [0-56]
+    Select [0-56]
+      SelectList [7-56]
+        SelectColumn [7-56]
+          FunctionCall [7-43]
+            PathExpression [7-18]
+              Identifier(FORMAT_DATE) [7-18]
+            StringLiteral [19-23]
+              StringLiteralComponent('%x') [19-23]
+            DateOrTimeLiteral(TYPE_DATE) [25-42]
+              StringLiteral [30-42]
+                StringLiteralComponent('2008-12-25') [30-42]
+          Alias [44-56]
+            Identifier(US_format) [47-56]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#format_date
+SELECT FORMAT_DATE('%b-%d-%Y', DATE '2008-12-25') AS formatted
+--
+QueryStatement [0-62]
+  Query [0-62]
+    Select [0-62]
+      SelectList [7-62]
+        SelectColumn [7-62]
+          FunctionCall [7-49]
+            PathExpression [7-18]
+              Identifier(FORMAT_DATE) [7-18]
+            StringLiteral [19-29]
+              StringLiteralComponent('%b-%d-%Y') [19-29]
+            DateOrTimeLiteral(TYPE_DATE) [31-48]
+              StringLiteral [36-48]
+                StringLiteralComponent('2008-12-25') [36-48]
+          Alias [50-62]
+            Identifier(formatted) [53-62]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#format_date
+SELECT FORMAT_DATE('%b %Y', DATE '2008-12-25') AS formatted
+--
+QueryStatement [0-59]
+  Query [0-59]
+    Select [0-59]
+      SelectList [7-59]
+        SelectColumn [7-59]
+          FunctionCall [7-46]
+            PathExpression [7-18]
+              Identifier(FORMAT_DATE) [7-18]
+            StringLiteral [19-26]
+              StringLiteralComponent('%b %Y') [19-26]
+            DateOrTimeLiteral(TYPE_DATE) [28-45]
+              StringLiteral [33-45]
+                StringLiteralComponent('2008-12-25') [33-45]
+          Alias [47-59]
+            Identifier(formatted) [50-59]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#parse_date
+[mode=expression]
+PARSE_DATE(format_string, date_string)
+--
+FunctionCall [0-38]
+  PathExpression [0-10]
+    Identifier(PARSE_DATE) [0-10]
+  PathExpression [11-24]
+    Identifier(format_string) [11-24]
+  PathExpression [26-37]
+    Identifier(date_string) [26-37]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#parse_date
+-- This works because elements on both sides match.
+SELECT PARSE_DATE('%A %b %e %Y', 'Thursday Dec 25 2008')
+--
+QueryStatement [52-108]
+  Query [52-108]
+    Select [52-108]
+      SelectList [59-108]
+        SelectColumn [59-108]
+          FunctionCall [59-108]
+            PathExpression [59-69]
+              Identifier(PARSE_DATE) [59-69]
+            StringLiteral [70-83]
+              StringLiteralComponent('%A %b %e %Y') [70-83]
+            StringLiteral [85-107]
+              StringLiteralComponent('Thursday Dec 25 2008') [85-107]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#parse_date
+-- This produces an error because the year element is in different locations.
+SELECT PARSE_DATE('%Y %A %b %e', 'Thursday Dec 25 2008')
+--
+QueryStatement [78-134]
+  Query [78-134]
+    Select [78-134]
+      SelectList [85-134]
+        SelectColumn [85-134]
+          FunctionCall [85-134]
+            PathExpression [85-95]
+              Identifier(PARSE_DATE) [85-95]
+            StringLiteral [96-109]
+              StringLiteralComponent('%Y %A %b %e') [96-109]
+            StringLiteral [111-133]
+              StringLiteralComponent('Thursday Dec 25 2008') [111-133]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#parse_date
+-- This produces an error because one of the year elements is missing.
+SELECT PARSE_DATE('%A %b %e', 'Thursday Dec 25 2008')
+--
+QueryStatement [71-124]
+  Query [71-124]
+    Select [71-124]
+      SelectList [78-124]
+        SelectColumn [78-124]
+          FunctionCall [78-124]
+            PathExpression [78-88]
+              Identifier(PARSE_DATE) [78-88]
+            StringLiteral [89-99]
+              StringLiteralComponent('%A %b %e') [89-99]
+            StringLiteral [101-123]
+              StringLiteralComponent('Thursday Dec 25 2008') [101-123]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#parse_date
+-- This works because %F can find all matching elements in date_string.
+SELECT PARSE_DATE('%F', '2000-12-30')
+--
+QueryStatement [72-109]
+  Query [72-109]
+    Select [72-109]
+      SelectList [79-109]
+        SelectColumn [79-109]
+          FunctionCall [79-109]
+            PathExpression [79-89]
+              Identifier(PARSE_DATE) [79-89]
+            StringLiteral [90-94]
+              StringLiteralComponent('%F') [90-94]
+            StringLiteral [96-108]
+              StringLiteralComponent('2000-12-30') [96-108]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#parse_date
+SELECT PARSE_DATE('%x', '12/25/08') AS parsed
+--
+QueryStatement [0-45]
+  Query [0-45]
+    Select [0-45]
+      SelectList [7-45]
+        SelectColumn [7-45]
+          FunctionCall [7-35]
+            PathExpression [7-17]
+              Identifier(PARSE_DATE) [7-17]
+            StringLiteral [18-22]
+              StringLiteralComponent('%x') [18-22]
+            StringLiteral [24-34]
+              StringLiteralComponent('12/25/08') [24-34]
+          Alias [36-45]
+            Identifier(parsed) [39-45]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#parse_date
+SELECT PARSE_DATE('%Y%m%d', '20081225') AS parsed
+--
+QueryStatement [0-49]
+  Query [0-49]
+    Select [0-49]
+      SelectList [7-49]
+        SelectColumn [7-49]
+          FunctionCall [7-39]
+            PathExpression [7-17]
+              Identifier(PARSE_DATE) [7-17]
+            StringLiteral [18-26]
+              StringLiteralComponent('%Y%m%d') [18-26]
+            StringLiteral [28-38]
+              StringLiteralComponent('20081225') [28-38]
+          Alias [40-49]
+            Identifier(parsed) [43-49]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#subdate
+[mode=expression]
+SUBDATE(date_expression, INTERVAL step_size step_unit)
+--
+FunctionCall [0-54]
+  PathExpression [0-7]
+    Identifier(SUBDATE) [0-7]
+  PathExpression [8-23]
+    Identifier(date_expression) [8-23]
+  IntervalExpr [25-53]
+    PathExpression [34-43]
+      Identifier(step_size) [34-43]
+    Identifier(step_unit) [44-53]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#unix_date
+[mode=expression]
+UNIX_DATE(date_expression)
+--
+FunctionCall [0-26]
+  PathExpression [0-9]
+    Identifier(UNIX_DATE) [0-9]
+  PathExpression [10-25]
+    Identifier(date_expression) [10-25]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/date_functions#unix_date
+SELECT UNIX_DATE(DATE '2008-12-25') AS days_from_epoch
+--
+QueryStatement [0-54]
+  Query [0-54]
+    Select [0-54]
+      SelectList [7-54]
+        SelectColumn [7-54]
+          FunctionCall [7-35]
+            PathExpression [7-16]
+              Identifier(UNIX_DATE) [7-16]
+            DateOrTimeLiteral(TYPE_DATE) [17-34]
+              StringLiteral [22-34]
+                StringLiteralComponent('2008-12-25') [22-34]
+          Alias [36-54]
+            Identifier(days_from_epoch) [39-54]
diff --git a/parser/testdata/spanner/debugging_functions.test b/parser/testdata/spanner/debugging_functions.test
new file mode 100644
index 0000000..3c1f0d8
--- /dev/null
+++ b/parser/testdata/spanner/debugging_functions.test
@@ -0,0 +1,148 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/debugging_functions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/debugging_functions#error
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+ERROR(error_message)
+--
+FunctionCall [0-20]
+  PathExpression [0-5]
+    Identifier(ERROR) [0-5]
+  PathExpression [6-19]
+    Identifier(error_message) [6-19]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/debugging_functions#error
+-- ERROR: Show this error message (while evaluating error("Show this error message"))
+SELECT ERROR('Show this error message')
+--
+QueryStatement [86-125]
+  Query [86-125]
+    Select [86-125]
+      SelectList [93-125]
+        SelectColumn [93-125]
+          FunctionCall [93-125]
+            PathExpression [93-98]
+              Identifier(ERROR) [93-98]
+            StringLiteral [99-124]
+              StringLiteralComponent('Show this error message') [99-124]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/debugging_functions#error
+SELECT
+  CASE
+    WHEN value = 'foo' THEN 'Value is foo.'
+    WHEN value = 'bar' THEN 'Value is bar.'
+    ELSE ERROR(CONCAT('Found unexpected value: ', value))
+  END AS new_value
+FROM (
+  SELECT 'foo' AS value UNION ALL
+  SELECT 'bar' AS value UNION ALL
+  SELECT 'baz' AS value)
+--
+QueryStatement [0-278]
+  Query [0-278]
+    Select [0-278]
+      SelectList [9-178]
+        SelectColumn [9-178]
+          CaseNoValueExpression [9-165]
+            BinaryExpression(=) [23-36]
+              PathExpression [23-28]
+                Identifier(value) [23-28]
+              StringLiteral [31-36]
+                StringLiteralComponent('foo') [31-36]
+            StringLiteral [42-57]
+              StringLiteralComponent('Value is foo.') [42-57]
+            BinaryExpression(=) [67-80]
+              PathExpression [67-72]
+                Identifier(value) [67-72]
+              StringLiteral [75-80]
+                StringLiteralComponent('bar') [75-80]
+            StringLiteral [86-101]
+              StringLiteralComponent('Value is bar.') [86-101]
+            FunctionCall [111-159]
+              PathExpression [111-116]
+                Identifier(ERROR) [111-116]
+              FunctionCall [117-158]
+                PathExpression [117-123]
+                  Identifier(CONCAT) [117-123]
+                StringLiteral [124-150]
+                  StringLiteralComponent('Found unexpected value: ') [124-150]
+                PathExpression [152-157]
+                  Identifier(value) [152-157]
+          Alias [166-178]
+            Identifier(new_value) [169-178]
+      FromClause [179-278]
+        TableSubquery [184-278]
+          Query [188-277]
+            SetOperation(UNION ALL) [188-277]
+              SetOperationMetadataList [210-253]
+                SetOperationMetadata [210-219]
+                  SetOperationType [210-215]
+                  SetOperationAllOrDistinct [216-219]
+                SetOperationMetadata [244-253]
+                  SetOperationType [244-249]
+                  SetOperationAllOrDistinct [250-253]
+              Select [188-209]
+                SelectList [195-209]
+                  SelectColumn [195-209]
+                    StringLiteral [195-200]
+                      StringLiteralComponent('foo') [195-200]
+                    Alias [201-209]
+                      Identifier(value) [204-209]
+              Select [222-243]
+                SelectList [229-243]
+                  SelectColumn [229-243]
+                    StringLiteral [229-234]
+                      StringLiteralComponent('bar') [229-234]
+                    Alias [235-243]
+                      Identifier(value) [238-243]
+              Select [256-277]
+                SelectList [263-277]
+                  SelectColumn [263-277]
+                    StringLiteral [263-268]
+                      StringLiteralComponent('baz') [263-268]
+                    Alias [269-277]
+                      Identifier(value) [272-277]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/debugging_functions#error
+SELECT *
+FROM (SELECT -1 AS x)
+WHERE IF(x > 0, true, ERROR(FORMAT('Error: x must be positive but is %t', x)))
+--
+QueryStatement [0-109]
+  Query [0-109]
+    Select [0-109]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-30]
+        TableSubquery [14-30]
+          Query [15-29]
+            Select [15-29]
+              SelectList [22-29]
+                SelectColumn [22-29]
+                  UnaryExpression(-) [22-24]
+                    IntLiteral(1) [23-24]
+                  Alias [25-29]
+                    Identifier(x) [28-29]
+      WhereClause [31-109]
+        FunctionCall [37-109]
+          PathExpression [37-39]
+            Identifier(`IF`) [37-39]
+          BinaryExpression(>) [40-45]
+            PathExpression [40-41]
+              Identifier(x) [40-41]
+            IntLiteral(0) [44-45]
+          BooleanLiteral(true) [47-51]
+          FunctionCall [53-108]
+            PathExpression [53-58]
+              Identifier(ERROR) [53-58]
+            FunctionCall [59-107]
+              PathExpression [59-65]
+                Identifier(FORMAT) [59-65]
+              StringLiteral [66-103]
+                StringLiteralComponent('Error: x must be positive but is %t') [66-103]
+              PathExpression [105-106]
+                Identifier(x) [105-106]
diff --git a/parser/testdata/spanner/dml-syntax.spanner.sql b/parser/testdata/spanner/dml-syntax.spanner.sql
new file mode 100644
index 0000000..57e5da2
--- /dev/null
+++ b/parser/testdata/spanner/dml-syntax.spanner.sql
@@ -0,0 +1,24 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#tables
+# id c0682732ef08: column ON UPDATE expression (dml-syntax)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ")" or "," but got keyword ON [at 8:5]
+CREATE TABLE Singers (
+  SingerId    INT64 NOT NULL,
+  FirstName   STRING(1024),
+  LastName    STRING(1024),
+  BirthDate   DATE,
+  Status      STRING(1024),
+  LastUpdated TIMESTAMP DEFAULT (PENDING_COMMIT_TIMESTAMP())
+    ON UPDATE (PENDING_COMMIT_TIMESTAMP())
+    OPTIONS (allow_commit_timestamp = true),
+  SingerInfo  googlesql.example.SingerInfo,
+  AlbumInfo   googlesql.example.Album,
+) PRIMARY KEY(SingerId);
diff --git a/parser/testdata/spanner/dml-syntax.test b/parser/testdata/spanner/dml-syntax.test
new file mode 100644
index 0000000..e84f39f
--- /dev/null
+++ b/parser/testdata/spanner/dml-syntax.test
@@ -0,0 +1,1705 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#tables
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE AlbumInfo (
+  SingerId        INT64 NOT NULL,
+  AlbumId         INT64 NOT NULL,
+  AlbumTitle      STRING(MAX),
+  MarketingBudget INT64,
+) PRIMARY KEY(SingerId, AlbumId),
+  INTERLEAVE IN PARENT Singers ON DELETE CASCADE
+--
+CreateTableStatement [0-231]
+  PathExpression [13-22]
+    Identifier(AlbumInfo) [13-22]
+  TableElementList [23-150]
+    ColumnDefinition [27-57]
+      Identifier(SingerId) [27-35]
+      SimpleColumnSchema [43-57]
+        PathExpression [43-48]
+          Identifier(INT64) [43-48]
+        ColumnAttributeList [49-57]
+          NotNullColumnAttribute [49-57]
+    ColumnDefinition [61-91]
+      Identifier(AlbumId) [61-68]
+      SimpleColumnSchema [77-91]
+        PathExpression [77-82]
+          Identifier(INT64) [77-82]
+        ColumnAttributeList [83-91]
+          NotNullColumnAttribute [83-91]
+    ColumnDefinition [95-122]
+      Identifier(AlbumTitle) [95-105]
+      SimpleColumnSchema [111-122]
+        PathExpression [111-117]
+          Identifier(STRING) [111-117]
+        TypeParameterList [117-121]
+          MaxLiteral() [118-121]
+    ColumnDefinition [126-147]
+      Identifier(MarketingBudget) [126-141]
+      SimpleColumnSchema [142-147]
+        PathExpression [142-147]
+          Identifier(INT64) [142-147]
+  SpannerTableOptions [151-231]
+    PrimaryKey(ENFORCED) [151-181]
+      PrimaryKeyElementList [162-181]
+        PrimaryKeyElement(ASC) [163-171]
+          Identifier(SingerId) [163-171]
+        PrimaryKeyElement(ASC) [173-180]
+          Identifier(AlbumId) [173-180]
+    SpannerInterleaveClause [181-231]
+      PathExpression [206-213]
+        Identifier(Singers) [206-213]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#tables
+CREATE TABLE Songs (
+  SingerId  INT64 NOT NULL,
+  AlbumId   INT64 NOT NULL,
+  TrackId   INT64 NOT NULL,
+  SongName  STRING(MAX),
+  Duration  INT64,
+  SongGenre STRING(25),
+) PRIMARY KEY(SingerId, AlbumId, TrackId),
+  INTERLEAVE IN PARENT AlbumInfo ON DELETE CASCADE
+--
+CreateTableStatement [0-266]
+  PathExpression [13-18]
+    Identifier(Songs) [13-18]
+  TableElementList [19-174]
+    ColumnDefinition [23-47]
+      Identifier(SingerId) [23-31]
+      SimpleColumnSchema [33-47]
+        PathExpression [33-38]
+          Identifier(INT64) [33-38]
+        ColumnAttributeList [39-47]
+          NotNullColumnAttribute [39-47]
+    ColumnDefinition [51-75]
+      Identifier(AlbumId) [51-58]
+      SimpleColumnSchema [61-75]
+        PathExpression [61-66]
+          Identifier(INT64) [61-66]
+        ColumnAttributeList [67-75]
+          NotNullColumnAttribute [67-75]
+    ColumnDefinition [79-103]
+      Identifier(TrackId) [79-86]
+      SimpleColumnSchema [89-103]
+        PathExpression [89-94]
+          Identifier(INT64) [89-94]
+        ColumnAttributeList [95-103]
+          NotNullColumnAttribute [95-103]
+    ColumnDefinition [107-128]
+      Identifier(SongName) [107-115]
+      SimpleColumnSchema [117-128]
+        PathExpression [117-123]
+          Identifier(STRING) [117-123]
+        TypeParameterList [123-127]
+          MaxLiteral() [124-127]
+    ColumnDefinition [132-147]
+      Identifier(Duration) [132-140]
+      SimpleColumnSchema [142-147]
+        PathExpression [142-147]
+          Identifier(INT64) [142-147]
+    ColumnDefinition [151-171]
+      Identifier(SongGenre) [151-160]
+      SimpleColumnSchema [161-171]
+        PathExpression [161-167]
+          Identifier(STRING) [161-167]
+        TypeParameterList [167-170]
+          IntLiteral(25) [168-170]
+  SpannerTableOptions [175-266]
+    PrimaryKey(ENFORCED) [175-214]
+      PrimaryKeyElementList [186-214]
+        PrimaryKeyElement(ASC) [187-195]
+          Identifier(SingerId) [187-195]
+        PrimaryKeyElement(ASC) [197-204]
+          Identifier(AlbumId) [197-204]
+        PrimaryKeyElement(ASC) [206-213]
+          Identifier(TrackId) [206-213]
+    SpannerInterleaveClause [214-266]
+      PathExpression [239-248]
+        Identifier(AlbumInfo) [239-248]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#tables
+CREATE TABLE Concerts (
+  VenueId      INT64 NOT NULL,
+  SingerId     INT64 NOT NULL,
+  ConcertDate  DATE NOT NULL,
+  BeginTime    TIMESTAMP,
+  EndTime      TIMESTAMP,
+  TicketPrices ARRAY,
+) PRIMARY KEY(VenueId, SingerId, ConcertDate)
+--
+CreateTableStatement [0-242]
+  PathExpression [13-21]
+    Identifier(Concerts) [13-21]
+  TableElementList [22-198]
+    ColumnDefinition [26-53]
+      Identifier(VenueId) [26-33]
+      SimpleColumnSchema [39-53]
+        PathExpression [39-44]
+          Identifier(INT64) [39-44]
+        ColumnAttributeList [45-53]
+          NotNullColumnAttribute [45-53]
+    ColumnDefinition [57-84]
+      Identifier(SingerId) [57-65]
+      SimpleColumnSchema [70-84]
+        PathExpression [70-75]
+          Identifier(INT64) [70-75]
+        ColumnAttributeList [76-84]
+          NotNullColumnAttribute [76-84]
+    ColumnDefinition [88-114]
+      Identifier(ConcertDate) [88-99]
+      SimpleColumnSchema [101-114]
+        PathExpression [101-105]
+          Identifier(DATE) [101-105]
+        ColumnAttributeList [106-114]
+          NotNullColumnAttribute [106-114]
+    ColumnDefinition [118-140]
+      Identifier(BeginTime) [118-127]
+      SimpleColumnSchema [131-140]
+        PathExpression [131-140]
+          Identifier(TIMESTAMP) [131-140]
+    ColumnDefinition [144-166]
+      Identifier(EndTime) [144-151]
+      SimpleColumnSchema [157-166]
+        PathExpression [157-166]
+          Identifier(TIMESTAMP) [157-166]
+    ColumnDefinition [170-195]
+      Identifier(TicketPrices) [170-182]
+      ArrayColumnSchema [183-195]
+        SimpleColumnSchema [189-194]
+          PathExpression [189-194]
+            Identifier(INT64) [189-194]
+  SpannerTableOptions [199-242]
+    PrimaryKey(ENFORCED) [199-242]
+      PrimaryKeyElementList [210-242]
+        PrimaryKeyElement(ASC) [211-218]
+          Identifier(VenueId) [211-218]
+        PrimaryKeyElement(ASC) [220-228]
+          Identifier(SingerId) [220-228]
+        PrimaryKeyElement(ASC) [230-241]
+          Identifier(ConcertDate) [230-241]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#tables
+CREATE TABLE AckworthSingers (
+  SingerId   INT64 NOT NULL,
+  FirstName  STRING(1024),
+  LastName   STRING(1024),
+  BirthDate  DATE,
+) PRIMARY KEY(SingerId)
+--
+CreateTableStatement [0-156]
+  PathExpression [13-28]
+    Identifier(AckworthSingers) [13-28]
+  TableElementList [29-134]
+    ColumnDefinition [33-58]
+      Identifier(SingerId) [33-41]
+      SimpleColumnSchema [44-58]
+        PathExpression [44-49]
+          Identifier(INT64) [44-49]
+        ColumnAttributeList [50-58]
+          NotNullColumnAttribute [50-58]
+    ColumnDefinition [62-85]
+      Identifier(FirstName) [62-71]
+      SimpleColumnSchema [73-85]
+        PathExpression [73-79]
+          Identifier(STRING) [73-79]
+        TypeParameterList [79-84]
+          IntLiteral(1024) [80-84]
+    ColumnDefinition [89-112]
+      Identifier(LastName) [89-97]
+      SimpleColumnSchema [100-112]
+        PathExpression [100-106]
+          Identifier(STRING) [100-106]
+        TypeParameterList [106-111]
+          IntLiteral(1024) [107-111]
+    ColumnDefinition [116-131]
+      Identifier(BirthDate) [116-125]
+      SimpleColumnSchema [127-131]
+        PathExpression [127-131]
+          Identifier(DATE) [127-131]
+  SpannerTableOptions [135-156]
+    PrimaryKey(ENFORCED) [135-156]
+      PrimaryKeyElementList [146-156]
+        PrimaryKeyElement(ASC) [147-155]
+          Identifier(SingerId) [147-155]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#tables
+CREATE TABLE Fans (
+  FanId     STRING(36) DEFAULT (GENERATE_UUID()),
+  FirstName STRING(1024),
+  LastName  STRING(1024),
+) PRIMARY KEY(FanId)
+--
+CreateTableStatement [0-142]
+  PathExpression [13-17]
+    Identifier(Fans) [13-17]
+  TableElementList [18-123]
+    ColumnDefinition [22-68]
+      Identifier(FanId) [22-27]
+      SimpleColumnSchema [32-68]
+        PathExpression [32-38]
+          Identifier(STRING) [32-38]
+        TypeParameterList [38-41]
+          IntLiteral(36) [39-41]
+        FunctionCall [52-67]
+          PathExpression [52-65]
+            Identifier(GENERATE_UUID) [52-65]
+    ColumnDefinition [72-94]
+      Identifier(FirstName) [72-81]
+      SimpleColumnSchema [82-94]
+        PathExpression [82-88]
+          Identifier(STRING) [82-88]
+        TypeParameterList [88-93]
+          IntLiteral(1024) [89-93]
+    ColumnDefinition [98-120]
+      Identifier(LastName) [98-106]
+      SimpleColumnSchema [108-120]
+        PathExpression [108-114]
+          Identifier(STRING) [108-114]
+        TypeParameterList [114-119]
+          IntLiteral(1024) [115-119]
+  SpannerTableOptions [124-142]
+    PrimaryKey(ENFORCED) [124-142]
+      PrimaryKeyElementList [135-142]
+        PrimaryKeyElement(ASC) [136-141]
+          Identifier(FanId) [136-141]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#default_values
+INSERT INTO T (id, col_a, col_b) VALUES (1, DEFAULT, 1)
+--
+InsertStatement [0-55]
+  PathExpression [12-13]
+    Identifier(T) [12-13]
+  ColumnList [14-32]
+    Identifier(id) [15-17]
+    Identifier(col_a) [19-24]
+    Identifier(col_b) [26-31]
+  InsertValuesRowList [33-55]
+    InsertValuesRow [40-55]
+      IntLiteral(1) [41-42]
+      DefaultLiteral [44-51]
+      IntLiteral(1) [53-54]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#default_values
+INSERT INTO T (id, col_a, col_b) VALUES (2, 200, 2)
+--
+InsertStatement [0-51]
+  PathExpression [12-13]
+    Identifier(T) [12-13]
+  ColumnList [14-32]
+    Identifier(id) [15-17]
+    Identifier(col_a) [19-24]
+    Identifier(col_b) [26-31]
+  InsertValuesRowList [33-51]
+    InsertValuesRow [40-51]
+      IntLiteral(2) [41-42]
+      IntLiteral(200) [44-47]
+      IntLiteral(2) [49-50]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#default_values
+INSERT INTO T (id, col_b) VALUES (3, 3)
+--
+InsertStatement [0-39]
+  PathExpression [12-13]
+    Identifier(T) [12-13]
+  ColumnList [14-25]
+    Identifier(id) [15-17]
+    Identifier(col_b) [19-24]
+  InsertValuesRowList [26-39]
+    InsertValuesRow [33-39]
+      IntLiteral(3) [34-35]
+      IntLiteral(3) [37-38]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#insert-ignore
+INSERT OR IGNORE INTO Singers
+    (SingerId, FirstName, LastName, Birthdate, Status, SingerInfo)
+VALUES (5, "Zak", "Sterling", "1996-03-12", "active", "nationality:'USA'"),
+       (7, "Edie", "Silver", "1998-01-23", "active", "nationality:'USA'")
+--
+InsertStatement(insert_mode=IGNORE) [0-246]
+  PathExpression [22-29]
+    Identifier(Singers) [22-29]
+  ColumnList [34-96]
+    Identifier(SingerId) [35-43]
+    Identifier(FirstName) [45-54]
+    Identifier(LastName) [56-64]
+    Identifier(Birthdate) [66-75]
+    Identifier(Status) [77-83]
+    Identifier(SingerInfo) [85-95]
+  InsertValuesRowList [97-246]
+    InsertValuesRow [104-171]
+      IntLiteral(5) [105-106]
+      StringLiteral [108-113]
+        StringLiteralComponent("Zak") [108-113]
+      StringLiteral [115-125]
+        StringLiteralComponent("Sterling") [115-125]
+      StringLiteral [127-139]
+        StringLiteralComponent("1996-03-12") [127-139]
+      StringLiteral [141-149]
+        StringLiteralComponent("active") [141-149]
+      StringLiteral [151-170]
+        StringLiteralComponent("nationality:'USA'") [151-170]
+    InsertValuesRow [180-246]
+      IntLiteral(7) [181-182]
+      StringLiteral [184-190]
+        StringLiteralComponent("Edie") [184-190]
+      StringLiteral [192-200]
+        StringLiteralComponent("Silver") [192-200]
+      StringLiteral [202-214]
+        StringLiteralComponent("1998-01-23") [202-214]
+      StringLiteral [216-224]
+        StringLiteralComponent("active") [216-224]
+      StringLiteral [226-245]
+        StringLiteralComponent("nationality:'USA'") [226-245]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#insert-or-update
+INSERT OR UPDATE INTO Singers
+    (SingerId, Status)
+VALUES (5, "inactive")
+--
+InsertStatement(insert_mode=UPDATE) [0-75]
+  PathExpression [22-29]
+    Identifier(Singers) [22-29]
+  ColumnList [34-52]
+    Identifier(SingerId) [35-43]
+    Identifier(Status) [45-51]
+  InsertValuesRowList [53-75]
+    InsertValuesRow [60-75]
+      IntLiteral(5) [61-62]
+      StringLiteral [64-74]
+        StringLiteralComponent("inactive") [64-74]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#insert_using_literal_values_examples
+INSERT INTO Singers (SingerId, FirstName, LastName, SingerInfo)
+VALUES(1, 'Marc', 'Richards', "nationality: 'USA'"),
+      (2, 'Catalina', 'Smith', "nationality: 'Brazil'"),
+      (3, "Andrew", "Duneskipper", NULL)
+--
+InsertStatement [0-214]
+  PathExpression [12-19]
+    Identifier(Singers) [12-19]
+  ColumnList [20-63]
+    Identifier(SingerId) [21-29]
+    Identifier(FirstName) [31-40]
+    Identifier(LastName) [42-50]
+    Identifier(SingerInfo) [52-62]
+  InsertValuesRowList [64-214]
+    InsertValuesRow [70-115]
+      IntLiteral(1) [71-72]
+      StringLiteral [74-80]
+        StringLiteralComponent('Marc') [74-80]
+      StringLiteral [82-92]
+        StringLiteralComponent('Richards') [82-92]
+      StringLiteral [94-114]
+        StringLiteralComponent("nationality: 'USA'") [94-114]
+    InsertValuesRow [123-172]
+      IntLiteral(2) [124-125]
+      StringLiteral [127-137]
+        StringLiteralComponent('Catalina') [127-137]
+      StringLiteral [139-146]
+        StringLiteralComponent('Smith') [139-146]
+      StringLiteral [148-171]
+        StringLiteralComponent("nationality: 'Brazil'") [148-171]
+    InsertValuesRow [180-214]
+      IntLiteral(3) [181-182]
+      StringLiteral [184-192]
+        StringLiteralComponent("Andrew") [184-192]
+      StringLiteral [194-207]
+        StringLiteralComponent("Duneskipper") [194-207]
+      NullLiteral(NULL) [209-213]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#insert_using_a_select_statement_example
+INSERT INTO Singers (SingerId, FirstName, LastName)
+SELECT SingerId, FirstName, LastName
+FROM AckworthSingers
+--
+InsertStatement [0-109]
+  PathExpression [12-19]
+    Identifier(Singers) [12-19]
+  ColumnList [20-51]
+    Identifier(SingerId) [21-29]
+    Identifier(FirstName) [31-40]
+    Identifier(LastName) [42-50]
+  Query [52-109]
+    Select [52-109]
+      SelectList [59-88]
+        SelectColumn [59-67]
+          PathExpression [59-67]
+            Identifier(SingerId) [59-67]
+        SelectColumn [69-78]
+          PathExpression [69-78]
+            Identifier(FirstName) [69-78]
+        SelectColumn [80-88]
+          PathExpression [80-88]
+            Identifier(LastName) [80-88]
+      FromClause [89-109]
+        TablePathExpression [94-109]
+          PathExpression [94-109]
+            Identifier(AckworthSingers) [94-109]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#insert_using_a_select_statement_example
+INSERT INTO Singers (SingerId, FirstName, LastName)
+SELECT *
+FROM UNNEST ([(4, 'Lea', 'Martin'),
+      (5, 'David', 'Lomond'),
+      (6, 'Elena', 'Campbell')])
+--
+InsertStatement [0-159]
+  PathExpression [12-19]
+    Identifier(Singers) [12-19]
+  ColumnList [20-51]
+    Identifier(SingerId) [21-29]
+    Identifier(FirstName) [31-40]
+    Identifier(LastName) [42-50]
+  Query [52-159]
+    Select [52-159]
+      SelectList [59-60]
+        SelectColumn [59-60]
+          Star(*) [59-60]
+      FromClause [61-159]
+        TablePathExpression [66-159]
+          UnnestExpression [66-159]
+            ExpressionWithOptAlias [74-158]
+              ArrayConstructor [74-158]
+                StructConstructorWithParens [75-95]
+                  IntLiteral(4) [76-77]
+                  StringLiteral [79-84]
+                    StringLiteralComponent('Lea') [79-84]
+                  StringLiteral [86-94]
+                    StringLiteralComponent('Martin') [86-94]
+                StructConstructorWithParens [103-125]
+                  IntLiteral(5) [104-105]
+                  StringLiteral [107-114]
+                    StringLiteralComponent('David') [107-114]
+                  StringLiteral [116-124]
+                    StringLiteralComponent('Lomond') [116-124]
+                StructConstructorWithParens [133-157]
+                  IntLiteral(6) [134-135]
+                  StringLiteral [137-144]
+                    StringLiteralComponent('Elena') [137-144]
+                  StringLiteral [146-156]
+                    StringLiteralComponent('Campbell') [146-156]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#insert_using_a_subquery_example
+INSERT INTO Singers (SingerId, FirstName)
+VALUES (4, (SELECT FirstName FROM AckworthSingers WHERE SingerId = 4))
+--
+InsertStatement [0-112]
+  PathExpression [12-19]
+    Identifier(Singers) [12-19]
+  ColumnList [20-41]
+    Identifier(SingerId) [21-29]
+    Identifier(FirstName) [31-40]
+  InsertValuesRowList [42-112]
+    InsertValuesRow [49-112]
+      IntLiteral(4) [50-51]
+      ExpressionSubquery [53-111]
+        Query [54-110]
+          Select [54-110]
+            SelectList [61-70]
+              SelectColumn [61-70]
+                PathExpression [61-70]
+                  Identifier(FirstName) [61-70]
+            FromClause [71-91]
+              TablePathExpression [76-91]
+                PathExpression [76-91]
+                  Identifier(AckworthSingers) [76-91]
+            WhereClause [92-110]
+              BinaryExpression(=) [98-110]
+                PathExpression [98-106]
+                  Identifier(SingerId) [98-106]
+                IntLiteral(4) [109-110]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#insert_using_a_subquery_example
+INSERT INTO Singers (SingerId, FirstName, LastName)
+VALUES (4,
+        (SELECT FirstName FROM AckworthSingers WHERE SingerId = 4),
+        (SELECT LastName  FROM AckworthSingers WHERE SingerId = 4))
+--
+InsertStatement [0-198]
+  PathExpression [12-19]
+    Identifier(Singers) [12-19]
+  ColumnList [20-51]
+    Identifier(SingerId) [21-29]
+    Identifier(FirstName) [31-40]
+    Identifier(LastName) [42-50]
+  InsertValuesRowList [52-198]
+    InsertValuesRow [59-198]
+      IntLiteral(4) [60-61]
+      ExpressionSubquery [71-129]
+        Query [72-128]
+          Select [72-128]
+            SelectList [79-88]
+              SelectColumn [79-88]
+                PathExpression [79-88]
+                  Identifier(FirstName) [79-88]
+            FromClause [89-109]
+              TablePathExpression [94-109]
+                PathExpression [94-109]
+                  Identifier(AckworthSingers) [94-109]
+            WhereClause [110-128]
+              BinaryExpression(=) [116-128]
+                PathExpression [116-124]
+                  Identifier(SingerId) [116-124]
+                IntLiteral(4) [127-128]
+      ExpressionSubquery [139-197]
+        Query [140-196]
+          Select [140-196]
+            SelectList [147-155]
+              SelectColumn [147-155]
+                PathExpression [147-155]
+                  Identifier(LastName) [147-155]
+            FromClause [157-177]
+              TablePathExpression [162-177]
+                PathExpression [162-177]
+                  Identifier(AckworthSingers) [162-177]
+            WhereClause [178-196]
+              BinaryExpression(=) [184-196]
+                PathExpression [184-192]
+                  Identifier(SingerId) [184-192]
+                IntLiteral(4) [195-196]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#insert_with_then_return_examples
+INSERT INTO Singers (SingerId, FirstName, LastName)
+VALUES
+    (7, 'Melissa', 'Garcia'),
+    (8, 'Russell', 'Morales')
+THEN RETURN SingerId, FirstName || ' ' || LastName AS FullName
+--
+InsertStatement [0-181]
+  PathExpression [12-19]
+    Identifier(Singers) [12-19]
+  ColumnList [20-51]
+    Identifier(SingerId) [21-29]
+    Identifier(FirstName) [31-40]
+    Identifier(LastName) [42-50]
+  InsertValuesRowList [52-118]
+    InsertValuesRow [63-87]
+      IntLiteral(7) [64-65]
+      StringLiteral [67-76]
+        StringLiteralComponent('Melissa') [67-76]
+      StringLiteral [78-86]
+        StringLiteralComponent('Garcia') [78-86]
+    InsertValuesRow [93-118]
+      IntLiteral(8) [94-95]
+      StringLiteral [97-106]
+        StringLiteralComponent('Russell') [97-106]
+      StringLiteral [108-117]
+        StringLiteralComponent('Morales') [108-117]
+  ReturningClause [119-181]
+    SelectList [131-181]
+      SelectColumn [131-139]
+        PathExpression [131-139]
+          Identifier(SingerId) [131-139]
+      SelectColumn [141-181]
+        BinaryExpression(||) [141-169]
+          BinaryExpression(||) [141-157]
+            PathExpression [141-150]
+              Identifier(FirstName) [141-150]
+            StringLiteral [154-157]
+              StringLiteralComponent(' ') [154-157]
+          PathExpression [161-169]
+            Identifier(LastName) [161-169]
+        Alias [170-181]
+          Identifier(FullName) [173-181]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#insert_with_then_return_examples
+INSERT INTO Fans (FirstName, LastName)
+VALUES ('Melissa', 'Garcia')
+THEN RETURN FanId
+--
+InsertStatement [0-85]
+  PathExpression [12-16]
+    Identifier(Fans) [12-16]
+  ColumnList [17-38]
+    Identifier(FirstName) [18-27]
+    Identifier(LastName) [29-37]
+  InsertValuesRowList [39-67]
+    InsertValuesRow [46-67]
+      StringLiteral [47-56]
+        StringLiteralComponent('Melissa') [47-56]
+      StringLiteral [58-66]
+        StringLiteralComponent('Garcia') [58-66]
+  ReturningClause [68-85]
+    SelectList [80-85]
+      SelectColumn [80-85]
+        PathExpression [80-85]
+          Identifier(FanId) [80-85]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#insert_with_then_return_examples
+INSERT OR UPDATE Singers (SingerId, FirstName, LastName)
+VALUES (7, 'Melissa', 'Gartner')
+THEN RETURN WITH ACTION SingerId, FirstName || ' ' || LastName AS FullName
+--
+InsertStatement(insert_mode=UPDATE) [0-164]
+  PathExpression [17-24]
+    Identifier(Singers) [17-24]
+  ColumnList [25-56]
+    Identifier(SingerId) [26-34]
+    Identifier(FirstName) [36-45]
+    Identifier(LastName) [47-55]
+  InsertValuesRowList [57-89]
+    InsertValuesRow [64-89]
+      IntLiteral(7) [65-66]
+      StringLiteral [68-77]
+        StringLiteralComponent('Melissa') [68-77]
+      StringLiteral [79-88]
+        StringLiteralComponent('Gartner') [79-88]
+  ReturningClause [90-164]
+    SelectList [114-164]
+      SelectColumn [114-122]
+        PathExpression [114-122]
+          Identifier(SingerId) [114-122]
+      SelectColumn [124-164]
+        BinaryExpression(||) [124-152]
+          BinaryExpression(||) [124-140]
+            PathExpression [124-133]
+              Identifier(FirstName) [124-133]
+            StringLiteral [137-140]
+              StringLiteralComponent(' ') [137-140]
+          PathExpression [144-152]
+            Identifier(LastName) [144-152]
+        Alias [153-164]
+          Identifier(FullName) [156-164]
+    Alias [90-164]
+      Identifier(ACTION) [107-113]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#insert_or_ignore_example
+INSERT OR IGNORE INTO Singers (
+  SingerId, FirstName, LastName, BirthDate, Status, SingerInfo)
+  (SELECT id, fname, lname, dob, status, info
+    FROM latest_album
+    WHERE id > 10
+    AND id < 100)
+--
+InsertStatement(insert_mode=IGNORE) [0-199]
+  PathExpression [22-29]
+    Identifier(Singers) [22-29]
+  ColumnList [30-95]
+    Identifier(SingerId) [34-42]
+    Identifier(FirstName) [44-53]
+    Identifier(LastName) [55-63]
+    Identifier(BirthDate) [65-74]
+    Identifier(Status) [76-82]
+    Identifier(SingerInfo) [84-94]
+  Query [99-198]
+    Select [99-198]
+      SelectList [106-141]
+        SelectColumn [106-108]
+          PathExpression [106-108]
+            Identifier(id) [106-108]
+        SelectColumn [110-115]
+          PathExpression [110-115]
+            Identifier(fname) [110-115]
+        SelectColumn [117-122]
+          PathExpression [117-122]
+            Identifier(lname) [117-122]
+        SelectColumn [124-127]
+          PathExpression [124-127]
+            Identifier(dob) [124-127]
+        SelectColumn [129-135]
+          PathExpression [129-135]
+            Identifier(status) [129-135]
+        SelectColumn [137-141]
+          PathExpression [137-141]
+            Identifier(info) [137-141]
+      FromClause [146-163]
+        TablePathExpression [151-163]
+          PathExpression [151-163]
+            Identifier(latest_album) [151-163]
+      WhereClause [168-198]
+        AndExpr [174-198]
+          BinaryExpression(>) [174-181]
+            PathExpression [174-176]
+              Identifier(id) [174-176]
+            IntLiteral(10) [179-181]
+          BinaryExpression(<) [190-198]
+            PathExpression [190-192]
+              Identifier(id) [190-192]
+            IntLiteral(100) [195-198]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#insert_with_on_conflict_do_update_examples
+INSERT INTO Singers (SingerId, FirstName, LastName, BirthDate, Status, LastUpdated)
+VALUES (101, 'Adele', 'Adkins', '1988-05-05', 'Active', CURRENT_TIMESTAMP())
+ON CONFLICT ON UNIQUE CONSTRAINT UniqueIndex_SingerName
+DO UPDATE SET Status = excluded.Status, LastUpdated = Singers.LastUpdated
+--
+InsertStatement [0-290]
+  PathExpression [12-19]
+    Identifier(Singers) [12-19]
+  ColumnList [20-83]
+    Identifier(SingerId) [21-29]
+    Identifier(FirstName) [31-40]
+    Identifier(LastName) [42-50]
+    Identifier(BirthDate) [52-61]
+    Identifier(Status) [63-69]
+    Identifier(LastUpdated) [71-82]
+  InsertValuesRowList [84-160]
+    InsertValuesRow [91-160]
+      IntLiteral(101) [92-95]
+      StringLiteral [97-104]
+        StringLiteralComponent('Adele') [97-104]
+      StringLiteral [106-114]
+        StringLiteralComponent('Adkins') [106-114]
+      StringLiteral [116-128]
+        StringLiteralComponent('1988-05-05') [116-128]
+      StringLiteral [130-138]
+        StringLiteralComponent('Active') [130-138]
+      FunctionCall [140-159]
+        PathExpression [140-157]
+          Identifier(`CURRENT_TIMESTAMP`) [140-157]
+  OnConflictClause(conflict_action=UPDATE) [161-290]
+    Identifier(UniqueIndex_SingerName) [194-216]
+    UpdateItemList [231-290]
+      UpdateItem [231-255]
+        UpdateSetValue [231-255]
+          PathExpression [231-237]
+            Identifier(Status) [231-237]
+          PathExpression [240-255]
+            Identifier(excluded) [240-248]
+            Identifier(Status) [249-255]
+      UpdateItem [257-290]
+        UpdateSetValue [257-290]
+          PathExpression [257-268]
+            Identifier(LastUpdated) [257-268]
+          PathExpression [271-290]
+            Identifier(Singers) [271-278]
+            Identifier(LastUpdated) [279-290]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#insert_with_on_conflict_do_update_examples
+CREATE UNIQUE INDEX Index_AlbumInfo_SingerAlbum ON AlbumInfo (SingerId, AlbumId)
+--
+CreateIndexStatement(UNIQUE) [0-80]
+  PathExpression [20-47]
+    Identifier(Index_AlbumInfo_SingerAlbum) [20-47]
+  PathExpression [51-60]
+    Identifier(AlbumInfo) [51-60]
+  IndexItemList [61-80]
+    OrderingExpression(ASC) [62-70]
+      PathExpression [62-70]
+        Identifier(SingerId) [62-70]
+    OrderingExpression(ASC) [72-79]
+      PathExpression [72-79]
+        Identifier(AlbumId) [72-79]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#insert_with_on_conflict_do_update_examples
+INSERT INTO AlbumInfo (SingerId, AlbumId, AlbumTitle, MarketingBudget)
+VALUES (10, 1, 'My New Album', 750000)
+ON CONFLICT(SingerId, AlbumId)
+DO UPDATE SET AlbumTitle = EXCLUDED.AlbumTitle, MarketingBudget = EXCLUDED.MarketingBudget
+--
+InsertStatement [0-231]
+  PathExpression [12-21]
+    Identifier(AlbumInfo) [12-21]
+  ColumnList [22-70]
+    Identifier(SingerId) [23-31]
+    Identifier(AlbumId) [33-40]
+    Identifier(AlbumTitle) [42-52]
+    Identifier(MarketingBudget) [54-69]
+  InsertValuesRowList [71-109]
+    InsertValuesRow [78-109]
+      IntLiteral(10) [79-81]
+      IntLiteral(1) [83-84]
+      StringLiteral [86-100]
+        StringLiteralComponent('My New Album') [86-100]
+      IntLiteral(750000) [102-108]
+  OnConflictClause(conflict_action=UPDATE) [110-231]
+    ColumnList [121-140]
+      Identifier(SingerId) [122-130]
+      Identifier(AlbumId) [132-139]
+    UpdateItemList [155-231]
+      UpdateItem [155-187]
+        UpdateSetValue [155-187]
+          PathExpression [155-165]
+            Identifier(AlbumTitle) [155-165]
+          PathExpression [168-187]
+            Identifier(EXCLUDED) [168-176]
+            Identifier(AlbumTitle) [177-187]
+      UpdateItem [189-231]
+        UpdateSetValue [189-231]
+          PathExpression [189-204]
+            Identifier(MarketingBudget) [189-204]
+          PathExpression [207-231]
+            Identifier(EXCLUDED) [207-215]
+            Identifier(MarketingBudget) [216-231]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#insert_with_on_conflict_do_update_examples
+INSERT INTO AlbumInfo (SingerId, AlbumId, AlbumTitle, MarketingBudget)
+VALUES (10, 2, 'The Second Album', 600000)
+ON CONFLICT(SingerId, AlbumId)
+DO UPDATE SET LastReviewScore = EXCLUDED.LastReviewScore, EstimatedProfit = EXCLUDED.EstimatedProfit + 10000
+--
+InsertStatement [0-253]
+  PathExpression [12-21]
+    Identifier(AlbumInfo) [12-21]
+  ColumnList [22-70]
+    Identifier(SingerId) [23-31]
+    Identifier(AlbumId) [33-40]
+    Identifier(AlbumTitle) [42-52]
+    Identifier(MarketingBudget) [54-69]
+  InsertValuesRowList [71-113]
+    InsertValuesRow [78-113]
+      IntLiteral(10) [79-81]
+      IntLiteral(2) [83-84]
+      StringLiteral [86-104]
+        StringLiteralComponent('The Second Album') [86-104]
+      IntLiteral(600000) [106-112]
+  OnConflictClause(conflict_action=UPDATE) [114-253]
+    ColumnList [125-144]
+      Identifier(SingerId) [126-134]
+      Identifier(AlbumId) [136-143]
+    UpdateItemList [159-253]
+      UpdateItem [159-201]
+        UpdateSetValue [159-201]
+          PathExpression [159-174]
+            Identifier(LastReviewScore) [159-174]
+          PathExpression [177-201]
+            Identifier(EXCLUDED) [177-185]
+            Identifier(LastReviewScore) [186-201]
+      UpdateItem [203-253]
+        UpdateSetValue [203-253]
+          PathExpression [203-218]
+            Identifier(EstimatedProfit) [203-218]
+          BinaryExpression(+) [221-253]
+            PathExpression [221-245]
+              Identifier(EXCLUDED) [221-229]
+              Identifier(EstimatedProfit) [230-245]
+            IntLiteral(10000) [248-253]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#insert_with_on_conflict_do_update_examples
+INSERT INTO AlbumInfo (SingerId, AlbumId, AlbumTitle, MarketingBudget)
+VALUES (10, 1, 'Brand New Album Title', 800000)
+ON CONFLICT(SingerId, AlbumId)
+DO UPDATE SET AlbumTitle = EXCLUDED.AlbumTitle
+--
+InsertStatement [0-196]
+  PathExpression [12-21]
+    Identifier(AlbumInfo) [12-21]
+  ColumnList [22-70]
+    Identifier(SingerId) [23-31]
+    Identifier(AlbumId) [33-40]
+    Identifier(AlbumTitle) [42-52]
+    Identifier(MarketingBudget) [54-69]
+  InsertValuesRowList [71-118]
+    InsertValuesRow [78-118]
+      IntLiteral(10) [79-81]
+      IntLiteral(1) [83-84]
+      StringLiteral [86-109]
+        StringLiteralComponent('Brand New Album Title') [86-109]
+      IntLiteral(800000) [111-117]
+  OnConflictClause(conflict_action=UPDATE) [119-196]
+    ColumnList [130-149]
+      Identifier(SingerId) [131-139]
+      Identifier(AlbumId) [141-148]
+    UpdateItemList [164-196]
+      UpdateItem [164-196]
+        UpdateSetValue [164-196]
+          PathExpression [164-174]
+            Identifier(AlbumTitle) [164-174]
+          PathExpression [177-196]
+            Identifier(EXCLUDED) [177-185]
+            Identifier(AlbumTitle) [186-196]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#insert_with_on_conflict_do_nothing_examples
+INSERT INTO Singers (SingerId, FirstName)
+VALUES (1, 'John')
+ON CONFLICT DO NOTHING
+--
+InsertStatement [0-83]
+  PathExpression [12-19]
+    Identifier(Singers) [12-19]
+  ColumnList [20-41]
+    Identifier(SingerId) [21-29]
+    Identifier(FirstName) [31-40]
+  InsertValuesRowList [42-60]
+    InsertValuesRow [49-60]
+      IntLiteral(1) [50-51]
+      StringLiteral [53-59]
+        StringLiteralComponent('John') [53-59]
+  OnConflictClause(conflict_action=NOTHING) [61-83]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#where_clause
+DELETE FROM table_name WHERE true
+--
+DeleteStatement [0-33]
+  PathExpression [12-22]
+    Identifier(table_name) [12-22]
+  BooleanLiteral(true) [29-33]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#aliases
+DELETE FROM Singers WHERE FirstName = 'Alice'
+--
+DeleteStatement [0-45]
+  PathExpression [12-19]
+    Identifier(Singers) [12-19]
+  BinaryExpression(=) [26-45]
+    PathExpression [26-35]
+      Identifier(FirstName) [26-35]
+    StringLiteral [38-45]
+      StringLiteralComponent('Alice') [38-45]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#delete_with_subquery_example
+DELETE FROM Singers
+WHERE FirstName NOT IN (SELECT FirstName from AckworthSingers)
+--
+DeleteStatement [0-82]
+  PathExpression [12-19]
+    Identifier(Singers) [12-19]
+  InExpression(NOT IN) [26-82]
+    PathExpression [26-35]
+      Identifier(FirstName) [26-35]
+    Location [40-42]
+    Query [44-81]
+      Select [44-81]
+        SelectList [51-60]
+          SelectColumn [51-60]
+            PathExpression [51-60]
+              Identifier(FirstName) [51-60]
+        FromClause [61-81]
+          TablePathExpression [66-81]
+            PathExpression [66-81]
+              Identifier(AckworthSingers) [66-81]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#delete_with_then_return_example
+DELETE FROM Singers WHERE Firstname = 'Melissa'
+THEN RETURN * EXCEPT (LastUpdated)
+--
+DeleteStatement [0-82]
+  PathExpression [12-19]
+    Identifier(Singers) [12-19]
+  BinaryExpression(=) [26-47]
+    PathExpression [26-35]
+      Identifier(Firstname) [26-35]
+    StringLiteral [38-47]
+      StringLiteralComponent('Melissa') [38-47]
+  ReturningClause [48-82]
+    SelectList [60-82]
+      SelectColumn [60-82]
+        StarWithModifiers [60-82]
+          StarModifiers [62-82]
+            StarExceptList [62-82]
+              Identifier(LastUpdated) [70-81]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#default_values_2
+UPDATE T SET col_a = 1000 WHERE id=1
+--
+UpdateStatement [0-36]
+  PathExpression [7-8]
+    Identifier(T) [7-8]
+  UpdateItemList [13-25]
+    UpdateItem [13-25]
+      UpdateSetValue [13-25]
+        PathExpression [13-18]
+          Identifier(col_a) [13-18]
+        IntLiteral(1000) [21-25]
+  BinaryExpression(=) [32-36]
+    PathExpression [32-34]
+      Identifier(id) [32-34]
+    IntLiteral(1) [35-36]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#default_values_2
+UPDATE T SET col_a = DEFAULT WHERE id=3
+--
+UpdateStatement [0-39]
+  PathExpression [7-8]
+    Identifier(T) [7-8]
+  UpdateItemList [13-28]
+    UpdateItem [13-28]
+      UpdateSetValue [13-28]
+        PathExpression [13-18]
+          Identifier(col_a) [13-18]
+        DefaultLiteral [21-28]
+  BinaryExpression(=) [35-39]
+    PathExpression [35-37]
+      Identifier(id) [35-37]
+    IntLiteral(3) [38-39]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#on_update
+UPDATE Singers SET Status = 'inactive' WHERE SingerId = 10
+--
+UpdateStatement [0-58]
+  PathExpression [7-14]
+    Identifier(Singers) [7-14]
+  UpdateItemList [19-38]
+    UpdateItem [19-38]
+      UpdateSetValue [19-38]
+        PathExpression [19-25]
+          Identifier(Status) [19-25]
+        StringLiteral [28-38]
+          StringLiteralComponent('inactive') [28-38]
+  BinaryExpression(=) [45-58]
+    PathExpression [45-53]
+      Identifier(SingerId) [45-53]
+    IntLiteral(10) [56-58]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#on_update
+UPDATE Singers
+SET Status = 'active', LastUpdated = TIMESTAMP ("2025-07-15 15:30:00+00")
+WHERE SingerId = 10
+--
+UpdateStatement [0-108]
+  PathExpression [7-14]
+    Identifier(Singers) [7-14]
+  UpdateItemList [19-88]
+    UpdateItem [19-36]
+      UpdateSetValue [19-36]
+        PathExpression [19-25]
+          Identifier(Status) [19-25]
+        StringLiteral [28-36]
+          StringLiteralComponent('active') [28-36]
+    UpdateItem [38-88]
+      UpdateSetValue [38-88]
+        PathExpression [38-49]
+          Identifier(LastUpdated) [38-49]
+        FunctionCall [52-88]
+          PathExpression [52-61]
+            Identifier(TIMESTAMP) [52-61]
+          StringLiteral [63-87]
+            StringLiteralComponent("2025-07-15 15:30:00+00") [63-87]
+  BinaryExpression(=) [95-108]
+    PathExpression [95-103]
+      Identifier(SingerId) [95-103]
+    IntLiteral(10) [106-108]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#aliases_2
+UPDATE Singers
+SET BirthDate = '1990-10-10'
+WHERE FirstName = 'Marc' AND LastName = 'Richards'
+--
+UpdateStatement [0-94]
+  PathExpression [7-14]
+    Identifier(Singers) [7-14]
+  UpdateItemList [19-43]
+    UpdateItem [19-43]
+      UpdateSetValue [19-43]
+        PathExpression [19-28]
+          Identifier(BirthDate) [19-28]
+        StringLiteral [31-43]
+          StringLiteralComponent('1990-10-10') [31-43]
+  AndExpr [50-94]
+    BinaryExpression(=) [50-68]
+      PathExpression [50-59]
+        Identifier(FirstName) [50-59]
+      StringLiteral [62-68]
+        StringLiteralComponent('Marc') [62-68]
+    BinaryExpression(=) [73-94]
+      PathExpression [73-81]
+        Identifier(LastName) [73-81]
+      StringLiteral [84-94]
+        StringLiteralComponent('Richards') [84-94]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#update_with_literal_values_example
+UPDATE Singers
+SET BirthDate = '1990-10-10', SingerInfo = "nationality:'USA'"
+WHERE FirstName = 'Marc' AND LastName = 'Richards'
+--
+UpdateStatement [0-128]
+  PathExpression [7-14]
+    Identifier(Singers) [7-14]
+  UpdateItemList [19-77]
+    UpdateItem [19-43]
+      UpdateSetValue [19-43]
+        PathExpression [19-28]
+          Identifier(BirthDate) [19-28]
+        StringLiteral [31-43]
+          StringLiteralComponent('1990-10-10') [31-43]
+    UpdateItem [45-77]
+      UpdateSetValue [45-77]
+        PathExpression [45-55]
+          Identifier(SingerInfo) [45-55]
+        StringLiteral [58-77]
+          StringLiteralComponent("nationality:'USA'") [58-77]
+  AndExpr [84-128]
+    BinaryExpression(=) [84-102]
+      PathExpression [84-93]
+        Identifier(FirstName) [84-93]
+      StringLiteral [96-102]
+        StringLiteralComponent('Marc') [96-102]
+    BinaryExpression(=) [107-128]
+      PathExpression [107-115]
+        Identifier(LastName) [107-115]
+      StringLiteral [118-128]
+        StringLiteralComponent('Richards') [118-128]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#update_array_columns_example
+UPDATE Concerts SET TicketPrices = [25, 50, 100] WHERE VenueId = 1
+--
+UpdateStatement [0-66]
+  PathExpression [7-15]
+    Identifier(Concerts) [7-15]
+  UpdateItemList [20-48]
+    UpdateItem [20-48]
+      UpdateSetValue [20-48]
+        PathExpression [20-32]
+          Identifier(TicketPrices) [20-32]
+        ArrayConstructor [35-48]
+          IntLiteral(25) [36-38]
+          IntLiteral(50) [40-42]
+          IntLiteral(100) [44-47]
+  BinaryExpression(=) [55-66]
+    PathExpression [55-62]
+      Identifier(VenueId) [55-62]
+    IntLiteral(1) [65-66]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#update_with_then_return_example
+UPDATE Singers
+SET BirthDate = '1990-10-10'
+WHERE FirstName = 'Russell'
+THEN RETURN SingerId, EXTRACT(YEAR FROM BirthDate) AS year
+--
+UpdateStatement [0-130]
+  PathExpression [7-14]
+    Identifier(Singers) [7-14]
+  UpdateItemList [19-43]
+    UpdateItem [19-43]
+      UpdateSetValue [19-43]
+        PathExpression [19-28]
+          Identifier(BirthDate) [19-28]
+        StringLiteral [31-43]
+          StringLiteralComponent('1990-10-10') [31-43]
+  BinaryExpression(=) [50-71]
+    PathExpression [50-59]
+      Identifier(FirstName) [50-59]
+    StringLiteral [62-71]
+      StringLiteralComponent('Russell') [62-71]
+  ReturningClause [72-130]
+    SelectList [84-130]
+      SelectColumn [84-92]
+        PathExpression [84-92]
+          Identifier(SingerId) [84-92]
+      SelectColumn [94-130]
+        ExtractExpression [94-122]
+          PathExpression [102-106]
+            Identifier(YEAR) [102-106]
+          PathExpression [112-121]
+            Identifier(BirthDate) [112-121]
+        Alias [123-130]
+          Identifier(year) [126-130]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#commit_timestamps
+UPDATE Singers SET LastUpdated = PENDING_COMMIT_TIMESTAMP() WHERE SingerId = 1
+--
+UpdateStatement [0-78]
+  PathExpression [7-14]
+    Identifier(Singers) [7-14]
+  UpdateItemList [19-59]
+    UpdateItem [19-59]
+      UpdateSetValue [19-59]
+        PathExpression [19-30]
+          Identifier(LastUpdated) [19-30]
+        FunctionCall [33-59]
+          PathExpression [33-57]
+            Identifier(PENDING_COMMIT_TIMESTAMP) [33-57]
+  BinaryExpression(=) [66-78]
+    PathExpression [66-74]
+      Identifier(SingerId) [66-74]
+    IntLiteral(1) [77-78]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#update-fields-protocol-buffers
+UPDATE Singers s
+SET s.AlbumInfo.tracks = 15
+WHERE s.SingerId = 5 AND s.AlbumInfo.title = "Fire is hot"
+--
+UpdateStatement [0-103]
+  PathExpression [7-14]
+    Identifier(Singers) [7-14]
+  Alias [15-16]
+    Identifier(s) [15-16]
+  UpdateItemList [21-44]
+    UpdateItem [21-44]
+      UpdateSetValue [21-44]
+        PathExpression [21-39]
+          Identifier(s) [21-22]
+          Identifier(AlbumInfo) [23-32]
+          Identifier(tracks) [33-39]
+        IntLiteral(15) [42-44]
+  AndExpr [51-103]
+    BinaryExpression(=) [51-65]
+      PathExpression [51-61]
+        Identifier(s) [51-52]
+        Identifier(SingerId) [53-61]
+      IntLiteral(5) [64-65]
+    BinaryExpression(=) [70-103]
+      PathExpression [70-87]
+        Identifier(s) [70-71]
+        Identifier(AlbumInfo) [72-81]
+        Identifier(title) [82-87]
+      StringLiteral [90-103]
+        StringLiteralComponent("Fire is hot") [90-103]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#update-fields-protocol-buffers
+UPDATE Singers s
+SET s.AlbumInfo.comments = ["A good album!", "Hurt my ears!", "Totally unlistenable."]
+WHERE s.SingerId = 5 AND s.AlbumInfo.title = "Fire is Hot"
+--
+UpdateStatement [0-162]
+  PathExpression [7-14]
+    Identifier(Singers) [7-14]
+  Alias [15-16]
+    Identifier(s) [15-16]
+  UpdateItemList [21-103]
+    UpdateItem [21-103]
+      UpdateSetValue [21-103]
+        PathExpression [21-41]
+          Identifier(s) [21-22]
+          Identifier(AlbumInfo) [23-32]
+          Identifier(comments) [33-41]
+        ArrayConstructor [44-103]
+          StringLiteral [45-60]
+            StringLiteralComponent("A good album!") [45-60]
+          StringLiteral [62-77]
+            StringLiteralComponent("Hurt my ears!") [62-77]
+          StringLiteral [79-102]
+            StringLiteralComponent("Totally unlistenable.") [79-102]
+  AndExpr [110-162]
+    BinaryExpression(=) [110-124]
+      PathExpression [110-120]
+        Identifier(s) [110-111]
+        Identifier(SingerId) [112-120]
+      IntLiteral(5) [123-124]
+    BinaryExpression(=) [129-162]
+      PathExpression [129-146]
+        Identifier(s) [129-130]
+        Identifier(AlbumInfo) [131-140]
+        Identifier(title) [141-146]
+      StringLiteral [149-162]
+        StringLiteralComponent("Fire is Hot") [149-162]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#nested-updates-protocol-buffers
+UPDATE Singers s
+SET (INSERT s.AlbumInfo.comments
+     VALUES ("Groovy!"))
+WHERE s.SingerId = 5 AND s.AlbumInfo.title = "Fire is Hot"
+--
+UpdateStatement [0-133]
+  PathExpression [7-14]
+    Identifier(Singers) [7-14]
+  Alias [15-16]
+    Identifier(s) [15-16]
+  UpdateItemList [21-74]
+    UpdateItem [21-74]
+      InsertStatement [22-73]
+        PathExpression [29-49]
+          Identifier(s) [29-30]
+          Identifier(AlbumInfo) [31-40]
+          Identifier(comments) [41-49]
+        InsertValuesRowList [55-73]
+          InsertValuesRow [62-73]
+            StringLiteral [63-72]
+              StringLiteralComponent("Groovy!") [63-72]
+  AndExpr [81-133]
+    BinaryExpression(=) [81-95]
+      PathExpression [81-91]
+        Identifier(s) [81-82]
+        Identifier(SingerId) [83-91]
+      IntLiteral(5) [94-95]
+    BinaryExpression(=) [100-133]
+      PathExpression [100-117]
+        Identifier(s) [100-101]
+        Identifier(AlbumInfo) [102-111]
+        Identifier(title) [112-117]
+      StringLiteral [120-133]
+        StringLiteralComponent("Fire is Hot") [120-133]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#nested-updates-protocol-buffers
+UPDATE Singers s
+SET (INSERT s.AlbumInfo.Song(Song)
+     VALUES ("songtitle: 'Bonus Track', length: 180"))
+WHERE s.SingerId = 5 AND s.AlbumInfo.title = "Fire is Hot"
+--
+UpdateStatement [0-165]
+  PathExpression [7-14]
+    Identifier(Singers) [7-14]
+  Alias [15-16]
+    Identifier(s) [15-16]
+  UpdateItemList [21-106]
+    UpdateItem [21-106]
+      InsertStatement [22-105]
+        PathExpression [29-45]
+          Identifier(s) [29-30]
+          Identifier(AlbumInfo) [31-40]
+          Identifier(Song) [41-45]
+        ColumnList [45-51]
+          Identifier(Song) [46-50]
+        InsertValuesRowList [57-105]
+          InsertValuesRow [64-105]
+            StringLiteral [65-104]
+              StringLiteralComponent("songtitle: 'Bonus Track', length: 180") [65-104]
+  AndExpr [113-165]
+    BinaryExpression(=) [113-127]
+      PathExpression [113-123]
+        Identifier(s) [113-114]
+        Identifier(SingerId) [115-123]
+      IntLiteral(5) [126-127]
+    BinaryExpression(=) [132-165]
+      PathExpression [132-149]
+        Identifier(s) [132-133]
+        Identifier(AlbumInfo) [134-143]
+        Identifier(title) [144-149]
+      StringLiteral [152-165]
+        StringLiteralComponent("Fire is Hot") [152-165]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#nested-updates-protocol-buffers
+UPDATE Singers s
+SET (INSERT s.AlbumInfo.Song
+     VALUES ('''songtitle: 'Bonus Track', length:180''')),
+     s.Albums.tracks = 16
+WHERE s.SingerId = 5 and s.AlbumInfo.title = "Fire is Hot"
+--
+UpdateStatement [0-189]
+  PathExpression [7-14]
+    Identifier(Singers) [7-14]
+  Alias [15-16]
+    Identifier(s) [15-16]
+  UpdateItemList [21-130]
+    UpdateItem [21-103]
+      InsertStatement [22-102]
+        PathExpression [29-45]
+          Identifier(s) [29-30]
+          Identifier(AlbumInfo) [31-40]
+          Identifier(Song) [41-45]
+        InsertValuesRowList [51-102]
+          InsertValuesRow [58-102]
+            StringLiteral [59-101]
+              StringLiteralComponent('''songtitle: 'Bonus Track', length:180''') [59-101]
+    UpdateItem [110-130]
+      UpdateSetValue [110-130]
+        PathExpression [110-125]
+          Identifier(s) [110-111]
+          Identifier(Albums) [112-118]
+          Identifier(tracks) [119-125]
+        IntLiteral(16) [128-130]
+  AndExpr [137-189]
+    BinaryExpression(=) [137-151]
+      PathExpression [137-147]
+        Identifier(s) [137-138]
+        Identifier(SingerId) [139-147]
+      IntLiteral(5) [150-151]
+    BinaryExpression(=) [156-189]
+      PathExpression [156-173]
+        Identifier(s) [156-157]
+        Identifier(AlbumInfo) [158-167]
+        Identifier(title) [168-173]
+      StringLiteral [176-189]
+        StringLiteralComponent("Fire is Hot") [176-189]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#nested-updates-protocol-buffers
+UPDATE Singers s
+SET (UPDATE s.AlbumInfo.Song so
+    SET (INSERT INTO so.Chart
+         VALUES ("chartname: 'Galaxy Top 100', rank: 5"))
+    WHERE so.songtitle = "Bonus Track")
+WHERE s.SingerId = 5
+--
+UpdateStatement [0-197]
+  PathExpression [7-14]
+    Identifier(Singers) [7-14]
+  Alias [15-16]
+    Identifier(s) [15-16]
+  UpdateItemList [21-176]
+    UpdateItem [21-176]
+      UpdateStatement [22-175]
+        PathExpression [29-45]
+          Identifier(s) [29-30]
+          Identifier(AlbumInfo) [31-40]
+          Identifier(Song) [41-45]
+        Alias [46-48]
+          Identifier(so) [46-48]
+        UpdateItemList [57-136]
+          UpdateItem [57-136]
+            InsertStatement [58-135]
+              PathExpression [70-78]
+                Identifier(so) [70-72]
+                Identifier(Chart) [73-78]
+              InsertValuesRowList [88-135]
+                InsertValuesRow [95-135]
+                  StringLiteral [96-134]
+                    StringLiteralComponent("chartname: 'Galaxy Top 100', rank: 5") [96-134]
+        BinaryExpression(=) [147-175]
+          PathExpression [147-159]
+            Identifier(so) [147-149]
+            Identifier(songtitle) [150-159]
+          StringLiteral [162-175]
+            StringLiteralComponent("Bonus Track") [162-175]
+  BinaryExpression(=) [183-197]
+    PathExpression [183-193]
+      Identifier(s) [183-184]
+      Identifier(SingerId) [185-193]
+    IntLiteral(5) [196-197]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#nested-updates-protocol-buffers
+UPDATE Singers s
+SET (UPDATE s.AlbumInfo.Song so
+     SET (UPDATE so.Chart c
+          SET c.rank = 2
+          WHERE c.chartname = "Galaxy Top 100")
+     WHERE so.songtitle = "Bonus Track")
+WHERE s.SingerId = 5
+--
+UpdateStatement [0-211]
+  PathExpression [7-14]
+    Identifier(Singers) [7-14]
+  Alias [15-16]
+    Identifier(s) [15-16]
+  UpdateItemList [21-190]
+    UpdateItem [21-190]
+      UpdateStatement [22-189]
+        PathExpression [29-45]
+          Identifier(s) [29-30]
+          Identifier(AlbumInfo) [31-40]
+          Identifier(Song) [41-45]
+        Alias [46-48]
+          Identifier(so) [46-48]
+        UpdateItemList [58-149]
+          UpdateItem [58-149]
+            UpdateStatement [59-148]
+              PathExpression [66-74]
+                Identifier(so) [66-68]
+                Identifier(Chart) [69-74]
+              Alias [75-76]
+                Identifier(c) [75-76]
+              UpdateItemList [91-101]
+                UpdateItem [91-101]
+                  UpdateSetValue [91-101]
+                    PathExpression [91-97]
+                      Identifier(c) [91-92]
+                      Identifier(rank) [93-97]
+                    IntLiteral(2) [100-101]
+              BinaryExpression(=) [118-148]
+                PathExpression [118-129]
+                  Identifier(c) [118-119]
+                  Identifier(chartname) [120-129]
+                StringLiteral [132-148]
+                  StringLiteralComponent("Galaxy Top 100") [132-148]
+        BinaryExpression(=) [161-189]
+          PathExpression [161-173]
+            Identifier(so) [161-163]
+            Identifier(songtitle) [164-173]
+          StringLiteral [176-189]
+            StringLiteralComponent("Bonus Track") [176-189]
+  BinaryExpression(=) [197-211]
+    PathExpression [197-207]
+      Identifier(s) [197-198]
+      Identifier(SingerId) [199-207]
+    IntLiteral(5) [210-211]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#modify_multiple_fields
+UPDATE Singers s
+SET
+    (DELETE FROM s.SingerInfo.Residence r WHERE r.City = 'Seattle'),
+    (UPDATE s.AlbumInfo.Song song SET song.songtitle = 'No, This Is Rubbish' WHERE song.songtitle = 'This Is Pretty Good'),
+    (INSERT s.AlbumInfo.Song VALUES ("songtitle: 'The Second Best Song'"))
+WHERE SingerId = 3 AND s.AlbumInfo.title = 'Go! Go! Go!'
+--
+UpdateStatement [0-345]
+  PathExpression [7-14]
+    Identifier(Singers) [7-14]
+  Alias [15-16]
+    Identifier(s) [15-16]
+  UpdateItemList [25-288]
+    UpdateItem [25-88]
+      DeleteStatement [26-87]
+        PathExpression [38-60]
+          Identifier(s) [38-39]
+          Identifier(SingerInfo) [40-50]
+          Identifier(Residence) [51-60]
+        Alias [61-62]
+          Identifier(r) [61-62]
+        BinaryExpression(=) [69-87]
+          PathExpression [69-75]
+            Identifier(r) [69-70]
+            Identifier(City) [71-75]
+          StringLiteral [78-87]
+            StringLiteralComponent('Seattle') [78-87]
+    UpdateItem [94-212]
+      UpdateStatement [95-211]
+        PathExpression [102-118]
+          Identifier(s) [102-103]
+          Identifier(AlbumInfo) [104-113]
+          Identifier(Song) [114-118]
+        Alias [119-123]
+          Identifier(song) [119-123]
+        UpdateItemList [128-166]
+          UpdateItem [128-166]
+            UpdateSetValue [128-166]
+              PathExpression [128-142]
+                Identifier(song) [128-132]
+                Identifier(songtitle) [133-142]
+              StringLiteral [145-166]
+                StringLiteralComponent('No, This Is Rubbish') [145-166]
+        BinaryExpression(=) [173-211]
+          PathExpression [173-187]
+            Identifier(song) [173-177]
+            Identifier(songtitle) [178-187]
+          StringLiteral [190-211]
+            StringLiteralComponent('This Is Pretty Good') [190-211]
+    UpdateItem [218-288]
+      InsertStatement [219-287]
+        PathExpression [226-242]
+          Identifier(s) [226-227]
+          Identifier(AlbumInfo) [228-237]
+          Identifier(Song) [238-242]
+        InsertValuesRowList [243-287]
+          InsertValuesRow [250-287]
+            StringLiteral [251-286]
+              StringLiteralComponent("songtitle: 'The Second Best Song'") [251-286]
+  AndExpr [295-345]
+    BinaryExpression(=) [295-307]
+      PathExpression [295-303]
+        Identifier(SingerId) [295-303]
+      IntLiteral(3) [306-307]
+    BinaryExpression(=) [312-345]
+      PathExpression [312-329]
+        Identifier(s) [312-313]
+        Identifier(AlbumInfo) [314-323]
+        Identifier(title) [324-329]
+      StringLiteral [332-345]
+        StringLiteralComponent('Go! Go! Go!') [332-345]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#modify_multiple_fields
+UPDATE Singers s
+SET
+    (DELETE FROM s.SingerInfo.Residence r WHERE r.City = 'Seattle'),
+    (UPDATE s.SingerInfo.Residence r SET r.end_year = 2015 WHERE r.City = 'Eugene'),
+    (INSERT s.AlbumInfo.Song VALUES ("songtitle: 'The Second Best Song'"))
+WHERE SingerId = 3 AND s.AlbumInfo.title = 'Go! Go! Go!'
+--
+UpdateStatement [0-306]
+  PathExpression [7-14]
+    Identifier(Singers) [7-14]
+  Alias [15-16]
+    Identifier(s) [15-16]
+  UpdateItemList [25-249]
+    UpdateItem [25-88]
+      DeleteStatement [26-87]
+        PathExpression [38-60]
+          Identifier(s) [38-39]
+          Identifier(SingerInfo) [40-50]
+          Identifier(Residence) [51-60]
+        Alias [61-62]
+          Identifier(r) [61-62]
+        BinaryExpression(=) [69-87]
+          PathExpression [69-75]
+            Identifier(r) [69-70]
+            Identifier(City) [71-75]
+          StringLiteral [78-87]
+            StringLiteralComponent('Seattle') [78-87]
+    UpdateItem [94-173]
+      UpdateStatement [95-172]
+        PathExpression [102-124]
+          Identifier(s) [102-103]
+          Identifier(SingerInfo) [104-114]
+          Identifier(Residence) [115-124]
+        Alias [125-126]
+          Identifier(r) [125-126]
+        UpdateItemList [131-148]
+          UpdateItem [131-148]
+            UpdateSetValue [131-148]
+              PathExpression [131-141]
+                Identifier(r) [131-132]
+                Identifier(end_year) [133-141]
+              IntLiteral(2015) [144-148]
+        BinaryExpression(=) [155-172]
+          PathExpression [155-161]
+            Identifier(r) [155-156]
+            Identifier(City) [157-161]
+          StringLiteral [164-172]
+            StringLiteralComponent('Eugene') [164-172]
+    UpdateItem [179-249]
+      InsertStatement [180-248]
+        PathExpression [187-203]
+          Identifier(s) [187-188]
+          Identifier(AlbumInfo) [189-198]
+          Identifier(Song) [199-203]
+        InsertValuesRowList [204-248]
+          InsertValuesRow [211-248]
+            StringLiteral [212-247]
+              StringLiteralComponent("songtitle: 'The Second Best Song'") [212-247]
+  AndExpr [256-306]
+    BinaryExpression(=) [256-268]
+      PathExpression [256-264]
+        Identifier(SingerId) [256-264]
+      IntLiteral(3) [267-268]
+    BinaryExpression(=) [273-306]
+      PathExpression [273-290]
+        Identifier(s) [273-274]
+        Identifier(AlbumInfo) [275-284]
+        Identifier(title) [285-290]
+      StringLiteral [293-306]
+        StringLiteralComponent('Go! Go! Go!') [293-306]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax#modify_multiple_fields
+UPDATE Singers s
+SET
+    (DELETE FROM s.SingerInfo.Residence r WHERE r.City = 'Seattle'),
+    (INSERT s.AlbumInfo.Song VALUES ("songtitle: 'The Second Best Song'")),
+    (UPDATE s.SingerInfo.Residence r SET r.end_year = 2015 WHERE r.City = 'Eugene')
+WHERE SingerId = 3 AND s.AlbumInfo.title = 'Go! Go! Go!'
+--
+UpdateStatement [0-306]
+  PathExpression [7-14]
+    Identifier(Singers) [7-14]
+  Alias [15-16]
+    Identifier(s) [15-16]
+  UpdateItemList [25-249]
+    UpdateItem [25-88]
+      DeleteStatement [26-87]
+        PathExpression [38-60]
+          Identifier(s) [38-39]
+          Identifier(SingerInfo) [40-50]
+          Identifier(Residence) [51-60]
+        Alias [61-62]
+          Identifier(r) [61-62]
+        BinaryExpression(=) [69-87]
+          PathExpression [69-75]
+            Identifier(r) [69-70]
+            Identifier(City) [71-75]
+          StringLiteral [78-87]
+            StringLiteralComponent('Seattle') [78-87]
+    UpdateItem [94-164]
+      InsertStatement [95-163]
+        PathExpression [102-118]
+          Identifier(s) [102-103]
+          Identifier(AlbumInfo) [104-113]
+          Identifier(Song) [114-118]
+        InsertValuesRowList [119-163]
+          InsertValuesRow [126-163]
+            StringLiteral [127-162]
+              StringLiteralComponent("songtitle: 'The Second Best Song'") [127-162]
+    UpdateItem [170-249]
+      UpdateStatement [171-248]
+        PathExpression [178-200]
+          Identifier(s) [178-179]
+          Identifier(SingerInfo) [180-190]
+          Identifier(Residence) [191-200]
+        Alias [201-202]
+          Identifier(r) [201-202]
+        UpdateItemList [207-224]
+          UpdateItem [207-224]
+            UpdateSetValue [207-224]
+              PathExpression [207-217]
+                Identifier(r) [207-208]
+                Identifier(end_year) [209-217]
+              IntLiteral(2015) [220-224]
+        BinaryExpression(=) [231-248]
+          PathExpression [231-237]
+            Identifier(r) [231-232]
+            Identifier(City) [233-237]
+          StringLiteral [240-248]
+            StringLiteralComponent('Eugene') [240-248]
+  AndExpr [256-306]
+    BinaryExpression(=) [256-268]
+      PathExpression [256-264]
+        Identifier(SingerId) [256-264]
+      IntLiteral(3) [267-268]
+    BinaryExpression(=) [273-306]
+      PathExpression [273-290]
+        Identifier(s) [273-274]
+        Identifier(AlbumInfo) [275-284]
+        Identifier(title) [285-290]
+      StringLiteral [293-306]
+        StringLiteralComponent('Go! Go! Go!') [293-306]
diff --git a/parser/testdata/spanner/format-elements.test b/parser/testdata/spanner/format-elements.test
new file mode 100644
index 0000000..310abfb
--- /dev/null
+++ b/parser/testdata/spanner/format-elements.test
@@ -0,0 +1,62 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/format-elements
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/format-elements#format_elements_date_time
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT FORMAT_DATE("%b-%d-%Y", DATE "2008-12-25") AS formatted
+--
+QueryStatement [0-62]
+  Query [0-62]
+    Select [0-62]
+      SelectList [7-62]
+        SelectColumn [7-62]
+          FunctionCall [7-49]
+            PathExpression [7-18]
+              Identifier(FORMAT_DATE) [7-18]
+            StringLiteral [19-29]
+              StringLiteralComponent("%b-%d-%Y") [19-29]
+            DateOrTimeLiteral(TYPE_DATE) [31-48]
+              StringLiteral [36-48]
+                StringLiteralComponent("2008-12-25") [36-48]
+          Alias [50-62]
+            Identifier(formatted) [53-62]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/format-elements#format_elements_date_time
+SELECT FORMAT_TIMESTAMP("%b %Y %Ez", TIMESTAMP "2008-12-25 15:30:00+00")
+  AS formatted
+--
+QueryStatement [0-87]
+  Query [0-87]
+    Select [0-87]
+      SelectList [7-87]
+        SelectColumn [7-87]
+          FunctionCall [7-72]
+            PathExpression [7-23]
+              Identifier(FORMAT_TIMESTAMP) [7-23]
+            StringLiteral [24-35]
+              StringLiteralComponent("%b %Y %Ez") [24-35]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [37-71]
+              StringLiteral [47-71]
+                StringLiteralComponent("2008-12-25 15:30:00+00") [47-71]
+          Alias [75-87]
+            Identifier(formatted) [78-87]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/format-elements#format_elements_date_time
+SELECT PARSE_DATE("%Y%m%d", "20081225") AS parsed
+--
+QueryStatement [0-49]
+  Query [0-49]
+    Select [0-49]
+      SelectList [7-49]
+        SelectColumn [7-49]
+          FunctionCall [7-39]
+            PathExpression [7-17]
+              Identifier(PARSE_DATE) [7-17]
+            StringLiteral [18-26]
+              StringLiteralComponent("%Y%m%d") [18-26]
+            StringLiteral [28-38]
+              StringLiteralComponent("20081225") [28-38]
+          Alias [40-49]
+            Identifier(parsed) [43-49]
diff --git a/parser/testdata/spanner/graph-gql-functions.test b/parser/testdata/spanner/graph-gql-functions.test
new file mode 100644
index 0000000..d0bae9b
--- /dev/null
+++ b/parser/testdata/spanner/graph-gql-functions.test
@@ -0,0 +1,2034 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#destination_node_id
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+DESTINATION_NODE_ID(edge_element)
+--
+FunctionCall [0-33]
+  PathExpression [0-19]
+    Identifier(DESTINATION_NODE_ID) [0-19]
+  PathExpression [20-32]
+    Identifier(edge_element) [20-32]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#destination_node_id
+GRAPH FinGraph
+MATCH (:Person)-[o:Owns]->(a:Account)
+RETURN a.id AS account_id, DESTINATION_NODE_ID(o) AS destination_node_id
+--
+QueryStatement [0-125]
+  Query [0-125]
+    GqlQuery [0-125]
+      GraphTableQuery [0-125]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-125]
+          GqlOperatorList [15-125]
+            GqlMatch [15-52]
+              GraphPattern [21-52]
+                GraphPathPattern [21-52]
+                  GraphNodePattern [21-30]
+                    GraphElementPatternFiller [22-29]
+                      GraphLabelFilter [22-29]
+                        GraphElementLabel [23-29]
+                          Identifier(Person) [23-29]
+                  GraphEdgePattern [30-41]
+                    GraphElementPatternFiller [32-38]
+                      Identifier(o) [32-33]
+                      GraphLabelFilter [33-38]
+                        GraphElementLabel [34-38]
+                          Identifier(Owns) [34-38]
+                  GraphNodePattern [41-52]
+                    GraphElementPatternFiller [42-51]
+                      Identifier(a) [42-43]
+                      GraphLabelFilter [43-51]
+                        GraphElementLabel [44-51]
+                          Identifier(Account) [44-51]
+            GqlReturn [53-125]
+              Select [60-125]
+                SelectList [60-125]
+                  SelectColumn [60-78]
+                    PathExpression [60-64]
+                      Identifier(a) [60-61]
+                      Identifier(id) [62-64]
+                    Alias [65-78]
+                      Identifier(account_id) [68-78]
+                  SelectColumn [80-125]
+                    FunctionCall [80-102]
+                      PathExpression [80-99]
+                        Identifier(DESTINATION_NODE_ID) [80-99]
+                      PathExpression [100-101]
+                        Identifier(o) [100-101]
+                    Alias [103-125]
+                      Identifier(destination_node_id) [106-125]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#edges
+[mode=expression]
+EDGES(graph_path)
+--
+FunctionCall [0-17]
+  PathExpression [0-5]
+    Identifier(EDGES) [0-5]
+  PathExpression [6-16]
+    Identifier(graph_path) [6-16]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#edges
+GRAPH FinGraph
+MATCH p=(src:Account)-[t1:Transfers]->(mid:Account)-[t2:Transfers]->(dst:Account)
+LET es = EDGES(p)
+RETURN ARRAY_CONCAT(ARRAY_TRANSFORM(es, e -> e.Id), [dst.Id]) as ids_in_path
+--
+QueryStatement [0-191]
+  Query [0-191]
+    GqlQuery [0-191]
+      GraphTableQuery [0-191]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-191]
+          GqlOperatorList [15-191]
+            GqlMatch [15-96]
+              GraphPattern [21-96]
+                GraphPathPattern [21-96]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [23-36]
+                    GraphElementPatternFiller [24-35]
+                      Identifier(src) [24-27]
+                      GraphLabelFilter [27-35]
+                        GraphElementLabel [28-35]
+                          Identifier(Account) [28-35]
+                  GraphEdgePattern [36-53]
+                    GraphElementPatternFiller [38-50]
+                      Identifier(t1) [38-40]
+                      GraphLabelFilter [40-50]
+                        GraphElementLabel [41-50]
+                          Identifier(Transfers) [41-50]
+                  GraphNodePattern [53-66]
+                    GraphElementPatternFiller [54-65]
+                      Identifier(mid) [54-57]
+                      GraphLabelFilter [57-65]
+                        GraphElementLabel [58-65]
+                          Identifier(Account) [58-65]
+                  GraphEdgePattern [66-83]
+                    GraphElementPatternFiller [68-80]
+                      Identifier(t2) [68-70]
+                      GraphLabelFilter [70-80]
+                        GraphElementLabel [71-80]
+                          Identifier(Transfers) [71-80]
+                  GraphNodePattern [83-96]
+                    GraphElementPatternFiller [84-95]
+                      Identifier(dst) [84-87]
+                      GraphLabelFilter [87-95]
+                        GraphElementLabel [88-95]
+                          Identifier(Account) [88-95]
+            GqlLet [97-114]
+              GqlLetVariableDefinitionList [101-114]
+                GqlLetVariableDefinition [101-114]
+                  Identifier(es) [101-103]
+                  FunctionCall [106-114]
+                    PathExpression [106-111]
+                      Identifier(EDGES) [106-111]
+                    PathExpression [112-113]
+                      Identifier(p) [112-113]
+            GqlReturn [115-191]
+              Select [122-191]
+                SelectList [122-191]
+                  SelectColumn [122-191]
+                    FunctionCall [122-176]
+                      PathExpression [122-134]
+                        Identifier(ARRAY_CONCAT) [122-134]
+                      FunctionCall [135-165]
+                        PathExpression [135-150]
+                          Identifier(ARRAY_TRANSFORM) [135-150]
+                        PathExpression [151-153]
+                          Identifier(es) [151-153]
+                        Lambda [155-164]
+                          PathExpression [155-156]
+                            Identifier(e) [155-156]
+                          PathExpression [160-164]
+                            Identifier(e) [160-161]
+                            Identifier(Id) [162-164]
+                      ArrayConstructor [167-175]
+                        PathExpression [168-174]
+                          Identifier(dst) [168-171]
+                          Identifier(Id) [172-174]
+                    Alias [177-191]
+                      Identifier(ids_in_path) [180-191]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#edges
+GRAPH FinGraph
+MATCH p=(src:Account)-[t1:Transfers]->(mid:Account)-[t2:Transfers]->(dst:Account)
+LET es = EDGES(p)
+RETURN TO_JSON(es) AS edges
+--
+QueryStatement [0-142]
+  Query [0-142]
+    GqlQuery [0-142]
+      GraphTableQuery [0-142]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-142]
+          GqlOperatorList [15-142]
+            GqlMatch [15-96]
+              GraphPattern [21-96]
+                GraphPathPattern [21-96]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [23-36]
+                    GraphElementPatternFiller [24-35]
+                      Identifier(src) [24-27]
+                      GraphLabelFilter [27-35]
+                        GraphElementLabel [28-35]
+                          Identifier(Account) [28-35]
+                  GraphEdgePattern [36-53]
+                    GraphElementPatternFiller [38-50]
+                      Identifier(t1) [38-40]
+                      GraphLabelFilter [40-50]
+                        GraphElementLabel [41-50]
+                          Identifier(Transfers) [41-50]
+                  GraphNodePattern [53-66]
+                    GraphElementPatternFiller [54-65]
+                      Identifier(mid) [54-57]
+                      GraphLabelFilter [57-65]
+                        GraphElementLabel [58-65]
+                          Identifier(Account) [58-65]
+                  GraphEdgePattern [66-83]
+                    GraphElementPatternFiller [68-80]
+                      Identifier(t2) [68-70]
+                      GraphLabelFilter [70-80]
+                        GraphElementLabel [71-80]
+                          Identifier(Transfers) [71-80]
+                  GraphNodePattern [83-96]
+                    GraphElementPatternFiller [84-95]
+                      Identifier(dst) [84-87]
+                      GraphLabelFilter [87-95]
+                        GraphElementLabel [88-95]
+                          Identifier(Account) [88-95]
+            GqlLet [97-114]
+              GqlLetVariableDefinitionList [101-114]
+                GqlLetVariableDefinition [101-114]
+                  Identifier(es) [101-103]
+                  FunctionCall [106-114]
+                    PathExpression [106-111]
+                      Identifier(EDGES) [106-111]
+                    PathExpression [112-113]
+                      Identifier(p) [112-113]
+            GqlReturn [115-142]
+              Select [122-142]
+                SelectList [122-142]
+                  SelectColumn [122-142]
+                    FunctionCall [122-133]
+                      PathExpression [122-129]
+                        Identifier(TO_JSON) [122-129]
+                      PathExpression [130-132]
+                        Identifier(es) [130-132]
+                    Alias [134-142]
+                      Identifier(edges) [137-142]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#element_definition_name
+[mode=expression]
+ELEMENT_DEFINITION_NAME(element)
+--
+FunctionCall [0-32]
+  PathExpression [0-23]
+    Identifier(ELEMENT_DEFINITION_NAME) [0-23]
+  PathExpression [24-31]
+    Identifier(element) [24-31]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#element_definition_name
+GRAPH FinGraph
+MATCH (p:Person)-[o:Owns]->(:Account)
+RETURN
+  p.name AS name,
+  ELEMENT_DEFINITION_NAME(p) AS node_element_definition_name,
+  ELEMENT_DEFINITION_NAME(o) AS edge_element_definition_name
+--
+QueryStatement [0-200]
+  Query [0-200]
+    GqlQuery [0-200]
+      GraphTableQuery [0-200]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-200]
+          GqlOperatorList [15-200]
+            GqlMatch [15-52]
+              GraphPattern [21-52]
+                GraphPathPattern [21-52]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                  GraphEdgePattern [31-42]
+                    GraphElementPatternFiller [33-39]
+                      Identifier(o) [33-34]
+                      GraphLabelFilter [34-39]
+                        GraphElementLabel [35-39]
+                          Identifier(Owns) [35-39]
+                  GraphNodePattern [42-52]
+                    GraphElementPatternFiller [43-51]
+                      GraphLabelFilter [43-51]
+                        GraphElementLabel [44-51]
+                          Identifier(Account) [44-51]
+            GqlReturn [53-200]
+              Select [62-200]
+                SelectList [62-200]
+                  SelectColumn [62-76]
+                    PathExpression [62-68]
+                      Identifier(p) [62-63]
+                      Identifier(name) [64-68]
+                    Alias [69-76]
+                      Identifier(name) [72-76]
+                  SelectColumn [80-138]
+                    FunctionCall [80-106]
+                      PathExpression [80-103]
+                        Identifier(ELEMENT_DEFINITION_NAME) [80-103]
+                      PathExpression [104-105]
+                        Identifier(p) [104-105]
+                    Alias [107-138]
+                      Identifier(node_element_definition_name) [110-138]
+                  SelectColumn [142-200]
+                    FunctionCall [142-168]
+                      PathExpression [142-165]
+                        Identifier(ELEMENT_DEFINITION_NAME) [142-165]
+                      PathExpression [166-167]
+                        Identifier(o) [166-167]
+                    Alias [169-200]
+                      Identifier(edge_element_definition_name) [172-200]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#element_id
+[mode=expression]
+ELEMENT_ID(element)
+--
+FunctionCall [0-19]
+  PathExpression [0-10]
+    Identifier(ELEMENT_ID) [0-10]
+  PathExpression [11-18]
+    Identifier(element) [11-18]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#element_id
+GRAPH FinGraph
+MATCH (p:Person)-[o:Owns]->(:Account)
+RETURN p.name AS name, ELEMENT_ID(p) AS node_element_id, ELEMENT_ID(o) AS edge_element_id
+--
+QueryStatement [0-142]
+  Query [0-142]
+    GqlQuery [0-142]
+      GraphTableQuery [0-142]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-142]
+          GqlOperatorList [15-142]
+            GqlMatch [15-52]
+              GraphPattern [21-52]
+                GraphPathPattern [21-52]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                  GraphEdgePattern [31-42]
+                    GraphElementPatternFiller [33-39]
+                      Identifier(o) [33-34]
+                      GraphLabelFilter [34-39]
+                        GraphElementLabel [35-39]
+                          Identifier(Owns) [35-39]
+                  GraphNodePattern [42-52]
+                    GraphElementPatternFiller [43-51]
+                      GraphLabelFilter [43-51]
+                        GraphElementLabel [44-51]
+                          Identifier(Account) [44-51]
+            GqlReturn [53-142]
+              Select [60-142]
+                SelectList [60-142]
+                  SelectColumn [60-74]
+                    PathExpression [60-66]
+                      Identifier(p) [60-61]
+                      Identifier(name) [62-66]
+                    Alias [67-74]
+                      Identifier(name) [70-74]
+                  SelectColumn [76-108]
+                    FunctionCall [76-89]
+                      PathExpression [76-86]
+                        Identifier(ELEMENT_ID) [76-86]
+                      PathExpression [87-88]
+                        Identifier(p) [87-88]
+                    Alias [90-108]
+                      Identifier(node_element_id) [93-108]
+                  SelectColumn [110-142]
+                    FunctionCall [110-123]
+                      PathExpression [110-120]
+                        Identifier(ELEMENT_ID) [110-120]
+                      PathExpression [121-122]
+                        Identifier(o) [121-122]
+                    Alias [124-142]
+                      Identifier(edge_element_id) [127-142]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#is_acyclic
+[mode=expression]
+IS_ACYCLIC(graph_path)
+--
+FunctionCall [0-22]
+  PathExpression [0-10]
+    Identifier(IS_ACYCLIC) [0-10]
+  PathExpression [11-21]
+    Identifier(graph_path) [11-21]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#is_acyclic
+GRAPH FinGraph
+MATCH p=(src:Account)-[t1:Transfers]->(mid:Account)-[t2:Transfers]->(dst:Account)
+RETURN src.id AS source_account_id, IS_ACYCLIC(p) AS is_acyclic_path
+--
+QueryStatement [0-165]
+  Query [0-165]
+    GqlQuery [0-165]
+      GraphTableQuery [0-165]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-165]
+          GqlOperatorList [15-165]
+            GqlMatch [15-96]
+              GraphPattern [21-96]
+                GraphPathPattern [21-96]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [23-36]
+                    GraphElementPatternFiller [24-35]
+                      Identifier(src) [24-27]
+                      GraphLabelFilter [27-35]
+                        GraphElementLabel [28-35]
+                          Identifier(Account) [28-35]
+                  GraphEdgePattern [36-53]
+                    GraphElementPatternFiller [38-50]
+                      Identifier(t1) [38-40]
+                      GraphLabelFilter [40-50]
+                        GraphElementLabel [41-50]
+                          Identifier(Transfers) [41-50]
+                  GraphNodePattern [53-66]
+                    GraphElementPatternFiller [54-65]
+                      Identifier(mid) [54-57]
+                      GraphLabelFilter [57-65]
+                        GraphElementLabel [58-65]
+                          Identifier(Account) [58-65]
+                  GraphEdgePattern [66-83]
+                    GraphElementPatternFiller [68-80]
+                      Identifier(t2) [68-70]
+                      GraphLabelFilter [70-80]
+                        GraphElementLabel [71-80]
+                          Identifier(Transfers) [71-80]
+                  GraphNodePattern [83-96]
+                    GraphElementPatternFiller [84-95]
+                      Identifier(dst) [84-87]
+                      GraphLabelFilter [87-95]
+                        GraphElementLabel [88-95]
+                          Identifier(Account) [88-95]
+            GqlReturn [97-165]
+              Select [104-165]
+                SelectList [104-165]
+                  SelectColumn [104-131]
+                    PathExpression [104-110]
+                      Identifier(src) [104-107]
+                      Identifier(id) [108-110]
+                    Alias [111-131]
+                      Identifier(source_account_id) [114-131]
+                  SelectColumn [133-165]
+                    FunctionCall [133-146]
+                      PathExpression [133-143]
+                        Identifier(IS_ACYCLIC) [133-143]
+                      PathExpression [144-145]
+                        Identifier(p) [144-145]
+                    Alias [147-165]
+                      Identifier(is_acyclic_path) [150-165]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#is_simple
+[mode=expression]
+IS_SIMPLE(graph_path)
+--
+FunctionCall [0-21]
+  PathExpression [0-9]
+    Identifier(IS_SIMPLE) [0-9]
+  PathExpression [10-20]
+    Identifier(graph_path) [10-20]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#is_simple
+GRAPH FinGraph
+MATCH p=(a1:Account)-[t1:Transfers where t1.amount > 200]->
+        (a2:Account)-[t2:Transfers where t2.amount > 200]->
+        (a3:Account)-[t3:Transfers where t3.amount > 100]->(a4:Account)
+RETURN
+  IS_SIMPLE(p) AS is_simple_path,
+  a1.id as a1_id, a2.id as a2_id, a3.id as a3_id, a4.id as a4_id
+--
+QueryStatement [0-312]
+  Query [0-312]
+    GqlQuery [0-312]
+      GraphTableQuery [0-312]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-312]
+          GqlOperatorList [15-312]
+            GqlMatch [15-206]
+              GraphPattern [21-206]
+                GraphPathPattern [21-206]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [23-35]
+                    GraphElementPatternFiller [24-34]
+                      Identifier(a1) [24-26]
+                      GraphLabelFilter [26-34]
+                        GraphElementLabel [27-34]
+                          Identifier(Account) [27-34]
+                  GraphEdgePattern [35-74]
+                    GraphElementPatternFiller [37-71]
+                      Identifier(t1) [37-39]
+                      GraphLabelFilter [39-49]
+                        GraphElementLabel [40-49]
+                          Identifier(Transfers) [40-49]
+                      WhereClause [50-71]
+                        BinaryExpression(>) [56-71]
+                          PathExpression [56-65]
+                            Identifier(t1) [56-58]
+                            Identifier(amount) [59-65]
+                          IntLiteral(200) [68-71]
+                  GraphNodePattern [83-95]
+                    GraphElementPatternFiller [84-94]
+                      Identifier(a2) [84-86]
+                      GraphLabelFilter [86-94]
+                        GraphElementLabel [87-94]
+                          Identifier(Account) [87-94]
+                  GraphEdgePattern [95-134]
+                    GraphElementPatternFiller [97-131]
+                      Identifier(t2) [97-99]
+                      GraphLabelFilter [99-109]
+                        GraphElementLabel [100-109]
+                          Identifier(Transfers) [100-109]
+                      WhereClause [110-131]
+                        BinaryExpression(>) [116-131]
+                          PathExpression [116-125]
+                            Identifier(t2) [116-118]
+                            Identifier(amount) [119-125]
+                          IntLiteral(200) [128-131]
+                  GraphNodePattern [143-155]
+                    GraphElementPatternFiller [144-154]
+                      Identifier(a3) [144-146]
+                      GraphLabelFilter [146-154]
+                        GraphElementLabel [147-154]
+                          Identifier(Account) [147-154]
+                  GraphEdgePattern [155-194]
+                    GraphElementPatternFiller [157-191]
+                      Identifier(t3) [157-159]
+                      GraphLabelFilter [159-169]
+                        GraphElementLabel [160-169]
+                          Identifier(Transfers) [160-169]
+                      WhereClause [170-191]
+                        BinaryExpression(>) [176-191]
+                          PathExpression [176-185]
+                            Identifier(t3) [176-178]
+                            Identifier(amount) [179-185]
+                          IntLiteral(100) [188-191]
+                  GraphNodePattern [194-206]
+                    GraphElementPatternFiller [195-205]
+                      Identifier(a4) [195-197]
+                      GraphLabelFilter [197-205]
+                        GraphElementLabel [198-205]
+                          Identifier(Account) [198-205]
+            GqlReturn [207-312]
+              Select [216-312]
+                SelectList [216-312]
+                  SelectColumn [216-246]
+                    FunctionCall [216-228]
+                      PathExpression [216-225]
+                        Identifier(IS_SIMPLE) [216-225]
+                      PathExpression [226-227]
+                        Identifier(p) [226-227]
+                    Alias [229-246]
+                      Identifier(is_simple_path) [232-246]
+                  SelectColumn [250-264]
+                    PathExpression [250-255]
+                      Identifier(a1) [250-252]
+                      Identifier(id) [253-255]
+                    Alias [256-264]
+                      Identifier(a1_id) [259-264]
+                  SelectColumn [266-280]
+                    PathExpression [266-271]
+                      Identifier(a2) [266-268]
+                      Identifier(id) [269-271]
+                    Alias [272-280]
+                      Identifier(a2_id) [275-280]
+                  SelectColumn [282-296]
+                    PathExpression [282-287]
+                      Identifier(a3) [282-284]
+                      Identifier(id) [285-287]
+                    Alias [288-296]
+                      Identifier(a3_id) [291-296]
+                  SelectColumn [298-312]
+                    PathExpression [298-303]
+                      Identifier(a4) [298-300]
+                      Identifier(id) [301-303]
+                    Alias [304-312]
+                      Identifier(a4_id) [307-312]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#is_trail
+[mode=expression]
+IS_TRAIL(graph_path)
+--
+FunctionCall [0-20]
+  PathExpression [0-8]
+    Identifier(IS_TRAIL) [0-8]
+  PathExpression [9-19]
+    Identifier(graph_path) [9-19]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#is_trail
+GRAPH FinGraph
+MATCH
+  p=(a1:Account)-[t1:Transfers]->(a2:Account)-[t2:Transfers]->
+    (a3:Account)-[t3:Transfers]->(a4:Account)
+WHERE a1.id < a4.id
+RETURN
+  IS_TRAIL(p) AS is_trail_path, t1.id as t1_id, t2.id as t2_id, t3.id as t3_id
+--
+QueryStatement [0-235]
+  Query [0-235]
+    GqlQuery [0-235]
+      GraphTableQuery [0-235]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-235]
+          GqlOperatorList [15-235]
+            GqlMatch [15-149]
+              GraphPattern [23-149]
+                GraphPathPattern [23-129]
+                  Identifier(p) [23-24]
+                  GraphNodePattern [25-37]
+                    GraphElementPatternFiller [26-36]
+                      Identifier(a1) [26-28]
+                      GraphLabelFilter [28-36]
+                        GraphElementLabel [29-36]
+                          Identifier(Account) [29-36]
+                  GraphEdgePattern [37-54]
+                    GraphElementPatternFiller [39-51]
+                      Identifier(t1) [39-41]
+                      GraphLabelFilter [41-51]
+                        GraphElementLabel [42-51]
+                          Identifier(Transfers) [42-51]
+                  GraphNodePattern [54-66]
+                    GraphElementPatternFiller [55-65]
+                      Identifier(a2) [55-57]
+                      GraphLabelFilter [57-65]
+                        GraphElementLabel [58-65]
+                          Identifier(Account) [58-65]
+                  GraphEdgePattern [66-83]
+                    GraphElementPatternFiller [68-80]
+                      Identifier(t2) [68-70]
+                      GraphLabelFilter [70-80]
+                        GraphElementLabel [71-80]
+                          Identifier(Transfers) [71-80]
+                  GraphNodePattern [88-100]
+                    GraphElementPatternFiller [89-99]
+                      Identifier(a3) [89-91]
+                      GraphLabelFilter [91-99]
+                        GraphElementLabel [92-99]
+                          Identifier(Account) [92-99]
+                  GraphEdgePattern [100-117]
+                    GraphElementPatternFiller [102-114]
+                      Identifier(t3) [102-104]
+                      GraphLabelFilter [104-114]
+                        GraphElementLabel [105-114]
+                          Identifier(Transfers) [105-114]
+                  GraphNodePattern [117-129]
+                    GraphElementPatternFiller [118-128]
+                      Identifier(a4) [118-120]
+                      GraphLabelFilter [120-128]
+                        GraphElementLabel [121-128]
+                          Identifier(Account) [121-128]
+                WhereClause [130-149]
+                  BinaryExpression(<) [136-149]
+                    PathExpression [136-141]
+                      Identifier(a1) [136-138]
+                      Identifier(id) [139-141]
+                    PathExpression [144-149]
+                      Identifier(a4) [144-146]
+                      Identifier(id) [147-149]
+            GqlReturn [150-235]
+              Select [159-235]
+                SelectList [159-235]
+                  SelectColumn [159-187]
+                    FunctionCall [159-170]
+                      PathExpression [159-167]
+                        Identifier(IS_TRAIL) [159-167]
+                      PathExpression [168-169]
+                        Identifier(p) [168-169]
+                    Alias [171-187]
+                      Identifier(is_trail_path) [174-187]
+                  SelectColumn [189-203]
+                    PathExpression [189-194]
+                      Identifier(t1) [189-191]
+                      Identifier(id) [192-194]
+                    Alias [195-203]
+                      Identifier(t1_id) [198-203]
+                  SelectColumn [205-219]
+                    PathExpression [205-210]
+                      Identifier(t2) [205-207]
+                      Identifier(id) [208-210]
+                    Alias [211-219]
+                      Identifier(t2_id) [214-219]
+                  SelectColumn [221-235]
+                    PathExpression [221-226]
+                      Identifier(t3) [221-223]
+                      Identifier(id) [224-226]
+                    Alias [227-235]
+                      Identifier(t3_id) [230-235]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#labels
+[mode=expression]
+LABELS(element)
+--
+FunctionCall [0-15]
+  PathExpression [0-6]
+    Identifier(LABELS) [0-6]
+  PathExpression [7-14]
+    Identifier(element) [7-14]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#nodes
+[mode=expression]
+NODES(graph_path)
+--
+FunctionCall [0-17]
+  PathExpression [0-5]
+    Identifier(NODES) [0-5]
+  PathExpression [6-16]
+    Identifier(graph_path) [6-16]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#nodes
+GRAPH FinGraph
+MATCH p=(src:Account)-[t1:Transfers]->(mid:Account)-[t2:Transfers]->(dst:Account)
+LET ns = NODES(p)
+RETURN
+  JSON_QUERY(TO_JSON(ns)[0], '$.labels') AS labels,
+  JSON_QUERY(TO_JSON(ns)[0], '$.properties.nick_name') AS nick_name
+--
+QueryStatement [0-241]
+  Query [0-241]
+    GqlQuery [0-241]
+      GraphTableQuery [0-241]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-241]
+          GqlOperatorList [15-241]
+            GqlMatch [15-96]
+              GraphPattern [21-96]
+                GraphPathPattern [21-96]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [23-36]
+                    GraphElementPatternFiller [24-35]
+                      Identifier(src) [24-27]
+                      GraphLabelFilter [27-35]
+                        GraphElementLabel [28-35]
+                          Identifier(Account) [28-35]
+                  GraphEdgePattern [36-53]
+                    GraphElementPatternFiller [38-50]
+                      Identifier(t1) [38-40]
+                      GraphLabelFilter [40-50]
+                        GraphElementLabel [41-50]
+                          Identifier(Transfers) [41-50]
+                  GraphNodePattern [53-66]
+                    GraphElementPatternFiller [54-65]
+                      Identifier(mid) [54-57]
+                      GraphLabelFilter [57-65]
+                        GraphElementLabel [58-65]
+                          Identifier(Account) [58-65]
+                  GraphEdgePattern [66-83]
+                    GraphElementPatternFiller [68-80]
+                      Identifier(t2) [68-70]
+                      GraphLabelFilter [70-80]
+                        GraphElementLabel [71-80]
+                          Identifier(Transfers) [71-80]
+                  GraphNodePattern [83-96]
+                    GraphElementPatternFiller [84-95]
+                      Identifier(dst) [84-87]
+                      GraphLabelFilter [87-95]
+                        GraphElementLabel [88-95]
+                          Identifier(Account) [88-95]
+            GqlLet [97-114]
+              GqlLetVariableDefinitionList [101-114]
+                GqlLetVariableDefinition [101-114]
+                  Identifier(ns) [101-103]
+                  FunctionCall [106-114]
+                    PathExpression [106-111]
+                      Identifier(NODES) [106-111]
+                    PathExpression [112-113]
+                      Identifier(p) [112-113]
+            GqlReturn [115-241]
+              Select [124-241]
+                SelectList [124-241]
+                  SelectColumn [124-172]
+                    FunctionCall [124-162]
+                      PathExpression [124-134]
+                        Identifier(JSON_QUERY) [124-134]
+                      ArrayElement [135-149]
+                        FunctionCall [135-146]
+                          PathExpression [135-142]
+                            Identifier(TO_JSON) [135-142]
+                          PathExpression [143-145]
+                            Identifier(ns) [143-145]
+                        Location [146-147]
+                        IntLiteral(0) [147-148]
+                      StringLiteral [151-161]
+                        StringLiteralComponent('$.labels') [151-161]
+                    Alias [163-172]
+                      Identifier(labels) [166-172]
+                  SelectColumn [176-241]
+                    FunctionCall [176-228]
+                      PathExpression [176-186]
+                        Identifier(JSON_QUERY) [176-186]
+                      ArrayElement [187-201]
+                        FunctionCall [187-198]
+                          PathExpression [187-194]
+                            Identifier(TO_JSON) [187-194]
+                          PathExpression [195-197]
+                            Identifier(ns) [195-197]
+                        Location [198-199]
+                        IntLiteral(0) [199-200]
+                      StringLiteral [203-227]
+                        StringLiteralComponent('$.properties.nick_name') [203-227]
+                    Alias [229-241]
+                      Identifier(nick_name) [232-241]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#path
+GRAPH FinGraph
+MATCH (src:Account)-[t1:Transfers]->(mid:Account)-[t2:Transfers]->(dst:Account)
+LET p = PATH(src, t1, mid, t2, dst)
+RETURN
+  JSON_QUERY(TO_JSON(p)[0], '$.labels') AS element_a,
+  JSON_QUERY(TO_JSON(p)[1], '$.labels') AS element_b,
+  JSON_QUERY(TO_JSON(p)[2], '$.labels') AS element_c
+--
+QueryStatement [0-298]
+  Query [0-298]
+    GqlQuery [0-298]
+      GraphTableQuery [0-298]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-298]
+          GqlOperatorList [15-298]
+            GqlMatch [15-94]
+              GraphPattern [21-94]
+                GraphPathPattern [21-94]
+                  GraphNodePattern [21-34]
+                    GraphElementPatternFiller [22-33]
+                      Identifier(src) [22-25]
+                      GraphLabelFilter [25-33]
+                        GraphElementLabel [26-33]
+                          Identifier(Account) [26-33]
+                  GraphEdgePattern [34-51]
+                    GraphElementPatternFiller [36-48]
+                      Identifier(t1) [36-38]
+                      GraphLabelFilter [38-48]
+                        GraphElementLabel [39-48]
+                          Identifier(Transfers) [39-48]
+                  GraphNodePattern [51-64]
+                    GraphElementPatternFiller [52-63]
+                      Identifier(mid) [52-55]
+                      GraphLabelFilter [55-63]
+                        GraphElementLabel [56-63]
+                          Identifier(Account) [56-63]
+                  GraphEdgePattern [64-81]
+                    GraphElementPatternFiller [66-78]
+                      Identifier(t2) [66-68]
+                      GraphLabelFilter [68-78]
+                        GraphElementLabel [69-78]
+                          Identifier(Transfers) [69-78]
+                  GraphNodePattern [81-94]
+                    GraphElementPatternFiller [82-93]
+                      Identifier(dst) [82-85]
+                      GraphLabelFilter [85-93]
+                        GraphElementLabel [86-93]
+                          Identifier(Account) [86-93]
+            GqlLet [95-130]
+              GqlLetVariableDefinitionList [99-130]
+                GqlLetVariableDefinition [99-130]
+                  Identifier(p) [99-100]
+                  FunctionCall [103-130]
+                    PathExpression [103-107]
+                      Identifier(PATH) [103-107]
+                    PathExpression [108-111]
+                      Identifier(src) [108-111]
+                    PathExpression [113-115]
+                      Identifier(t1) [113-115]
+                    PathExpression [117-120]
+                      Identifier(mid) [117-120]
+                    PathExpression [122-124]
+                      Identifier(t2) [122-124]
+                    PathExpression [126-129]
+                      Identifier(dst) [126-129]
+            GqlReturn [131-298]
+              Select [140-298]
+                SelectList [140-298]
+                  SelectColumn [140-190]
+                    FunctionCall [140-177]
+                      PathExpression [140-150]
+                        Identifier(JSON_QUERY) [140-150]
+                      ArrayElement [151-164]
+                        FunctionCall [151-161]
+                          PathExpression [151-158]
+                            Identifier(TO_JSON) [151-158]
+                          PathExpression [159-160]
+                            Identifier(p) [159-160]
+                        Location [161-162]
+                        IntLiteral(0) [162-163]
+                      StringLiteral [166-176]
+                        StringLiteralComponent('$.labels') [166-176]
+                    Alias [178-190]
+                      Identifier(element_a) [181-190]
+                  SelectColumn [194-244]
+                    FunctionCall [194-231]
+                      PathExpression [194-204]
+                        Identifier(JSON_QUERY) [194-204]
+                      ArrayElement [205-218]
+                        FunctionCall [205-215]
+                          PathExpression [205-212]
+                            Identifier(TO_JSON) [205-212]
+                          PathExpression [213-214]
+                            Identifier(p) [213-214]
+                        Location [215-216]
+                        IntLiteral(1) [216-217]
+                      StringLiteral [220-230]
+                        StringLiteralComponent('$.labels') [220-230]
+                    Alias [232-244]
+                      Identifier(element_b) [235-244]
+                  SelectColumn [248-298]
+                    FunctionCall [248-285]
+                      PathExpression [248-258]
+                        Identifier(JSON_QUERY) [248-258]
+                      ArrayElement [259-272]
+                        FunctionCall [259-269]
+                          PathExpression [259-266]
+                            Identifier(TO_JSON) [259-266]
+                          PathExpression [267-268]
+                            Identifier(p) [267-268]
+                        Location [269-270]
+                        IntLiteral(2) [270-271]
+                      StringLiteral [274-284]
+                        StringLiteralComponent('$.labels') [274-284]
+                    Alias [286-298]
+                      Identifier(element_c) [289-298]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#path
+-- Error: in 'p', a graph element is NULL.
+GRAPH FinGraph
+MATCH (src:Account)-[t1:Transfers]->(mid:Account)-[t2:Transfers]->(dst:Account)
+LET p = PATH(src, NULL, mid, t2, dst)
+RETURN TO_JSON(p) AS results
+--
+QueryStatement [43-204]
+  Query [43-204]
+    GqlQuery [43-204]
+      GraphTableQuery [43-204]
+        PathExpression [49-57]
+          Identifier(FinGraph) [49-57]
+        GqlOperatorList [58-204]
+          GqlOperatorList [58-204]
+            GqlMatch [58-137]
+              GraphPattern [64-137]
+                GraphPathPattern [64-137]
+                  GraphNodePattern [64-77]
+                    GraphElementPatternFiller [65-76]
+                      Identifier(src) [65-68]
+                      GraphLabelFilter [68-76]
+                        GraphElementLabel [69-76]
+                          Identifier(Account) [69-76]
+                  GraphEdgePattern [77-94]
+                    GraphElementPatternFiller [79-91]
+                      Identifier(t1) [79-81]
+                      GraphLabelFilter [81-91]
+                        GraphElementLabel [82-91]
+                          Identifier(Transfers) [82-91]
+                  GraphNodePattern [94-107]
+                    GraphElementPatternFiller [95-106]
+                      Identifier(mid) [95-98]
+                      GraphLabelFilter [98-106]
+                        GraphElementLabel [99-106]
+                          Identifier(Account) [99-106]
+                  GraphEdgePattern [107-124]
+                    GraphElementPatternFiller [109-121]
+                      Identifier(t2) [109-111]
+                      GraphLabelFilter [111-121]
+                        GraphElementLabel [112-121]
+                          Identifier(Transfers) [112-121]
+                  GraphNodePattern [124-137]
+                    GraphElementPatternFiller [125-136]
+                      Identifier(dst) [125-128]
+                      GraphLabelFilter [128-136]
+                        GraphElementLabel [129-136]
+                          Identifier(Account) [129-136]
+            GqlLet [138-175]
+              GqlLetVariableDefinitionList [142-175]
+                GqlLetVariableDefinition [142-175]
+                  Identifier(p) [142-143]
+                  FunctionCall [146-175]
+                    PathExpression [146-150]
+                      Identifier(PATH) [146-150]
+                    PathExpression [151-154]
+                      Identifier(src) [151-154]
+                    NullLiteral(NULL) [156-160]
+                    PathExpression [162-165]
+                      Identifier(mid) [162-165]
+                    PathExpression [167-169]
+                      Identifier(t2) [167-169]
+                    PathExpression [171-174]
+                      Identifier(dst) [171-174]
+            GqlReturn [176-204]
+              Select [183-204]
+                SelectList [183-204]
+                  SelectColumn [183-204]
+                    FunctionCall [183-193]
+                      PathExpression [183-190]
+                        Identifier(TO_JSON) [183-190]
+                      PathExpression [191-192]
+                        Identifier(p) [191-192]
+                    Alias [194-204]
+                      Identifier(results) [197-204]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#path
+-- Error: in 'p', 'src' and 'mid' are nodes that should be interleaved with an
+-- edge.
+GRAPH FinGraph
+MATCH (src:Account)-[t1:Transfers]->(mid:Account)-[t2:Transfers]->(dst:Account)
+LET p = PATH(src, mid, t2, dst)
+RETURN TO_JSON(p) AS results
+--
+QueryStatement [88-243]
+  Query [88-243]
+    GqlQuery [88-243]
+      GraphTableQuery [88-243]
+        PathExpression [94-102]
+          Identifier(FinGraph) [94-102]
+        GqlOperatorList [103-243]
+          GqlOperatorList [103-243]
+            GqlMatch [103-182]
+              GraphPattern [109-182]
+                GraphPathPattern [109-182]
+                  GraphNodePattern [109-122]
+                    GraphElementPatternFiller [110-121]
+                      Identifier(src) [110-113]
+                      GraphLabelFilter [113-121]
+                        GraphElementLabel [114-121]
+                          Identifier(Account) [114-121]
+                  GraphEdgePattern [122-139]
+                    GraphElementPatternFiller [124-136]
+                      Identifier(t1) [124-126]
+                      GraphLabelFilter [126-136]
+                        GraphElementLabel [127-136]
+                          Identifier(Transfers) [127-136]
+                  GraphNodePattern [139-152]
+                    GraphElementPatternFiller [140-151]
+                      Identifier(mid) [140-143]
+                      GraphLabelFilter [143-151]
+                        GraphElementLabel [144-151]
+                          Identifier(Account) [144-151]
+                  GraphEdgePattern [152-169]
+                    GraphElementPatternFiller [154-166]
+                      Identifier(t2) [154-156]
+                      GraphLabelFilter [156-166]
+                        GraphElementLabel [157-166]
+                          Identifier(Transfers) [157-166]
+                  GraphNodePattern [169-182]
+                    GraphElementPatternFiller [170-181]
+                      Identifier(dst) [170-173]
+                      GraphLabelFilter [173-181]
+                        GraphElementLabel [174-181]
+                          Identifier(Account) [174-181]
+            GqlLet [183-214]
+              GqlLetVariableDefinitionList [187-214]
+                GqlLetVariableDefinition [187-214]
+                  Identifier(p) [187-188]
+                  FunctionCall [191-214]
+                    PathExpression [191-195]
+                      Identifier(PATH) [191-195]
+                    PathExpression [196-199]
+                      Identifier(src) [196-199]
+                    PathExpression [201-204]
+                      Identifier(mid) [201-204]
+                    PathExpression [206-208]
+                      Identifier(t2) [206-208]
+                    PathExpression [210-213]
+                      Identifier(dst) [210-213]
+            GqlReturn [215-243]
+              Select [222-243]
+                SelectList [222-243]
+                  SelectColumn [222-243]
+                    FunctionCall [222-232]
+                      PathExpression [222-229]
+                        Identifier(TO_JSON) [222-229]
+                      PathExpression [230-231]
+                        Identifier(p) [230-231]
+                    Alias [233-243]
+                      Identifier(results) [236-243]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#path
+-- Error: in 'p', 't2' is an edge that doesn't connect to a neighboring node on
+-- the right.
+GRAPH FinGraph
+MATCH (src:Account)-[t1:Transfers]->(mid:Account)-[t2:Transfers]->(dst:Account)
+LET p = PATH(src, t2, mid)
+RETURN TO_JSON(p) AS results
+--
+QueryStatement [94-244]
+  Query [94-244]
+    GqlQuery [94-244]
+      GraphTableQuery [94-244]
+        PathExpression [100-108]
+          Identifier(FinGraph) [100-108]
+        GqlOperatorList [109-244]
+          GqlOperatorList [109-244]
+            GqlMatch [109-188]
+              GraphPattern [115-188]
+                GraphPathPattern [115-188]
+                  GraphNodePattern [115-128]
+                    GraphElementPatternFiller [116-127]
+                      Identifier(src) [116-119]
+                      GraphLabelFilter [119-127]
+                        GraphElementLabel [120-127]
+                          Identifier(Account) [120-127]
+                  GraphEdgePattern [128-145]
+                    GraphElementPatternFiller [130-142]
+                      Identifier(t1) [130-132]
+                      GraphLabelFilter [132-142]
+                        GraphElementLabel [133-142]
+                          Identifier(Transfers) [133-142]
+                  GraphNodePattern [145-158]
+                    GraphElementPatternFiller [146-157]
+                      Identifier(mid) [146-149]
+                      GraphLabelFilter [149-157]
+                        GraphElementLabel [150-157]
+                          Identifier(Account) [150-157]
+                  GraphEdgePattern [158-175]
+                    GraphElementPatternFiller [160-172]
+                      Identifier(t2) [160-162]
+                      GraphLabelFilter [162-172]
+                        GraphElementLabel [163-172]
+                          Identifier(Transfers) [163-172]
+                  GraphNodePattern [175-188]
+                    GraphElementPatternFiller [176-187]
+                      Identifier(dst) [176-179]
+                      GraphLabelFilter [179-187]
+                        GraphElementLabel [180-187]
+                          Identifier(Account) [180-187]
+            GqlLet [189-215]
+              GqlLetVariableDefinitionList [193-215]
+                GqlLetVariableDefinition [193-215]
+                  Identifier(p) [193-194]
+                  FunctionCall [197-215]
+                    PathExpression [197-201]
+                      Identifier(PATH) [197-201]
+                    PathExpression [202-205]
+                      Identifier(src) [202-205]
+                    PathExpression [207-209]
+                      Identifier(t2) [207-209]
+                    PathExpression [211-214]
+                      Identifier(mid) [211-214]
+            GqlReturn [216-244]
+              Select [223-244]
+                SelectList [223-244]
+                  SelectColumn [223-244]
+                    FunctionCall [223-233]
+                      PathExpression [223-230]
+                        Identifier(TO_JSON) [223-230]
+                      PathExpression [231-232]
+                        Identifier(p) [231-232]
+                    Alias [234-244]
+                      Identifier(results) [237-244]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#path_first
+[mode=expression]
+PATH_FIRST(graph_path)
+--
+FunctionCall [0-22]
+  PathExpression [0-10]
+    Identifier(PATH_FIRST) [0-10]
+  PathExpression [11-21]
+    Identifier(graph_path) [11-21]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#path_first
+GRAPH FinGraph
+MATCH p=(src:Account)-[t1:Transfers]->(mid:Account)-[t2:Transfers]->(dst:Account)
+LET f = PATH_FIRST(p)
+RETURN
+  LABELS(f) AS labels,
+  f.nick_name AS nick_name
+--
+QueryStatement [0-175]
+  Query [0-175]
+    GqlQuery [0-175]
+      GraphTableQuery [0-175]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-175]
+          GqlOperatorList [15-175]
+            GqlMatch [15-96]
+              GraphPattern [21-96]
+                GraphPathPattern [21-96]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [23-36]
+                    GraphElementPatternFiller [24-35]
+                      Identifier(src) [24-27]
+                      GraphLabelFilter [27-35]
+                        GraphElementLabel [28-35]
+                          Identifier(Account) [28-35]
+                  GraphEdgePattern [36-53]
+                    GraphElementPatternFiller [38-50]
+                      Identifier(t1) [38-40]
+                      GraphLabelFilter [40-50]
+                        GraphElementLabel [41-50]
+                          Identifier(Transfers) [41-50]
+                  GraphNodePattern [53-66]
+                    GraphElementPatternFiller [54-65]
+                      Identifier(mid) [54-57]
+                      GraphLabelFilter [57-65]
+                        GraphElementLabel [58-65]
+                          Identifier(Account) [58-65]
+                  GraphEdgePattern [66-83]
+                    GraphElementPatternFiller [68-80]
+                      Identifier(t2) [68-70]
+                      GraphLabelFilter [70-80]
+                        GraphElementLabel [71-80]
+                          Identifier(Transfers) [71-80]
+                  GraphNodePattern [83-96]
+                    GraphElementPatternFiller [84-95]
+                      Identifier(dst) [84-87]
+                      GraphLabelFilter [87-95]
+                        GraphElementLabel [88-95]
+                          Identifier(Account) [88-95]
+            GqlLet [97-118]
+              GqlLetVariableDefinitionList [101-118]
+                GqlLetVariableDefinition [101-118]
+                  Identifier(f) [101-102]
+                  FunctionCall [105-118]
+                    PathExpression [105-115]
+                      Identifier(PATH_FIRST) [105-115]
+                    PathExpression [116-117]
+                      Identifier(p) [116-117]
+            GqlReturn [119-175]
+              Select [128-175]
+                SelectList [128-175]
+                  SelectColumn [128-147]
+                    FunctionCall [128-137]
+                      PathExpression [128-134]
+                        Identifier(LABELS) [128-134]
+                      PathExpression [135-136]
+                        Identifier(f) [135-136]
+                    Alias [138-147]
+                      Identifier(labels) [141-147]
+                  SelectColumn [151-175]
+                    PathExpression [151-162]
+                      Identifier(f) [151-152]
+                      Identifier(nick_name) [153-162]
+                    Alias [163-175]
+                      Identifier(nick_name) [166-175]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#path_last
+[mode=expression]
+PATH_LAST(graph_path)
+--
+FunctionCall [0-21]
+  PathExpression [0-9]
+    Identifier(PATH_LAST) [0-9]
+  PathExpression [10-20]
+    Identifier(graph_path) [10-20]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#path_last
+GRAPH FinGraph
+MATCH p=(src:Account)-[t1:Transfers]->(mid:Account)-[t2:Transfers]->(dst:Account)
+LET f = PATH_LAST(p)
+RETURN
+  LABELS(f) AS labels,
+  f.nick_name AS nick_name
+--
+QueryStatement [0-174]
+  Query [0-174]
+    GqlQuery [0-174]
+      GraphTableQuery [0-174]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-174]
+          GqlOperatorList [15-174]
+            GqlMatch [15-96]
+              GraphPattern [21-96]
+                GraphPathPattern [21-96]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [23-36]
+                    GraphElementPatternFiller [24-35]
+                      Identifier(src) [24-27]
+                      GraphLabelFilter [27-35]
+                        GraphElementLabel [28-35]
+                          Identifier(Account) [28-35]
+                  GraphEdgePattern [36-53]
+                    GraphElementPatternFiller [38-50]
+                      Identifier(t1) [38-40]
+                      GraphLabelFilter [40-50]
+                        GraphElementLabel [41-50]
+                          Identifier(Transfers) [41-50]
+                  GraphNodePattern [53-66]
+                    GraphElementPatternFiller [54-65]
+                      Identifier(mid) [54-57]
+                      GraphLabelFilter [57-65]
+                        GraphElementLabel [58-65]
+                          Identifier(Account) [58-65]
+                  GraphEdgePattern [66-83]
+                    GraphElementPatternFiller [68-80]
+                      Identifier(t2) [68-70]
+                      GraphLabelFilter [70-80]
+                        GraphElementLabel [71-80]
+                          Identifier(Transfers) [71-80]
+                  GraphNodePattern [83-96]
+                    GraphElementPatternFiller [84-95]
+                      Identifier(dst) [84-87]
+                      GraphLabelFilter [87-95]
+                        GraphElementLabel [88-95]
+                          Identifier(Account) [88-95]
+            GqlLet [97-117]
+              GqlLetVariableDefinitionList [101-117]
+                GqlLetVariableDefinition [101-117]
+                  Identifier(f) [101-102]
+                  FunctionCall [105-117]
+                    PathExpression [105-114]
+                      Identifier(PATH_LAST) [105-114]
+                    PathExpression [115-116]
+                      Identifier(p) [115-116]
+            GqlReturn [118-174]
+              Select [127-174]
+                SelectList [127-174]
+                  SelectColumn [127-146]
+                    FunctionCall [127-136]
+                      PathExpression [127-133]
+                        Identifier(LABELS) [127-133]
+                      PathExpression [134-135]
+                        Identifier(f) [134-135]
+                    Alias [137-146]
+                      Identifier(labels) [140-146]
+                  SelectColumn [150-174]
+                    PathExpression [150-161]
+                      Identifier(f) [150-151]
+                      Identifier(nick_name) [152-161]
+                    Alias [162-174]
+                      Identifier(nick_name) [165-174]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#path_length
+[mode=expression]
+PATH_LENGTH(graph_path)
+--
+FunctionCall [0-23]
+  PathExpression [0-11]
+    Identifier(PATH_LENGTH) [0-11]
+  PathExpression [12-22]
+    Identifier(graph_path) [12-22]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#path_length
+GRAPH FinGraph
+MATCH p=(src:Account)-[t1:Transfers]->(mid:Account)-[t2:Transfers]->(dst:Account)
+RETURN PATH_LENGTH(p) AS results
+--
+QueryStatement [0-129]
+  Query [0-129]
+    GqlQuery [0-129]
+      GraphTableQuery [0-129]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-129]
+          GqlOperatorList [15-129]
+            GqlMatch [15-96]
+              GraphPattern [21-96]
+                GraphPathPattern [21-96]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [23-36]
+                    GraphElementPatternFiller [24-35]
+                      Identifier(src) [24-27]
+                      GraphLabelFilter [27-35]
+                        GraphElementLabel [28-35]
+                          Identifier(Account) [28-35]
+                  GraphEdgePattern [36-53]
+                    GraphElementPatternFiller [38-50]
+                      Identifier(t1) [38-40]
+                      GraphLabelFilter [40-50]
+                        GraphElementLabel [41-50]
+                          Identifier(Transfers) [41-50]
+                  GraphNodePattern [53-66]
+                    GraphElementPatternFiller [54-65]
+                      Identifier(mid) [54-57]
+                      GraphLabelFilter [57-65]
+                        GraphElementLabel [58-65]
+                          Identifier(Account) [58-65]
+                  GraphEdgePattern [66-83]
+                    GraphElementPatternFiller [68-80]
+                      Identifier(t2) [68-70]
+                      GraphLabelFilter [70-80]
+                        GraphElementLabel [71-80]
+                          Identifier(Transfers) [71-80]
+                  GraphNodePattern [83-96]
+                    GraphElementPatternFiller [84-95]
+                      Identifier(dst) [84-87]
+                      GraphLabelFilter [87-95]
+                        GraphElementLabel [88-95]
+                          Identifier(Account) [88-95]
+            GqlReturn [97-129]
+              Select [104-129]
+                SelectList [104-129]
+                  SelectColumn [104-129]
+                    FunctionCall [104-118]
+                      PathExpression [104-115]
+                        Identifier(PATH_LENGTH) [104-115]
+                      PathExpression [116-117]
+                        Identifier(p) [116-117]
+                    Alias [119-129]
+                      Identifier(results) [122-129]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#property_names
+[mode=expression]
+PROPERTY_NAMES(element)
+--
+FunctionCall [0-23]
+  PathExpression [0-14]
+    Identifier(PROPERTY_NAMES) [0-14]
+  PathExpression [15-22]
+    Identifier(element) [15-22]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#property_names
+GRAPH FinGraph
+MATCH (n:Person|Account)
+RETURN PROPERTY_NAMES(n) AS property_names, n.id
+--
+QueryStatement [0-88]
+  Query [0-88]
+    GqlQuery [0-88]
+      GraphTableQuery [0-88]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-88]
+          GqlOperatorList [15-88]
+            GqlMatch [15-39]
+              GraphPattern [21-39]
+                GraphPathPattern [21-39]
+                  GraphNodePattern [21-39]
+                    GraphElementPatternFiller [22-38]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-38]
+                        GraphLabelOperation(OR) [24-38]
+                          GraphElementLabel [24-30]
+                            Identifier(Person) [24-30]
+                          GraphElementLabel [31-38]
+                            Identifier(Account) [31-38]
+            GqlReturn [40-88]
+              Select [47-88]
+                SelectList [47-88]
+                  SelectColumn [47-82]
+                    FunctionCall [47-64]
+                      PathExpression [47-61]
+                        Identifier(PROPERTY_NAMES) [47-61]
+                      PathExpression [62-63]
+                        Identifier(n) [62-63]
+                    Alias [65-82]
+                      Identifier(property_names) [68-82]
+                  SelectColumn [84-88]
+                    PathExpression [84-88]
+                      Identifier(n) [84-85]
+                      Identifier(id) [86-88]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#source_node_id
+[mode=expression]
+SOURCE_NODE_ID(edge_element)
+--
+FunctionCall [0-28]
+  PathExpression [0-14]
+    Identifier(SOURCE_NODE_ID) [0-14]
+  PathExpression [15-27]
+    Identifier(edge_element) [15-27]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#source_node_id
+GRAPH FinGraph
+MATCH (p:Person)-[o:Owns]->(:Account)
+RETURN p.name AS name, SOURCE_NODE_ID(o) AS source_node_id
+--
+QueryStatement [0-111]
+  Query [0-111]
+    GqlQuery [0-111]
+      GraphTableQuery [0-111]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-111]
+          GqlOperatorList [15-111]
+            GqlMatch [15-52]
+              GraphPattern [21-52]
+                GraphPathPattern [21-52]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                  GraphEdgePattern [31-42]
+                    GraphElementPatternFiller [33-39]
+                      Identifier(o) [33-34]
+                      GraphLabelFilter [34-39]
+                        GraphElementLabel [35-39]
+                          Identifier(Owns) [35-39]
+                  GraphNodePattern [42-52]
+                    GraphElementPatternFiller [43-51]
+                      GraphLabelFilter [43-51]
+                        GraphElementLabel [44-51]
+                          Identifier(Account) [44-51]
+            GqlReturn [53-111]
+              Select [60-111]
+                SelectList [60-111]
+                  SelectColumn [60-74]
+                    PathExpression [60-66]
+                      Identifier(p) [60-61]
+                      Identifier(name) [62-66]
+                    Alias [67-74]
+                      Identifier(name) [70-74]
+                  SelectColumn [76-111]
+                    FunctionCall [76-93]
+                      PathExpression [76-90]
+                        Identifier(SOURCE_NODE_ID) [76-90]
+                      PathExpression [91-92]
+                        Identifier(o) [91-92]
+                    Alias [94-111]
+                      Identifier(source_node_id) [97-111]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#examples
+GRAPH FinGraph
+LET arr = [1, 2, 3]
+LET total = SUM(arr)
+RETURN total
+--
+QueryStatement [0-68]
+  Query [0-68]
+    GqlQuery [0-68]
+      GraphTableQuery [0-68]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-68]
+          GqlOperatorList [15-68]
+            GqlLet [15-34]
+              GqlLetVariableDefinitionList [19-34]
+                GqlLetVariableDefinition [19-34]
+                  Identifier(arr) [19-22]
+                  ArrayConstructor [25-34]
+                    IntLiteral(1) [26-27]
+                    IntLiteral(2) [29-30]
+                    IntLiteral(3) [32-33]
+            GqlLet [35-55]
+              GqlLetVariableDefinitionList [39-55]
+                GqlLetVariableDefinition [39-55]
+                  Identifier(total) [39-44]
+                  FunctionCall [47-55]
+                    PathExpression [47-50]
+                      Identifier(SUM) [47-50]
+                    PathExpression [51-54]
+                      Identifier(arr) [51-54]
+            GqlReturn [56-68]
+              Select [63-68]
+                SelectList [63-68]
+                  SelectColumn [63-68]
+                    PathExpression [63-68]
+                      Identifier(total) [63-68]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#examples
+GRAPH FinGraph
+LET arr = [STRUCT(1 as x, 10 as y), STRUCT(2, 9), STRUCT(3, 8)]
+LET total = SUM(arr.x)
+RETURN total
+--
+QueryStatement [0-114]
+  Query [0-114]
+    GqlQuery [0-114]
+      GraphTableQuery [0-114]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-114]
+          GqlOperatorList [15-114]
+            GqlLet [15-78]
+              GqlLetVariableDefinitionList [19-78]
+                GqlLetVariableDefinition [19-78]
+                  Identifier(arr) [19-22]
+                  ArrayConstructor [25-78]
+                    StructConstructorWithKeyword [26-49]
+                      StructConstructorArg [33-39]
+                        IntLiteral(1) [33-34]
+                        Alias [35-39]
+                          Identifier(x) [38-39]
+                      StructConstructorArg [41-48]
+                        IntLiteral(10) [41-43]
+                        Alias [44-48]
+                          Identifier(y) [47-48]
+                    StructConstructorWithKeyword [51-63]
+                      StructConstructorArg [58-59]
+                        IntLiteral(2) [58-59]
+                      StructConstructorArg [61-62]
+                        IntLiteral(9) [61-62]
+                    StructConstructorWithKeyword [65-77]
+                      StructConstructorArg [72-73]
+                        IntLiteral(3) [72-73]
+                      StructConstructorArg [75-76]
+                        IntLiteral(8) [75-76]
+            GqlLet [79-101]
+              GqlLetVariableDefinitionList [83-101]
+                GqlLetVariableDefinition [83-101]
+                  Identifier(total) [83-88]
+                  FunctionCall [91-101]
+                    PathExpression [91-94]
+                      Identifier(SUM) [91-94]
+                    PathExpression [95-100]
+                      Identifier(arr) [95-98]
+                      Identifier(x) [99-100]
+            GqlReturn [102-114]
+              Select [109-114]
+                SelectList [109-114]
+                  SelectColumn [109-114]
+                    PathExpression [109-114]
+                      Identifier(total) [109-114]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#examples
+GRAPH FinGraph
+LET arr = [STRUCT(1 as x, 10 as y), STRUCT(2, 9), STRUCT(3, 8)]
+LET avg_sum = AVG(arr.x + arr.y)
+RETURN avg_sum
+--
+QueryStatement [0-126]
+  Query [0-126]
+    GqlQuery [0-126]
+      GraphTableQuery [0-126]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-126]
+          GqlOperatorList [15-126]
+            GqlLet [15-78]
+              GqlLetVariableDefinitionList [19-78]
+                GqlLetVariableDefinition [19-78]
+                  Identifier(arr) [19-22]
+                  ArrayConstructor [25-78]
+                    StructConstructorWithKeyword [26-49]
+                      StructConstructorArg [33-39]
+                        IntLiteral(1) [33-34]
+                        Alias [35-39]
+                          Identifier(x) [38-39]
+                      StructConstructorArg [41-48]
+                        IntLiteral(10) [41-43]
+                        Alias [44-48]
+                          Identifier(y) [47-48]
+                    StructConstructorWithKeyword [51-63]
+                      StructConstructorArg [58-59]
+                        IntLiteral(2) [58-59]
+                      StructConstructorArg [61-62]
+                        IntLiteral(9) [61-62]
+                    StructConstructorWithKeyword [65-77]
+                      StructConstructorArg [72-73]
+                        IntLiteral(3) [72-73]
+                      StructConstructorArg [75-76]
+                        IntLiteral(8) [75-76]
+            GqlLet [79-111]
+              GqlLetVariableDefinitionList [83-111]
+                GqlLetVariableDefinition [83-111]
+                  Identifier(avg_sum) [83-90]
+                  FunctionCall [93-111]
+                    PathExpression [93-96]
+                      Identifier(AVG) [93-96]
+                    BinaryExpression(+) [97-110]
+                      PathExpression [97-102]
+                        Identifier(arr) [97-100]
+                        Identifier(x) [101-102]
+                      PathExpression [105-110]
+                        Identifier(arr) [105-108]
+                        Identifier(y) [109-110]
+            GqlReturn [112-126]
+              Select [119-126]
+                SelectList [119-126]
+                  SelectColumn [119-126]
+                    PathExpression [119-126]
+                      Identifier(avg_sum) [119-126]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#examples
+GRAPH FinGraph
+LET arr = [STRUCT(1 as x, 9 as y), STRUCT(2, 9), STRUCT(4, 8)]
+LET result = ARRAY_AGG(arr.x + arr.y)
+RETURN result
+--
+QueryStatement [0-129]
+  Query [0-129]
+    GqlQuery [0-129]
+      GraphTableQuery [0-129]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-129]
+          GqlOperatorList [15-129]
+            GqlLet [15-77]
+              GqlLetVariableDefinitionList [19-77]
+                GqlLetVariableDefinition [19-77]
+                  Identifier(arr) [19-22]
+                  ArrayConstructor [25-77]
+                    StructConstructorWithKeyword [26-48]
+                      StructConstructorArg [33-39]
+                        IntLiteral(1) [33-34]
+                        Alias [35-39]
+                          Identifier(x) [38-39]
+                      StructConstructorArg [41-47]
+                        IntLiteral(9) [41-42]
+                        Alias [43-47]
+                          Identifier(y) [46-47]
+                    StructConstructorWithKeyword [50-62]
+                      StructConstructorArg [57-58]
+                        IntLiteral(2) [57-58]
+                      StructConstructorArg [60-61]
+                        IntLiteral(9) [60-61]
+                    StructConstructorWithKeyword [64-76]
+                      StructConstructorArg [71-72]
+                        IntLiteral(4) [71-72]
+                      StructConstructorArg [74-75]
+                        IntLiteral(8) [74-75]
+            GqlLet [78-115]
+              GqlLetVariableDefinitionList [82-115]
+                GqlLetVariableDefinition [82-115]
+                  Identifier(result) [82-88]
+                  FunctionCall [91-115]
+                    PathExpression [91-100]
+                      Identifier(ARRAY_AGG) [91-100]
+                    BinaryExpression(+) [101-114]
+                      PathExpression [101-106]
+                        Identifier(arr) [101-104]
+                        Identifier(x) [105-106]
+                      PathExpression [109-114]
+                        Identifier(arr) [109-112]
+                        Identifier(y) [113-114]
+            GqlReturn [116-129]
+              Select [123-129]
+                SelectList [123-129]
+                  SelectColumn [123-129]
+                    PathExpression [123-129]
+                      Identifier(result) [123-129]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#examples
+-- ERROR: Horizontal aggregation on more than one array-typed variable
+-- isn't allowed
+GRAPH FinGraph
+LET arr1 = [1, 2, 3]
+LET arr2 = [5, 4, 3]
+LET avg_val = AVG(arr1 + arr2)
+RETURN avg_val
+--
+QueryStatement [88-190]
+  Query [88-190]
+    GqlQuery [88-190]
+      GraphTableQuery [88-190]
+        PathExpression [94-102]
+          Identifier(FinGraph) [94-102]
+        GqlOperatorList [103-190]
+          GqlOperatorList [103-190]
+            GqlLet [103-123]
+              GqlLetVariableDefinitionList [107-123]
+                GqlLetVariableDefinition [107-123]
+                  Identifier(arr1) [107-111]
+                  ArrayConstructor [114-123]
+                    IntLiteral(1) [115-116]
+                    IntLiteral(2) [118-119]
+                    IntLiteral(3) [121-122]
+            GqlLet [124-144]
+              GqlLetVariableDefinitionList [128-144]
+                GqlLetVariableDefinition [128-144]
+                  Identifier(arr2) [128-132]
+                  ArrayConstructor [135-144]
+                    IntLiteral(5) [136-137]
+                    IntLiteral(4) [139-140]
+                    IntLiteral(3) [142-143]
+            GqlLet [145-175]
+              GqlLetVariableDefinitionList [149-175]
+                GqlLetVariableDefinition [149-175]
+                  Identifier(avg_val) [149-156]
+                  FunctionCall [159-175]
+                    PathExpression [159-162]
+                      Identifier(AVG) [159-162]
+                    BinaryExpression(+) [163-174]
+                      PathExpression [163-167]
+                        Identifier(arr1) [163-167]
+                      PathExpression [170-174]
+                        Identifier(arr2) [170-174]
+            GqlReturn [176-190]
+              Select [183-190]
+                SelectList [183-190]
+                  SelectColumn [183-190]
+                    PathExpression [183-190]
+                      Identifier(avg_val) [183-190]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#examples
+-- ERROR: No matching signature for function ARRAY_LENGTH for argument types: INT64
+GRAPH FinGraph
+LET arr1 = [1, 2, 3]
+LET bad_avg_val = SUM(arr1 / ARRAY_LENGTH(arr1))
+RETURN bad_avg_val
+--
+QueryStatement [84-187]
+  Query [84-187]
+    GqlQuery [84-187]
+      GraphTableQuery [84-187]
+        PathExpression [90-98]
+          Identifier(FinGraph) [90-98]
+        GqlOperatorList [99-187]
+          GqlOperatorList [99-187]
+            GqlLet [99-119]
+              GqlLetVariableDefinitionList [103-119]
+                GqlLetVariableDefinition [103-119]
+                  Identifier(arr1) [103-107]
+                  ArrayConstructor [110-119]
+                    IntLiteral(1) [111-112]
+                    IntLiteral(2) [114-115]
+                    IntLiteral(3) [117-118]
+            GqlLet [120-168]
+              GqlLetVariableDefinitionList [124-168]
+                GqlLetVariableDefinition [124-168]
+                  Identifier(bad_avg_val) [124-135]
+                  FunctionCall [138-168]
+                    PathExpression [138-141]
+                      Identifier(SUM) [138-141]
+                    BinaryExpression(/) [142-167]
+                      PathExpression [142-146]
+                        Identifier(arr1) [142-146]
+                      FunctionCall [149-167]
+                        PathExpression [149-161]
+                          Identifier(ARRAY_LENGTH) [149-161]
+                        PathExpression [162-166]
+                          Identifier(arr1) [162-166]
+            GqlReturn [169-187]
+              Select [176-187]
+                SelectList [176-187]
+                  SelectColumn [176-187]
+                    PathExpression [176-187]
+                      Identifier(bad_avg_val) [176-187]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#examples
+GRAPH FinGraph
+LET arr1 = [1, 2, 3]
+LET len = ARRAY_LENGTH(arr1)
+LET avg_val = SUM(arr1 / len)
+RETURN avg_val
+--
+QueryStatement [0-109]
+  Query [0-109]
+    GqlQuery [0-109]
+      GraphTableQuery [0-109]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-109]
+          GqlOperatorList [15-109]
+            GqlLet [15-35]
+              GqlLetVariableDefinitionList [19-35]
+                GqlLetVariableDefinition [19-35]
+                  Identifier(arr1) [19-23]
+                  ArrayConstructor [26-35]
+                    IntLiteral(1) [27-28]
+                    IntLiteral(2) [30-31]
+                    IntLiteral(3) [33-34]
+            GqlLet [36-64]
+              GqlLetVariableDefinitionList [40-64]
+                GqlLetVariableDefinition [40-64]
+                  Identifier(len) [40-43]
+                  FunctionCall [46-64]
+                    PathExpression [46-58]
+                      Identifier(ARRAY_LENGTH) [46-58]
+                    PathExpression [59-63]
+                      Identifier(arr1) [59-63]
+            GqlLet [65-94]
+              GqlLetVariableDefinitionList [69-94]
+                GqlLetVariableDefinition [69-94]
+                  Identifier(avg_val) [69-76]
+                  FunctionCall [79-94]
+                    PathExpression [79-82]
+                      Identifier(SUM) [79-82]
+                    BinaryExpression(/) [83-93]
+                      PathExpression [83-87]
+                        Identifier(arr1) [83-87]
+                      PathExpression [90-93]
+                        Identifier(len) [90-93]
+            GqlReturn [95-109]
+              Select [102-109]
+                SelectList [102-109]
+                  SelectColumn [102-109]
+                    PathExpression [102-109]
+                      Identifier(avg_val) [102-109]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#examples
+GRAPH FinGraph
+MATCH (src:Account)-[e:Transfers]->{1, 3}(dst:Account)
+WHERE src != dst
+LET num_transfers = COUNT(e)
+LET unique_amount_transfers = COUNT(DISTINCT e.amount)
+FILTER unique_amount_transfers != num_transfers
+RETURN src.id as src_id, num_transfers, unique_amount_transfers, dst.id AS destination_account_id
+--
+QueryStatement [0-316]
+  Query [0-316]
+    GqlQuery [0-316]
+      GraphTableQuery [0-316]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-316]
+          GqlOperatorList [15-316]
+            GqlMatch [15-86]
+              GraphPattern [21-86]
+                GraphPathPattern [21-69]
+                  GraphNodePattern [21-34]
+                    GraphElementPatternFiller [22-33]
+                      Identifier(src) [22-25]
+                      GraphLabelFilter [25-33]
+                        GraphElementLabel [26-33]
+                          Identifier(Account) [26-33]
+                  ParenthesizedGraphPathPattern [34-56]
+                    BoundedQuantifier [50-56]
+                      QuantifierBound [51-52]
+                        IntLiteral(1) [51-52]
+                      QuantifierBound [54-55]
+                        IntLiteral(3) [54-55]
+                    GraphEdgePattern [34-50]
+                      GraphElementPatternFiller [36-47]
+                        Identifier(e) [36-37]
+                        GraphLabelFilter [37-47]
+                          GraphElementLabel [38-47]
+                            Identifier(Transfers) [38-47]
+                  GraphNodePattern [56-69]
+                    GraphElementPatternFiller [57-68]
+                      Identifier(dst) [57-60]
+                      GraphLabelFilter [60-68]
+                        GraphElementLabel [61-68]
+                          Identifier(Account) [61-68]
+                WhereClause [70-86]
+                  BinaryExpression(!=) [76-86]
+                    PathExpression [76-79]
+                      Identifier(src) [76-79]
+                    PathExpression [83-86]
+                      Identifier(dst) [83-86]
+            GqlLet [87-115]
+              GqlLetVariableDefinitionList [91-115]
+                GqlLetVariableDefinition [91-115]
+                  Identifier(num_transfers) [91-104]
+                  FunctionCall [107-115]
+                    PathExpression [107-112]
+                      Identifier(COUNT) [107-112]
+                    PathExpression [113-114]
+                      Identifier(e) [113-114]
+            GqlLet [116-170]
+              GqlLetVariableDefinitionList [120-170]
+                GqlLetVariableDefinition [120-170]
+                  Identifier(unique_amount_transfers) [120-143]
+                  FunctionCall(distinct=true) [146-170]
+                    PathExpression [146-151]
+                      Identifier(COUNT) [146-151]
+                    PathExpression [161-169]
+                      Identifier(e) [161-162]
+                      Identifier(amount) [163-169]
+            GqlFilter [171-218]
+              WhereClause [171-218]
+                BinaryExpression(!=) [178-218]
+                  PathExpression [178-201]
+                    Identifier(unique_amount_transfers) [178-201]
+                  PathExpression [205-218]
+                    Identifier(num_transfers) [205-218]
+            GqlReturn [219-316]
+              Select [226-316]
+                SelectList [226-316]
+                  SelectColumn [226-242]
+                    PathExpression [226-232]
+                      Identifier(src) [226-229]
+                      Identifier(id) [230-232]
+                    Alias [233-242]
+                      Identifier(src_id) [236-242]
+                  SelectColumn [244-257]
+                    PathExpression [244-257]
+                      Identifier(num_transfers) [244-257]
+                  SelectColumn [259-282]
+                    PathExpression [259-282]
+                      Identifier(unique_amount_transfers) [259-282]
+                  SelectColumn [284-316]
+                    PathExpression [284-290]
+                      Identifier(dst) [284-287]
+                      Identifier(id) [288-290]
+                    Alias [291-316]
+                      Identifier(destination_account_id) [294-316]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-gql-functions#examples
+GRAPH FinGraph
+MATCH (src:Account {id: 7})-[e:Transfers]->{1,2}(dst:Account)
+LET total_amount = SUM(e.amount)
+RETURN
+  src.id AS source_account_id, dst.id AS destination_account_id,
+  ARRAY_AGG(total_amount) as total_amounts_per_path
+--
+QueryStatement [0-233]
+  Query [0-233]
+    GqlQuery [0-233]
+      GraphTableQuery [0-233]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-233]
+          GqlOperatorList [15-233]
+            GqlMatch [15-76]
+              GraphPattern [21-76]
+                GraphPathPattern [21-76]
+                  GraphNodePattern [21-42]
+                    GraphElementPatternFiller [22-41]
+                      Identifier(src) [22-25]
+                      GraphLabelFilter [25-33]
+                        GraphElementLabel [26-33]
+                          Identifier(Account) [26-33]
+                      GraphPropertySpecification [34-41]
+                        GraphPropertyNameAndValue [35-40]
+                          Identifier(id) [35-37]
+                          IntLiteral(7) [39-40]
+                  ParenthesizedGraphPathPattern [42-63]
+                    BoundedQuantifier [58-63]
+                      QuantifierBound [59-60]
+                        IntLiteral(1) [59-60]
+                      QuantifierBound [61-62]
+                        IntLiteral(2) [61-62]
+                    GraphEdgePattern [42-58]
+                      GraphElementPatternFiller [44-55]
+                        Identifier(e) [44-45]
+                        GraphLabelFilter [45-55]
+                          GraphElementLabel [46-55]
+                            Identifier(Transfers) [46-55]
+                  GraphNodePattern [63-76]
+                    GraphElementPatternFiller [64-75]
+                      Identifier(dst) [64-67]
+                      GraphLabelFilter [67-75]
+                        GraphElementLabel [68-75]
+                          Identifier(Account) [68-75]
+            GqlLet [77-109]
+              GqlLetVariableDefinitionList [81-109]
+                GqlLetVariableDefinition [81-109]
+                  Identifier(total_amount) [81-93]
+                  FunctionCall [96-109]
+                    PathExpression [96-99]
+                      Identifier(SUM) [96-99]
+                    PathExpression [100-108]
+                      Identifier(e) [100-101]
+                      Identifier(amount) [102-108]
+            GqlReturn [110-233]
+              Select [119-233]
+                SelectList [119-233]
+                  SelectColumn [119-146]
+                    PathExpression [119-125]
+                      Identifier(src) [119-122]
+                      Identifier(id) [123-125]
+                    Alias [126-146]
+                      Identifier(source_account_id) [129-146]
+                  SelectColumn [148-180]
+                    PathExpression [148-154]
+                      Identifier(dst) [148-151]
+                      Identifier(id) [152-154]
+                    Alias [155-180]
+                      Identifier(destination_account_id) [158-180]
+                  SelectColumn [184-233]
+                    FunctionCall [184-207]
+                      PathExpression [184-193]
+                        Identifier(ARRAY_AGG) [184-193]
+                      PathExpression [194-206]
+                        Identifier(total_amount) [194-206]
+                    Alias [208-233]
+                      Identifier(total_amounts_per_path) [211-233]
diff --git a/parser/testdata/spanner/graph-operators.test b/parser/testdata/spanner/graph-operators.test
new file mode 100644
index 0000000..71e3f59
--- /dev/null
+++ b/parser/testdata/spanner/graph-operators.test
@@ -0,0 +1,865 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-operators
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-operators#graph_concatenation_operator
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+GRAPH FinGraph
+MATCH
+  p=(src:Account)-[t1:Transfers]->(mid:Account),
+  q = (mid)-[t2:Transfers]->(dst:Account)
+LET full_path = p || q
+RETURN
+  JSON_QUERY(TO_JSON(full_path)[0], '$.labels') AS element_a,
+  JSON_QUERY(TO_JSON(full_path)[1], '$.labels') AS element_b,
+  JSON_QUERY(TO_JSON(full_path)[2], '$.labels') AS element_c,
+  JSON_QUERY(TO_JSON(full_path)[3], '$.labels') AS element_d,
+  JSON_QUERY(TO_JSON(full_path)[4], '$.labels') AS element_e,
+  JSON_QUERY(TO_JSON(full_path)[5], '$.labels') AS element_f
+--
+QueryStatement [0-512]
+  Query [0-512]
+    GqlQuery [0-512]
+      GraphTableQuery [0-512]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-512]
+          GqlOperatorList [15-512]
+            GqlMatch [15-111]
+              GraphPattern [23-111]
+                GraphPathPattern [23-68]
+                  Identifier(p) [23-24]
+                  GraphNodePattern [25-38]
+                    GraphElementPatternFiller [26-37]
+                      Identifier(src) [26-29]
+                      GraphLabelFilter [29-37]
+                        GraphElementLabel [30-37]
+                          Identifier(Account) [30-37]
+                  GraphEdgePattern [38-55]
+                    GraphElementPatternFiller [40-52]
+                      Identifier(t1) [40-42]
+                      GraphLabelFilter [42-52]
+                        GraphElementLabel [43-52]
+                          Identifier(Transfers) [43-52]
+                  GraphNodePattern [55-68]
+                    GraphElementPatternFiller [56-67]
+                      Identifier(mid) [56-59]
+                      GraphLabelFilter [59-67]
+                        GraphElementLabel [60-67]
+                          Identifier(Account) [60-67]
+                GraphPathPattern [72-111]
+                  Identifier(q) [72-73]
+                  GraphNodePattern [76-81]
+                    GraphElementPatternFiller [77-80]
+                      Identifier(mid) [77-80]
+                  GraphEdgePattern [81-98]
+                    GraphElementPatternFiller [83-95]
+                      Identifier(t2) [83-85]
+                      GraphLabelFilter [85-95]
+                        GraphElementLabel [86-95]
+                          Identifier(Transfers) [86-95]
+                  GraphNodePattern [98-111]
+                    GraphElementPatternFiller [99-110]
+                      Identifier(dst) [99-102]
+                      GraphLabelFilter [102-110]
+                        GraphElementLabel [103-110]
+                          Identifier(Account) [103-110]
+            GqlLet [112-134]
+              GqlLetVariableDefinitionList [116-134]
+                GqlLetVariableDefinition [116-134]
+                  Identifier(full_path) [116-125]
+                  BinaryExpression(||) [128-134]
+                    PathExpression [128-129]
+                      Identifier(p) [128-129]
+                    PathExpression [133-134]
+                      Identifier(q) [133-134]
+            GqlReturn [135-512]
+              Select [144-512]
+                SelectList [144-512]
+                  SelectColumn [144-202]
+                    FunctionCall [144-189]
+                      PathExpression [144-154]
+                        Identifier(JSON_QUERY) [144-154]
+                      ArrayElement [155-176]
+                        FunctionCall [155-173]
+                          PathExpression [155-162]
+                            Identifier(TO_JSON) [155-162]
+                          PathExpression [163-172]
+                            Identifier(full_path) [163-172]
+                        Location [173-174]
+                        IntLiteral(0) [174-175]
+                      StringLiteral [178-188]
+                        StringLiteralComponent('$.labels') [178-188]
+                    Alias [190-202]
+                      Identifier(element_a) [193-202]
+                  SelectColumn [206-264]
+                    FunctionCall [206-251]
+                      PathExpression [206-216]
+                        Identifier(JSON_QUERY) [206-216]
+                      ArrayElement [217-238]
+                        FunctionCall [217-235]
+                          PathExpression [217-224]
+                            Identifier(TO_JSON) [217-224]
+                          PathExpression [225-234]
+                            Identifier(full_path) [225-234]
+                        Location [235-236]
+                        IntLiteral(1) [236-237]
+                      StringLiteral [240-250]
+                        StringLiteralComponent('$.labels') [240-250]
+                    Alias [252-264]
+                      Identifier(element_b) [255-264]
+                  SelectColumn [268-326]
+                    FunctionCall [268-313]
+                      PathExpression [268-278]
+                        Identifier(JSON_QUERY) [268-278]
+                      ArrayElement [279-300]
+                        FunctionCall [279-297]
+                          PathExpression [279-286]
+                            Identifier(TO_JSON) [279-286]
+                          PathExpression [287-296]
+                            Identifier(full_path) [287-296]
+                        Location [297-298]
+                        IntLiteral(2) [298-299]
+                      StringLiteral [302-312]
+                        StringLiteralComponent('$.labels') [302-312]
+                    Alias [314-326]
+                      Identifier(element_c) [317-326]
+                  SelectColumn [330-388]
+                    FunctionCall [330-375]
+                      PathExpression [330-340]
+                        Identifier(JSON_QUERY) [330-340]
+                      ArrayElement [341-362]
+                        FunctionCall [341-359]
+                          PathExpression [341-348]
+                            Identifier(TO_JSON) [341-348]
+                          PathExpression [349-358]
+                            Identifier(full_path) [349-358]
+                        Location [359-360]
+                        IntLiteral(3) [360-361]
+                      StringLiteral [364-374]
+                        StringLiteralComponent('$.labels') [364-374]
+                    Alias [376-388]
+                      Identifier(element_d) [379-388]
+                  SelectColumn [392-450]
+                    FunctionCall [392-437]
+                      PathExpression [392-402]
+                        Identifier(JSON_QUERY) [392-402]
+                      ArrayElement [403-424]
+                        FunctionCall [403-421]
+                          PathExpression [403-410]
+                            Identifier(TO_JSON) [403-410]
+                          PathExpression [411-420]
+                            Identifier(full_path) [411-420]
+                        Location [421-422]
+                        IntLiteral(4) [422-423]
+                      StringLiteral [426-436]
+                        StringLiteralComponent('$.labels') [426-436]
+                    Alias [438-450]
+                      Identifier(element_e) [441-450]
+                  SelectColumn [454-512]
+                    FunctionCall [454-499]
+                      PathExpression [454-464]
+                        Identifier(JSON_QUERY) [454-464]
+                      ArrayElement [465-486]
+                        FunctionCall [465-483]
+                          PathExpression [465-472]
+                            Identifier(TO_JSON) [465-472]
+                          PathExpression [473-482]
+                            Identifier(full_path) [473-482]
+                        Location [483-484]
+                        IntLiteral(5) [484-485]
+                      StringLiteral [488-498]
+                        StringLiteralComponent('$.labels') [488-498]
+                    Alias [500-512]
+                      Identifier(element_f) [503-512]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-operators#graph_concatenation_operator
+-- Error: `mid1` and `mid2` aren't equal.
+GRAPH FinGraph
+MATCH
+  p=(src:Account)-[t1:Transfers]->(mid1:Account),
+  q=(mid2:Account)-[t2:Transfers]->(dst:Account)
+LET full_path = p || q
+RETURN TO_JSON(full_path) AS results
+--
+QueryStatement [42-221]
+  Query [42-221]
+    GqlQuery [42-221]
+      GraphTableQuery [42-221]
+        PathExpression [48-56]
+          Identifier(FinGraph) [48-56]
+        GqlOperatorList [57-221]
+          GqlOperatorList [57-221]
+            GqlMatch [57-161]
+              GraphPattern [65-161]
+                GraphPathPattern [65-111]
+                  Identifier(p) [65-66]
+                  GraphNodePattern [67-80]
+                    GraphElementPatternFiller [68-79]
+                      Identifier(src) [68-71]
+                      GraphLabelFilter [71-79]
+                        GraphElementLabel [72-79]
+                          Identifier(Account) [72-79]
+                  GraphEdgePattern [80-97]
+                    GraphElementPatternFiller [82-94]
+                      Identifier(t1) [82-84]
+                      GraphLabelFilter [84-94]
+                        GraphElementLabel [85-94]
+                          Identifier(Transfers) [85-94]
+                  GraphNodePattern [97-111]
+                    GraphElementPatternFiller [98-110]
+                      Identifier(mid1) [98-102]
+                      GraphLabelFilter [102-110]
+                        GraphElementLabel [103-110]
+                          Identifier(Account) [103-110]
+                GraphPathPattern [115-161]
+                  Identifier(q) [115-116]
+                  GraphNodePattern [117-131]
+                    GraphElementPatternFiller [118-130]
+                      Identifier(mid2) [118-122]
+                      GraphLabelFilter [122-130]
+                        GraphElementLabel [123-130]
+                          Identifier(Account) [123-130]
+                  GraphEdgePattern [131-148]
+                    GraphElementPatternFiller [133-145]
+                      Identifier(t2) [133-135]
+                      GraphLabelFilter [135-145]
+                        GraphElementLabel [136-145]
+                          Identifier(Transfers) [136-145]
+                  GraphNodePattern [148-161]
+                    GraphElementPatternFiller [149-160]
+                      Identifier(dst) [149-152]
+                      GraphLabelFilter [152-160]
+                        GraphElementLabel [153-160]
+                          Identifier(Account) [153-160]
+            GqlLet [162-184]
+              GqlLetVariableDefinitionList [166-184]
+                GqlLetVariableDefinition [166-184]
+                  Identifier(full_path) [166-175]
+                  BinaryExpression(||) [178-184]
+                    PathExpression [178-179]
+                      Identifier(p) [178-179]
+                    PathExpression [183-184]
+                      Identifier(q) [183-184]
+            GqlReturn [185-221]
+              Select [192-221]
+                SelectList [192-221]
+                  SelectColumn [192-221]
+                    FunctionCall [192-210]
+                      PathExpression [192-199]
+                        Identifier(TO_JSON) [192-199]
+                      PathExpression [200-209]
+                        Identifier(full_path) [200-209]
+                    Alias [211-221]
+                      Identifier(results) [214-221]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-operators#all_different_predicate
+GRAPH FinGraph
+MATCH
+  (a1:Account)-[t1:Transfers]->(a2:Account)-[t2:Transfers]->
+  (a3:Account)-[t3:Transfers]->(a4:Account)
+WHERE a1.id < a4.id
+RETURN
+  ALL_DIFFERENT(t1, t2, t3) AS results
+--
+QueryStatement [0-191]
+  Query [0-191]
+    GqlQuery [0-191]
+      GraphTableQuery [0-191]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-191]
+          GqlOperatorList [15-191]
+            GqlMatch [15-145]
+              GraphPattern [23-145]
+                GraphPathPattern [23-125]
+                  GraphNodePattern [23-35]
+                    GraphElementPatternFiller [24-34]
+                      Identifier(a1) [24-26]
+                      GraphLabelFilter [26-34]
+                        GraphElementLabel [27-34]
+                          Identifier(Account) [27-34]
+                  GraphEdgePattern [35-52]
+                    GraphElementPatternFiller [37-49]
+                      Identifier(t1) [37-39]
+                      GraphLabelFilter [39-49]
+                        GraphElementLabel [40-49]
+                          Identifier(Transfers) [40-49]
+                  GraphNodePattern [52-64]
+                    GraphElementPatternFiller [53-63]
+                      Identifier(a2) [53-55]
+                      GraphLabelFilter [55-63]
+                        GraphElementLabel [56-63]
+                          Identifier(Account) [56-63]
+                  GraphEdgePattern [64-81]
+                    GraphElementPatternFiller [66-78]
+                      Identifier(t2) [66-68]
+                      GraphLabelFilter [68-78]
+                        GraphElementLabel [69-78]
+                          Identifier(Transfers) [69-78]
+                  GraphNodePattern [84-96]
+                    GraphElementPatternFiller [85-95]
+                      Identifier(a3) [85-87]
+                      GraphLabelFilter [87-95]
+                        GraphElementLabel [88-95]
+                          Identifier(Account) [88-95]
+                  GraphEdgePattern [96-113]
+                    GraphElementPatternFiller [98-110]
+                      Identifier(t3) [98-100]
+                      GraphLabelFilter [100-110]
+                        GraphElementLabel [101-110]
+                          Identifier(Transfers) [101-110]
+                  GraphNodePattern [113-125]
+                    GraphElementPatternFiller [114-124]
+                      Identifier(a4) [114-116]
+                      GraphLabelFilter [116-124]
+                        GraphElementLabel [117-124]
+                          Identifier(Account) [117-124]
+                WhereClause [126-145]
+                  BinaryExpression(<) [132-145]
+                    PathExpression [132-137]
+                      Identifier(a1) [132-134]
+                      Identifier(id) [135-137]
+                    PathExpression [140-145]
+                      Identifier(a4) [140-142]
+                      Identifier(id) [143-145]
+            GqlReturn [146-191]
+              Select [155-191]
+                SelectList [155-191]
+                  SelectColumn [155-191]
+                    FunctionCall [155-180]
+                      PathExpression [155-168]
+                        Identifier(ALL_DIFFERENT) [155-168]
+                      PathExpression [169-171]
+                        Identifier(t1) [169-171]
+                      PathExpression [173-175]
+                        Identifier(t2) [173-175]
+                      PathExpression [177-179]
+                        Identifier(t3) [177-179]
+                    Alias [181-191]
+                      Identifier(results) [184-191]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-operators#is_destination_predicate
+GRAPH FinGraph
+MATCH (a:Account)-[transfer:Transfers]-(b:Account)
+WHERE a IS DESTINATION of transfer
+RETURN a.id AS a_id, b.id AS b_id
+--
+QueryStatement [0-134]
+  Query [0-134]
+    GqlQuery [0-134]
+      GraphTableQuery [0-134]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-134]
+          GqlOperatorList [15-134]
+            GqlMatch [15-100]
+              GraphPattern [21-100]
+                GraphPathPattern [21-65]
+                  GraphNodePattern [21-32]
+                    GraphElementPatternFiller [22-31]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                  GraphEdgePattern [32-54]
+                    GraphElementPatternFiller [34-52]
+                      Identifier(transfer) [34-42]
+                      GraphLabelFilter [42-52]
+                        GraphElementLabel [43-52]
+                          Identifier(Transfers) [43-52]
+                  GraphNodePattern [54-65]
+                    GraphElementPatternFiller [55-64]
+                      Identifier(b) [55-56]
+                      GraphLabelFilter [56-64]
+                        GraphElementLabel [57-64]
+                          Identifier(Account) [57-64]
+                WhereClause [66-100]
+                  BinaryExpression(IS DESTINATION OF) [72-100]
+                    PathExpression [72-73]
+                      Identifier(a) [72-73]
+                    PathExpression [92-100]
+                      Identifier(transfer) [92-100]
+            GqlReturn [101-134]
+              Select [108-134]
+                SelectList [108-134]
+                  SelectColumn [108-120]
+                    PathExpression [108-112]
+                      Identifier(a) [108-109]
+                      Identifier(id) [110-112]
+                    Alias [113-120]
+                      Identifier(a_id) [116-120]
+                  SelectColumn [122-134]
+                    PathExpression [122-126]
+                      Identifier(b) [122-123]
+                      Identifier(id) [124-126]
+                    Alias [127-134]
+                      Identifier(b_id) [130-134]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-operators#is_destination_predicate
+GRAPH FinGraph
+MATCH (a:Account)-[transfer:Transfers]-(b:Account)
+WHERE b IS DESTINATION of transfer
+RETURN a.id AS a_id, b.id AS b_id
+--
+QueryStatement [0-134]
+  Query [0-134]
+    GqlQuery [0-134]
+      GraphTableQuery [0-134]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-134]
+          GqlOperatorList [15-134]
+            GqlMatch [15-100]
+              GraphPattern [21-100]
+                GraphPathPattern [21-65]
+                  GraphNodePattern [21-32]
+                    GraphElementPatternFiller [22-31]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                  GraphEdgePattern [32-54]
+                    GraphElementPatternFiller [34-52]
+                      Identifier(transfer) [34-42]
+                      GraphLabelFilter [42-52]
+                        GraphElementLabel [43-52]
+                          Identifier(Transfers) [43-52]
+                  GraphNodePattern [54-65]
+                    GraphElementPatternFiller [55-64]
+                      Identifier(b) [55-56]
+                      GraphLabelFilter [56-64]
+                        GraphElementLabel [57-64]
+                          Identifier(Account) [57-64]
+                WhereClause [66-100]
+                  BinaryExpression(IS DESTINATION OF) [72-100]
+                    PathExpression [72-73]
+                      Identifier(b) [72-73]
+                    PathExpression [92-100]
+                      Identifier(transfer) [92-100]
+            GqlReturn [101-134]
+              Select [108-134]
+                SelectList [108-134]
+                  SelectColumn [108-120]
+                    PathExpression [108-112]
+                      Identifier(a) [108-109]
+                      Identifier(id) [110-112]
+                    Alias [113-120]
+                      Identifier(a_id) [116-120]
+                  SelectColumn [122-134]
+                    PathExpression [122-126]
+                      Identifier(b) [122-123]
+                      Identifier(id) [124-126]
+                    Alias [127-134]
+                      Identifier(b_id) [130-134]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-operators#is_labeled_predicate
+GRAPH FinGraph
+MATCH (a)
+WHERE a IS LABELED Account | Person
+RETURN a.id AS a_id, LABELS(a) AS labels
+--
+QueryStatement [0-101]
+  Query [0-101]
+    GqlQuery [0-101]
+      GraphTableQuery [0-101]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-101]
+          GqlOperatorList [15-101]
+            GqlMatch [15-60]
+              GraphPattern [21-60]
+                GraphPathPattern [21-24]
+                  GraphNodePattern [21-24]
+                    GraphElementPatternFiller [22-23]
+                      Identifier(a) [22-23]
+                WhereClause [25-60]
+                  GraphIsLabeledPredicate [31-60]
+                    PathExpression [31-32]
+                      Identifier(a) [31-32]
+                    GraphLabelOperation(OR) [44-60]
+                      GraphElementLabel [44-51]
+                        Identifier(Account) [44-51]
+                      GraphElementLabel [54-60]
+                        Identifier(Person) [54-60]
+            GqlReturn [61-101]
+              Select [68-101]
+                SelectList [68-101]
+                  SelectColumn [68-80]
+                    PathExpression [68-72]
+                      Identifier(a) [68-69]
+                      Identifier(id) [70-72]
+                    Alias [73-80]
+                      Identifier(a_id) [76-80]
+                  SelectColumn [82-101]
+                    FunctionCall [82-91]
+                      PathExpression [82-88]
+                        Identifier(LABELS) [82-88]
+                      PathExpression [89-90]
+                        Identifier(a) [89-90]
+                    Alias [92-101]
+                      Identifier(labels) [95-101]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-operators#is_labeled_predicate
+GRAPH FinGraph
+MATCH (a)-[e]-(b:Account)
+WHERE e IS LABELED Transfers | Owns
+RETURN a.Id as a_id, Labels(e) AS labels, b.Id as b_id
+ORDER BY a_id, b_id
+--
+QueryStatement [0-151]
+  Query [0-151]
+    GqlQuery [0-151]
+      GraphTableQuery [0-151]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-151]
+          GqlOperatorList [15-151]
+            GqlMatch [15-76]
+              GraphPattern [21-76]
+                GraphPathPattern [21-40]
+                  GraphNodePattern [21-24]
+                    GraphElementPatternFiller [22-23]
+                      Identifier(a) [22-23]
+                  GraphEdgePattern [24-29]
+                    GraphElementPatternFiller [26-27]
+                      Identifier(e) [26-27]
+                  GraphNodePattern [29-40]
+                    GraphElementPatternFiller [30-39]
+                      Identifier(b) [30-31]
+                      GraphLabelFilter [31-39]
+                        GraphElementLabel [32-39]
+                          Identifier(Account) [32-39]
+                WhereClause [41-76]
+                  GraphIsLabeledPredicate [47-76]
+                    PathExpression [47-48]
+                      Identifier(e) [47-48]
+                    GraphLabelOperation(OR) [60-76]
+                      GraphElementLabel [60-69]
+                        Identifier(Transfers) [60-69]
+                      GraphElementLabel [72-76]
+                        Identifier(Owns) [72-76]
+            GqlReturn [77-151]
+              Select [84-131]
+                SelectList [84-131]
+                  SelectColumn [84-96]
+                    PathExpression [84-88]
+                      Identifier(a) [84-85]
+                      Identifier(Id) [86-88]
+                    Alias [89-96]
+                      Identifier(a_id) [92-96]
+                  SelectColumn [98-117]
+                    FunctionCall [98-107]
+                      PathExpression [98-104]
+                        Identifier(Labels) [98-104]
+                      PathExpression [105-106]
+                        Identifier(e) [105-106]
+                    Alias [108-117]
+                      Identifier(labels) [111-117]
+                  SelectColumn [119-131]
+                    PathExpression [119-123]
+                      Identifier(b) [119-120]
+                      Identifier(Id) [121-123]
+                    Alias [124-131]
+                      Identifier(b_id) [127-131]
+              GqlOrderByAndPage [132-151]
+                OrderBy [132-151]
+                  OrderingExpression(ASC) [141-145]
+                    PathExpression [141-145]
+                      Identifier(a_id) [141-145]
+                  OrderingExpression(ASC) [147-151]
+                    PathExpression [147-151]
+                      Identifier(b_id) [147-151]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-operators#is_labeled_predicate
+GRAPH FinGraph
+MATCH (a:Account {Id: 7})
+OPTIONAL MATCH (a)-[:OWNS]->(b)
+RETURN a.Id AS a_id, b.Id AS b_id, b IS LABELED Account AS b_is_account
+--
+QueryStatement [0-144]
+  Query [0-144]
+    GqlQuery [0-144]
+      GraphTableQuery [0-144]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-144]
+          GqlOperatorList [15-144]
+            GqlMatch [15-40]
+              GraphPattern [21-40]
+                GraphPathPattern [21-40]
+                  GraphNodePattern [21-40]
+                    GraphElementPatternFiller [22-39]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                      GraphPropertySpecification [32-39]
+                        GraphPropertyNameAndValue [33-38]
+                          Identifier(Id) [33-35]
+                          IntLiteral(7) [37-38]
+            GqlMatch (optional) [41-72]
+              GraphPattern [56-72]
+                GraphPathPattern [56-72]
+                  GraphNodePattern [56-59]
+                    GraphElementPatternFiller [57-58]
+                      Identifier(a) [57-58]
+                  GraphEdgePattern [59-69]
+                    GraphElementPatternFiller [61-66]
+                      GraphLabelFilter [61-66]
+                        GraphElementLabel [62-66]
+                          Identifier(OWNS) [62-66]
+                  GraphNodePattern [69-72]
+                    GraphElementPatternFiller [70-71]
+                      Identifier(b) [70-71]
+            GqlReturn [73-144]
+              Select [80-144]
+                SelectList [80-144]
+                  SelectColumn [80-92]
+                    PathExpression [80-84]
+                      Identifier(a) [80-81]
+                      Identifier(Id) [82-84]
+                    Alias [85-92]
+                      Identifier(a_id) [88-92]
+                  SelectColumn [94-106]
+                    PathExpression [94-98]
+                      Identifier(b) [94-95]
+                      Identifier(Id) [96-98]
+                    Alias [99-106]
+                      Identifier(b_id) [102-106]
+                  SelectColumn [108-144]
+                    GraphIsLabeledPredicate [108-128]
+                      PathExpression [108-109]
+                        Identifier(b) [108-109]
+                      GraphElementLabel [121-128]
+                        Identifier(Account) [121-128]
+                    Alias [129-144]
+                      Identifier(b_is_account) [132-144]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-operators#is_source_predicate
+GRAPH FinGraph
+MATCH (a:Account)-[transfer:Transfers]-(b:Account)
+WHERE a IS SOURCE of transfer
+RETURN a.id AS a_id, b.id AS b_id
+--
+QueryStatement [0-129]
+  Query [0-129]
+    GqlQuery [0-129]
+      GraphTableQuery [0-129]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-129]
+          GqlOperatorList [15-129]
+            GqlMatch [15-95]
+              GraphPattern [21-95]
+                GraphPathPattern [21-65]
+                  GraphNodePattern [21-32]
+                    GraphElementPatternFiller [22-31]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                  GraphEdgePattern [32-54]
+                    GraphElementPatternFiller [34-52]
+                      Identifier(transfer) [34-42]
+                      GraphLabelFilter [42-52]
+                        GraphElementLabel [43-52]
+                          Identifier(Transfers) [43-52]
+                  GraphNodePattern [54-65]
+                    GraphElementPatternFiller [55-64]
+                      Identifier(b) [55-56]
+                      GraphLabelFilter [56-64]
+                        GraphElementLabel [57-64]
+                          Identifier(Account) [57-64]
+                WhereClause [66-95]
+                  BinaryExpression(IS SOURCE OF) [72-95]
+                    PathExpression [72-73]
+                      Identifier(a) [72-73]
+                    PathExpression [87-95]
+                      Identifier(transfer) [87-95]
+            GqlReturn [96-129]
+              Select [103-129]
+                SelectList [103-129]
+                  SelectColumn [103-115]
+                    PathExpression [103-107]
+                      Identifier(a) [103-104]
+                      Identifier(id) [105-107]
+                    Alias [108-115]
+                      Identifier(a_id) [111-115]
+                  SelectColumn [117-129]
+                    PathExpression [117-121]
+                      Identifier(b) [117-118]
+                      Identifier(id) [119-121]
+                    Alias [122-129]
+                      Identifier(b_id) [125-129]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-operators#is_source_predicate
+GRAPH FinGraph
+MATCH (a:Account)-[transfer:Transfers]-(b:Account)
+WHERE b IS SOURCE of transfer
+RETURN a.id AS a_id, b.id AS b_id
+--
+QueryStatement [0-129]
+  Query [0-129]
+    GqlQuery [0-129]
+      GraphTableQuery [0-129]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-129]
+          GqlOperatorList [15-129]
+            GqlMatch [15-95]
+              GraphPattern [21-95]
+                GraphPathPattern [21-65]
+                  GraphNodePattern [21-32]
+                    GraphElementPatternFiller [22-31]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                  GraphEdgePattern [32-54]
+                    GraphElementPatternFiller [34-52]
+                      Identifier(transfer) [34-42]
+                      GraphLabelFilter [42-52]
+                        GraphElementLabel [43-52]
+                          Identifier(Transfers) [43-52]
+                  GraphNodePattern [54-65]
+                    GraphElementPatternFiller [55-64]
+                      Identifier(b) [55-56]
+                      GraphLabelFilter [56-64]
+                        GraphElementLabel [57-64]
+                          Identifier(Account) [57-64]
+                WhereClause [66-95]
+                  BinaryExpression(IS SOURCE OF) [72-95]
+                    PathExpression [72-73]
+                      Identifier(b) [72-73]
+                    PathExpression [87-95]
+                      Identifier(transfer) [87-95]
+            GqlReturn [96-129]
+              Select [103-129]
+                SelectList [103-129]
+                  SelectColumn [103-115]
+                    PathExpression [103-107]
+                      Identifier(a) [103-104]
+                      Identifier(id) [105-107]
+                    Alias [108-115]
+                      Identifier(a_id) [111-115]
+                  SelectColumn [117-129]
+                    PathExpression [117-121]
+                      Identifier(b) [117-118]
+                      Identifier(id) [119-121]
+                    Alias [122-129]
+                      Identifier(b_id) [125-129]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-operators#property_exists_predicate
+[mode=expression]
+PROPERTY_EXISTS(element, element_property)
+--
+FunctionCall [0-42]
+  PathExpression [0-15]
+    Identifier(PROPERTY_EXISTS) [0-15]
+  PathExpression [16-23]
+    Identifier(element) [16-23]
+  PathExpression [25-41]
+    Identifier(element_property) [25-41]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-operators#property_exists_predicate
+GRAPH FinGraph
+MATCH (n:Person|Account WHERE PROPERTY_EXISTS(n, name))
+RETURN n.name
+--
+QueryStatement [0-84]
+  Query [0-84]
+    GqlQuery [0-84]
+      GraphTableQuery [0-84]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-84]
+          GqlOperatorList [15-84]
+            GqlMatch [15-70]
+              GraphPattern [21-70]
+                GraphPathPattern [21-70]
+                  GraphNodePattern [21-70]
+                    GraphElementPatternFiller [22-69]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-38]
+                        GraphLabelOperation(OR) [24-38]
+                          GraphElementLabel [24-30]
+                            Identifier(Person) [24-30]
+                          GraphElementLabel [31-38]
+                            Identifier(Account) [31-38]
+                      WhereClause [39-69]
+                        FunctionCall [45-69]
+                          PathExpression [45-60]
+                            Identifier(PROPERTY_EXISTS) [45-60]
+                          PathExpression [61-62]
+                            Identifier(n) [61-62]
+                          PathExpression [64-68]
+                            Identifier(name) [64-68]
+            GqlReturn [71-84]
+              Select [78-84]
+                SelectList [78-84]
+                  SelectColumn [78-84]
+                    PathExpression [78-84]
+                      Identifier(n) [78-79]
+                      Identifier(name) [80-84]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-operators#same_predicate
+GRAPH FinGraph
+MATCH (src:Account)<-[transfer:Transfers]-(dest:Account)
+WHERE NOT SAME(src, dest)
+RETURN src.id AS source_id, dest.id AS destination_id
+--
+QueryStatement [0-151]
+  Query [0-151]
+    GqlQuery [0-151]
+      GraphTableQuery [0-151]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-151]
+          GqlOperatorList [15-151]
+            GqlMatch [15-97]
+              GraphPattern [21-97]
+                GraphPathPattern [21-71]
+                  GraphNodePattern [21-34]
+                    GraphElementPatternFiller [22-33]
+                      Identifier(src) [22-25]
+                      GraphLabelFilter [25-33]
+                        GraphElementLabel [26-33]
+                          Identifier(Account) [26-33]
+                  GraphEdgePattern [34-57]
+                    GraphElementPatternFiller [37-55]
+                      Identifier(transfer) [37-45]
+                      GraphLabelFilter [45-55]
+                        GraphElementLabel [46-55]
+                          Identifier(Transfers) [46-55]
+                  GraphNodePattern [57-71]
+                    GraphElementPatternFiller [58-70]
+                      Identifier(dest) [58-62]
+                      GraphLabelFilter [62-70]
+                        GraphElementLabel [63-70]
+                          Identifier(Account) [63-70]
+                WhereClause [72-97]
+                  UnaryExpression(NOT) [78-97]
+                    FunctionCall [82-97]
+                      PathExpression [82-86]
+                        Identifier(SAME) [82-86]
+                      PathExpression [87-90]
+                        Identifier(src) [87-90]
+                      PathExpression [92-96]
+                        Identifier(dest) [92-96]
+            GqlReturn [98-151]
+              Select [105-151]
+                SelectList [105-151]
+                  SelectColumn [105-124]
+                    PathExpression [105-111]
+                      Identifier(src) [105-108]
+                      Identifier(id) [109-111]
+                    Alias [112-124]
+                      Identifier(source_id) [115-124]
+                  SelectColumn [126-151]
+                    PathExpression [126-133]
+                      Identifier(dest) [126-130]
+                      Identifier(id) [131-133]
+                    Alias [134-151]
+                      Identifier(destination_id) [137-151]
diff --git a/parser/testdata/spanner/graph-patterns.test b/parser/testdata/spanner/graph-patterns.test
new file mode 100644
index 0000000..21d96cf
--- /dev/null
+++ b/parser/testdata/spanner/graph-patterns.test
@@ -0,0 +1,2818 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+GRAPH FinGraph
+MATCH (n)
+RETURN n.name, n.id
+--
+QueryStatement [0-44]
+  Query [0-44]
+    GqlQuery [0-44]
+      GraphTableQuery [0-44]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-44]
+          GqlOperatorList [15-44]
+            GqlMatch [15-24]
+              GraphPattern [21-24]
+                GraphPathPattern [21-24]
+                  GraphNodePattern [21-24]
+                    GraphElementPatternFiller [22-23]
+                      Identifier(n) [22-23]
+            GqlReturn [25-44]
+              Select [32-44]
+                SelectList [32-44]
+                  SelectColumn [32-38]
+                    PathExpression [32-38]
+                      Identifier(n) [32-33]
+                      Identifier(name) [34-38]
+                  SelectColumn [40-44]
+                    PathExpression [40-44]
+                      Identifier(n) [40-41]
+                      Identifier(id) [42-44]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples
+GRAPH FinGraph
+MATCH ()-[e]->()
+RETURN COUNT(e.id) AS results
+--
+QueryStatement [0-61]
+  Query [0-61]
+    GqlQuery [0-61]
+      GraphTableQuery [0-61]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-61]
+          GqlOperatorList [15-61]
+            GqlMatch [15-31]
+              GraphPattern [21-31]
+                GraphPathPattern [21-31]
+                  GraphNodePattern [21-23]
+                    GraphElementPatternFiller [22-22]
+                  GraphEdgePattern [23-29]
+                    GraphElementPatternFiller [25-26]
+                      Identifier(e) [25-26]
+                  GraphNodePattern [29-31]
+                    GraphElementPatternFiller [30-30]
+            GqlReturn [32-61]
+              Select [39-61]
+                SelectList [39-61]
+                  SelectColumn [39-61]
+                    FunctionCall [39-50]
+                      PathExpression [39-44]
+                        Identifier(COUNT) [39-44]
+                      PathExpression [45-49]
+                        Identifier(e) [45-46]
+                        Identifier(id) [47-49]
+                    Alias [51-61]
+                      Identifier(results) [54-61]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples
+GRAPH FinGraph
+MATCH ()-[e]-()
+RETURN COUNT(e.id) AS results
+--
+QueryStatement [0-60]
+  Query [0-60]
+    GqlQuery [0-60]
+      GraphTableQuery [0-60]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-60]
+          GqlOperatorList [15-60]
+            GqlMatch [15-30]
+              GraphPattern [21-30]
+                GraphPathPattern [21-30]
+                  GraphNodePattern [21-23]
+                    GraphElementPatternFiller [22-22]
+                  GraphEdgePattern [23-28]
+                    GraphElementPatternFiller [25-26]
+                      Identifier(e) [25-26]
+                  GraphNodePattern [28-30]
+                    GraphElementPatternFiller [29-29]
+            GqlReturn [31-60]
+              Select [38-60]
+                SelectList [38-60]
+                  SelectColumn [38-60]
+                    FunctionCall [38-49]
+                      PathExpression [38-43]
+                        Identifier(COUNT) [38-43]
+                      PathExpression [44-48]
+                        Identifier(e) [44-45]
+                        Identifier(id) [46-48]
+                    Alias [50-60]
+                      Identifier(results) [53-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples
+GRAPH FinGraph
+MATCH
+  (src:Account)-[t1:Transfers]->(mid:Account)-[t2:Transfers]->(dst:Account),
+  (mid)<-[:Owns]-(p:Person)
+RETURN
+  p.name, src.id AS src_account_id, mid.id AS mid_account_id,
+  dst.id AS dst_account_id
+--
+QueryStatement [0-221]
+  Query [0-221]
+    GqlQuery [0-221]
+      GraphTableQuery [0-221]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-221]
+          GqlOperatorList [15-221]
+            GqlMatch [15-125]
+              GraphPattern [23-125]
+                GraphPathPattern [23-96]
+                  GraphNodePattern [23-36]
+                    GraphElementPatternFiller [24-35]
+                      Identifier(src) [24-27]
+                      GraphLabelFilter [27-35]
+                        GraphElementLabel [28-35]
+                          Identifier(Account) [28-35]
+                  GraphEdgePattern [36-53]
+                    GraphElementPatternFiller [38-50]
+                      Identifier(t1) [38-40]
+                      GraphLabelFilter [40-50]
+                        GraphElementLabel [41-50]
+                          Identifier(Transfers) [41-50]
+                  GraphNodePattern [53-66]
+                    GraphElementPatternFiller [54-65]
+                      Identifier(mid) [54-57]
+                      GraphLabelFilter [57-65]
+                        GraphElementLabel [58-65]
+                          Identifier(Account) [58-65]
+                  GraphEdgePattern [66-83]
+                    GraphElementPatternFiller [68-80]
+                      Identifier(t2) [68-70]
+                      GraphLabelFilter [70-80]
+                        GraphElementLabel [71-80]
+                          Identifier(Transfers) [71-80]
+                  GraphNodePattern [83-96]
+                    GraphElementPatternFiller [84-95]
+                      Identifier(dst) [84-87]
+                      GraphLabelFilter [87-95]
+                        GraphElementLabel [88-95]
+                          Identifier(Account) [88-95]
+                GraphPathPattern [100-125]
+                  GraphNodePattern [100-105]
+                    GraphElementPatternFiller [101-104]
+                      Identifier(mid) [101-104]
+                  GraphEdgePattern [105-115]
+                    GraphElementPatternFiller [108-113]
+                      GraphLabelFilter [108-113]
+                        GraphElementLabel [109-113]
+                          Identifier(Owns) [109-113]
+                  GraphNodePattern [115-125]
+                    GraphElementPatternFiller [116-124]
+                      Identifier(p) [116-117]
+                      GraphLabelFilter [117-124]
+                        GraphElementLabel [118-124]
+                          Identifier(Person) [118-124]
+            GqlReturn [126-221]
+              Select [135-221]
+                SelectList [135-221]
+                  SelectColumn [135-141]
+                    PathExpression [135-141]
+                      Identifier(p) [135-136]
+                      Identifier(name) [137-141]
+                  SelectColumn [143-167]
+                    PathExpression [143-149]
+                      Identifier(src) [143-146]
+                      Identifier(id) [147-149]
+                    Alias [150-167]
+                      Identifier(src_account_id) [153-167]
+                  SelectColumn [169-193]
+                    PathExpression [169-175]
+                      Identifier(mid) [169-172]
+                      Identifier(id) [173-175]
+                    Alias [176-193]
+                      Identifier(mid_account_id) [179-193]
+                  SelectColumn [197-221]
+                    PathExpression [197-203]
+                      Identifier(dst) [197-200]
+                      Identifier(id) [201-203]
+                    Alias [204-221]
+                      Identifier(dst_account_id) [207-221]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples
+GRAPH FinGraph
+MATCH p=(account:Account {is_blocked: false})-[transfer:Transfers]-(dst:Account)
+RETURN TO_JSON(p) as results
+--
+QueryStatement [0-124]
+  Query [0-124]
+    GqlQuery [0-124]
+      GraphTableQuery [0-124]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-124]
+          GqlOperatorList [15-124]
+            GqlMatch [15-95]
+              GraphPattern [21-95]
+                GraphPathPattern [21-95]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [23-60]
+                    GraphElementPatternFiller [24-59]
+                      Identifier(account) [24-31]
+                      GraphLabelFilter [31-39]
+                        GraphElementLabel [32-39]
+                          Identifier(Account) [32-39]
+                      GraphPropertySpecification [40-59]
+                        GraphPropertyNameAndValue [41-58]
+                          Identifier(is_blocked) [41-51]
+                          BooleanLiteral(false) [53-58]
+                  GraphEdgePattern [60-82]
+                    GraphElementPatternFiller [62-80]
+                      Identifier(transfer) [62-70]
+                      GraphLabelFilter [70-80]
+                        GraphElementLabel [71-80]
+                          Identifier(Transfers) [71-80]
+                  GraphNodePattern [82-95]
+                    GraphElementPatternFiller [83-94]
+                      Identifier(dst) [83-86]
+                      GraphLabelFilter [86-94]
+                        GraphElementLabel [87-94]
+                          Identifier(Account) [87-94]
+            GqlReturn [96-124]
+              Select [103-124]
+                SelectList [103-124]
+                  SelectColumn [103-124]
+                    FunctionCall [103-113]
+                      PathExpression [103-110]
+                        Identifier(TO_JSON) [103-110]
+                      PathExpression [111-112]
+                        Identifier(p) [111-112]
+                    Alias [114-124]
+                      Identifier(results) [117-124]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#definitions_2
+GRAPH FinGraph
+MATCH ANY CHEAPEST (a)-[e:Transfer COST e.amount]->{1,3}(b)
+RETURN a.id, b.id
+--
+QueryStatement [0-92]
+  Query [0-92]
+    GqlQuery [0-92]
+      GraphTableQuery [0-92]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-92]
+          GqlOperatorList [15-92]
+            GqlMatch [15-74]
+              GraphPattern [21-74]
+                GraphPathPattern [21-74]
+                  GraphPathSearchPrefix [21-33]
+                  GraphNodePattern [34-37]
+                    GraphElementPatternFiller [35-36]
+                      Identifier(a) [35-36]
+                  ParenthesizedGraphPathPattern [37-71]
+                    BoundedQuantifier [66-71]
+                      QuantifierBound [67-68]
+                        IntLiteral(1) [67-68]
+                      QuantifierBound [69-70]
+                        IntLiteral(3) [69-70]
+                    GraphEdgePattern [37-66]
+                      GraphElementPatternFiller [39-63]
+                        Identifier(e) [39-40]
+                        GraphLabelFilter [40-49]
+                          GraphElementLabel [41-49]
+                            Identifier(Transfer) [41-49]
+                        PathExpression [55-63]
+                          Identifier(e) [55-56]
+                          Identifier(amount) [57-63]
+                  GraphNodePattern [71-74]
+                    GraphElementPatternFiller [72-73]
+                      Identifier(b) [72-73]
+            GqlReturn [75-92]
+              Select [82-92]
+                SelectList [82-92]
+                  SelectColumn [82-86]
+                    PathExpression [82-86]
+                      Identifier(a) [82-83]
+                      Identifier(id) [84-86]
+                  SelectColumn [88-92]
+                    PathExpression [88-92]
+                      Identifier(b) [88-89]
+                      Identifier(id) [90-92]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#definitions_2
+[mode=expression]
+{name: 'Dana'}
+--
+BracedConstructor [0-14]
+  BracedConstructorField [1-13]
+    BracedConstructorLhs [1-5]
+      PathExpression [1-5]
+        Identifier(name) [1-5]
+    BracedConstructorFieldValue [7-13]
+      StringLiteral [7-13]
+        StringLiteralComponent('Dana') [7-13]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#definitions_2
+[mode=expression]
+{nick_name: 'Vacation Fund', is_blocked: false}
+--
+BracedConstructor [0-47]
+  BracedConstructorField [1-27]
+    BracedConstructorLhs [1-10]
+      PathExpression [1-10]
+        Identifier(nick_name) [1-10]
+    BracedConstructorFieldValue [12-27]
+      StringLiteral [12-27]
+        StringLiteralComponent('Vacation Fund') [12-27]
+  BracedConstructorField [29-46]
+    BracedConstructorLhs [29-39]
+      PathExpression [29-39]
+        Identifier(is_blocked) [29-39]
+    BracedConstructorFieldValue [41-46]
+      BooleanLiteral(false) [41-46]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_2
+GRAPH FinGraph
+MATCH (n)
+RETURN LABELS(n) AS label
+--
+QueryStatement [0-50]
+  Query [0-50]
+    GqlQuery [0-50]
+      GraphTableQuery [0-50]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-50]
+          GqlOperatorList [15-50]
+            GqlMatch [15-24]
+              GraphPattern [21-24]
+                GraphPathPattern [21-24]
+                  GraphNodePattern [21-24]
+                    GraphElementPatternFiller [22-23]
+                      Identifier(n) [22-23]
+            GqlReturn [25-50]
+              Select [32-50]
+                SelectList [32-50]
+                  SelectColumn [32-50]
+                    FunctionCall [32-41]
+                      PathExpression [32-38]
+                        Identifier(LABELS) [32-38]
+                      PathExpression [39-40]
+                        Identifier(n) [39-40]
+                    Alias [42-50]
+                      Identifier(label) [45-50]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_2
+GRAPH FinGraph
+MATCH -[e]->
+RETURN e.id
+--
+QueryStatement [0-39]
+  Query [0-39]
+    GqlQuery [0-39]
+      GraphTableQuery [0-39]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-39]
+          GqlOperatorList [15-39]
+            GqlMatch [15-27]
+              GraphPattern [21-27]
+                GraphPathPattern [21-27]
+                  GraphEdgePattern [21-27]
+                    GraphElementPatternFiller [23-24]
+                      Identifier(e) [23-24]
+            GqlReturn [28-39]
+              Select [35-39]
+                SelectList [35-39]
+                  SelectColumn [35-39]
+                    PathExpression [35-39]
+                      Identifier(e) [35-36]
+                      Identifier(id) [37-39]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_2
+GRAPH FinGraph
+MATCH (n:Person)
+RETURN n.name, n.id
+--
+QueryStatement [0-51]
+  Query [0-51]
+    GqlQuery [0-51]
+      GraphTableQuery [0-51]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-51]
+          GqlOperatorList [15-51]
+            GqlMatch [15-31]
+              GraphPattern [21-31]
+                GraphPathPattern [21-31]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+            GqlReturn [32-51]
+              Select [39-51]
+                SelectList [39-51]
+                  SelectColumn [39-45]
+                    PathExpression [39-45]
+                      Identifier(n) [39-40]
+                      Identifier(name) [41-45]
+                  SelectColumn [47-51]
+                    PathExpression [47-51]
+                      Identifier(n) [47-48]
+                      Identifier(id) [49-51]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_2
+GRAPH FinGraph
+MATCH (n:Person|Account)
+RETURN n.id, n.name, n.nick_name
+--
+QueryStatement [0-72]
+  Query [0-72]
+    GqlQuery [0-72]
+      GraphTableQuery [0-72]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-72]
+          GqlOperatorList [15-72]
+            GqlMatch [15-39]
+              GraphPattern [21-39]
+                GraphPathPattern [21-39]
+                  GraphNodePattern [21-39]
+                    GraphElementPatternFiller [22-38]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-38]
+                        GraphLabelOperation(OR) [24-38]
+                          GraphElementLabel [24-30]
+                            Identifier(Person) [24-30]
+                          GraphElementLabel [31-38]
+                            Identifier(Account) [31-38]
+            GqlReturn [40-72]
+              Select [47-72]
+                SelectList [47-72]
+                  SelectColumn [47-51]
+                    PathExpression [47-51]
+                      Identifier(n) [47-48]
+                      Identifier(id) [49-51]
+                  SelectColumn [53-59]
+                    PathExpression [53-59]
+                      Identifier(n) [53-54]
+                      Identifier(name) [55-59]
+                  SelectColumn [61-72]
+                    PathExpression [61-72]
+                      Identifier(n) [61-62]
+                      Identifier(nick_name) [63-72]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_2
+GRAPH FinGraph
+MATCH (n:Person WHERE n.birthday > '1990-01-10')
+RETURN n.name
+--
+QueryStatement [0-77]
+  Query [0-77]
+    GqlQuery [0-77]
+      GraphTableQuery [0-77]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-77]
+          GqlOperatorList [15-77]
+            GqlMatch [15-63]
+              GraphPattern [21-63]
+                GraphPathPattern [21-63]
+                  GraphNodePattern [21-63]
+                    GraphElementPatternFiller [22-62]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                      WhereClause [31-62]
+                        BinaryExpression(>) [37-62]
+                          PathExpression [37-47]
+                            Identifier(n) [37-38]
+                            Identifier(birthday) [39-47]
+                          StringLiteral [50-62]
+                            StringLiteralComponent('1990-01-10') [50-62]
+            GqlReturn [64-77]
+              Select [71-77]
+                SelectList [71-77]
+                  SelectColumn [71-77]
+                    PathExpression [71-77]
+                      Identifier(n) [71-72]
+                      Identifier(name) [73-77]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_2
+GRAPH FinGraph
+MATCH -[e:Owns WHERE e.create_time > '2020-01-14'
+                 AND e.create_time < '2020-05-14']->
+RETURN e.id
+--
+QueryStatement [0-129]
+  Query [0-129]
+    GqlQuery [0-129]
+      GraphTableQuery [0-129]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-129]
+          GqlOperatorList [15-129]
+            GqlMatch [15-117]
+              GraphPattern [21-117]
+                GraphPathPattern [21-117]
+                  GraphEdgePattern [21-117]
+                    GraphElementPatternFiller [23-114]
+                      Identifier(e) [23-24]
+                      GraphLabelFilter [24-29]
+                        GraphElementLabel [25-29]
+                          Identifier(Owns) [25-29]
+                      WhereClause [30-114]
+                        AndExpr [36-114]
+                          BinaryExpression(>) [36-64]
+                            PathExpression [36-49]
+                              Identifier(e) [36-37]
+                              Identifier(create_time) [38-49]
+                            StringLiteral [52-64]
+                              StringLiteralComponent('2020-01-14') [52-64]
+                          BinaryExpression(<) [86-114]
+                            PathExpression [86-99]
+                              Identifier(e) [86-87]
+                              Identifier(create_time) [88-99]
+                            StringLiteral [102-114]
+                              StringLiteralComponent('2020-05-14') [102-114]
+            GqlReturn [118-129]
+              Select [125-129]
+                SelectList [125-129]
+                  SelectColumn [125-129]
+                    PathExpression [125-129]
+                      Identifier(e) [125-126]
+                      Identifier(id) [127-129]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_2
+GRAPH FinGraph
+MATCH (n:Person|Account WHERE PROPERTY_EXISTS(n, name))
+RETURN n.id, n.name
+--
+QueryStatement [0-90]
+  Query [0-90]
+    GqlQuery [0-90]
+      GraphTableQuery [0-90]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-90]
+          GqlOperatorList [15-90]
+            GqlMatch [15-70]
+              GraphPattern [21-70]
+                GraphPathPattern [21-70]
+                  GraphNodePattern [21-70]
+                    GraphElementPatternFiller [22-69]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-38]
+                        GraphLabelOperation(OR) [24-38]
+                          GraphElementLabel [24-30]
+                            Identifier(Person) [24-30]
+                          GraphElementLabel [31-38]
+                            Identifier(Account) [31-38]
+                      WhereClause [39-69]
+                        FunctionCall [45-69]
+                          PathExpression [45-60]
+                            Identifier(PROPERTY_EXISTS) [45-60]
+                          PathExpression [61-62]
+                            Identifier(n) [61-62]
+                          PathExpression [64-68]
+                            Identifier(name) [64-68]
+            GqlReturn [71-90]
+              Select [78-90]
+                SelectList [78-90]
+                  SelectColumn [78-82]
+                    PathExpression [78-82]
+                      Identifier(n) [78-79]
+                      Identifier(id) [80-82]
+                  SelectColumn [84-90]
+                    PathExpression [84-90]
+                      Identifier(n) [84-85]
+                      Identifier(name) [86-90]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_2
+GRAPH FinGraph
+MATCH (a:Account {is_blocked: false, nick_name: 'Vacation Fund'})
+RETURN a.id
+--
+QueryStatement [0-92]
+  Query [0-92]
+    GqlQuery [0-92]
+      GraphTableQuery [0-92]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-92]
+          GqlOperatorList [15-92]
+            GqlMatch [15-80]
+              GraphPattern [21-80]
+                GraphPathPattern [21-80]
+                  GraphNodePattern [21-80]
+                    GraphElementPatternFiller [22-79]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                      GraphPropertySpecification [32-79]
+                        GraphPropertyNameAndValue [33-50]
+                          Identifier(is_blocked) [33-43]
+                          BooleanLiteral(false) [45-50]
+                        GraphPropertyNameAndValue [52-78]
+                          Identifier(nick_name) [52-61]
+                          StringLiteral [63-78]
+                            StringLiteralComponent('Vacation Fund') [63-78]
+            GqlReturn [81-92]
+              Select [88-92]
+                SelectList [88-92]
+                  SelectColumn [88-92]
+                    PathExpression [88-92]
+                      Identifier(a) [88-89]
+                      Identifier(id) [90-92]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_2
+GRAPH FinGraph
+MATCH (src:Account)-[transfer:Transfers]->(dst:Account)
+RETURN src.id AS src_id, transfer.amount, dst.id AS dst_id
+--
+QueryStatement [0-129]
+  Query [0-129]
+    GqlQuery [0-129]
+      GraphTableQuery [0-129]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-129]
+          GqlOperatorList [15-129]
+            GqlMatch [15-70]
+              GraphPattern [21-70]
+                GraphPathPattern [21-70]
+                  GraphNodePattern [21-34]
+                    GraphElementPatternFiller [22-33]
+                      Identifier(src) [22-25]
+                      GraphLabelFilter [25-33]
+                        GraphElementLabel [26-33]
+                          Identifier(Account) [26-33]
+                  GraphEdgePattern [34-57]
+                    GraphElementPatternFiller [36-54]
+                      Identifier(transfer) [36-44]
+                      GraphLabelFilter [44-54]
+                        GraphElementLabel [45-54]
+                          Identifier(Transfers) [45-54]
+                  GraphNodePattern [57-70]
+                    GraphElementPatternFiller [58-69]
+                      Identifier(dst) [58-61]
+                      GraphLabelFilter [61-69]
+                        GraphElementLabel [62-69]
+                          Identifier(Account) [62-69]
+            GqlReturn [71-129]
+              Select [78-129]
+                SelectList [78-129]
+                  SelectColumn [78-94]
+                    PathExpression [78-84]
+                      Identifier(src) [78-81]
+                      Identifier(id) [82-84]
+                    Alias [85-94]
+                      Identifier(src_id) [88-94]
+                  SelectColumn [96-111]
+                    PathExpression [96-111]
+                      Identifier(transfer) [96-104]
+                      Identifier(amount) [105-111]
+                  SelectColumn [113-129]
+                    PathExpression [113-119]
+                      Identifier(dst) [113-116]
+                      Identifier(id) [117-119]
+                    Alias [120-129]
+                      Identifier(dst_id) [123-129]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_2
+GRAPH FinGraph
+MATCH (src:Account)-[transfer:Transfers]-(dst:Account)
+RETURN src.id AS src_id, transfer.amount, dst.id AS dst_id
+--
+QueryStatement [0-128]
+  Query [0-128]
+    GqlQuery [0-128]
+      GraphTableQuery [0-128]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-128]
+          GqlOperatorList [15-128]
+            GqlMatch [15-69]
+              GraphPattern [21-69]
+                GraphPathPattern [21-69]
+                  GraphNodePattern [21-34]
+                    GraphElementPatternFiller [22-33]
+                      Identifier(src) [22-25]
+                      GraphLabelFilter [25-33]
+                        GraphElementLabel [26-33]
+                          Identifier(Account) [26-33]
+                  GraphEdgePattern [34-56]
+                    GraphElementPatternFiller [36-54]
+                      Identifier(transfer) [36-44]
+                      GraphLabelFilter [44-54]
+                        GraphElementLabel [45-54]
+                          Identifier(Transfers) [45-54]
+                  GraphNodePattern [56-69]
+                    GraphElementPatternFiller [57-68]
+                      Identifier(dst) [57-60]
+                      GraphLabelFilter [60-68]
+                        GraphElementLabel [61-68]
+                          Identifier(Account) [61-68]
+            GqlReturn [70-128]
+              Select [77-128]
+                SelectList [77-128]
+                  SelectColumn [77-93]
+                    PathExpression [77-83]
+                      Identifier(src) [77-80]
+                      Identifier(id) [81-83]
+                    Alias [84-93]
+                      Identifier(src_id) [87-93]
+                  SelectColumn [95-110]
+                    PathExpression [95-110]
+                      Identifier(transfer) [95-103]
+                      Identifier(amount) [104-110]
+                  SelectColumn [112-128]
+                    PathExpression [112-118]
+                      Identifier(dst) [112-115]
+                      Identifier(id) [116-118]
+                    Alias [119-128]
+                      Identifier(dst_id) [122-128]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_2
+GRAPH FinGraph
+MATCH (account:Account)<-(person:Person)
+RETURN account.id, person.name
+--
+QueryStatement [0-86]
+  Query [0-86]
+    GqlQuery [0-86]
+      GraphTableQuery [0-86]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-86]
+          GqlOperatorList [15-86]
+            GqlMatch [15-55]
+              GraphPattern [21-55]
+                GraphPathPattern [21-55]
+                  GraphNodePattern [21-38]
+                    GraphElementPatternFiller [22-37]
+                      Identifier(account) [22-29]
+                      GraphLabelFilter [29-37]
+                        GraphElementLabel [30-37]
+                          Identifier(Account) [30-37]
+                  GraphEdgePattern [38-40]
+                  GraphNodePattern [40-55]
+                    GraphElementPatternFiller [41-54]
+                      Identifier(person) [41-47]
+                      GraphLabelFilter [47-54]
+                        GraphElementLabel [48-54]
+                          Identifier(Person) [48-54]
+            GqlReturn [56-86]
+              Select [63-86]
+                SelectList [63-86]
+                  SelectColumn [63-73]
+                    PathExpression [63-73]
+                      Identifier(account) [63-70]
+                      Identifier(id) [71-73]
+                  SelectColumn [75-86]
+                    PathExpression [75-86]
+                      Identifier(person) [75-81]
+                      Identifier(name) [82-86]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_2
+GRAPH FinGraph
+MATCH (a:Account)-[t1:Transfers]->(mid:Account)-[t2:Transfers]->(a:Account)
+RETURN a.id AS a_id
+--
+QueryStatement [0-110]
+  Query [0-110]
+    GqlQuery [0-110]
+      GraphTableQuery [0-110]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-110]
+          GqlOperatorList [15-110]
+            GqlMatch [15-90]
+              GraphPattern [21-90]
+                GraphPathPattern [21-90]
+                  GraphNodePattern [21-32]
+                    GraphElementPatternFiller [22-31]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                  GraphEdgePattern [32-49]
+                    GraphElementPatternFiller [34-46]
+                      Identifier(t1) [34-36]
+                      GraphLabelFilter [36-46]
+                        GraphElementLabel [37-46]
+                          Identifier(Transfers) [37-46]
+                  GraphNodePattern [49-62]
+                    GraphElementPatternFiller [50-61]
+                      Identifier(mid) [50-53]
+                      GraphLabelFilter [53-61]
+                        GraphElementLabel [54-61]
+                          Identifier(Account) [54-61]
+                  GraphEdgePattern [62-79]
+                    GraphElementPatternFiller [64-76]
+                      Identifier(t2) [64-66]
+                      GraphLabelFilter [66-76]
+                        GraphElementLabel [67-76]
+                          Identifier(Transfers) [67-76]
+                  GraphNodePattern [79-90]
+                    GraphElementPatternFiller [80-89]
+                      Identifier(a) [80-81]
+                      GraphLabelFilter [81-89]
+                        GraphElementLabel [82-89]
+                          Identifier(Account) [82-89]
+            GqlReturn [91-110]
+              Select [98-110]
+                SelectList [98-110]
+                  SelectColumn [98-110]
+                    PathExpression [98-102]
+                      Identifier(a) [98-99]
+                      Identifier(id) [100-102]
+                    Alias [103-110]
+                      Identifier(a_id) [106-110]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_2
+GRAPH FinGraph
+MATCH (a:Account)-[t1:Transfers]->(mid:Account)-[t2:Transfers]->(a2)
+RETURN a.id AS a_id, a2.id AS a2_id
+--
+QueryStatement [0-119]
+  Query [0-119]
+    GqlQuery [0-119]
+      GraphTableQuery [0-119]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-119]
+          GqlOperatorList [15-119]
+            GqlMatch [15-83]
+              GraphPattern [21-83]
+                GraphPathPattern [21-83]
+                  GraphNodePattern [21-32]
+                    GraphElementPatternFiller [22-31]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                  GraphEdgePattern [32-49]
+                    GraphElementPatternFiller [34-46]
+                      Identifier(t1) [34-36]
+                      GraphLabelFilter [36-46]
+                        GraphElementLabel [37-46]
+                          Identifier(Transfers) [37-46]
+                  GraphNodePattern [49-62]
+                    GraphElementPatternFiller [50-61]
+                      Identifier(mid) [50-53]
+                      GraphLabelFilter [53-61]
+                        GraphElementLabel [54-61]
+                          Identifier(Account) [54-61]
+                  GraphEdgePattern [62-79]
+                    GraphElementPatternFiller [64-76]
+                      Identifier(t2) [64-66]
+                      GraphLabelFilter [66-76]
+                        GraphElementLabel [67-76]
+                          Identifier(Transfers) [67-76]
+                  GraphNodePattern [79-83]
+                    GraphElementPatternFiller [80-82]
+                      Identifier(a2) [80-82]
+            GqlReturn [84-119]
+              Select [91-119]
+                SelectList [91-119]
+                  SelectColumn [91-103]
+                    PathExpression [91-95]
+                      Identifier(a) [91-92]
+                      Identifier(id) [93-95]
+                    Alias [96-103]
+                      Identifier(a_id) [99-103]
+                  SelectColumn [105-119]
+                    PathExpression [105-110]
+                      Identifier(a2) [105-107]
+                      Identifier(id) [108-110]
+                    Alias [111-119]
+                      Identifier(a2_id) [114-119]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_2
+GRAPH FinGraph
+MATCH (a:Account)-[t1:Transfers]->(mid:Account)-[t2:Transfers]->(a2)
+WHERE a.id != a2.id
+RETURN a.id AS a_id, a2.id AS a2_id
+--
+QueryStatement [0-139]
+  Query [0-139]
+    GqlQuery [0-139]
+      GraphTableQuery [0-139]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-139]
+          GqlOperatorList [15-139]
+            GqlMatch [15-103]
+              GraphPattern [21-103]
+                GraphPathPattern [21-83]
+                  GraphNodePattern [21-32]
+                    GraphElementPatternFiller [22-31]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                  GraphEdgePattern [32-49]
+                    GraphElementPatternFiller [34-46]
+                      Identifier(t1) [34-36]
+                      GraphLabelFilter [36-46]
+                        GraphElementLabel [37-46]
+                          Identifier(Transfers) [37-46]
+                  GraphNodePattern [49-62]
+                    GraphElementPatternFiller [50-61]
+                      Identifier(mid) [50-53]
+                      GraphLabelFilter [53-61]
+                        GraphElementLabel [54-61]
+                          Identifier(Account) [54-61]
+                  GraphEdgePattern [62-79]
+                    GraphElementPatternFiller [64-76]
+                      Identifier(t2) [64-66]
+                      GraphLabelFilter [66-76]
+                        GraphElementLabel [67-76]
+                          Identifier(Transfers) [67-76]
+                  GraphNodePattern [79-83]
+                    GraphElementPatternFiller [80-82]
+                      Identifier(a2) [80-82]
+                WhereClause [84-103]
+                  BinaryExpression(!=) [90-103]
+                    PathExpression [90-94]
+                      Identifier(a) [90-91]
+                      Identifier(id) [92-94]
+                    PathExpression [98-103]
+                      Identifier(a2) [98-100]
+                      Identifier(id) [101-103]
+            GqlReturn [104-139]
+              Select [111-139]
+                SelectList [111-139]
+                  SelectColumn [111-123]
+                    PathExpression [111-115]
+                      Identifier(a) [111-112]
+                      Identifier(id) [113-115]
+                    Alias [116-123]
+                      Identifier(a_id) [119-123]
+                  SelectColumn [125-139]
+                    PathExpression [125-130]
+                      Identifier(a2) [125-127]
+                      Identifier(id) [128-130]
+                    Alias [131-139]
+                      Identifier(a2_id) [134-139]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_3
+GRAPH FinGraph
+MATCH
+  ((src:Account)-[t1:Transfers]->(mid:Account))-[t2:Transfers]->(dst:Account)
+RETURN
+  src.id AS src_account_id, mid.id AS mid_account_id, dst.id AS dst_account_id
+--
+QueryStatement [0-184]
+  Query [0-184]
+    GqlQuery [0-184]
+      GraphTableQuery [0-184]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-184]
+          GqlOperatorList [15-184]
+            GqlMatch [15-98]
+              GraphPattern [23-98]
+                GraphPathPattern [23-98]
+                  ParenthesizedGraphPathPattern [23-68]
+                    GraphNodePattern [24-37]
+                      GraphElementPatternFiller [25-36]
+                        Identifier(src) [25-28]
+                        GraphLabelFilter [28-36]
+                          GraphElementLabel [29-36]
+                            Identifier(Account) [29-36]
+                    GraphEdgePattern [37-54]
+                      GraphElementPatternFiller [39-51]
+                        Identifier(t1) [39-41]
+                        GraphLabelFilter [41-51]
+                          GraphElementLabel [42-51]
+                            Identifier(Transfers) [42-51]
+                    GraphNodePattern [54-67]
+                      GraphElementPatternFiller [55-66]
+                        Identifier(mid) [55-58]
+                        GraphLabelFilter [58-66]
+                          GraphElementLabel [59-66]
+                            Identifier(Account) [59-66]
+                  GraphEdgePattern [68-85]
+                    GraphElementPatternFiller [70-82]
+                      Identifier(t2) [70-72]
+                      GraphLabelFilter [72-82]
+                        GraphElementLabel [73-82]
+                          Identifier(Transfers) [73-82]
+                  GraphNodePattern [85-98]
+                    GraphElementPatternFiller [86-97]
+                      Identifier(dst) [86-89]
+                      GraphLabelFilter [89-97]
+                        GraphElementLabel [90-97]
+                          Identifier(Account) [90-97]
+            GqlReturn [99-184]
+              Select [108-184]
+                SelectList [108-184]
+                  SelectColumn [108-132]
+                    PathExpression [108-114]
+                      Identifier(src) [108-111]
+                      Identifier(id) [112-114]
+                    Alias [115-132]
+                      Identifier(src_account_id) [118-132]
+                  SelectColumn [134-158]
+                    PathExpression [134-140]
+                      Identifier(mid) [134-137]
+                      Identifier(id) [138-140]
+                    Alias [141-158]
+                      Identifier(mid_account_id) [144-158]
+                  SelectColumn [160-184]
+                    PathExpression [160-166]
+                      Identifier(dst) [160-163]
+                      Identifier(id) [164-166]
+                    Alias [167-184]
+                      Identifier(dst_account_id) [170-184]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_4
+GRAPH FinGraph
+MATCH (src:Account {id: 7})-[e:Transfers]->{1, 3}(dst:Account)
+WHERE src != dst
+RETURN ARRAY_LENGTH(e) AS hops, dst.id AS dst_account_id
+--
+QueryStatement [0-151]
+  Query [0-151]
+    GqlQuery [0-151]
+      GraphTableQuery [0-151]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-151]
+          GqlOperatorList [15-151]
+            GqlMatch [15-94]
+              GraphPattern [21-94]
+                GraphPathPattern [21-77]
+                  GraphNodePattern [21-42]
+                    GraphElementPatternFiller [22-41]
+                      Identifier(src) [22-25]
+                      GraphLabelFilter [25-33]
+                        GraphElementLabel [26-33]
+                          Identifier(Account) [26-33]
+                      GraphPropertySpecification [34-41]
+                        GraphPropertyNameAndValue [35-40]
+                          Identifier(id) [35-37]
+                          IntLiteral(7) [39-40]
+                  ParenthesizedGraphPathPattern [42-64]
+                    BoundedQuantifier [58-64]
+                      QuantifierBound [59-60]
+                        IntLiteral(1) [59-60]
+                      QuantifierBound [62-63]
+                        IntLiteral(3) [62-63]
+                    GraphEdgePattern [42-58]
+                      GraphElementPatternFiller [44-55]
+                        Identifier(e) [44-45]
+                        GraphLabelFilter [45-55]
+                          GraphElementLabel [46-55]
+                            Identifier(Transfers) [46-55]
+                  GraphNodePattern [64-77]
+                    GraphElementPatternFiller [65-76]
+                      Identifier(dst) [65-68]
+                      GraphLabelFilter [68-76]
+                        GraphElementLabel [69-76]
+                          Identifier(Account) [69-76]
+                WhereClause [78-94]
+                  BinaryExpression(!=) [84-94]
+                    PathExpression [84-87]
+                      Identifier(src) [84-87]
+                    PathExpression [91-94]
+                      Identifier(dst) [91-94]
+            GqlReturn [95-151]
+              Select [102-151]
+                SelectList [102-151]
+                  SelectColumn [102-125]
+                    FunctionCall [102-117]
+                      PathExpression [102-114]
+                        Identifier(ARRAY_LENGTH) [102-114]
+                      PathExpression [115-116]
+                        Identifier(e) [115-116]
+                    Alias [118-125]
+                      Identifier(hops) [121-125]
+                  SelectColumn [127-151]
+                    PathExpression [127-133]
+                      Identifier(dst) [127-130]
+                      Identifier(id) [131-133]
+                    Alias [134-151]
+                      Identifier(dst_account_id) [137-151]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_4
+GRAPH FinGraph
+MATCH
+  (src:Account)
+  ((:Account)-[:Transfers]->(mid:Account {is_blocked:true})){1,2}
+  -[:Transfers]->(dst:Account)
+RETURN src.id AS src_account_id, dst.id AS dst_account_id
+--
+QueryStatement [0-191]
+  Query [0-191]
+    GqlQuery [0-191]
+      GraphTableQuery [0-191]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-191]
+          GqlOperatorList [15-191]
+            GqlMatch [15-133]
+              GraphPattern [23-133]
+                GraphPathPattern [23-133]
+                  GraphNodePattern [23-36]
+                    GraphElementPatternFiller [24-35]
+                      Identifier(src) [24-27]
+                      GraphLabelFilter [27-35]
+                        GraphElementLabel [28-35]
+                          Identifier(Account) [28-35]
+                  ParenthesizedGraphPathPattern [39-102]
+                    BoundedQuantifier [97-102]
+                      QuantifierBound [98-99]
+                        IntLiteral(1) [98-99]
+                      QuantifierBound [100-101]
+                        IntLiteral(2) [100-101]
+                    GraphNodePattern [40-50]
+                      GraphElementPatternFiller [41-49]
+                        GraphLabelFilter [41-49]
+                          GraphElementLabel [42-49]
+                            Identifier(Account) [42-49]
+                    GraphEdgePattern [50-65]
+                      GraphElementPatternFiller [52-62]
+                        GraphLabelFilter [52-62]
+                          GraphElementLabel [53-62]
+                            Identifier(Transfers) [53-62]
+                    GraphNodePattern [65-96]
+                      GraphElementPatternFiller [66-95]
+                        Identifier(mid) [66-69]
+                        GraphLabelFilter [69-77]
+                          GraphElementLabel [70-77]
+                            Identifier(Account) [70-77]
+                        GraphPropertySpecification [78-95]
+                          GraphPropertyNameAndValue [79-94]
+                            Identifier(is_blocked) [79-89]
+                            BooleanLiteral(true) [90-94]
+                  GraphEdgePattern [105-120]
+                    GraphElementPatternFiller [107-117]
+                      GraphLabelFilter [107-117]
+                        GraphElementLabel [108-117]
+                          Identifier(Transfers) [108-117]
+                  GraphNodePattern [120-133]
+                    GraphElementPatternFiller [121-132]
+                      Identifier(dst) [121-124]
+                      GraphLabelFilter [124-132]
+                        GraphElementLabel [125-132]
+                          Identifier(Account) [125-132]
+            GqlReturn [134-191]
+              Select [141-191]
+                SelectList [141-191]
+                  SelectColumn [141-165]
+                    PathExpression [141-147]
+                      Identifier(src) [141-144]
+                      Identifier(id) [145-147]
+                    Alias [148-165]
+                      Identifier(src_account_id) [151-165]
+                  SelectColumn [167-191]
+                    PathExpression [167-173]
+                      Identifier(dst) [167-170]
+                      Identifier(id) [171-173]
+                    Alias [174-191]
+                      Identifier(dst_account_id) [177-191]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_4
+GRAPH FinGraph
+MATCH
+  (src:Account {id: 7})-[e:Transfers WHERE e.amount > 100]->{0,2}
+  (dst:Account)
+WHERE src.id != dst.id
+LET total_amount = SUM(e.amount)
+RETURN
+  src.id AS src_account_id, dst.id AS dst_account_id,
+  ARRAY_LENGTH(e) AS number_of_hops, total_amount
+--
+QueryStatement [0-269]
+  Query [0-269]
+    GqlQuery [0-269]
+      GraphTableQuery [0-269]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-269]
+          GqlOperatorList [15-269]
+            GqlMatch [15-125]
+              GraphPattern [23-125]
+                GraphPathPattern [23-102]
+                  GraphNodePattern [23-44]
+                    GraphElementPatternFiller [24-43]
+                      Identifier(src) [24-27]
+                      GraphLabelFilter [27-35]
+                        GraphElementLabel [28-35]
+                          Identifier(Account) [28-35]
+                      GraphPropertySpecification [36-43]
+                        GraphPropertyNameAndValue [37-42]
+                          Identifier(id) [37-39]
+                          IntLiteral(7) [41-42]
+                  ParenthesizedGraphPathPattern [44-86]
+                    BoundedQuantifier [81-86]
+                      QuantifierBound [82-83]
+                        IntLiteral(0) [82-83]
+                      QuantifierBound [84-85]
+                        IntLiteral(2) [84-85]
+                    GraphEdgePattern [44-81]
+                      GraphElementPatternFiller [46-78]
+                        Identifier(e) [46-47]
+                        GraphLabelFilter [47-57]
+                          GraphElementLabel [48-57]
+                            Identifier(Transfers) [48-57]
+                        WhereClause [58-78]
+                          BinaryExpression(>) [64-78]
+                            PathExpression [64-72]
+                              Identifier(e) [64-65]
+                              Identifier(amount) [66-72]
+                            IntLiteral(100) [75-78]
+                  GraphNodePattern [89-102]
+                    GraphElementPatternFiller [90-101]
+                      Identifier(dst) [90-93]
+                      GraphLabelFilter [93-101]
+                        GraphElementLabel [94-101]
+                          Identifier(Account) [94-101]
+                WhereClause [103-125]
+                  BinaryExpression(!=) [109-125]
+                    PathExpression [109-115]
+                      Identifier(src) [109-112]
+                      Identifier(id) [113-115]
+                    PathExpression [119-125]
+                      Identifier(dst) [119-122]
+                      Identifier(id) [123-125]
+            GqlLet [126-158]
+              GqlLetVariableDefinitionList [130-158]
+                GqlLetVariableDefinition [130-158]
+                  Identifier(total_amount) [130-142]
+                  FunctionCall [145-158]
+                    PathExpression [145-148]
+                      Identifier(SUM) [145-148]
+                    PathExpression [149-157]
+                      Identifier(e) [149-150]
+                      Identifier(amount) [151-157]
+            GqlReturn [159-269]
+              Select [168-269]
+                SelectList [168-269]
+                  SelectColumn [168-192]
+                    PathExpression [168-174]
+                      Identifier(src) [168-171]
+                      Identifier(id) [172-174]
+                    Alias [175-192]
+                      Identifier(src_account_id) [178-192]
+                  SelectColumn [194-218]
+                    PathExpression [194-200]
+                      Identifier(dst) [194-197]
+                      Identifier(id) [198-200]
+                    Alias [201-218]
+                      Identifier(dst_account_id) [204-218]
+                  SelectColumn [222-255]
+                    FunctionCall [222-237]
+                      PathExpression [222-234]
+                        Identifier(ARRAY_LENGTH) [222-234]
+                      PathExpression [235-236]
+                        Identifier(e) [235-236]
+                    Alias [238-255]
+                      Identifier(number_of_hops) [241-255]
+                  SelectColumn [257-269]
+                    PathExpression [257-269]
+                      Identifier(total_amount) [257-269]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_4
+GRAPH FinGraph
+MATCH TRAIL (a)->{2,}(b)
+RETURN DISTINCT a.id AS src_id, b.id AS dest_id
+ORDER BY src_id, dest_id
+--
+QueryStatement [0-112]
+  Query [0-112]
+    GqlQuery [0-112]
+      GraphTableQuery [0-112]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-112]
+          GqlOperatorList [15-112]
+            GqlMatch [15-39]
+              GraphPattern [21-39]
+                GraphPathPattern [21-39]
+                  GraphPathMode [21-26]
+                  GraphNodePattern [27-30]
+                    GraphElementPatternFiller [28-29]
+                      Identifier(a) [28-29]
+                  ParenthesizedGraphPathPattern [30-36]
+                    BoundedQuantifier [32-36]
+                      QuantifierBound [33-34]
+                        IntLiteral(2) [33-34]
+                      QuantifierBound [35-35]
+                    GraphEdgePattern [30-32]
+                  GraphNodePattern [36-39]
+                    GraphElementPatternFiller [37-38]
+                      Identifier(b) [37-38]
+            GqlReturn [40-112]
+              Select(distinct=true) [47-87]
+                SelectList [56-87]
+                  SelectColumn [56-70]
+                    PathExpression [56-60]
+                      Identifier(a) [56-57]
+                      Identifier(id) [58-60]
+                    Alias [61-70]
+                      Identifier(src_id) [64-70]
+                  SelectColumn [72-87]
+                    PathExpression [72-76]
+                      Identifier(b) [72-73]
+                      Identifier(id) [74-76]
+                    Alias [77-87]
+                      Identifier(dest_id) [80-87]
+              GqlOrderByAndPage [88-112]
+                OrderBy [88-112]
+                  OrderingExpression(ASC) [97-103]
+                    PathExpression [97-103]
+                      Identifier(src_id) [97-103]
+                  OrderingExpression(ASC) [105-112]
+                    PathExpression [105-112]
+                      Identifier(dest_id) [105-112]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_5
+GRAPH FinGraph
+MATCH (n:Person)
+RETURN n.name
+--
+QueryStatement [0-45]
+  Query [0-45]
+    GqlQuery [0-45]
+      GraphTableQuery [0-45]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-45]
+          GqlOperatorList [15-45]
+            GqlMatch [15-31]
+              GraphPattern [21-31]
+                GraphPathPattern [21-31]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+            GqlReturn [32-45]
+              Select [39-45]
+                SelectList [39-45]
+                  SelectColumn [39-45]
+                    PathExpression [39-45]
+                      Identifier(n) [39-40]
+                      Identifier(name) [41-45]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_5
+GRAPH FinGraph
+MATCH (n:Person|Account)
+RETURN n.id
+--
+QueryStatement [0-51]
+  Query [0-51]
+    GqlQuery [0-51]
+      GraphTableQuery [0-51]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-51]
+          GqlOperatorList [15-51]
+            GqlMatch [15-39]
+              GraphPattern [21-39]
+                GraphPathPattern [21-39]
+                  GraphNodePattern [21-39]
+                    GraphElementPatternFiller [22-38]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-38]
+                        GraphLabelOperation(OR) [24-38]
+                          GraphElementLabel [24-30]
+                            Identifier(Person) [24-30]
+                          GraphElementLabel [31-38]
+                            Identifier(Account) [31-38]
+            GqlReturn [40-51]
+              Select [47-51]
+                SelectList [47-51]
+                  SelectColumn [47-51]
+                    PathExpression [47-51]
+                      Identifier(n) [47-48]
+                      Identifier(id) [49-51]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_6
+GRAPH FinGraph
+MATCH ANY SHORTEST (a:Account {is_blocked:true})-[t:Transfers]->{1, 4} (b:Account)
+LET path_length = COUNT(t)
+RETURN a.id AS a_id, path_length, b.id AS b_id
+--
+QueryStatement [0-171]
+  Query [0-171]
+    GqlQuery [0-171]
+      GraphTableQuery [0-171]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-171]
+          GqlOperatorList [15-171]
+            GqlMatch [15-97]
+              GraphPattern [21-97]
+                GraphPathPattern [21-97]
+                  GraphPathSearchPrefix [21-33]
+                  GraphNodePattern [34-63]
+                    GraphElementPatternFiller [35-62]
+                      Identifier(a) [35-36]
+                      GraphLabelFilter [36-44]
+                        GraphElementLabel [37-44]
+                          Identifier(Account) [37-44]
+                      GraphPropertySpecification [45-62]
+                        GraphPropertyNameAndValue [46-61]
+                          Identifier(is_blocked) [46-56]
+                          BooleanLiteral(true) [57-61]
+                  ParenthesizedGraphPathPattern [63-85]
+                    BoundedQuantifier [79-85]
+                      QuantifierBound [80-81]
+                        IntLiteral(1) [80-81]
+                      QuantifierBound [83-84]
+                        IntLiteral(4) [83-84]
+                    GraphEdgePattern [63-79]
+                      GraphElementPatternFiller [65-76]
+                        Identifier(t) [65-66]
+                        GraphLabelFilter [66-76]
+                          GraphElementLabel [67-76]
+                            Identifier(Transfers) [67-76]
+                  GraphNodePattern [86-97]
+                    GraphElementPatternFiller [87-96]
+                      Identifier(b) [87-88]
+                      GraphLabelFilter [88-96]
+                        GraphElementLabel [89-96]
+                          Identifier(Account) [89-96]
+            GqlLet [98-124]
+              GqlLetVariableDefinitionList [102-124]
+                GqlLetVariableDefinition [102-124]
+                  Identifier(path_length) [102-113]
+                  FunctionCall [116-124]
+                    PathExpression [116-121]
+                      Identifier(COUNT) [116-121]
+                    PathExpression [122-123]
+                      Identifier(t) [122-123]
+            GqlReturn [125-171]
+              Select [132-171]
+                SelectList [132-171]
+                  SelectColumn [132-144]
+                    PathExpression [132-136]
+                      Identifier(a) [132-133]
+                      Identifier(id) [134-136]
+                    Alias [137-144]
+                      Identifier(a_id) [140-144]
+                  SelectColumn [146-157]
+                    PathExpression [146-157]
+                      Identifier(path_length) [146-157]
+                  SelectColumn [159-171]
+                    PathExpression [159-163]
+                      Identifier(b) [159-160]
+                      Identifier(id) [161-163]
+                    Alias [164-171]
+                      Identifier(b_id) [167-171]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_6
+GRAPH FinGraph
+MATCH ANY (a:Account {is_blocked: true})->(mid:Account)->(b:Account)
+RETURN a.id AS a_id, mid.id AS mid_id, b.id AS b_id
+--
+QueryStatement [0-135]
+  Query [0-135]
+    GqlQuery [0-135]
+      GraphTableQuery [0-135]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-135]
+          GqlOperatorList [15-135]
+            GqlMatch [15-83]
+              GraphPattern [21-83]
+                GraphPathPattern [21-83]
+                  GraphPathSearchPrefix [21-24]
+                  GraphNodePattern [25-55]
+                    GraphElementPatternFiller [26-54]
+                      Identifier(a) [26-27]
+                      GraphLabelFilter [27-35]
+                        GraphElementLabel [28-35]
+                          Identifier(Account) [28-35]
+                      GraphPropertySpecification [36-54]
+                        GraphPropertyNameAndValue [37-53]
+                          Identifier(is_blocked) [37-47]
+                          BooleanLiteral(true) [49-53]
+                  GraphEdgePattern [55-57]
+                  GraphNodePattern [57-70]
+                    GraphElementPatternFiller [58-69]
+                      Identifier(mid) [58-61]
+                      GraphLabelFilter [61-69]
+                        GraphElementLabel [62-69]
+                          Identifier(Account) [62-69]
+                  GraphEdgePattern [70-72]
+                  GraphNodePattern [72-83]
+                    GraphElementPatternFiller [73-82]
+                      Identifier(b) [73-74]
+                      GraphLabelFilter [74-82]
+                        GraphElementLabel [75-82]
+                          Identifier(Account) [75-82]
+            GqlReturn [84-135]
+              Select [91-135]
+                SelectList [91-135]
+                  SelectColumn [91-103]
+                    PathExpression [91-95]
+                      Identifier(a) [91-92]
+                      Identifier(id) [93-95]
+                    Alias [96-103]
+                      Identifier(a_id) [99-103]
+                  SelectColumn [105-121]
+                    PathExpression [105-111]
+                      Identifier(mid) [105-108]
+                      Identifier(id) [109-111]
+                    Alias [112-121]
+                      Identifier(mid_id) [115-121]
+                  SelectColumn [123-135]
+                    PathExpression [123-127]
+                      Identifier(b) [123-124]
+                      Identifier(id) [125-127]
+                    Alias [128-135]
+                      Identifier(b_id) [131-135]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_6
+GRAPH FinGraph
+MATCH ALL (a:Account {id: 20})-[t:Transfers]->(b:Account)
+RETURN a.id AS a_id, t.amount, b.id AS b_id
+
+-- Equivalent:
+--
+QueryStatement [0-116]
+  Query [0-116]
+    GqlQuery [0-116]
+      GraphTableQuery [0-116]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-116]
+          GqlOperatorList [15-116]
+            GqlMatch [15-72]
+              GraphPattern [21-72]
+                GraphPathPattern [21-72]
+                  GraphPathSearchPrefix [21-24]
+                  GraphNodePattern [25-45]
+                    GraphElementPatternFiller [26-44]
+                      Identifier(a) [26-27]
+                      GraphLabelFilter [27-35]
+                        GraphElementLabel [28-35]
+                          Identifier(Account) [28-35]
+                      GraphPropertySpecification [36-44]
+                        GraphPropertyNameAndValue [37-43]
+                          Identifier(id) [37-39]
+                          IntLiteral(20) [41-43]
+                  GraphEdgePattern [45-61]
+                    GraphElementPatternFiller [47-58]
+                      Identifier(t) [47-48]
+                      GraphLabelFilter [48-58]
+                        GraphElementLabel [49-58]
+                          Identifier(Transfers) [49-58]
+                  GraphNodePattern [61-72]
+                    GraphElementPatternFiller [62-71]
+                      Identifier(b) [62-63]
+                      GraphLabelFilter [63-71]
+                        GraphElementLabel [64-71]
+                          Identifier(Account) [64-71]
+            GqlReturn [73-116]
+              Select [80-116]
+                SelectList [80-116]
+                  SelectColumn [80-92]
+                    PathExpression [80-84]
+                      Identifier(a) [80-81]
+                      Identifier(id) [82-84]
+                    Alias [85-92]
+                      Identifier(a_id) [88-92]
+                  SelectColumn [94-102]
+                    PathExpression [94-102]
+                      Identifier(t) [94-95]
+                      Identifier(amount) [96-102]
+                  SelectColumn [104-116]
+                    PathExpression [104-108]
+                      Identifier(b) [104-105]
+                      Identifier(id) [106-108]
+                    Alias [109-116]
+                      Identifier(b_id) [112-116]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_6
+GRAPH FinGraph
+MATCH (a:Account {id: 20})-[t:Transfers]->(b:Account)
+RETURN a.id AS a_id, t.amount, b.id AS b_id
+--
+QueryStatement [0-112]
+  Query [0-112]
+    GqlQuery [0-112]
+      GraphTableQuery [0-112]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-112]
+          GqlOperatorList [15-112]
+            GqlMatch [15-68]
+              GraphPattern [21-68]
+                GraphPathPattern [21-68]
+                  GraphNodePattern [21-41]
+                    GraphElementPatternFiller [22-40]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                      GraphPropertySpecification [32-40]
+                        GraphPropertyNameAndValue [33-39]
+                          Identifier(id) [33-35]
+                          IntLiteral(20) [37-39]
+                  GraphEdgePattern [41-57]
+                    GraphElementPatternFiller [43-54]
+                      Identifier(t) [43-44]
+                      GraphLabelFilter [44-54]
+                        GraphElementLabel [45-54]
+                          Identifier(Transfers) [45-54]
+                  GraphNodePattern [57-68]
+                    GraphElementPatternFiller [58-67]
+                      Identifier(b) [58-59]
+                      GraphLabelFilter [59-67]
+                        GraphElementLabel [60-67]
+                          Identifier(Account) [60-67]
+            GqlReturn [69-112]
+              Select [76-112]
+                SelectList [76-112]
+                  SelectColumn [76-88]
+                    PathExpression [76-80]
+                      Identifier(a) [76-77]
+                      Identifier(id) [78-80]
+                    Alias [81-88]
+                      Identifier(a_id) [84-88]
+                  SelectColumn [90-98]
+                    PathExpression [90-98]
+                      Identifier(t) [90-91]
+                      Identifier(amount) [92-98]
+                  SelectColumn [100-112]
+                    PathExpression [100-104]
+                      Identifier(b) [100-101]
+                      Identifier(id) [102-104]
+                    Alias [105-112]
+                      Identifier(b_id) [108-112]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_6
+GRAPH FinGraph
+MATCH ANY (a:Account {id: 20})->(mid:Account)->(a:Account)
+MATCH ALL (p:Person)->(mid)
+RETURN p.name, mid.id
+--
+QueryStatement [0-123]
+  Query [0-123]
+    GqlQuery [0-123]
+      GraphTableQuery [0-123]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-123]
+          GqlOperatorList [15-123]
+            GqlMatch [15-73]
+              GraphPattern [21-73]
+                GraphPathPattern [21-73]
+                  GraphPathSearchPrefix [21-24]
+                  GraphNodePattern [25-45]
+                    GraphElementPatternFiller [26-44]
+                      Identifier(a) [26-27]
+                      GraphLabelFilter [27-35]
+                        GraphElementLabel [28-35]
+                          Identifier(Account) [28-35]
+                      GraphPropertySpecification [36-44]
+                        GraphPropertyNameAndValue [37-43]
+                          Identifier(id) [37-39]
+                          IntLiteral(20) [41-43]
+                  GraphEdgePattern [45-47]
+                  GraphNodePattern [47-60]
+                    GraphElementPatternFiller [48-59]
+                      Identifier(mid) [48-51]
+                      GraphLabelFilter [51-59]
+                        GraphElementLabel [52-59]
+                          Identifier(Account) [52-59]
+                  GraphEdgePattern [60-62]
+                  GraphNodePattern [62-73]
+                    GraphElementPatternFiller [63-72]
+                      Identifier(a) [63-64]
+                      GraphLabelFilter [64-72]
+                        GraphElementLabel [65-72]
+                          Identifier(Account) [65-72]
+            GqlMatch [74-101]
+              GraphPattern [80-101]
+                GraphPathPattern [80-101]
+                  GraphPathSearchPrefix [80-83]
+                  GraphNodePattern [84-94]
+                    GraphElementPatternFiller [85-93]
+                      Identifier(p) [85-86]
+                      GraphLabelFilter [86-93]
+                        GraphElementLabel [87-93]
+                          Identifier(Person) [87-93]
+                  GraphEdgePattern [94-96]
+                  GraphNodePattern [96-101]
+                    GraphElementPatternFiller [97-100]
+                      Identifier(mid) [97-100]
+            GqlReturn [102-123]
+              Select [109-123]
+                SelectList [109-123]
+                  SelectColumn [109-115]
+                    PathExpression [109-115]
+                      Identifier(p) [109-110]
+                      Identifier(name) [111-115]
+                  SelectColumn [117-123]
+                    PathExpression [117-123]
+                      Identifier(mid) [117-120]
+                      Identifier(id) [121-123]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_6
+-- Error
+GRAPH FinGraph
+MATCH
+  ANY (a:Account {id: 20})->(mid:Account)->(a:Account)->(mid:Account)->(a:Account),
+  ALL (p:Person)->(mid)
+RETURN p.name
+--
+QueryStatement [9-151]
+  Query [9-151]
+    GqlQuery [9-151]
+      GraphTableQuery [9-151]
+        PathExpression [15-23]
+          Identifier(FinGraph) [15-23]
+        GqlOperatorList [24-151]
+          GqlOperatorList [24-151]
+            GqlMatch [24-137]
+              GraphPattern [32-137]
+                GraphPathPattern [32-112]
+                  GraphPathSearchPrefix [32-35]
+                  GraphNodePattern [36-56]
+                    GraphElementPatternFiller [37-55]
+                      Identifier(a) [37-38]
+                      GraphLabelFilter [38-46]
+                        GraphElementLabel [39-46]
+                          Identifier(Account) [39-46]
+                      GraphPropertySpecification [47-55]
+                        GraphPropertyNameAndValue [48-54]
+                          Identifier(id) [48-50]
+                          IntLiteral(20) [52-54]
+                  GraphEdgePattern [56-58]
+                  GraphNodePattern [58-71]
+                    GraphElementPatternFiller [59-70]
+                      Identifier(mid) [59-62]
+                      GraphLabelFilter [62-70]
+                        GraphElementLabel [63-70]
+                          Identifier(Account) [63-70]
+                  GraphEdgePattern [71-73]
+                  GraphNodePattern [73-84]
+                    GraphElementPatternFiller [74-83]
+                      Identifier(a) [74-75]
+                      GraphLabelFilter [75-83]
+                        GraphElementLabel [76-83]
+                          Identifier(Account) [76-83]
+                  GraphEdgePattern [84-86]
+                  GraphNodePattern [86-99]
+                    GraphElementPatternFiller [87-98]
+                      Identifier(mid) [87-90]
+                      GraphLabelFilter [90-98]
+                        GraphElementLabel [91-98]
+                          Identifier(Account) [91-98]
+                  GraphEdgePattern [99-101]
+                  GraphNodePattern [101-112]
+                    GraphElementPatternFiller [102-111]
+                      Identifier(a) [102-103]
+                      GraphLabelFilter [103-111]
+                        GraphElementLabel [104-111]
+                          Identifier(Account) [104-111]
+                GraphPathPattern [116-137]
+                  GraphPathSearchPrefix [116-119]
+                  GraphNodePattern [120-130]
+                    GraphElementPatternFiller [121-129]
+                      Identifier(p) [121-122]
+                      GraphLabelFilter [122-129]
+                        GraphElementLabel [123-129]
+                          Identifier(Person) [123-129]
+                  GraphEdgePattern [130-132]
+                  GraphNodePattern [132-137]
+                    GraphElementPatternFiller [133-136]
+                      Identifier(mid) [133-136]
+            GqlReturn [138-151]
+              Select [145-151]
+                SelectList [145-151]
+                  SelectColumn [145-151]
+                    PathExpression [145-151]
+                      Identifier(p) [145-146]
+                      Identifier(name) [147-151]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_6
+-- Succeeds
+GRAPH FinGraph
+MATCH
+  ANY (a:Account {id: 20})->(mid:Account)->(a:Account)->(mid:Account)->(a:Account),
+  ALL (p:Person)->(a)
+RETURN p.name
+--
+QueryStatement [12-152]
+  Query [12-152]
+    GqlQuery [12-152]
+      GraphTableQuery [12-152]
+        PathExpression [18-26]
+          Identifier(FinGraph) [18-26]
+        GqlOperatorList [27-152]
+          GqlOperatorList [27-152]
+            GqlMatch [27-138]
+              GraphPattern [35-138]
+                GraphPathPattern [35-115]
+                  GraphPathSearchPrefix [35-38]
+                  GraphNodePattern [39-59]
+                    GraphElementPatternFiller [40-58]
+                      Identifier(a) [40-41]
+                      GraphLabelFilter [41-49]
+                        GraphElementLabel [42-49]
+                          Identifier(Account) [42-49]
+                      GraphPropertySpecification [50-58]
+                        GraphPropertyNameAndValue [51-57]
+                          Identifier(id) [51-53]
+                          IntLiteral(20) [55-57]
+                  GraphEdgePattern [59-61]
+                  GraphNodePattern [61-74]
+                    GraphElementPatternFiller [62-73]
+                      Identifier(mid) [62-65]
+                      GraphLabelFilter [65-73]
+                        GraphElementLabel [66-73]
+                          Identifier(Account) [66-73]
+                  GraphEdgePattern [74-76]
+                  GraphNodePattern [76-87]
+                    GraphElementPatternFiller [77-86]
+                      Identifier(a) [77-78]
+                      GraphLabelFilter [78-86]
+                        GraphElementLabel [79-86]
+                          Identifier(Account) [79-86]
+                  GraphEdgePattern [87-89]
+                  GraphNodePattern [89-102]
+                    GraphElementPatternFiller [90-101]
+                      Identifier(mid) [90-93]
+                      GraphLabelFilter [93-101]
+                        GraphElementLabel [94-101]
+                          Identifier(Account) [94-101]
+                  GraphEdgePattern [102-104]
+                  GraphNodePattern [104-115]
+                    GraphElementPatternFiller [105-114]
+                      Identifier(a) [105-106]
+                      GraphLabelFilter [106-114]
+                        GraphElementLabel [107-114]
+                          Identifier(Account) [107-114]
+                GraphPathPattern [119-138]
+                  GraphPathSearchPrefix [119-122]
+                  GraphNodePattern [123-133]
+                    GraphElementPatternFiller [124-132]
+                      Identifier(p) [124-125]
+                      GraphLabelFilter [125-132]
+                        GraphElementLabel [126-132]
+                          Identifier(Person) [126-132]
+                  GraphEdgePattern [133-135]
+                  GraphNodePattern [135-138]
+                    GraphElementPatternFiller [136-137]
+                      Identifier(a) [136-137]
+            GqlReturn [139-152]
+              Select [146-152]
+                SelectList [146-152]
+                  SelectColumn [146-152]
+                    PathExpression [146-152]
+                      Identifier(p) [146-147]
+                      Identifier(name) [148-152]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_6
+-- Succeeds
+GRAPH FinGraph
+MATCH ANY (a:Account {id: 20})->(mid:Account)->(a:Account)->(mid:Account)->(a:Account)
+MATCH ALL (p:Person)->(mid)
+RETURN p.name
+--
+QueryStatement [12-155]
+  Query [12-155]
+    GqlQuery [12-155]
+      GraphTableQuery [12-155]
+        PathExpression [18-26]
+          Identifier(FinGraph) [18-26]
+        GqlOperatorList [27-155]
+          GqlOperatorList [27-155]
+            GqlMatch [27-113]
+              GraphPattern [33-113]
+                GraphPathPattern [33-113]
+                  GraphPathSearchPrefix [33-36]
+                  GraphNodePattern [37-57]
+                    GraphElementPatternFiller [38-56]
+                      Identifier(a) [38-39]
+                      GraphLabelFilter [39-47]
+                        GraphElementLabel [40-47]
+                          Identifier(Account) [40-47]
+                      GraphPropertySpecification [48-56]
+                        GraphPropertyNameAndValue [49-55]
+                          Identifier(id) [49-51]
+                          IntLiteral(20) [53-55]
+                  GraphEdgePattern [57-59]
+                  GraphNodePattern [59-72]
+                    GraphElementPatternFiller [60-71]
+                      Identifier(mid) [60-63]
+                      GraphLabelFilter [63-71]
+                        GraphElementLabel [64-71]
+                          Identifier(Account) [64-71]
+                  GraphEdgePattern [72-74]
+                  GraphNodePattern [74-85]
+                    GraphElementPatternFiller [75-84]
+                      Identifier(a) [75-76]
+                      GraphLabelFilter [76-84]
+                        GraphElementLabel [77-84]
+                          Identifier(Account) [77-84]
+                  GraphEdgePattern [85-87]
+                  GraphNodePattern [87-100]
+                    GraphElementPatternFiller [88-99]
+                      Identifier(mid) [88-91]
+                      GraphLabelFilter [91-99]
+                        GraphElementLabel [92-99]
+                          Identifier(Account) [92-99]
+                  GraphEdgePattern [100-102]
+                  GraphNodePattern [102-113]
+                    GraphElementPatternFiller [103-112]
+                      Identifier(a) [103-104]
+                      GraphLabelFilter [104-112]
+                        GraphElementLabel [105-112]
+                          Identifier(Account) [105-112]
+            GqlMatch [114-141]
+              GraphPattern [120-141]
+                GraphPathPattern [120-141]
+                  GraphPathSearchPrefix [120-123]
+                  GraphNodePattern [124-134]
+                    GraphElementPatternFiller [125-133]
+                      Identifier(p) [125-126]
+                      GraphLabelFilter [126-133]
+                        GraphElementLabel [127-133]
+                          Identifier(Person) [127-133]
+                  GraphEdgePattern [134-136]
+                  GraphNodePattern [136-141]
+                    GraphElementPatternFiller [137-140]
+                      Identifier(mid) [137-140]
+            GqlReturn [142-155]
+              Select [149-155]
+                SelectList [149-155]
+                  SelectColumn [149-155]
+                    PathExpression [149-155]
+                      Identifier(p) [149-150]
+                      Identifier(name) [151-155]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_6
+-- Error
+GRAPH FinGraph
+MATCH ANY ((p:Person)->(f:Person)){1, 3},
+      ALL ->(p)->
+RETURN p.name
+--
+QueryStatement [9-97]
+  Query [9-97]
+    GqlQuery [9-97]
+      GraphTableQuery [9-97]
+        PathExpression [15-23]
+          Identifier(FinGraph) [15-23]
+        GqlOperatorList [24-97]
+          GqlOperatorList [24-97]
+            GqlMatch [24-83]
+              GraphPattern [30-83]
+                GraphPathPattern [30-64]
+                  GraphPathSearchPrefix [30-33]
+                  ParenthesizedGraphPathPattern [34-64]
+                    BoundedQuantifier [58-64]
+                      QuantifierBound [59-60]
+                        IntLiteral(1) [59-60]
+                      QuantifierBound [62-63]
+                        IntLiteral(3) [62-63]
+                    GraphNodePattern [35-45]
+                      GraphElementPatternFiller [36-44]
+                        Identifier(p) [36-37]
+                        GraphLabelFilter [37-44]
+                          GraphElementLabel [38-44]
+                            Identifier(Person) [38-44]
+                    GraphEdgePattern [45-47]
+                    GraphNodePattern [47-57]
+                      GraphElementPatternFiller [48-56]
+                        Identifier(f) [48-49]
+                        GraphLabelFilter [49-56]
+                          GraphElementLabel [50-56]
+                            Identifier(Person) [50-56]
+                GraphPathPattern [72-83]
+                  GraphPathSearchPrefix [72-75]
+                  GraphEdgePattern [76-78]
+                  GraphNodePattern [78-81]
+                    GraphElementPatternFiller [79-80]
+                      Identifier(p) [79-80]
+                  GraphEdgePattern [81-83]
+            GqlReturn [84-97]
+              Select [91-97]
+                SelectList [91-97]
+                  SelectColumn [91-97]
+                    PathExpression [91-97]
+                      Identifier(p) [91-92]
+                      Identifier(name) [93-97]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_6
+-- Error
+GRAPH FinGraph
+MATCH ANY ((p:Person)->(f:Person)){1, 3},
+      ALL ((p)->){1, 3}
+RETURN p.name
+--
+QueryStatement [9-103]
+  Query [9-103]
+    GqlQuery [9-103]
+      GraphTableQuery [9-103]
+        PathExpression [15-23]
+          Identifier(FinGraph) [15-23]
+        GqlOperatorList [24-103]
+          GqlOperatorList [24-103]
+            GqlMatch [24-89]
+              GraphPattern [30-89]
+                GraphPathPattern [30-64]
+                  GraphPathSearchPrefix [30-33]
+                  ParenthesizedGraphPathPattern [34-64]
+                    BoundedQuantifier [58-64]
+                      QuantifierBound [59-60]
+                        IntLiteral(1) [59-60]
+                      QuantifierBound [62-63]
+                        IntLiteral(3) [62-63]
+                    GraphNodePattern [35-45]
+                      GraphElementPatternFiller [36-44]
+                        Identifier(p) [36-37]
+                        GraphLabelFilter [37-44]
+                          GraphElementLabel [38-44]
+                            Identifier(Person) [38-44]
+                    GraphEdgePattern [45-47]
+                    GraphNodePattern [47-57]
+                      GraphElementPatternFiller [48-56]
+                        Identifier(f) [48-49]
+                        GraphLabelFilter [49-56]
+                          GraphElementLabel [50-56]
+                            Identifier(Person) [50-56]
+                GraphPathPattern [72-89]
+                  GraphPathSearchPrefix [72-75]
+                  ParenthesizedGraphPathPattern [76-89]
+                    BoundedQuantifier [83-89]
+                      QuantifierBound [84-85]
+                        IntLiteral(1) [84-85]
+                      QuantifierBound [87-88]
+                        IntLiteral(3) [87-88]
+                    GraphNodePattern [77-80]
+                      GraphElementPatternFiller [78-79]
+                        Identifier(p) [78-79]
+                    GraphEdgePattern [80-82]
+            GqlReturn [90-103]
+              Select [97-103]
+                SelectList [97-103]
+                  SelectColumn [97-103]
+                    PathExpression [97-103]
+                      Identifier(p) [97-98]
+                      Identifier(name) [99-103]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_6
+GRAPH FinGraph
+MATCH ANY CHEAPEST (a:Account)-[t:Transfers COST t.amount]->{1,3}(b:Account)
+LET total_cost = sum(t.amount)
+RETURN a.id AS a_id, b.id AS b_id, total_cost
+--
+QueryStatement [0-168]
+  Query [0-168]
+    GqlQuery [0-168]
+      GraphTableQuery [0-168]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-168]
+          GqlOperatorList [15-168]
+            GqlMatch [15-91]
+              GraphPattern [21-91]
+                GraphPathPattern [21-91]
+                  GraphPathSearchPrefix [21-33]
+                  GraphNodePattern [34-45]
+                    GraphElementPatternFiller [35-44]
+                      Identifier(a) [35-36]
+                      GraphLabelFilter [36-44]
+                        GraphElementLabel [37-44]
+                          Identifier(Account) [37-44]
+                  ParenthesizedGraphPathPattern [45-80]
+                    BoundedQuantifier [75-80]
+                      QuantifierBound [76-77]
+                        IntLiteral(1) [76-77]
+                      QuantifierBound [78-79]
+                        IntLiteral(3) [78-79]
+                    GraphEdgePattern [45-75]
+                      GraphElementPatternFiller [47-72]
+                        Identifier(t) [47-48]
+                        GraphLabelFilter [48-58]
+                          GraphElementLabel [49-58]
+                            Identifier(Transfers) [49-58]
+                        PathExpression [64-72]
+                          Identifier(t) [64-65]
+                          Identifier(amount) [66-72]
+                  GraphNodePattern [80-91]
+                    GraphElementPatternFiller [81-90]
+                      Identifier(b) [81-82]
+                      GraphLabelFilter [82-90]
+                        GraphElementLabel [83-90]
+                          Identifier(Account) [83-90]
+            GqlLet [92-122]
+              GqlLetVariableDefinitionList [96-122]
+                GqlLetVariableDefinition [96-122]
+                  Identifier(total_cost) [96-106]
+                  FunctionCall [109-122]
+                    PathExpression [109-112]
+                      Identifier(sum) [109-112]
+                    PathExpression [113-121]
+                      Identifier(t) [113-114]
+                      Identifier(amount) [115-121]
+            GqlReturn [123-168]
+              Select [130-168]
+                SelectList [130-168]
+                  SelectColumn [130-142]
+                    PathExpression [130-134]
+                      Identifier(a) [130-131]
+                      Identifier(id) [132-134]
+                    Alias [135-142]
+                      Identifier(a_id) [138-142]
+                  SelectColumn [144-156]
+                    PathExpression [144-148]
+                      Identifier(b) [144-145]
+                      Identifier(id) [146-148]
+                    Alias [149-156]
+                      Identifier(b_id) [152-156]
+                  SelectColumn [158-168]
+                    PathExpression [158-168]
+                      Identifier(total_cost) [158-168]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_7
+GRAPH FinGraph
+MATCH
+  WALK (a1:Account)-[t1:Transfers]->(a2:Account)-[t2:Transfers]->
+  (a3:Account)-[t3:Transfers]->(a4:Account)
+WHERE a1.id < a4.id
+RETURN
+  t1.id as transfer1_id, t2.id as transfer2_id, t3.id as transfer3_id
+--
+QueryStatement [0-227]
+  Query [0-227]
+    GqlQuery [0-227]
+      GraphTableQuery [0-227]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-227]
+          GqlOperatorList [15-227]
+            GqlMatch [15-150]
+              GraphPattern [23-150]
+                GraphPathPattern [23-130]
+                  GraphPathMode [23-27]
+                  GraphNodePattern [28-40]
+                    GraphElementPatternFiller [29-39]
+                      Identifier(a1) [29-31]
+                      GraphLabelFilter [31-39]
+                        GraphElementLabel [32-39]
+                          Identifier(Account) [32-39]
+                  GraphEdgePattern [40-57]
+                    GraphElementPatternFiller [42-54]
+                      Identifier(t1) [42-44]
+                      GraphLabelFilter [44-54]
+                        GraphElementLabel [45-54]
+                          Identifier(Transfers) [45-54]
+                  GraphNodePattern [57-69]
+                    GraphElementPatternFiller [58-68]
+                      Identifier(a2) [58-60]
+                      GraphLabelFilter [60-68]
+                        GraphElementLabel [61-68]
+                          Identifier(Account) [61-68]
+                  GraphEdgePattern [69-86]
+                    GraphElementPatternFiller [71-83]
+                      Identifier(t2) [71-73]
+                      GraphLabelFilter [73-83]
+                        GraphElementLabel [74-83]
+                          Identifier(Transfers) [74-83]
+                  GraphNodePattern [89-101]
+                    GraphElementPatternFiller [90-100]
+                      Identifier(a3) [90-92]
+                      GraphLabelFilter [92-100]
+                        GraphElementLabel [93-100]
+                          Identifier(Account) [93-100]
+                  GraphEdgePattern [101-118]
+                    GraphElementPatternFiller [103-115]
+                      Identifier(t3) [103-105]
+                      GraphLabelFilter [105-115]
+                        GraphElementLabel [106-115]
+                          Identifier(Transfers) [106-115]
+                  GraphNodePattern [118-130]
+                    GraphElementPatternFiller [119-129]
+                      Identifier(a4) [119-121]
+                      GraphLabelFilter [121-129]
+                        GraphElementLabel [122-129]
+                          Identifier(Account) [122-129]
+                WhereClause [131-150]
+                  BinaryExpression(<) [137-150]
+                    PathExpression [137-142]
+                      Identifier(a1) [137-139]
+                      Identifier(id) [140-142]
+                    PathExpression [145-150]
+                      Identifier(a4) [145-147]
+                      Identifier(id) [148-150]
+            GqlReturn [151-227]
+              Select [160-227]
+                SelectList [160-227]
+                  SelectColumn [160-181]
+                    PathExpression [160-165]
+                      Identifier(t1) [160-162]
+                      Identifier(id) [163-165]
+                    Alias [166-181]
+                      Identifier(transfer1_id) [169-181]
+                  SelectColumn [183-204]
+                    PathExpression [183-188]
+                      Identifier(t2) [183-185]
+                      Identifier(id) [186-188]
+                    Alias [189-204]
+                      Identifier(transfer2_id) [192-204]
+                  SelectColumn [206-227]
+                    PathExpression [206-211]
+                      Identifier(t3) [206-208]
+                      Identifier(id) [209-211]
+                    Alias [212-227]
+                      Identifier(transfer3_id) [215-227]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_7
+GRAPH FinGraph
+MATCH
+  ACYCLIC (a1:Account)-[t1:Transfers]->(a2:Account)-[t2:Transfers]->
+  (a3:Account)
+RETURN
+  a1.id as account1_id, a2.id as account2_id,
+  a3.id as account3_id
+--
+QueryStatement [0-180]
+  Query [0-180]
+    GqlQuery [0-180]
+      GraphTableQuery [0-180]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-180]
+          GqlOperatorList [15-180]
+            GqlMatch [15-104]
+              GraphPattern [23-104]
+                GraphPathPattern [23-104]
+                  GraphPathMode [23-30]
+                  GraphNodePattern [31-43]
+                    GraphElementPatternFiller [32-42]
+                      Identifier(a1) [32-34]
+                      GraphLabelFilter [34-42]
+                        GraphElementLabel [35-42]
+                          Identifier(Account) [35-42]
+                  GraphEdgePattern [43-60]
+                    GraphElementPatternFiller [45-57]
+                      Identifier(t1) [45-47]
+                      GraphLabelFilter [47-57]
+                        GraphElementLabel [48-57]
+                          Identifier(Transfers) [48-57]
+                  GraphNodePattern [60-72]
+                    GraphElementPatternFiller [61-71]
+                      Identifier(a2) [61-63]
+                      GraphLabelFilter [63-71]
+                        GraphElementLabel [64-71]
+                          Identifier(Account) [64-71]
+                  GraphEdgePattern [72-89]
+                    GraphElementPatternFiller [74-86]
+                      Identifier(t2) [74-76]
+                      GraphLabelFilter [76-86]
+                        GraphElementLabel [77-86]
+                          Identifier(Transfers) [77-86]
+                  GraphNodePattern [92-104]
+                    GraphElementPatternFiller [93-103]
+                      Identifier(a3) [93-95]
+                      GraphLabelFilter [95-103]
+                        GraphElementLabel [96-103]
+                          Identifier(Account) [96-103]
+            GqlReturn [105-180]
+              Select [114-180]
+                SelectList [114-180]
+                  SelectColumn [114-134]
+                    PathExpression [114-119]
+                      Identifier(a1) [114-116]
+                      Identifier(id) [117-119]
+                    Alias [120-134]
+                      Identifier(account1_id) [123-134]
+                  SelectColumn [136-156]
+                    PathExpression [136-141]
+                      Identifier(a2) [136-138]
+                      Identifier(id) [139-141]
+                    Alias [142-156]
+                      Identifier(account2_id) [145-156]
+                  SelectColumn [160-180]
+                    PathExpression [160-165]
+                      Identifier(a3) [160-162]
+                      Identifier(id) [163-165]
+                    Alias [166-180]
+                      Identifier(account3_id) [169-180]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_7
+GRAPH FinGraph
+MATCH
+  TRAIL (a1:Account)-[t1:Transfers]->(a2:Account)-[t2:Transfers]->
+  (a3:Account)-[t3:Transfers]->(a4:Account)
+WHERE a1.id < a4.id
+RETURN
+  t1.id as transfer1_id, t2.id as transfer2_id,
+  t3.id as transfer3_id
+--
+QueryStatement [0-230]
+  Query [0-230]
+    GqlQuery [0-230]
+      GraphTableQuery [0-230]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-230]
+          GqlOperatorList [15-230]
+            GqlMatch [15-151]
+              GraphPattern [23-151]
+                GraphPathPattern [23-131]
+                  GraphPathMode [23-28]
+                  GraphNodePattern [29-41]
+                    GraphElementPatternFiller [30-40]
+                      Identifier(a1) [30-32]
+                      GraphLabelFilter [32-40]
+                        GraphElementLabel [33-40]
+                          Identifier(Account) [33-40]
+                  GraphEdgePattern [41-58]
+                    GraphElementPatternFiller [43-55]
+                      Identifier(t1) [43-45]
+                      GraphLabelFilter [45-55]
+                        GraphElementLabel [46-55]
+                          Identifier(Transfers) [46-55]
+                  GraphNodePattern [58-70]
+                    GraphElementPatternFiller [59-69]
+                      Identifier(a2) [59-61]
+                      GraphLabelFilter [61-69]
+                        GraphElementLabel [62-69]
+                          Identifier(Account) [62-69]
+                  GraphEdgePattern [70-87]
+                    GraphElementPatternFiller [72-84]
+                      Identifier(t2) [72-74]
+                      GraphLabelFilter [74-84]
+                        GraphElementLabel [75-84]
+                          Identifier(Transfers) [75-84]
+                  GraphNodePattern [90-102]
+                    GraphElementPatternFiller [91-101]
+                      Identifier(a3) [91-93]
+                      GraphLabelFilter [93-101]
+                        GraphElementLabel [94-101]
+                          Identifier(Account) [94-101]
+                  GraphEdgePattern [102-119]
+                    GraphElementPatternFiller [104-116]
+                      Identifier(t3) [104-106]
+                      GraphLabelFilter [106-116]
+                        GraphElementLabel [107-116]
+                          Identifier(Transfers) [107-116]
+                  GraphNodePattern [119-131]
+                    GraphElementPatternFiller [120-130]
+                      Identifier(a4) [120-122]
+                      GraphLabelFilter [122-130]
+                        GraphElementLabel [123-130]
+                          Identifier(Account) [123-130]
+                WhereClause [132-151]
+                  BinaryExpression(<) [138-151]
+                    PathExpression [138-143]
+                      Identifier(a1) [138-140]
+                      Identifier(id) [141-143]
+                    PathExpression [146-151]
+                      Identifier(a4) [146-148]
+                      Identifier(id) [149-151]
+            GqlReturn [152-230]
+              Select [161-230]
+                SelectList [161-230]
+                  SelectColumn [161-182]
+                    PathExpression [161-166]
+                      Identifier(t1) [161-163]
+                      Identifier(id) [164-166]
+                    Alias [167-182]
+                      Identifier(transfer1_id) [170-182]
+                  SelectColumn [184-205]
+                    PathExpression [184-189]
+                      Identifier(t2) [184-186]
+                      Identifier(id) [187-189]
+                    Alias [190-205]
+                      Identifier(transfer2_id) [193-205]
+                  SelectColumn [209-230]
+                    PathExpression [209-214]
+                      Identifier(t3) [209-211]
+                      Identifier(id) [212-214]
+                    Alias [215-230]
+                      Identifier(transfer3_id) [218-230]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_7
+GRAPH FinGraph
+MATCH TRAIL (a1)-[t1]-(a2), (a2)-[t1]-(a3)
+RETURN COUNT(1) as num_paths
+--
+QueryStatement [0-86]
+  Query [0-86]
+    GqlQuery [0-86]
+      GraphTableQuery [0-86]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-86]
+          GqlOperatorList [15-86]
+            GqlMatch [15-57]
+              GraphPattern [21-57]
+                GraphPathPattern [21-41]
+                  GraphPathMode [21-26]
+                  GraphNodePattern [27-31]
+                    GraphElementPatternFiller [28-30]
+                      Identifier(a1) [28-30]
+                  GraphEdgePattern [31-37]
+                    GraphElementPatternFiller [33-35]
+                      Identifier(t1) [33-35]
+                  GraphNodePattern [37-41]
+                    GraphElementPatternFiller [38-40]
+                      Identifier(a2) [38-40]
+                GraphPathPattern [43-57]
+                  GraphNodePattern [43-47]
+                    GraphElementPatternFiller [44-46]
+                      Identifier(a2) [44-46]
+                  GraphEdgePattern [47-53]
+                    GraphElementPatternFiller [49-51]
+                      Identifier(t1) [49-51]
+                  GraphNodePattern [53-57]
+                    GraphElementPatternFiller [54-56]
+                      Identifier(a3) [54-56]
+            GqlReturn [58-86]
+              Select [65-86]
+                SelectList [65-86]
+                  SelectColumn [65-86]
+                    FunctionCall [65-73]
+                      PathExpression [65-70]
+                        Identifier(COUNT) [65-70]
+                      IntLiteral(1) [71-72]
+                    Alias [74-86]
+                      Identifier(num_paths) [77-86]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_7
+GRAPH FinGraph
+MATCH TRAIL (a1)-[t1]-(a2)-[t1]-(a3)
+RETURN COUNT(1) as num_paths
+--
+QueryStatement [0-80]
+  Query [0-80]
+    GqlQuery [0-80]
+      GraphTableQuery [0-80]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-80]
+          GqlOperatorList [15-80]
+            GqlMatch [15-51]
+              GraphPattern [21-51]
+                GraphPathPattern [21-51]
+                  GraphPathMode [21-26]
+                  GraphNodePattern [27-31]
+                    GraphElementPatternFiller [28-30]
+                      Identifier(a1) [28-30]
+                  GraphEdgePattern [31-37]
+                    GraphElementPatternFiller [33-35]
+                      Identifier(t1) [33-35]
+                  GraphNodePattern [37-41]
+                    GraphElementPatternFiller [38-40]
+                      Identifier(a2) [38-40]
+                  GraphEdgePattern [41-47]
+                    GraphElementPatternFiller [43-45]
+                      Identifier(t1) [43-45]
+                  GraphNodePattern [47-51]
+                    GraphElementPatternFiller [48-50]
+                      Identifier(a3) [48-50]
+            GqlReturn [52-80]
+              Select [59-80]
+                SelectList [59-80]
+                  SelectColumn [59-80]
+                    FunctionCall [59-67]
+                      PathExpression [59-64]
+                        Identifier(COUNT) [59-64]
+                      IntLiteral(1) [65-66]
+                    Alias [68-80]
+                      Identifier(num_paths) [71-80]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_7
+GRAPH FinGraph
+MATCH TRAIL (a1:Account)-[t1:Transfers]->{4}(a5:Account)
+RETURN COUNT(1) as num_paths
+--
+QueryStatement [0-100]
+  Query [0-100]
+    GqlQuery [0-100]
+      GraphTableQuery [0-100]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-100]
+          GqlOperatorList [15-100]
+            GqlMatch [15-71]
+              GraphPattern [21-71]
+                GraphPathPattern [21-71]
+                  GraphPathMode [21-26]
+                  GraphNodePattern [27-39]
+                    GraphElementPatternFiller [28-38]
+                      Identifier(a1) [28-30]
+                      GraphLabelFilter [30-38]
+                        GraphElementLabel [31-38]
+                          Identifier(Account) [31-38]
+                  ParenthesizedGraphPathPattern [39-59]
+                    FixedQuantifier [57-58]
+                      IntLiteral(4) [57-58]
+                    GraphEdgePattern [39-56]
+                      GraphElementPatternFiller [41-53]
+                        Identifier(t1) [41-43]
+                        GraphLabelFilter [43-53]
+                          GraphElementLabel [44-53]
+                            Identifier(Transfers) [44-53]
+                  GraphNodePattern [59-71]
+                    GraphElementPatternFiller [60-70]
+                      Identifier(a5) [60-62]
+                      GraphLabelFilter [62-70]
+                        GraphElementLabel [63-70]
+                          Identifier(Account) [63-70]
+            GqlReturn [72-100]
+              Select [79-100]
+                SelectList [79-100]
+                  SelectColumn [79-100]
+                    FunctionCall [79-87]
+                      PathExpression [79-84]
+                        Identifier(COUNT) [79-84]
+                      IntLiteral(1) [85-86]
+                    Alias [88-100]
+                      Identifier(num_paths) [91-100]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_7
+GRAPH FinGraph
+MATCH TRAIL (WALK (a1:Account)-[t1:Transfers]->{4}(a5:Account))
+RETURN COUNT(1) as num_paths
+--
+QueryStatement [0-107]
+  Query [0-107]
+    GqlQuery [0-107]
+      GraphTableQuery [0-107]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-107]
+          GqlOperatorList [15-107]
+            GqlMatch [15-78]
+              GraphPattern [21-78]
+                GraphPathPattern [21-78]
+                  GraphPathMode [21-26]
+                  ParenthesizedGraphPathPattern [27-78]
+                    GraphPathMode [28-32]
+                    GraphNodePattern [33-45]
+                      GraphElementPatternFiller [34-44]
+                        Identifier(a1) [34-36]
+                        GraphLabelFilter [36-44]
+                          GraphElementLabel [37-44]
+                            Identifier(Account) [37-44]
+                    ParenthesizedGraphPathPattern [45-65]
+                      FixedQuantifier [63-64]
+                        IntLiteral(4) [63-64]
+                      GraphEdgePattern [45-62]
+                        GraphElementPatternFiller [47-59]
+                          Identifier(t1) [47-49]
+                          GraphLabelFilter [49-59]
+                            GraphElementLabel [50-59]
+                              Identifier(Transfers) [50-59]
+                    GraphNodePattern [65-77]
+                      GraphElementPatternFiller [66-76]
+                        Identifier(a5) [66-68]
+                        GraphLabelFilter [68-76]
+                          GraphElementLabel [69-76]
+                            Identifier(Account) [69-76]
+            GqlReturn [79-107]
+              Select [86-107]
+                SelectList [86-107]
+                  SelectColumn [86-107]
+                    FunctionCall [86-94]
+                      PathExpression [86-91]
+                        Identifier(COUNT) [86-91]
+                      IntLiteral(1) [92-93]
+                    Alias [95-107]
+                      Identifier(num_paths) [98-107]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_7
+GRAPH FinGraph
+MATCH
+  (TRAIL (a1:Account)-[t1:Transfers]->{3}(a4:Account))
+  -[t4:Transfers]->(a5:Account)
+RETURN COUNT(1) as num_paths
+--
+QueryStatement [0-136]
+  Query [0-136]
+    GqlQuery [0-136]
+      GraphTableQuery [0-136]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-136]
+          GqlOperatorList [15-136]
+            GqlMatch [15-107]
+              GraphPattern [23-107]
+                GraphPathPattern [23-107]
+                  ParenthesizedGraphPathPattern [23-75]
+                    GraphPathMode [24-29]
+                    GraphNodePattern [30-42]
+                      GraphElementPatternFiller [31-41]
+                        Identifier(a1) [31-33]
+                        GraphLabelFilter [33-41]
+                          GraphElementLabel [34-41]
+                            Identifier(Account) [34-41]
+                    ParenthesizedGraphPathPattern [42-62]
+                      FixedQuantifier [60-61]
+                        IntLiteral(3) [60-61]
+                      GraphEdgePattern [42-59]
+                        GraphElementPatternFiller [44-56]
+                          Identifier(t1) [44-46]
+                          GraphLabelFilter [46-56]
+                            GraphElementLabel [47-56]
+                              Identifier(Transfers) [47-56]
+                    GraphNodePattern [62-74]
+                      GraphElementPatternFiller [63-73]
+                        Identifier(a4) [63-65]
+                        GraphLabelFilter [65-73]
+                          GraphElementLabel [66-73]
+                            Identifier(Account) [66-73]
+                  GraphEdgePattern [78-95]
+                    GraphElementPatternFiller [80-92]
+                      Identifier(t4) [80-82]
+                      GraphLabelFilter [82-92]
+                        GraphElementLabel [83-92]
+                          Identifier(Transfers) [83-92]
+                  GraphNodePattern [95-107]
+                    GraphElementPatternFiller [96-106]
+                      Identifier(a5) [96-98]
+                      GraphLabelFilter [98-106]
+                        GraphElementLabel [99-106]
+                          Identifier(Account) [99-106]
+            GqlReturn [108-136]
+              Select [115-136]
+                SelectList [115-136]
+                  SelectColumn [115-136]
+                    FunctionCall [115-123]
+                      PathExpression [115-120]
+                        Identifier(COUNT) [115-120]
+                      IntLiteral(1) [121-122]
+                    Alias [124-136]
+                      Identifier(num_paths) [127-136]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_7
+GRAPH FinGraph
+MATCH (TRAIL -[t1:Transfers]->()-[t2:Transfers]->()-[t3:Transfers]->){2}
+RETURN COUNT(1) as num_paths
+--
+QueryStatement [0-116]
+  Query [0-116]
+    GqlQuery [0-116]
+      GraphTableQuery [0-116]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-116]
+          GqlOperatorList [15-116]
+            GqlMatch [15-87]
+              GraphPattern [21-87]
+                ParenthesizedGraphPathPattern [21-87]
+                  FixedQuantifier [85-86]
+                    IntLiteral(2) [85-86]
+                  GraphPathMode [22-27]
+                  GraphEdgePattern [28-45]
+                    GraphElementPatternFiller [30-42]
+                      Identifier(t1) [30-32]
+                      GraphLabelFilter [32-42]
+                        GraphElementLabel [33-42]
+                          Identifier(Transfers) [33-42]
+                  GraphNodePattern [45-47]
+                    GraphElementPatternFiller [46-46]
+                  GraphEdgePattern [47-64]
+                    GraphElementPatternFiller [49-61]
+                      Identifier(t2) [49-51]
+                      GraphLabelFilter [51-61]
+                        GraphElementLabel [52-61]
+                          Identifier(Transfers) [52-61]
+                  GraphNodePattern [64-66]
+                    GraphElementPatternFiller [65-65]
+                  GraphEdgePattern [66-83]
+                    GraphElementPatternFiller [68-80]
+                      Identifier(t3) [68-70]
+                      GraphLabelFilter [70-80]
+                        GraphElementLabel [71-80]
+                          Identifier(Transfers) [71-80]
+            GqlReturn [88-116]
+              Select [95-116]
+                SelectList [95-116]
+                  SelectColumn [95-116]
+                    FunctionCall [95-103]
+                      PathExpression [95-100]
+                        Identifier(COUNT) [95-100]
+                      IntLiteral(1) [101-102]
+                    Alias [104-116]
+                      Identifier(num_paths) [107-116]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_7
+GRAPH FinGraph
+MATCH TRAIL -[:Transfers]->{6}
+RETURN COUNT(1) as num_paths
+--
+QueryStatement [0-74]
+  Query [0-74]
+    GqlQuery [0-74]
+      GraphTableQuery [0-74]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-74]
+          GqlOperatorList [15-74]
+            GqlMatch [15-45]
+              GraphPattern [21-45]
+                GraphPathPattern [21-45]
+                  GraphPathMode [21-26]
+                  ParenthesizedGraphPathPattern [27-45]
+                    FixedQuantifier [43-44]
+                      IntLiteral(6) [43-44]
+                    GraphEdgePattern [27-42]
+                      GraphElementPatternFiller [29-39]
+                        GraphLabelFilter [29-39]
+                          GraphElementLabel [30-39]
+                            Identifier(Transfers) [30-39]
+            GqlReturn [46-74]
+              Select [53-74]
+                SelectList [53-74]
+                  SelectColumn [53-74]
+                    FunctionCall [53-61]
+                      PathExpression [53-58]
+                        Identifier(COUNT) [53-58]
+                      IntLiteral(1) [59-60]
+                    Alias [62-74]
+                      Identifier(num_paths) [65-74]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_7
+-- Error
+GRAPH FinGraph
+MATCH ANY SHORTEST TRAIL ->{1,4}
+RETURN COUNT(1) as num_paths
+--
+QueryStatement [9-85]
+  Query [9-85]
+    GqlQuery [9-85]
+      GraphTableQuery [9-85]
+        PathExpression [15-23]
+          Identifier(FinGraph) [15-23]
+        GqlOperatorList [24-85]
+          GqlOperatorList [24-85]
+            GqlMatch [24-56]
+              GraphPattern [30-56]
+                GraphPathPattern [30-56]
+                  GraphPathSearchPrefix [30-42]
+                  GraphPathMode [43-48]
+                  ParenthesizedGraphPathPattern [49-56]
+                    BoundedQuantifier [51-56]
+                      QuantifierBound [52-53]
+                        IntLiteral(1) [52-53]
+                      QuantifierBound [54-55]
+                        IntLiteral(4) [54-55]
+                    GraphEdgePattern [49-51]
+            GqlReturn [57-85]
+              Select [64-85]
+                SelectList [64-85]
+                  SelectColumn [64-85]
+                    FunctionCall [64-72]
+                      PathExpression [64-69]
+                        Identifier(COUNT) [64-69]
+                      IntLiteral(1) [70-71]
+                    Alias [73-85]
+                      Identifier(num_paths) [76-85]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#examples_7
+GRAPH FinGraph
+MATCH ANY SHORTEST (TRAIL ->{1,4})
+RETURN COUNT(1) as num_paths
+--
+QueryStatement [0-78]
+  Query [0-78]
+    GqlQuery [0-78]
+      GraphTableQuery [0-78]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-78]
+          GqlOperatorList [15-78]
+            GqlMatch [15-49]
+              GraphPattern [21-49]
+                GraphPathPattern [21-49]
+                  GraphPathSearchPrefix [21-33]
+                  ParenthesizedGraphPathPattern [34-49]
+                    GraphPathMode [35-40]
+                    ParenthesizedGraphPathPattern [41-48]
+                      BoundedQuantifier [43-48]
+                        QuantifierBound [44-45]
+                          IntLiteral(1) [44-45]
+                        QuantifierBound [46-47]
+                          IntLiteral(4) [46-47]
+                      GraphEdgePattern [41-43]
+            GqlReturn [50-78]
+              Select [57-78]
+                SelectList [57-78]
+                  SelectColumn [57-78]
+                    FunctionCall [57-65]
+                      PathExpression [57-62]
+                        Identifier(COUNT) [57-62]
+                      IntLiteral(1) [63-64]
+                    Alias [66-78]
+                      Identifier(num_paths) [69-78]
diff --git a/parser/testdata/spanner/graph-query-statements.test b/parser/testdata/spanner/graph-query-statements.test
new file mode 100644
index 0000000..7476c92
--- /dev/null
+++ b/parser/testdata/spanner/graph-query-statements.test
@@ -0,0 +1,4840 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+GRAPH FinGraph
+MATCH (:Account)-[:Transfers]->(account:Account)
+RETURN account, COUNT(*) AS num_incoming_transfers
+GROUP BY account
+
+NEXT
+
+MATCH (account:Account)<-[:Owns]-(owner:Person)
+RETURN
+  account.id AS account_id, owner.name AS owner_name,
+  num_incoming_transfers
+--
+QueryStatement [0-272]
+  Query [0-272]
+    GqlQuery [0-272]
+      GraphTableQuery [0-272]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-272]
+          GqlOperatorList [15-131]
+            GqlMatch [15-63]
+              GraphPattern [21-63]
+                GraphPathPattern [21-63]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      GraphLabelFilter [22-30]
+                        GraphElementLabel [23-30]
+                          Identifier(Account) [23-30]
+                  GraphEdgePattern [31-46]
+                    GraphElementPatternFiller [33-43]
+                      GraphLabelFilter [33-43]
+                        GraphElementLabel [34-43]
+                          Identifier(Transfers) [34-43]
+                  GraphNodePattern [46-63]
+                    GraphElementPatternFiller [47-62]
+                      Identifier(account) [47-54]
+                      GraphLabelFilter [54-62]
+                        GraphElementLabel [55-62]
+                          Identifier(Account) [55-62]
+            GqlReturn [64-131]
+              Select [71-131]
+                SelectList [71-114]
+                  SelectColumn [71-78]
+                    PathExpression [71-78]
+                      Identifier(account) [71-78]
+                  SelectColumn [80-114]
+                    FunctionCall [80-88]
+                      PathExpression [80-85]
+                        Identifier(COUNT) [80-85]
+                      Star(*) [86-87]
+                    Alias [89-114]
+                      Identifier(num_incoming_transfers) [92-114]
+                GroupBy [115-131]
+                  GroupingItem [124-131]
+                    PathExpression [124-131]
+                      Identifier(account) [124-131]
+          GqlOperatorList [139-272]
+            GqlMatch [139-186]
+              GraphPattern [145-186]
+                GraphPathPattern [145-186]
+                  GraphNodePattern [145-162]
+                    GraphElementPatternFiller [146-161]
+                      Identifier(account) [146-153]
+                      GraphLabelFilter [153-161]
+                        GraphElementLabel [154-161]
+                          Identifier(Account) [154-161]
+                  GraphEdgePattern [162-172]
+                    GraphElementPatternFiller [165-170]
+                      GraphLabelFilter [165-170]
+                        GraphElementLabel [166-170]
+                          Identifier(Owns) [166-170]
+                  GraphNodePattern [172-186]
+                    GraphElementPatternFiller [173-185]
+                      Identifier(owner) [173-178]
+                      GraphLabelFilter [178-185]
+                        GraphElementLabel [179-185]
+                          Identifier(Person) [179-185]
+            GqlReturn [187-272]
+              Select [196-272]
+                SelectList [196-272]
+                  SelectColumn [196-220]
+                    PathExpression [196-206]
+                      Identifier(account) [196-203]
+                      Identifier(id) [204-206]
+                    Alias [207-220]
+                      Identifier(account_id) [210-220]
+                  SelectColumn [222-246]
+                    PathExpression [222-232]
+                      Identifier(owner) [222-227]
+                      Identifier(name) [228-232]
+                    Alias [233-246]
+                      Identifier(owner_name) [236-246]
+                  SelectColumn [250-272]
+                    PathExpression [250-272]
+                      Identifier(num_incoming_transfers) [250-272]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_2
+GRAPH FinGraph
+MATCH (p:Person {Id: 1})
+CALL graph.neighbors(p) YIELD neighbor AS friend
+RETURN p.name AS person_name, friend.name AS friend_name
+--
+QueryStatement [0-145]
+  Query [0-145]
+    GqlQuery [0-145]
+      GraphTableQuery [0-145]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-145]
+          GqlOperatorList [15-145]
+            GqlMatch [15-39]
+              GraphPattern [21-39]
+                GraphPathPattern [21-39]
+                  GraphNodePattern [21-39]
+                    GraphElementPatternFiller [22-38]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                      GraphPropertySpecification [31-38]
+                        GraphPropertyNameAndValue [32-37]
+                          Identifier(Id) [32-34]
+                          IntLiteral(1) [36-37]
+            GqlNamedCall [40-88]
+              TVF [45-63]
+                PathExpression [45-60]
+                  Identifier(graph) [45-50]
+                  Identifier(neighbors) [51-60]
+                TVFArgument [61-62]
+                  PathExpression [61-62]
+                    Identifier(p) [61-62]
+              YieldItemList [64-88]
+                ExpressionWithOptAlias [70-88]
+                  Identifier(neighbor) [70-78]
+                  Alias [79-88]
+                    Identifier(friend) [82-88]
+            GqlReturn [89-145]
+              Select [96-145]
+                SelectList [96-145]
+                  SelectColumn [96-117]
+                    PathExpression [96-102]
+                      Identifier(p) [96-97]
+                      Identifier(name) [98-102]
+                    Alias [103-117]
+                      Identifier(person_name) [106-117]
+                  SelectColumn [119-145]
+                    PathExpression [119-130]
+                      Identifier(friend) [119-125]
+                      Identifier(name) [126-130]
+                    Alias [131-145]
+                      Identifier(friend_name) [134-145]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_2
+GRAPH FinGraph
+MATCH (p:Person)
+CALL (p) {
+  MATCH (p)-[:Owns]->(a:Account)
+  RETURN a.Id AS account_Id
+}
+RETURN p.name AS person_name, account_Id
+ORDER BY person_name, account_Id
+--
+QueryStatement [0-179]
+  Query [0-179]
+    GqlQuery [0-179]
+      GraphTableQuery [0-179]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-179]
+          GqlOperatorList [15-179]
+            GqlMatch [15-31]
+              GraphPattern [21-31]
+                GraphPathPattern [21-31]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+            GqlInlineSubqueryCall [32-105]
+              Query [41-105]
+                GqlQuery [41-105]
+                  GraphTableQuery [41-105]
+                    GqlOperatorList [45-103]
+                      GqlOperatorList [45-103]
+                        GqlMatch [45-75]
+                          GraphPattern [51-75]
+                            GraphPathPattern [51-75]
+                              GraphNodePattern [51-54]
+                                GraphElementPatternFiller [52-53]
+                                  Identifier(p) [52-53]
+                              GraphEdgePattern [54-64]
+                                GraphElementPatternFiller [56-61]
+                                  GraphLabelFilter [56-61]
+                                    GraphElementLabel [57-61]
+                                      Identifier(Owns) [57-61]
+                              GraphNodePattern [64-75]
+                                GraphElementPatternFiller [65-74]
+                                  Identifier(a) [65-66]
+                                  GraphLabelFilter [66-74]
+                                    GraphElementLabel [67-74]
+                                      Identifier(Account) [67-74]
+                        GqlReturn [78-103]
+                          Select [85-103]
+                            SelectList [85-103]
+                              SelectColumn [85-103]
+                                PathExpression [85-89]
+                                  Identifier(a) [85-86]
+                                  Identifier(Id) [87-89]
+                                Alias [90-103]
+                                  Identifier(account_Id) [93-103]
+              IdentifierList [37-40]
+                Identifier(p) [38-39]
+            GqlReturn [106-179]
+              Select [113-146]
+                SelectList [113-146]
+                  SelectColumn [113-134]
+                    PathExpression [113-119]
+                      Identifier(p) [113-114]
+                      Identifier(name) [115-119]
+                    Alias [120-134]
+                      Identifier(person_name) [123-134]
+                  SelectColumn [136-146]
+                    PathExpression [136-146]
+                      Identifier(account_Id) [136-146]
+              GqlOrderByAndPage [147-179]
+                OrderBy [147-179]
+                  OrderingExpression(ASC) [156-167]
+                    PathExpression [156-167]
+                      Identifier(person_name) [156-167]
+                  OrderingExpression(ASC) [169-179]
+                    PathExpression [169-179]
+                      Identifier(account_Id) [169-179]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_2
+GRAPH FinGraph
+MATCH (p:Person)  -- Outer-scoped variable `p`
+CALL () {  -- `p` not declared
+  MATCH (p)-[:Owns]->(a:Account)  -- Inner-scoped variable `p`, independent of outer-scoped `p`
+  RETURN a.Id AS account_Id
+}
+RETURN p.name AS person_name, account_Id
+ORDER BY person_name, account_Id
+--
+QueryStatement [0-292]
+  Query [0-292]
+    GqlQuery [0-292]
+      GraphTableQuery [0-292]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-292]
+          GqlOperatorList [15-292]
+            GqlMatch [15-31]
+              GraphPattern [21-31]
+                GraphPathPattern [21-31]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+            GqlInlineSubqueryCall [62-218]
+              Query [70-218]
+                GqlQuery [70-218]
+                  GraphTableQuery [70-218]
+                    GqlOperatorList [95-216]
+                      GqlOperatorList [95-216]
+                        GqlMatch [95-125]
+                          GraphPattern [101-125]
+                            GraphPathPattern [101-125]
+                              GraphNodePattern [101-104]
+                                GraphElementPatternFiller [102-103]
+                                  Identifier(p) [102-103]
+                              GraphEdgePattern [104-114]
+                                GraphElementPatternFiller [106-111]
+                                  GraphLabelFilter [106-111]
+                                    GraphElementLabel [107-111]
+                                      Identifier(Owns) [107-111]
+                              GraphNodePattern [114-125]
+                                GraphElementPatternFiller [115-124]
+                                  Identifier(a) [115-116]
+                                  GraphLabelFilter [116-124]
+                                    GraphElementLabel [117-124]
+                                      Identifier(Account) [117-124]
+                        GqlReturn [191-216]
+                          Select [198-216]
+                            SelectList [198-216]
+                              SelectColumn [198-216]
+                                PathExpression [198-202]
+                                  Identifier(a) [198-199]
+                                  Identifier(Id) [200-202]
+                                Alias [203-216]
+                                  Identifier(account_Id) [206-216]
+              IdentifierList [67-69]
+            GqlReturn [219-292]
+              Select [226-259]
+                SelectList [226-259]
+                  SelectColumn [226-247]
+                    PathExpression [226-232]
+                      Identifier(p) [226-227]
+                      Identifier(name) [228-232]
+                    Alias [233-247]
+                      Identifier(person_name) [236-247]
+                  SelectColumn [249-259]
+                    PathExpression [249-259]
+                      Identifier(account_Id) [249-259]
+              GqlOrderByAndPage [260-292]
+                OrderBy [260-292]
+                  OrderingExpression(ASC) [269-280]
+                    PathExpression [269-280]
+                      Identifier(person_name) [269-280]
+                  OrderingExpression(ASC) [282-292]
+                    PathExpression [282-292]
+                      Identifier(account_Id) [282-292]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_2
+GRAPH FinGraph
+MATCH (p:Person {Id:2})
+LET Id = p.Id
+CALL (Id) {  -- Non-node, non-edge variable `Id` declared
+  MATCH (p)-[:Owns]->(a:Account)
+  RETURN a.Id  -- Not allowed, outer-scoped `Id` isn't a node or edge variable, so you can't redeclare it.
+}
+RETURN p.name AS person_name, Id
+--
+QueryStatement [0-285]
+  Query [0-285]
+    GqlQuery [0-285]
+      GraphTableQuery [0-285]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-285]
+          GqlOperatorList [15-285]
+            GqlMatch [15-38]
+              GraphPattern [21-38]
+                GraphPathPattern [21-38]
+                  GraphNodePattern [21-38]
+                    GraphElementPatternFiller [22-37]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                      GraphPropertySpecification [31-37]
+                        GraphPropertyNameAndValue [32-36]
+                          Identifier(Id) [32-34]
+                          IntLiteral(2) [35-36]
+            GqlLet [39-52]
+              GqlLetVariableDefinitionList [43-52]
+                GqlLetVariableDefinition [43-52]
+                  Identifier(Id) [43-45]
+                  PathExpression [48-52]
+                    Identifier(p) [48-49]
+                    Identifier(Id) [50-52]
+            GqlInlineSubqueryCall [53-252]
+              Query [63-252]
+                GqlQuery [63-252]
+                  GraphTableQuery [63-252]
+                    GqlOperatorList [113-157]
+                      GqlOperatorList [113-157]
+                        GqlMatch [113-143]
+                          GraphPattern [119-143]
+                            GraphPathPattern [119-143]
+                              GraphNodePattern [119-122]
+                                GraphElementPatternFiller [120-121]
+                                  Identifier(p) [120-121]
+                              GraphEdgePattern [122-132]
+                                GraphElementPatternFiller [124-129]
+                                  GraphLabelFilter [124-129]
+                                    GraphElementLabel [125-129]
+                                      Identifier(Owns) [125-129]
+                              GraphNodePattern [132-143]
+                                GraphElementPatternFiller [133-142]
+                                  Identifier(a) [133-134]
+                                  GraphLabelFilter [134-142]
+                                    GraphElementLabel [135-142]
+                                      Identifier(Account) [135-142]
+                        GqlReturn [146-157]
+                          Select [153-157]
+                            SelectList [153-157]
+                              SelectColumn [153-157]
+                                PathExpression [153-157]
+                                  Identifier(a) [153-154]
+                                  Identifier(Id) [155-157]
+              IdentifierList [58-62]
+                Identifier(Id) [59-61]
+            GqlReturn [253-285]
+              Select [260-285]
+                SelectList [260-285]
+                  SelectColumn [260-281]
+                    PathExpression [260-266]
+                      Identifier(p) [260-261]
+                      Identifier(name) [262-266]
+                    Alias [267-281]
+                      Identifier(person_name) [270-281]
+                  SelectColumn [283-285]
+                    PathExpression [283-285]
+                      Identifier(Id) [283-285]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_2
+GRAPH FinGraph
+MATCH (p:Person)
+CALL (p) {
+  MATCH (p)-[:Owns]->(a:Account)
+  RETURN count(a) AS num_accounts
+}
+RETURN p.name, num_accounts
+ORDER BY num_accounts DESC, p.name
+--
+QueryStatement [0-174]
+  Query [0-174]
+    GqlQuery [0-174]
+      GraphTableQuery [0-174]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-174]
+          GqlOperatorList [15-174]
+            GqlMatch [15-31]
+              GraphPattern [21-31]
+                GraphPathPattern [21-31]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+            GqlInlineSubqueryCall [32-111]
+              Query [41-111]
+                GqlQuery [41-111]
+                  GraphTableQuery [41-111]
+                    GqlOperatorList [45-109]
+                      GqlOperatorList [45-109]
+                        GqlMatch [45-75]
+                          GraphPattern [51-75]
+                            GraphPathPattern [51-75]
+                              GraphNodePattern [51-54]
+                                GraphElementPatternFiller [52-53]
+                                  Identifier(p) [52-53]
+                              GraphEdgePattern [54-64]
+                                GraphElementPatternFiller [56-61]
+                                  GraphLabelFilter [56-61]
+                                    GraphElementLabel [57-61]
+                                      Identifier(Owns) [57-61]
+                              GraphNodePattern [64-75]
+                                GraphElementPatternFiller [65-74]
+                                  Identifier(a) [65-66]
+                                  GraphLabelFilter [66-74]
+                                    GraphElementLabel [67-74]
+                                      Identifier(Account) [67-74]
+                        GqlReturn [78-109]
+                          Select [85-109]
+                            SelectList [85-109]
+                              SelectColumn [85-109]
+                                FunctionCall [85-93]
+                                  PathExpression [85-90]
+                                    Identifier(count) [85-90]
+                                  PathExpression [91-92]
+                                    Identifier(a) [91-92]
+                                Alias [94-109]
+                                  Identifier(num_accounts) [97-109]
+              IdentifierList [37-40]
+                Identifier(p) [38-39]
+            GqlReturn [112-174]
+              Select [119-139]
+                SelectList [119-139]
+                  SelectColumn [119-125]
+                    PathExpression [119-125]
+                      Identifier(p) [119-120]
+                      Identifier(name) [121-125]
+                  SelectColumn [127-139]
+                    PathExpression [127-139]
+                      Identifier(num_accounts) [127-139]
+              GqlOrderByAndPage [140-174]
+                OrderBy [140-174]
+                  OrderingExpression(DESC) [149-166]
+                    PathExpression [149-161]
+                      Identifier(num_accounts) [149-161]
+                  OrderingExpression(ASC) [168-174]
+                    PathExpression [168-174]
+                      Identifier(p) [168-169]
+                      Identifier(name) [170-174]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_2
+GRAPH FinGraph
+MATCH (p:Person)
+OPTIONAL CALL (p) {
+  MATCH (p)-[:Owns]->(a:Account)-[t:Transfers]->()
+  RETURN a.Id AS account_id, t.amount AS transfer_amount, DATE(t.create_time) AS transfer_date
+}
+RETURN p.name, account_id, transfer_amount, transfer_date
+ORDER BY p.name, transfer_date DESC
+--
+QueryStatement [0-293]
+  Query [0-293]
+    GqlQuery [0-293]
+      GraphTableQuery [0-293]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-293]
+          GqlOperatorList [15-293]
+            GqlMatch [15-31]
+              GraphPattern [21-31]
+                GraphPathPattern [21-31]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+            GqlInlineSubqueryCall [32-199]
+              Query [50-199]
+                GqlQuery [50-199]
+                  GraphTableQuery [50-199]
+                    GqlOperatorList [54-197]
+                      GqlOperatorList [54-197]
+                        GqlMatch [54-102]
+                          GraphPattern [60-102]
+                            GraphPathPattern [60-102]
+                              GraphNodePattern [60-63]
+                                GraphElementPatternFiller [61-62]
+                                  Identifier(p) [61-62]
+                              GraphEdgePattern [63-73]
+                                GraphElementPatternFiller [65-70]
+                                  GraphLabelFilter [65-70]
+                                    GraphElementLabel [66-70]
+                                      Identifier(Owns) [66-70]
+                              GraphNodePattern [73-84]
+                                GraphElementPatternFiller [74-83]
+                                  Identifier(a) [74-75]
+                                  GraphLabelFilter [75-83]
+                                    GraphElementLabel [76-83]
+                                      Identifier(Account) [76-83]
+                              GraphEdgePattern [84-100]
+                                GraphElementPatternFiller [86-97]
+                                  Identifier(t) [86-87]
+                                  GraphLabelFilter [87-97]
+                                    GraphElementLabel [88-97]
+                                      Identifier(Transfers) [88-97]
+                              GraphNodePattern [100-102]
+                                GraphElementPatternFiller [101-101]
+                        GqlReturn [105-197]
+                          Select [112-197]
+                            SelectList [112-197]
+                              SelectColumn [112-130]
+                                PathExpression [112-116]
+                                  Identifier(a) [112-113]
+                                  Identifier(Id) [114-116]
+                                Alias [117-130]
+                                  Identifier(account_id) [120-130]
+                              SelectColumn [132-159]
+                                PathExpression [132-140]
+                                  Identifier(t) [132-133]
+                                  Identifier(amount) [134-140]
+                                Alias [141-159]
+                                  Identifier(transfer_amount) [144-159]
+                              SelectColumn [161-197]
+                                FunctionCall [161-180]
+                                  PathExpression [161-165]
+                                    Identifier(DATE) [161-165]
+                                  PathExpression [166-179]
+                                    Identifier(t) [166-167]
+                                    Identifier(create_time) [168-179]
+                                Alias [181-197]
+                                  Identifier(transfer_date) [184-197]
+              IdentifierList [46-49]
+                Identifier(p) [47-48]
+            GqlReturn [200-293]
+              Select [207-257]
+                SelectList [207-257]
+                  SelectColumn [207-213]
+                    PathExpression [207-213]
+                      Identifier(p) [207-208]
+                      Identifier(name) [209-213]
+                  SelectColumn [215-225]
+                    PathExpression [215-225]
+                      Identifier(account_id) [215-225]
+                  SelectColumn [227-242]
+                    PathExpression [227-242]
+                      Identifier(transfer_amount) [227-242]
+                  SelectColumn [244-257]
+                    PathExpression [244-257]
+                      Identifier(transfer_date) [244-257]
+              GqlOrderByAndPage [258-293]
+                OrderBy [258-293]
+                  OrderingExpression(ASC) [267-273]
+                    PathExpression [267-273]
+                      Identifier(p) [267-268]
+                      Identifier(name) [269-273]
+                  OrderingExpression(DESC) [275-293]
+                    PathExpression [275-288]
+                      Identifier(transfer_date) [275-288]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_2
+GRAPH FinGraph
+MATCH (p:Person {Id: 1})
+CALL my_tvf(p) YIELD Id AS tvf_Id
+RETURN p.Id AS person_Id, tvf_Id
+--
+QueryStatement [0-106]
+  Query [0-106]
+    GqlQuery [0-106]
+      GraphTableQuery [0-106]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-106]
+          GqlOperatorList [15-106]
+            GqlMatch [15-39]
+              GraphPattern [21-39]
+                GraphPathPattern [21-39]
+                  GraphNodePattern [21-39]
+                    GraphElementPatternFiller [22-38]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                      GraphPropertySpecification [31-38]
+                        GraphPropertyNameAndValue [32-37]
+                          Identifier(Id) [32-34]
+                          IntLiteral(1) [36-37]
+            GqlNamedCall [40-73]
+              TVF [45-54]
+                PathExpression [45-51]
+                  Identifier(my_tvf) [45-51]
+                TVFArgument [52-53]
+                  PathExpression [52-53]
+                    Identifier(p) [52-53]
+              YieldItemList [55-73]
+                ExpressionWithOptAlias [61-73]
+                  Identifier(Id) [61-63]
+                  Alias [64-73]
+                    Identifier(tvf_Id) [67-73]
+            GqlReturn [74-106]
+              Select [81-106]
+                SelectList [81-106]
+                  SelectColumn [81-98]
+                    PathExpression [81-85]
+                      Identifier(p) [81-82]
+                      Identifier(Id) [83-85]
+                    Alias [86-98]
+                      Identifier(person_Id) [89-98]
+                  SelectColumn [100-106]
+                    PathExpression [100-106]
+                      Identifier(tvf_Id) [100-106]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_2
+GRAPH FinGraph
+MATCH (p:Person {Id: 1})
+CALL (p) {
+  MATCH (p)-[:Owns]->(a:Account)
+  RETURN a.Id AS account_Id
+}
+RETURN p.Id AS person_Id, account_Id
+--
+QueryStatement [0-150]
+  Query [0-150]
+    GqlQuery [0-150]
+      GraphTableQuery [0-150]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-150]
+          GqlOperatorList [15-150]
+            GqlMatch [15-39]
+              GraphPattern [21-39]
+                GraphPathPattern [21-39]
+                  GraphNodePattern [21-39]
+                    GraphElementPatternFiller [22-38]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                      GraphPropertySpecification [31-38]
+                        GraphPropertyNameAndValue [32-37]
+                          Identifier(Id) [32-34]
+                          IntLiteral(1) [36-37]
+            GqlInlineSubqueryCall [40-113]
+              Query [49-113]
+                GqlQuery [49-113]
+                  GraphTableQuery [49-113]
+                    GqlOperatorList [53-111]
+                      GqlOperatorList [53-111]
+                        GqlMatch [53-83]
+                          GraphPattern [59-83]
+                            GraphPathPattern [59-83]
+                              GraphNodePattern [59-62]
+                                GraphElementPatternFiller [60-61]
+                                  Identifier(p) [60-61]
+                              GraphEdgePattern [62-72]
+                                GraphElementPatternFiller [64-69]
+                                  GraphLabelFilter [64-69]
+                                    GraphElementLabel [65-69]
+                                      Identifier(Owns) [65-69]
+                              GraphNodePattern [72-83]
+                                GraphElementPatternFiller [73-82]
+                                  Identifier(a) [73-74]
+                                  GraphLabelFilter [74-82]
+                                    GraphElementLabel [75-82]
+                                      Identifier(Account) [75-82]
+                        GqlReturn [86-111]
+                          Select [93-111]
+                            SelectList [93-111]
+                              SelectColumn [93-111]
+                                PathExpression [93-97]
+                                  Identifier(a) [93-94]
+                                  Identifier(Id) [95-97]
+                                Alias [98-111]
+                                  Identifier(account_Id) [101-111]
+              IdentifierList [45-48]
+                Identifier(p) [46-47]
+            GqlReturn [114-150]
+              Select [121-150]
+                SelectList [121-150]
+                  SelectColumn [121-138]
+                    PathExpression [121-125]
+                      Identifier(p) [121-122]
+                      Identifier(Id) [123-125]
+                    Alias [126-138]
+                      Identifier(person_Id) [129-138]
+                  SelectColumn [140-150]
+                    PathExpression [140-150]
+                      Identifier(account_Id) [140-150]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_2
+GRAPH FinGraph
+MATCH (p:Person)
+CALL (p) {
+  MATCH (p)-[:Owns]->(a:Account)-[t:Transfers]->()
+  WHERE t.amount > 50
+  RETURN a.Id AS account_id, t.amount
+}
+RETURN p.name, account_id, amount
+ORDER BY p.name, amount DESC
+--
+QueryStatement [0-218]
+  Query [0-218]
+    GqlQuery [0-218]
+      GraphTableQuery [0-218]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-218]
+          GqlOperatorList [15-218]
+            GqlMatch [15-31]
+              GraphPattern [21-31]
+                GraphPathPattern [21-31]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+            GqlInlineSubqueryCall [32-155]
+              Query [41-155]
+                GqlQuery [41-155]
+                  GraphTableQuery [41-155]
+                    GqlOperatorList [45-153]
+                      GqlOperatorList [45-153]
+                        GqlMatch [45-115]
+                          GraphPattern [51-115]
+                            GraphPathPattern [51-93]
+                              GraphNodePattern [51-54]
+                                GraphElementPatternFiller [52-53]
+                                  Identifier(p) [52-53]
+                              GraphEdgePattern [54-64]
+                                GraphElementPatternFiller [56-61]
+                                  GraphLabelFilter [56-61]
+                                    GraphElementLabel [57-61]
+                                      Identifier(Owns) [57-61]
+                              GraphNodePattern [64-75]
+                                GraphElementPatternFiller [65-74]
+                                  Identifier(a) [65-66]
+                                  GraphLabelFilter [66-74]
+                                    GraphElementLabel [67-74]
+                                      Identifier(Account) [67-74]
+                              GraphEdgePattern [75-91]
+                                GraphElementPatternFiller [77-88]
+                                  Identifier(t) [77-78]
+                                  GraphLabelFilter [78-88]
+                                    GraphElementLabel [79-88]
+                                      Identifier(Transfers) [79-88]
+                              GraphNodePattern [91-93]
+                                GraphElementPatternFiller [92-92]
+                            WhereClause [96-115]
+                              BinaryExpression(>) [102-115]
+                                PathExpression [102-110]
+                                  Identifier(t) [102-103]
+                                  Identifier(amount) [104-110]
+                                IntLiteral(50) [113-115]
+                        GqlReturn [118-153]
+                          Select [125-153]
+                            SelectList [125-153]
+                              SelectColumn [125-143]
+                                PathExpression [125-129]
+                                  Identifier(a) [125-126]
+                                  Identifier(Id) [127-129]
+                                Alias [130-143]
+                                  Identifier(account_id) [133-143]
+                              SelectColumn [145-153]
+                                PathExpression [145-153]
+                                  Identifier(t) [145-146]
+                                  Identifier(amount) [147-153]
+              IdentifierList [37-40]
+                Identifier(p) [38-39]
+            GqlReturn [156-218]
+              Select [163-189]
+                SelectList [163-189]
+                  SelectColumn [163-169]
+                    PathExpression [163-169]
+                      Identifier(p) [163-164]
+                      Identifier(name) [165-169]
+                  SelectColumn [171-181]
+                    PathExpression [171-181]
+                      Identifier(account_id) [171-181]
+                  SelectColumn [183-189]
+                    PathExpression [183-189]
+                      Identifier(amount) [183-189]
+              GqlOrderByAndPage [190-218]
+                OrderBy [190-218]
+                  OrderingExpression(ASC) [199-205]
+                    PathExpression [199-205]
+                      Identifier(p) [199-200]
+                      Identifier(name) [201-205]
+                  OrderingExpression(DESC) [207-218]
+                    PathExpression [207-213]
+                      Identifier(amount) [207-213]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_2
+GRAPH FinGraph
+MATCH (p:Person)
+CALL () {
+  MATCH (n:Person)
+  RETURN count(n) AS total_persons
+}
+RETURN p.name, total_persons
+ORDER BY p.name
+--
+QueryStatement [0-142]
+  Query [0-142]
+    GqlQuery [0-142]
+      GraphTableQuery [0-142]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-142]
+          GqlOperatorList [15-142]
+            GqlMatch [15-31]
+              GraphPattern [21-31]
+                GraphPathPattern [21-31]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+            GqlInlineSubqueryCall [32-97]
+              Query [40-97]
+                GqlQuery [40-97]
+                  GraphTableQuery [40-97]
+                    GqlOperatorList [44-95]
+                      GqlOperatorList [44-95]
+                        GqlMatch [44-60]
+                          GraphPattern [50-60]
+                            GraphPathPattern [50-60]
+                              GraphNodePattern [50-60]
+                                GraphElementPatternFiller [51-59]
+                                  Identifier(n) [51-52]
+                                  GraphLabelFilter [52-59]
+                                    GraphElementLabel [53-59]
+                                      Identifier(Person) [53-59]
+                        GqlReturn [63-95]
+                          Select [70-95]
+                            SelectList [70-95]
+                              SelectColumn [70-95]
+                                FunctionCall [70-78]
+                                  PathExpression [70-75]
+                                    Identifier(count) [70-75]
+                                  PathExpression [76-77]
+                                    Identifier(n) [76-77]
+                                Alias [79-95]
+                                  Identifier(total_persons) [82-95]
+              IdentifierList [37-39]
+            GqlReturn [98-142]
+              Select [105-126]
+                SelectList [105-126]
+                  SelectColumn [105-111]
+                    PathExpression [105-111]
+                      Identifier(p) [105-106]
+                      Identifier(name) [107-111]
+                  SelectColumn [113-126]
+                    PathExpression [113-126]
+                      Identifier(total_persons) [113-126]
+              GqlOrderByAndPage [127-142]
+                OrderBy [127-142]
+                  OrderingExpression(ASC) [136-142]
+                    PathExpression [136-142]
+                      Identifier(p) [136-137]
+                      Identifier(name) [138-142]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_2
+GRAPH FinGraph
+MATCH (p:Person {Id:2})
+CALL (p) {
+  MATCH (p)-[:Owns]->(a:Account)
+  OPTIONAL CALL (p, a) {
+    MATCH (a)-[t:Transfers]->(other:Account), (p)-[:Owns]->(other)
+    RETURN count(t) AS num_internal_transfers
+  }
+  RETURN a.Id AS account_Id, COALESCE(num_internal_transfers, 0) AS internal_transfers
+}
+RETURN p.name, account_Id, internal_transfers
+ORDER BY account_Id
+--
+QueryStatement [0-379]
+  Query [0-379]
+    GqlQuery [0-379]
+      GraphTableQuery [0-379]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-379]
+          GqlOperatorList [15-379]
+            GqlMatch [15-38]
+              GraphPattern [21-38]
+                GraphPathPattern [21-38]
+                  GraphNodePattern [21-38]
+                    GraphElementPatternFiller [22-37]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                      GraphPropertySpecification [31-37]
+                        GraphPropertyNameAndValue [32-36]
+                          Identifier(Id) [32-34]
+                          IntLiteral(2) [35-36]
+            GqlInlineSubqueryCall [39-313]
+              Query [48-313]
+                GqlQuery [48-313]
+                  GraphTableQuery [48-313]
+                    GqlOperatorList [52-311]
+                      GqlOperatorList [52-311]
+                        GqlMatch [52-82]
+                          GraphPattern [58-82]
+                            GraphPathPattern [58-82]
+                              GraphNodePattern [58-61]
+                                GraphElementPatternFiller [59-60]
+                                  Identifier(p) [59-60]
+                              GraphEdgePattern [61-71]
+                                GraphElementPatternFiller [63-68]
+                                  GraphLabelFilter [63-68]
+                                    GraphElementLabel [64-68]
+                                      Identifier(Owns) [64-68]
+                              GraphNodePattern [71-82]
+                                GraphElementPatternFiller [72-81]
+                                  Identifier(a) [72-73]
+                                  GraphLabelFilter [73-81]
+                                    GraphElementLabel [74-81]
+                                      Identifier(Account) [74-81]
+                        GqlInlineSubqueryCall [85-224]
+                          Query [106-224]
+                            GqlQuery [106-224]
+                              GraphTableQuery [106-224]
+                                GqlOperatorList [112-220]
+                                  GqlOperatorList [112-220]
+                                    GqlMatch [112-174]
+                                      GraphPattern [118-174]
+                                        GraphPathPattern [118-152]
+                                          GraphNodePattern [118-121]
+                                            GraphElementPatternFiller [119-120]
+                                              Identifier(a) [119-120]
+                                          GraphEdgePattern [121-137]
+                                            GraphElementPatternFiller [123-134]
+                                              Identifier(t) [123-124]
+                                              GraphLabelFilter [124-134]
+                                                GraphElementLabel [125-134]
+                                                  Identifier(Transfers) [125-134]
+                                          GraphNodePattern [137-152]
+                                            GraphElementPatternFiller [138-151]
+                                              Identifier(other) [138-143]
+                                              GraphLabelFilter [143-151]
+                                                GraphElementLabel [144-151]
+                                                  Identifier(Account) [144-151]
+                                        GraphPathPattern [154-174]
+                                          GraphNodePattern [154-157]
+                                            GraphElementPatternFiller [155-156]
+                                              Identifier(p) [155-156]
+                                          GraphEdgePattern [157-167]
+                                            GraphElementPatternFiller [159-164]
+                                              GraphLabelFilter [159-164]
+                                                GraphElementLabel [160-164]
+                                                  Identifier(Owns) [160-164]
+                                          GraphNodePattern [167-174]
+                                            GraphElementPatternFiller [168-173]
+                                              Identifier(other) [168-173]
+                                    GqlReturn [179-220]
+                                      Select [186-220]
+                                        SelectList [186-220]
+                                          SelectColumn [186-220]
+                                            FunctionCall [186-194]
+                                              PathExpression [186-191]
+                                                Identifier(count) [186-191]
+                                              PathExpression [192-193]
+                                                Identifier(t) [192-193]
+                                            Alias [195-220]
+                                              Identifier(num_internal_transfers) [198-220]
+                          IdentifierList [99-105]
+                            Identifier(p) [100-101]
+                            Identifier(a) [103-104]
+                        GqlReturn [227-311]
+                          Select [234-311]
+                            SelectList [234-311]
+                              SelectColumn [234-252]
+                                PathExpression [234-238]
+                                  Identifier(a) [234-235]
+                                  Identifier(Id) [236-238]
+                                Alias [239-252]
+                                  Identifier(account_Id) [242-252]
+                              SelectColumn [254-311]
+                                FunctionCall [254-289]
+                                  PathExpression [254-262]
+                                    Identifier(COALESCE) [254-262]
+                                  PathExpression [263-285]
+                                    Identifier(num_internal_transfers) [263-285]
+                                  IntLiteral(0) [287-288]
+                                Alias [290-311]
+                                  Identifier(internal_transfers) [293-311]
+              IdentifierList [44-47]
+                Identifier(p) [45-46]
+            GqlReturn [314-379]
+              Select [321-359]
+                SelectList [321-359]
+                  SelectColumn [321-327]
+                    PathExpression [321-327]
+                      Identifier(p) [321-322]
+                      Identifier(name) [323-327]
+                  SelectColumn [329-339]
+                    PathExpression [329-339]
+                      Identifier(account_Id) [329-339]
+                  SelectColumn [341-359]
+                    PathExpression [341-359]
+                      Identifier(internal_transfers) [341-359]
+              GqlOrderByAndPage [360-379]
+                OrderBy [360-379]
+                  OrderingExpression(ASC) [369-379]
+                    PathExpression [369-379]
+                      Identifier(account_Id) [369-379]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_2
+GRAPH FinGraph
+MATCH (n)
+RETURN n
+
+NEXT
+
+-- Assumes PageRank() is a built-in TVF that takes a TABLE of graph elements
+-- and returns the same table with an additional 'rank' column.
+CALL PER() PageRank() YIELD n, rank
+RETURN n.Id AS node_id, rank
+ORDER BY rank DESC
+LIMIT 5
+--
+QueryStatement [0-273]
+  Query [0-273]
+    GqlQuery [0-273]
+      GraphTableQuery [0-273]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-273]
+          GqlOperatorList [15-33]
+            GqlMatch [15-24]
+              GraphPattern [21-24]
+                GraphPathPattern [21-24]
+                  GraphNodePattern [21-24]
+                    GraphElementPatternFiller [22-23]
+                      Identifier(n) [22-23]
+            GqlReturn [25-33]
+              Select [32-33]
+                SelectList [32-33]
+                  SelectColumn [32-33]
+                    PathExpression [32-33]
+                      Identifier(n) [32-33]
+          GqlOperatorList [182-273]
+            GqlNamedCall [182-217]
+              TVF [193-203]
+                PathExpression [193-201]
+                  Identifier(PageRank) [193-201]
+              YieldItemList [204-217]
+                ExpressionWithOptAlias [210-211]
+                  Identifier(n) [210-211]
+                ExpressionWithOptAlias [213-217]
+                  Identifier(rank) [213-217]
+              IdentifierList [190-192]
+            GqlReturn [218-273]
+              Select [225-246]
+                SelectList [225-246]
+                  SelectColumn [225-240]
+                    PathExpression [225-229]
+                      Identifier(n) [225-226]
+                      Identifier(Id) [227-229]
+                    Alias [230-240]
+                      Identifier(node_id) [233-240]
+                  SelectColumn [242-246]
+                    PathExpression [242-246]
+                      Identifier(rank) [242-246]
+              GqlOrderByAndPage [247-273]
+                OrderBy [247-265]
+                  OrderingExpression(DESC) [256-265]
+                    PathExpression [256-260]
+                      Identifier(rank) [256-260]
+                GqlPage [266-273]
+                  GqlPageLimit [266-273]
+                    IntLiteral(5) [272-273]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_3
+GRAPH FinGraph
+MATCH (p:Person)-[o:Owns]->(a:Account)
+FILTER p.Id <> 1
+RETURN p.name, a.Id AS account_id
+--
+QueryStatement [0-104]
+  Query [0-104]
+    GqlQuery [0-104]
+      GraphTableQuery [0-104]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-104]
+          GqlOperatorList [15-104]
+            GqlMatch [15-53]
+              GraphPattern [21-53]
+                GraphPathPattern [21-53]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                  GraphEdgePattern [31-42]
+                    GraphElementPatternFiller [33-39]
+                      Identifier(o) [33-34]
+                      GraphLabelFilter [34-39]
+                        GraphElementLabel [35-39]
+                          Identifier(Owns) [35-39]
+                  GraphNodePattern [42-53]
+                    GraphElementPatternFiller [43-52]
+                      Identifier(a) [43-44]
+                      GraphLabelFilter [44-52]
+                        GraphElementLabel [45-52]
+                          Identifier(Account) [45-52]
+            GqlFilter [54-70]
+              WhereClause [54-70]
+                BinaryExpression(<>) [61-70]
+                  PathExpression [61-65]
+                    Identifier(p) [61-62]
+                    Identifier(Id) [63-65]
+                  IntLiteral(1) [69-70]
+            GqlReturn [71-104]
+              Select [78-104]
+                SelectList [78-104]
+                  SelectColumn [78-84]
+                    PathExpression [78-84]
+                      Identifier(p) [78-79]
+                      Identifier(name) [80-84]
+                  SelectColumn [86-104]
+                    PathExpression [86-90]
+                      Identifier(a) [86-87]
+                      Identifier(Id) [88-90]
+                    Alias [91-104]
+                      Identifier(account_id) [94-104]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_3
+GRAPH FinGraph
+MATCH (p:Person)-[o:Owns]->(a:Account)
+FILTER WHERE p.Id <> 1
+RETURN p.name, a.Id AS account_id
+--
+QueryStatement [0-110]
+  Query [0-110]
+    GqlQuery [0-110]
+      GraphTableQuery [0-110]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-110]
+          GqlOperatorList [15-110]
+            GqlMatch [15-53]
+              GraphPattern [21-53]
+                GraphPathPattern [21-53]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                  GraphEdgePattern [31-42]
+                    GraphElementPatternFiller [33-39]
+                      Identifier(o) [33-34]
+                      GraphLabelFilter [34-39]
+                        GraphElementLabel [35-39]
+                          Identifier(Owns) [35-39]
+                  GraphNodePattern [42-53]
+                    GraphElementPatternFiller [43-52]
+                      Identifier(a) [43-44]
+                      GraphLabelFilter [44-52]
+                        GraphElementLabel [45-52]
+                          Identifier(Account) [45-52]
+            GqlFilter [54-76]
+              WhereClause [61-76]
+                BinaryExpression(<>) [67-76]
+                  PathExpression [67-71]
+                    Identifier(p) [67-68]
+                    Identifier(Id) [69-71]
+                  IntLiteral(1) [75-76]
+            GqlReturn [77-110]
+              Select [84-110]
+                SelectList [84-110]
+                  SelectColumn [84-90]
+                    PathExpression [84-90]
+                      Identifier(p) [84-85]
+                      Identifier(name) [86-90]
+                  SelectColumn [92-110]
+                    PathExpression [92-96]
+                      Identifier(a) [92-93]
+                      Identifier(Id) [94-96]
+                    Alias [97-110]
+                      Identifier(account_id) [100-110]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_3
+GRAPH FinGraph
+MATCH (source:Account)-[e:Transfers]->(dest:Account)
+RETURN source, COUNT(e) AS num_transfers
+GROUP BY source
+
+NEXT
+
+FILTER WHERE num_transfers > 1
+RETURN source.id AS source_id, num_transfers
+--
+QueryStatement [0-207]
+  Query [0-207]
+    GqlQuery [0-207]
+      GraphTableQuery [0-207]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-207]
+          GqlOperatorList [15-124]
+            GqlMatch [15-67]
+              GraphPattern [21-67]
+                GraphPathPattern [21-67]
+                  GraphNodePattern [21-37]
+                    GraphElementPatternFiller [22-36]
+                      Identifier(source) [22-28]
+                      GraphLabelFilter [28-36]
+                        GraphElementLabel [29-36]
+                          Identifier(Account) [29-36]
+                  GraphEdgePattern [37-53]
+                    GraphElementPatternFiller [39-50]
+                      Identifier(e) [39-40]
+                      GraphLabelFilter [40-50]
+                        GraphElementLabel [41-50]
+                          Identifier(Transfers) [41-50]
+                  GraphNodePattern [53-67]
+                    GraphElementPatternFiller [54-66]
+                      Identifier(dest) [54-58]
+                      GraphLabelFilter [58-66]
+                        GraphElementLabel [59-66]
+                          Identifier(Account) [59-66]
+            GqlReturn [68-124]
+              Select [75-124]
+                SelectList [75-108]
+                  SelectColumn [75-81]
+                    PathExpression [75-81]
+                      Identifier(source) [75-81]
+                  SelectColumn [83-108]
+                    FunctionCall [83-91]
+                      PathExpression [83-88]
+                        Identifier(COUNT) [83-88]
+                      PathExpression [89-90]
+                        Identifier(e) [89-90]
+                    Alias [92-108]
+                      Identifier(num_transfers) [95-108]
+                GroupBy [109-124]
+                  GroupingItem [118-124]
+                    PathExpression [118-124]
+                      Identifier(source) [118-124]
+          GqlOperatorList [132-207]
+            GqlFilter [132-162]
+              WhereClause [139-162]
+                BinaryExpression(>) [145-162]
+                  PathExpression [145-158]
+                    Identifier(num_transfers) [145-158]
+                  IntLiteral(1) [161-162]
+            GqlReturn [163-207]
+              Select [170-207]
+                SelectList [170-207]
+                  SelectColumn [170-192]
+                    PathExpression [170-179]
+                      Identifier(source) [170-176]
+                      Identifier(id) [177-179]
+                    Alias [180-192]
+                      Identifier(source_id) [183-192]
+                  SelectColumn [194-207]
+                    PathExpression [194-207]
+                      Identifier(num_transfers) [194-207]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_3
+-- Error: m doesn't exist
+GRAPH FinGraph
+MATCH (p:Person)-[o:Owns]->(a:Account)
+FILTER WHERE m.Id <> 1
+RETURN p.name
+--
+QueryStatement [26-116]
+  Query [26-116]
+    GqlQuery [26-116]
+      GraphTableQuery [26-116]
+        PathExpression [32-40]
+          Identifier(FinGraph) [32-40]
+        GqlOperatorList [41-116]
+          GqlOperatorList [41-116]
+            GqlMatch [41-79]
+              GraphPattern [47-79]
+                GraphPathPattern [47-79]
+                  GraphNodePattern [47-57]
+                    GraphElementPatternFiller [48-56]
+                      Identifier(p) [48-49]
+                      GraphLabelFilter [49-56]
+                        GraphElementLabel [50-56]
+                          Identifier(Person) [50-56]
+                  GraphEdgePattern [57-68]
+                    GraphElementPatternFiller [59-65]
+                      Identifier(o) [59-60]
+                      GraphLabelFilter [60-65]
+                        GraphElementLabel [61-65]
+                          Identifier(Owns) [61-65]
+                  GraphNodePattern [68-79]
+                    GraphElementPatternFiller [69-78]
+                      Identifier(a) [69-70]
+                      GraphLabelFilter [70-78]
+                        GraphElementLabel [71-78]
+                          Identifier(Account) [71-78]
+            GqlFilter [80-102]
+              WhereClause [87-102]
+                BinaryExpression(<>) [93-102]
+                  PathExpression [93-97]
+                    Identifier(m) [93-94]
+                    Identifier(Id) [95-97]
+                  IntLiteral(1) [101-102]
+            GqlReturn [103-116]
+              Select [110-116]
+                SelectList [110-116]
+                  SelectColumn [110-116]
+                    PathExpression [110-116]
+                      Identifier(p) [110-111]
+                      Identifier(name) [112-116]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_3
+-- ERROR: date_of_birth isn't a property of p
+GRAPH FinGraph
+MATCH (p:Person)-[o:Owns]->(a:Account)
+FILTER WHERE p.date_of_birth < '1990-01-10'
+RETURN p.name
+--
+QueryStatement [46-157]
+  Query [46-157]
+    GqlQuery [46-157]
+      GraphTableQuery [46-157]
+        PathExpression [52-60]
+          Identifier(FinGraph) [52-60]
+        GqlOperatorList [61-157]
+          GqlOperatorList [61-157]
+            GqlMatch [61-99]
+              GraphPattern [67-99]
+                GraphPathPattern [67-99]
+                  GraphNodePattern [67-77]
+                    GraphElementPatternFiller [68-76]
+                      Identifier(p) [68-69]
+                      GraphLabelFilter [69-76]
+                        GraphElementLabel [70-76]
+                          Identifier(Person) [70-76]
+                  GraphEdgePattern [77-88]
+                    GraphElementPatternFiller [79-85]
+                      Identifier(o) [79-80]
+                      GraphLabelFilter [80-85]
+                        GraphElementLabel [81-85]
+                          Identifier(Owns) [81-85]
+                  GraphNodePattern [88-99]
+                    GraphElementPatternFiller [89-98]
+                      Identifier(a) [89-90]
+                      GraphLabelFilter [90-98]
+                        GraphElementLabel [91-98]
+                          Identifier(Account) [91-98]
+            GqlFilter [100-143]
+              WhereClause [107-143]
+                BinaryExpression(<) [113-143]
+                  PathExpression [113-128]
+                    Identifier(p) [113-114]
+                    Identifier(date_of_birth) [115-128]
+                  StringLiteral [131-143]
+                    StringLiteralComponent('1990-01-10') [131-143]
+            GqlReturn [144-157]
+              Select [151-157]
+                SelectList [151-157]
+                  SelectColumn [151-157]
+                    PathExpression [151-157]
+                      Identifier(p) [151-152]
+                      Identifier(name) [153-157]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_4
+GRAPH FinGraph
+MATCH (p:Person)-[o:Owns]->(a:Account)
+FOR element in ["all","some"] WITH OFFSET
+RETURN p.Id, element as alert_type, offset
+ORDER BY p.Id, element, offset
+--
+QueryStatement [0-169]
+  Query [0-169]
+    GqlQuery [0-169]
+      GraphTableQuery [0-169]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-169]
+          GqlOperatorList [15-169]
+            GqlMatch [15-53]
+              GraphPattern [21-53]
+                GraphPathPattern [21-53]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                  GraphEdgePattern [31-42]
+                    GraphElementPatternFiller [33-39]
+                      Identifier(o) [33-34]
+                      GraphLabelFilter [34-39]
+                        GraphElementLabel [35-39]
+                          Identifier(Owns) [35-39]
+                  GraphNodePattern [42-53]
+                    GraphElementPatternFiller [43-52]
+                      Identifier(a) [43-44]
+                      GraphLabelFilter [44-52]
+                        GraphElementLabel [45-52]
+                          Identifier(Account) [45-52]
+            GqlFor [54-95]
+              Identifier(element) [58-65]
+              ArrayConstructor [69-83]
+                StringLiteral [70-75]
+                  StringLiteralComponent("all") [70-75]
+                StringLiteral [76-82]
+                  StringLiteralComponent("some") [76-82]
+              WithOffset [84-95]
+            GqlReturn [96-169]
+              Select [103-138]
+                SelectList [103-138]
+                  SelectColumn [103-107]
+                    PathExpression [103-107]
+                      Identifier(p) [103-104]
+                      Identifier(Id) [105-107]
+                  SelectColumn [109-130]
+                    PathExpression [109-116]
+                      Identifier(element) [109-116]
+                    Alias [117-130]
+                      Identifier(alert_type) [120-130]
+                  SelectColumn [132-138]
+                    PathExpression [132-138]
+                      Identifier(offset) [132-138]
+              GqlOrderByAndPage [139-169]
+                OrderBy [139-169]
+                  OrderingExpression(ASC) [148-152]
+                    PathExpression [148-152]
+                      Identifier(p) [148-149]
+                      Identifier(Id) [150-152]
+                  OrderingExpression(ASC) [154-161]
+                    PathExpression [154-161]
+                      Identifier(element) [154-161]
+                  OrderingExpression(ASC) [163-169]
+                    PathExpression [163-169]
+                      Identifier(offset) [163-169]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_4
+GRAPH FinGraph
+MATCH (p:Person)-[o:Owns]->(a:Account)
+FILTER WHERE p.Id <> 1
+FOR element in GENERATE_ARRAY(1, p.Id)
+RETURN p.Id, element
+ORDER BY p.Id, element
+--
+QueryStatement [0-159]
+  Query [0-159]
+    GqlQuery [0-159]
+      GraphTableQuery [0-159]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-159]
+          GqlOperatorList [15-159]
+            GqlMatch [15-53]
+              GraphPattern [21-53]
+                GraphPathPattern [21-53]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                  GraphEdgePattern [31-42]
+                    GraphElementPatternFiller [33-39]
+                      Identifier(o) [33-34]
+                      GraphLabelFilter [34-39]
+                        GraphElementLabel [35-39]
+                          Identifier(Owns) [35-39]
+                  GraphNodePattern [42-53]
+                    GraphElementPatternFiller [43-52]
+                      Identifier(a) [43-44]
+                      GraphLabelFilter [44-52]
+                        GraphElementLabel [45-52]
+                          Identifier(Account) [45-52]
+            GqlFilter [54-76]
+              WhereClause [61-76]
+                BinaryExpression(<>) [67-76]
+                  PathExpression [67-71]
+                    Identifier(p) [67-68]
+                    Identifier(Id) [69-71]
+                  IntLiteral(1) [75-76]
+            GqlFor [77-115]
+              Identifier(element) [81-88]
+              FunctionCall [92-115]
+                PathExpression [92-106]
+                  Identifier(GENERATE_ARRAY) [92-106]
+                IntLiteral(1) [107-108]
+                PathExpression [110-114]
+                  Identifier(p) [110-111]
+                  Identifier(Id) [112-114]
+            GqlReturn [116-159]
+              Select [123-136]
+                SelectList [123-136]
+                  SelectColumn [123-127]
+                    PathExpression [123-127]
+                      Identifier(p) [123-124]
+                      Identifier(Id) [125-127]
+                  SelectColumn [129-136]
+                    PathExpression [129-136]
+                      Identifier(element) [129-136]
+              GqlOrderByAndPage [137-159]
+                OrderBy [137-159]
+                  OrderingExpression(ASC) [146-150]
+                    PathExpression [146-150]
+                      Identifier(p) [146-147]
+                      Identifier(Id) [148-150]
+                  OrderingExpression(ASC) [152-159]
+                    PathExpression [152-159]
+                      Identifier(element) [152-159]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_4
+-- No rows produced
+GRAPH FinGraph
+MATCH (p:Person)
+FOR element in [] WITH OFFSET AS off
+RETURN p.name, element, off
+--
+QueryStatement [20-116]
+  Query [20-116]
+    GqlQuery [20-116]
+      GraphTableQuery [20-116]
+        PathExpression [26-34]
+          Identifier(FinGraph) [26-34]
+        GqlOperatorList [35-116]
+          GqlOperatorList [35-116]
+            GqlMatch [35-51]
+              GraphPattern [41-51]
+                GraphPathPattern [41-51]
+                  GraphNodePattern [41-51]
+                    GraphElementPatternFiller [42-50]
+                      Identifier(p) [42-43]
+                      GraphLabelFilter [43-50]
+                        GraphElementLabel [44-50]
+                          Identifier(Person) [44-50]
+            GqlFor [52-88]
+              Identifier(element) [56-63]
+              ArrayConstructor [67-69]
+              WithOffset [70-88]
+                Alias [82-88]
+                  Identifier(off) [85-88]
+            GqlReturn [89-116]
+              Select [96-116]
+                SelectList [96-116]
+                  SelectColumn [96-102]
+                    PathExpression [96-102]
+                      Identifier(p) [96-97]
+                      Identifier(name) [98-102]
+                  SelectColumn [104-111]
+                    PathExpression [104-111]
+                      Identifier(element) [104-111]
+                  SelectColumn [113-116]
+                    PathExpression [113-116]
+                      Identifier(off) [113-116]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_4
+-- No rows produced
+GRAPH FinGraph
+MATCH (p:Person)
+FOR element in CAST(NULL AS ARRAY) WITH OFFSET
+RETURN p.name, element, offset
+--
+QueryStatement [20-137]
+  Query [20-137]
+    GqlQuery [20-137]
+      GraphTableQuery [20-137]
+        PathExpression [26-34]
+          Identifier(FinGraph) [26-34]
+        GqlOperatorList [35-137]
+          GqlOperatorList [35-137]
+            GqlMatch [35-51]
+              GraphPattern [41-51]
+                GraphPathPattern [41-51]
+                  GraphNodePattern [41-51]
+                    GraphElementPatternFiller [42-50]
+                      Identifier(p) [42-43]
+                      GraphLabelFilter [43-50]
+                        GraphElementLabel [44-50]
+                          Identifier(Person) [44-50]
+            GqlFor [52-106]
+              Identifier(element) [56-63]
+              CastExpression [67-94]
+                NullLiteral(NULL) [72-76]
+                ArrayType [80-93]
+                  SimpleType [86-92]
+                    PathExpression [86-92]
+                      Identifier(STRING) [86-92]
+              WithOffset [95-106]
+            GqlReturn [107-137]
+              Select [114-137]
+                SelectList [114-137]
+                  SelectColumn [114-120]
+                    PathExpression [114-120]
+                      Identifier(p) [114-115]
+                      Identifier(name) [116-120]
+                  SelectColumn [122-129]
+                    PathExpression [122-129]
+                      Identifier(element) [122-129]
+                  SelectColumn [131-137]
+                    PathExpression [131-137]
+                      Identifier(offset) [131-137]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_4
+GRAPH FinGraph
+FOR element in [1,2,3] WITH OFFSET
+WITH element as col
+RETURN col
+ORDER BY col
+--
+QueryStatement [0-93]
+  Query [0-93]
+    GqlQuery [0-93]
+      GraphTableQuery [0-93]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-93]
+          GqlOperatorList [15-93]
+            GqlFor [15-49]
+              Identifier(element) [19-26]
+              ArrayConstructor [30-37]
+                IntLiteral(1) [31-32]
+                IntLiteral(2) [33-34]
+                IntLiteral(3) [35-36]
+              WithOffset [38-49]
+            GqlWith [50-69]
+              Select [55-69]
+                SelectList [55-69]
+                  SelectColumn [55-69]
+                    PathExpression [55-62]
+                      Identifier(element) [55-62]
+                    Alias [63-69]
+                      Identifier(col) [66-69]
+            GqlReturn [70-93]
+              Select [77-80]
+                SelectList [77-80]
+                  SelectColumn [77-80]
+                    PathExpression [77-80]
+                      Identifier(col) [77-80]
+              GqlOrderByAndPage [81-93]
+                OrderBy [81-93]
+                  OrderingExpression(ASC) [90-93]
+                    PathExpression [90-93]
+                      Identifier(col) [90-93]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_5
+GRAPH FinGraph
+MATCH (source:Account)-[e:Transfers]->(destination:Account)
+LET a = source
+RETURN a.id AS a_id
+--
+QueryStatement [0-109]
+  Query [0-109]
+    GqlQuery [0-109]
+      GraphTableQuery [0-109]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-109]
+          GqlOperatorList [15-109]
+            GqlMatch [15-74]
+              GraphPattern [21-74]
+                GraphPathPattern [21-74]
+                  GraphNodePattern [21-37]
+                    GraphElementPatternFiller [22-36]
+                      Identifier(source) [22-28]
+                      GraphLabelFilter [28-36]
+                        GraphElementLabel [29-36]
+                          Identifier(Account) [29-36]
+                  GraphEdgePattern [37-53]
+                    GraphElementPatternFiller [39-50]
+                      Identifier(e) [39-40]
+                      GraphLabelFilter [40-50]
+                        GraphElementLabel [41-50]
+                          Identifier(Transfers) [41-50]
+                  GraphNodePattern [53-74]
+                    GraphElementPatternFiller [54-73]
+                      Identifier(destination) [54-65]
+                      GraphLabelFilter [65-73]
+                        GraphElementLabel [66-73]
+                          Identifier(Account) [66-73]
+            GqlLet [75-89]
+              GqlLetVariableDefinitionList [79-89]
+                GqlLetVariableDefinition [79-89]
+                  Identifier(a) [79-80]
+                  PathExpression [83-89]
+                    Identifier(source) [83-89]
+            GqlReturn [90-109]
+              Select [97-109]
+                SelectList [97-109]
+                  SelectColumn [97-109]
+                    PathExpression [97-101]
+                      Identifier(a) [97-98]
+                      Identifier(id) [99-101]
+                    Alias [102-109]
+                      Identifier(a_id) [105-109]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_5
+GRAPH FinGraph
+MATCH (source:Account)-[e:Transfers]->(destination:Account)
+LET a = source
+RETURN a
+
+NEXT
+
+LET b = a -- Valid: 'a' is defined and returned from the linear query statement above.
+RETURN b.id AS b_id
+--
+QueryStatement [0-212]
+  Query [0-212]
+    GqlQuery [0-212]
+      GraphTableQuery [0-212]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-212]
+          GqlOperatorList [15-98]
+            GqlMatch [15-74]
+              GraphPattern [21-74]
+                GraphPathPattern [21-74]
+                  GraphNodePattern [21-37]
+                    GraphElementPatternFiller [22-36]
+                      Identifier(source) [22-28]
+                      GraphLabelFilter [28-36]
+                        GraphElementLabel [29-36]
+                          Identifier(Account) [29-36]
+                  GraphEdgePattern [37-53]
+                    GraphElementPatternFiller [39-50]
+                      Identifier(e) [39-40]
+                      GraphLabelFilter [40-50]
+                        GraphElementLabel [41-50]
+                          Identifier(Transfers) [41-50]
+                  GraphNodePattern [53-74]
+                    GraphElementPatternFiller [54-73]
+                      Identifier(destination) [54-65]
+                      GraphLabelFilter [65-73]
+                        GraphElementLabel [66-73]
+                          Identifier(Account) [66-73]
+            GqlLet [75-89]
+              GqlLetVariableDefinitionList [79-89]
+                GqlLetVariableDefinition [79-89]
+                  Identifier(a) [79-80]
+                  PathExpression [83-89]
+                    Identifier(source) [83-89]
+            GqlReturn [90-98]
+              Select [97-98]
+                SelectList [97-98]
+                  SelectColumn [97-98]
+                    PathExpression [97-98]
+                      Identifier(a) [97-98]
+          GqlOperatorList [106-212]
+            GqlLet [106-115]
+              GqlLetVariableDefinitionList [110-115]
+                GqlLetVariableDefinition [110-115]
+                  Identifier(b) [110-111]
+                  PathExpression [114-115]
+                    Identifier(a) [114-115]
+            GqlReturn [193-212]
+              Select [200-212]
+                SelectList [200-212]
+                  SelectColumn [200-212]
+                    PathExpression [200-204]
+                      Identifier(b) [200-201]
+                      Identifier(id) [202-204]
+                    Alias [205-212]
+                      Identifier(b_id) [208-212]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_5
+GRAPH FinGraph
+MATCH (source:Account)-[e:Transfers]->(destination:Account)
+LET a = source
+RETURN source.id
+
+NEXT
+
+LET b = a  -- ERROR: 'a' doesn't exist.
+RETURN b.id AS b_id
+--
+QueryStatement [0-173]
+  Query [0-173]
+    GqlQuery [0-173]
+      GraphTableQuery [0-173]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-173]
+          GqlOperatorList [15-106]
+            GqlMatch [15-74]
+              GraphPattern [21-74]
+                GraphPathPattern [21-74]
+                  GraphNodePattern [21-37]
+                    GraphElementPatternFiller [22-36]
+                      Identifier(source) [22-28]
+                      GraphLabelFilter [28-36]
+                        GraphElementLabel [29-36]
+                          Identifier(Account) [29-36]
+                  GraphEdgePattern [37-53]
+                    GraphElementPatternFiller [39-50]
+                      Identifier(e) [39-40]
+                      GraphLabelFilter [40-50]
+                        GraphElementLabel [41-50]
+                          Identifier(Transfers) [41-50]
+                  GraphNodePattern [53-74]
+                    GraphElementPatternFiller [54-73]
+                      Identifier(destination) [54-65]
+                      GraphLabelFilter [65-73]
+                        GraphElementLabel [66-73]
+                          Identifier(Account) [66-73]
+            GqlLet [75-89]
+              GqlLetVariableDefinitionList [79-89]
+                GqlLetVariableDefinition [79-89]
+                  Identifier(a) [79-80]
+                  PathExpression [83-89]
+                    Identifier(source) [83-89]
+            GqlReturn [90-106]
+              Select [97-106]
+                SelectList [97-106]
+                  SelectColumn [97-106]
+                    PathExpression [97-106]
+                      Identifier(source) [97-103]
+                      Identifier(id) [104-106]
+          GqlOperatorList [114-173]
+            GqlLet [114-123]
+              GqlLetVariableDefinitionList [118-123]
+                GqlLetVariableDefinition [118-123]
+                  Identifier(b) [118-119]
+                  PathExpression [122-123]
+                    Identifier(a) [122-123]
+            GqlReturn [154-173]
+              Select [161-173]
+                SelectList [161-173]
+                  SelectColumn [161-173]
+                    PathExpression [161-165]
+                      Identifier(b) [161-162]
+                      Identifier(id) [163-165]
+                    Alias [166-173]
+                      Identifier(b_id) [169-173]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_5
+GRAPH FinGraph
+MATCH (source:Account)-[e:Transfers]->(destination:Account)
+LET a = source, b = a -- ERROR: Can't define and reference 'a' in the same operation.
+RETURN a
+--
+QueryStatement [0-169]
+  Query [0-169]
+    GqlQuery [0-169]
+      GraphTableQuery [0-169]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-169]
+          GqlOperatorList [15-169]
+            GqlMatch [15-74]
+              GraphPattern [21-74]
+                GraphPathPattern [21-74]
+                  GraphNodePattern [21-37]
+                    GraphElementPatternFiller [22-36]
+                      Identifier(source) [22-28]
+                      GraphLabelFilter [28-36]
+                        GraphElementLabel [29-36]
+                          Identifier(Account) [29-36]
+                  GraphEdgePattern [37-53]
+                    GraphElementPatternFiller [39-50]
+                      Identifier(e) [39-40]
+                      GraphLabelFilter [40-50]
+                        GraphElementLabel [41-50]
+                          Identifier(Transfers) [41-50]
+                  GraphNodePattern [53-74]
+                    GraphElementPatternFiller [54-73]
+                      Identifier(destination) [54-65]
+                      GraphLabelFilter [65-73]
+                        GraphElementLabel [66-73]
+                          Identifier(Account) [66-73]
+            GqlLet [75-96]
+              GqlLetVariableDefinitionList [79-96]
+                GqlLetVariableDefinition [79-89]
+                  Identifier(a) [79-80]
+                  PathExpression [83-89]
+                    Identifier(source) [83-89]
+                GqlLetVariableDefinition [91-96]
+                  Identifier(b) [91-92]
+                  PathExpression [95-96]
+                    Identifier(a) [95-96]
+            GqlReturn [161-169]
+              Select [168-169]
+                SelectList [168-169]
+                  SelectColumn [168-169]
+                    PathExpression [168-169]
+                      Identifier(a) [168-169]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_5
+GRAPH FinGraph
+MATCH (source:Account)-[e:Transfers]->(destination:Account)
+LET a = source
+LET b = a
+RETURN b.id AS b_id
+--
+QueryStatement [0-119]
+  Query [0-119]
+    GqlQuery [0-119]
+      GraphTableQuery [0-119]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-119]
+          GqlOperatorList [15-119]
+            GqlMatch [15-74]
+              GraphPattern [21-74]
+                GraphPathPattern [21-74]
+                  GraphNodePattern [21-37]
+                    GraphElementPatternFiller [22-36]
+                      Identifier(source) [22-28]
+                      GraphLabelFilter [28-36]
+                        GraphElementLabel [29-36]
+                          Identifier(Account) [29-36]
+                  GraphEdgePattern [37-53]
+                    GraphElementPatternFiller [39-50]
+                      Identifier(e) [39-40]
+                      GraphLabelFilter [40-50]
+                        GraphElementLabel [41-50]
+                          Identifier(Transfers) [41-50]
+                  GraphNodePattern [53-74]
+                    GraphElementPatternFiller [54-73]
+                      Identifier(destination) [54-65]
+                      GraphLabelFilter [65-73]
+                        GraphElementLabel [66-73]
+                          Identifier(Account) [66-73]
+            GqlLet [75-89]
+              GqlLetVariableDefinitionList [79-89]
+                GqlLetVariableDefinition [79-89]
+                  Identifier(a) [79-80]
+                  PathExpression [83-89]
+                    Identifier(source) [83-89]
+            GqlLet [90-99]
+              GqlLetVariableDefinitionList [94-99]
+                GqlLetVariableDefinition [94-99]
+                  Identifier(b) [94-95]
+                  PathExpression [98-99]
+                    Identifier(a) [98-99]
+            GqlReturn [100-119]
+              Select [107-119]
+                SelectList [107-119]
+                  SelectColumn [107-119]
+                    PathExpression [107-111]
+                      Identifier(b) [107-108]
+                      Identifier(id) [109-111]
+                    Alias [112-119]
+                      Identifier(b_id) [115-119]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_5
+GRAPH FinGraph
+MATCH (source:Account)-[e:Transfers]->(destination:Account)
+LET a = source, a = destination -- ERROR: 'a' has already been defined.
+RETURN a.id AS a_id
+--
+QueryStatement [0-166]
+  Query [0-166]
+    GqlQuery [0-166]
+      GraphTableQuery [0-166]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-166]
+          GqlOperatorList [15-166]
+            GqlMatch [15-74]
+              GraphPattern [21-74]
+                GraphPathPattern [21-74]
+                  GraphNodePattern [21-37]
+                    GraphElementPatternFiller [22-36]
+                      Identifier(source) [22-28]
+                      GraphLabelFilter [28-36]
+                        GraphElementLabel [29-36]
+                          Identifier(Account) [29-36]
+                  GraphEdgePattern [37-53]
+                    GraphElementPatternFiller [39-50]
+                      Identifier(e) [39-40]
+                      GraphLabelFilter [40-50]
+                        GraphElementLabel [41-50]
+                          Identifier(Transfers) [41-50]
+                  GraphNodePattern [53-74]
+                    GraphElementPatternFiller [54-73]
+                      Identifier(destination) [54-65]
+                      GraphLabelFilter [65-73]
+                        GraphElementLabel [66-73]
+                          Identifier(Account) [66-73]
+            GqlLet [75-106]
+              GqlLetVariableDefinitionList [79-106]
+                GqlLetVariableDefinition [79-89]
+                  Identifier(a) [79-80]
+                  PathExpression [83-89]
+                    Identifier(source) [83-89]
+                GqlLetVariableDefinition [91-106]
+                  Identifier(a) [91-92]
+                  PathExpression [95-106]
+                    Identifier(destination) [95-106]
+            GqlReturn [147-166]
+              Select [154-166]
+                SelectList [154-166]
+                  SelectColumn [154-166]
+                    PathExpression [154-158]
+                      Identifier(a) [154-155]
+                      Identifier(id) [156-158]
+                    Alias [159-166]
+                      Identifier(a_id) [162-166]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_5
+GRAPH FinGraph
+MATCH (source:Account)-[e:Transfers]->(destination:Account)
+LET a = source
+LET a = destination -- ERROR: 'a' has already been defined.
+RETURN a.id AS a_id
+--
+QueryStatement [0-169]
+  Query [0-169]
+    GqlQuery [0-169]
+      GraphTableQuery [0-169]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-169]
+          GqlOperatorList [15-169]
+            GqlMatch [15-74]
+              GraphPattern [21-74]
+                GraphPathPattern [21-74]
+                  GraphNodePattern [21-37]
+                    GraphElementPatternFiller [22-36]
+                      Identifier(source) [22-28]
+                      GraphLabelFilter [28-36]
+                        GraphElementLabel [29-36]
+                          Identifier(Account) [29-36]
+                  GraphEdgePattern [37-53]
+                    GraphElementPatternFiller [39-50]
+                      Identifier(e) [39-40]
+                      GraphLabelFilter [40-50]
+                        GraphElementLabel [41-50]
+                          Identifier(Transfers) [41-50]
+                  GraphNodePattern [53-74]
+                    GraphElementPatternFiller [54-73]
+                      Identifier(destination) [54-65]
+                      GraphLabelFilter [65-73]
+                        GraphElementLabel [66-73]
+                          Identifier(Account) [66-73]
+            GqlLet [75-89]
+              GqlLetVariableDefinitionList [79-89]
+                GqlLetVariableDefinition [79-89]
+                  Identifier(a) [79-80]
+                  PathExpression [83-89]
+                    Identifier(source) [83-89]
+            GqlLet [90-109]
+              GqlLetVariableDefinitionList [94-109]
+                GqlLetVariableDefinition [94-109]
+                  Identifier(a) [94-95]
+                  PathExpression [98-109]
+                    Identifier(destination) [98-109]
+            GqlReturn [150-169]
+              Select [157-169]
+                SelectList [157-169]
+                  SelectColumn [157-169]
+                    PathExpression [157-161]
+                      Identifier(a) [157-158]
+                      Identifier(id) [159-161]
+                    Alias [162-169]
+                      Identifier(a_id) [165-169]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_5
+GRAPH FinGraph
+MATCH (source:Account)-[e:Transfers]->(destination:Account)
+LET a = source
+LET b = destination
+RETURN a, b
+
+NEXT
+
+MATCH (a)
+LET b = a -- ERROR: 'b' has already been defined.
+RETURN b.id
+--
+QueryStatement [0-200]
+  Query [0-200]
+    GqlQuery [0-200]
+      GraphTableQuery [0-200]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-200]
+          GqlOperatorList [15-121]
+            GqlMatch [15-74]
+              GraphPattern [21-74]
+                GraphPathPattern [21-74]
+                  GraphNodePattern [21-37]
+                    GraphElementPatternFiller [22-36]
+                      Identifier(source) [22-28]
+                      GraphLabelFilter [28-36]
+                        GraphElementLabel [29-36]
+                          Identifier(Account) [29-36]
+                  GraphEdgePattern [37-53]
+                    GraphElementPatternFiller [39-50]
+                      Identifier(e) [39-40]
+                      GraphLabelFilter [40-50]
+                        GraphElementLabel [41-50]
+                          Identifier(Transfers) [41-50]
+                  GraphNodePattern [53-74]
+                    GraphElementPatternFiller [54-73]
+                      Identifier(destination) [54-65]
+                      GraphLabelFilter [65-73]
+                        GraphElementLabel [66-73]
+                          Identifier(Account) [66-73]
+            GqlLet [75-89]
+              GqlLetVariableDefinitionList [79-89]
+                GqlLetVariableDefinition [79-89]
+                  Identifier(a) [79-80]
+                  PathExpression [83-89]
+                    Identifier(source) [83-89]
+            GqlLet [90-109]
+              GqlLetVariableDefinitionList [94-109]
+                GqlLetVariableDefinition [94-109]
+                  Identifier(b) [94-95]
+                  PathExpression [98-109]
+                    Identifier(destination) [98-109]
+            GqlReturn [110-121]
+              Select [117-121]
+                SelectList [117-121]
+                  SelectColumn [117-118]
+                    PathExpression [117-118]
+                      Identifier(a) [117-118]
+                  SelectColumn [120-121]
+                    PathExpression [120-121]
+                      Identifier(b) [120-121]
+          GqlOperatorList [129-200]
+            GqlMatch [129-138]
+              GraphPattern [135-138]
+                GraphPathPattern [135-138]
+                  GraphNodePattern [135-138]
+                    GraphElementPatternFiller [136-137]
+                      Identifier(a) [136-137]
+            GqlLet [139-148]
+              GqlLetVariableDefinitionList [143-148]
+                GqlLetVariableDefinition [143-148]
+                  Identifier(b) [143-144]
+                  PathExpression [147-148]
+                    Identifier(a) [147-148]
+            GqlReturn [189-200]
+              Select [196-200]
+                SelectList [196-200]
+                  SelectColumn [196-200]
+                    PathExpression [196-200]
+                      Identifier(b) [196-197]
+                      Identifier(id) [198-200]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_5
+GRAPH FinGraph
+MATCH (source:Account)-[e:Transfers]->(destination:Account)
+LET a = source
+LET b = destination
+RETURN a
+
+NEXT
+
+MATCH (a)
+LET b = a
+RETURN b.id
+--
+QueryStatement [0-157]
+  Query [0-157]
+    GqlQuery [0-157]
+      GraphTableQuery [0-157]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-157]
+          GqlOperatorList [15-118]
+            GqlMatch [15-74]
+              GraphPattern [21-74]
+                GraphPathPattern [21-74]
+                  GraphNodePattern [21-37]
+                    GraphElementPatternFiller [22-36]
+                      Identifier(source) [22-28]
+                      GraphLabelFilter [28-36]
+                        GraphElementLabel [29-36]
+                          Identifier(Account) [29-36]
+                  GraphEdgePattern [37-53]
+                    GraphElementPatternFiller [39-50]
+                      Identifier(e) [39-40]
+                      GraphLabelFilter [40-50]
+                        GraphElementLabel [41-50]
+                          Identifier(Transfers) [41-50]
+                  GraphNodePattern [53-74]
+                    GraphElementPatternFiller [54-73]
+                      Identifier(destination) [54-65]
+                      GraphLabelFilter [65-73]
+                        GraphElementLabel [66-73]
+                          Identifier(Account) [66-73]
+            GqlLet [75-89]
+              GqlLetVariableDefinitionList [79-89]
+                GqlLetVariableDefinition [79-89]
+                  Identifier(a) [79-80]
+                  PathExpression [83-89]
+                    Identifier(source) [83-89]
+            GqlLet [90-109]
+              GqlLetVariableDefinitionList [94-109]
+                GqlLetVariableDefinition [94-109]
+                  Identifier(b) [94-95]
+                  PathExpression [98-109]
+                    Identifier(destination) [98-109]
+            GqlReturn [110-118]
+              Select [117-118]
+                SelectList [117-118]
+                  SelectColumn [117-118]
+                    PathExpression [117-118]
+                      Identifier(a) [117-118]
+          GqlOperatorList [126-157]
+            GqlMatch [126-135]
+              GraphPattern [132-135]
+                GraphPathPattern [132-135]
+                  GraphNodePattern [132-135]
+                    GraphElementPatternFiller [133-134]
+                      Identifier(a) [133-134]
+            GqlLet [136-145]
+              GqlLetVariableDefinitionList [140-145]
+                GqlLetVariableDefinition [140-145]
+                  Identifier(b) [140-141]
+                  PathExpression [144-145]
+                    Identifier(a) [144-145]
+            GqlReturn [146-157]
+              Select [153-157]
+                SelectList [153-157]
+                  SelectColumn [153-157]
+                    PathExpression [153-157]
+                      Identifier(b) [153-154]
+                      Identifier(id) [155-157]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_6
+GRAPH FinGraph
+MATCH (source:Account)-[e:Transfers]->(destination:Account)
+ORDER BY source.Id
+LIMIT 3
+RETURN source.Id, source.nick_name
+--
+QueryStatement [0-136]
+  Query [0-136]
+    GqlQuery [0-136]
+      GraphTableQuery [0-136]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-136]
+          GqlOperatorList [15-136]
+            GqlMatch [15-74]
+              GraphPattern [21-74]
+                GraphPathPattern [21-74]
+                  GraphNodePattern [21-37]
+                    GraphElementPatternFiller [22-36]
+                      Identifier(source) [22-28]
+                      GraphLabelFilter [28-36]
+                        GraphElementLabel [29-36]
+                          Identifier(Account) [29-36]
+                  GraphEdgePattern [37-53]
+                    GraphElementPatternFiller [39-50]
+                      Identifier(e) [39-40]
+                      GraphLabelFilter [40-50]
+                        GraphElementLabel [41-50]
+                          Identifier(Transfers) [41-50]
+                  GraphNodePattern [53-74]
+                    GraphElementPatternFiller [54-73]
+                      Identifier(destination) [54-65]
+                      GraphLabelFilter [65-73]
+                        GraphElementLabel [66-73]
+                          Identifier(Account) [66-73]
+            GqlOrderByAndPage [75-93]
+              OrderBy [75-93]
+                OrderingExpression(ASC) [84-93]
+                  PathExpression [84-93]
+                    Identifier(source) [84-90]
+                    Identifier(Id) [91-93]
+            GqlOrderByAndPage [94-101]
+              GqlPage [94-101]
+                GqlPageLimit [94-101]
+                  IntLiteral(3) [100-101]
+            GqlReturn [102-136]
+              Select [109-136]
+                SelectList [109-136]
+                  SelectColumn [109-118]
+                    PathExpression [109-118]
+                      Identifier(source) [109-115]
+                      Identifier(Id) [116-118]
+                  SelectColumn [120-136]
+                    PathExpression [120-136]
+                      Identifier(source) [120-126]
+                      Identifier(nick_name) [127-136]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_6
+GRAPH FinGraph
+MATCH (src_account:Account)-[transfer:Transfers]->(dst_account:Account {is_blocked:true})
+RETURN src_account, COUNT(transfer) as total_transfers
+ORDER BY total_transfers
+LIMIT 1
+NEXT
+MATCH (src_account:Account)<-[owns:Owns]-(owner:Person)
+RETURN src_account.id AS account_id, owner.name AS owner_name
+--
+QueryStatement [0-315]
+  Query [0-315]
+    GqlQuery [0-315]
+      GraphTableQuery [0-315]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-315]
+          GqlOperatorList [15-192]
+            GqlMatch [15-104]
+              GraphPattern [21-104]
+                GraphPathPattern [21-104]
+                  GraphNodePattern [21-42]
+                    GraphElementPatternFiller [22-41]
+                      Identifier(src_account) [22-33]
+                      GraphLabelFilter [33-41]
+                        GraphElementLabel [34-41]
+                          Identifier(Account) [34-41]
+                  GraphEdgePattern [42-65]
+                    GraphElementPatternFiller [44-62]
+                      Identifier(transfer) [44-52]
+                      GraphLabelFilter [52-62]
+                        GraphElementLabel [53-62]
+                          Identifier(Transfers) [53-62]
+                  GraphNodePattern [65-104]
+                    GraphElementPatternFiller [66-103]
+                      Identifier(dst_account) [66-77]
+                      GraphLabelFilter [77-85]
+                        GraphElementLabel [78-85]
+                          Identifier(Account) [78-85]
+                      GraphPropertySpecification [86-103]
+                        GraphPropertyNameAndValue [87-102]
+                          Identifier(is_blocked) [87-97]
+                          BooleanLiteral(true) [98-102]
+            GqlReturn [105-192]
+              Select [112-159]
+                SelectList [112-159]
+                  SelectColumn [112-123]
+                    PathExpression [112-123]
+                      Identifier(src_account) [112-123]
+                  SelectColumn [125-159]
+                    FunctionCall [125-140]
+                      PathExpression [125-130]
+                        Identifier(COUNT) [125-130]
+                      PathExpression [131-139]
+                        Identifier(transfer) [131-139]
+                    Alias [141-159]
+                      Identifier(total_transfers) [144-159]
+              GqlOrderByAndPage [160-192]
+                OrderBy [160-184]
+                  OrderingExpression(ASC) [169-184]
+                    PathExpression [169-184]
+                      Identifier(total_transfers) [169-184]
+                GqlPage [185-192]
+                  GqlPageLimit [185-192]
+                    IntLiteral(1) [191-192]
+          GqlOperatorList [198-315]
+            GqlMatch [198-253]
+              GraphPattern [204-253]
+                GraphPathPattern [204-253]
+                  GraphNodePattern [204-225]
+                    GraphElementPatternFiller [205-224]
+                      Identifier(src_account) [205-216]
+                      GraphLabelFilter [216-224]
+                        GraphElementLabel [217-224]
+                          Identifier(Account) [217-224]
+                  GraphEdgePattern [225-239]
+                    GraphElementPatternFiller [228-237]
+                      Identifier(owns) [228-232]
+                      GraphLabelFilter [232-237]
+                        GraphElementLabel [233-237]
+                          Identifier(Owns) [233-237]
+                  GraphNodePattern [239-253]
+                    GraphElementPatternFiller [240-252]
+                      Identifier(owner) [240-245]
+                      GraphLabelFilter [245-252]
+                        GraphElementLabel [246-252]
+                          Identifier(Person) [246-252]
+            GqlReturn [254-315]
+              Select [261-315]
+                SelectList [261-315]
+                  SelectColumn [261-289]
+                    PathExpression [261-275]
+                      Identifier(src_account) [261-272]
+                      Identifier(id) [273-275]
+                    Alias [276-289]
+                      Identifier(account_id) [279-289]
+                  SelectColumn [291-315]
+                    PathExpression [291-301]
+                      Identifier(owner) [291-296]
+                      Identifier(name) [297-301]
+                    Alias [302-315]
+                      Identifier(owner_name) [305-315]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_7
+GRAPH FinGraph
+MATCH (p:Person)
+RETURN p.name, p.id
+--
+QueryStatement [0-51]
+  Query [0-51]
+    GqlQuery [0-51]
+      GraphTableQuery [0-51]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-51]
+          GqlOperatorList [15-51]
+            GqlMatch [15-31]
+              GraphPattern [21-31]
+                GraphPathPattern [21-31]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+            GqlReturn [32-51]
+              Select [39-51]
+                SelectList [39-51]
+                  SelectColumn [39-45]
+                    PathExpression [39-45]
+                      Identifier(p) [39-40]
+                      Identifier(name) [41-45]
+                  SelectColumn [47-51]
+                    PathExpression [47-51]
+                      Identifier(p) [47-48]
+                      Identifier(id) [49-51]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_7
+GRAPH FinGraph
+MATCH (n:Person|Account)
+RETURN LABELS(n) AS label, n.id
+--
+QueryStatement [0-71]
+  Query [0-71]
+    GqlQuery [0-71]
+      GraphTableQuery [0-71]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-71]
+          GqlOperatorList [15-71]
+            GqlMatch [15-39]
+              GraphPattern [21-39]
+                GraphPathPattern [21-39]
+                  GraphNodePattern [21-39]
+                    GraphElementPatternFiller [22-38]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-38]
+                        GraphLabelOperation(OR) [24-38]
+                          GraphElementLabel [24-30]
+                            Identifier(Person) [24-30]
+                          GraphElementLabel [31-38]
+                            Identifier(Account) [31-38]
+            GqlReturn [40-71]
+              Select [47-71]
+                SelectList [47-71]
+                  SelectColumn [47-65]
+                    FunctionCall [47-56]
+                      PathExpression [47-53]
+                        Identifier(LABELS) [47-53]
+                      PathExpression [54-55]
+                        Identifier(n) [54-55]
+                    Alias [57-65]
+                      Identifier(label) [60-65]
+                  SelectColumn [67-71]
+                    PathExpression [67-71]
+                      Identifier(n) [67-68]
+                      Identifier(id) [69-71]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_7
+GRAPH FinGraph
+MATCH (a:Account {is_blocked: false})
+RETURN a.id
+--
+QueryStatement [0-64]
+  Query [0-64]
+    GqlQuery [0-64]
+      GraphTableQuery [0-64]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-64]
+          GqlOperatorList [15-64]
+            GqlMatch [15-52]
+              GraphPattern [21-52]
+                GraphPathPattern [21-52]
+                  GraphNodePattern [21-52]
+                    GraphElementPatternFiller [22-51]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                      GraphPropertySpecification [32-51]
+                        GraphPropertyNameAndValue [33-50]
+                          Identifier(is_blocked) [33-43]
+                          BooleanLiteral(false) [45-50]
+            GqlReturn [53-64]
+              Select [60-64]
+                SelectList [60-64]
+                  SelectColumn [60-64]
+                    PathExpression [60-64]
+                      Identifier(a) [60-61]
+                      Identifier(id) [62-64]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_7
+GRAPH FinGraph
+MATCH (p:Person WHERE p.birthday < '1990-01-10')
+RETURN p.name
+--
+QueryStatement [0-77]
+  Query [0-77]
+    GqlQuery [0-77]
+      GraphTableQuery [0-77]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-77]
+          GqlOperatorList [15-77]
+            GqlMatch [15-63]
+              GraphPattern [21-63]
+                GraphPathPattern [21-63]
+                  GraphNodePattern [21-63]
+                    GraphElementPatternFiller [22-62]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                      WhereClause [31-62]
+                        BinaryExpression(<) [37-62]
+                          PathExpression [37-47]
+                            Identifier(p) [37-38]
+                            Identifier(birthday) [39-47]
+                          StringLiteral [50-62]
+                            StringLiteralComponent('1990-01-10') [50-62]
+            GqlReturn [64-77]
+              Select [71-77]
+                SelectList [71-77]
+                  SelectColumn [71-77]
+                    PathExpression [71-77]
+                      Identifier(p) [71-72]
+                      Identifier(name) [73-77]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_7
+GRAPH FinGraph
+MATCH -[e:Owns]->
+RETURN e.id
+--
+QueryStatement [0-44]
+  Query [0-44]
+    GqlQuery [0-44]
+      GraphTableQuery [0-44]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-44]
+          GqlOperatorList [15-44]
+            GqlMatch [15-32]
+              GraphPattern [21-32]
+                GraphPathPattern [21-32]
+                  GraphEdgePattern [21-32]
+                    GraphElementPatternFiller [23-29]
+                      Identifier(e) [23-24]
+                      GraphLabelFilter [24-29]
+                        GraphElementLabel [25-29]
+                          Identifier(Owns) [25-29]
+            GqlReturn [33-44]
+              Select [40-44]
+                SelectList [40-44]
+                  SelectColumn [40-44]
+                    PathExpression [40-44]
+                      Identifier(e) [40-41]
+                      Identifier(id) [42-44]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_7
+GRAPH FinGraph
+MATCH -[e:Owns WHERE e.create_time > '2020-01-14' AND e.create_time < '2020-05-14']->
+RETURN e.id
+--
+QueryStatement [0-112]
+  Query [0-112]
+    GqlQuery [0-112]
+      GraphTableQuery [0-112]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-112]
+          GqlOperatorList [15-112]
+            GqlMatch [15-100]
+              GraphPattern [21-100]
+                GraphPathPattern [21-100]
+                  GraphEdgePattern [21-100]
+                    GraphElementPatternFiller [23-97]
+                      Identifier(e) [23-24]
+                      GraphLabelFilter [24-29]
+                        GraphElementLabel [25-29]
+                          Identifier(Owns) [25-29]
+                      WhereClause [30-97]
+                        AndExpr [36-97]
+                          BinaryExpression(>) [36-64]
+                            PathExpression [36-49]
+                              Identifier(e) [36-37]
+                              Identifier(create_time) [38-49]
+                            StringLiteral [52-64]
+                              StringLiteralComponent('2020-01-14') [52-64]
+                          BinaryExpression(<) [69-97]
+                            PathExpression [69-82]
+                              Identifier(e) [69-70]
+                              Identifier(create_time) [71-82]
+                            StringLiteral [85-97]
+                              StringLiteralComponent('2020-05-14') [85-97]
+            GqlReturn [101-112]
+              Select [108-112]
+                SelectList [108-112]
+                  SelectColumn [108-112]
+                    PathExpression [108-112]
+                      Identifier(e) [108-109]
+                      Identifier(id) [110-112]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_7
+GRAPH FinGraph
+MATCH (account:Account)-[transfer:Transfers]-(:Account {is_blocked:true})
+RETURN transfer.order_number, transfer.amount
+--
+QueryStatement [0-134]
+  Query [0-134]
+    GqlQuery [0-134]
+      GraphTableQuery [0-134]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-134]
+          GqlOperatorList [15-134]
+            GqlMatch [15-88]
+              GraphPattern [21-88]
+                GraphPathPattern [21-88]
+                  GraphNodePattern [21-38]
+                    GraphElementPatternFiller [22-37]
+                      Identifier(account) [22-29]
+                      GraphLabelFilter [29-37]
+                        GraphElementLabel [30-37]
+                          Identifier(Account) [30-37]
+                  GraphEdgePattern [38-60]
+                    GraphElementPatternFiller [40-58]
+                      Identifier(transfer) [40-48]
+                      GraphLabelFilter [48-58]
+                        GraphElementLabel [49-58]
+                          Identifier(Transfers) [49-58]
+                  GraphNodePattern [60-88]
+                    GraphElementPatternFiller [61-87]
+                      GraphLabelFilter [61-69]
+                        GraphElementLabel [62-69]
+                          Identifier(Account) [62-69]
+                      GraphPropertySpecification [70-87]
+                        GraphPropertyNameAndValue [71-86]
+                          Identifier(is_blocked) [71-81]
+                          BooleanLiteral(true) [82-86]
+            GqlReturn [89-134]
+              Select [96-134]
+                SelectList [96-134]
+                  SelectColumn [96-117]
+                    PathExpression [96-117]
+                      Identifier(transfer) [96-104]
+                      Identifier(order_number) [105-117]
+                  SelectColumn [119-134]
+                    PathExpression [119-134]
+                      Identifier(transfer) [119-127]
+                      Identifier(amount) [128-134]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_7
+GRAPH FinGraph
+MATCH
+  (p:Person {id: 2})-[:Owns]->(account:Account)-[t:Transfers]->
+  (to_account:Account)
+RETURN p.id AS sender_id, to_account.id AS to_id
+--
+QueryStatement [0-156]
+  Query [0-156]
+    GqlQuery [0-156]
+      GraphTableQuery [0-156]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-156]
+          GqlOperatorList [15-156]
+            GqlMatch [15-107]
+              GraphPattern [23-107]
+                GraphPathPattern [23-107]
+                  GraphNodePattern [23-41]
+                    GraphElementPatternFiller [24-40]
+                      Identifier(p) [24-25]
+                      GraphLabelFilter [25-32]
+                        GraphElementLabel [26-32]
+                          Identifier(Person) [26-32]
+                      GraphPropertySpecification [33-40]
+                        GraphPropertyNameAndValue [34-39]
+                          Identifier(id) [34-36]
+                          IntLiteral(2) [38-39]
+                  GraphEdgePattern [41-51]
+                    GraphElementPatternFiller [43-48]
+                      GraphLabelFilter [43-48]
+                        GraphElementLabel [44-48]
+                          Identifier(Owns) [44-48]
+                  GraphNodePattern [51-68]
+                    GraphElementPatternFiller [52-67]
+                      Identifier(account) [52-59]
+                      GraphLabelFilter [59-67]
+                        GraphElementLabel [60-67]
+                          Identifier(Account) [60-67]
+                  GraphEdgePattern [68-84]
+                    GraphElementPatternFiller [70-81]
+                      Identifier(t) [70-71]
+                      GraphLabelFilter [71-81]
+                        GraphElementLabel [72-81]
+                          Identifier(Transfers) [72-81]
+                  GraphNodePattern [87-107]
+                    GraphElementPatternFiller [88-106]
+                      Identifier(to_account) [88-98]
+                      GraphLabelFilter [98-106]
+                        GraphElementLabel [99-106]
+                          Identifier(Account) [99-106]
+            GqlReturn [108-156]
+              Select [115-156]
+                SelectList [115-156]
+                  SelectColumn [115-132]
+                    PathExpression [115-119]
+                      Identifier(p) [115-116]
+                      Identifier(id) [117-119]
+                    Alias [120-132]
+                      Identifier(sender_id) [123-132]
+                  SelectColumn [134-156]
+                    PathExpression [134-147]
+                      Identifier(to_account) [134-144]
+                      Identifier(id) [145-147]
+                    Alias [148-156]
+                      Identifier(to_id) [151-156]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_7
+GRAPH FinGraph
+MATCH (src:Account {id: 7})-[e:Transfers]->{1, 3}(dst:Account)
+WHERE src != dst
+RETURN ARRAY_LENGTH(e) AS hops, dst.id AS destination_account_id
+--
+QueryStatement [0-159]
+  Query [0-159]
+    GqlQuery [0-159]
+      GraphTableQuery [0-159]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-159]
+          GqlOperatorList [15-159]
+            GqlMatch [15-94]
+              GraphPattern [21-94]
+                GraphPathPattern [21-77]
+                  GraphNodePattern [21-42]
+                    GraphElementPatternFiller [22-41]
+                      Identifier(src) [22-25]
+                      GraphLabelFilter [25-33]
+                        GraphElementLabel [26-33]
+                          Identifier(Account) [26-33]
+                      GraphPropertySpecification [34-41]
+                        GraphPropertyNameAndValue [35-40]
+                          Identifier(id) [35-37]
+                          IntLiteral(7) [39-40]
+                  ParenthesizedGraphPathPattern [42-64]
+                    BoundedQuantifier [58-64]
+                      QuantifierBound [59-60]
+                        IntLiteral(1) [59-60]
+                      QuantifierBound [62-63]
+                        IntLiteral(3) [62-63]
+                    GraphEdgePattern [42-58]
+                      GraphElementPatternFiller [44-55]
+                        Identifier(e) [44-45]
+                        GraphLabelFilter [45-55]
+                          GraphElementLabel [46-55]
+                            Identifier(Transfers) [46-55]
+                  GraphNodePattern [64-77]
+                    GraphElementPatternFiller [65-76]
+                      Identifier(dst) [65-68]
+                      GraphLabelFilter [68-76]
+                        GraphElementLabel [69-76]
+                          Identifier(Account) [69-76]
+                WhereClause [78-94]
+                  BinaryExpression(!=) [84-94]
+                    PathExpression [84-87]
+                      Identifier(src) [84-87]
+                    PathExpression [91-94]
+                      Identifier(dst) [91-94]
+            GqlReturn [95-159]
+              Select [102-159]
+                SelectList [102-159]
+                  SelectColumn [102-125]
+                    FunctionCall [102-117]
+                      PathExpression [102-114]
+                        Identifier(ARRAY_LENGTH) [102-114]
+                      PathExpression [115-116]
+                        Identifier(e) [115-116]
+                    Alias [118-125]
+                      Identifier(hops) [121-125]
+                  SelectColumn [127-159]
+                    PathExpression [127-133]
+                      Identifier(dst) [127-130]
+                      Identifier(id) [131-133]
+                    Alias [134-159]
+                      Identifier(destination_account_id) [137-159]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_7
+GRAPH FinGraph
+MATCH
+  (src:Account)
+  ((a:Account)-[:Transfers]->(b:Account {is_blocked:true}) WHERE a != b ){1,2}
+  -[:Transfers]->(dst:Account)
+RETURN src.id AS source_account_id, dst.id AS destination_account_id
+--
+QueryStatement [0-215]
+  Query [0-215]
+    GqlQuery [0-215]
+      GraphTableQuery [0-215]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-215]
+          GqlOperatorList [15-215]
+            GqlMatch [15-146]
+              GraphPattern [23-146]
+                GraphPathPattern [23-146]
+                  GraphNodePattern [23-36]
+                    GraphElementPatternFiller [24-35]
+                      Identifier(src) [24-27]
+                      GraphLabelFilter [27-35]
+                        GraphElementLabel [28-35]
+                          Identifier(Account) [28-35]
+                  ParenthesizedGraphPathPattern [39-115]
+                    BoundedQuantifier [110-115]
+                      QuantifierBound [111-112]
+                        IntLiteral(1) [111-112]
+                      QuantifierBound [113-114]
+                        IntLiteral(2) [113-114]
+                    WhereClause [96-108]
+                      BinaryExpression(!=) [102-108]
+                        PathExpression [102-103]
+                          Identifier(a) [102-103]
+                        PathExpression [107-108]
+                          Identifier(b) [107-108]
+                    GraphNodePattern [40-51]
+                      GraphElementPatternFiller [41-50]
+                        Identifier(a) [41-42]
+                        GraphLabelFilter [42-50]
+                          GraphElementLabel [43-50]
+                            Identifier(Account) [43-50]
+                    GraphEdgePattern [51-66]
+                      GraphElementPatternFiller [53-63]
+                        GraphLabelFilter [53-63]
+                          GraphElementLabel [54-63]
+                            Identifier(Transfers) [54-63]
+                    GraphNodePattern [66-95]
+                      GraphElementPatternFiller [67-94]
+                        Identifier(b) [67-68]
+                        GraphLabelFilter [68-76]
+                          GraphElementLabel [69-76]
+                            Identifier(Account) [69-76]
+                        GraphPropertySpecification [77-94]
+                          GraphPropertyNameAndValue [78-93]
+                            Identifier(is_blocked) [78-88]
+                            BooleanLiteral(true) [89-93]
+                  GraphEdgePattern [118-133]
+                    GraphElementPatternFiller [120-130]
+                      GraphLabelFilter [120-130]
+                        GraphElementLabel [121-130]
+                          Identifier(Transfers) [121-130]
+                  GraphNodePattern [133-146]
+                    GraphElementPatternFiller [134-145]
+                      Identifier(dst) [134-137]
+                      GraphLabelFilter [137-145]
+                        GraphElementLabel [138-145]
+                          Identifier(Account) [138-145]
+            GqlReturn [147-215]
+              Select [154-215]
+                SelectList [154-215]
+                  SelectColumn [154-181]
+                    PathExpression [154-160]
+                      Identifier(src) [154-157]
+                      Identifier(id) [158-160]
+                    Alias [161-181]
+                      Identifier(source_account_id) [164-181]
+                  SelectColumn [183-215]
+                    PathExpression [183-189]
+                      Identifier(dst) [183-186]
+                      Identifier(id) [187-189]
+                    Alias [190-215]
+                      Identifier(destination_account_id) [193-215]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_7
+GRAPH FinGraph
+MATCH ANY (src:Account {id: 7})-[e:Transfers]->{1,2}(dst:Account)
+LET ids_in_path = ARRAY_CONCAT(ARRAY_AGG(e.Id), [dst.Id])
+RETURN src.id AS source_account_id, dst.id AS destination_account_id, ids_in_path
+--
+QueryStatement [0-220]
+  Query [0-220]
+    GqlQuery [0-220]
+      GraphTableQuery [0-220]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-220]
+          GqlOperatorList [15-220]
+            GqlMatch [15-80]
+              GraphPattern [21-80]
+                GraphPathPattern [21-80]
+                  GraphPathSearchPrefix [21-24]
+                  GraphNodePattern [25-46]
+                    GraphElementPatternFiller [26-45]
+                      Identifier(src) [26-29]
+                      GraphLabelFilter [29-37]
+                        GraphElementLabel [30-37]
+                          Identifier(Account) [30-37]
+                      GraphPropertySpecification [38-45]
+                        GraphPropertyNameAndValue [39-44]
+                          Identifier(id) [39-41]
+                          IntLiteral(7) [43-44]
+                  ParenthesizedGraphPathPattern [46-67]
+                    BoundedQuantifier [62-67]
+                      QuantifierBound [63-64]
+                        IntLiteral(1) [63-64]
+                      QuantifierBound [65-66]
+                        IntLiteral(2) [65-66]
+                    GraphEdgePattern [46-62]
+                      GraphElementPatternFiller [48-59]
+                        Identifier(e) [48-49]
+                        GraphLabelFilter [49-59]
+                          GraphElementLabel [50-59]
+                            Identifier(Transfers) [50-59]
+                  GraphNodePattern [67-80]
+                    GraphElementPatternFiller [68-79]
+                      Identifier(dst) [68-71]
+                      GraphLabelFilter [71-79]
+                        GraphElementLabel [72-79]
+                          Identifier(Account) [72-79]
+            GqlLet [81-138]
+              GqlLetVariableDefinitionList [85-138]
+                GqlLetVariableDefinition [85-138]
+                  Identifier(ids_in_path) [85-96]
+                  FunctionCall [99-138]
+                    PathExpression [99-111]
+                      Identifier(ARRAY_CONCAT) [99-111]
+                    FunctionCall [112-127]
+                      PathExpression [112-121]
+                        Identifier(ARRAY_AGG) [112-121]
+                      PathExpression [122-126]
+                        Identifier(e) [122-123]
+                        Identifier(Id) [124-126]
+                    ArrayConstructor [129-137]
+                      PathExpression [130-136]
+                        Identifier(dst) [130-133]
+                        Identifier(Id) [134-136]
+            GqlReturn [139-220]
+              Select [146-220]
+                SelectList [146-220]
+                  SelectColumn [146-173]
+                    PathExpression [146-152]
+                      Identifier(src) [146-149]
+                      Identifier(id) [150-152]
+                    Alias [153-173]
+                      Identifier(source_account_id) [156-173]
+                  SelectColumn [175-207]
+                    PathExpression [175-181]
+                      Identifier(dst) [175-178]
+                      Identifier(id) [179-181]
+                    Alias [182-207]
+                      Identifier(destination_account_id) [185-207]
+                  SelectColumn [209-220]
+                    PathExpression [209-220]
+                      Identifier(ids_in_path) [209-220]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_7
+GRAPH FinGraph
+MATCH (n:Person)
+OPTIONAL MATCH (n:Person)-[:Owns]->(a:Account {is_blocked: TRUE})
+RETURN n.name, a.id AS blocked_account_id
+--
+QueryStatement [0-139]
+  Query [0-139]
+    GqlQuery [0-139]
+      GraphTableQuery [0-139]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-139]
+          GqlOperatorList [15-139]
+            GqlMatch [15-31]
+              GraphPattern [21-31]
+                GraphPathPattern [21-31]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+            GqlMatch (optional) [32-97]
+              GraphPattern [47-97]
+                GraphPathPattern [47-97]
+                  GraphNodePattern [47-57]
+                    GraphElementPatternFiller [48-56]
+                      Identifier(n) [48-49]
+                      GraphLabelFilter [49-56]
+                        GraphElementLabel [50-56]
+                          Identifier(Person) [50-56]
+                  GraphEdgePattern [57-67]
+                    GraphElementPatternFiller [59-64]
+                      GraphLabelFilter [59-64]
+                        GraphElementLabel [60-64]
+                          Identifier(Owns) [60-64]
+                  GraphNodePattern [67-97]
+                    GraphElementPatternFiller [68-96]
+                      Identifier(a) [68-69]
+                      GraphLabelFilter [69-77]
+                        GraphElementLabel [70-77]
+                          Identifier(Account) [70-77]
+                      GraphPropertySpecification [78-96]
+                        GraphPropertyNameAndValue [79-95]
+                          Identifier(is_blocked) [79-89]
+                          BooleanLiteral(TRUE) [91-95]
+            GqlReturn [98-139]
+              Select [105-139]
+                SelectList [105-139]
+                  SelectColumn [105-111]
+                    PathExpression [105-111]
+                      Identifier(n) [105-106]
+                      Identifier(name) [107-111]
+                  SelectColumn [113-139]
+                    PathExpression [113-117]
+                      Identifier(a) [113-114]
+                      Identifier(id) [115-117]
+                    Alias [118-139]
+                      Identifier(blocked_account_id) [121-139]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_8
+GRAPH FinGraph
+MATCH (:Account)-[:Transfers]->(account:Account)
+RETURN account, COUNT(*) AS num_incoming_transfers
+GROUP BY account
+
+NEXT
+
+MATCH (account:Account)<-[:Owns]-(owner:Person)
+RETURN
+  account.id AS account_id, owner.name AS owner_name,
+  num_incoming_transfers
+
+NEXT
+
+FILTER num_incoming_transfers < 2
+RETURN account_id, owner_name
+--
+QueryStatement [0-343]
+  Query [0-343]
+    GqlQuery [0-343]
+      GraphTableQuery [0-343]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-343]
+          GqlOperatorList [15-131]
+            GqlMatch [15-63]
+              GraphPattern [21-63]
+                GraphPathPattern [21-63]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      GraphLabelFilter [22-30]
+                        GraphElementLabel [23-30]
+                          Identifier(Account) [23-30]
+                  GraphEdgePattern [31-46]
+                    GraphElementPatternFiller [33-43]
+                      GraphLabelFilter [33-43]
+                        GraphElementLabel [34-43]
+                          Identifier(Transfers) [34-43]
+                  GraphNodePattern [46-63]
+                    GraphElementPatternFiller [47-62]
+                      Identifier(account) [47-54]
+                      GraphLabelFilter [54-62]
+                        GraphElementLabel [55-62]
+                          Identifier(Account) [55-62]
+            GqlReturn [64-131]
+              Select [71-131]
+                SelectList [71-114]
+                  SelectColumn [71-78]
+                    PathExpression [71-78]
+                      Identifier(account) [71-78]
+                  SelectColumn [80-114]
+                    FunctionCall [80-88]
+                      PathExpression [80-85]
+                        Identifier(COUNT) [80-85]
+                      Star(*) [86-87]
+                    Alias [89-114]
+                      Identifier(num_incoming_transfers) [92-114]
+                GroupBy [115-131]
+                  GroupingItem [124-131]
+                    PathExpression [124-131]
+                      Identifier(account) [124-131]
+          GqlOperatorList [139-272]
+            GqlMatch [139-186]
+              GraphPattern [145-186]
+                GraphPathPattern [145-186]
+                  GraphNodePattern [145-162]
+                    GraphElementPatternFiller [146-161]
+                      Identifier(account) [146-153]
+                      GraphLabelFilter [153-161]
+                        GraphElementLabel [154-161]
+                          Identifier(Account) [154-161]
+                  GraphEdgePattern [162-172]
+                    GraphElementPatternFiller [165-170]
+                      GraphLabelFilter [165-170]
+                        GraphElementLabel [166-170]
+                          Identifier(Owns) [166-170]
+                  GraphNodePattern [172-186]
+                    GraphElementPatternFiller [173-185]
+                      Identifier(owner) [173-178]
+                      GraphLabelFilter [178-185]
+                        GraphElementLabel [179-185]
+                          Identifier(Person) [179-185]
+            GqlReturn [187-272]
+              Select [196-272]
+                SelectList [196-272]
+                  SelectColumn [196-220]
+                    PathExpression [196-206]
+                      Identifier(account) [196-203]
+                      Identifier(id) [204-206]
+                    Alias [207-220]
+                      Identifier(account_id) [210-220]
+                  SelectColumn [222-246]
+                    PathExpression [222-232]
+                      Identifier(owner) [222-227]
+                      Identifier(name) [228-232]
+                    Alias [233-246]
+                      Identifier(owner_name) [236-246]
+                  SelectColumn [250-272]
+                    PathExpression [250-272]
+                      Identifier(num_incoming_transfers) [250-272]
+          GqlOperatorList [280-343]
+            GqlFilter [280-313]
+              WhereClause [280-313]
+                BinaryExpression(<) [287-313]
+                  PathExpression [287-309]
+                    Identifier(num_incoming_transfers) [287-309]
+                  IntLiteral(2) [312-313]
+            GqlReturn [314-343]
+              Select [321-343]
+                SelectList [321-343]
+                  SelectColumn [321-331]
+                    PathExpression [321-331]
+                      Identifier(account_id) [321-331]
+                  SelectColumn [333-343]
+                    PathExpression [333-343]
+                      Identifier(owner_name) [333-343]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_9
+GRAPH FinGraph
+MATCH (p:Person)
+OFFSET 2
+RETURN p.name, p.id
+--
+QueryStatement [0-60]
+  Query [0-60]
+    GqlQuery [0-60]
+      GraphTableQuery [0-60]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-60]
+          GqlOperatorList [15-60]
+            GqlMatch [15-31]
+              GraphPattern [21-31]
+                GraphPathPattern [21-31]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+            GqlOrderByAndPage [32-40]
+              GqlPage [32-40]
+                GqlPageOffset [32-40]
+                  IntLiteral(2) [39-40]
+            GqlReturn [41-60]
+              Select [48-60]
+                SelectList [48-60]
+                  SelectColumn [48-54]
+                    PathExpression [48-54]
+                      Identifier(p) [48-49]
+                      Identifier(name) [50-54]
+                  SelectColumn [56-60]
+                    PathExpression [56-60]
+                      Identifier(p) [56-57]
+                      Identifier(id) [58-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_10
+GRAPH FinGraph
+MATCH (src_account:Account)-[transfer:Transfers]->(dst_account:Account)
+ORDER BY transfer.amount DESC
+LIMIT 3
+RETURN src_account.id AS account_id, transfer.amount AS transfer_amount
+--
+QueryStatement [0-196]
+  Query [0-196]
+    GqlQuery [0-196]
+      GraphTableQuery [0-196]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-196]
+          GqlOperatorList [15-196]
+            GqlMatch [15-86]
+              GraphPattern [21-86]
+                GraphPathPattern [21-86]
+                  GraphNodePattern [21-42]
+                    GraphElementPatternFiller [22-41]
+                      Identifier(src_account) [22-33]
+                      GraphLabelFilter [33-41]
+                        GraphElementLabel [34-41]
+                          Identifier(Account) [34-41]
+                  GraphEdgePattern [42-65]
+                    GraphElementPatternFiller [44-62]
+                      Identifier(transfer) [44-52]
+                      GraphLabelFilter [52-62]
+                        GraphElementLabel [53-62]
+                          Identifier(Transfers) [53-62]
+                  GraphNodePattern [65-86]
+                    GraphElementPatternFiller [66-85]
+                      Identifier(dst_account) [66-77]
+                      GraphLabelFilter [77-85]
+                        GraphElementLabel [78-85]
+                          Identifier(Account) [78-85]
+            GqlOrderByAndPage [87-116]
+              OrderBy [87-116]
+                OrderingExpression(DESC) [96-116]
+                  PathExpression [96-111]
+                    Identifier(transfer) [96-104]
+                    Identifier(amount) [105-111]
+            GqlOrderByAndPage [117-124]
+              GqlPage [117-124]
+                GqlPageLimit [117-124]
+                  IntLiteral(3) [123-124]
+            GqlReturn [125-196]
+              Select [132-196]
+                SelectList [132-196]
+                  SelectColumn [132-160]
+                    PathExpression [132-146]
+                      Identifier(src_account) [132-143]
+                      Identifier(id) [144-146]
+                    Alias [147-160]
+                      Identifier(account_id) [150-160]
+                  SelectColumn [162-196]
+                    PathExpression [162-177]
+                      Identifier(transfer) [162-170]
+                      Identifier(amount) [171-177]
+                    Alias [178-196]
+                      Identifier(transfer_amount) [181-196]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_10
+GRAPH FinGraph
+MATCH (src_account:Account)-[transfer:Transfers]->(dst_account:Account)
+ORDER BY transfer.amount DESC
+OFFSET 1
+RETURN src_account.id AS account_id, transfer.amount AS transfer_amount
+--
+QueryStatement [0-197]
+  Query [0-197]
+    GqlQuery [0-197]
+      GraphTableQuery [0-197]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-197]
+          GqlOperatorList [15-197]
+            GqlMatch [15-86]
+              GraphPattern [21-86]
+                GraphPathPattern [21-86]
+                  GraphNodePattern [21-42]
+                    GraphElementPatternFiller [22-41]
+                      Identifier(src_account) [22-33]
+                      GraphLabelFilter [33-41]
+                        GraphElementLabel [34-41]
+                          Identifier(Account) [34-41]
+                  GraphEdgePattern [42-65]
+                    GraphElementPatternFiller [44-62]
+                      Identifier(transfer) [44-52]
+                      GraphLabelFilter [52-62]
+                        GraphElementLabel [53-62]
+                          Identifier(Transfers) [53-62]
+                  GraphNodePattern [65-86]
+                    GraphElementPatternFiller [66-85]
+                      Identifier(dst_account) [66-77]
+                      GraphLabelFilter [77-85]
+                        GraphElementLabel [78-85]
+                          Identifier(Account) [78-85]
+            GqlOrderByAndPage [87-116]
+              OrderBy [87-116]
+                OrderingExpression(DESC) [96-116]
+                  PathExpression [96-111]
+                    Identifier(transfer) [96-104]
+                    Identifier(amount) [105-111]
+            GqlOrderByAndPage [117-125]
+              GqlPage [117-125]
+                GqlPageOffset [117-125]
+                  IntLiteral(1) [124-125]
+            GqlReturn [126-197]
+              Select [133-197]
+                SelectList [133-197]
+                  SelectColumn [133-161]
+                    PathExpression [133-147]
+                      Identifier(src_account) [133-144]
+                      Identifier(id) [145-147]
+                    Alias [148-161]
+                      Identifier(account_id) [151-161]
+                  SelectColumn [163-197]
+                    PathExpression [163-178]
+                      Identifier(transfer) [163-171]
+                      Identifier(amount) [172-178]
+                    Alias [179-197]
+                      Identifier(transfer_amount) [182-197]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_10
+-- Warning: The transfer.amount values aren't sorted because the
+-- LIMIT statement is missing.
+GRAPH FinGraph
+MATCH (src_account:Account)-[transfer:Transfers]->(dst_account:Account)
+ORDER BY transfer.amount DESC
+RETURN src_account.id AS account_id, transfer.amount AS transfer_amount
+--
+QueryStatement [96-284]
+  Query [96-284]
+    GqlQuery [96-284]
+      GraphTableQuery [96-284]
+        PathExpression [102-110]
+          Identifier(FinGraph) [102-110]
+        GqlOperatorList [111-284]
+          GqlOperatorList [111-284]
+            GqlMatch [111-182]
+              GraphPattern [117-182]
+                GraphPathPattern [117-182]
+                  GraphNodePattern [117-138]
+                    GraphElementPatternFiller [118-137]
+                      Identifier(src_account) [118-129]
+                      GraphLabelFilter [129-137]
+                        GraphElementLabel [130-137]
+                          Identifier(Account) [130-137]
+                  GraphEdgePattern [138-161]
+                    GraphElementPatternFiller [140-158]
+                      Identifier(transfer) [140-148]
+                      GraphLabelFilter [148-158]
+                        GraphElementLabel [149-158]
+                          Identifier(Transfers) [149-158]
+                  GraphNodePattern [161-182]
+                    GraphElementPatternFiller [162-181]
+                      Identifier(dst_account) [162-173]
+                      GraphLabelFilter [173-181]
+                        GraphElementLabel [174-181]
+                          Identifier(Account) [174-181]
+            GqlOrderByAndPage [183-212]
+              OrderBy [183-212]
+                OrderingExpression(DESC) [192-212]
+                  PathExpression [192-207]
+                    Identifier(transfer) [192-200]
+                    Identifier(amount) [201-207]
+            GqlReturn [213-284]
+              Select [220-284]
+                SelectList [220-284]
+                  SelectColumn [220-248]
+                    PathExpression [220-234]
+                      Identifier(src_account) [220-231]
+                      Identifier(id) [232-234]
+                    Alias [235-248]
+                      Identifier(account_id) [238-248]
+                  SelectColumn [250-284]
+                    PathExpression [250-265]
+                      Identifier(transfer) [250-258]
+                      Identifier(amount) [259-265]
+                    Alias [266-284]
+                      Identifier(transfer_amount) [269-284]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_10
+-- Warning: Using the LIMIT clause in the RETURN statement, but not immediately
+-- after the ORDER BY statement, also returns the unordered transfer.amount
+-- values.
+GRAPH FinGraph
+MATCH (src_account:Account)-[transfer:Transfers]->(dst_account:Account)
+ORDER BY transfer.amount DESC
+RETURN src_account.id AS account_id, transfer.amount AS transfer_amount
+LIMIT 10
+--
+QueryStatement [167-364]
+  Query [167-364]
+    GqlQuery [167-364]
+      GraphTableQuery [167-364]
+        PathExpression [173-181]
+          Identifier(FinGraph) [173-181]
+        GqlOperatorList [182-364]
+          GqlOperatorList [182-364]
+            GqlMatch [182-253]
+              GraphPattern [188-253]
+                GraphPathPattern [188-253]
+                  GraphNodePattern [188-209]
+                    GraphElementPatternFiller [189-208]
+                      Identifier(src_account) [189-200]
+                      GraphLabelFilter [200-208]
+                        GraphElementLabel [201-208]
+                          Identifier(Account) [201-208]
+                  GraphEdgePattern [209-232]
+                    GraphElementPatternFiller [211-229]
+                      Identifier(transfer) [211-219]
+                      GraphLabelFilter [219-229]
+                        GraphElementLabel [220-229]
+                          Identifier(Transfers) [220-229]
+                  GraphNodePattern [232-253]
+                    GraphElementPatternFiller [233-252]
+                      Identifier(dst_account) [233-244]
+                      GraphLabelFilter [244-252]
+                        GraphElementLabel [245-252]
+                          Identifier(Account) [245-252]
+            GqlOrderByAndPage [254-283]
+              OrderBy [254-283]
+                OrderingExpression(DESC) [263-283]
+                  PathExpression [263-278]
+                    Identifier(transfer) [263-271]
+                    Identifier(amount) [272-278]
+            GqlReturn [284-364]
+              Select [291-355]
+                SelectList [291-355]
+                  SelectColumn [291-319]
+                    PathExpression [291-305]
+                      Identifier(src_account) [291-302]
+                      Identifier(id) [303-305]
+                    Alias [306-319]
+                      Identifier(account_id) [309-319]
+                  SelectColumn [321-355]
+                    PathExpression [321-336]
+                      Identifier(transfer) [321-329]
+                      Identifier(amount) [330-336]
+                    Alias [337-355]
+                      Identifier(transfer_amount) [340-355]
+              GqlOrderByAndPage [355-364]
+                GqlPage [356-364]
+                  GqlPageLimit [356-364]
+                    IntLiteral(10) [362-364]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_11
+-- Error: You can't return a graph element
+GRAPH FinGraph
+MATCH (p:Person)
+RETURN p
+--
+QueryStatement [43-83]
+  Query [43-83]
+    GqlQuery [43-83]
+      GraphTableQuery [43-83]
+        PathExpression [49-57]
+          Identifier(FinGraph) [49-57]
+        GqlOperatorList [58-83]
+          GqlOperatorList [58-83]
+            GqlMatch [58-74]
+              GraphPattern [64-74]
+                GraphPathPattern [64-74]
+                  GraphNodePattern [64-74]
+                    GraphElementPatternFiller [65-73]
+                      Identifier(p) [65-66]
+                      GraphLabelFilter [66-73]
+                        GraphElementLabel [67-73]
+                          Identifier(Person) [67-73]
+            GqlReturn [75-83]
+              Select [82-83]
+                SelectList [82-83]
+                  SelectColumn [82-83]
+                    PathExpression [82-83]
+                      Identifier(p) [82-83]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_11
+GRAPH FinGraph
+MATCH (p:Person)
+LET a = 1, b = 2, c = 3
+RETURN *
+
+NEXT
+
+RETURN p.name, (a + b + c) AS d
+--
+QueryStatement [0-103]
+  Query [0-103]
+    GqlQuery [0-103]
+      GraphTableQuery [0-103]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-103]
+          GqlOperatorList [15-64]
+            GqlMatch [15-31]
+              GraphPattern [21-31]
+                GraphPathPattern [21-31]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+            GqlLet [32-55]
+              GqlLetVariableDefinitionList [36-55]
+                GqlLetVariableDefinition [36-41]
+                  Identifier(a) [36-37]
+                  IntLiteral(1) [40-41]
+                GqlLetVariableDefinition [43-48]
+                  Identifier(b) [43-44]
+                  IntLiteral(2) [47-48]
+                GqlLetVariableDefinition [50-55]
+                  Identifier(c) [50-51]
+                  IntLiteral(3) [54-55]
+            GqlReturn [56-64]
+              Select [63-64]
+                SelectList [63-64]
+                  SelectColumn [63-64]
+                    Star(*) [63-64]
+          GqlOperatorList [72-103]
+            GqlReturn [72-103]
+              Select [79-103]
+                SelectList [79-103]
+                  SelectColumn [79-85]
+                    PathExpression [79-85]
+                      Identifier(p) [79-80]
+                      Identifier(name) [81-85]
+                  SelectColumn [87-103]
+                    BinaryExpression(+) [88-97]
+                      BinaryExpression(+) [88-93]
+                        PathExpression [88-89]
+                          Identifier(a) [88-89]
+                        PathExpression [92-93]
+                          Identifier(b) [92-93]
+                      PathExpression [96-97]
+                        Identifier(c) [96-97]
+                    Alias [99-103]
+                      Identifier(d) [102-103]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_11
+GRAPH FinGraph
+MATCH (src:Account {id: 7})-[e:Transfers]->{1, 3}(dst:Account)
+RETURN DISTINCT ARRAY_LENGTH(e) AS hops, dst.id AS destination_account_id
+--
+QueryStatement [0-151]
+  Query [0-151]
+    GqlQuery [0-151]
+      GraphTableQuery [0-151]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-151]
+          GqlOperatorList [15-151]
+            GqlMatch [15-77]
+              GraphPattern [21-77]
+                GraphPathPattern [21-77]
+                  GraphNodePattern [21-42]
+                    GraphElementPatternFiller [22-41]
+                      Identifier(src) [22-25]
+                      GraphLabelFilter [25-33]
+                        GraphElementLabel [26-33]
+                          Identifier(Account) [26-33]
+                      GraphPropertySpecification [34-41]
+                        GraphPropertyNameAndValue [35-40]
+                          Identifier(id) [35-37]
+                          IntLiteral(7) [39-40]
+                  ParenthesizedGraphPathPattern [42-64]
+                    BoundedQuantifier [58-64]
+                      QuantifierBound [59-60]
+                        IntLiteral(1) [59-60]
+                      QuantifierBound [62-63]
+                        IntLiteral(3) [62-63]
+                    GraphEdgePattern [42-58]
+                      GraphElementPatternFiller [44-55]
+                        Identifier(e) [44-45]
+                        GraphLabelFilter [45-55]
+                          GraphElementLabel [46-55]
+                            Identifier(Transfers) [46-55]
+                  GraphNodePattern [64-77]
+                    GraphElementPatternFiller [65-76]
+                      Identifier(dst) [65-68]
+                      GraphLabelFilter [68-76]
+                        GraphElementLabel [69-76]
+                          Identifier(Account) [69-76]
+            GqlReturn [78-151]
+              Select(distinct=true) [85-151]
+                SelectList [94-151]
+                  SelectColumn [94-117]
+                    FunctionCall [94-109]
+                      PathExpression [94-106]
+                        Identifier(ARRAY_LENGTH) [94-106]
+                      PathExpression [107-108]
+                        Identifier(e) [107-108]
+                    Alias [110-117]
+                      Identifier(hops) [113-117]
+                  SelectColumn [119-151]
+                    PathExpression [119-125]
+                      Identifier(dst) [119-122]
+                      Identifier(id) [123-125]
+                    Alias [126-151]
+                      Identifier(destination_account_id) [129-151]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_11
+GRAPH FinGraph
+MATCH (:Account)-[:Transfers]->(account:Account)
+RETURN account, COUNT(*) AS num_incoming_transfers
+GROUP BY account
+
+NEXT
+
+MATCH (account:Account)<-[:Owns]-(owner:Person)
+RETURN owner.name AS owner_name, num_incoming_transfers
+ORDER BY num_incoming_transfers DESC
+--
+QueryStatement [0-279]
+  Query [0-279]
+    GqlQuery [0-279]
+      GraphTableQuery [0-279]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-279]
+          GqlOperatorList [15-131]
+            GqlMatch [15-63]
+              GraphPattern [21-63]
+                GraphPathPattern [21-63]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      GraphLabelFilter [22-30]
+                        GraphElementLabel [23-30]
+                          Identifier(Account) [23-30]
+                  GraphEdgePattern [31-46]
+                    GraphElementPatternFiller [33-43]
+                      GraphLabelFilter [33-43]
+                        GraphElementLabel [34-43]
+                          Identifier(Transfers) [34-43]
+                  GraphNodePattern [46-63]
+                    GraphElementPatternFiller [47-62]
+                      Identifier(account) [47-54]
+                      GraphLabelFilter [54-62]
+                        GraphElementLabel [55-62]
+                          Identifier(Account) [55-62]
+            GqlReturn [64-131]
+              Select [71-131]
+                SelectList [71-114]
+                  SelectColumn [71-78]
+                    PathExpression [71-78]
+                      Identifier(account) [71-78]
+                  SelectColumn [80-114]
+                    FunctionCall [80-88]
+                      PathExpression [80-85]
+                        Identifier(COUNT) [80-85]
+                      Star(*) [86-87]
+                    Alias [89-114]
+                      Identifier(num_incoming_transfers) [92-114]
+                GroupBy [115-131]
+                  GroupingItem [124-131]
+                    PathExpression [124-131]
+                      Identifier(account) [124-131]
+          GqlOperatorList [139-279]
+            GqlMatch [139-186]
+              GraphPattern [145-186]
+                GraphPathPattern [145-186]
+                  GraphNodePattern [145-162]
+                    GraphElementPatternFiller [146-161]
+                      Identifier(account) [146-153]
+                      GraphLabelFilter [153-161]
+                        GraphElementLabel [154-161]
+                          Identifier(Account) [154-161]
+                  GraphEdgePattern [162-172]
+                    GraphElementPatternFiller [165-170]
+                      GraphLabelFilter [165-170]
+                        GraphElementLabel [166-170]
+                          Identifier(Owns) [166-170]
+                  GraphNodePattern [172-186]
+                    GraphElementPatternFiller [173-185]
+                      Identifier(owner) [173-178]
+                      GraphLabelFilter [178-185]
+                        GraphElementLabel [179-185]
+                          Identifier(Person) [179-185]
+            GqlReturn [187-279]
+              Select [194-242]
+                SelectList [194-242]
+                  SelectColumn [194-218]
+                    PathExpression [194-204]
+                      Identifier(owner) [194-199]
+                      Identifier(name) [200-204]
+                    Alias [205-218]
+                      Identifier(owner_name) [208-218]
+                  SelectColumn [220-242]
+                    PathExpression [220-242]
+                      Identifier(num_incoming_transfers) [220-242]
+              GqlOrderByAndPage [243-279]
+                OrderBy [243-279]
+                  OrderingExpression(DESC) [252-279]
+                    PathExpression [252-274]
+                      Identifier(num_incoming_transfers) [252-274]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_11
+GRAPH FinGraph
+MATCH (p:Person)
+RETURN p.name, p.id
+LIMIT 1
+--
+QueryStatement [0-59]
+  Query [0-59]
+    GqlQuery [0-59]
+      GraphTableQuery [0-59]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-59]
+          GqlOperatorList [15-59]
+            GqlMatch [15-31]
+              GraphPattern [21-31]
+                GraphPathPattern [21-31]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+            GqlReturn [32-59]
+              Select [39-51]
+                SelectList [39-51]
+                  SelectColumn [39-45]
+                    PathExpression [39-45]
+                      Identifier(p) [39-40]
+                      Identifier(name) [41-45]
+                  SelectColumn [47-51]
+                    PathExpression [47-51]
+                      Identifier(p) [47-48]
+                      Identifier(id) [49-51]
+              GqlOrderByAndPage [51-59]
+                GqlPage [52-59]
+                  GqlPageLimit [52-59]
+                    IntLiteral(1) [58-59]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_11
+GRAPH FinGraph
+MATCH (p:Person)
+RETURN p.name, p.id
+OFFSET 1
+--
+QueryStatement [0-60]
+  Query [0-60]
+    GqlQuery [0-60]
+      GraphTableQuery [0-60]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-60]
+          GqlOperatorList [15-60]
+            GqlMatch [15-31]
+              GraphPattern [21-31]
+                GraphPathPattern [21-31]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+            GqlReturn [32-60]
+              Select [39-51]
+                SelectList [39-51]
+                  SelectColumn [39-45]
+                    PathExpression [39-45]
+                      Identifier(p) [39-40]
+                      Identifier(name) [41-45]
+                  SelectColumn [47-51]
+                    PathExpression [47-51]
+                      Identifier(p) [47-48]
+                      Identifier(id) [49-51]
+              GqlOrderByAndPage [51-60]
+                GqlPage [52-60]
+                  GqlPageOffset [52-60]
+                    IntLiteral(1) [59-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_11
+GRAPH FinGraph
+MATCH (p:Person)
+RETURN p.name, p.id
+OFFSET 1
+LIMIT 1
+--
+QueryStatement [0-68]
+  Query [0-68]
+    GqlQuery [0-68]
+      GraphTableQuery [0-68]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-68]
+          GqlOperatorList [15-68]
+            GqlMatch [15-31]
+              GraphPattern [21-31]
+                GraphPathPattern [21-31]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+            GqlReturn [32-68]
+              Select [39-51]
+                SelectList [39-51]
+                  SelectColumn [39-45]
+                    PathExpression [39-45]
+                      Identifier(p) [39-40]
+                      Identifier(name) [41-45]
+                  SelectColumn [47-51]
+                    PathExpression [47-51]
+                      Identifier(p) [47-48]
+                      Identifier(id) [49-51]
+              GqlOrderByAndPage [51-68]
+                GqlPage [52-68]
+                  GqlPageOffset [52-60]
+                    IntLiteral(1) [59-60]
+                  GqlPageLimit [61-68]
+                    IntLiteral(1) [67-68]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_11
+GRAPH FinGraph
+MATCH (src:Account {id: 7})-[e:Transfers]->{1, 3}(dst:Account)
+RETURN DISTINCT ARRAY_LENGTH(e) AS hops, dst.id AS destination_account_id
+ORDER BY hops, destination_account_id
+--
+QueryStatement [0-189]
+  Query [0-189]
+    GqlQuery [0-189]
+      GraphTableQuery [0-189]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-189]
+          GqlOperatorList [15-189]
+            GqlMatch [15-77]
+              GraphPattern [21-77]
+                GraphPathPattern [21-77]
+                  GraphNodePattern [21-42]
+                    GraphElementPatternFiller [22-41]
+                      Identifier(src) [22-25]
+                      GraphLabelFilter [25-33]
+                        GraphElementLabel [26-33]
+                          Identifier(Account) [26-33]
+                      GraphPropertySpecification [34-41]
+                        GraphPropertyNameAndValue [35-40]
+                          Identifier(id) [35-37]
+                          IntLiteral(7) [39-40]
+                  ParenthesizedGraphPathPattern [42-64]
+                    BoundedQuantifier [58-64]
+                      QuantifierBound [59-60]
+                        IntLiteral(1) [59-60]
+                      QuantifierBound [62-63]
+                        IntLiteral(3) [62-63]
+                    GraphEdgePattern [42-58]
+                      GraphElementPatternFiller [44-55]
+                        Identifier(e) [44-45]
+                        GraphLabelFilter [45-55]
+                          GraphElementLabel [46-55]
+                            Identifier(Transfers) [46-55]
+                  GraphNodePattern [64-77]
+                    GraphElementPatternFiller [65-76]
+                      Identifier(dst) [65-68]
+                      GraphLabelFilter [68-76]
+                        GraphElementLabel [69-76]
+                          Identifier(Account) [69-76]
+            GqlReturn [78-189]
+              Select(distinct=true) [85-151]
+                SelectList [94-151]
+                  SelectColumn [94-117]
+                    FunctionCall [94-109]
+                      PathExpression [94-106]
+                        Identifier(ARRAY_LENGTH) [94-106]
+                      PathExpression [107-108]
+                        Identifier(e) [107-108]
+                    Alias [110-117]
+                      Identifier(hops) [113-117]
+                  SelectColumn [119-151]
+                    PathExpression [119-125]
+                      Identifier(dst) [119-122]
+                      Identifier(id) [123-125]
+                    Alias [126-151]
+                      Identifier(destination_account_id) [129-151]
+              GqlOrderByAndPage [152-189]
+                OrderBy [152-189]
+                  OrderingExpression(ASC) [161-165]
+                    PathExpression [161-165]
+                      Identifier(hops) [161-165]
+                  OrderingExpression(ASC) [167-189]
+                    PathExpression [167-189]
+                      Identifier(destination_account_id) [167-189]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_12
+GRAPH FinGraph
+MATCH (p:Person)
+SKIP 2
+RETURN p.name, p.id
+--
+QueryStatement [0-58]
+  Query [0-58]
+    GqlQuery [0-58]
+      GraphTableQuery [0-58]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-58]
+          GqlOperatorList [15-58]
+            GqlMatch [15-31]
+              GraphPattern [21-31]
+                GraphPathPattern [21-31]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+            GqlOrderByAndPage [32-38]
+              GqlPage [32-38]
+                GqlPageOffset [32-38]
+                  IntLiteral(2) [37-38]
+            GqlReturn [39-58]
+              Select [46-58]
+                SelectList [46-58]
+                  SelectColumn [46-52]
+                    PathExpression [46-52]
+                      Identifier(p) [46-47]
+                      Identifier(name) [48-52]
+                  SelectColumn [54-58]
+                    PathExpression [54-58]
+                      Identifier(p) [54-55]
+                      Identifier(id) [56-58]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_13
+GRAPH FinGraph
+MATCH (src:Account)-[transfer:Transfers]->(dst:Account)
+WITH DISTINCT dst
+RETURN dst.id AS destination_id
+--
+QueryStatement [0-120]
+  Query [0-120]
+    GqlQuery [0-120]
+      GraphTableQuery [0-120]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-120]
+          GqlOperatorList [15-120]
+            GqlMatch [15-70]
+              GraphPattern [21-70]
+                GraphPathPattern [21-70]
+                  GraphNodePattern [21-34]
+                    GraphElementPatternFiller [22-33]
+                      Identifier(src) [22-25]
+                      GraphLabelFilter [25-33]
+                        GraphElementLabel [26-33]
+                          Identifier(Account) [26-33]
+                  GraphEdgePattern [34-57]
+                    GraphElementPatternFiller [36-54]
+                      Identifier(transfer) [36-44]
+                      GraphLabelFilter [44-54]
+                        GraphElementLabel [45-54]
+                          Identifier(Transfers) [45-54]
+                  GraphNodePattern [57-70]
+                    GraphElementPatternFiller [58-69]
+                      Identifier(dst) [58-61]
+                      GraphLabelFilter [61-69]
+                        GraphElementLabel [62-69]
+                          Identifier(Account) [62-69]
+            GqlWith [71-88]
+              Select(distinct=true) [76-88]
+                SelectList [85-88]
+                  SelectColumn [85-88]
+                    PathExpression [85-88]
+                      Identifier(dst) [85-88]
+            GqlReturn [89-120]
+              Select [96-120]
+                SelectList [96-120]
+                  SelectColumn [96-120]
+                    PathExpression [96-102]
+                      Identifier(dst) [96-99]
+                      Identifier(id) [100-102]
+                    Alias [103-120]
+                      Identifier(destination_id) [106-120]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_13
+GRAPH FinGraph
+MATCH (src:Account)-[transfer:Transfers]->(dst:Account)
+WITH *, dst.id
+RETURN dst.id AS destination_id
+--
+QueryStatement [0-117]
+  Query [0-117]
+    GqlQuery [0-117]
+      GraphTableQuery [0-117]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-117]
+          GqlOperatorList [15-117]
+            GqlMatch [15-70]
+              GraphPattern [21-70]
+                GraphPathPattern [21-70]
+                  GraphNodePattern [21-34]
+                    GraphElementPatternFiller [22-33]
+                      Identifier(src) [22-25]
+                      GraphLabelFilter [25-33]
+                        GraphElementLabel [26-33]
+                          Identifier(Account) [26-33]
+                  GraphEdgePattern [34-57]
+                    GraphElementPatternFiller [36-54]
+                      Identifier(transfer) [36-44]
+                      GraphLabelFilter [44-54]
+                        GraphElementLabel [45-54]
+                          Identifier(Transfers) [45-54]
+                  GraphNodePattern [57-70]
+                    GraphElementPatternFiller [58-69]
+                      Identifier(dst) [58-61]
+                      GraphLabelFilter [61-69]
+                        GraphElementLabel [62-69]
+                          Identifier(Account) [62-69]
+            GqlWith [71-85]
+              Select [76-85]
+                SelectList [76-85]
+                  SelectColumn [76-77]
+                    Star(*) [76-77]
+                  SelectColumn [79-85]
+                    PathExpression [79-85]
+                      Identifier(dst) [79-82]
+                      Identifier(id) [83-85]
+            GqlReturn [86-117]
+              Select [93-117]
+                SelectList [93-117]
+                  SelectColumn [93-117]
+                    PathExpression [93-99]
+                      Identifier(dst) [93-96]
+                      Identifier(id) [97-99]
+                    Alias [100-117]
+                      Identifier(destination_id) [103-117]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_13
+GRAPH FinGraph
+MATCH (src:Account)-[transfer:Transfers]->(dst:Account)
+WITH COUNT(*) AS transfer_total, src.id AS source_id, dst.id AS destination_id
+RETURN transfer_total, destination_id, source_id
+--
+QueryStatement [0-198]
+  Query [0-198]
+    GqlQuery [0-198]
+      GraphTableQuery [0-198]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-198]
+          GqlOperatorList [15-198]
+            GqlMatch [15-70]
+              GraphPattern [21-70]
+                GraphPathPattern [21-70]
+                  GraphNodePattern [21-34]
+                    GraphElementPatternFiller [22-33]
+                      Identifier(src) [22-25]
+                      GraphLabelFilter [25-33]
+                        GraphElementLabel [26-33]
+                          Identifier(Account) [26-33]
+                  GraphEdgePattern [34-57]
+                    GraphElementPatternFiller [36-54]
+                      Identifier(transfer) [36-44]
+                      GraphLabelFilter [44-54]
+                        GraphElementLabel [45-54]
+                          Identifier(Transfers) [45-54]
+                  GraphNodePattern [57-70]
+                    GraphElementPatternFiller [58-69]
+                      Identifier(dst) [58-61]
+                      GraphLabelFilter [61-69]
+                        GraphElementLabel [62-69]
+                          Identifier(Account) [62-69]
+            GqlWith [71-149]
+              Select [76-149]
+                SelectList [76-149]
+                  SelectColumn [76-102]
+                    FunctionCall [76-84]
+                      PathExpression [76-81]
+                        Identifier(COUNT) [76-81]
+                      Star(*) [82-83]
+                    Alias [85-102]
+                      Identifier(transfer_total) [88-102]
+                  SelectColumn [104-123]
+                    PathExpression [104-110]
+                      Identifier(src) [104-107]
+                      Identifier(id) [108-110]
+                    Alias [111-123]
+                      Identifier(source_id) [114-123]
+                  SelectColumn [125-149]
+                    PathExpression [125-131]
+                      Identifier(dst) [125-128]
+                      Identifier(id) [129-131]
+                    Alias [132-149]
+                      Identifier(destination_id) [135-149]
+            GqlReturn [150-198]
+              Select [157-198]
+                SelectList [157-198]
+                  SelectColumn [157-171]
+                    PathExpression [157-171]
+                      Identifier(transfer_total) [157-171]
+                  SelectColumn [173-187]
+                    PathExpression [173-187]
+                      Identifier(destination_id) [173-187]
+                  SelectColumn [189-198]
+                    PathExpression [189-198]
+                      Identifier(source_id) [189-198]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_13
+-- Error: src doesn't exist
+GRAPH FinGraph
+MATCH (src:Account)-[transfer:Transfers]->(dst:Account)
+WITH dst
+RETURN src.id AS source_id
+--
+QueryStatement [28-134]
+  Query [28-134]
+    GqlQuery [28-134]
+      GraphTableQuery [28-134]
+        PathExpression [34-42]
+          Identifier(FinGraph) [34-42]
+        GqlOperatorList [43-134]
+          GqlOperatorList [43-134]
+            GqlMatch [43-98]
+              GraphPattern [49-98]
+                GraphPathPattern [49-98]
+                  GraphNodePattern [49-62]
+                    GraphElementPatternFiller [50-61]
+                      Identifier(src) [50-53]
+                      GraphLabelFilter [53-61]
+                        GraphElementLabel [54-61]
+                          Identifier(Account) [54-61]
+                  GraphEdgePattern [62-85]
+                    GraphElementPatternFiller [64-82]
+                      Identifier(transfer) [64-72]
+                      GraphLabelFilter [72-82]
+                        GraphElementLabel [73-82]
+                          Identifier(Transfers) [73-82]
+                  GraphNodePattern [85-98]
+                    GraphElementPatternFiller [86-97]
+                      Identifier(dst) [86-89]
+                      GraphLabelFilter [89-97]
+                        GraphElementLabel [90-97]
+                          Identifier(Account) [90-97]
+            GqlWith [99-107]
+              Select [104-107]
+                SelectList [104-107]
+                  SelectColumn [104-107]
+                    PathExpression [104-107]
+                      Identifier(dst) [104-107]
+            GqlReturn [108-134]
+              Select [115-134]
+                SelectList [115-134]
+                  SelectColumn [115-134]
+                    PathExpression [115-121]
+                      Identifier(src) [115-118]
+                      Identifier(id) [119-121]
+                    Alias [122-134]
+                      Identifier(source_id) [125-134]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_14
+GRAPH FinGraph
+MATCH (p:Person)
+RETURN p.name, 1 AS group_id
+UNION ALL
+MATCH (p:Person)
+RETURN 2 AS group_id, p.name
+--
+QueryStatement [0-116]
+  Query [0-116]
+    GqlQuery [0-116]
+      GraphTableQuery [0-116]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-116]
+          GqlSetOperation [15-116]
+            SetOperationMetadataList [61-70]
+              SetOperationMetadata [61-70]
+                SetOperationType [61-66]
+                SetOperationAllOrDistinct [67-70]
+            GqlOperatorList [15-60]
+              GqlMatch [15-31]
+                GraphPattern [21-31]
+                  GraphPathPattern [21-31]
+                    GraphNodePattern [21-31]
+                      GraphElementPatternFiller [22-30]
+                        Identifier(p) [22-23]
+                        GraphLabelFilter [23-30]
+                          GraphElementLabel [24-30]
+                            Identifier(Person) [24-30]
+              GqlReturn [32-60]
+                Select [39-60]
+                  SelectList [39-60]
+                    SelectColumn [39-45]
+                      PathExpression [39-45]
+                        Identifier(p) [39-40]
+                        Identifier(name) [41-45]
+                    SelectColumn [47-60]
+                      IntLiteral(1) [47-48]
+                      Alias [49-60]
+                        Identifier(group_id) [52-60]
+            GqlOperatorList [71-116]
+              GqlMatch [71-87]
+                GraphPattern [77-87]
+                  GraphPathPattern [77-87]
+                    GraphNodePattern [77-87]
+                      GraphElementPatternFiller [78-86]
+                        Identifier(p) [78-79]
+                        GraphLabelFilter [79-86]
+                          GraphElementLabel [80-86]
+                            Identifier(Person) [80-86]
+              GqlReturn [88-116]
+                Select [95-116]
+                  SelectList [95-116]
+                    SelectColumn [95-108]
+                      IntLiteral(2) [95-96]
+                      Alias [97-108]
+                        Identifier(group_id) [100-108]
+                    SelectColumn [110-116]
+                      PathExpression [110-116]
+                        Identifier(p) [110-111]
+                        Identifier(name) [112-116]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_14
+GRAPH FinGraph
+MATCH (p:Person)
+RETURN p.name, 1 AS group_id
+UNION ALL
+MATCH (p:Person)
+RETURN 2 AS group_id, p.name
+UNION ALL
+MATCH (p:Person)
+RETURN 3 AS group_id, p.name
+--
+QueryStatement [0-172]
+  Query [0-172]
+    GqlQuery [0-172]
+      GraphTableQuery [0-172]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-172]
+          GqlSetOperation [15-172]
+            SetOperationMetadataList [61-126]
+              SetOperationMetadata [61-70]
+                SetOperationType [61-66]
+                SetOperationAllOrDistinct [67-70]
+              SetOperationMetadata [117-126]
+                SetOperationType [117-122]
+                SetOperationAllOrDistinct [123-126]
+            GqlOperatorList [15-60]
+              GqlMatch [15-31]
+                GraphPattern [21-31]
+                  GraphPathPattern [21-31]
+                    GraphNodePattern [21-31]
+                      GraphElementPatternFiller [22-30]
+                        Identifier(p) [22-23]
+                        GraphLabelFilter [23-30]
+                          GraphElementLabel [24-30]
+                            Identifier(Person) [24-30]
+              GqlReturn [32-60]
+                Select [39-60]
+                  SelectList [39-60]
+                    SelectColumn [39-45]
+                      PathExpression [39-45]
+                        Identifier(p) [39-40]
+                        Identifier(name) [41-45]
+                    SelectColumn [47-60]
+                      IntLiteral(1) [47-48]
+                      Alias [49-60]
+                        Identifier(group_id) [52-60]
+            GqlOperatorList [71-116]
+              GqlMatch [71-87]
+                GraphPattern [77-87]
+                  GraphPathPattern [77-87]
+                    GraphNodePattern [77-87]
+                      GraphElementPatternFiller [78-86]
+                        Identifier(p) [78-79]
+                        GraphLabelFilter [79-86]
+                          GraphElementLabel [80-86]
+                            Identifier(Person) [80-86]
+              GqlReturn [88-116]
+                Select [95-116]
+                  SelectList [95-116]
+                    SelectColumn [95-108]
+                      IntLiteral(2) [95-96]
+                      Alias [97-108]
+                        Identifier(group_id) [100-108]
+                    SelectColumn [110-116]
+                      PathExpression [110-116]
+                        Identifier(p) [110-111]
+                        Identifier(name) [112-116]
+            GqlOperatorList [127-172]
+              GqlMatch [127-143]
+                GraphPattern [133-143]
+                  GraphPathPattern [133-143]
+                    GraphNodePattern [133-143]
+                      GraphElementPatternFiller [134-142]
+                        Identifier(p) [134-135]
+                        GraphLabelFilter [135-142]
+                          GraphElementLabel [136-142]
+                            Identifier(Person) [136-142]
+              GqlReturn [144-172]
+                Select [151-172]
+                  SelectList [151-172]
+                    SelectColumn [151-164]
+                      IntLiteral(3) [151-152]
+                      Alias [153-164]
+                        Identifier(group_id) [156-164]
+                    SelectColumn [166-172]
+                      PathExpression [166-172]
+                        Identifier(p) [166-167]
+                        Identifier(name) [168-172]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#examples_14
+-- ERROR: GQL doesn't allow chaining EXCEPT DISTINCT with UNION ALL
+GRAPH FinGraph
+MATCH (p:Person)
+RETURN p.name, 1 AS group_id
+UNION ALL
+MATCH (p:Person)
+RETURN 2 AS group_id, p.name
+EXCEPT DISTINCT
+MATCH (p:Person)
+RETURN 3 AS group_id, p.name
+--
+QueryStatement [68-246]
+  Query [68-246]
+    GqlQuery [68-246]
+      GraphTableQuery [68-246]
+        PathExpression [74-82]
+          Identifier(FinGraph) [74-82]
+        GqlOperatorList [83-246]
+          GqlSetOperation [83-246]
+            SetOperationMetadataList [129-200]
+              SetOperationMetadata [129-138]
+                SetOperationType [129-134]
+                SetOperationAllOrDistinct [135-138]
+              SetOperationMetadata [185-200]
+                SetOperationType [185-191]
+                SetOperationAllOrDistinct [192-200]
+            GqlOperatorList [83-128]
+              GqlMatch [83-99]
+                GraphPattern [89-99]
+                  GraphPathPattern [89-99]
+                    GraphNodePattern [89-99]
+                      GraphElementPatternFiller [90-98]
+                        Identifier(p) [90-91]
+                        GraphLabelFilter [91-98]
+                          GraphElementLabel [92-98]
+                            Identifier(Person) [92-98]
+              GqlReturn [100-128]
+                Select [107-128]
+                  SelectList [107-128]
+                    SelectColumn [107-113]
+                      PathExpression [107-113]
+                        Identifier(p) [107-108]
+                        Identifier(name) [109-113]
+                    SelectColumn [115-128]
+                      IntLiteral(1) [115-116]
+                      Alias [117-128]
+                        Identifier(group_id) [120-128]
+            GqlOperatorList [139-184]
+              GqlMatch [139-155]
+                GraphPattern [145-155]
+                  GraphPathPattern [145-155]
+                    GraphNodePattern [145-155]
+                      GraphElementPatternFiller [146-154]
+                        Identifier(p) [146-147]
+                        GraphLabelFilter [147-154]
+                          GraphElementLabel [148-154]
+                            Identifier(Person) [148-154]
+              GqlReturn [156-184]
+                Select [163-184]
+                  SelectList [163-184]
+                    SelectColumn [163-176]
+                      IntLiteral(2) [163-164]
+                      Alias [165-176]
+                        Identifier(group_id) [168-176]
+                    SelectColumn [178-184]
+                      PathExpression [178-184]
+                        Identifier(p) [178-179]
+                        Identifier(name) [180-184]
+            GqlOperatorList [201-246]
+              GqlMatch [201-217]
+                GraphPattern [207-217]
+                  GraphPathPattern [207-217]
+                    GraphNodePattern [207-217]
+                      GraphElementPatternFiller [208-216]
+                        Identifier(p) [208-209]
+                        GraphLabelFilter [209-216]
+                          GraphElementLabel [210-216]
+                            Identifier(Person) [210-216]
+              GqlReturn [218-246]
+                Select [225-246]
+                  SelectList [225-246]
+                    SelectColumn [225-238]
+                      IntLiteral(3) [225-226]
+                      Alias [227-238]
+                        Identifier(group_id) [230-238]
+                    SelectColumn [240-246]
+                      PathExpression [240-246]
+                        Identifier(p) [240-241]
+                        Identifier(name) [242-246]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#graph_hints
+GRAPH FinGraph
+MATCH (p:Person {id: 1})-[:Owns]->(a:Account)
+MATCH @{JOIN_METHOD=APPLY_JOIN}(a:Account)-[e:Transfers]->(oa:Account)
+RETURN oa.id
+--
+QueryStatement [0-144]
+  Query [0-144]
+    GqlQuery [0-144]
+      GraphTableQuery [0-144]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-144]
+          GqlOperatorList [15-144]
+            GqlMatch [15-60]
+              GraphPattern [21-60]
+                GraphPathPattern [21-60]
+                  GraphNodePattern [21-39]
+                    GraphElementPatternFiller [22-38]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                      GraphPropertySpecification [31-38]
+                        GraphPropertyNameAndValue [32-37]
+                          Identifier(id) [32-34]
+                          IntLiteral(1) [36-37]
+                  GraphEdgePattern [39-49]
+                    GraphElementPatternFiller [41-46]
+                      GraphLabelFilter [41-46]
+                        GraphElementLabel [42-46]
+                          Identifier(Owns) [42-46]
+                  GraphNodePattern [49-60]
+                    GraphElementPatternFiller [50-59]
+                      Identifier(a) [50-51]
+                      GraphLabelFilter [51-59]
+                        GraphElementLabel [52-59]
+                          Identifier(Account) [52-59]
+            GqlMatch [61-131]
+              GraphPattern [92-131]
+                GraphPathPattern [92-131]
+                  GraphNodePattern [92-103]
+                    GraphElementPatternFiller [93-102]
+                      Identifier(a) [93-94]
+                      GraphLabelFilter [94-102]
+                        GraphElementLabel [95-102]
+                          Identifier(Account) [95-102]
+                  GraphEdgePattern [103-119]
+                    GraphElementPatternFiller [105-116]
+                      Identifier(e) [105-106]
+                      GraphLabelFilter [106-116]
+                        GraphElementLabel [107-116]
+                          Identifier(Transfers) [107-116]
+                  GraphNodePattern [119-131]
+                    GraphElementPatternFiller [120-130]
+                      Identifier(oa) [120-122]
+                      GraphLabelFilter [122-130]
+                        GraphElementLabel [123-130]
+                          Identifier(Account) [123-130]
+              Hint [67-92]
+                HintEntry [69-91]
+                  Identifier(JOIN_METHOD) [69-80]
+                  PathExpression [81-91]
+                    Identifier(APPLY_JOIN) [81-91]
+            GqlReturn [132-144]
+              Select [139-144]
+                SelectList [139-144]
+                  SelectColumn [139-144]
+                    PathExpression [139-144]
+                      Identifier(oa) [139-141]
+                      Identifier(id) [142-144]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#graph_hints
+GRAPH FinGraph
+MATCH
+  (p:Person {id: 1})-[:Owns]->(a:Account),                   -- path pattern 1
+  @{JOIN_METHOD=HASH_JOIN, HASH_JOIN_BUILD_SIDE=BUILD_RIGHT} -- traversal hint
+  (a:Account)-[e:Transfers]->(c:Account)                     -- path pattern 2
+RETURN c.id
+--
+QueryStatement [0-269]
+  Query [0-269]
+    GqlQuery [0-269]
+      GraphTableQuery [0-269]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-269]
+          GqlOperatorList [15-269]
+            GqlMatch [15-219]
+              GraphPattern [23-219]
+                GraphPathPattern [23-62]
+                  GraphNodePattern [23-41]
+                    GraphElementPatternFiller [24-40]
+                      Identifier(p) [24-25]
+                      GraphLabelFilter [25-32]
+                        GraphElementLabel [26-32]
+                          Identifier(Person) [26-32]
+                      GraphPropertySpecification [33-40]
+                        GraphPropertyNameAndValue [34-39]
+                          Identifier(id) [34-36]
+                          IntLiteral(1) [38-39]
+                  GraphEdgePattern [41-51]
+                    GraphElementPatternFiller [43-48]
+                      GraphLabelFilter [43-48]
+                        GraphElementLabel [44-48]
+                          Identifier(Owns) [44-48]
+                  GraphNodePattern [51-62]
+                    GraphElementPatternFiller [52-61]
+                      Identifier(a) [52-53]
+                      GraphLabelFilter [53-61]
+                        GraphElementLabel [54-61]
+                          Identifier(Account) [54-61]
+                GraphPathPattern [102-219]
+                  Hint [102-160]
+                    HintEntry [104-125]
+                      Identifier(JOIN_METHOD) [104-115]
+                      PathExpression [116-125]
+                        Identifier(HASH_JOIN) [116-125]
+                    HintEntry [127-159]
+                      Identifier(HASH_JOIN_BUILD_SIDE) [127-147]
+                      PathExpression [148-159]
+                        Identifier(BUILD_RIGHT) [148-159]
+                  GraphNodePattern [181-192]
+                    GraphElementPatternFiller [182-191]
+                      Identifier(a) [182-183]
+                      GraphLabelFilter [183-191]
+                        GraphElementLabel [184-191]
+                          Identifier(Account) [184-191]
+                  GraphEdgePattern [192-208]
+                    GraphElementPatternFiller [194-205]
+                      Identifier(e) [194-195]
+                      GraphLabelFilter [195-205]
+                        GraphElementLabel [196-205]
+                          Identifier(Transfers) [196-205]
+                  GraphNodePattern [208-219]
+                    GraphElementPatternFiller [209-218]
+                      Identifier(c) [209-210]
+                      GraphLabelFilter [210-218]
+                        GraphElementLabel [211-218]
+                          Identifier(Account) [211-218]
+            GqlReturn [258-269]
+              Select [265-269]
+                SelectList [265-269]
+                  SelectColumn [265-269]
+                    PathExpression [265-269]
+                      Identifier(c) [265-266]
+                      Identifier(id) [267-269]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#graph_hints
+GRAPH FinGraph
+MATCH (p:Person {id:1})-[e:Owns]->@{JOIN_METHOD=APPLY_JOIN}(a:Account)
+RETURN a.id
+--
+QueryStatement [0-97]
+  Query [0-97]
+    GqlQuery [0-97]
+      GraphTableQuery [0-97]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-97]
+          GqlOperatorList [15-97]
+            GqlMatch [15-85]
+              GraphPattern [21-85]
+                GraphPathPattern [21-85]
+                  GraphNodePattern [21-38]
+                    GraphElementPatternFiller [22-37]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                      GraphPropertySpecification [31-37]
+                        GraphPropertyNameAndValue [32-36]
+                          Identifier(id) [32-34]
+                          IntLiteral(1) [35-36]
+                  GraphEdgePattern [38-74]
+                    GraphRhsHint [49-74]
+                      Hint [49-74]
+                        HintEntry [51-73]
+                          Identifier(JOIN_METHOD) [51-62]
+                          PathExpression [63-73]
+                            Identifier(APPLY_JOIN) [63-73]
+                    GraphElementPatternFiller [40-46]
+                      Identifier(e) [40-41]
+                      GraphLabelFilter [41-46]
+                        GraphElementLabel [42-46]
+                          Identifier(Owns) [42-46]
+                  GraphNodePattern [74-85]
+                    GraphElementPatternFiller [75-84]
+                      Identifier(a) [75-76]
+                      GraphLabelFilter [76-84]
+                        GraphElementLabel [77-84]
+                          Identifier(Account) [77-84]
+            GqlReturn [86-97]
+              Select [93-97]
+                SelectList [93-97]
+                  SelectColumn [93-97]
+                    PathExpression [93-97]
+                      Identifier(a) [93-94]
+                      Identifier(id) [95-97]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#graph_hints
+GRAPH FinGraph
+MATCH (p:Person {id: 1})@{JOIN_METHOD=APPLY_JOIN}-[e:Owns]->(a:Account)
+RETURN a.id
+--
+QueryStatement [0-98]
+  Query [0-98]
+    GqlQuery [0-98]
+      GraphTableQuery [0-98]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-98]
+          GqlOperatorList [15-98]
+            GqlMatch [15-86]
+              GraphPattern [21-86]
+                GraphPathPattern [21-86]
+                  GraphNodePattern [21-39]
+                    GraphElementPatternFiller [22-38]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                      GraphPropertySpecification [31-38]
+                        GraphPropertyNameAndValue [32-37]
+                          Identifier(id) [32-34]
+                          IntLiteral(1) [36-37]
+                  GraphEdgePattern [39-75]
+                    GraphLhsHint [39-64]
+                      Hint [39-64]
+                        HintEntry [41-63]
+                          Identifier(JOIN_METHOD) [41-52]
+                          PathExpression [53-63]
+                            Identifier(APPLY_JOIN) [53-63]
+                    GraphElementPatternFiller [66-72]
+                      Identifier(e) [66-67]
+                      GraphLabelFilter [67-72]
+                        GraphElementLabel [68-72]
+                          Identifier(Owns) [68-72]
+                  GraphNodePattern [75-86]
+                    GraphElementPatternFiller [76-85]
+                      Identifier(a) [76-77]
+                      GraphLabelFilter [77-85]
+                        GraphElementLabel [78-85]
+                          Identifier(Account) [78-85]
+            GqlReturn [87-98]
+              Select [94-98]
+                SelectList [94-98]
+                  SelectColumn [94-98]
+                    PathExpression [94-98]
+                      Identifier(a) [94-95]
+                      Identifier(id) [96-98]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#graph_hints
+GRAPH FinGraph
+MATCH
+  (p:Person {id: 1})-[e:Owns]->
+  @{JOIN_METHOD=APPLY_JOIN}
+  ((a:Account)-[s:Transfers]->(oa:Account))
+RETURN oa.id
+--
+QueryStatement [0-137]
+  Query [0-137]
+    GqlQuery [0-137]
+      GraphTableQuery [0-137]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-137]
+          GqlOperatorList [15-137]
+            GqlMatch [15-124]
+              GraphPattern [23-124]
+                GraphPathPattern [23-124]
+                  GraphNodePattern [23-41]
+                    GraphElementPatternFiller [24-40]
+                      Identifier(p) [24-25]
+                      GraphLabelFilter [25-32]
+                        GraphElementLabel [26-32]
+                          Identifier(Person) [26-32]
+                      GraphPropertySpecification [33-40]
+                        GraphPropertyNameAndValue [34-39]
+                          Identifier(id) [34-36]
+                          IntLiteral(1) [38-39]
+                  GraphEdgePattern [41-80]
+                    GraphRhsHint [55-80]
+                      Hint [55-80]
+                        HintEntry [57-79]
+                          Identifier(JOIN_METHOD) [57-68]
+                          PathExpression [69-79]
+                            Identifier(APPLY_JOIN) [69-79]
+                    GraphElementPatternFiller [43-49]
+                      Identifier(e) [43-44]
+                      GraphLabelFilter [44-49]
+                        GraphElementLabel [45-49]
+                          Identifier(Owns) [45-49]
+                  ParenthesizedGraphPathPattern [83-124]
+                    GraphNodePattern [84-95]
+                      GraphElementPatternFiller [85-94]
+                        Identifier(a) [85-86]
+                        GraphLabelFilter [86-94]
+                          GraphElementLabel [87-94]
+                            Identifier(Account) [87-94]
+                    GraphEdgePattern [95-111]
+                      GraphElementPatternFiller [97-108]
+                        Identifier(s) [97-98]
+                        GraphLabelFilter [98-108]
+                          GraphElementLabel [99-108]
+                            Identifier(Transfers) [99-108]
+                    GraphNodePattern [111-123]
+                      GraphElementPatternFiller [112-122]
+                        Identifier(oa) [112-114]
+                        GraphLabelFilter [114-122]
+                          GraphElementLabel [115-122]
+                            Identifier(Account) [115-122]
+            GqlReturn [125-137]
+              Select [132-137]
+                SelectList [132-137]
+                  SelectColumn [132-137]
+                    PathExpression [132-137]
+                      Identifier(oa) [132-134]
+                      Identifier(id) [135-137]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#graph_hints
+GRAPH FinGraph
+MATCH (a:Account {id:7})-[@{INDEX_STRATEGY=FORCE_INDEX_UNION} :Transfers]-(oa:Account)
+RETURN oa.id
+--
+QueryStatement [0-114]
+  Query [0-114]
+    GqlQuery [0-114]
+      GraphTableQuery [0-114]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-114]
+          GqlOperatorList [15-114]
+            GqlMatch [15-101]
+              GraphPattern [21-101]
+                GraphPathPattern [21-101]
+                  GraphNodePattern [21-39]
+                    GraphElementPatternFiller [22-38]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                      GraphPropertySpecification [32-38]
+                        GraphPropertyNameAndValue [33-37]
+                          Identifier(id) [33-35]
+                          IntLiteral(7) [36-37]
+                  GraphEdgePattern [39-89]
+                    GraphElementPatternFiller [41-87]
+                      GraphLabelFilter [77-87]
+                        GraphElementLabel [78-87]
+                          Identifier(Transfers) [78-87]
+                      Hint [41-76]
+                        HintEntry [43-75]
+                          Identifier(INDEX_STRATEGY) [43-57]
+                          PathExpression [58-75]
+                            Identifier(FORCE_INDEX_UNION) [58-75]
+                  GraphNodePattern [89-101]
+                    GraphElementPatternFiller [90-100]
+                      Identifier(oa) [90-92]
+                      GraphLabelFilter [92-100]
+                        GraphElementLabel [93-100]
+                          Identifier(Account) [93-100]
+            GqlReturn [102-114]
+              Select [109-114]
+                SelectList [109-114]
+                  SelectColumn [109-114]
+                    PathExpression [109-114]
+                      Identifier(oa) [109-111]
+                      Identifier(id) [112-114]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#graph_hints
+GRAPH FinGraph
+MATCH (p:Person)-[:Owns]->(:Account)
+RETURN p.name, COUNT(*) AS num_account
+GROUP @{GROUP_METHOD=HASH_GROUP} BY p.name
+--
+QueryStatement [0-133]
+  Query [0-133]
+    GqlQuery [0-133]
+      GraphTableQuery [0-133]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-133]
+          GqlOperatorList [15-133]
+            GqlMatch [15-51]
+              GraphPattern [21-51]
+                GraphPathPattern [21-51]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                  GraphEdgePattern [31-41]
+                    GraphElementPatternFiller [33-38]
+                      GraphLabelFilter [33-38]
+                        GraphElementLabel [34-38]
+                          Identifier(Owns) [34-38]
+                  GraphNodePattern [41-51]
+                    GraphElementPatternFiller [42-50]
+                      GraphLabelFilter [42-50]
+                        GraphElementLabel [43-50]
+                          Identifier(Account) [43-50]
+            GqlReturn [52-133]
+              Select [59-133]
+                SelectList [59-90]
+                  SelectColumn [59-65]
+                    PathExpression [59-65]
+                      Identifier(p) [59-60]
+                      Identifier(name) [61-65]
+                  SelectColumn [67-90]
+                    FunctionCall [67-75]
+                      PathExpression [67-72]
+                        Identifier(COUNT) [67-72]
+                      Star(*) [73-74]
+                    Alias [76-90]
+                      Identifier(num_account) [79-90]
+                GroupBy [91-133]
+                  Hint [97-123]
+                    HintEntry [99-122]
+                      Identifier(GROUP_METHOD) [99-111]
+                      PathExpression [112-122]
+                        Identifier(HASH_GROUP) [112-122]
+                  GroupingItem [127-133]
+                    PathExpression [127-133]
+                      Identifier(p) [127-128]
+                      Identifier(name) [129-133]
diff --git a/parser/testdata/spanner/graph-schema-statements.metadata.json b/parser/testdata/spanner/graph-schema-statements.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/graph-schema-statements.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/graph-schema-statements.test b/parser/testdata/spanner/graph-schema-statements.test
new file mode 100644
index 0000000..9eb05e0
--- /dev/null
+++ b/parser/testdata/spanner/graph-schema-statements.test
@@ -0,0 +1,224 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-schema-statements
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-schema-statements#element_definition
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+KEY (item1_column, item2_column)
+--
+FunctionCall [0-32]
+  PathExpression [0-3]
+    Identifier(KEY) [0-3]
+  PathExpression [5-17]
+    Identifier(item1_column) [5-17]
+  PathExpression [19-31]
+    Identifier(item2_column) [19-31]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-schema-statements#element_table_property_definition
+[mode=expression]
+PROPERTIES (id, name, salary + bonus AS income)
+--
+FunctionCall [0-47]
+  PathExpression [0-10]
+    Identifier(PROPERTIES) [0-10]
+  PathExpression [12-14]
+    Identifier(id) [12-14]
+  PathExpression [16-20]
+    Identifier(name) [16-20]
+  ExpressionWithAlias [22-46]
+    BinaryExpression(+) [22-36]
+      PathExpression [22-28]
+        Identifier(salary) [22-28]
+      PathExpression [31-36]
+        Identifier(bonus) [31-36]
+    Alias [37-46]
+      Identifier(income) [40-46]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-schema-statements#fin_graph
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Account,
+    Person
+  )
+  EDGE TABLES (
+    PersonOwnAccount
+      SOURCE KEY (id) REFERENCES Person (id)
+      DESTINATION KEY (account_id) REFERENCES Account (id)
+      LABEL Owns,
+    AccountTransferAccount
+      SOURCE KEY (id) REFERENCES Account (id)
+      DESTINATION KEY (to_id) REFERENCES Account (id)
+      LABEL Transfers
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [0-397]
+  PathExpression [33-41]
+    Identifier(FinGraph) [33-41]
+  GraphElementTableList [56-85]
+    GraphElementTable [62-69]
+      PathExpression [62-69]
+        Identifier(Account) [62-69]
+      GraphElementLabelAndPropertiesList [69-69]
+        GraphElementLabelAndProperties [69-69]
+          GraphProperties [69-69]
+    GraphElementTable [75-81]
+      PathExpression [75-81]
+        Identifier(Person) [75-81]
+      GraphElementLabelAndPropertiesList [81-81]
+        GraphElementLabelAndProperties [81-81]
+          GraphProperties [81-81]
+  GraphElementTableList [100-397]
+    GraphElementTable [106-243]
+      PathExpression [106-122]
+        Identifier(PersonOwnAccount) [106-122]
+      GraphNodeTableReference(SOURCE) [129-167]
+        Identifier(Person) [156-162]
+        ColumnList [140-144]
+          Identifier(id) [141-143]
+        ColumnList [163-167]
+          Identifier(id) [164-166]
+      GraphNodeTableReference(DESTINATION) [174-226]
+        Identifier(Account) [214-221]
+        ColumnList [190-202]
+          Identifier(account_id) [191-201]
+        ColumnList [222-226]
+          Identifier(id) [223-225]
+      GraphElementLabelAndPropertiesList [233-243]
+        GraphElementLabelAndProperties [233-243]
+          Identifier(Owns) [239-243]
+          GraphProperties [243-243]
+    GraphElementTable [249-393]
+      PathExpression [249-271]
+        Identifier(AccountTransferAccount) [249-271]
+      GraphNodeTableReference(SOURCE) [278-317]
+        Identifier(Account) [305-312]
+        ColumnList [289-293]
+          Identifier(id) [290-292]
+        ColumnList [313-317]
+          Identifier(id) [314-316]
+      GraphNodeTableReference(DESTINATION) [324-371]
+        Identifier(Account) [359-366]
+        ColumnList [340-347]
+          Identifier(to_id) [341-346]
+        ColumnList [367-371]
+          Identifier(id) [368-370]
+      GraphElementLabelAndPropertiesList [378-393]
+        GraphElementLabelAndProperties [378-393]
+          Identifier(Transfers) [384-393]
+          GraphProperties [393-393]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-schema-statements#fin_graph
+GRAPH FinGraph
+MATCH (p:Person)
+RETURN p.name
+--
+QueryStatement [0-45]
+  Query [0-45]
+    GqlQuery [0-45]
+      GraphTableQuery [0-45]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-45]
+          GqlOperatorList [15-45]
+            GqlMatch [15-31]
+              GraphPattern [21-31]
+                GraphPathPattern [21-31]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+            GqlReturn [32-45]
+              Select [39-45]
+                SelectList [39-45]
+                  SelectColumn [39-45]
+                    PathExpression [39-45]
+                      Identifier(p) [39-40]
+                      Identifier(name) [41-45]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-schema-statements#fingraph_with_dynamic_label_and_dynamic_properties
+CREATE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    GraphNode
+      DYNAMIC LABEL (label)
+      DYNAMIC PROPERTIES (properties)
+)
+  EDGE TABLES (
+    GraphEdge
+      SOURCE KEY (id) REFERENCES GraphNode(id)
+      DESTINATION KEY (dest_id) REFERENCES GraphNode(id)
+      DYNAMIC LABEL (label)
+      DYNAMIC PROPERTIES (properties)
+)
+--
+CreatePropertyGraphStatement [0-330]
+  PathExpression [22-30]
+    Identifier(FinGraph) [22-30]
+  GraphElementTableList [45-128]
+    GraphElementTable [51-126]
+      PathExpression [51-60]
+        Identifier(GraphNode) [51-60]
+      GraphElementLabelAndPropertiesList [60-60]
+        GraphElementLabelAndProperties [60-60]
+          GraphProperties [60-60]
+      GraphDynamicLabel [82-87]
+        PathExpression [82-87]
+          Identifier(label) [82-87]
+      GraphDynamicProperties [115-125]
+        PathExpression [115-125]
+          Identifier(properties) [115-125]
+  GraphElementTableList [143-330]
+    GraphElementTable [149-328]
+      PathExpression [149-158]
+        Identifier(GraphEdge) [149-158]
+      GraphNodeTableReference(SOURCE) [165-205]
+        Identifier(GraphNode) [192-201]
+        ColumnList [176-180]
+          Identifier(id) [177-179]
+        ColumnList [201-205]
+          Identifier(id) [202-204]
+      GraphNodeTableReference(DESTINATION) [212-262]
+        Identifier(GraphNode) [249-258]
+        ColumnList [228-237]
+          Identifier(dest_id) [229-236]
+        ColumnList [258-262]
+          Identifier(id) [259-261]
+      GraphElementLabelAndPropertiesList [262-262]
+        GraphElementLabelAndProperties [262-262]
+          GraphProperties [262-262]
+      GraphDynamicLabel [284-289]
+        PathExpression [284-289]
+          Identifier(label) [284-289]
+      GraphDynamicProperties [317-327]
+        PathExpression [317-327]
+          Identifier(properties) [317-327]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-schema-statements#fingraph_with_dynamic_label_and_dynamic_properties
+INSERT INTO GraphNode (id, label, properties)
+VALUES (1, "person", JSON '{"name": "Alex", "age": 33}')
+--
+InsertStatement [0-102]
+  PathExpression [12-21]
+    Identifier(GraphNode) [12-21]
+  ColumnList [22-45]
+    Identifier(id) [23-25]
+    Identifier(label) [27-32]
+    Identifier(properties) [34-44]
+  InsertValuesRowList [46-102]
+    InsertValuesRow [53-102]
+      IntLiteral(1) [54-55]
+      StringLiteral [57-65]
+        StringLiteralComponent("person") [57-65]
+      JSONLiteral [67-101]
+        StringLiteral [72-101]
+          StringLiteralComponent('{"name": "Alex", "age": 33}') [72-101]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-schema-statements#gql_drop_graph
+DROP PROPERTY GRAPH FinGraph
+--
+DropStatement PROPERTY GRAPH [0-28]
+  PathExpression [20-28]
+    Identifier(FinGraph) [20-28]
diff --git a/parser/testdata/spanner/graph-sql-queries.test b/parser/testdata/spanner/graph-sql-queries.test
new file mode 100644
index 0000000..5e05d72
--- /dev/null
+++ b/parser/testdata/spanner/graph-sql-queries.test
@@ -0,0 +1,217 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-sql-queries
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-sql-queries#examples
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT name, id
+FROM GRAPH_TABLE(
+  FinGraph
+  MATCH (n:Person)
+  RETURN n.name AS name, n.id AS id
+)
+--
+QueryStatement [0-101]
+  Query [0-101]
+    Select [0-101]
+      SelectList [7-15]
+        SelectColumn [7-11]
+          PathExpression [7-11]
+            Identifier(name) [7-11]
+        SelectColumn [13-15]
+          PathExpression [13-15]
+            Identifier(id) [13-15]
+      FromClause [16-101]
+        GraphTableQuery [21-101]
+          PathExpression [36-44]
+            Identifier(FinGraph) [36-44]
+          GqlOperatorList [47-99]
+            GqlOperatorList [47-99]
+              GqlMatch [47-63]
+                GraphPattern [53-63]
+                  GraphPathPattern [53-63]
+                    GraphNodePattern [53-63]
+                      GraphElementPatternFiller [54-62]
+                        Identifier(n) [54-55]
+                        GraphLabelFilter [55-62]
+                          GraphElementLabel [56-62]
+                            Identifier(Person) [56-62]
+              GqlReturn [66-99]
+                Select [73-99]
+                  SelectList [73-99]
+                    SelectColumn [73-87]
+                      PathExpression [73-79]
+                        Identifier(n) [73-74]
+                        Identifier(name) [75-79]
+                      Alias [80-87]
+                        Identifier(name) [83-87]
+                    SelectColumn [89-99]
+                      PathExpression [89-93]
+                        Identifier(n) [89-90]
+                        Identifier(id) [91-93]
+                      Alias [94-99]
+                        Identifier(id) [97-99]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-sql-queries#examples
+SELECT n.name, n.id
+FROM GRAPH_TABLE(
+  FinGraph
+  MATCH (n:Person)
+  RETURN n
+)
+--
+QueryStatement [0-80]
+  Query [0-80]
+    Select [0-80]
+      SelectList [7-19]
+        SelectColumn [7-13]
+          PathExpression [7-13]
+            Identifier(n) [7-8]
+            Identifier(name) [9-13]
+        SelectColumn [15-19]
+          PathExpression [15-19]
+            Identifier(n) [15-16]
+            Identifier(id) [17-19]
+      FromClause [20-80]
+        GraphTableQuery [25-80]
+          PathExpression [40-48]
+            Identifier(FinGraph) [40-48]
+          GqlOperatorList [51-78]
+            GqlOperatorList [51-78]
+              GqlMatch [51-67]
+                GraphPattern [57-67]
+                  GraphPathPattern [57-67]
+                    GraphNodePattern [57-67]
+                      GraphElementPatternFiller [58-66]
+                        Identifier(n) [58-59]
+                        GraphLabelFilter [59-66]
+                          GraphElementLabel [60-66]
+                            Identifier(Person) [60-66]
+              GqlReturn [70-78]
+                Select [77-78]
+                  SelectList [77-78]
+                    SelectColumn [77-78]
+                      PathExpression [77-78]
+                        Identifier(n) [77-78]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-sql-queries#examples
+SELECT name, id
+FROM GRAPH_TABLE(
+  FinGraph
+  MATCH (n:Person)
+  RETURN n.name
+)
+--
+QueryStatement [0-81]
+  Query [0-81]
+    Select [0-81]
+      SelectList [7-15]
+        SelectColumn [7-11]
+          PathExpression [7-11]
+            Identifier(name) [7-11]
+        SelectColumn [13-15]
+          PathExpression [13-15]
+            Identifier(id) [13-15]
+      FromClause [16-81]
+        GraphTableQuery [21-81]
+          PathExpression [36-44]
+            Identifier(FinGraph) [36-44]
+          GqlOperatorList [47-79]
+            GqlOperatorList [47-79]
+              GqlMatch [47-63]
+                GraphPattern [53-63]
+                  GraphPathPattern [53-63]
+                    GraphNodePattern [53-63]
+                      GraphElementPatternFiller [54-62]
+                        Identifier(n) [54-55]
+                        GraphLabelFilter [55-62]
+                          GraphElementLabel [56-62]
+                            Identifier(Person) [56-62]
+              GqlReturn [66-79]
+                Select [73-79]
+                  SelectList [73-79]
+                    SelectColumn [73-79]
+                      PathExpression [73-79]
+                        Identifier(n) [73-74]
+                        Identifier(name) [75-79]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-sql-queries#examples
+-- Error
+SELECT n
+FROM GRAPH_TABLE(
+  FinGraph
+  MATCH (n:Person)
+  RETURN n
+)
+--
+QueryStatement [9-78]
+  Query [9-78]
+    Select [9-78]
+      SelectList [16-17]
+        SelectColumn [16-17]
+          PathExpression [16-17]
+            Identifier(n) [16-17]
+      FromClause [18-78]
+        GraphTableQuery [23-78]
+          PathExpression [38-46]
+            Identifier(FinGraph) [38-46]
+          GqlOperatorList [49-76]
+            GqlOperatorList [49-76]
+              GqlMatch [49-65]
+                GraphPattern [55-65]
+                  GraphPathPattern [55-65]
+                    GraphNodePattern [55-65]
+                      GraphElementPatternFiller [56-64]
+                        Identifier(n) [56-57]
+                        GraphLabelFilter [57-64]
+                          GraphElementLabel [58-64]
+                            Identifier(Person) [58-64]
+              GqlReturn [68-76]
+                Select [75-76]
+                  SelectList [75-76]
+                    SelectColumn [75-76]
+                      PathExpression [75-76]
+                        Identifier(n) [75-76]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-sql-queries#examples
+SELECT SAFE_TO_JSON(n) as json_node
+FROM GRAPH_TABLE(
+  FinGraph
+  MATCH (n:Person)
+  RETURN n
+)
+--
+QueryStatement [0-96]
+  Query [0-96]
+    Select [0-96]
+      SelectList [7-35]
+        SelectColumn [7-35]
+          FunctionCall [7-22]
+            PathExpression [7-19]
+              Identifier(SAFE_TO_JSON) [7-19]
+            PathExpression [20-21]
+              Identifier(n) [20-21]
+          Alias [23-35]
+            Identifier(json_node) [26-35]
+      FromClause [36-96]
+        GraphTableQuery [41-96]
+          PathExpression [56-64]
+            Identifier(FinGraph) [56-64]
+          GqlOperatorList [67-94]
+            GqlOperatorList [67-94]
+              GqlMatch [67-83]
+                GraphPattern [73-83]
+                  GraphPathPattern [73-83]
+                    GraphNodePattern [73-83]
+                      GraphElementPatternFiller [74-82]
+                        Identifier(n) [74-75]
+                        GraphLabelFilter [75-82]
+                          GraphElementLabel [76-82]
+                            Identifier(Person) [76-82]
+              GqlReturn [86-94]
+                Select [93-94]
+                  SelectList [93-94]
+                    SelectColumn [93-94]
+                      PathExpression [93-94]
+                        Identifier(n) [93-94]
diff --git a/parser/testdata/spanner/graph-subqueries.test b/parser/testdata/spanner/graph-subqueries.test
new file mode 100644
index 0000000..a1400d1
--- /dev/null
+++ b/parser/testdata/spanner/graph-subqueries.test
@@ -0,0 +1,381 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-subqueries
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-subqueries#examples
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+GRAPH FinGraph
+MATCH (p:Person)-[:Owns]->(account:Account)
+RETURN
+ p.name, account.id AS account_id,
+ ARRAY {
+   MATCH (a:Account)-[transfer:Transfers]->(:Account)
+   WHERE a = account
+   RETURN transfer.amount AS transfers
+ } AS transfers
+--
+QueryStatement [0-239]
+  Query [0-239]
+    GqlQuery [0-239]
+      GraphTableQuery [0-239]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-239]
+          GqlOperatorList [15-239]
+            GqlMatch [15-58]
+              GraphPattern [21-58]
+                GraphPathPattern [21-58]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                  GraphEdgePattern [31-41]
+                    GraphElementPatternFiller [33-38]
+                      GraphLabelFilter [33-38]
+                        GraphElementLabel [34-38]
+                          Identifier(Owns) [34-38]
+                  GraphNodePattern [41-58]
+                    GraphElementPatternFiller [42-57]
+                      Identifier(account) [42-49]
+                      GraphLabelFilter [49-57]
+                        GraphElementLabel [50-57]
+                          Identifier(Account) [50-57]
+            GqlReturn [59-239]
+              Select [67-239]
+                SelectList [67-239]
+                  SelectColumn [67-73]
+                    PathExpression [67-73]
+                      Identifier(p) [67-68]
+                      Identifier(name) [69-73]
+                  SelectColumn [75-99]
+                    PathExpression [75-85]
+                      Identifier(account) [75-82]
+                      Identifier(id) [83-85]
+                    Alias [86-99]
+                      Identifier(account_id) [89-99]
+                  SelectColumn [102-239]
+                    ExpressionSubquery(modifier=ARRAY) [102-226]
+                      Query [108-226]
+                        GqlQuery [108-226]
+                          GraphTableQuery [108-226]
+                            GqlOperatorList [113-223]
+                              GqlOperatorList [113-223]
+                                GqlMatch [113-184]
+                                  GraphPattern [119-184]
+                                    GraphPathPattern [119-163]
+                                      GraphNodePattern [119-130]
+                                        GraphElementPatternFiller [120-129]
+                                          Identifier(a) [120-121]
+                                          GraphLabelFilter [121-129]
+                                            GraphElementLabel [122-129]
+                                              Identifier(Account) [122-129]
+                                      GraphEdgePattern [130-153]
+                                        GraphElementPatternFiller [132-150]
+                                          Identifier(transfer) [132-140]
+                                          GraphLabelFilter [140-150]
+                                            GraphElementLabel [141-150]
+                                              Identifier(Transfers) [141-150]
+                                      GraphNodePattern [153-163]
+                                        GraphElementPatternFiller [154-162]
+                                          GraphLabelFilter [154-162]
+                                            GraphElementLabel [155-162]
+                                              Identifier(Account) [155-162]
+                                    WhereClause [167-184]
+                                      BinaryExpression(=) [173-184]
+                                        PathExpression [173-174]
+                                          Identifier(a) [173-174]
+                                        PathExpression [177-184]
+                                          Identifier(account) [177-184]
+                                GqlReturn [188-223]
+                                  Select [195-223]
+                                    SelectList [195-223]
+                                      SelectColumn [195-223]
+                                        PathExpression [195-210]
+                                          Identifier(transfer) [195-203]
+                                          Identifier(amount) [204-210]
+                                        Alias [211-223]
+                                          Identifier(transfers) [214-223]
+                    Alias [227-239]
+                      Identifier(transfers) [230-239]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-subqueries#examples_2
+GRAPH FinGraph
+RETURN EXISTS {
+  MATCH (p:Person {Name: "Lee"})-[o:Owns]->(a:Account)
+  RETURN p.Name
+  LIMIT 1
+} AS results
+--
+QueryStatement [0-124]
+  Query [0-124]
+    GqlQuery [0-124]
+      GraphTableQuery [0-124]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-124]
+          GqlOperatorList [15-124]
+            GqlReturn [15-124]
+              Select [22-124]
+                SelectList [22-124]
+                  SelectColumn [22-124]
+                    ExpressionSubquery(modifier=EXISTS) [22-113]
+                      Query [29-113]
+                        GqlQuery [29-113]
+                          GraphTableQuery [29-113]
+                            GqlOperatorList [33-111]
+                              GqlOperatorList [33-111]
+                                GqlMatch [33-85]
+                                  GraphPattern [39-85]
+                                    GraphPathPattern [39-85]
+                                      GraphNodePattern [39-63]
+                                        GraphElementPatternFiller [40-62]
+                                          Identifier(p) [40-41]
+                                          GraphLabelFilter [41-48]
+                                            GraphElementLabel [42-48]
+                                              Identifier(Person) [42-48]
+                                          GraphPropertySpecification [49-62]
+                                            GraphPropertyNameAndValue [50-61]
+                                              Identifier(Name) [50-54]
+                                              StringLiteral [56-61]
+                                                StringLiteralComponent("Lee") [56-61]
+                                      GraphEdgePattern [63-74]
+                                        GraphElementPatternFiller [65-71]
+                                          Identifier(o) [65-66]
+                                          GraphLabelFilter [66-71]
+                                            GraphElementLabel [67-71]
+                                              Identifier(Owns) [67-71]
+                                      GraphNodePattern [74-85]
+                                        GraphElementPatternFiller [75-84]
+                                          Identifier(a) [75-76]
+                                          GraphLabelFilter [76-84]
+                                            GraphElementLabel [77-84]
+                                              Identifier(Account) [77-84]
+                                GqlReturn [88-111]
+                                  Select [95-101]
+                                    SelectList [95-101]
+                                      SelectColumn [95-101]
+                                        PathExpression [95-101]
+                                          Identifier(p) [95-96]
+                                          Identifier(Name) [97-101]
+                                  GqlOrderByAndPage [101-111]
+                                    GqlPage [104-111]
+                                      GqlPageLimit [104-111]
+                                        IntLiteral(1) [110-111]
+                    Alias [114-124]
+                      Identifier(results) [117-124]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-subqueries#examples_2
+GRAPH FinGraph
+RETURN EXISTS {
+  MATCH (p:Person {Name: "Lee"})-[o:Owns]->(a:Account)
+} AS results
+--
+QueryStatement [0-98]
+  Query [0-98]
+    GqlQuery [0-98]
+      GraphTableQuery [0-98]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-98]
+          GqlOperatorList [15-98]
+            GqlReturn [15-98]
+              Select [22-98]
+                SelectList [22-98]
+                  SelectColumn [22-98]
+                    ExpressionSubquery(modifier=EXISTS) [22-87]
+                      Query [22-87]
+                        GqlLinearOpsQuery [22-87]
+                          GqlOperatorList [33-85]
+                            GqlMatch [33-85]
+                              GraphPattern [39-85]
+                                GraphPathPattern [39-85]
+                                  GraphNodePattern [39-63]
+                                    GraphElementPatternFiller [40-62]
+                                      Identifier(p) [40-41]
+                                      GraphLabelFilter [41-48]
+                                        GraphElementLabel [42-48]
+                                          Identifier(Person) [42-48]
+                                      GraphPropertySpecification [49-62]
+                                        GraphPropertyNameAndValue [50-61]
+                                          Identifier(Name) [50-54]
+                                          StringLiteral [56-61]
+                                            StringLiteralComponent("Lee") [56-61]
+                                  GraphEdgePattern [63-74]
+                                    GraphElementPatternFiller [65-71]
+                                      Identifier(o) [65-66]
+                                      GraphLabelFilter [66-71]
+                                        GraphElementLabel [67-71]
+                                          Identifier(Owns) [67-71]
+                                  GraphNodePattern [74-85]
+                                    GraphElementPatternFiller [75-84]
+                                      Identifier(a) [75-76]
+                                      GraphLabelFilter [76-84]
+                                        GraphElementLabel [77-84]
+                                          Identifier(Account) [77-84]
+                    Alias [88-98]
+                      Identifier(results) [91-98]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-subqueries#examples_2
+GRAPH FinGraph
+RETURN EXISTS {
+  (p:Person {Name: "Lee"})-[o:Owns]->(a:Account)
+} AS results
+--
+QueryStatement [0-92]
+  Query [0-92]
+    GqlQuery [0-92]
+      GraphTableQuery [0-92]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-92]
+          GqlOperatorList [15-92]
+            GqlReturn [15-92]
+              Select [22-92]
+                SelectList [22-92]
+                  SelectColumn [22-92]
+                    ExpressionSubquery(modifier=EXISTS) [22-81]
+                      Query [22-81]
+                        GqlGraphPatternQuery [22-81]
+                          GraphPattern [33-79]
+                            GraphPathPattern [33-79]
+                              GraphNodePattern [33-57]
+                                GraphElementPatternFiller [34-56]
+                                  Identifier(p) [34-35]
+                                  GraphLabelFilter [35-42]
+                                    GraphElementLabel [36-42]
+                                      Identifier(Person) [36-42]
+                                  GraphPropertySpecification [43-56]
+                                    GraphPropertyNameAndValue [44-55]
+                                      Identifier(Name) [44-48]
+                                      StringLiteral [50-55]
+                                        StringLiteralComponent("Lee") [50-55]
+                              GraphEdgePattern [57-68]
+                                GraphElementPatternFiller [59-65]
+                                  Identifier(o) [59-60]
+                                  GraphLabelFilter [60-65]
+                                    GraphElementLabel [61-65]
+                                      Identifier(Owns) [61-65]
+                              GraphNodePattern [68-79]
+                                GraphElementPatternFiller [69-78]
+                                  Identifier(a) [69-70]
+                                  GraphLabelFilter [70-78]
+                                    GraphElementLabel [71-78]
+                                      Identifier(Account) [71-78]
+                    Alias [82-92]
+                      Identifier(results) [85-92]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-subqueries#examples_3
+GRAPH FinGraph
+RETURN 'Dana' IN {
+  MATCH (p:Person)-[o:Owns]->(a:Account)
+  RETURN p.name
+} AS results
+--
+QueryStatement [0-103]
+  Query [0-103]
+    GqlQuery [0-103]
+      GraphTableQuery [0-103]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-103]
+          GqlOperatorList [15-103]
+            GqlReturn [15-103]
+              Select [22-103]
+                SelectList [22-103]
+                  SelectColumn [22-103]
+                    InExpression(IN) [22-92]
+                      StringLiteral [22-28]
+                        StringLiteralComponent('Dana') [22-28]
+                      Location [29-31]
+                      Query [32-92]
+                        GqlQuery [32-92]
+                          GraphTableQuery [32-92]
+                            GqlOperatorList [36-90]
+                              GqlOperatorList [36-90]
+                                GqlMatch [36-74]
+                                  GraphPattern [42-74]
+                                    GraphPathPattern [42-74]
+                                      GraphNodePattern [42-52]
+                                        GraphElementPatternFiller [43-51]
+                                          Identifier(p) [43-44]
+                                          GraphLabelFilter [44-51]
+                                            GraphElementLabel [45-51]
+                                              Identifier(Person) [45-51]
+                                      GraphEdgePattern [52-63]
+                                        GraphElementPatternFiller [54-60]
+                                          Identifier(o) [54-55]
+                                          GraphLabelFilter [55-60]
+                                            GraphElementLabel [56-60]
+                                              Identifier(Owns) [56-60]
+                                      GraphNodePattern [63-74]
+                                        GraphElementPatternFiller [64-73]
+                                          Identifier(a) [64-65]
+                                          GraphLabelFilter [65-73]
+                                            GraphElementLabel [66-73]
+                                              Identifier(Account) [66-73]
+                                GqlReturn [77-90]
+                                  Select [84-90]
+                                    SelectList [84-90]
+                                      SelectColumn [84-90]
+                                        PathExpression [84-90]
+                                          Identifier(p) [84-85]
+                                          Identifier(name) [86-90]
+                    Alias [93-103]
+                      Identifier(results) [96-103]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/graph-subqueries#examples_4
+GRAPH FinGraph
+RETURN VALUE {
+  MATCH (p:Person {country: "Australia"})
+  RETURN p.name
+  LIMIT 1
+} AS results
+--
+QueryStatement [0-110]
+  Query [0-110]
+    GqlQuery [0-110]
+      GraphTableQuery [0-110]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-110]
+          GqlOperatorList [15-110]
+            GqlReturn [15-110]
+              Select [22-110]
+                SelectList [22-110]
+                  SelectColumn [22-110]
+                    ExpressionSubquery(modifier=VALUE) [22-99]
+                      Query [28-99]
+                        GqlQuery [28-99]
+                          GraphTableQuery [28-99]
+                            GqlOperatorList [32-97]
+                              GqlOperatorList [32-97]
+                                GqlMatch [32-71]
+                                  GraphPattern [38-71]
+                                    GraphPathPattern [38-71]
+                                      GraphNodePattern [38-71]
+                                        GraphElementPatternFiller [39-70]
+                                          Identifier(p) [39-40]
+                                          GraphLabelFilter [40-47]
+                                            GraphElementLabel [41-47]
+                                              Identifier(Person) [41-47]
+                                          GraphPropertySpecification [48-70]
+                                            GraphPropertyNameAndValue [49-69]
+                                              Identifier(country) [49-56]
+                                              StringLiteral [58-69]
+                                                StringLiteralComponent("Australia") [58-69]
+                                GqlReturn [74-97]
+                                  Select [81-87]
+                                    SelectList [81-87]
+                                      SelectColumn [81-87]
+                                        PathExpression [81-87]
+                                          Identifier(p) [81-82]
+                                          Identifier(name) [83-87]
+                                  GqlOrderByAndPage [87-97]
+                                    GqlPage [90-97]
+                                      GqlPageLimit [90-97]
+                                        IntLiteral(1) [96-97]
+                    Alias [100-110]
+                      Identifier(results) [103-110]
diff --git a/parser/testdata/spanner/guides/backfill-embeddings.test b/parser/testdata/spanner/guides/backfill-embeddings.test
new file mode 100644
index 0000000..a9151cb
--- /dev/null
+++ b/parser/testdata/spanner/guides/backfill-embeddings.test
@@ -0,0 +1,141 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/backfill-embeddings
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/backfill-embeddings#example-use-case
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Products (
+  product_id INT64 NOT NULL,
+  name STRING(MAX),
+  description STRING(MAX)
+) PRIMARY KEY(product_id)
+--
+CreateTableStatement [0-124]
+  PathExpression [13-21]
+    Identifier(Products) [13-21]
+  TableElementList [22-100]
+    ColumnDefinition [26-51]
+      Identifier(product_id) [26-36]
+      SimpleColumnSchema [37-51]
+        PathExpression [37-42]
+          Identifier(INT64) [37-42]
+        ColumnAttributeList [43-51]
+          NotNullColumnAttribute [43-51]
+    ColumnDefinition [55-71]
+      Identifier(name) [55-59]
+      SimpleColumnSchema [60-71]
+        PathExpression [60-66]
+          Identifier(STRING) [60-66]
+        TypeParameterList [66-70]
+          MaxLiteral() [67-70]
+    ColumnDefinition [75-98]
+      Identifier(description) [75-86]
+      SimpleColumnSchema [87-98]
+        PathExpression [87-93]
+          Identifier(STRING) [87-93]
+        TypeParameterList [93-97]
+          MaxLiteral() [94-97]
+  SpannerTableOptions [101-124]
+    PrimaryKey(ENFORCED) [101-124]
+      PrimaryKeyElementList [112-124]
+        PrimaryKeyElement(ASC) [113-123]
+          Identifier(product_id) [113-123]
+==
+# https://docs.cloud.google.com/spanner/docs/backfill-embeddings#update-source-table
+ALTER TABLE Products
+ADD COLUMN desc_embed ARRAY
+--
+AlterTableStatement [0-57]
+  PathExpression [12-20]
+    Identifier(Products) [12-20]
+  AlterActionList [21-57]
+    AddColumnAction [21-57]
+      ColumnDefinition [32-57]
+        Identifier(desc_embed) [32-42]
+        ArrayColumnSchema [43-57]
+          SimpleColumnSchema [49-56]
+            PathExpression [49-56]
+              Identifier(FLOAT32) [49-56]
+==
+# https://docs.cloud.google.com/spanner/docs/backfill-embeddings#update-source-table
+ALTER TABLE Products
+ADD COLUMN desc_embed_model_version INT64
+--
+AlterTableStatement [0-62]
+  PathExpression [12-20]
+    Identifier(Products) [12-20]
+  AlterActionList [21-62]
+    AddColumnAction [21-62]
+      ColumnDefinition [32-62]
+        Identifier(desc_embed_model_version) [32-56]
+        SimpleColumnSchema [57-62]
+          PathExpression [57-62]
+            Identifier(INT64) [57-62]
+==
+# https://docs.cloud.google.com/spanner/docs/backfill-embeddings#backfill
+UPDATE products
+SET
+  products.desc_embed = (
+    SELECT embeddings.values
+    FROM SAFE.ML.PREDICT(
+      MODEL embedding_model,
+      (SELECT products.description AS content)
+    ) @{remote_udf_max_rows_per_rpc=200}
+  ),
+  products.desc_embed_model_version = 3
+WHERE products.desc_embed IS NULL
+--
+UpdateStatement [0-296]
+  PathExpression [7-15]
+    Identifier(products) [7-15]
+  UpdateItemList [22-262]
+    UpdateItem [22-221]
+      UpdateSetValue [22-221]
+        PathExpression [22-41]
+          Identifier(products) [22-30]
+          Identifier(desc_embed) [31-41]
+        ExpressionSubquery [44-221]
+          Query [50-217]
+            Select [50-217]
+              SelectList [57-74]
+                SelectColumn [57-74]
+                  PathExpression [57-74]
+                    Identifier(embeddings) [57-67]
+                    Identifier(values) [68-74]
+              FromClause [79-217]
+                TVF [84-217]
+                  PathExpression [84-99]
+                    Identifier(SAFE) [84-88]
+                    Identifier(ML) [89-91]
+                    Identifier(PREDICT) [92-99]
+                  TVFArgument [107-128]
+                    ModelClause [107-128]
+                      PathExpression [113-128]
+                        Identifier(embedding_model) [113-128]
+                  TVFArgument [136-176]
+                    ExpressionSubquery [136-176]
+                      Query [137-175]
+                        Select [137-175]
+                          SelectList [144-175]
+                            SelectColumn [144-175]
+                              PathExpression [144-164]
+                                Identifier(products) [144-152]
+                                Identifier(description) [153-164]
+                              Alias [165-175]
+                                Identifier(content) [168-175]
+                  Hint [183-217]
+                    HintEntry [185-216]
+                      Identifier(remote_udf_max_rows_per_rpc) [185-212]
+                      IntLiteral(200) [213-216]
+    UpdateItem [225-262]
+      UpdateSetValue [225-262]
+        PathExpression [225-258]
+          Identifier(products) [225-233]
+          Identifier(desc_embed_model_version) [234-258]
+        IntLiteral(3) [261-262]
+  BinaryExpression(IS) [269-296]
+    PathExpression [269-288]
+      Identifier(products) [269-277]
+      Identifier(desc_embed) [278-288]
+    NullLiteral(NULL) [292-296]
diff --git a/parser/testdata/spanner/guides/best-practices-gaming-database.test b/parser/testdata/spanner/guides/best-practices-gaming-database.test
new file mode 100644
index 0000000..ad3b848
--- /dev/null
+++ b/parser/testdata/spanner/guides/best-practices-gaming-database.test
@@ -0,0 +1,247 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/best-practices-gaming-database
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/best-practices-gaming-database#design_indexing_to_avoid_hotspots
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Ranking (
+        PlayerID STRING(36) NOT NULL,
+        GameMode INT64 NOT NULL,
+        Score INT64 NOT NULL
+) PRIMARY KEY (PlayerID, GameMode)
+--
+CreateTableStatement [0-157]
+  PathExpression [13-20]
+    Identifier(Ranking) [13-20]
+  TableElementList [21-124]
+    ColumnDefinition [31-59]
+      Identifier(PlayerID) [31-39]
+      SimpleColumnSchema [40-59]
+        PathExpression [40-46]
+          Identifier(STRING) [40-46]
+        TypeParameterList [46-49]
+          IntLiteral(36) [47-49]
+        ColumnAttributeList [51-59]
+          NotNullColumnAttribute [51-59]
+    ColumnDefinition [69-92]
+      Identifier(GameMode) [69-77]
+      SimpleColumnSchema [78-92]
+        PathExpression [78-83]
+          Identifier(INT64) [78-83]
+        ColumnAttributeList [84-92]
+          NotNullColumnAttribute [84-92]
+    ColumnDefinition [102-122]
+      Identifier(Score) [102-107]
+      SimpleColumnSchema [108-122]
+        PathExpression [108-113]
+          Identifier(INT64) [108-113]
+        ColumnAttributeList [114-122]
+          NotNullColumnAttribute [114-122]
+  SpannerTableOptions [125-157]
+    PrimaryKey(ENFORCED) [125-157]
+      PrimaryKeyElementList [137-157]
+        PrimaryKeyElement(ASC) [138-146]
+          Identifier(PlayerID) [138-146]
+        PrimaryKeyElement(ASC) [148-156]
+          Identifier(GameMode) [148-156]
+==
+# https://docs.cloud.google.com/spanner/docs/best-practices-gaming-database#design_indexing_to_avoid_hotspots
+CREATE INDEX idx_score_ranking ON Ranking (
+        GameMode,
+        Score DESC
+)
+--
+CreateIndexStatement [0-82]
+  PathExpression [13-30]
+    Identifier(idx_score_ranking) [13-30]
+  PathExpression [34-41]
+    Identifier(Ranking) [34-41]
+  IndexItemList [42-82]
+    OrderingExpression(ASC) [52-60]
+      PathExpression [52-60]
+        Identifier(GameMode) [52-60]
+    OrderingExpression(DESC) [70-80]
+      PathExpression [70-75]
+        Identifier(Score) [70-75]
+==
+# https://docs.cloud.google.com/spanner/docs/best-practices-gaming-database#design_indexing_to_avoid_hotspots
+CREATE INDEX idx_score_ranking ON Ranking (
+        Score DESC,
+        GameMode
+)
+--
+CreateIndexStatement [0-82]
+  PathExpression [13-30]
+    Identifier(idx_score_ranking) [13-30]
+  PathExpression [34-41]
+    Identifier(Ranking) [34-41]
+  IndexItemList [42-82]
+    OrderingExpression(DESC) [52-62]
+      PathExpression [52-57]
+        Identifier(Score) [52-57]
+    OrderingExpression(ASC) [72-80]
+      PathExpression [72-80]
+        Identifier(GameMode) [72-80]
+==
+# https://docs.cloud.google.com/spanner/docs/best-practices-gaming-database#design_indexing_to_avoid_hotspots
+CREATE TABLE GameMode1Ranking (
+        PlayerID STRING(36) NOT NULL,
+        Score INT64 NOT NULL
+) PRIMARY KEY (PlayerID)
+--
+CreateTableStatement [0-123]
+  PathExpression [13-29]
+    Identifier(GameMode1Ranking) [13-29]
+  TableElementList [30-100]
+    ColumnDefinition [40-68]
+      Identifier(PlayerID) [40-48]
+      SimpleColumnSchema [49-68]
+        PathExpression [49-55]
+          Identifier(STRING) [49-55]
+        TypeParameterList [55-58]
+          IntLiteral(36) [56-58]
+        ColumnAttributeList [60-68]
+          NotNullColumnAttribute [60-68]
+    ColumnDefinition [78-98]
+      Identifier(Score) [78-83]
+      SimpleColumnSchema [84-98]
+        PathExpression [84-89]
+          Identifier(INT64) [84-89]
+        ColumnAttributeList [90-98]
+          NotNullColumnAttribute [90-98]
+  SpannerTableOptions [101-123]
+    PrimaryKey(ENFORCED) [101-123]
+      PrimaryKeyElementList [113-123]
+        PrimaryKeyElement(ASC) [114-122]
+          Identifier(PlayerID) [114-122]
+==
+# https://docs.cloud.google.com/spanner/docs/best-practices-gaming-database#design_indexing_to_avoid_hotspots
+CREATE INDEX idx_score_ranking ON Ranking (
+        Score DESC
+)
+--
+CreateIndexStatement [0-64]
+  PathExpression [13-30]
+    Identifier(idx_score_ranking) [13-30]
+  PathExpression [34-41]
+    Identifier(Ranking) [34-41]
+  IndexItemList [42-64]
+    OrderingExpression(DESC) [52-62]
+      PathExpression [52-57]
+        Identifier(Score) [52-57]
+==
+# https://docs.cloud.google.com/spanner/docs/best-practices-gaming-database#run_load_tests_using_real_data
+CREATE TABLE Player (
+        PlayerID STRING(36) NOT NULL,
+        Attribute INT64 NOT NULL
+) PRIMARY KEY (PlayerID)
+--
+CreateTableStatement [0-117]
+  PathExpression [13-19]
+    Identifier(Player) [13-19]
+  TableElementList [20-94]
+    ColumnDefinition [30-58]
+      Identifier(PlayerID) [30-38]
+      SimpleColumnSchema [39-58]
+        PathExpression [39-45]
+          Identifier(STRING) [39-45]
+        TypeParameterList [45-48]
+          IntLiteral(36) [46-48]
+        ColumnAttributeList [50-58]
+          NotNullColumnAttribute [50-58]
+    ColumnDefinition [68-92]
+      Identifier(Attribute) [68-77]
+      SimpleColumnSchema [78-92]
+        PathExpression [78-83]
+          Identifier(INT64) [78-83]
+        ColumnAttributeList [84-92]
+          NotNullColumnAttribute [84-92]
+  SpannerTableOptions [95-117]
+    PrimaryKey(ENFORCED) [95-117]
+      PrimaryKeyElementList [107-117]
+        PrimaryKeyElement(ASC) [108-116]
+          Identifier(PlayerID) [108-116]
+==
+# https://docs.cloud.google.com/spanner/docs/best-practices-gaming-database#run_load_tests_using_real_data
+CREATE INDEX idx_attribute ON Player(Attribute)
+--
+CreateIndexStatement [0-47]
+  PathExpression [13-26]
+    Identifier(idx_attribute) [13-26]
+  PathExpression [30-36]
+    Identifier(Player) [30-36]
+  IndexItemList [36-47]
+    OrderingExpression(ASC) [37-46]
+      PathExpression [37-46]
+        Identifier(Attribute) [37-46]
+==
+# https://docs.cloud.google.com/spanner/docs/best-practices-gaming-database#run_load_tests_using_real_data
+SELECT PlayerID
+        FROM Player@{force_index=idx_attribute}
+        WHERE Attribute = 23
+        LIMIT 10
+--
+QueryStatement [0-109]
+  Query [0-109]
+    Select [0-92]
+      SelectList [7-15]
+        SelectColumn [7-15]
+          PathExpression [7-15]
+            Identifier(PlayerID) [7-15]
+      FromClause [24-63]
+        TablePathExpression [29-63]
+          PathExpression [29-35]
+            Identifier(Player) [29-35]
+          Hint [35-63]
+            HintEntry [37-62]
+              Identifier(force_index) [37-48]
+              PathExpression [49-62]
+                Identifier(idx_attribute) [49-62]
+      WhereClause [72-92]
+        BinaryExpression(=) [78-92]
+          PathExpression [78-87]
+            Identifier(Attribute) [78-87]
+          IntLiteral(23) [90-92]
+    LimitOffset [101-109]
+      Limit [101-109]
+        IntLiteral(10) [107-109]
+==
+# https://docs.cloud.google.com/spanner/docs/best-practices-gaming-database#run_load_tests_using_real_data
+SELECT PlayerID
+        FROM Player@{force_index=idx_attribute}
+        WHERE IndexPartition BETWEEN 1 and 6
+        AND Attribute = 23
+        LIMIT 10
+--
+QueryStatement [0-152]
+  Query [0-152]
+    Select [0-135]
+      SelectList [7-15]
+        SelectColumn [7-15]
+          PathExpression [7-15]
+            Identifier(PlayerID) [7-15]
+      FromClause [24-63]
+        TablePathExpression [29-63]
+          PathExpression [29-35]
+            Identifier(Player) [29-35]
+          Hint [35-63]
+            HintEntry [37-62]
+              Identifier(force_index) [37-48]
+              PathExpression [49-62]
+                Identifier(idx_attribute) [49-62]
+      WhereClause [72-135]
+        AndExpr [78-135]
+          BetweenExpression(BETWEEN) [78-108]
+            PathExpression [78-92]
+              Identifier(IndexPartition) [78-92]
+            Location [93-100]
+            IntLiteral(1) [101-102]
+            IntLiteral(6) [107-108]
+          BinaryExpression(=) [121-135]
+            PathExpression [121-130]
+              Identifier(Attribute) [121-130]
+            IntLiteral(23) [133-135]
+    LimitOffset [144-152]
+      Limit [144-152]
+        IntLiteral(10) [150-152]
diff --git a/parser/testdata/spanner/guides/bulk-loading.test b/parser/testdata/spanner/guides/bulk-loading.test
new file mode 100644
index 0000000..7d43066
--- /dev/null
+++ b/parser/testdata/spanner/guides/bulk-loading.test
@@ -0,0 +1,42 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/bulk-loading
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/bulk-loading#interleave-in
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT pk1, pk2 FROM child
+      EXCEPT DISTINCT
+      SELECT pk1, pk2 FROM parent
+--
+QueryStatement [0-82]
+  Query [0-82]
+    SetOperation(EXCEPT DISTINCT) [0-82]
+      SetOperationMetadataList [33-48]
+        SetOperationMetadata [33-48]
+          SetOperationType [33-39]
+          SetOperationAllOrDistinct [40-48]
+      Select [0-26]
+        SelectList [7-15]
+          SelectColumn [7-10]
+            PathExpression [7-10]
+              Identifier(pk1) [7-10]
+          SelectColumn [12-15]
+            PathExpression [12-15]
+              Identifier(pk2) [12-15]
+        FromClause [16-26]
+          TablePathExpression [21-26]
+            PathExpression [21-26]
+              Identifier(child) [21-26]
+      Select [55-82]
+        SelectList [62-70]
+          SelectColumn [62-65]
+            PathExpression [62-65]
+              Identifier(pk1) [62-65]
+          SelectColumn [67-70]
+            PathExpression [67-70]
+              Identifier(pk2) [67-70]
+        FromClause [71-82]
+          TablePathExpression [76-82]
+            PathExpression [76-82]
+              Identifier(parent) [76-82]
diff --git a/parser/testdata/spanner/guides/change-streams.details.test b/parser/testdata/spanner/guides/change-streams.details.test
new file mode 100644
index 0000000..a4645ef
--- /dev/null
+++ b/parser/testdata/spanner/guides/change-streams.details.test
@@ -0,0 +1,231 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/change-streams/details
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/change-streams/details#change_stream_query_syntax
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT ChangeRecord
+FROM READ_SingersNameStream (
+    start_timestamp,
+    end_timestamp,
+    partition_token,
+    heartbeat_milliseconds,
+    read_options
+)
+--
+QueryStatement [0-157]
+  Query [0-157]
+    Select [0-157]
+      SelectList [7-19]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(ChangeRecord) [7-19]
+      FromClause [20-157]
+        TVF [25-157]
+          PathExpression [25-47]
+            Identifier(READ_SingersNameStream) [25-47]
+          TVFArgument [54-69]
+            PathExpression [54-69]
+              Identifier(start_timestamp) [54-69]
+          TVFArgument [75-88]
+            PathExpression [75-88]
+              Identifier(end_timestamp) [75-88]
+          TVFArgument [94-109]
+            PathExpression [94-109]
+              Identifier(partition_token) [94-109]
+          TVFArgument [115-137]
+            PathExpression [115-137]
+              Identifier(heartbeat_milliseconds) [115-137]
+          TVFArgument [143-155]
+            PathExpression [143-155]
+              Identifier(read_options) [143-155]
+==
+# https://docs.cloud.google.com/spanner/docs/change-streams/details#example-change-stream-query
+SELECT ChangeRecord FROM READ_SingersNameStream (
+  start_timestamp => "2022-05-01T09:00:00Z",
+  end_timestamp => NULL,
+  partition_token => NULL,
+  heartbeat_milliseconds => 10000
+)
+--
+QueryStatement [0-182]
+  Query [0-182]
+    Select [0-182]
+      SelectList [7-19]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(ChangeRecord) [7-19]
+      FromClause [20-182]
+        TVF [25-182]
+          PathExpression [25-47]
+            Identifier(READ_SingersNameStream) [25-47]
+          TVFArgument [52-93]
+            NamedArgument [52-93]
+              Identifier(start_timestamp) [52-67]
+              StringLiteral [71-93]
+                StringLiteralComponent("2022-05-01T09:00:00Z") [71-93]
+          TVFArgument [97-118]
+            NamedArgument [97-118]
+              Identifier(end_timestamp) [97-110]
+              NullLiteral(NULL) [114-118]
+          TVFArgument [122-145]
+            NamedArgument [122-145]
+              Identifier(partition_token) [122-137]
+              NullLiteral(NULL) [141-145]
+          TVFArgument [149-180]
+            NamedArgument [149-180]
+              Identifier(heartbeat_milliseconds) [149-171]
+              IntLiteral(10000) [175-180]
+==
+# https://docs.cloud.google.com/spanner/docs/change-streams/details#example-change-stream-query
+SELECT ChangeRecord FROM READ_SingersNameStream (
+  start_timestamp => "2022-05-01T09:00:01Z",
+  end_timestamp => NULL,
+  partition_token => "child_token_1",
+  heartbeat_milliseconds => 10000
+)
+--
+QueryStatement [0-193]
+  Query [0-193]
+    Select [0-193]
+      SelectList [7-19]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(ChangeRecord) [7-19]
+      FromClause [20-193]
+        TVF [25-193]
+          PathExpression [25-47]
+            Identifier(READ_SingersNameStream) [25-47]
+          TVFArgument [52-93]
+            NamedArgument [52-93]
+              Identifier(start_timestamp) [52-67]
+              StringLiteral [71-93]
+                StringLiteralComponent("2022-05-01T09:00:01Z") [71-93]
+          TVFArgument [97-118]
+            NamedArgument [97-118]
+              Identifier(end_timestamp) [97-110]
+              NullLiteral(NULL) [114-118]
+          TVFArgument [122-156]
+            NamedArgument [122-156]
+              Identifier(partition_token) [122-137]
+              StringLiteral [141-156]
+                StringLiteralComponent("child_token_1") [141-156]
+          TVFArgument [160-191]
+            NamedArgument [160-191]
+              Identifier(heartbeat_milliseconds) [160-182]
+              IntLiteral(10000) [186-191]
+==
+# https://docs.cloud.google.com/spanner/docs/change-streams/details#example-change-stream-query
+SELECT ChangeRecord FROM READ_SingersNameStream (
+  start_timestamp => "2022-05-01T09:00:01Z",
+  end_timestamp => NULL,
+  partition_token => "child_token_2",
+  heartbeat_milliseconds => 10000
+)
+--
+QueryStatement [0-193]
+  Query [0-193]
+    Select [0-193]
+      SelectList [7-19]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(ChangeRecord) [7-19]
+      FromClause [20-193]
+        TVF [25-193]
+          PathExpression [25-47]
+            Identifier(READ_SingersNameStream) [25-47]
+          TVFArgument [52-93]
+            NamedArgument [52-93]
+              Identifier(start_timestamp) [52-67]
+              StringLiteral [71-93]
+                StringLiteralComponent("2022-05-01T09:00:01Z") [71-93]
+          TVFArgument [97-118]
+            NamedArgument [97-118]
+              Identifier(end_timestamp) [97-110]
+              NullLiteral(NULL) [114-118]
+          TVFArgument [122-156]
+            NamedArgument [122-156]
+              Identifier(partition_token) [122-137]
+              StringLiteral [141-156]
+                StringLiteralComponent("child_token_2") [141-156]
+          TVFArgument [160-191]
+            NamedArgument [160-191]
+              Identifier(heartbeat_milliseconds) [160-182]
+              IntLiteral(10000) [186-191]
+==
+# https://docs.cloud.google.com/spanner/docs/change-streams/details#example-change-stream-query
+SELECT ChangeRecord FROM READ_SingersNameStream (
+  start_timestamp => "2022-05-01T09:00:01Z",
+  end_timestamp => NULL,
+  partition_token => "child_token_3",
+  heartbeat_milliseconds => 10000
+)
+--
+QueryStatement [0-193]
+  Query [0-193]
+    Select [0-193]
+      SelectList [7-19]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(ChangeRecord) [7-19]
+      FromClause [20-193]
+        TVF [25-193]
+          PathExpression [25-47]
+            Identifier(READ_SingersNameStream) [25-47]
+          TVFArgument [52-93]
+            NamedArgument [52-93]
+              Identifier(start_timestamp) [52-67]
+              StringLiteral [71-93]
+                StringLiteralComponent("2022-05-01T09:00:01Z") [71-93]
+          TVFArgument [97-118]
+            NamedArgument [97-118]
+              Identifier(end_timestamp) [97-110]
+              NullLiteral(NULL) [114-118]
+          TVFArgument [122-156]
+            NamedArgument [122-156]
+              Identifier(partition_token) [122-137]
+              StringLiteral [141-156]
+                StringLiteralComponent("child_token_3") [141-156]
+          TVFArgument [160-191]
+            NamedArgument [160-191]
+              Identifier(heartbeat_milliseconds) [160-182]
+              IntLiteral(10000) [186-191]
+==
+# https://docs.cloud.google.com/spanner/docs/change-streams/details#example-change-stream-query
+SELECT ChangeRecord FROM READ_SingersNameStream(
+  start_timestamp => "2022-05-01T09:30:15Z",
+  end_timestamp => NULL,
+  partition_token => "child_token_4",
+  heartbeat_milliseconds => 10000
+)
+--
+QueryStatement [0-192]
+  Query [0-192]
+    Select [0-192]
+      SelectList [7-19]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(ChangeRecord) [7-19]
+      FromClause [20-192]
+        TVF [25-192]
+          PathExpression [25-47]
+            Identifier(READ_SingersNameStream) [25-47]
+          TVFArgument [51-92]
+            NamedArgument [51-92]
+              Identifier(start_timestamp) [51-66]
+              StringLiteral [70-92]
+                StringLiteralComponent("2022-05-01T09:30:15Z") [70-92]
+          TVFArgument [96-117]
+            NamedArgument [96-117]
+              Identifier(end_timestamp) [96-109]
+              NullLiteral(NULL) [113-117]
+          TVFArgument [121-155]
+            NamedArgument [121-155]
+              Identifier(partition_token) [121-136]
+              StringLiteral [140-155]
+                StringLiteralComponent("child_token_4") [140-155]
+          TVFArgument [159-190]
+            NamedArgument [159-190]
+              Identifier(heartbeat_milliseconds) [159-181]
+              IntLiteral(10000) [185-190]
diff --git a/parser/testdata/spanner/guides/change-streams.manage.spanner.sql b/parser/testdata/spanner/guides/change-streams.manage.spanner.sql
new file mode 100644
index 0000000..a15a2bd
--- /dev/null
+++ b/parser/testdata/spanner/guides/change-streams.manage.spanner.sql
@@ -0,0 +1,149 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/change-streams/manage
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#watch-entire-db
+# id 225eb11e770e: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:8]
+CREATE CHANGE STREAM EverythingStream
+FOR ALL;
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#watch-specific-tables
+# id 8e3dc42d0625: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:8]
+CREATE CHANGE STREAM SingerAlbumStream
+FOR Singers, Albums;
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#watch-specific-columns
+# id 34c6b852464a: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:8]
+CREATE CHANGE STREAM NamesAndTitles
+FOR Singers(FirstName, LastName), Albums(Title);
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#watch-single-stream
+# id 52f1211c24a1: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:8]
+CREATE CHANGE STREAM NamesAndAlbums
+FOR Singers(FirstName, LastName), Albums;
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#specify-retention
+# id 4a6707c3d12a: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:8]
+CREATE CHANGE STREAM LongerDataRetention
+FOR ALL
+OPTIONS ( retention_period = '36h' );
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#specify-retention
+# id 5c61bc693ebd: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:8]
+CREATE CHANGE STREAM MaximumDataRetention
+FOR ALL
+OPTIONS ( retention_period = '7d' );
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#specify-value-capture-type
+# id 169335cabce7: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:8]
+CREATE CHANGE STREAM NewRowChangeStream
+FOR ALL
+OPTIONS ( value_capture_type = 'NEW_ROW' );
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#specify-value-capture-type
+# id 74879c0f988e: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:8]
+CREATE CHANGE STREAM NewValuesChangeStream
+FOR ALL
+OPTIONS ( value_capture_type = 'NEW_VALUES' );
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#specify-value-capture-type
+# id 87211d59c264: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:8]
+CREATE CHANGE STREAM NewRowAndOldValuesChangeStream
+FOR ALL
+OPTIONS ( value_capture_type = 'NEW_ROW_AND_OLD_VALUES' );
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#filter-ttl-deletes
+# id 863484ba8dd9: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:8]
+CREATE CHANGE STREAM NewFilterChangeStream FOR ALL
+OPTIONS (exclude_ttl_deletes = true);
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#filter-mod-type
+# id 9e2d2b822536: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:8]
+CREATE CHANGE STREAM NewFilterChangeStream FOR ALL
+OPTIONS (exclude_insert = true, exclude_update = true);
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#enable-transaction-exclusion
+# id e68a813ec69e: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:8]
+CREATE CHANGE STREAM NewChangeStream FOR ALL
+OPTIONS (allow_txn_exclusion = true);
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#modify-table-columns
+# id 35d491ab634c: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:7]
+ALTER CHANGE STREAM NamesAndAlbums
+SET FOR Singers(FirstName, LastName), Albums, Songs;
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#modify-retention
+# id 5c4ce0908b11: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:7]
+ALTER CHANGE STREAM NamesAndAlbums
+SET OPTIONS ( retention_period = '36h' );
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#modify-value-capture-type
+# id d4439304f0c3: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:7]
+ALTER CHANGE STREAM NamesAndAlbums
+SET OPTIONS ( value_capture_type = 'NEW_VALUES' );
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#add_the_ttl-based_deletes_filter_to_an_existing_change_stream
+# id 545827191d61: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:7]
+ALTER CHANGE STREAM NewFilterChangeStream
+SET OPTIONS (exclude_ttl_deletes = true);
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#remove_the_ttl-based_deletes_filter_from_an_existing_change_stream
+# id e86c61083c22: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:7]
+ALTER CHANGE STREAM NewFilterChangeStream
+SET OPTIONS (exclude_ttl_deletes = false);
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#add_a_table_modification_type_filter_to_an_existing_change_stream
+# id bc4cac6f97b3: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:7]
+ALTER CHANGE STREAM NewFilterChangeStream
+SET OPTIONS (exclude_delete = true);
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#remove_a_table_modification_type_filter_from_an_existing_change_stream
+# id d4f93cc03140: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:7]
+ALTER CHANGE STREAM NewFilterChangeStream
+SET OPTIONS (exclude_delete = false);
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#enable_transaction-level_records_exclusion_for_an_existing_change_stream
+# id dfdced3e930e: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:7]
+ALTER CHANGE STREAM NewAllowedChangeStream
+SET OPTIONS (allow_txn_exclusion = true);
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#disable_transaction-level_records_exclusion_for_an_existing_change_stream
+# id ad1588a2a382: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:7]
+ALTER CHANGE STREAM NewAllowedChangeStream
+SET OPTIONS (allow_txn_exclusion = false);
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#suspend
+# id f795e8dbf4ec: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:7]
+ALTER CHANGE STREAM MyStream DROP FOR ALL;
+
+# https://docs.cloud.google.com/spanner/docs/change-streams/manage#delete
+# id f5f4fc880a29: change streams (change-streams.manage)
+# execute_query: INVALID_ARGUMENT: CHANGE is not a supported object type [at 1:6]
+DROP CHANGE STREAM NamesAndAlbums;
diff --git a/parser/testdata/spanner/guides/change-streams.use-dataflow.test b/parser/testdata/spanner/guides/change-streams.use-dataflow.test
new file mode 100644
index 0000000..51382c2
--- /dev/null
+++ b/parser/testdata/spanner/guides/change-streams.use-dataflow.test
@@ -0,0 +1,43 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/change-streams/use-dataflow
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/change-streams/use-dataflow#fetch-full-row
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Singers (
+  SingerId INT64 NOT NULL,
+  FirstName STRING(1024),
+  LastName STRING(1024)
+) PRIMARY KEY (SingerId)
+--
+CreateTableStatement [0-124]
+  PathExpression [13-20]
+    Identifier(Singers) [13-20]
+  TableElementList [21-101]
+    ColumnDefinition [25-48]
+      Identifier(SingerId) [25-33]
+      SimpleColumnSchema [34-48]
+        PathExpression [34-39]
+          Identifier(INT64) [34-39]
+        ColumnAttributeList [40-48]
+          NotNullColumnAttribute [40-48]
+    ColumnDefinition [52-74]
+      Identifier(FirstName) [52-61]
+      SimpleColumnSchema [62-74]
+        PathExpression [62-68]
+          Identifier(STRING) [62-68]
+        TypeParameterList [68-73]
+          IntLiteral(1024) [69-73]
+    ColumnDefinition [78-99]
+      Identifier(LastName) [78-86]
+      SimpleColumnSchema [87-99]
+        PathExpression [87-93]
+          Identifier(STRING) [87-93]
+        TypeParameterList [93-98]
+          IntLiteral(1024) [94-98]
+  SpannerTableOptions [102-124]
+    PrimaryKey(ENFORCED) [102-124]
+      PrimaryKeyElementList [114-124]
+        PrimaryKeyElement(ASC) [115-123]
+          Identifier(SingerId) [115-123]
diff --git a/parser/testdata/spanner/guides/check-constraint.how-to.test b/parser/testdata/spanner/guides/check-constraint.how-to.test
new file mode 100644
index 0000000..3f72639
--- /dev/null
+++ b/parser/testdata/spanner/guides/check-constraint.how-to.test
@@ -0,0 +1,132 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/check-constraint/how-to
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/check-constraint/how-to#create-table
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Concerts (
+  ConcertId INT64,
+  StartTime Timestamp,
+  EndTime Timestamp,
+  CONSTRAINT start_before_end CHECK(StartTime < EndTime),
+) PRIMARY KEY (ConcertId)
+--
+CreateTableStatement [0-170]
+  PathExpression [13-21]
+    Identifier(Concerts) [13-21]
+  TableElementList [22-146]
+    ColumnDefinition [26-41]
+      Identifier(ConcertId) [26-35]
+      SimpleColumnSchema [36-41]
+        PathExpression [36-41]
+          Identifier(INT64) [36-41]
+    ColumnDefinition [45-64]
+      Identifier(StartTime) [45-54]
+      SimpleColumnSchema [55-64]
+        PathExpression [55-64]
+          Identifier(Timestamp) [55-64]
+    ColumnDefinition [68-85]
+      Identifier(EndTime) [68-75]
+      SimpleColumnSchema [76-85]
+        PathExpression [76-85]
+          Identifier(Timestamp) [76-85]
+    CheckConstraint(ENFORCED) [89-143]
+      BinaryExpression(<) [123-142]
+        PathExpression [123-132]
+          Identifier(StartTime) [123-132]
+        PathExpression [135-142]
+          Identifier(EndTime) [135-142]
+      Identifier(start_before_end) [100-116]
+  SpannerTableOptions [147-170]
+    PrimaryKey(ENFORCED) [147-170]
+      PrimaryKeyElementList [159-170]
+        PrimaryKeyElement(ASC) [160-169]
+          Identifier(ConcertId) [160-169]
+==
+# https://docs.cloud.google.com/spanner/docs/check-constraint/how-to#alter-table
+ALTER TABLE Concerts
+ADD CONSTRAINT concert_id_gt_0 CHECK (ConcertId > 0)
+--
+AlterTableStatement [0-73]
+  PathExpression [12-20]
+    Identifier(Concerts) [12-20]
+  AlterActionList [21-73]
+    AddConstraintAction [21-73]
+      CheckConstraint(ENFORCED) [36-73]
+        BinaryExpression(>) [59-72]
+          PathExpression [59-68]
+            Identifier(ConcertId) [59-68]
+          IntLiteral(0) [71-72]
+        Identifier(concert_id_gt_0) [36-51]
+==
+# https://docs.cloud.google.com/spanner/docs/check-constraint/how-to#drop-constraint
+ALTER TABLE Concerts
+DROP CONSTRAINT concert_id_gt_0
+--
+AlterTableStatement [0-52]
+  PathExpression [12-20]
+    Identifier(Concerts) [12-20]
+  AlterActionList [21-52]
+    DropConstraintAction [21-52]
+      Identifier(concert_id_gt_0) [37-52]
+==
+# https://docs.cloud.google.com/spanner/docs/check-constraint/how-to#view_props
+SELECT tc.CONSTRAINT_NAME, tc.TABLE_NAME, tc.CONSTRAINT_TYPE
+FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS as tc
+WHERE tc.CONSTRAINT_TYPE = 'CHECK'
+--
+QueryStatement [0-143]
+  Query [0-143]
+    Select [0-143]
+      SelectList [7-60]
+        SelectColumn [7-25]
+          PathExpression [7-25]
+            Identifier(tc) [7-9]
+            Identifier(CONSTRAINT_NAME) [10-25]
+        SelectColumn [27-40]
+          PathExpression [27-40]
+            Identifier(tc) [27-29]
+            Identifier(TABLE_NAME) [30-40]
+        SelectColumn [42-60]
+          PathExpression [42-60]
+            Identifier(tc) [42-44]
+            Identifier(CONSTRAINT_TYPE) [45-60]
+      FromClause [61-108]
+        TablePathExpression [66-108]
+          PathExpression [66-102]
+            Identifier(INFORMATION_SCHEMA) [66-84]
+            Identifier(TABLE_CONSTRAINTS) [85-102]
+          Alias [103-108]
+            Identifier(tc) [106-108]
+      WhereClause [109-143]
+        BinaryExpression(=) [115-143]
+          PathExpression [115-133]
+            Identifier(tc) [115-117]
+            Identifier(CONSTRAINT_TYPE) [118-133]
+          StringLiteral [136-143]
+            StringLiteralComponent('CHECK') [136-143]
+==
+# https://docs.cloud.google.com/spanner/docs/check-constraint/how-to#view_props
+SELECT cc.CONSTRAINT_NAME, cc.SPANNER_STATE
+FROM INFORMATION_SCHEMA.CHECK_CONSTRAINTS as cc
+--
+QueryStatement [0-91]
+  Query [0-91]
+    Select [0-91]
+      SelectList [7-43]
+        SelectColumn [7-25]
+          PathExpression [7-25]
+            Identifier(cc) [7-9]
+            Identifier(CONSTRAINT_NAME) [10-25]
+        SelectColumn [27-43]
+          PathExpression [27-43]
+            Identifier(cc) [27-29]
+            Identifier(SPANNER_STATE) [30-43]
+      FromClause [44-91]
+        TablePathExpression [49-91]
+          PathExpression [49-85]
+            Identifier(INFORMATION_SCHEMA) [49-67]
+            Identifier(CHECK_CONSTRAINTS) [68-85]
+          Alias [86-91]
+            Identifier(cc) [89-91]
diff --git a/parser/testdata/spanner/guides/commit-timestamp.test b/parser/testdata/spanner/guides/commit-timestamp.test
new file mode 100644
index 0000000..6541529
--- /dev/null
+++ b/parser/testdata/spanner/guides/commit-timestamp.test
@@ -0,0 +1,383 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/commit-timestamp
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/commit-timestamp#keys-indexes
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE INDEX UsersByLastAccess ON Users(LastAccess)
+--
+CreateIndexStatement [0-51]
+  PathExpression [13-30]
+    Identifier(UsersByLastAccess) [13-30]
+  PathExpression [34-39]
+    Identifier(Users) [34-39]
+  IndexItemList [39-51]
+    OrderingExpression(ASC) [40-50]
+      PathExpression [40-50]
+        Identifier(LastAccess) [40-50]
+==
+# https://docs.cloud.google.com/spanner/docs/commit-timestamp#keys-indexes
+CREATE INDEX UsersByLastAccessAndName ON Users(LastAccess, FirstName)
+--
+CreateIndexStatement [0-69]
+  PathExpression [13-37]
+    Identifier(UsersByLastAccessAndName) [13-37]
+  PathExpression [41-46]
+    Identifier(Users) [41-46]
+  IndexItemList [46-69]
+    OrderingExpression(ASC) [47-57]
+      PathExpression [47-57]
+        Identifier(LastAccess) [47-57]
+    OrderingExpression(ASC) [59-68]
+      PathExpression [59-68]
+        Identifier(FirstName) [59-68]
+==
+# https://docs.cloud.google.com/spanner/docs/commit-timestamp#create-column
+CREATE TABLE Performances (
+    SingerId        INT64 NOT NULL,
+    VenueId         INT64 NOT NULL,
+    EventDate       Date,
+    Revenue         INT64,
+    LastUpdateTime  TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)
+) PRIMARY KEY (SingerId, VenueId, EventDate),
+  INTERLEAVE IN PARENT Singers ON DELETE CASCADE
+--
+CreateTableStatement [0-324]
+  PathExpression [13-25]
+    Identifier(Performances) [13-25]
+  TableElementList [26-231]
+    ColumnDefinition [32-62]
+      Identifier(SingerId) [32-40]
+      SimpleColumnSchema [48-62]
+        PathExpression [48-53]
+          Identifier(INT64) [48-53]
+        ColumnAttributeList [54-62]
+          NotNullColumnAttribute [54-62]
+    ColumnDefinition [68-98]
+      Identifier(VenueId) [68-75]
+      SimpleColumnSchema [84-98]
+        PathExpression [84-89]
+          Identifier(INT64) [84-89]
+        ColumnAttributeList [90-98]
+          NotNullColumnAttribute [90-98]
+    ColumnDefinition [104-124]
+      Identifier(EventDate) [104-113]
+      SimpleColumnSchema [120-124]
+        PathExpression [120-124]
+          Identifier(Date) [120-124]
+    ColumnDefinition [130-151]
+      Identifier(Revenue) [130-137]
+      SimpleColumnSchema [146-151]
+        PathExpression [146-151]
+          Identifier(INT64) [146-151]
+    ColumnDefinition [157-229]
+      Identifier(LastUpdateTime) [157-171]
+      SimpleColumnSchema [173-229]
+        PathExpression [173-182]
+          Identifier(TIMESTAMP) [173-182]
+        ColumnAttributeList [183-191]
+          NotNullColumnAttribute [183-191]
+        OptionsList [200-229]
+          OptionsEntry [201-228]
+            Identifier(allow_commit_timestamp) [201-223]
+            BooleanLiteral(true) [224-228]
+  SpannerTableOptions [232-324]
+    PrimaryKey(ENFORCED) [232-274]
+      PrimaryKeyElementList [244-274]
+        PrimaryKeyElement(ASC) [245-253]
+          Identifier(SingerId) [245-253]
+        PrimaryKeyElement(ASC) [255-262]
+          Identifier(VenueId) [255-262]
+        PrimaryKeyElement(ASC) [264-273]
+          Identifier(EventDate) [264-273]
+    SpannerInterleaveClause [274-324]
+      PathExpression [299-306]
+        Identifier(Singers) [299-306]
+==
+# https://docs.cloud.google.com/spanner/docs/commit-timestamp#add_a_commit_timestamp_column_to_an_existing_table
+ALTER TABLE Performances ADD COLUMN LastUpdateTime TIMESTAMP
+    NOT NULL OPTIONS (allow_commit_timestamp=true)
+--
+AlterTableStatement [0-111]
+  PathExpression [12-24]
+    Identifier(Performances) [12-24]
+  AlterActionList [25-111]
+    AddColumnAction [25-111]
+      ColumnDefinition [36-111]
+        Identifier(LastUpdateTime) [36-50]
+        SimpleColumnSchema [51-111]
+          PathExpression [51-60]
+            Identifier(TIMESTAMP) [51-60]
+          ColumnAttributeList [65-73]
+            NotNullColumnAttribute [65-73]
+          OptionsList [82-111]
+            OptionsEntry [83-110]
+              Identifier(allow_commit_timestamp) [83-105]
+              BooleanLiteral(true) [106-110]
+==
+# https://docs.cloud.google.com/spanner/docs/commit-timestamp#convert-timestamp
+ALTER TABLE Performances ALTER COLUMN LastUpdateTime
+    SET OPTIONS (allow_commit_timestamp=true)
+--
+AlterTableStatement [0-98]
+  PathExpression [12-24]
+    Identifier(Performances) [12-24]
+  AlterActionList [25-98]
+    AlterColumnOptionsAction [25-98]
+      Identifier(LastUpdateTime) [38-52]
+      OptionsList [69-98]
+        OptionsEntry [70-97]
+          Identifier(allow_commit_timestamp) [70-92]
+          BooleanLiteral(true) [93-97]
+==
+# https://docs.cloud.google.com/spanner/docs/commit-timestamp#remove-timestamp
+ALTER TABLE Performances ALTER COLUMN LastUpdateTime
+    SET OPTIONS (allow_commit_timestamp=null)
+--
+AlterTableStatement [0-98]
+  PathExpression [12-24]
+    Identifier(Performances) [12-24]
+  AlterActionList [25-98]
+    AlterColumnOptionsAction [25-98]
+      Identifier(LastUpdateTime) [38-52]
+      OptionsList [69-98]
+        OptionsEntry [70-97]
+          Identifier(allow_commit_timestamp) [70-92]
+          NullLiteral(null) [93-97]
+==
+# https://docs.cloud.google.com/spanner/docs/commit-timestamp#dml
+UPDATE Performances SET LastUpdateTime = PENDING_COMMIT_TIMESTAMP()
+   WHERE SingerId=1 AND VenueId=2 AND EventDate="2015-10-21"
+--
+UpdateStatement [0-128]
+  PathExpression [7-19]
+    Identifier(Performances) [7-19]
+  UpdateItemList [24-67]
+    UpdateItem [24-67]
+      UpdateSetValue [24-67]
+        PathExpression [24-38]
+          Identifier(LastUpdateTime) [24-38]
+        FunctionCall [41-67]
+          PathExpression [41-65]
+            Identifier(PENDING_COMMIT_TIMESTAMP) [41-65]
+  AndExpr [77-128]
+    BinaryExpression(=) [77-87]
+      PathExpression [77-85]
+        Identifier(SingerId) [77-85]
+      IntLiteral(1) [86-87]
+    BinaryExpression(=) [92-101]
+      PathExpression [92-99]
+        Identifier(VenueId) [92-99]
+      IntLiteral(2) [100-101]
+    BinaryExpression(=) [106-128]
+      PathExpression [106-115]
+        Identifier(EventDate) [106-115]
+      StringLiteral [116-128]
+        StringLiteralComponent("2015-10-21") [116-128]
+==
+# https://docs.cloud.google.com/spanner/docs/commit-timestamp#create-changelog
+CREATE TABLE Documents (
+  UserId     INT64 NOT NULL,
+  DocumentId INT64 NOT NULL,
+  Contents   STRING(MAX) NOT NULL,
+) PRIMARY KEY (UserId, DocumentId)
+--
+CreateTableStatement [0-152]
+  PathExpression [13-22]
+    Identifier(Documents) [13-22]
+  TableElementList [23-119]
+    ColumnDefinition [27-52]
+      Identifier(UserId) [27-33]
+      SimpleColumnSchema [38-52]
+        PathExpression [38-43]
+          Identifier(INT64) [38-43]
+        ColumnAttributeList [44-52]
+          NotNullColumnAttribute [44-52]
+    ColumnDefinition [56-81]
+      Identifier(DocumentId) [56-66]
+      SimpleColumnSchema [67-81]
+        PathExpression [67-72]
+          Identifier(INT64) [67-72]
+        ColumnAttributeList [73-81]
+          NotNullColumnAttribute [73-81]
+    ColumnDefinition [85-116]
+      Identifier(Contents) [85-93]
+      SimpleColumnSchema [96-116]
+        PathExpression [96-102]
+          Identifier(STRING) [96-102]
+        TypeParameterList [102-106]
+          MaxLiteral() [103-106]
+        ColumnAttributeList [108-116]
+          NotNullColumnAttribute [108-116]
+  SpannerTableOptions [120-152]
+    PrimaryKey(ENFORCED) [120-152]
+      PrimaryKeyElementList [132-152]
+        PrimaryKeyElement(ASC) [133-139]
+          Identifier(UserId) [133-139]
+        PrimaryKeyElement(ASC) [141-151]
+          Identifier(DocumentId) [141-151]
+==
+# https://docs.cloud.google.com/spanner/docs/commit-timestamp#create-changelog
+CREATE TABLE DocumentHistory (
+  UserId     INT64 NOT NULL,
+  DocumentId INT64 NOT NULL,
+  Ts         TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true),
+  Delta      STRING(MAX),
+) PRIMARY KEY (UserId, DocumentId, Ts),
+  INTERLEAVE IN PARENT Documents ON DELETE NO ACTION
+--
+CreateTableStatement [0-278]
+  PathExpression [13-28]
+    Identifier(DocumentHistory) [13-28]
+  TableElementList [29-187]
+    ColumnDefinition [33-58]
+      Identifier(UserId) [33-39]
+      SimpleColumnSchema [44-58]
+        PathExpression [44-49]
+          Identifier(INT64) [44-49]
+        ColumnAttributeList [50-58]
+          NotNullColumnAttribute [50-58]
+    ColumnDefinition [62-87]
+      Identifier(DocumentId) [62-72]
+      SimpleColumnSchema [73-87]
+        PathExpression [73-78]
+          Identifier(INT64) [73-78]
+        ColumnAttributeList [79-87]
+          NotNullColumnAttribute [79-87]
+    ColumnDefinition [91-158]
+      Identifier(Ts) [91-93]
+      SimpleColumnSchema [102-158]
+        PathExpression [102-111]
+          Identifier(TIMESTAMP) [102-111]
+        ColumnAttributeList [112-120]
+          NotNullColumnAttribute [112-120]
+        OptionsList [129-158]
+          OptionsEntry [130-157]
+            Identifier(allow_commit_timestamp) [130-152]
+            BooleanLiteral(true) [153-157]
+    ColumnDefinition [162-184]
+      Identifier(Delta) [162-167]
+      SimpleColumnSchema [173-184]
+        PathExpression [173-179]
+          Identifier(STRING) [173-179]
+        TypeParameterList [179-183]
+          MaxLiteral() [180-183]
+  SpannerTableOptions [188-278]
+    PrimaryKey(ENFORCED) [188-224]
+      PrimaryKeyElementList [200-224]
+        PrimaryKeyElement(ASC) [201-207]
+          Identifier(UserId) [201-207]
+        PrimaryKeyElement(ASC) [209-219]
+          Identifier(DocumentId) [209-219]
+        PrimaryKeyElement(ASC) [221-223]
+          Identifier(Ts) [221-223]
+    SpannerInterleaveClause [224-278]
+      PathExpression [249-258]
+        Identifier(Documents) [249-258]
+==
+# https://docs.cloud.google.com/spanner/docs/commit-timestamp#optimize
+CREATE TABLE Performances (
+    SingerId INT64 NOT NULL,
+    VenueId INT64 NOT NULL,
+    EventDate DATE,
+    Revenue INT64,
+    LastUpdateTime TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)
+) PRIMARY KEY (SingerId, VenueId, EventDate)
+--
+CreateTableStatement [0-244]
+  PathExpression [13-25]
+    Identifier(Performances) [13-25]
+  TableElementList [26-201]
+    ColumnDefinition [32-55]
+      Identifier(SingerId) [32-40]
+      SimpleColumnSchema [41-55]
+        PathExpression [41-46]
+          Identifier(INT64) [41-46]
+        ColumnAttributeList [47-55]
+          NotNullColumnAttribute [47-55]
+    ColumnDefinition [61-83]
+      Identifier(VenueId) [61-68]
+      SimpleColumnSchema [69-83]
+        PathExpression [69-74]
+          Identifier(INT64) [69-74]
+        ColumnAttributeList [75-83]
+          NotNullColumnAttribute [75-83]
+    ColumnDefinition [89-103]
+      Identifier(EventDate) [89-98]
+      SimpleColumnSchema [99-103]
+        PathExpression [99-103]
+          Identifier(DATE) [99-103]
+    ColumnDefinition [109-122]
+      Identifier(Revenue) [109-116]
+      SimpleColumnSchema [117-122]
+        PathExpression [117-122]
+          Identifier(INT64) [117-122]
+    ColumnDefinition [128-199]
+      Identifier(LastUpdateTime) [128-142]
+      SimpleColumnSchema [143-199]
+        PathExpression [143-152]
+          Identifier(TIMESTAMP) [143-152]
+        ColumnAttributeList [153-161]
+          NotNullColumnAttribute [153-161]
+        OptionsList [170-199]
+          OptionsEntry [171-198]
+            Identifier(allow_commit_timestamp) [171-193]
+            BooleanLiteral(true) [194-198]
+  SpannerTableOptions [202-244]
+    PrimaryKey(ENFORCED) [202-244]
+      PrimaryKeyElementList [214-244]
+        PrimaryKeyElement(ASC) [215-223]
+          Identifier(SingerId) [215-223]
+        PrimaryKeyElement(ASC) [225-232]
+          Identifier(VenueId) [225-232]
+        PrimaryKeyElement(ASC) [234-243]
+          Identifier(EventDate) [234-243]
+==
+# https://docs.cloud.google.com/spanner/docs/commit-timestamp#optimize
+SELECT * FROM Performances WHERE LastUpdateTime >= "2022-05-01"
+--
+QueryStatement [0-63]
+  Query [0-63]
+    Select [0-63]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-26]
+        TablePathExpression [14-26]
+          PathExpression [14-26]
+            Identifier(Performances) [14-26]
+      WhereClause [27-63]
+        BinaryExpression(>=) [33-63]
+          PathExpression [33-47]
+            Identifier(LastUpdateTime) [33-47]
+          StringLiteral [51-63]
+            StringLiteralComponent("2022-05-01") [51-63]
+==
+# https://docs.cloud.google.com/spanner/docs/commit-timestamp#optimize
+SELECT * FROM Performances
+  WHERE LastUpdateTime > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
+--
+QueryStatement [0-103]
+  Query [0-103]
+    Select [0-103]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-26]
+        TablePathExpression [14-26]
+          PathExpression [14-26]
+            Identifier(Performances) [14-26]
+      WhereClause [29-103]
+        BinaryExpression(>) [35-103]
+          PathExpression [35-49]
+            Identifier(LastUpdateTime) [35-49]
+          FunctionCall [52-103]
+            PathExpression [52-65]
+              Identifier(TIMESTAMP_SUB) [52-65]
+            FunctionCall [66-85]
+              PathExpression [66-83]
+                Identifier(`CURRENT_TIMESTAMP`) [66-83]
+            IntervalExpr [87-102]
+              IntLiteral(30) [96-98]
+              Identifier(DAY) [99-102]
diff --git a/parser/testdata/spanner/guides/configure-columnar-engine.spanner.sql b/parser/testdata/spanner/guides/configure-columnar-engine.spanner.sql
new file mode 100644
index 0000000..db86d4a
--- /dev/null
+++ b/parser/testdata/spanner/guides/configure-columnar-engine.spanner.sql
@@ -0,0 +1,20 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/configure-columnar-engine
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/configure-columnar-engine#enable-columnar-engine-gsql
+# id 51eb77fd9a06: table OPTIONS after PRIMARY KEY (configure-columnar-engine)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected keyword INTERLEAVE but got keyword OPTIONS [at 8:3]
+CREATE TABLE Concerts(
+  VenueId INT64 NOT NULL,
+  SingerId INT64 NOT NULL,
+  ConcertDate DATE NOT NULL,
+  BeginTime TIMESTAMP,
+  EndTime TIMESTAMP,)
+  PRIMARY KEY(VenueId, SingerId, ConcertDate),
+  OPTIONS (columnar_policy = 'disabled');
diff --git a/parser/testdata/spanner/guides/configure-columnar-engine.test b/parser/testdata/spanner/guides/configure-columnar-engine.test
new file mode 100644
index 0000000..e23a6bb
--- /dev/null
+++ b/parser/testdata/spanner/guides/configure-columnar-engine.test
@@ -0,0 +1,98 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/configure-columnar-engine
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/configure-columnar-engine#enable-columnar-engine-gsql
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE DATABASE Music
+--
+CreateDatabaseStatement [0-21]
+  PathExpression [16-21]
+    Identifier(Music) [16-21]
+==
+# https://docs.cloud.google.com/spanner/docs/configure-columnar-engine#enable-columnar-engine-gsql
+ALTER DATABASE Music SET OPTIONS (columnar_policy = 'enabled')
+--
+AlterDatabaseStatement [0-62]
+  PathExpression [15-20]
+    Identifier(Music) [15-20]
+  AlterActionList [21-62]
+    SetOptionsAction [21-62]
+      OptionsList [33-62]
+        OptionsEntry [34-61]
+          Identifier(columnar_policy) [34-49]
+          StringLiteral [52-61]
+            StringLiteralComponent('enabled') [52-61]
+==
+# https://docs.cloud.google.com/spanner/docs/configure-columnar-engine#enable-columnar-engine-gsql
+CREATE TABLE Singers(
+  SingerId INT64 NOT NULL,
+  FirstName STRING(1024),
+  LastName STRING(1024),
+  BirthDate DATE,
+  Status STRING(1024),
+  LastUpdated TIMESTAMP,)
+  PRIMARY KEY(SingerId)
+--
+CreateTableStatement [0-190]
+  PathExpression [13-20]
+    Identifier(Singers) [13-20]
+  TableElementList [20-166]
+    ColumnDefinition [24-47]
+      Identifier(SingerId) [24-32]
+      SimpleColumnSchema [33-47]
+        PathExpression [33-38]
+          Identifier(INT64) [33-38]
+        ColumnAttributeList [39-47]
+          NotNullColumnAttribute [39-47]
+    ColumnDefinition [51-73]
+      Identifier(FirstName) [51-60]
+      SimpleColumnSchema [61-73]
+        PathExpression [61-67]
+          Identifier(STRING) [61-67]
+        TypeParameterList [67-72]
+          IntLiteral(1024) [68-72]
+    ColumnDefinition [77-98]
+      Identifier(LastName) [77-85]
+      SimpleColumnSchema [86-98]
+        PathExpression [86-92]
+          Identifier(STRING) [86-92]
+        TypeParameterList [92-97]
+          IntLiteral(1024) [93-97]
+    ColumnDefinition [102-116]
+      Identifier(BirthDate) [102-111]
+      SimpleColumnSchema [112-116]
+        PathExpression [112-116]
+          Identifier(DATE) [112-116]
+    ColumnDefinition [120-139]
+      Identifier(Status) [120-126]
+      SimpleColumnSchema [127-139]
+        PathExpression [127-133]
+          Identifier(STRING) [127-133]
+        TypeParameterList [133-138]
+          IntLiteral(1024) [134-138]
+    ColumnDefinition [143-164]
+      Identifier(LastUpdated) [143-154]
+      SimpleColumnSchema [155-164]
+        PathExpression [155-164]
+          Identifier(TIMESTAMP) [155-164]
+  SpannerTableOptions [169-190]
+    PrimaryKey(ENFORCED) [169-190]
+      PrimaryKeyElementList [180-190]
+        PrimaryKeyElement(ASC) [181-189]
+          Identifier(SingerId) [181-189]
+==
+# https://docs.cloud.google.com/spanner/docs/configure-columnar-engine#enable-columnar-engine-gsql
+ALTER TABLE Singers SET OPTIONS (columnar_policy = 'disabled')
+--
+AlterTableStatement [0-62]
+  PathExpression [12-19]
+    Identifier(Singers) [12-19]
+  AlterActionList [20-62]
+    SetOptionsAction [20-62]
+      OptionsList [32-62]
+        OptionsEntry [33-61]
+          Identifier(columnar_policy) [33-48]
+          StringLiteral [51-61]
+            StringLiteralComponent('disabled') [51-61]
diff --git a/parser/testdata/spanner/guides/configure-fgac.spanner.sql b/parser/testdata/spanner/guides/configure-fgac.spanner.sql
new file mode 100644
index 0000000..b38730c
--- /dev/null
+++ b/parser/testdata/spanner/guides/configure-fgac.spanner.sql
@@ -0,0 +1,23 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/configure-fgac
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/configure-fgac#create-fgac-role
+# id aeacd0809f0e: GRANT/REVOKE for fine-grained access control (configure-fgac)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "." or keyword TO but got "," [at 1:48]
+GRANT SELECT, INSERT, UPDATE ON TABLE employees, contractors TO ROLE hr_manager;
+
+# https://docs.cloud.google.com/spanner/docs/configure-fgac#role-hierarchy
+# id 6d9c8bec9783: GRANT/REVOKE for fine-grained access control (configure-fgac)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected keyword ON but got identifier "role1" [at 1:12]
+GRANT ROLE role1 TO ROLE role2;
+
+# https://docs.cloud.google.com/spanner/docs/configure-fgac#drop_a_database_role
+# id 43c581426688: GRANT/REVOKE for fine-grained access control (configure-fgac)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "." but got "," [at 1:49]
+REVOKE SELECT, INSERT, UPDATE ON TABLE employees, contractors FROM ROLE hr_manager;
diff --git a/parser/testdata/spanner/guides/configure-fgac.test b/parser/testdata/spanner/guides/configure-fgac.test
new file mode 100644
index 0000000..60ea52b
--- /dev/null
+++ b/parser/testdata/spanner/guides/configure-fgac.test
@@ -0,0 +1,20 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/configure-fgac
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/configure-fgac#list_database_roles
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT * FROM INFORMATION_SCHEMA.ROLES
+--
+QueryStatement [0-38]
+  Query [0-38]
+    Select [0-38]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-38]
+        TablePathExpression [14-38]
+          PathExpression [14-38]
+            Identifier(INFORMATION_SCHEMA) [14-32]
+            Identifier(ROLES) [33-38]
diff --git a/parser/testdata/spanner/guides/create-manage-data-placements.spanner.sql b/parser/testdata/spanner/guides/create-manage-data-placements.spanner.sql
new file mode 100644
index 0000000..6aac82c
--- /dev/null
+++ b/parser/testdata/spanner/guides/create-manage-data-placements.spanner.sql
@@ -0,0 +1,24 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/create-manage-data-placements
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/create-manage-data-placements#create-placement
+# id 49501c169a47: data placement / geo-partitioning (create-manage-data-placements)
+# execute_query: INVALID_ARGUMENT: PLACEMENT is not a supported object type [at 1:8]
+CREATE PLACEMENT europeplacement OPTIONS (instance_partition="europe-partition");
+
+# https://docs.cloud.google.com/spanner/docs/create-manage-data-placements#modify-default-leader
+# id f751a8bfa73e: data placement / geo-partitioning (create-manage-data-placements)
+# execute_query: INVALID_ARGUMENT: PLACEMENT is not a supported object type [at 1:8]
+CREATE PLACEMENT `nam8placement`
+  OPTIONS (instance_partition="nam8-partition", default_leader="us-west1");
+
+# https://docs.cloud.google.com/spanner/docs/create-manage-data-placements#drop-placement
+# id 4a7131371b56: data placement / geo-partitioning (create-manage-data-placements)
+# execute_query: INVALID_ARGUMENT: PLACEMENT is not a supported object type [at 1:6]
+DROP PLACEMENT europeplacement;
diff --git a/parser/testdata/spanner/guides/create-manage-data-placements.test b/parser/testdata/spanner/guides/create-manage-data-placements.test
new file mode 100644
index 0000000..b0b5479
--- /dev/null
+++ b/parser/testdata/spanner/guides/create-manage-data-placements.test
@@ -0,0 +1,116 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/create-manage-data-placements
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/create-manage-data-placements#delete-table
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+DROP TABLE Singers
+--
+DropStatement TABLE [0-18]
+  PathExpression [11-18]
+    Identifier(Singers) [11-18]
+==
+# https://docs.cloud.google.com/spanner/docs/create-manage-data-placements#insert-row
+INSERT INTO Singers(SingerId, SingerName, Location)
+VALUES (1, 'Marc Richards', 'europeplacement')
+--
+InsertStatement [0-98]
+  PathExpression [12-19]
+    Identifier(Singers) [12-19]
+  ColumnList [19-51]
+    Identifier(SingerId) [20-28]
+    Identifier(SingerName) [30-40]
+    Identifier(Location) [42-50]
+  InsertValuesRowList [52-98]
+    InsertValuesRow [59-98]
+      IntLiteral(1) [60-61]
+      StringLiteral [63-78]
+        StringLiteralComponent('Marc Richards') [63-78]
+      StringLiteral [80-97]
+        StringLiteralComponent('europeplacement') [80-97]
+==
+# https://docs.cloud.google.com/spanner/docs/create-manage-data-placements#update-row
+UPDATE Singers s
+SET s.name='Catalina Smith'
+WHERE s.id=1
+--
+UpdateStatement [0-57]
+  PathExpression [7-14]
+    Identifier(Singers) [7-14]
+  Alias [15-16]
+    Identifier(s) [15-16]
+  UpdateItemList [21-44]
+    UpdateItem [21-44]
+      UpdateSetValue [21-44]
+        PathExpression [21-27]
+          Identifier(s) [21-22]
+          Identifier(name) [23-27]
+        StringLiteral [28-44]
+          StringLiteralComponent('Catalina Smith') [28-44]
+  BinaryExpression(=) [51-57]
+    PathExpression [51-55]
+      Identifier(s) [51-52]
+      Identifier(id) [53-55]
+    IntLiteral(1) [56-57]
+==
+# https://docs.cloud.google.com/spanner/docs/create-manage-data-placements#move-row
+UPDATE Singers s
+SET s.location='asiaplacement'
+WHERE s.id=1
+--
+UpdateStatement [0-60]
+  PathExpression [7-14]
+    Identifier(Singers) [7-14]
+  Alias [15-16]
+    Identifier(s) [15-16]
+  UpdateItemList [21-47]
+    UpdateItem [21-47]
+      UpdateSetValue [21-47]
+        PathExpression [21-31]
+          Identifier(s) [21-22]
+          Identifier(location) [23-31]
+        StringLiteral [32-47]
+          StringLiteralComponent('asiaplacement') [32-47]
+  BinaryExpression(=) [54-60]
+    PathExpression [54-58]
+      Identifier(s) [54-55]
+      Identifier(id) [56-58]
+    IntLiteral(1) [59-60]
+==
+# https://docs.cloud.google.com/spanner/docs/create-manage-data-placements#delete-row
+DELETE FROM Singers s
+WHERE s.id=1
+--
+DeleteStatement [0-34]
+  PathExpression [12-19]
+    Identifier(Singers) [12-19]
+  Alias [20-21]
+    Identifier(s) [20-21]
+  BinaryExpression(=) [28-34]
+    PathExpression [28-32]
+      Identifier(s) [28-29]
+      Identifier(id) [30-32]
+    IntLiteral(1) [33-34]
+==
+# https://docs.cloud.google.com/spanner/docs/create-manage-data-placements#query-data
+SELECT * FROM Singers s WHERE s.SingerId=1
+--
+QueryStatement [0-42]
+  Query [0-42]
+    Select [0-42]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-23]
+        TablePathExpression [14-23]
+          PathExpression [14-21]
+            Identifier(Singers) [14-21]
+          Alias [22-23]
+            Identifier(s) [22-23]
+      WhereClause [24-42]
+        BinaryExpression(=) [30-42]
+          PathExpression [30-40]
+            Identifier(s) [30-31]
+            Identifier(SingerId) [32-40]
+          IntLiteral(1) [41-42]
diff --git a/parser/testdata/spanner/guides/create-manage-databases.test b/parser/testdata/spanner/guides/create-manage-databases.test
new file mode 100644
index 0000000..22bf683
--- /dev/null
+++ b/parser/testdata/spanner/guides/create-manage-databases.test
@@ -0,0 +1,34 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/create-manage-databases
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/create-manage-databases#update-database
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+ALTER TABLE Songwriters ADD COLUMN Publisher STRING(10)
+--
+AlterTableStatement [0-55]
+  PathExpression [12-23]
+    Identifier(Songwriters) [12-23]
+  AlterActionList [24-55]
+    AddColumnAction [24-55]
+      ColumnDefinition [35-55]
+        Identifier(Publisher) [35-44]
+        SimpleColumnSchema [45-55]
+          PathExpression [45-51]
+            Identifier(STRING) [45-51]
+          TypeParameterList [51-54]
+            IntLiteral(10) [52-54]
+==
+# https://docs.cloud.google.com/spanner/docs/create-manage-databases#update-database
+ALTER DATABASE Music SET OPTIONS(optimizer_version=null)
+--
+AlterDatabaseStatement [0-56]
+  PathExpression [15-20]
+    Identifier(Music) [15-20]
+  AlterActionList [21-56]
+    SetOptionsAction [21-56]
+      OptionsList [32-56]
+        OptionsEntry [33-55]
+          Identifier(optimizer_version) [33-50]
+          NullLiteral(null) [51-55]
diff --git a/parser/testdata/spanner/guides/create-manage-locality-groups.spanner.sql b/parser/testdata/spanner/guides/create-manage-locality-groups.spanner.sql
new file mode 100644
index 0000000..f8cd213
--- /dev/null
+++ b/parser/testdata/spanner/guides/create-manage-locality-groups.spanner.sql
@@ -0,0 +1,82 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/create-manage-locality-groups
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/create-manage-locality-groups#create-locality-group
+# id 285931116d26: locality groups (create-manage-locality-groups)
+# execute_query: INVALID_ARGUMENT: CREATE LOCALITY GROUP is not supported [at 1:8]
+CREATE LOCALITY GROUP separate_storage;
+
+# https://docs.cloud.google.com/spanner/docs/create-manage-locality-groups#create-locality-group
+# id 999a3d5dfef6: locality groups (create-manage-locality-groups)
+# execute_query: INVALID_ARGUMENT: CREATE LOCALITY GROUP is not supported [at 1:8]
+CREATE LOCALITY GROUP ssd_only OPTIONS (storage='ssd');
+
+# https://docs.cloud.google.com/spanner/docs/create-manage-locality-groups#create-locality-group
+# id 2a925ceede24: locality groups (create-manage-locality-groups)
+# execute_query: INVALID_ARGUMENT: CREATE LOCALITY GROUP is not supported [at 1:8]
+CREATE LOCALITY GROUP hdd_only OPTIONS (storage='hdd');
+
+# https://docs.cloud.google.com/spanner/docs/create-manage-locality-groups#create-age-based-policy
+# id 01a0dff9e706: locality groups (create-manage-locality-groups)
+# execute_query: INVALID_ARGUMENT: CREATE LOCALITY GROUP is not supported [at 1:8]
+CREATE LOCALITY GROUP spill_to_hdd
+OPTIONS (storage = 'ssd', ssd_to_hdd_spill_timespan = '10d');
+
+# https://docs.cloud.google.com/spanner/docs/create-manage-locality-groups#set_a_database-level_locality_group
+# id 32396e1bf247: locality groups (create-manage-locality-groups)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected keyword LOCALITY [at 1:7]
+ALTER LOCALITY GROUP `default` SET OPTIONS (storage = 'ssd', ssd_to_hdd_spill_timespan = '10d');
+
+# https://docs.cloud.google.com/spanner/docs/create-manage-locality-groups#set-storage-policy-table
+# id 440625a3c44c: table OPTIONS after PRIMARY KEY (create-manage-locality-groups)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected keyword INTERLEAVE but got keyword OPTIONS [at 6:27]
+CREATE TABLE Singers (
+  SingerId   INT64 NOT NULL,
+  FirstName  STRING(1024),
+  LastName   STRING(1024),
+  SingerInfo BYTES(MAX)
+) PRIMARY KEY (SingerId), OPTIONS (locality_group = 'ssd_only');
+
+# https://docs.cloud.google.com/spanner/docs/create-manage-locality-groups#set-storage-policy-column
+# id da7b47723b15: table OPTIONS after PRIMARY KEY (create-manage-locality-groups)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected keyword INTERLEAVE but got keyword OPTIONS [at 6:27]
+CREATE TABLE Singers (
+  SingerId   INT64 NOT NULL,
+  FirstName  STRING(1024),
+  LastName   STRING(1024),
+  Awards     ARRAY OPTIONS (locality_group = 'spill_to_hdd')
+) PRIMARY KEY (SingerId), OPTIONS (locality_group = 'ssd_only');
+
+# https://docs.cloud.google.com/spanner/docs/create-manage-locality-groups#set-locality-group-column
+# id d135863ff683: table OPTIONS after PRIMARY KEY (create-manage-locality-groups)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ";" or end of input but got "," [at 8:49]
+CREATE TABLE Songs (
+  SingerId INT64 NOT NULL,
+  SongId INT64 NOT NULL,
+  Title STRING(MAX),
+  Description STRING(MAX),
+  LyricsCompressed BYTES(MAX) OPTIONS (locality_group = 'hdd_only')
+) PRIMARY KEY (SingerId, SongId),
+  INTERLEAVE IN PARENT Singers ON DELETE CASCADE,
+  OPTIONS (locality_group = 'ssd_only');
+
+# https://docs.cloud.google.com/spanner/docs/create-manage-locality-groups#change-storage-option
+# id b2707791e64d: locality groups (create-manage-locality-groups)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected keyword LOCALITY [at 1:7]
+ALTER LOCALITY GROUP separate_storage SET OPTIONS (storage='hdd');
+
+# https://docs.cloud.google.com/spanner/docs/create-manage-locality-groups#change-age-based-policy
+# id bd3d36a38974: locality groups (create-manage-locality-groups)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected keyword LOCALITY [at 1:7]
+ALTER LOCALITY GROUP spill_to_hdd SET OPTIONS (ssd_to_hdd_spill_timespan = '20d');
+
+# https://docs.cloud.google.com/spanner/docs/create-manage-locality-groups#delete-locality-group
+# id c8f11334b5a8: locality groups (create-manage-locality-groups)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected keyword LOCALITY [at 1:6]
+DROP LOCALITY GROUP ssd_only;
diff --git a/parser/testdata/spanner/guides/create-manage-locality-groups.test b/parser/testdata/spanner/guides/create-manage-locality-groups.test
new file mode 100644
index 0000000..4644e84
--- /dev/null
+++ b/parser/testdata/spanner/guides/create-manage-locality-groups.test
@@ -0,0 +1,57 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/create-manage-locality-groups
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/create-manage-locality-groups#set-storage-policy-index
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName)
+OPTIONS (locality_group = 'spill_to_hdd')
+--
+CreateIndexStatement [0-109]
+  PathExpression [13-35]
+    Identifier(SingersByFirstLastName) [13-35]
+  PathExpression [39-46]
+    Identifier(Singers) [39-46]
+  IndexItemList [46-67]
+    OrderingExpression(ASC) [47-56]
+      PathExpression [47-56]
+        Identifier(FirstName) [47-56]
+    OrderingExpression(ASC) [58-66]
+      PathExpression [58-66]
+        Identifier(LastName) [58-66]
+  OptionsList [76-109]
+    OptionsEntry [77-108]
+      Identifier(locality_group) [77-91]
+      StringLiteral [94-108]
+        StringLiteralComponent('spill_to_hdd') [94-108]
+==
+# https://docs.cloud.google.com/spanner/docs/create-manage-locality-groups#alter-table-locality-group
+ALTER TABLE Singers SET OPTIONS (locality_group = 'spill_to_hdd')
+--
+AlterTableStatement [0-65]
+  PathExpression [12-19]
+    Identifier(Singers) [12-19]
+  AlterActionList [20-65]
+    SetOptionsAction [20-65]
+      OptionsList [32-65]
+        OptionsEntry [33-64]
+          Identifier(locality_group) [33-47]
+          StringLiteral [50-64]
+            StringLiteralComponent('spill_to_hdd') [50-64]
+==
+# https://docs.cloud.google.com/spanner/docs/create-manage-locality-groups#alter-column-locality-group
+ALTER TABLE Singers
+ALTER COLUMN LastName SET OPTIONS(locality_group = 'spill_to_hdd')
+--
+AlterTableStatement [0-86]
+  PathExpression [12-19]
+    Identifier(Singers) [12-19]
+  AlterActionList [20-86]
+    AlterColumnOptionsAction [20-86]
+      Identifier(LastName) [33-41]
+      OptionsList [53-86]
+        OptionsEntry [54-85]
+          Identifier(locality_group) [54-68]
+          StringLiteral [71-85]
+            StringLiteralComponent('spill_to_hdd') [71-85]
diff --git a/parser/testdata/spanner/guides/create-manage-split-points.test b/parser/testdata/spanner/guides/create-manage-split-points.test
new file mode 100644
index 0000000..4069a07
--- /dev/null
+++ b/parser/testdata/spanner/guides/create-manage-split-points.test
@@ -0,0 +1,20 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/create-manage-split-points
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/create-manage-split-points#view-split-points
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT * FROM SPANNER_SYS.USER_SPLIT_POINTS
+--
+QueryStatement [0-43]
+  Query [0-43]
+    Select [0-43]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-43]
+        TablePathExpression [14-43]
+          PathExpression [14-43]
+            Identifier(SPANNER_SYS) [14-25]
+            Identifier(USER_SPLIT_POINTS) [26-43]
diff --git a/parser/testdata/spanner/guides/create-manage-views.spanner.sql b/parser/testdata/spanner/guides/create-manage-views.spanner.sql
new file mode 100644
index 0000000..77662ad
--- /dev/null
+++ b/parser/testdata/spanner/guides/create-manage-views.spanner.sql
@@ -0,0 +1,18 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/create-manage-views
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/create-manage-views#grante-revoke-access
+# id 6ed82bd51f5f: GRANT/REVOKE for fine-grained access control (create-manage-views)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:37]
+GRANT SELECT ON VIEW SingerNames TO ROLE Analyst;
+
+# https://docs.cloud.google.com/spanner/docs/create-manage-views#grante-revoke-access
+# id 6591668a948f: GRANT/REVOKE for fine-grained access control (create-manage-views)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:40]
+REVOKE SELECT ON VIEW SingerNames FROM ROLE Analyst;
diff --git a/parser/testdata/spanner/guides/create-manage-views.test b/parser/testdata/spanner/guides/create-manage-views.test
new file mode 100644
index 0000000..3079ec9
--- /dev/null
+++ b/parser/testdata/spanner/guides/create-manage-views.test
@@ -0,0 +1,228 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/create-manage-views
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/create-manage-views#create_a_view
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Singers (
+  SingerId   INT64 NOT NULL,
+  FirstName  STRING(1024),
+  LastName   STRING(1024),
+  SingerInfo BYTES(MAX)
+) PRIMARY KEY (SingerId)
+--
+CreateTableStatement [0-154]
+  PathExpression [13-20]
+    Identifier(Singers) [13-20]
+  TableElementList [21-131]
+    ColumnDefinition [25-50]
+      Identifier(SingerId) [25-33]
+      SimpleColumnSchema [36-50]
+        PathExpression [36-41]
+          Identifier(INT64) [36-41]
+        ColumnAttributeList [42-50]
+          NotNullColumnAttribute [42-50]
+    ColumnDefinition [54-77]
+      Identifier(FirstName) [54-63]
+      SimpleColumnSchema [65-77]
+        PathExpression [65-71]
+          Identifier(STRING) [65-71]
+        TypeParameterList [71-76]
+          IntLiteral(1024) [72-76]
+    ColumnDefinition [81-104]
+      Identifier(LastName) [81-89]
+      SimpleColumnSchema [92-104]
+        PathExpression [92-98]
+          Identifier(STRING) [92-98]
+        TypeParameterList [98-103]
+          IntLiteral(1024) [99-103]
+    ColumnDefinition [108-129]
+      Identifier(SingerInfo) [108-118]
+      SimpleColumnSchema [119-129]
+        PathExpression [119-124]
+          Identifier(BYTES) [119-124]
+        TypeParameterList [124-128]
+          MaxLiteral() [125-128]
+  SpannerTableOptions [132-154]
+    PrimaryKey(ENFORCED) [132-154]
+      PrimaryKeyElementList [144-154]
+        PrimaryKeyElement(ASC) [145-153]
+          Identifier(SingerId) [145-153]
+==
+# https://docs.cloud.google.com/spanner/docs/create-manage-views#create_a_view
+CREATE VIEW SingerNames
+SQL SECURITY INVOKER
+AS SELECT
+   Singers.SingerId AS SingerId,
+   Singers.FirstName || ' ' || Singers.LastName AS Name
+FROM Singers
+--
+CreateViewStatement(SQL SECURITY INVOKER) [0-156]
+  PathExpression [12-23]
+    Identifier(SingerNames) [12-23]
+  Query [48-156]
+    Select [48-156]
+      SelectList [58-143]
+        SelectColumn [58-86]
+          PathExpression [58-74]
+            Identifier(Singers) [58-65]
+            Identifier(SingerId) [66-74]
+          Alias [75-86]
+            Identifier(SingerId) [78-86]
+        SelectColumn [91-143]
+          BinaryExpression(||) [91-135]
+            BinaryExpression(||) [91-115]
+              PathExpression [91-108]
+                Identifier(Singers) [91-98]
+                Identifier(FirstName) [99-108]
+              StringLiteral [112-115]
+                StringLiteralComponent(' ') [112-115]
+            PathExpression [119-135]
+              Identifier(Singers) [119-126]
+              Identifier(LastName) [127-135]
+          Alias [136-143]
+            Identifier(Name) [139-143]
+      FromClause [144-156]
+        TablePathExpression [149-156]
+          PathExpression [149-156]
+            Identifier(Singers) [149-156]
+==
+# https://docs.cloud.google.com/spanner/docs/create-manage-views#create-view-guidance
+CREATE OR REPLACE VIEW SingerNames
+SQL SECURITY INVOKER
+AS SELECT
+ CAST(Singers.SingerId AS INT64) AS SingerId,
+ CAST(Singers.FirstName AS STRING) || " " || CAST(Singers.LastName AS STRING) AS Name
+FROM Singers
+--
+CreateViewStatement(is_or_replace, SQL SECURITY INVOKER) [0-210]
+  PathExpression [23-34]
+    Identifier(SingerNames) [23-34]
+  Query [59-210]
+    Select [59-210]
+      SelectList [67-197]
+        SelectColumn [67-110]
+          CastExpression [67-98]
+            PathExpression [72-88]
+              Identifier(Singers) [72-79]
+              Identifier(SingerId) [80-88]
+            SimpleType [92-97]
+              PathExpression [92-97]
+                Identifier(INT64) [92-97]
+          Alias [99-110]
+            Identifier(SingerId) [102-110]
+        SelectColumn [113-197]
+          BinaryExpression(||) [113-189]
+            BinaryExpression(||) [113-153]
+              CastExpression [113-146]
+                PathExpression [118-135]
+                  Identifier(Singers) [118-125]
+                  Identifier(FirstName) [126-135]
+                SimpleType [139-145]
+                  PathExpression [139-145]
+                    Identifier(STRING) [139-145]
+              StringLiteral [150-153]
+                StringLiteralComponent(" ") [150-153]
+            CastExpression [157-189]
+              PathExpression [162-178]
+                Identifier(Singers) [162-169]
+                Identifier(LastName) [170-178]
+              SimpleType [182-188]
+                PathExpression [182-188]
+                  Identifier(STRING) [182-188]
+          Alias [190-197]
+            Identifier(Name) [193-197]
+      FromClause [198-210]
+        TablePathExpression [203-210]
+          PathExpression [203-210]
+            Identifier(Singers) [203-210]
+==
+# https://docs.cloud.google.com/spanner/docs/create-manage-views#query_an_invokers_rights_view
+SELECT COUNT(SingerID) as SingerCount
+FROM SingerNames
+--
+QueryStatement [0-54]
+  Query [0-54]
+    Select [0-54]
+      SelectList [7-37]
+        SelectColumn [7-37]
+          FunctionCall [7-22]
+            PathExpression [7-12]
+              Identifier(COUNT) [7-12]
+            PathExpression [13-21]
+              Identifier(SingerID) [13-21]
+          Alias [23-37]
+            Identifier(SingerCount) [26-37]
+      FromClause [38-54]
+        TablePathExpression [43-54]
+          PathExpression [43-54]
+            Identifier(SingerNames) [43-54]
+==
+# https://docs.cloud.google.com/spanner/docs/create-manage-views#replace-view
+CREATE OR REPLACE VIEW SingerNames
+SQL SECURITY DEFINER
+AS SELECT
+   Singers.SingerId AS SingerId,
+   Singers.FirstName || ' ' || Singers.LastName AS Name
+FROM Singers
+--
+CreateViewStatement(is_or_replace, SQL SECURITY DEFINER) [0-167]
+  PathExpression [23-34]
+    Identifier(SingerNames) [23-34]
+  Query [59-167]
+    Select [59-167]
+      SelectList [69-154]
+        SelectColumn [69-97]
+          PathExpression [69-85]
+            Identifier(Singers) [69-76]
+            Identifier(SingerId) [77-85]
+          Alias [86-97]
+            Identifier(SingerId) [89-97]
+        SelectColumn [102-154]
+          BinaryExpression(||) [102-146]
+            BinaryExpression(||) [102-126]
+              PathExpression [102-119]
+                Identifier(Singers) [102-109]
+                Identifier(FirstName) [110-119]
+              StringLiteral [123-126]
+                StringLiteralComponent(' ') [123-126]
+            PathExpression [130-146]
+              Identifier(Singers) [130-137]
+              Identifier(LastName) [138-146]
+          Alias [147-154]
+            Identifier(Name) [150-154]
+      FromClause [155-167]
+        TablePathExpression [160-167]
+          PathExpression [160-167]
+            Identifier(Singers) [160-167]
+==
+# https://docs.cloud.google.com/spanner/docs/create-manage-views#delete-view
+DROP VIEW SingerNames
+--
+DropStatement VIEW [0-21]
+  PathExpression [10-21]
+    Identifier(SingerNames) [10-21]
+==
+# https://docs.cloud.google.com/spanner/docs/create-manage-views#get-view-info
+SELECT *
+  FROM INFORMATION_SCHEMA.VIEWS
+  WHERE TABLE_NAME = 'ProductSoldLastWeek'
+--
+QueryStatement [0-83]
+  Query [0-83]
+    Select [0-83]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [11-40]
+        TablePathExpression [16-40]
+          PathExpression [16-40]
+            Identifier(INFORMATION_SCHEMA) [16-34]
+            Identifier(VIEWS) [35-40]
+      WhereClause [43-83]
+        BinaryExpression(=) [49-83]
+          PathExpression [49-59]
+            Identifier(TABLE_NAME) [49-59]
+          StringLiteral [62-83]
+            StringLiteralComponent('ProductSoldLastWeek') [62-83]
diff --git a/parser/testdata/spanner/guides/create-query-database-console.test b/parser/testdata/spanner/guides/create-query-database-console.test
new file mode 100644
index 0000000..cae4c04
--- /dev/null
+++ b/parser/testdata/spanner/guides/create-query-database-console.test
@@ -0,0 +1,284 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/create-query-database-console
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/create-query-database-console#create-schema
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Singers (
+  SingerId   INT64 NOT NULL,
+  FirstName  STRING(1024),
+  LastName   STRING(1024),
+  SingerInfo BYTES(MAX),
+  BirthDate  DATE
+) PRIMARY KEY(SingerId)
+--
+CreateTableStatement [0-172]
+  PathExpression [13-20]
+    Identifier(Singers) [13-20]
+  TableElementList [21-150]
+    ColumnDefinition [25-50]
+      Identifier(SingerId) [25-33]
+      SimpleColumnSchema [36-50]
+        PathExpression [36-41]
+          Identifier(INT64) [36-41]
+        ColumnAttributeList [42-50]
+          NotNullColumnAttribute [42-50]
+    ColumnDefinition [54-77]
+      Identifier(FirstName) [54-63]
+      SimpleColumnSchema [65-77]
+        PathExpression [65-71]
+          Identifier(STRING) [65-71]
+        TypeParameterList [71-76]
+          IntLiteral(1024) [72-76]
+    ColumnDefinition [81-104]
+      Identifier(LastName) [81-89]
+      SimpleColumnSchema [92-104]
+        PathExpression [92-98]
+          Identifier(STRING) [92-98]
+        TypeParameterList [98-103]
+          IntLiteral(1024) [99-103]
+    ColumnDefinition [108-129]
+      Identifier(SingerInfo) [108-118]
+      SimpleColumnSchema [119-129]
+        PathExpression [119-124]
+          Identifier(BYTES) [119-124]
+        TypeParameterList [124-128]
+          MaxLiteral() [125-128]
+    ColumnDefinition [133-148]
+      Identifier(BirthDate) [133-142]
+      SimpleColumnSchema [144-148]
+        PathExpression [144-148]
+          Identifier(DATE) [144-148]
+  SpannerTableOptions [151-172]
+    PrimaryKey(ENFORCED) [151-172]
+      PrimaryKeyElementList [162-172]
+        PrimaryKeyElement(ASC) [163-171]
+          Identifier(SingerId) [163-171]
+==
+# https://docs.cloud.google.com/spanner/docs/create-query-database-console#insert_data
+-- Add new values in the VALUES clause in order of the column list.
+  -- Each value must be type compatible with its associated column.
+INSERT INTO
+  Singers (SingerId,
+    BirthDate,
+    FirstName,
+    LastName,
+    SingerInfo)
+VALUES
+  (1, -- type: INT64
+    NULL, -- type: DATE
+    'Marc', -- type: STRING(1024)
+    'Richards', -- type: STRING(1024)
+    NULL -- type: BYTES(MAX)
+    )
+THEN RETURN
+  SingerId,
+  FirstName,
+  LastName,
+  SingerInfo,
+  BirthDate
+--
+InsertStatement [136-462]
+  PathExpression [150-157]
+    Identifier(Singers) [150-157]
+  ColumnList [158-228]
+    Identifier(SingerId) [159-167]
+    Identifier(BirthDate) [173-182]
+    Identifier(FirstName) [188-197]
+    Identifier(LastName) [203-211]
+    Identifier(SingerInfo) [217-227]
+  InsertValuesRowList [229-387]
+    InsertValuesRow [238-387]
+      IntLiteral(1) [239-240]
+      NullLiteral(NULL) [261-265]
+      StringLiteral [285-291]
+        StringLiteralComponent('Marc') [285-291]
+      StringLiteral [319-329]
+        StringLiteralComponent('Richards') [319-329]
+      NullLiteral(NULL) [357-361]
+  ReturningClause [388-462]
+    SelectList [402-462]
+      SelectColumn [402-410]
+        PathExpression [402-410]
+          Identifier(SingerId) [402-410]
+      SelectColumn [414-423]
+        PathExpression [414-423]
+          Identifier(FirstName) [414-423]
+      SelectColumn [427-435]
+        PathExpression [427-435]
+          Identifier(LastName) [427-435]
+      SelectColumn [439-449]
+        PathExpression [439-449]
+          Identifier(SingerInfo) [439-449]
+      SelectColumn [453-462]
+        PathExpression [453-462]
+          Identifier(BirthDate) [453-462]
+==
+# https://docs.cloud.google.com/spanner/docs/create-query-database-console#insert_data
+-- Add new values in the VALUES clause in order of the column list.
+  -- Each value must be type compatible with its associated column.
+INSERT INTO
+  Singers (SingerId,
+    BirthDate,
+    FirstName,
+    LastName,
+    SingerInfo)
+VALUES
+  (3, -- type: INT64
+    NULL, -- type: DATE
+    'Kena', -- type: STRING(1024)
+    '', -- type: STRING(1024)
+    NULL -- type: BYTES(MAX)
+    )
+--
+InsertStatement [136-379]
+  PathExpression [150-157]
+    Identifier(Singers) [150-157]
+  ColumnList [158-228]
+    Identifier(SingerId) [159-167]
+    Identifier(BirthDate) [173-182]
+    Identifier(FirstName) [188-197]
+    Identifier(LastName) [203-211]
+    Identifier(SingerInfo) [217-227]
+  InsertValuesRowList [229-379]
+    InsertValuesRow [238-379]
+      IntLiteral(3) [239-240]
+      NullLiteral(NULL) [261-265]
+      StringLiteral [285-291]
+        StringLiteralComponent('Kena') [285-291]
+      StringLiteral [319-321]
+        StringLiteralComponent('') [319-321]
+      NullLiteral(NULL) [349-353]
+==
+# https://docs.cloud.google.com/spanner/docs/create-query-database-console#insert_data
+-- Change values in the WHERE condition to match the inserted row.
+SELECT
+  *
+FROM
+  Singers
+WHERE
+  SingerId=3
+--
+QueryStatement [67-111]
+  Query [67-111]
+    Select [67-111]
+      SelectList [76-77]
+        SelectColumn [76-77]
+          Star(*) [76-77]
+      FromClause [78-92]
+        TablePathExpression [85-92]
+          PathExpression [85-92]
+            Identifier(Singers) [85-92]
+      WhereClause [93-111]
+        BinaryExpression(=) [101-111]
+          PathExpression [101-109]
+            Identifier(SingerId) [101-109]
+          IntLiteral(3) [110-111]
+==
+# https://docs.cloud.google.com/spanner/docs/create-query-database-console#edit_data
+-- Change values in the SET clause to update the row where the WHERE condition is true.
+UPDATE
+  Singers
+SET
+  BirthDate='',
+  FirstName='Kena',
+  LastName='',
+  SingerInfo=''
+WHERE
+  SingerId=3
+--
+UpdateStatement [88-194]
+  PathExpression [97-104]
+    Identifier(Singers) [97-104]
+  UpdateItemList [111-175]
+    UpdateItem [111-123]
+      UpdateSetValue [111-123]
+        PathExpression [111-120]
+          Identifier(BirthDate) [111-120]
+        StringLiteral [121-123]
+          StringLiteralComponent('') [121-123]
+    UpdateItem [127-143]
+      UpdateSetValue [127-143]
+        PathExpression [127-136]
+          Identifier(FirstName) [127-136]
+        StringLiteral [137-143]
+          StringLiteralComponent('Kena') [137-143]
+    UpdateItem [147-158]
+      UpdateSetValue [147-158]
+        PathExpression [147-155]
+          Identifier(LastName) [147-155]
+        StringLiteral [156-158]
+          StringLiteralComponent('') [156-158]
+    UpdateItem [162-175]
+      UpdateSetValue [162-175]
+        PathExpression [162-172]
+          Identifier(SingerInfo) [162-172]
+        StringLiteral [173-175]
+          StringLiteralComponent('') [173-175]
+  BinaryExpression(=) [184-194]
+    PathExpression [184-192]
+      Identifier(SingerId) [184-192]
+    IntLiteral(3) [193-194]
+==
+# https://docs.cloud.google.com/spanner/docs/create-query-database-console#edit_data
+SELECT
+  *
+FROM
+  Singers
+WHERE
+  SingerId=3
+--
+QueryStatement [0-44]
+  Query [0-44]
+    Select [0-44]
+      SelectList [9-10]
+        SelectColumn [9-10]
+          Star(*) [9-10]
+      FromClause [11-25]
+        TablePathExpression [18-25]
+          PathExpression [18-25]
+            Identifier(Singers) [18-25]
+      WhereClause [26-44]
+        BinaryExpression(=) [34-44]
+          PathExpression [34-42]
+            Identifier(SingerId) [34-42]
+          IntLiteral(3) [43-44]
+==
+# https://docs.cloud.google.com/spanner/docs/create-query-database-console#edit_data
+-- Change values in the SET clause to update the row where the WHERE condition is true.
+UPDATE
+  Singers
+SET
+  BirthDate='1961-04-01'
+WHERE
+  SingerId=3
+--
+UpdateStatement [88-152]
+  PathExpression [97-104]
+    Identifier(Singers) [97-104]
+  UpdateItemList [111-133]
+    UpdateItem [111-133]
+      UpdateSetValue [111-133]
+        PathExpression [111-120]
+          Identifier(BirthDate) [111-120]
+        StringLiteral [121-133]
+          StringLiteralComponent('1961-04-01') [121-133]
+  BinaryExpression(=) [142-152]
+    PathExpression [142-150]
+      Identifier(SingerId) [142-150]
+    IntLiteral(3) [151-152]
+==
+# https://docs.cloud.google.com/spanner/docs/create-query-database-console#run-query
+SELECT * FROM Singers
+--
+QueryStatement [0-21]
+  Query [0-21]
+    Select [0-21]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-21]
+        TablePathExpression [14-21]
+          PathExpression [14-21]
+            Identifier(Singers) [14-21]
diff --git a/parser/testdata/spanner/guides/databoost.databoost-run-queries.test b/parser/testdata/spanner/guides/databoost.databoost-run-queries.test
new file mode 100644
index 0000000..16c3c35
--- /dev/null
+++ b/parser/testdata/spanner/guides/databoost.databoost-run-queries.test
@@ -0,0 +1,124 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/databoost/databoost-run-queries
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/databoost/databoost-run-queries#run-query-from-spanner
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT c.customer_id, c.name, rq.first_order_date
+FROM mydataset.customers AS c
+LEFT OUTER JOIN EXTERNAL_QUERY(
+  'my-project.us.example-db',
+  '''SELECT customer_id, MIN(order_date) AS first_order_date
+  FROM orders
+  GROUP BY customer_id''') AS rq
+  ON rq.customer_id = c.customer_id
+GROUP BY c.customer_id, c.name, rq.first_order_date
+--
+QueryStatement [0-337]
+  Query [0-337]
+    Select [0-337]
+      SelectList [7-49]
+        SelectColumn [7-20]
+          PathExpression [7-20]
+            Identifier(c) [7-8]
+            Identifier(customer_id) [9-20]
+        SelectColumn [22-28]
+          PathExpression [22-28]
+            Identifier(c) [22-23]
+            Identifier(name) [24-28]
+        SelectColumn [30-49]
+          PathExpression [30-49]
+            Identifier(rq) [30-32]
+            Identifier(first_order_date) [33-49]
+      FromClause [50-285]
+        Join(LEFT) [55-285]
+          TablePathExpression [55-79]
+            PathExpression [55-74]
+              Identifier(mydataset) [55-64]
+              Identifier(customers) [65-74]
+            Alias [75-79]
+              Identifier(c) [78-79]
+          Location [80-95]
+          TVF [96-249]
+            PathExpression [96-110]
+              Identifier(EXTERNAL_QUERY) [96-110]
+            TVFArgument [114-140]
+              StringLiteral [114-140]
+                StringLiteralComponent('my-project.us.example-db') [114-140]
+            TVFArgument [144-242]
+              StringLiteral [144-242]
+                StringLiteralComponent('''SELECT customer_id, MIN(order_date) AS first_order_date
+  FROM orders
+  GROUP BY customer_id''') [144-242]
+            Alias [244-249]
+              Identifier(rq) [247-249]
+          OnClause [252-285]
+            BinaryExpression(=) [255-285]
+              PathExpression [255-269]
+                Identifier(rq) [255-257]
+                Identifier(customer_id) [258-269]
+              PathExpression [272-285]
+                Identifier(c) [272-273]
+                Identifier(customer_id) [274-285]
+      GroupBy [286-337]
+        GroupingItem [295-308]
+          PathExpression [295-308]
+            Identifier(c) [295-296]
+            Identifier(customer_id) [297-308]
+        GroupingItem [310-316]
+          PathExpression [310-316]
+            Identifier(c) [310-311]
+            Identifier(name) [312-316]
+        GroupingItem [318-337]
+          PathExpression [318-337]
+            Identifier(rq) [318-320]
+            Identifier(first_order_date) [321-337]
+==
+# https://docs.cloud.google.com/spanner/docs/databoost/databoost-run-queries#create_a_non-incremental_materialized_view_based_on_tables_from_an_external_dataset
+/*
+  You must create the spanner_external_dataset with a CLOUD_RESOURCE connection.
+*/
+CREATE MATERIALIZED VIEW sample_dataset.sample_spanner_mv
+  OPTIONS (
+      enable_refresh = true, refresh_interval_minutes = 60,
+      max_staleness = INTERVAL "24" HOUR,
+        allow_non_incremental_definition = true)
+AS
+  SELECT COUNT(*) cnt FROM spanner_external_dataset.spanner_table
+--
+CreateMaterializedViewStatement [87-376]
+  PathExpression [112-144]
+    Identifier(sample_dataset) [112-126]
+    Identifier(sample_spanner_mv) [127-144]
+  OptionsList [155-307]
+    OptionsEntry [163-184]
+      Identifier(enable_refresh) [163-177]
+      BooleanLiteral(true) [180-184]
+    OptionsEntry [186-215]
+      Identifier(refresh_interval_minutes) [186-210]
+      IntLiteral(60) [213-215]
+    OptionsEntry [223-257]
+      Identifier(max_staleness) [223-236]
+      IntervalExpr [239-257]
+        StringLiteral [248-252]
+          StringLiteralComponent("24") [248-252]
+        Identifier(HOUR) [253-257]
+    OptionsEntry [267-306]
+      Identifier(allow_non_incremental_definition) [267-299]
+      BooleanLiteral(true) [302-306]
+  Query [313-376]
+    Select [313-376]
+      SelectList [320-332]
+        SelectColumn [320-332]
+          FunctionCall [320-328]
+            PathExpression [320-325]
+              Identifier(COUNT) [320-325]
+            Star(*) [326-327]
+          Alias [329-332]
+            Identifier(cnt) [329-332]
+      FromClause [333-376]
+        TablePathExpression [338-376]
+          PathExpression [338-376]
+            Identifier(spanner_external_dataset) [338-362]
+            Identifier(spanner_table) [363-376]
diff --git a/parser/testdata/spanner/guides/dml-best-practices.test b/parser/testdata/spanner/guides/dml-best-practices.test
new file mode 100644
index 0000000..99a63a1
--- /dev/null
+++ b/parser/testdata/spanner/guides/dml-best-practices.test
@@ -0,0 +1,180 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/dml-best-practices
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/dml-best-practices#locking
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+-- ANTI-PATTERN: SENDING AN UPDATE WITHOUT THE PRIMARY KEY COLUMN
+-- IN THE WHERE CLAUSE
+
+UPDATE Singers SET FirstName = "Marcel"
+WHERE FirstName = "Marc" AND LastName = "Richards"
+--
+UpdateStatement [90-180]
+  PathExpression [97-104]
+    Identifier(Singers) [97-104]
+  UpdateItemList [109-129]
+    UpdateItem [109-129]
+      UpdateSetValue [109-129]
+        PathExpression [109-118]
+          Identifier(FirstName) [109-118]
+        StringLiteral [121-129]
+          StringLiteralComponent("Marcel") [121-129]
+  AndExpr [136-180]
+    BinaryExpression(=) [136-154]
+      PathExpression [136-145]
+        Identifier(FirstName) [136-145]
+      StringLiteral [148-154]
+        StringLiteralComponent("Marc") [148-154]
+    BinaryExpression(=) [159-180]
+      PathExpression [159-167]
+        Identifier(LastName) [159-167]
+      StringLiteral [170-180]
+        StringLiteralComponent("Richards") [170-180]
+==
+# https://docs.cloud.google.com/spanner/docs/dml-best-practices#locking
+-- ANTI-PATTERN: SENDING AN UPDATE THAT MUST SCAN THE ENTIRE TABLE
+
+UPDATE Singers SET FirstName = "Marcel"
+WHERE FirstName = "Marc" AND LastName = "Richards"
+--
+UpdateStatement [68-158]
+  PathExpression [75-82]
+    Identifier(Singers) [75-82]
+  UpdateItemList [87-107]
+    UpdateItem [87-107]
+      UpdateSetValue [87-107]
+        PathExpression [87-96]
+          Identifier(FirstName) [87-96]
+        StringLiteral [99-107]
+          StringLiteralComponent("Marcel") [99-107]
+  AndExpr [114-158]
+    BinaryExpression(=) [114-132]
+      PathExpression [114-123]
+        Identifier(FirstName) [114-123]
+      StringLiteral [126-132]
+        StringLiteralComponent("Marc") [126-132]
+    BinaryExpression(=) [137-158]
+      PathExpression [137-145]
+        Identifier(LastName) [137-145]
+      StringLiteral [148-158]
+        StringLiteralComponent("Richards") [148-158]
+==
+# https://docs.cloud.google.com/spanner/docs/dml-best-practices#batch-dml
+INSERT INTO Albums (SingerId, AlbumId, AlbumTitle) VALUES (@Singer, @Album, @Title)
+--
+InsertStatement [0-83]
+  PathExpression [12-18]
+    Identifier(Albums) [12-18]
+  ColumnList [19-50]
+    Identifier(SingerId) [20-28]
+    Identifier(AlbumId) [30-37]
+    Identifier(AlbumTitle) [39-49]
+  InsertValuesRowList [51-83]
+    InsertValuesRow [58-83]
+      ParameterExpr [59-66]
+        Identifier(Singer) [60-66]
+      ParameterExpr [68-74]
+        Identifier(Album) [69-74]
+      ParameterExpr [76-82]
+        Identifier(Title) [77-82]
+==
+# https://docs.cloud.google.com/spanner/docs/dml-best-practices#batch-dml
+INSERT INTO Singers (SingerId, Name) VALUES(1, "John Doe")
+--
+InsertStatement [0-58]
+  PathExpression [12-19]
+    Identifier(Singers) [12-19]
+  ColumnList [20-36]
+    Identifier(SingerId) [21-29]
+    Identifier(Name) [31-35]
+  InsertValuesRowList [37-58]
+    InsertValuesRow [43-58]
+      IntLiteral(1) [44-45]
+      StringLiteral [47-57]
+        StringLiteralComponent("John Doe") [47-57]
+==
+# https://docs.cloud.google.com/spanner/docs/dml-best-practices#batch-dml
+INSERT INTO Singers (SingerId, Name) VALUES(2, "Marcel Richards")
+--
+InsertStatement [0-65]
+  PathExpression [12-19]
+    Identifier(Singers) [12-19]
+  ColumnList [20-36]
+    Identifier(SingerId) [21-29]
+    Identifier(Name) [31-35]
+  InsertValuesRowList [37-65]
+    InsertValuesRow [43-65]
+      IntLiteral(2) [44-45]
+      StringLiteral [47-64]
+        StringLiteralComponent("Marcel Richards") [47-64]
+==
+# https://docs.cloud.google.com/spanner/docs/dml-best-practices#batch-dml
+INSERT INTO Albums(SingerId, AlbumId, AlbumTitle) VALUES (1, 10001, "Album 1")
+--
+InsertStatement [0-78]
+  PathExpression [12-18]
+    Identifier(Albums) [12-18]
+  ColumnList [18-49]
+    Identifier(SingerId) [19-27]
+    Identifier(AlbumId) [29-36]
+    Identifier(AlbumTitle) [38-48]
+  InsertValuesRowList [50-78]
+    InsertValuesRow [57-78]
+      IntLiteral(1) [58-59]
+      IntLiteral(10001) [61-66]
+      StringLiteral [68-77]
+        StringLiteralComponent("Album 1") [68-77]
+==
+# https://docs.cloud.google.com/spanner/docs/dml-best-practices#batch-dml
+INSERT INTO Albums(SingerId, AlbumId, AlbumTitle) VALUES (1, 10002, "Album 2")
+--
+InsertStatement [0-78]
+  PathExpression [12-18]
+    Identifier(Albums) [12-18]
+  ColumnList [18-49]
+    Identifier(SingerId) [19-27]
+    Identifier(AlbumId) [29-36]
+    Identifier(AlbumTitle) [38-48]
+  InsertValuesRowList [50-78]
+    InsertValuesRow [57-78]
+      IntLiteral(1) [58-59]
+      IntLiteral(10002) [61-66]
+      StringLiteral [68-77]
+        StringLiteralComponent("Album 2") [68-77]
+==
+# https://docs.cloud.google.com/spanner/docs/dml-best-practices#batch-dml
+INSERT INTO Albums(SingerId, AlbumId, AlbumTitle) VALUES (2, 10001, "Album 1")
+--
+InsertStatement [0-78]
+  PathExpression [12-18]
+    Identifier(Albums) [12-18]
+  ColumnList [18-49]
+    Identifier(SingerId) [19-27]
+    Identifier(AlbumId) [29-36]
+    Identifier(AlbumTitle) [38-48]
+  InsertValuesRowList [50-78]
+    InsertValuesRow [57-78]
+      IntLiteral(2) [58-59]
+      IntLiteral(10001) [61-66]
+      StringLiteral [68-77]
+        StringLiteralComponent("Album 1") [68-77]
+==
+# https://docs.cloud.google.com/spanner/docs/dml-best-practices#batch-dml
+UPDATE Accounts SET Balance = 100 WHERE AccountId = @AccountId
+--
+UpdateStatement [0-62]
+  PathExpression [7-15]
+    Identifier(Accounts) [7-15]
+  UpdateItemList [20-33]
+    UpdateItem [20-33]
+      UpdateSetValue [20-33]
+        PathExpression [20-27]
+          Identifier(Balance) [20-27]
+        IntLiteral(100) [30-33]
+  BinaryExpression(=) [40-62]
+    PathExpression [40-49]
+      Identifier(AccountId) [40-49]
+    ParameterExpr [52-62]
+      Identifier(AccountId) [53-62]
diff --git a/parser/testdata/spanner/guides/fgac-about.spanner.sql b/parser/testdata/spanner/guides/fgac-about.spanner.sql
new file mode 100644
index 0000000..1eefc39
--- /dev/null
+++ b/parser/testdata/spanner/guides/fgac-about.spanner.sql
@@ -0,0 +1,18 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/fgac-about
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/fgac-about#
+# id 83fdbb648537: CREATE ROLE (fgac-about)
+# execute_query: INVALID_ARGUMENT: ROLE is not a supported object type [at 1:8]
+CREATE ROLE hr_rep;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-about#role_hierarchy
+# id 2a3d1404fb33: GRANT/REVOKE for fine-grained access control (fgac-about)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:36]
+GRANT SELECT ON TABLE employees TO ROLE pii_access;
diff --git a/parser/testdata/spanner/guides/fgac-privileges.spanner.sql b/parser/testdata/spanner/guides/fgac-privileges.spanner.sql
new file mode 100644
index 0000000..256bfd1
--- /dev/null
+++ b/parser/testdata/spanner/guides/fgac-privileges.spanner.sql
@@ -0,0 +1,73 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/fgac-privileges
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/fgac-privileges#examples_for_using_grant_select
+# id 42e822b83cd2: GRANT/REVOKE for fine-grained access control (fgac-privileges)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:36]
+GRANT SELECT ON TABLE employees TO ROLE hr_director;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-privileges#examples_for_using_grant_select
+# id 81ae2c0b5695: GRANT/REVOKE for fine-grained access control (fgac-privileges)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "." or keyword TO but got "," [at 1:32]
+GRANT SELECT ON TABLE customers, orders, items TO ROLE account_mgr;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-privileges#examples_for_using_grant_select
+# id adfe06052399: GRANT/REVOKE for fine-grained access control (fgac-privileges)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:81]
+GRANT SELECT(name, level, cost_center, location, manager) ON TABLE employees TO ROLE hr_manager;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-privileges#examples_for_using_grant_select
+# id d9c104a7af3a: GRANT/REVOKE for fine-grained access control (fgac-privileges)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "." or keyword TO but got "," [at 1:54]
+GRANT SELECT(name, address, phone) ON TABLE employees, contractors TO ROLE hr_rep;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-privileges#examples_for_using_grant_select
+# id 2b4080c56596: GRANT/REVOKE for fine-grained access control (fgac-privileges)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:37]
+GRANT SELECT ON VIEW orders_view TO ROLE hr_manager;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-privileges#examples_for_using_grant_select
+# id 871554001108: GRANT/REVOKE for fine-grained access control (fgac-privileges)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:53]
+GRANT SELECT ON CHANGE STREAM ordersChangeStream TO ROLE hr_analyst;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-privileges#examples_for_using_grant_select
+# id fb67d4171d70: GRANT/REVOKE for fine-grained access control (fgac-privileges)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:43]
+GRANT SELECT ON SEQUENCE sequence_name TO ROLE role_name;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-privileges#examples_for_using_grant_insert
+# id 8f3a85a82a45: GRANT/REVOKE for fine-grained access control (fgac-privileges)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "." or keyword TO but got "," [at 1:32]
+GRANT INSERT ON TABLE employees, contractors TO ROLE hr_manager;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-privileges#examples_for_using_grant_insert
+# id 029cabe80e58: GRANT/REVOKE for fine-grained access control (fgac-privileges)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:58]
+GRANT INSERT(name, address, phone) ON TABLE employees TO ROLE hr_rep;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-privileges#examples_for_using_grant_update
+# id f0715dd33a8c: GRANT/REVOKE for fine-grained access control (fgac-privileges)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "." or keyword TO but got "," [at 1:32]
+GRANT UPDATE ON TABLE employees, contractors TO ROLE hr_manager;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-privileges#examples_for_using_grant_update
+# id 1ea527a72cbb: GRANT/REVOKE for fine-grained access control (fgac-privileges)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:58]
+GRANT UPDATE(name, address, phone) ON TABLE employees TO ROLE hr_rep;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-privileges#example_for_using_grant_delete
+# id 4808623c5c48: GRANT/REVOKE for fine-grained access control (fgac-privileges)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "." or keyword TO but got "," [at 1:32]
+GRANT DELETE ON TABLE employees, contractors TO ROLE hr_admin;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-privileges#example_for_using_grant_execute
+# id f64849984457: GRANT/REVOKE for fine-grained access control (fgac-privileges)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:58]
+GRANT EXECUTE ON TABLE FUNCTION READ_my_change_stream TO ROLE hr_analyst;
diff --git a/parser/testdata/spanner/guides/fgac-privileges.test b/parser/testdata/spanner/guides/fgac-privileges.test
new file mode 100644
index 0000000..7084b4e
--- /dev/null
+++ b/parser/testdata/spanner/guides/fgac-privileges.test
@@ -0,0 +1,58 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/fgac-privileges
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/fgac-privileges#select
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE VIEW SingerNames SQL SECURITY INVOKER AS
+SELECT Singers.SingerId, Singers.FirstName, Singers.LastName FROM Singers
+--
+CreateViewStatement(SQL SECURITY INVOKER) [0-121]
+  PathExpression [12-23]
+    Identifier(SingerNames) [12-23]
+  Query [48-121]
+    Select [48-121]
+      SelectList [55-108]
+        SelectColumn [55-71]
+          PathExpression [55-71]
+            Identifier(Singers) [55-62]
+            Identifier(SingerId) [63-71]
+        SelectColumn [73-90]
+          PathExpression [73-90]
+            Identifier(Singers) [73-80]
+            Identifier(FirstName) [81-90]
+        SelectColumn [92-108]
+          PathExpression [92-108]
+            Identifier(Singers) [92-99]
+            Identifier(LastName) [100-108]
+      FromClause [109-121]
+        TablePathExpression [114-121]
+          PathExpression [114-121]
+            Identifier(Singers) [114-121]
+==
+# https://docs.cloud.google.com/spanner/docs/fgac-privileges#select
+CREATE VIEW AlbumsBudget SQL SECURITY DEFINER AS
+SELECT Albums.Id, Albums.AlbumTitle, MarketingBudget FROM Albums
+--
+CreateViewStatement(SQL SECURITY DEFINER) [0-113]
+  PathExpression [12-24]
+    Identifier(AlbumsBudget) [12-24]
+  Query [49-113]
+    Select [49-113]
+      SelectList [56-101]
+        SelectColumn [56-65]
+          PathExpression [56-65]
+            Identifier(Albums) [56-62]
+            Identifier(Id) [63-65]
+        SelectColumn [67-84]
+          PathExpression [67-84]
+            Identifier(Albums) [67-73]
+            Identifier(AlbumTitle) [74-84]
+        SelectColumn [86-101]
+          PathExpression [86-101]
+            Identifier(MarketingBudget) [86-101]
+      FromClause [102-113]
+        TablePathExpression [107-113]
+          PathExpression [107-113]
+            Identifier(Albums) [107-113]
diff --git a/parser/testdata/spanner/guides/fgac-queues.spanner.sql b/parser/testdata/spanner/guides/fgac-queues.spanner.sql
new file mode 100644
index 0000000..a11a725
--- /dev/null
+++ b/parser/testdata/spanner/guides/fgac-queues.spanner.sql
@@ -0,0 +1,51 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/fgac-queues
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/fgac-queues#example_configure_producer_and_consumer_roles
+# id 5b54ac5fbc37: CREATE ROLE (fgac-queues)
+# execute_query: INVALID_ARGUMENT: ROLE is not a supported object type [at 2:8]
+-- Create producer role and grant send permissions
+CREATE ROLE queue_producer;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-queues#example_configure_producer_and_consumer_roles
+# id 3f61756abb45: GRANT/REVOKE for fine-grained access control (fgac-queues)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:38]
+GRANT INSERT ON QUEUE OrdersQueue TO ROLE queue_producer;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-queues#example_configure_producer_and_consumer_roles
+# id 1d9d096db2f8: CREATE ROLE (fgac-queues)
+# execute_query: INVALID_ARGUMENT: ROLE is not a supported object type [at 2:8]
+-- Create consumer role and grant receive, renew, and delete permissions
+CREATE ROLE queue_consumer;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-queues#example_configure_producer_and_consumer_roles
+# id 4836493a0b5e: GRANT/REVOKE for fine-grained access control (fgac-queues)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:56]
+GRANT EXECUTE ON TABLE FUNCTION RECEIVE_OrdersQueue TO ROLE queue_consumer;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-queues#example_configure_producer_and_consumer_roles
+# id 2e55ab4a6d03: GRANT/REVOKE for fine-grained access control (fgac-queues)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:59]
+GRANT EXECUTE ON TABLE FUNCTION RENEWLEASE_OrdersQueue TO ROLE queue_consumer;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-queues#example_configure_producer_and_consumer_roles
+# id 4f7c0784c8e3: GRANT/REVOKE for fine-grained access control (fgac-queues)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:38]
+GRANT DELETE ON QUEUE OrdersQueue TO ROLE queue_consumer;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-queues#example_configure_producer_and_consumer_roles
+# id fa3fd655dd8d: CREATE ROLE (fgac-queues)
+# execute_query: INVALID_ARGUMENT: ROLE is not a supported object type [at 2:8]
+-- Create reader role for inspection without consumer streaming permissions
+CREATE ROLE queue_reader;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-queues#example_configure_producer_and_consumer_roles
+# id 012cb76568a3: GRANT/REVOKE for fine-grained access control (fgac-queues)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:38]
+GRANT SELECT ON QUEUE OrdersQueue TO ROLE queue_reader;
diff --git a/parser/testdata/spanner/guides/fgac-system-roles.spanner.sql b/parser/testdata/spanner/guides/fgac-system-roles.spanner.sql
new file mode 100644
index 0000000..932c6a1
--- /dev/null
+++ b/parser/testdata/spanner/guides/fgac-system-roles.spanner.sql
@@ -0,0 +1,28 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/fgac-system-roles
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/fgac-system-roles#restrictions_on_system_roles
+# id 69358adb0d47: GRANT/REVOKE for fine-grained access control (fgac-system-roles)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected keyword ON but got identifier "pii_access" [at 1:12]
+GRANT ROLE pii_access TO ROLE spanner_info_reader;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-system-roles#restrictions_on_system_roles
+# id aaf1ea5ec0ab: GRANT/REVOKE for fine-grained access control (fgac-system-roles)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected keyword ON but got keyword PUBLIC [at 1:12]
+GRANT ROLE public TO ROLE pii_access;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-system-roles#restrictions_on_system_roles
+# id cf49207a4c42: GRANT/REVOKE for fine-grained access control (fgac-system-roles)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected keyword ON but got identifier "spanner_info_reader" [at 1:12]
+GRANT ROLE spanner_info_reader TO ROLE pii_access;
+
+# https://docs.cloud.google.com/spanner/docs/fgac-system-roles#restrictions_on_system_roles
+# id 6f87e7b906f6: GRANT/REVOKE for fine-grained access control (fgac-system-roles)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected keyword ON but got identifier "spanner_sys_reader" [at 1:12]
+GRANT ROLE spanner_sys_reader TO ROLE pii_access;
diff --git a/parser/testdata/spanner/guides/find-approximate-nearest-neighbors.test b/parser/testdata/spanner/guides/find-approximate-nearest-neighbors.test
new file mode 100644
index 0000000..1084d6e
--- /dev/null
+++ b/parser/testdata/spanner/guides/find-approximate-nearest-neighbors.test
@@ -0,0 +1,365 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/find-approximate-nearest-neighbors
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/find-approximate-nearest-neighbors#query-vector-embeddings
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Documents (
+  UserId       INT64 NOT NULL,
+  DocId        INT64 NOT NULL,
+  Author       STRING(1024),
+  DocContents  BYTES(MAX),
+  DocEmbedding ARRAY NOT NULL,
+  NullableDocEmbedding ARRAY,
+  WordCount    INT64
+) PRIMARY KEY (UserId, DocId)
+--
+CreateTableStatement [0-272]
+  PathExpression [13-22]
+    Identifier(Documents) [13-22]
+  TableElementList [23-244]
+    ColumnDefinition [27-54]
+      Identifier(UserId) [27-33]
+      SimpleColumnSchema [40-54]
+        PathExpression [40-45]
+          Identifier(INT64) [40-45]
+        ColumnAttributeList [46-54]
+          NotNullColumnAttribute [46-54]
+    ColumnDefinition [58-85]
+      Identifier(DocId) [58-63]
+      SimpleColumnSchema [71-85]
+        PathExpression [71-76]
+          Identifier(INT64) [71-76]
+        ColumnAttributeList [77-85]
+          NotNullColumnAttribute [77-85]
+    ColumnDefinition [89-114]
+      Identifier(Author) [89-95]
+      SimpleColumnSchema [102-114]
+        PathExpression [102-108]
+          Identifier(STRING) [102-108]
+        TypeParameterList [108-113]
+          IntLiteral(1024) [109-113]
+    ColumnDefinition [118-141]
+      Identifier(DocContents) [118-129]
+      SimpleColumnSchema [131-141]
+        PathExpression [131-136]
+          Identifier(BYTES) [131-136]
+        TypeParameterList [136-140]
+          MaxLiteral() [137-140]
+    ColumnDefinition [145-181]
+      Identifier(DocEmbedding) [145-157]
+      ArrayColumnSchema [158-181]
+        SimpleColumnSchema [164-171]
+          PathExpression [164-171]
+            Identifier(FLOAT32) [164-171]
+        ColumnAttributeList [173-181]
+          NotNullColumnAttribute [173-181]
+    ColumnDefinition [185-220]
+      Identifier(NullableDocEmbedding) [185-205]
+      ArrayColumnSchema [206-220]
+        SimpleColumnSchema [212-219]
+          PathExpression [212-219]
+            Identifier(FLOAT32) [212-219]
+    ColumnDefinition [224-242]
+      Identifier(WordCount) [224-233]
+      SimpleColumnSchema [237-242]
+        PathExpression [237-242]
+          Identifier(INT64) [237-242]
+  SpannerTableOptions [245-272]
+    PrimaryKey(ENFORCED) [245-272]
+      PrimaryKeyElementList [257-272]
+        PrimaryKeyElement(ASC) [258-264]
+          Identifier(UserId) [258-264]
+        PrimaryKeyElement(ASC) [266-271]
+          Identifier(DocId) [266-271]
+==
+# https://docs.cloud.google.com/spanner/docs/find-approximate-nearest-neighbors#query-vector-embeddings
+SELECT DocId
+FROM Documents
+WHERE WordCount > 1000
+ORDER BY APPROX_EUCLIDEAN_DISTANCE(
+  ARRAY[1.0, 2.0, 3.0], DocEmbedding,
+  options => JSON '{"num_leaves_to_search": 10}')
+LIMIT 100
+--
+QueryStatement [0-193]
+  Query [0-193]
+    Select [0-50]
+      SelectList [7-12]
+        SelectColumn [7-12]
+          PathExpression [7-12]
+            Identifier(DocId) [7-12]
+      FromClause [13-27]
+        TablePathExpression [18-27]
+          PathExpression [18-27]
+            Identifier(Documents) [18-27]
+      WhereClause [28-50]
+        BinaryExpression(>) [34-50]
+          PathExpression [34-43]
+            Identifier(WordCount) [34-43]
+          IntLiteral(1000) [46-50]
+    OrderBy [51-183]
+      OrderingExpression(ASC) [60-183]
+        FunctionCall [60-183]
+          PathExpression [60-85]
+            Identifier(APPROX_EUCLIDEAN_DISTANCE) [60-85]
+          ArrayConstructor [89-118]
+            ArrayType [89-103]
+              SimpleType [95-102]
+                PathExpression [95-102]
+                  Identifier(FLOAT32) [95-102]
+            FloatLiteral(1.0) [104-107]
+            FloatLiteral(2.0) [109-112]
+            FloatLiteral(3.0) [114-117]
+          PathExpression [120-132]
+            Identifier(DocEmbedding) [120-132]
+          NamedArgument [136-182]
+            Identifier(options) [136-143]
+            JSONLiteral [147-182]
+              StringLiteral [152-182]
+                StringLiteralComponent('{"num_leaves_to_search": 10}') [152-182]
+    LimitOffset [184-193]
+      Limit [184-193]
+        IntLiteral(100) [190-193]
+==
+# https://docs.cloud.google.com/spanner/docs/find-approximate-nearest-neighbors#query-vector-embeddings
+SELECT DocId
+FROM Documents
+WHERE WordCount > 1000
+ORDER BY APPROX_EUCLIDEAN_DISTANCE(
+  ARRAY[1.0, 2.0, 3.0], DocEmbedding,
+  options => JSON '{"pct_leaves_to_search": 10.0}')
+LIMIT 100
+--
+QueryStatement [0-195]
+  Query [0-195]
+    Select [0-50]
+      SelectList [7-12]
+        SelectColumn [7-12]
+          PathExpression [7-12]
+            Identifier(DocId) [7-12]
+      FromClause [13-27]
+        TablePathExpression [18-27]
+          PathExpression [18-27]
+            Identifier(Documents) [18-27]
+      WhereClause [28-50]
+        BinaryExpression(>) [34-50]
+          PathExpression [34-43]
+            Identifier(WordCount) [34-43]
+          IntLiteral(1000) [46-50]
+    OrderBy [51-185]
+      OrderingExpression(ASC) [60-185]
+        FunctionCall [60-185]
+          PathExpression [60-85]
+            Identifier(APPROX_EUCLIDEAN_DISTANCE) [60-85]
+          ArrayConstructor [89-118]
+            ArrayType [89-103]
+              SimpleType [95-102]
+                PathExpression [95-102]
+                  Identifier(FLOAT32) [95-102]
+            FloatLiteral(1.0) [104-107]
+            FloatLiteral(2.0) [109-112]
+            FloatLiteral(3.0) [114-117]
+          PathExpression [120-132]
+            Identifier(DocEmbedding) [120-132]
+          NamedArgument [136-184]
+            Identifier(options) [136-143]
+            JSONLiteral [147-184]
+              StringLiteral [152-184]
+                StringLiteralComponent('{"pct_leaves_to_search": 10.0}') [152-184]
+    LimitOffset [186-195]
+      Limit [186-195]
+        IntLiteral(100) [192-195]
+==
+# https://docs.cloud.google.com/spanner/docs/find-approximate-nearest-neighbors#query-vector-embeddings
+WITH emb AS (
+  SELECT DocEmbedding AS value
+  FROM Documents
+  WHERE UserId = 1 AND DocId = 1
+  LIMIT 1
+)
+SELECT DocId
+FROM Documents, emb
+ORDER BY APPROX_EUCLIDEAN_DISTANCE(
+  emb.value, DocEmbedding,
+  options => JSON '{"num_leaves_to_search": 10}')
+LIMIT 100
+--
+QueryStatement [0-262]
+  Query [0-262]
+    WithClause [0-106]
+      WithClauseEntry [5-106]
+        AliasedQuery [5-106]
+          Identifier(emb) [5-8]
+          Query [12-106]
+            Select [16-94]
+              SelectList [23-44]
+                SelectColumn [23-44]
+                  PathExpression [23-35]
+                    Identifier(DocEmbedding) [23-35]
+                  Alias [36-44]
+                    Identifier(value) [39-44]
+              FromClause [47-61]
+                TablePathExpression [52-61]
+                  PathExpression [52-61]
+                    Identifier(Documents) [52-61]
+              WhereClause [64-94]
+                AndExpr [70-94]
+                  BinaryExpression(=) [70-80]
+                    PathExpression [70-76]
+                      Identifier(UserId) [70-76]
+                    IntLiteral(1) [79-80]
+                  BinaryExpression(=) [85-94]
+                    PathExpression [85-90]
+                      Identifier(DocId) [85-90]
+                    IntLiteral(1) [93-94]
+            LimitOffset [97-104]
+              Limit [97-104]
+                IntLiteral(1) [103-104]
+    Select [107-139]
+      SelectList [114-119]
+        SelectColumn [114-119]
+          PathExpression [114-119]
+            Identifier(DocId) [114-119]
+      FromClause [120-139]
+        Join(COMMA) [125-139]
+          TablePathExpression [125-134]
+            PathExpression [125-134]
+              Identifier(Documents) [125-134]
+          Location [134-135]
+          TablePathExpression [136-139]
+            PathExpression [136-139]
+              Identifier(emb) [136-139]
+    OrderBy [140-252]
+      OrderingExpression(ASC) [149-252]
+        FunctionCall [149-252]
+          PathExpression [149-174]
+            Identifier(APPROX_EUCLIDEAN_DISTANCE) [149-174]
+          PathExpression [178-187]
+            Identifier(emb) [178-181]
+            Identifier(value) [182-187]
+          PathExpression [189-201]
+            Identifier(DocEmbedding) [189-201]
+          NamedArgument [205-251]
+            Identifier(options) [205-212]
+            JSONLiteral [216-251]
+              StringLiteral [221-251]
+                StringLiteralComponent('{"num_leaves_to_search": 10}') [221-251]
+    LimitOffset [253-262]
+      Limit [253-262]
+        IntLiteral(100) [259-262]
+==
+# https://docs.cloud.google.com/spanner/docs/find-approximate-nearest-neighbors#query-vector-embeddings
+SELECT DocId
+FROM Documents
+WHERE NullableDocEmbedding IS NOT NULL AND WordCount > 1000
+ORDER BY APPROX_EUCLIDEAN_DISTANCE(
+  ARRAY[1.0, 2.0, 3.0], NullableDocEmbedding,
+  options => JSON '{"num_leaves_to_search": 10}')
+LIMIT 100
+--
+QueryStatement [0-238]
+  Query [0-238]
+    Select [0-87]
+      SelectList [7-12]
+        SelectColumn [7-12]
+          PathExpression [7-12]
+            Identifier(DocId) [7-12]
+      FromClause [13-27]
+        TablePathExpression [18-27]
+          PathExpression [18-27]
+            Identifier(Documents) [18-27]
+      WhereClause [28-87]
+        AndExpr [34-87]
+          BinaryExpression(IS NOT) [34-66]
+            PathExpression [34-54]
+              Identifier(NullableDocEmbedding) [34-54]
+            NullLiteral(NULL) [62-66]
+          BinaryExpression(>) [71-87]
+            PathExpression [71-80]
+              Identifier(WordCount) [71-80]
+            IntLiteral(1000) [83-87]
+    OrderBy [88-228]
+      OrderingExpression(ASC) [97-228]
+        FunctionCall [97-228]
+          PathExpression [97-122]
+            Identifier(APPROX_EUCLIDEAN_DISTANCE) [97-122]
+          ArrayConstructor [126-155]
+            ArrayType [126-140]
+              SimpleType [132-139]
+                PathExpression [132-139]
+                  Identifier(FLOAT32) [132-139]
+            FloatLiteral(1.0) [141-144]
+            FloatLiteral(2.0) [146-149]
+            FloatLiteral(3.0) [151-154]
+          PathExpression [157-177]
+            Identifier(NullableDocEmbedding) [157-177]
+          NamedArgument [181-227]
+            Identifier(options) [181-188]
+            JSONLiteral [192-227]
+              StringLiteral [197-227]
+                StringLiteralComponent('{"num_leaves_to_search": 10}') [197-227]
+    LimitOffset [229-238]
+      Limit [229-238]
+        IntLiteral(100) [235-238]
+==
+# https://docs.cloud.google.com/spanner/docs/find-approximate-nearest-neighbors#get-distance-score
+SELECT DocId,
+  APPROX_EUCLIDEAN_DISTANCE(
+    ARRAY[1.0, 2.0, 3.0], NullableDocEmbedding,
+    options => JSON '{"num_leaves_to_search": 10}') AS distance
+FROM Documents
+WHERE NullableDocEmbedding IS NOT NULL AND WordCount > 1000
+ORDER BY distance
+LIMIT 100
+--
+QueryStatement [0-266]
+  Query [0-266]
+    Select [0-238]
+      SelectList [7-163]
+        SelectColumn [7-12]
+          PathExpression [7-12]
+            Identifier(DocId) [7-12]
+        SelectColumn [16-163]
+          FunctionCall [16-151]
+            PathExpression [16-41]
+              Identifier(APPROX_EUCLIDEAN_DISTANCE) [16-41]
+            ArrayConstructor [47-76]
+              ArrayType [47-61]
+                SimpleType [53-60]
+                  PathExpression [53-60]
+                    Identifier(FLOAT32) [53-60]
+              FloatLiteral(1.0) [62-65]
+              FloatLiteral(2.0) [67-70]
+              FloatLiteral(3.0) [72-75]
+            PathExpression [78-98]
+              Identifier(NullableDocEmbedding) [78-98]
+            NamedArgument [104-150]
+              Identifier(options) [104-111]
+              JSONLiteral [115-150]
+                StringLiteral [120-150]
+                  StringLiteralComponent('{"num_leaves_to_search": 10}') [120-150]
+          Alias [152-163]
+            Identifier(distance) [155-163]
+      FromClause [164-178]
+        TablePathExpression [169-178]
+          PathExpression [169-178]
+            Identifier(Documents) [169-178]
+      WhereClause [179-238]
+        AndExpr [185-238]
+          BinaryExpression(IS NOT) [185-217]
+            PathExpression [185-205]
+              Identifier(NullableDocEmbedding) [185-205]
+            NullLiteral(NULL) [213-217]
+          BinaryExpression(>) [222-238]
+            PathExpression [222-231]
+              Identifier(WordCount) [222-231]
+            IntLiteral(1000) [234-238]
+    OrderBy [239-256]
+      OrderingExpression(ASC) [248-256]
+        PathExpression [248-256]
+          Identifier(distance) [248-256]
+    LimitOffset [257-266]
+      Limit [257-266]
+        IntLiteral(100) [263-266]
diff --git a/parser/testdata/spanner/guides/find-k-nearest-neighbors.test b/parser/testdata/spanner/guides/find-k-nearest-neighbors.test
new file mode 100644
index 0000000..055b878
--- /dev/null
+++ b/parser/testdata/spanner/guides/find-k-nearest-neighbors.test
@@ -0,0 +1,167 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/find-k-nearest-neighbors
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/find-k-nearest-neighbors#example_1_knn_search
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Documents (
+UserId       INT64 NOT NULL,
+DocId        INT64 NOT NULL,
+Author       STRING(1024),
+DocContents  BYTES(MAX),
+DocEmbedding ARRAY
+) PRIMARY KEY (UserId, DocId)
+--
+CreateTableStatement [0-192]
+  PathExpression [13-22]
+    Identifier(Documents) [13-22]
+  TableElementList [23-164]
+    ColumnDefinition [25-52]
+      Identifier(UserId) [25-31]
+      SimpleColumnSchema [38-52]
+        PathExpression [38-43]
+          Identifier(INT64) [38-43]
+        ColumnAttributeList [44-52]
+          NotNullColumnAttribute [44-52]
+    ColumnDefinition [54-81]
+      Identifier(DocId) [54-59]
+      SimpleColumnSchema [67-81]
+        PathExpression [67-72]
+          Identifier(INT64) [67-72]
+        ColumnAttributeList [73-81]
+          NotNullColumnAttribute [73-81]
+    ColumnDefinition [83-108]
+      Identifier(Author) [83-89]
+      SimpleColumnSchema [96-108]
+        PathExpression [96-102]
+          Identifier(STRING) [96-102]
+        TypeParameterList [102-107]
+          IntLiteral(1024) [103-107]
+    ColumnDefinition [110-133]
+      Identifier(DocContents) [110-121]
+      SimpleColumnSchema [123-133]
+        PathExpression [123-128]
+          Identifier(BYTES) [123-128]
+        TypeParameterList [128-132]
+          MaxLiteral() [129-132]
+    ColumnDefinition [135-162]
+      Identifier(DocEmbedding) [135-147]
+      ArrayColumnSchema [148-162]
+        SimpleColumnSchema [154-161]
+          PathExpression [154-161]
+            Identifier(FLOAT32) [154-161]
+  SpannerTableOptions [165-192]
+    PrimaryKey(ENFORCED) [165-192]
+      PrimaryKeyElementList [177-192]
+        PrimaryKeyElement(ASC) [178-184]
+          Identifier(UserId) [178-184]
+        PrimaryKeyElement(ASC) [186-191]
+          Identifier(DocId) [186-191]
+==
+# https://docs.cloud.google.com/spanner/docs/find-k-nearest-neighbors#example_1_knn_search
+SELECT DocId, DocEmbedding FROM Documents
+ORDER BY EUCLIDEAN_DISTANCE(DocEmbedding,
+ARRAY[0.3, 0.3, 0.7, 0.8])
+LIMIT 5
+--
+QueryStatement [0-127]
+  Query [0-127]
+    Select [0-41]
+      SelectList [7-26]
+        SelectColumn [7-12]
+          PathExpression [7-12]
+            Identifier(DocId) [7-12]
+        SelectColumn [14-26]
+          PathExpression [14-26]
+            Identifier(DocEmbedding) [14-26]
+      FromClause [27-41]
+        TablePathExpression [32-41]
+          PathExpression [32-41]
+            Identifier(Documents) [32-41]
+    OrderBy [42-119]
+      OrderingExpression(ASC) [51-119]
+        FunctionCall [51-119]
+          PathExpression [51-69]
+            Identifier(EUCLIDEAN_DISTANCE) [51-69]
+          PathExpression [70-82]
+            Identifier(DocEmbedding) [70-82]
+          ArrayConstructor [84-118]
+            ArrayType [84-98]
+              SimpleType [90-97]
+                PathExpression [90-97]
+                  Identifier(FLOAT32) [90-97]
+            FloatLiteral(0.3) [99-102]
+            FloatLiteral(0.3) [104-107]
+            FloatLiteral(0.7) [109-112]
+            FloatLiteral(0.8) [114-117]
+    LimitOffset [120-127]
+      Limit [120-127]
+        IntLiteral(5) [126-127]
+==
+# https://docs.cloud.google.com/spanner/docs/find-k-nearest-neighbors#example_2_knn_search_over_partitioned_data
+SELECT UserId, DocId, DocEmbedding FROM Documents
+WHERE UserId=18
+ORDER BY EUCLIDEAN_DISTANCE(DocEmbedding,
+ARRAY[0.3, 0.3, 0.7, 0.8])
+LIMIT 5
+--
+QueryStatement [0-151]
+  Query [0-151]
+    Select [0-65]
+      SelectList [7-34]
+        SelectColumn [7-13]
+          PathExpression [7-13]
+            Identifier(UserId) [7-13]
+        SelectColumn [15-20]
+          PathExpression [15-20]
+            Identifier(DocId) [15-20]
+        SelectColumn [22-34]
+          PathExpression [22-34]
+            Identifier(DocEmbedding) [22-34]
+      FromClause [35-49]
+        TablePathExpression [40-49]
+          PathExpression [40-49]
+            Identifier(Documents) [40-49]
+      WhereClause [50-65]
+        BinaryExpression(=) [56-65]
+          PathExpression [56-62]
+            Identifier(UserId) [56-62]
+          IntLiteral(18) [63-65]
+    OrderBy [66-143]
+      OrderingExpression(ASC) [75-143]
+        FunctionCall [75-143]
+          PathExpression [75-93]
+            Identifier(EUCLIDEAN_DISTANCE) [75-93]
+          PathExpression [94-106]
+            Identifier(DocEmbedding) [94-106]
+          ArrayConstructor [108-142]
+            ArrayType [108-122]
+              SimpleType [114-121]
+                PathExpression [114-121]
+                  Identifier(FLOAT32) [114-121]
+            FloatLiteral(0.3) [123-126]
+            FloatLiteral(0.3) [128-131]
+            FloatLiteral(0.7) [133-136]
+            FloatLiteral(0.8) [138-141]
+    LimitOffset [144-151]
+      Limit [144-151]
+        IntLiteral(5) [150-151]
+==
+# https://docs.cloud.google.com/spanner/docs/find-k-nearest-neighbors#example_3_knn_search_over_secondary_index_ranges
+CREATE INDEX DocsByAuthor
+ON Documents(Author)
+STORING (DocEmbedding)
+--
+CreateIndexStatement [0-69]
+  PathExpression [13-25]
+    Identifier(DocsByAuthor) [13-25]
+  PathExpression [29-38]
+    Identifier(Documents) [29-38]
+  IndexItemList [38-46]
+    OrderingExpression(ASC) [39-45]
+      PathExpression [39-45]
+        Identifier(Author) [39-45]
+  IndexStoringExpressionList [55-69]
+    PathExpression [56-68]
+      Identifier(DocEmbedding) [56-68]
diff --git a/parser/testdata/spanner/guides/foreign-keys.how-to.test b/parser/testdata/spanner/guides/foreign-keys.how-to.test
new file mode 100644
index 0000000..41aaf1b
--- /dev/null
+++ b/parser/testdata/spanner/guides/foreign-keys.how-to.test
@@ -0,0 +1,690 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/foreign-keys/how-to
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/how-to#create-table
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Orders (
+OrderID INT64 NOT NULL,
+CustomerID INT64 NOT NULL,
+Quantity INT64 NOT NULL,
+ProductID INT64 NOT NULL,
+CONSTRAINT FK_CustomerOrder FOREIGN KEY (CustomerID) REFERENCES Customers (CustomerID)
+) PRIMARY KEY (OrderID)
+--
+CreateTableStatement [0-234]
+  PathExpression [13-19]
+    Identifier(Orders) [13-19]
+  TableElementList [20-212]
+    ColumnDefinition [22-44]
+      Identifier(OrderID) [22-29]
+      SimpleColumnSchema [30-44]
+        PathExpression [30-35]
+          Identifier(INT64) [30-35]
+        ColumnAttributeList [36-44]
+          NotNullColumnAttribute [36-44]
+    ColumnDefinition [46-71]
+      Identifier(CustomerID) [46-56]
+      SimpleColumnSchema [57-71]
+        PathExpression [57-62]
+          Identifier(INT64) [57-62]
+        ColumnAttributeList [63-71]
+          NotNullColumnAttribute [63-71]
+    ColumnDefinition [73-96]
+      Identifier(Quantity) [73-81]
+      SimpleColumnSchema [82-96]
+        PathExpression [82-87]
+          Identifier(INT64) [82-87]
+        ColumnAttributeList [88-96]
+          NotNullColumnAttribute [88-96]
+    ColumnDefinition [98-122]
+      Identifier(ProductID) [98-107]
+      SimpleColumnSchema [108-122]
+        PathExpression [108-113]
+          Identifier(INT64) [108-113]
+        ColumnAttributeList [114-122]
+          NotNullColumnAttribute [114-122]
+    ForeignKey [124-210]
+      ColumnList [164-176]
+        Identifier(CustomerID) [165-175]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [177-210]
+        PathExpression [188-197]
+          Identifier(Customers) [188-197]
+        ColumnList [198-210]
+          Identifier(CustomerID) [199-209]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [210-210]
+      Identifier(FK_CustomerOrder) [135-151]
+  SpannerTableOptions [213-234]
+    PrimaryKey(ENFORCED) [213-234]
+      PrimaryKeyElementList [225-234]
+        PrimaryKeyElement(ASC) [226-233]
+          Identifier(OrderID) [226-233]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/how-to#create-table
+CREATE TABLE Orders (
+OrderID INT64 NOT NULL,
+CustomerID INT64 NOT NULL,
+ProductID INT64 NOT NULL,
+FOREIGN KEY (CustomerID) REFERENCES Customers (CustomerID)
+) PRIMARY KEY (OrderID)
+--
+CreateTableStatement [0-181]
+  PathExpression [13-19]
+    Identifier(Orders) [13-19]
+  TableElementList [20-159]
+    ColumnDefinition [22-44]
+      Identifier(OrderID) [22-29]
+      SimpleColumnSchema [30-44]
+        PathExpression [30-35]
+          Identifier(INT64) [30-35]
+        ColumnAttributeList [36-44]
+          NotNullColumnAttribute [36-44]
+    ColumnDefinition [46-71]
+      Identifier(CustomerID) [46-56]
+      SimpleColumnSchema [57-71]
+        PathExpression [57-62]
+          Identifier(INT64) [57-62]
+        ColumnAttributeList [63-71]
+          NotNullColumnAttribute [63-71]
+    ColumnDefinition [73-97]
+      Identifier(ProductID) [73-82]
+      SimpleColumnSchema [83-97]
+        PathExpression [83-88]
+          Identifier(INT64) [83-88]
+        ColumnAttributeList [89-97]
+          NotNullColumnAttribute [89-97]
+    ForeignKey [99-157]
+      ColumnList [111-123]
+        Identifier(CustomerID) [112-122]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [124-157]
+        PathExpression [135-144]
+          Identifier(Customers) [135-144]
+        ColumnList [145-157]
+          Identifier(CustomerID) [146-156]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [157-157]
+  SpannerTableOptions [160-181]
+    PrimaryKey(ENFORCED) [160-181]
+      PrimaryKeyElementList [172-181]
+        PrimaryKeyElement(ASC) [173-180]
+          Identifier(OrderID) [173-180]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/how-to#alter-table
+ALTER TABLE Orders
+  ADD CONSTRAINT DB_ProductOrder FOREIGN KEY (ProductID) REFERENCES Products (ProductID)
+--
+AlterTableStatement [0-107]
+  PathExpression [12-18]
+    Identifier(Orders) [12-18]
+  AlterActionList [21-107]
+    AddConstraintAction [21-107]
+      ForeignKey [36-107]
+        ColumnList [64-75]
+          Identifier(ProductID) [65-74]
+        ForeignKeyReference(MATCH SIMPLE ENFORCED) [76-107]
+          PathExpression [87-95]
+            Identifier(Products) [87-95]
+          ColumnList [96-107]
+            Identifier(ProductID) [97-106]
+          ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [107-107]
+        Identifier(DB_ProductOrder) [36-51]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/how-to#alter-table
+ALTER TABLE Orders
+  ADD FOREIGN KEY (ProductID) REFERENCES Products (ProductID)
+--
+AlterTableStatement [0-80]
+  PathExpression [12-18]
+    Identifier(Orders) [12-18]
+  AlterActionList [21-80]
+    AddConstraintAction [21-80]
+      ForeignKey [25-80]
+        ColumnList [37-48]
+          Identifier(ProductID) [38-47]
+        ForeignKeyReference(MATCH SIMPLE ENFORCED) [49-80]
+          PathExpression [60-68]
+            Identifier(Products) [60-68]
+          ColumnList [69-80]
+            Identifier(ProductID) [70-79]
+          ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [80-80]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/how-to#add-fk-action
+CREATE TABLE Orders (
+OrderID INT64 NOT NULL,
+CustomerID INT64 NOT NULL,
+Quantity INT64 NOT NULL,
+ProductID INT64 NOT NULL,
+CONSTRAINT FK_CustomerOrder FOREIGN KEY (CustomerID)
+  REFERENCES Customers (CustomerID) ON DELETE CASCADE
+) PRIMARY KEY (OrderID)
+--
+CreateTableStatement [0-254]
+  PathExpression [13-19]
+    Identifier(Orders) [13-19]
+  TableElementList [20-232]
+    ColumnDefinition [22-44]
+      Identifier(OrderID) [22-29]
+      SimpleColumnSchema [30-44]
+        PathExpression [30-35]
+          Identifier(INT64) [30-35]
+        ColumnAttributeList [36-44]
+          NotNullColumnAttribute [36-44]
+    ColumnDefinition [46-71]
+      Identifier(CustomerID) [46-56]
+      SimpleColumnSchema [57-71]
+        PathExpression [57-62]
+          Identifier(INT64) [57-62]
+        ColumnAttributeList [63-71]
+          NotNullColumnAttribute [63-71]
+    ColumnDefinition [73-96]
+      Identifier(Quantity) [73-81]
+      SimpleColumnSchema [82-96]
+        PathExpression [82-87]
+          Identifier(INT64) [82-87]
+        ColumnAttributeList [88-96]
+          NotNullColumnAttribute [88-96]
+    ColumnDefinition [98-122]
+      Identifier(ProductID) [98-107]
+      SimpleColumnSchema [108-122]
+        PathExpression [108-113]
+          Identifier(INT64) [108-113]
+        ColumnAttributeList [114-122]
+          NotNullColumnAttribute [114-122]
+    ForeignKey [124-230]
+      ColumnList [164-176]
+        Identifier(CustomerID) [165-175]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [179-230]
+        PathExpression [190-199]
+          Identifier(Customers) [190-199]
+        ColumnList [200-212]
+          Identifier(CustomerID) [201-211]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE CASCADE) [213-230]
+      Identifier(FK_CustomerOrder) [135-151]
+  SpannerTableOptions [233-254]
+    PrimaryKey(ENFORCED) [233-254]
+      PrimaryKeyElementList [245-254]
+        PrimaryKeyElement(ASC) [246-253]
+          Identifier(OrderID) [246-253]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/how-to#alter-table-with-delete
+ALTER TABLE Orders
+  ADD CONSTRAINT DB_ProductOrder FOREIGN KEY (ProductID)
+    REFERENCES Products (ProductID) ON DELETE CASCADE
+--
+AlterTableStatement [0-129]
+  PathExpression [12-18]
+    Identifier(Orders) [12-18]
+  AlterActionList [21-129]
+    AddConstraintAction [21-129]
+      ForeignKey [36-129]
+        ColumnList [64-75]
+          Identifier(ProductID) [65-74]
+        ForeignKeyReference(MATCH SIMPLE ENFORCED) [80-129]
+          PathExpression [91-99]
+            Identifier(Products) [91-99]
+          ColumnList [100-111]
+            Identifier(ProductID) [101-110]
+          ForeignKeyActions(ON UPDATE NO ACTION ON DELETE CASCADE) [112-129]
+        Identifier(DB_ProductOrder) [36-51]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/how-to#use-informational-fk
+CREATE TABLE Orders (
+    OrderID INT64 NOT NULL,
+    CustomerID INT64 NOT NULL,
+    Quantity INT64 NOT NULL,
+    ProductID INT64 NOT NULL,
+    CONSTRAINT FK_CustomerOrder FOREIGN KEY (CustomerID) REFERENCES Customers (CustomerID) NOT ENFORCED,
+    CONSTRAINT FK_ProductOrder FOREIGN KEY (ProductID) REFERENCES Products (ProductID) NOT ENFORCED
+) PRIMARY KEY (OrderID)
+--
+CreateTableStatement [0-368]
+  PathExpression [13-19]
+    Identifier(Orders) [13-19]
+  TableElementList [20-346]
+    ColumnDefinition [26-48]
+      Identifier(OrderID) [26-33]
+      SimpleColumnSchema [34-48]
+        PathExpression [34-39]
+          Identifier(INT64) [34-39]
+        ColumnAttributeList [40-48]
+          NotNullColumnAttribute [40-48]
+    ColumnDefinition [54-79]
+      Identifier(CustomerID) [54-64]
+      SimpleColumnSchema [65-79]
+        PathExpression [65-70]
+          Identifier(INT64) [65-70]
+        ColumnAttributeList [71-79]
+          NotNullColumnAttribute [71-79]
+    ColumnDefinition [85-108]
+      Identifier(Quantity) [85-93]
+      SimpleColumnSchema [94-108]
+        PathExpression [94-99]
+          Identifier(INT64) [94-99]
+        ColumnAttributeList [100-108]
+          NotNullColumnAttribute [100-108]
+    ColumnDefinition [114-138]
+      Identifier(ProductID) [114-123]
+      SimpleColumnSchema [124-138]
+        PathExpression [124-129]
+          Identifier(INT64) [124-129]
+        ColumnAttributeList [130-138]
+          NotNullColumnAttribute [130-138]
+    ForeignKey [144-243]
+      ColumnList [184-196]
+        Identifier(CustomerID) [185-195]
+      ForeignKeyReference(MATCH SIMPLE NOT ENFORCED) [197-230]
+        PathExpression [208-217]
+          Identifier(Customers) [208-217]
+        ColumnList [218-230]
+          Identifier(CustomerID) [219-229]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [230-230]
+      Identifier(FK_CustomerOrder) [155-171]
+    ForeignKey [249-344]
+      ColumnList [288-299]
+        Identifier(ProductID) [289-298]
+      ForeignKeyReference(MATCH SIMPLE NOT ENFORCED) [300-331]
+        PathExpression [311-319]
+          Identifier(Products) [311-319]
+        ColumnList [320-331]
+          Identifier(ProductID) [321-330]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [331-331]
+      Identifier(FK_ProductOrder) [260-275]
+  SpannerTableOptions [347-368]
+    PrimaryKey(ENFORCED) [347-368]
+      PrimaryKeyElementList [359-368]
+        PrimaryKeyElement(ASC) [360-367]
+          Identifier(OrderID) [360-367]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/how-to#query_data_across_foreign_key_relationships
+SELECT * FROM Orders
+  INNER JOIN Customers ON Orders.CustomerID = Customers.CustomerID
+  INNER JOIN Products ON Orders.ProductsID = Products.ProductID
+--
+QueryStatement [0-151]
+  Query [0-151]
+    Select [0-151]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-151]
+        Join(INNER) [14-151]
+          Join(INNER) [14-87]
+            TablePathExpression [14-20]
+              PathExpression [14-20]
+                Identifier(Orders) [14-20]
+            Location [23-33]
+            TablePathExpression [34-43]
+              PathExpression [34-43]
+                Identifier(Customers) [34-43]
+            OnClause [44-87]
+              BinaryExpression(=) [47-87]
+                PathExpression [47-64]
+                  Identifier(Orders) [47-53]
+                  Identifier(CustomerID) [54-64]
+                PathExpression [67-87]
+                  Identifier(Customers) [67-76]
+                  Identifier(CustomerID) [77-87]
+          Location [90-100]
+          TablePathExpression [101-109]
+            PathExpression [101-109]
+              Identifier(Products) [101-109]
+          OnClause [110-151]
+            BinaryExpression(=) [113-151]
+              PathExpression [113-130]
+                Identifier(Orders) [113-119]
+                Identifier(ProductsID) [120-130]
+              PathExpression [133-151]
+                Identifier(Products) [133-141]
+                Identifier(ProductID) [142-151]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/how-to#referential-integrity
+INSERT INTO Orders (OrderID, ProductID, Quantity, CustomerID)
+VALUES (19, 337876, 4, 447)
+--
+InsertStatement [0-89]
+  PathExpression [12-18]
+    Identifier(Orders) [12-18]
+  ColumnList [19-61]
+    Identifier(OrderID) [20-27]
+    Identifier(ProductID) [29-38]
+    Identifier(Quantity) [40-48]
+    Identifier(CustomerID) [50-60]
+  InsertValuesRowList [62-89]
+    InsertValuesRow [69-89]
+      IntLiteral(19) [70-72]
+      IntLiteral(337876) [74-80]
+      IntLiteral(4) [82-83]
+      IntLiteral(447) [85-88]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/how-to#referential-integrity
+DELETE FROM Customers WHERE CustomerID = 721
+--
+DeleteStatement [0-44]
+  PathExpression [12-21]
+    Identifier(Customers) [12-21]
+  BinaryExpression(=) [28-44]
+    PathExpression [28-38]
+      Identifier(CustomerID) [28-38]
+    IntLiteral(721) [41-44]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/how-to#view_props
+SELECT tc.CONSTRAINT_NAME, tc.TABLE_NAME, tc.CONSTRAINT_TYPE
+FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS as tc
+WHERE tc.CONSTRAINT_TYPE = 'FOREIGN KEY'
+--
+QueryStatement [0-149]
+  Query [0-149]
+    Select [0-149]
+      SelectList [7-60]
+        SelectColumn [7-25]
+          PathExpression [7-25]
+            Identifier(tc) [7-9]
+            Identifier(CONSTRAINT_NAME) [10-25]
+        SelectColumn [27-40]
+          PathExpression [27-40]
+            Identifier(tc) [27-29]
+            Identifier(TABLE_NAME) [30-40]
+        SelectColumn [42-60]
+          PathExpression [42-60]
+            Identifier(tc) [42-44]
+            Identifier(CONSTRAINT_TYPE) [45-60]
+      FromClause [61-108]
+        TablePathExpression [66-108]
+          PathExpression [66-102]
+            Identifier(INFORMATION_SCHEMA) [66-84]
+            Identifier(TABLE_CONSTRAINTS) [85-102]
+          Alias [103-108]
+            Identifier(tc) [106-108]
+      WhereClause [109-149]
+        BinaryExpression(=) [115-149]
+          PathExpression [115-133]
+            Identifier(tc) [115-117]
+            Identifier(CONSTRAINT_TYPE) [118-133]
+          StringLiteral [136-149]
+            StringLiteralComponent('FOREIGN KEY') [136-149]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/how-to#view_props
+SELECT rc.CONSTRAINT_NAME, rc.UNIQUE_CONSTRAINT_NAME, rc.SPANNER_STATE
+FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS as rc
+--
+QueryStatement [0-124]
+  Query [0-124]
+    Select [0-124]
+      SelectList [7-70]
+        SelectColumn [7-25]
+          PathExpression [7-25]
+            Identifier(rc) [7-9]
+            Identifier(CONSTRAINT_NAME) [10-25]
+        SelectColumn [27-52]
+          PathExpression [27-52]
+            Identifier(rc) [27-29]
+            Identifier(UNIQUE_CONSTRAINT_NAME) [30-52]
+        SelectColumn [54-70]
+          PathExpression [54-70]
+            Identifier(rc) [54-56]
+            Identifier(SPANNER_STATE) [57-70]
+      FromClause [71-124]
+        TablePathExpression [76-124]
+          PathExpression [76-118]
+            Identifier(INFORMATION_SCHEMA) [76-94]
+            Identifier(REFERENTIAL_CONSTRAINTS) [95-118]
+          Alias [119-124]
+            Identifier(rc) [122-124]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/how-to#view_props
+SELECT i.TABLE_NAME, i.INDEX_NAME, i.INDEX_TYPE, i.INDEX_STATE,
+  i.IS_UNIQUE, i.IS_NULL_FILTERED, i.SPANNER_IS_MANAGED
+FROM INFORMATION_SCHEMA.INDEXES as i
+WHERE SPANNER_IS_MANAGED = 'YES'
+--
+QueryStatement [0-189]
+  Query [0-189]
+    Select [0-189]
+      SelectList [7-119]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(i) [7-8]
+            Identifier(TABLE_NAME) [9-19]
+        SelectColumn [21-33]
+          PathExpression [21-33]
+            Identifier(i) [21-22]
+            Identifier(INDEX_NAME) [23-33]
+        SelectColumn [35-47]
+          PathExpression [35-47]
+            Identifier(i) [35-36]
+            Identifier(INDEX_TYPE) [37-47]
+        SelectColumn [49-62]
+          PathExpression [49-62]
+            Identifier(i) [49-50]
+            Identifier(INDEX_STATE) [51-62]
+        SelectColumn [66-77]
+          PathExpression [66-77]
+            Identifier(i) [66-67]
+            Identifier(IS_UNIQUE) [68-77]
+        SelectColumn [79-97]
+          PathExpression [79-97]
+            Identifier(i) [79-80]
+            Identifier(IS_NULL_FILTERED) [81-97]
+        SelectColumn [99-119]
+          PathExpression [99-119]
+            Identifier(i) [99-100]
+            Identifier(SPANNER_IS_MANAGED) [101-119]
+      FromClause [120-156]
+        TablePathExpression [125-156]
+          PathExpression [125-151]
+            Identifier(INFORMATION_SCHEMA) [125-143]
+            Identifier(INDEXES) [144-151]
+          Alias [152-156]
+            Identifier(i) [155-156]
+      WhereClause [157-189]
+        BinaryExpression(=) [163-189]
+          PathExpression [163-181]
+            Identifier(SPANNER_IS_MANAGED) [163-181]
+          StringLiteral [184-189]
+            StringLiteralComponent('YES') [184-189]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/how-to#view_props
+SELECT rc.CONSTRAINT_NAME, rc.UNIQUE_CONSTRAINT_NAME, rc.DELETE_RULE,
+  rc.UPDATE_RULE
+FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS as rc
+--
+QueryStatement [0-140]
+  Query [0-140]
+    Select [0-140]
+      SelectList [7-86]
+        SelectColumn [7-25]
+          PathExpression [7-25]
+            Identifier(rc) [7-9]
+            Identifier(CONSTRAINT_NAME) [10-25]
+        SelectColumn [27-52]
+          PathExpression [27-52]
+            Identifier(rc) [27-29]
+            Identifier(UNIQUE_CONSTRAINT_NAME) [30-52]
+        SelectColumn [54-68]
+          PathExpression [54-68]
+            Identifier(rc) [54-56]
+            Identifier(DELETE_RULE) [57-68]
+        SelectColumn [72-86]
+          PathExpression [72-86]
+            Identifier(rc) [72-74]
+            Identifier(UPDATE_RULE) [75-86]
+      FromClause [87-140]
+        TablePathExpression [92-140]
+          PathExpression [92-134]
+            Identifier(INFORMATION_SCHEMA) [92-110]
+            Identifier(REFERENTIAL_CONSTRAINTS) [111-134]
+          Alias [135-140]
+            Identifier(rc) [138-140]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/how-to#view_props
+SELECT tc.CONSTRAINT_NAME, tc.TABLE_NAME, tc.CONSTRAINT_TYPE, tc.ENFORCED
+FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS as tc
+WHERE tc.CONSTRAINT_TYPE = 'FOREIGN KEY'
+--
+QueryStatement [0-162]
+  Query [0-162]
+    Select [0-162]
+      SelectList [7-73]
+        SelectColumn [7-25]
+          PathExpression [7-25]
+            Identifier(tc) [7-9]
+            Identifier(CONSTRAINT_NAME) [10-25]
+        SelectColumn [27-40]
+          PathExpression [27-40]
+            Identifier(tc) [27-29]
+            Identifier(TABLE_NAME) [30-40]
+        SelectColumn [42-60]
+          PathExpression [42-60]
+            Identifier(tc) [42-44]
+            Identifier(CONSTRAINT_TYPE) [45-60]
+        SelectColumn [62-73]
+          PathExpression [62-73]
+            Identifier(tc) [62-64]
+            Identifier(ENFORCED) [65-73]
+      FromClause [74-121]
+        TablePathExpression [79-121]
+          PathExpression [79-115]
+            Identifier(INFORMATION_SCHEMA) [79-97]
+            Identifier(TABLE_CONSTRAINTS) [98-115]
+          Alias [116-121]
+            Identifier(tc) [119-121]
+      WhereClause [122-162]
+        BinaryExpression(=) [128-162]
+          PathExpression [128-146]
+            Identifier(tc) [128-130]
+            Identifier(CONSTRAINT_TYPE) [131-146]
+          StringLiteral [149-162]
+            StringLiteralComponent('FOREIGN KEY') [149-162]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/how-to#drop-foreign-key
+ALTER TABLE Orders
+  DROP CONSTRAINT FK_CustomerOrder
+--
+AlterTableStatement [0-53]
+  PathExpression [12-18]
+    Identifier(Orders) [12-18]
+  AlterActionList [21-53]
+    DropConstraintAction [21-53]
+      Identifier(FK_CustomerOrder) [37-53]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/how-to#multiple_columns
+CREATE TABLE TopHits (
+Rank INT64 NOT NULL,
+SongName STRING(MAX),
+SingerFirstName STRING(MAX),
+SingerLastName STRING(MAX),
+
+-- Song names must either be NULL or have matching values in Songs.
+FOREIGN KEY (SongName) REFERENCES Songs (SongName),
+
+-- Singer names must either be NULL or have matching values in Singers.
+FOREIGN KEY (SingerFirstName, SingerLastName)
+REFERENCES Singers (FirstName, LastName)
+
+) PRIMARY KEY (Rank)
+--
+CreateTableStatement [0-425]
+  PathExpression [13-20]
+    Identifier(TopHits) [13-20]
+  TableElementList [21-406]
+    ColumnDefinition [23-42]
+      Identifier(Rank) [23-27]
+      SimpleColumnSchema [28-42]
+        PathExpression [28-33]
+          Identifier(INT64) [28-33]
+        ColumnAttributeList [34-42]
+          NotNullColumnAttribute [34-42]
+    ColumnDefinition [44-64]
+      Identifier(SongName) [44-52]
+      SimpleColumnSchema [53-64]
+        PathExpression [53-59]
+          Identifier(STRING) [53-59]
+        TypeParameterList [59-63]
+          MaxLiteral() [60-63]
+    ColumnDefinition [66-93]
+      Identifier(SingerFirstName) [66-81]
+      SimpleColumnSchema [82-93]
+        PathExpression [82-88]
+          Identifier(STRING) [82-88]
+        TypeParameterList [88-92]
+          MaxLiteral() [89-92]
+    ColumnDefinition [95-121]
+      Identifier(SingerLastName) [95-109]
+      SimpleColumnSchema [110-121]
+        PathExpression [110-116]
+          Identifier(STRING) [110-116]
+        TypeParameterList [116-120]
+          MaxLiteral() [117-120]
+    ForeignKey [192-242]
+      ColumnList [204-214]
+        Identifier(SongName) [205-213]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [215-242]
+        PathExpression [226-231]
+          Identifier(Songs) [226-231]
+        ColumnList [232-242]
+          Identifier(SongName) [233-241]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [242-242]
+    ForeignKey [317-403]
+      ColumnList [329-362]
+        Identifier(SingerFirstName) [330-345]
+        Identifier(SingerLastName) [347-361]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [363-403]
+        PathExpression [374-381]
+          Identifier(Singers) [374-381]
+        ColumnList [382-403]
+          Identifier(FirstName) [383-392]
+          Identifier(LastName) [394-402]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [403-403]
+  SpannerTableOptions [407-425]
+    PrimaryKey(ENFORCED) [407-425]
+      PrimaryKeyElementList [419-425]
+        PrimaryKeyElement(ASC) [420-424]
+          Identifier(Rank) [420-424]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/how-to#self-referencing_tables
+CREATE TABLE Employees (
+EmployeeId INT64 NOT NULL,
+EmployeeName STRING(MAX) NOT NULL,
+ManagerId INT64,
+FOREIGN KEY (ManagerId) REFERENCES Employees (EmployeeId)
+) PRIMARY KEY (EmployeeId)
+--
+CreateTableStatement [0-188]
+  PathExpression [13-22]
+    Identifier(Employees) [13-22]
+  TableElementList [23-163]
+    ColumnDefinition [25-50]
+      Identifier(EmployeeId) [25-35]
+      SimpleColumnSchema [36-50]
+        PathExpression [36-41]
+          Identifier(INT64) [36-41]
+        ColumnAttributeList [42-50]
+          NotNullColumnAttribute [42-50]
+    ColumnDefinition [52-85]
+      Identifier(EmployeeName) [52-64]
+      SimpleColumnSchema [65-85]
+        PathExpression [65-71]
+          Identifier(STRING) [65-71]
+        TypeParameterList [71-75]
+          MaxLiteral() [72-75]
+        ColumnAttributeList [77-85]
+          NotNullColumnAttribute [77-85]
+    ColumnDefinition [87-102]
+      Identifier(ManagerId) [87-96]
+      SimpleColumnSchema [97-102]
+        PathExpression [97-102]
+          Identifier(INT64) [97-102]
+    ForeignKey [104-161]
+      ColumnList [116-127]
+        Identifier(ManagerId) [117-126]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [128-161]
+        PathExpression [139-148]
+          Identifier(Employees) [139-148]
+        ColumnList [149-161]
+          Identifier(EmployeeId) [150-160]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [161-161]
+  SpannerTableOptions [164-188]
+    PrimaryKey(ENFORCED) [164-188]
+      PrimaryKeyElementList [176-188]
+        PrimaryKeyElement(ASC) [177-187]
+          Identifier(EmployeeId) [177-187]
diff --git a/parser/testdata/spanner/guides/foreign-keys.overview.test b/parser/testdata/spanner/guides/foreign-keys.overview.test
new file mode 100644
index 0000000..97e7260
--- /dev/null
+++ b/parser/testdata/spanner/guides/foreign-keys.overview.test
@@ -0,0 +1,367 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/foreign-keys/overview
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/overview#create-table-with-informational-fk
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Customers (
+  CustomerId INT64 NOT NULL,
+  CustomerName STRING(MAX) NOT NULL,
+) PRIMARY KEY(CustomerId)
+--
+CreateTableStatement [0-116]
+  PathExpression [13-22]
+    Identifier(Customers) [13-22]
+  TableElementList [23-92]
+    ColumnDefinition [27-52]
+      Identifier(CustomerId) [27-37]
+      SimpleColumnSchema [38-52]
+        PathExpression [38-43]
+          Identifier(INT64) [38-43]
+        ColumnAttributeList [44-52]
+          NotNullColumnAttribute [44-52]
+    ColumnDefinition [56-89]
+      Identifier(CustomerName) [56-68]
+      SimpleColumnSchema [69-89]
+        PathExpression [69-75]
+          Identifier(STRING) [69-75]
+        TypeParameterList [75-79]
+          MaxLiteral() [76-79]
+        ColumnAttributeList [81-89]
+          NotNullColumnAttribute [81-89]
+  SpannerTableOptions [93-116]
+    PrimaryKey(ENFORCED) [93-116]
+      PrimaryKeyElementList [104-116]
+        PrimaryKeyElement(ASC) [105-115]
+          Identifier(CustomerId) [105-115]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/overview#create-table-with-informational-fk
+CREATE TABLE Orders (
+  OrderId INT64 NOT NULL,
+  CustomerId INT64 NOT NULL,
+  Quantity INT64 NOT NULL,
+  ProductId INT64 NOT NULL,
+  CONSTRAINT FK_CustomerOrder FOREIGN KEY (CustomerId)
+   REFERENCES Customers (CustomerId) NOT ENFORCED
+ ) PRIMARY KEY (OrderId)
+--
+CreateTableStatement [0-261]
+  PathExpression [13-19]
+    Identifier(Orders) [13-19]
+  TableElementList [20-239]
+    ColumnDefinition [24-46]
+      Identifier(OrderId) [24-31]
+      SimpleColumnSchema [32-46]
+        PathExpression [32-37]
+          Identifier(INT64) [32-37]
+        ColumnAttributeList [38-46]
+          NotNullColumnAttribute [38-46]
+    ColumnDefinition [50-75]
+      Identifier(CustomerId) [50-60]
+      SimpleColumnSchema [61-75]
+        PathExpression [61-66]
+          Identifier(INT64) [61-66]
+        ColumnAttributeList [67-75]
+          NotNullColumnAttribute [67-75]
+    ColumnDefinition [79-102]
+      Identifier(Quantity) [79-87]
+      SimpleColumnSchema [88-102]
+        PathExpression [88-93]
+          Identifier(INT64) [88-93]
+        ColumnAttributeList [94-102]
+          NotNullColumnAttribute [94-102]
+    ColumnDefinition [106-130]
+      Identifier(ProductId) [106-115]
+      SimpleColumnSchema [116-130]
+        PathExpression [116-121]
+          Identifier(INT64) [116-121]
+        ColumnAttributeList [122-130]
+          NotNullColumnAttribute [122-130]
+    ForeignKey [134-236]
+      ColumnList [174-186]
+        Identifier(CustomerId) [175-185]
+      ForeignKeyReference(MATCH SIMPLE NOT ENFORCED) [190-223]
+        PathExpression [201-210]
+          Identifier(Customers) [201-210]
+        ColumnList [211-223]
+          Identifier(CustomerId) [212-222]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [223-223]
+      Identifier(FK_CustomerOrder) [145-161]
+  SpannerTableOptions [240-261]
+    PrimaryKey(ENFORCED) [240-261]
+      PrimaryKeyElementList [252-261]
+        PrimaryKeyElement(ASC) [253-260]
+          Identifier(OrderId) [253-260]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/overview#informational-fk-query-optimization
+@{use_unenforced_foreign_key=false} SELECT Orders.CustomerId
+    FROM Orders
+    INNER JOIN Customers ON Customers.CustomerId = Orders.CustomerId
+--
+HintedStatement [0-145]
+  Hint [0-35]
+    HintEntry [2-34]
+      Identifier(use_unenforced_foreign_key) [2-28]
+      BooleanLiteral(false) [29-34]
+  QueryStatement [36-145]
+    Query [36-145]
+      Select [36-145]
+        SelectList [43-60]
+          SelectColumn [43-60]
+            PathExpression [43-60]
+              Identifier(Orders) [43-49]
+              Identifier(CustomerId) [50-60]
+        FromClause [65-145]
+          Join(INNER) [70-145]
+            TablePathExpression [70-76]
+              PathExpression [70-76]
+                Identifier(Orders) [70-76]
+            Location [81-91]
+            TablePathExpression [92-101]
+              PathExpression [92-101]
+                Identifier(Customers) [92-101]
+            OnClause [102-145]
+              BinaryExpression(=) [105-145]
+                PathExpression [105-125]
+                  Identifier(Customers) [105-114]
+                  Identifier(CustomerId) [115-125]
+                PathExpression [128-145]
+                  Identifier(Orders) [128-134]
+                  Identifier(CustomerId) [135-145]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/overview#create-table-with-enforced-fk
+CREATE TABLE Customers (
+CustomerId INT64 NOT NULL,
+CustomerName STRING(MAX) NOT NULL,
+) PRIMARY KEY(CustomerId)
+--
+CreateTableStatement [0-112]
+  PathExpression [13-22]
+    Identifier(Customers) [13-22]
+  TableElementList [23-88]
+    ColumnDefinition [25-50]
+      Identifier(CustomerId) [25-35]
+      SimpleColumnSchema [36-50]
+        PathExpression [36-41]
+          Identifier(INT64) [36-41]
+        ColumnAttributeList [42-50]
+          NotNullColumnAttribute [42-50]
+    ColumnDefinition [52-85]
+      Identifier(CustomerName) [52-64]
+      SimpleColumnSchema [65-85]
+        PathExpression [65-71]
+          Identifier(STRING) [65-71]
+        TypeParameterList [71-75]
+          MaxLiteral() [72-75]
+        ColumnAttributeList [77-85]
+          NotNullColumnAttribute [77-85]
+  SpannerTableOptions [89-112]
+    PrimaryKey(ENFORCED) [89-112]
+      PrimaryKeyElementList [100-112]
+        PrimaryKeyElement(ASC) [101-111]
+          Identifier(CustomerId) [101-111]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/overview#create-table-with-enforced-fk
+CREATE TABLE Orders (
+OrderId INT64 NOT NULL,
+CustomerId INT64 NOT NULL,
+Quantity INT64 NOT NULL,
+ProductId INT64 NOT NULL,
+CONSTRAINT FK_CustomerOrder FOREIGN KEY (CustomerId)
+  REFERENCES Customers (CustomerId) ENFORCED
+) PRIMARY KEY (OrderId)
+--
+CreateTableStatement [0-245]
+  PathExpression [13-19]
+    Identifier(Orders) [13-19]
+  TableElementList [20-223]
+    ColumnDefinition [22-44]
+      Identifier(OrderId) [22-29]
+      SimpleColumnSchema [30-44]
+        PathExpression [30-35]
+          Identifier(INT64) [30-35]
+        ColumnAttributeList [36-44]
+          NotNullColumnAttribute [36-44]
+    ColumnDefinition [46-71]
+      Identifier(CustomerId) [46-56]
+      SimpleColumnSchema [57-71]
+        PathExpression [57-62]
+          Identifier(INT64) [57-62]
+        ColumnAttributeList [63-71]
+          NotNullColumnAttribute [63-71]
+    ColumnDefinition [73-96]
+      Identifier(Quantity) [73-81]
+      SimpleColumnSchema [82-96]
+        PathExpression [82-87]
+          Identifier(INT64) [82-87]
+        ColumnAttributeList [88-96]
+          NotNullColumnAttribute [88-96]
+    ColumnDefinition [98-122]
+      Identifier(ProductId) [98-107]
+      SimpleColumnSchema [108-122]
+        PathExpression [108-113]
+          Identifier(INT64) [108-113]
+        ColumnAttributeList [114-122]
+          NotNullColumnAttribute [114-122]
+    ForeignKey [124-221]
+      ColumnList [164-176]
+        Identifier(CustomerId) [165-175]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [179-212]
+        PathExpression [190-199]
+          Identifier(Customers) [190-199]
+        ColumnList [200-212]
+          Identifier(CustomerId) [201-211]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [212-212]
+      Identifier(FK_CustomerOrder) [135-151]
+  SpannerTableOptions [224-245]
+    PrimaryKey(ENFORCED) [224-245]
+      PrimaryKeyElementList [236-245]
+        PrimaryKeyElement(ASC) [237-244]
+          Identifier(OrderId) [237-244]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/overview#how-to-define-foreign-key-action
+CREATE TABLE ShoppingCarts (
+CartId INT64 NOT NULL,
+CustomerId INT64 NOT NULL,
+CustomerName STRING(MAX) NOT NULL,
+CONSTRAINT FKShoppingCartsCustomers FOREIGN KEY(CustomerId, CustomerName)
+  REFERENCES Customers(CustomerId, CustomerName) ON DELETE CASCADE,
+) PRIMARY KEY(CartId)
+--
+CreateTableStatement [0-277]
+  PathExpression [13-26]
+    Identifier(ShoppingCarts) [13-26]
+  TableElementList [27-257]
+    ColumnDefinition [29-50]
+      Identifier(CartId) [29-35]
+      SimpleColumnSchema [36-50]
+        PathExpression [36-41]
+          Identifier(INT64) [36-41]
+        ColumnAttributeList [42-50]
+          NotNullColumnAttribute [42-50]
+    ColumnDefinition [52-77]
+      Identifier(CustomerId) [52-62]
+      SimpleColumnSchema [63-77]
+        PathExpression [63-68]
+          Identifier(INT64) [63-68]
+        ColumnAttributeList [69-77]
+          NotNullColumnAttribute [69-77]
+    ColumnDefinition [79-112]
+      Identifier(CustomerName) [79-91]
+      SimpleColumnSchema [92-112]
+        PathExpression [92-98]
+          Identifier(STRING) [92-98]
+        TypeParameterList [98-102]
+          MaxLiteral() [99-102]
+        ColumnAttributeList [104-112]
+          NotNullColumnAttribute [104-112]
+    ForeignKey [114-254]
+      ColumnList [161-187]
+        Identifier(CustomerId) [162-172]
+        Identifier(CustomerName) [174-186]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [190-254]
+        PathExpression [201-210]
+          Identifier(Customers) [201-210]
+        ColumnList [210-236]
+          Identifier(CustomerId) [211-221]
+          Identifier(CustomerName) [223-235]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE CASCADE) [237-254]
+      Identifier(FKShoppingCartsCustomers) [125-149]
+  SpannerTableOptions [258-277]
+    PrimaryKey(ENFORCED) [258-277]
+      PrimaryKeyElementList [269-277]
+        PrimaryKeyElement(ASC) [270-276]
+          Identifier(CartId) [270-276]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/overview#fk-and-table-interleaving
+CREATE TABLE Products (
+ProductId INT64 NOT NULL,
+Name STRING(256) NOT NULL,
+Price FLOAT64
+) PRIMARY KEY(ProductId)
+--
+CreateTableStatement [0-115]
+  PathExpression [13-21]
+    Identifier(Products) [13-21]
+  TableElementList [22-92]
+    ColumnDefinition [24-48]
+      Identifier(ProductId) [24-33]
+      SimpleColumnSchema [34-48]
+        PathExpression [34-39]
+          Identifier(INT64) [34-39]
+        ColumnAttributeList [40-48]
+          NotNullColumnAttribute [40-48]
+    ColumnDefinition [50-75]
+      Identifier(Name) [50-54]
+      SimpleColumnSchema [55-75]
+        PathExpression [55-61]
+          Identifier(STRING) [55-61]
+        TypeParameterList [61-65]
+          IntLiteral(256) [62-65]
+        ColumnAttributeList [67-75]
+          NotNullColumnAttribute [67-75]
+    ColumnDefinition [77-90]
+      Identifier(Price) [77-82]
+      SimpleColumnSchema [83-90]
+        PathExpression [83-90]
+          Identifier(FLOAT64) [83-90]
+  SpannerTableOptions [93-115]
+    PrimaryKey(ENFORCED) [93-115]
+      PrimaryKeyElementList [104-115]
+        PrimaryKeyElement(ASC) [105-114]
+          Identifier(ProductId) [105-114]
+==
+# https://docs.cloud.google.com/spanner/docs/foreign-keys/overview#fk-and-table-interleaving
+CREATE TABLE OrderItems (
+OrderId INT64 NOT NULL,
+ProductId INT64 NOT NULL,
+Quantity INT64 NOT NULL,
+FOREIGN KEY (ProductId) REFERENCES Products (ProductId)
+) PRIMARY KEY (OrderId, ProductId),
+INTERLEAVE IN PARENT Orders ON DELETE CASCADE
+--
+CreateTableStatement [0-238]
+  PathExpression [13-23]
+    Identifier(OrderItems) [13-23]
+  TableElementList [24-158]
+    ColumnDefinition [26-48]
+      Identifier(OrderId) [26-33]
+      SimpleColumnSchema [34-48]
+        PathExpression [34-39]
+          Identifier(INT64) [34-39]
+        ColumnAttributeList [40-48]
+          NotNullColumnAttribute [40-48]
+    ColumnDefinition [50-74]
+      Identifier(ProductId) [50-59]
+      SimpleColumnSchema [60-74]
+        PathExpression [60-65]
+          Identifier(INT64) [60-65]
+        ColumnAttributeList [66-74]
+          NotNullColumnAttribute [66-74]
+    ColumnDefinition [76-99]
+      Identifier(Quantity) [76-84]
+      SimpleColumnSchema [85-99]
+        PathExpression [85-90]
+          Identifier(INT64) [85-90]
+        ColumnAttributeList [91-99]
+          NotNullColumnAttribute [91-99]
+    ForeignKey [101-156]
+      ColumnList [113-124]
+        Identifier(ProductId) [114-123]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [125-156]
+        PathExpression [136-144]
+          Identifier(Products) [136-144]
+        ColumnList [145-156]
+          Identifier(ProductId) [146-155]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [156-156]
+  SpannerTableOptions [159-238]
+    PrimaryKey(ENFORCED) [159-191]
+      PrimaryKeyElementList [171-191]
+        PrimaryKeyElement(ASC) [172-179]
+          Identifier(OrderId) [172-179]
+        PrimaryKeyElement(ASC) [181-190]
+          Identifier(ProductId) [181-190]
+    SpannerInterleaveClause [191-238]
+      PathExpression [214-220]
+        Identifier(Orders) [214-220]
diff --git a/parser/testdata/spanner/guides/full-text-search.combine-tokenlists.metadata.json b/parser/testdata/spanner/guides/full-text-search.combine-tokenlists.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.combine-tokenlists.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/full-text-search.combine-tokenlists.test b/parser/testdata/spanner/guides/full-text-search.combine-tokenlists.test
new file mode 100644
index 0000000..a43a397
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.combine-tokenlists.test
@@ -0,0 +1,321 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/full-text-search/combine-tokenlists
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/full-text-search/combine-tokenlists#tokenize-separately
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Albums (
+  AlbumId STRING(MAX) NOT NULL,
+  Title STRING(MAX),
+  Studio STRING(MAX),
+  Title_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(Title)) HIDDEN,
+  Studio_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(Studio)) HIDDEN,
+  Combined_Tokens TOKENLIST AS (TOKENLIST_CONCAT([Title_Tokens, Studio_Tokens])) HIDDEN,
+) PRIMARY KEY(AlbumId)
+--
+CreateTableStatement [0-336]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-315]
+    ColumnDefinition [24-52]
+      Identifier(AlbumId) [24-31]
+      SimpleColumnSchema [32-52]
+        PathExpression [32-38]
+          Identifier(STRING) [32-38]
+        TypeParameterList [38-42]
+          MaxLiteral() [39-42]
+        ColumnAttributeList [44-52]
+          NotNullColumnAttribute [44-52]
+    ColumnDefinition [56-73]
+      Identifier(Title) [56-61]
+      SimpleColumnSchema [62-73]
+        PathExpression [62-68]
+          Identifier(STRING) [62-68]
+        TypeParameterList [68-72]
+          MaxLiteral() [69-72]
+    ColumnDefinition [77-95]
+      Identifier(Studio) [77-83]
+      SimpleColumnSchema [84-95]
+        PathExpression [84-90]
+          Identifier(STRING) [84-90]
+        TypeParameterList [90-94]
+          MaxLiteral() [91-94]
+    ColumnDefinition [99-158]
+      Identifier(Title_Tokens) [99-111]
+      SimpleColumnSchema [112-158]
+        PathExpression [112-121]
+          Identifier(TOKENLIST) [112-121]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [122-151]
+          FunctionCall [126-150]
+            PathExpression [126-143]
+              Identifier(TOKENIZE_FULLTEXT) [126-143]
+            PathExpression [144-149]
+              Identifier(Title) [144-149]
+        ColumnAttributeList [152-158]
+          HiddenColumnAttribute [152-158]
+    ColumnDefinition [162-223]
+      Identifier(Studio_Tokens) [162-175]
+      SimpleColumnSchema [176-223]
+        PathExpression [176-185]
+          Identifier(TOKENLIST) [176-185]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [186-216]
+          FunctionCall [190-215]
+            PathExpression [190-207]
+              Identifier(TOKENIZE_FULLTEXT) [190-207]
+            PathExpression [208-214]
+              Identifier(Studio) [208-214]
+        ColumnAttributeList [217-223]
+          HiddenColumnAttribute [217-223]
+    ColumnDefinition [227-312]
+      Identifier(Combined_Tokens) [227-242]
+      SimpleColumnSchema [243-312]
+        PathExpression [243-252]
+          Identifier(TOKENLIST) [243-252]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [253-305]
+          FunctionCall [257-304]
+            PathExpression [257-273]
+              Identifier(TOKENLIST_CONCAT) [257-273]
+            ArrayConstructor [274-303]
+              PathExpression [275-287]
+                Identifier(Title_Tokens) [275-287]
+              PathExpression [289-302]
+                Identifier(Studio_Tokens) [289-302]
+        ColumnAttributeList [306-312]
+          HiddenColumnAttribute [306-312]
+  SpannerTableOptions [316-336]
+    PrimaryKey(ENFORCED) [316-336]
+      PrimaryKeyElementList [327-336]
+        PrimaryKeyElement(ASC) [328-335]
+          Identifier(AlbumId) [328-335]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/combine-tokenlists#tokenize-separately
+CREATE SEARCH INDEX AlbumsIndex ON Albums(Combined_Tokens)
+--
+CreateIndexStatement(SEARCH) [0-58]
+  PathExpression [20-31]
+    Identifier(AlbumsIndex) [20-31]
+  PathExpression [35-41]
+    Identifier(Albums) [35-41]
+  IndexItemList [41-58]
+    OrderingExpression(ASC) [42-57]
+      PathExpression [42-57]
+        Identifier(Combined_Tokens) [42-57]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/combine-tokenlists#tokenize-separately
+SELECT AlbumId FROM Albums WHERE SEARCH(Combined_Tokens, @p)
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-60]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-60]
+        FunctionCall [33-60]
+          PathExpression [33-39]
+            Identifier(SEARCH) [33-39]
+          PathExpression [40-55]
+            Identifier(Combined_Tokens) [40-55]
+          ParameterExpr [57-59]
+            Identifier(p) [58-59]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/combine-tokenlists#concatenate-strings
+CREATE TABLE Albums (
+  AlbumId STRING(MAX) NOT NULL,
+  Title STRING(MAX),
+  Studio STRING(MAX),
+  Combined_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(Title || " " || Studio)) HIDDEN,
+) PRIMARY KEY(AlbumId)
+--
+CreateTableStatement [0-202]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-181]
+    ColumnDefinition [24-52]
+      Identifier(AlbumId) [24-31]
+      SimpleColumnSchema [32-52]
+        PathExpression [32-38]
+          Identifier(STRING) [32-38]
+        TypeParameterList [38-42]
+          MaxLiteral() [39-42]
+        ColumnAttributeList [44-52]
+          NotNullColumnAttribute [44-52]
+    ColumnDefinition [56-73]
+      Identifier(Title) [56-61]
+      SimpleColumnSchema [62-73]
+        PathExpression [62-68]
+          Identifier(STRING) [62-68]
+        TypeParameterList [68-72]
+          MaxLiteral() [69-72]
+    ColumnDefinition [77-95]
+      Identifier(Studio) [77-83]
+      SimpleColumnSchema [84-95]
+        PathExpression [84-90]
+          Identifier(STRING) [84-90]
+        TypeParameterList [90-94]
+          MaxLiteral() [91-94]
+    ColumnDefinition [99-178]
+      Identifier(Combined_Tokens) [99-114]
+      SimpleColumnSchema [115-178]
+        PathExpression [115-124]
+          Identifier(TOKENLIST) [115-124]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [125-171]
+          FunctionCall [129-170]
+            PathExpression [129-146]
+              Identifier(TOKENIZE_FULLTEXT) [129-146]
+            BinaryExpression(||) [147-169]
+              BinaryExpression(||) [147-159]
+                PathExpression [147-152]
+                  Identifier(Title) [147-152]
+                StringLiteral [156-159]
+                  StringLiteralComponent(" ") [156-159]
+              PathExpression [163-169]
+                Identifier(Studio) [163-169]
+        ColumnAttributeList [172-178]
+          HiddenColumnAttribute [172-178]
+  SpannerTableOptions [182-202]
+    PrimaryKey(ENFORCED) [182-202]
+      PrimaryKeyElementList [193-202]
+        PrimaryKeyElement(ASC) [194-201]
+          Identifier(AlbumId) [194-201]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/combine-tokenlists#concatenate-tokenlists-query
+CREATE TABLE Albums (
+  AlbumId STRING(MAX) NOT NULL,
+  Title STRING(MAX),
+  Studio STRING(MAX),
+  Title_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(Title)) HIDDEN,
+  Studio_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(Studio)) HIDDEN,
+) PRIMARY KEY(AlbumId)
+--
+CreateTableStatement [0-247]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-226]
+    ColumnDefinition [24-52]
+      Identifier(AlbumId) [24-31]
+      SimpleColumnSchema [32-52]
+        PathExpression [32-38]
+          Identifier(STRING) [32-38]
+        TypeParameterList [38-42]
+          MaxLiteral() [39-42]
+        ColumnAttributeList [44-52]
+          NotNullColumnAttribute [44-52]
+    ColumnDefinition [56-73]
+      Identifier(Title) [56-61]
+      SimpleColumnSchema [62-73]
+        PathExpression [62-68]
+          Identifier(STRING) [62-68]
+        TypeParameterList [68-72]
+          MaxLiteral() [69-72]
+    ColumnDefinition [77-95]
+      Identifier(Studio) [77-83]
+      SimpleColumnSchema [84-95]
+        PathExpression [84-90]
+          Identifier(STRING) [84-90]
+        TypeParameterList [90-94]
+          MaxLiteral() [91-94]
+    ColumnDefinition [99-158]
+      Identifier(Title_Tokens) [99-111]
+      SimpleColumnSchema [112-158]
+        PathExpression [112-121]
+          Identifier(TOKENLIST) [112-121]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [122-151]
+          FunctionCall [126-150]
+            PathExpression [126-143]
+              Identifier(TOKENIZE_FULLTEXT) [126-143]
+            PathExpression [144-149]
+              Identifier(Title) [144-149]
+        ColumnAttributeList [152-158]
+          HiddenColumnAttribute [152-158]
+    ColumnDefinition [162-223]
+      Identifier(Studio_Tokens) [162-175]
+      SimpleColumnSchema [176-223]
+        PathExpression [176-185]
+          Identifier(TOKENLIST) [176-185]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [186-216]
+          FunctionCall [190-215]
+            PathExpression [190-207]
+              Identifier(TOKENIZE_FULLTEXT) [190-207]
+            PathExpression [208-214]
+              Identifier(Studio) [208-214]
+        ColumnAttributeList [217-223]
+          HiddenColumnAttribute [217-223]
+  SpannerTableOptions [227-247]
+    PrimaryKey(ENFORCED) [227-247]
+      PrimaryKeyElementList [238-247]
+        PrimaryKeyElement(ASC) [239-246]
+          Identifier(AlbumId) [239-246]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/combine-tokenlists#concatenate-tokenlists-query
+CREATE SEARCH INDEX AlbumsIndex ON Albums(Title_Tokens, Studio_Tokens)
+--
+CreateIndexStatement(SEARCH) [0-70]
+  PathExpression [20-31]
+    Identifier(AlbumsIndex) [20-31]
+  PathExpression [35-41]
+    Identifier(Albums) [35-41]
+  IndexItemList [41-70]
+    OrderingExpression(ASC) [42-54]
+      PathExpression [42-54]
+        Identifier(Title_Tokens) [42-54]
+    OrderingExpression(ASC) [56-69]
+      PathExpression [56-69]
+        Identifier(Studio_Tokens) [56-69]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/combine-tokenlists#concatenate-tokenlists-query
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH(TOKENLIST_CONCAT([AlbumTitle_Tokens, Studio_Tokens]), 'blue note')
+--
+QueryStatement [0-106]
+  Query [0-106]
+    Select [0-106]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-106]
+        FunctionCall [33-106]
+          PathExpression [33-39]
+            Identifier(SEARCH) [33-39]
+          FunctionCall [40-92]
+            PathExpression [40-56]
+              Identifier(TOKENLIST_CONCAT) [40-56]
+            ArrayConstructor [57-91]
+              PathExpression [58-75]
+                Identifier(AlbumTitle_Tokens) [58-75]
+              PathExpression [77-90]
+                Identifier(Studio_Tokens) [77-90]
+          StringLiteral [94-105]
+            StringLiteralComponent('blue note') [94-105]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/combine-tokenlists#concatenate-tokenlists-query
+[mode=expression]
+SEARCH(AlbumTitle_Tokens, 'Blue Note') OR SEARCH(Studio_Tokens, 'Blue Note')
+--
+OrExpr [0-76]
+  FunctionCall [0-38]
+    PathExpression [0-6]
+      Identifier(SEARCH) [0-6]
+    PathExpression [7-24]
+      Identifier(AlbumTitle_Tokens) [7-24]
+    StringLiteral [26-37]
+      StringLiteralComponent('Blue Note') [26-37]
+  FunctionCall [42-76]
+    PathExpression [42-48]
+      Identifier(SEARCH) [42-48]
+    PathExpression [49-62]
+      Identifier(Studio_Tokens) [49-62]
+    StringLiteral [64-75]
+      StringLiteralComponent('Blue Note') [64-75]
diff --git a/parser/testdata/spanner/guides/full-text-search.facets.metadata.json b/parser/testdata/spanner/guides/full-text-search.facets.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.facets.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/full-text-search.facets.spanner.sql b/parser/testdata/spanner/guides/full-text-search.facets.spanner.sql
new file mode 100644
index 0000000..d38c7f8
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.facets.spanner.sql
@@ -0,0 +1,16 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/full-text-search/facets
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/full-text-search/facets#add_facets_to_use_for_full-text_searches
+# id 4a2243fb3317: search index clauses (ORDER BY, WHERE, INTERLEAVE IN) (full-text-search.facets)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ";" or end of input but got keyword ORDER [at 4:1]
+CREATE SEARCH INDEX AlbumsIndex
+ON Albums (Title_Tokens)
+STORING (Title, Genres, Rating)
+ORDER BY Likes DESC;
diff --git a/parser/testdata/spanner/guides/full-text-search.facets.test b/parser/testdata/spanner/guides/full-text-search.facets.test
new file mode 100644
index 0000000..35508f0
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.facets.test
@@ -0,0 +1,307 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/full-text-search/facets
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/full-text-search/facets#add_facets_to_use_for_full-text_searches
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Albums (
+  AlbumId INT64 NOT NULL,
+  Title STRING(MAX),
+  Rating INT64,
+  Genres ARRAY,
+  Likes INT64 NOT NULL,
+  Title_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(Title)) HIDDEN,
+) PRIMARY KEY(AlbumId)
+--
+CreateTableStatement [0-223]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-202]
+    ColumnDefinition [24-46]
+      Identifier(AlbumId) [24-31]
+      SimpleColumnSchema [32-46]
+        PathExpression [32-37]
+          Identifier(INT64) [32-37]
+        ColumnAttributeList [38-46]
+          NotNullColumnAttribute [38-46]
+    ColumnDefinition [50-67]
+      Identifier(Title) [50-55]
+      SimpleColumnSchema [56-67]
+        PathExpression [56-62]
+          Identifier(STRING) [56-62]
+        TypeParameterList [62-66]
+          MaxLiteral() [63-66]
+    ColumnDefinition [71-83]
+      Identifier(Rating) [71-77]
+      SimpleColumnSchema [78-83]
+        PathExpression [78-83]
+          Identifier(INT64) [78-83]
+    ColumnDefinition [87-112]
+      Identifier(Genres) [87-93]
+      ArrayColumnSchema [94-112]
+        SimpleColumnSchema [100-111]
+          PathExpression [100-106]
+            Identifier(STRING) [100-106]
+          TypeParameterList [106-110]
+            MaxLiteral() [107-110]
+    ColumnDefinition [116-136]
+      Identifier(Likes) [116-121]
+      SimpleColumnSchema [122-136]
+        PathExpression [122-127]
+          Identifier(INT64) [122-127]
+        ColumnAttributeList [128-136]
+          NotNullColumnAttribute [128-136]
+    ColumnDefinition [140-199]
+      Identifier(Title_Tokens) [140-152]
+      SimpleColumnSchema [153-199]
+        PathExpression [153-162]
+          Identifier(TOKENLIST) [153-162]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [163-192]
+          FunctionCall [167-191]
+            PathExpression [167-184]
+              Identifier(TOKENIZE_FULLTEXT) [167-184]
+            PathExpression [185-190]
+              Identifier(Title) [185-190]
+        ColumnAttributeList [193-199]
+          HiddenColumnAttribute [193-199]
+  SpannerTableOptions [203-223]
+    PrimaryKey(ENFORCED) [203-223]
+      PrimaryKeyElementList [214-223]
+        PrimaryKeyElement(ASC) [215-222]
+          Identifier(AlbumId) [215-222]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/facets#add_facets_to_use_for_full-text_searches
+INSERT INTO Albums (AlbumId, Title, Rating, Genres, Likes) VALUES
+(1, "The Foo Strike Again", 5, ["Rock", "Alternative"], 600),
+(2, "Who are the Who?", 5, ["Progressive", "Indie"], 200),
+(3, "No Foo For You", 4, ["Metal", "Alternative"], 50)
+--
+InsertStatement [0-241]
+  PathExpression [12-18]
+    Identifier(Albums) [12-18]
+  ColumnList [19-58]
+    Identifier(AlbumId) [20-27]
+    Identifier(Title) [29-34]
+    Identifier(Rating) [36-42]
+    Identifier(Genres) [44-50]
+    Identifier(Likes) [52-57]
+  InsertValuesRowList [59-241]
+    InsertValuesRow [66-126]
+      IntLiteral(1) [67-68]
+      StringLiteral [70-92]
+        StringLiteralComponent("The Foo Strike Again") [70-92]
+      IntLiteral(5) [94-95]
+      ArrayConstructor [97-120]
+        StringLiteral [98-104]
+          StringLiteralComponent("Rock") [98-104]
+        StringLiteral [106-119]
+          StringLiteralComponent("Alternative") [106-119]
+      IntLiteral(600) [122-125]
+    InsertValuesRow [128-185]
+      IntLiteral(2) [129-130]
+      StringLiteral [132-150]
+        StringLiteralComponent("Who are the Who?") [132-150]
+      IntLiteral(5) [152-153]
+      ArrayConstructor [155-179]
+        StringLiteral [156-169]
+          StringLiteralComponent("Progressive") [156-169]
+        StringLiteral [171-178]
+          StringLiteralComponent("Indie") [171-178]
+      IntLiteral(200) [181-184]
+    InsertValuesRow [187-241]
+      IntLiteral(3) [188-189]
+      StringLiteral [191-207]
+        StringLiteralComponent("No Foo For You") [191-207]
+      IntLiteral(4) [209-210]
+      ArrayConstructor [212-236]
+        StringLiteral [213-220]
+          StringLiteralComponent("Metal") [213-220]
+        StringLiteral [222-235]
+          StringLiteralComponent("Alternative") [222-235]
+      IntLiteral(50) [238-240]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/facets#retrieve_count_values_for_multiple_facets
+WITH search_results AS (
+  SELECT AlbumId, Title, Genres, Rating, Likes
+  FROM Albums
+  WHERE SEARCH(Title_Tokens, "foo")
+  ORDER BY Likes DESC, AlbumId
+  LIMIT 10000
+)
+
+SELECT
+-- Result set #1: First page of search results
+ARRAY(
+  SELECT AS STRUCT *
+  FROM search_results
+  ORDER BY Likes DESC, AlbumId
+  LIMIT 50
+) as result_page,
+-- Result set #2: Number of results by rating
+ARRAY(
+  SELECT AS STRUCT Rating, COUNT(*) as result_count
+  FROM search_results
+  GROUP BY Rating
+  ORDER BY result_count DESC, Rating DESC
+) as rating_counts,
+-- Result set #3: Number of results for top 5 genres
+ARRAY(
+  SELECT AS STRUCT genre, COUNT(*) as result_count
+  FROM search_results
+  JOIN UNNEST(Genres) genre
+  GROUP BY genre
+  ORDER BY result_count DESC, genre
+  LIMIT 5
+) as genres_counts
+--
+QueryStatement [0-783]
+  Query [0-783]
+    WithClause [0-168]
+      WithClauseEntry [5-168]
+        AliasedQuery [5-168]
+          Identifier(search_results) [5-19]
+          Query [23-168]
+            Select [27-121]
+              SelectList [34-71]
+                SelectColumn [34-41]
+                  PathExpression [34-41]
+                    Identifier(AlbumId) [34-41]
+                SelectColumn [43-48]
+                  PathExpression [43-48]
+                    Identifier(Title) [43-48]
+                SelectColumn [50-56]
+                  PathExpression [50-56]
+                    Identifier(Genres) [50-56]
+                SelectColumn [58-64]
+                  PathExpression [58-64]
+                    Identifier(Rating) [58-64]
+                SelectColumn [66-71]
+                  PathExpression [66-71]
+                    Identifier(Likes) [66-71]
+              FromClause [74-85]
+                TablePathExpression [79-85]
+                  PathExpression [79-85]
+                    Identifier(Albums) [79-85]
+              WhereClause [88-121]
+                FunctionCall [94-121]
+                  PathExpression [94-100]
+                    Identifier(SEARCH) [94-100]
+                  PathExpression [101-113]
+                    Identifier(Title_Tokens) [101-113]
+                  StringLiteral [115-120]
+                    StringLiteralComponent("foo") [115-120]
+            OrderBy [124-152]
+              OrderingExpression(DESC) [133-143]
+                PathExpression [133-138]
+                  Identifier(Likes) [133-138]
+              OrderingExpression(ASC) [145-152]
+                PathExpression [145-152]
+                  Identifier(AlbumId) [145-152]
+            LimitOffset [155-166]
+              Limit [155-166]
+                IntLiteral(10000) [161-166]
+    Select [170-783]
+      SelectList [224-783]
+        SelectColumn [224-332]
+          ExpressionSubquery(modifier=ARRAY) [224-317]
+            Query [233-315]
+              Select [233-273]
+                SelectAs(as_mode=STRUCT) [240-249]
+                SelectList [250-251]
+                  SelectColumn [250-251]
+                    Star(*) [250-251]
+                FromClause [254-273]
+                  TablePathExpression [259-273]
+                    PathExpression [259-273]
+                      Identifier(search_results) [259-273]
+              OrderBy [276-304]
+                OrderingExpression(DESC) [285-295]
+                  PathExpression [285-290]
+                    Identifier(Likes) [285-290]
+                OrderingExpression(ASC) [297-304]
+                  PathExpression [297-304]
+                    Identifier(AlbumId) [297-304]
+              LimitOffset [307-315]
+                Limit [307-315]
+                  IntLiteral(50) [313-315]
+          Alias [318-332]
+            Identifier(result_page) [321-332]
+        SelectColumn [380-539]
+          ExpressionSubquery(modifier=ARRAY) [380-522]
+            Query [389-520]
+              Select [389-478]
+                SelectAs(as_mode=STRUCT) [396-405]
+                SelectList [406-438]
+                  SelectColumn [406-412]
+                    PathExpression [406-412]
+                      Identifier(Rating) [406-412]
+                  SelectColumn [414-438]
+                    FunctionCall [414-422]
+                      PathExpression [414-419]
+                        Identifier(COUNT) [414-419]
+                      Star(*) [420-421]
+                    Alias [423-438]
+                      Identifier(result_count) [426-438]
+                FromClause [441-460]
+                  TablePathExpression [446-460]
+                    PathExpression [446-460]
+                      Identifier(search_results) [446-460]
+                GroupBy [463-478]
+                  GroupingItem [472-478]
+                    PathExpression [472-478]
+                      Identifier(Rating) [472-478]
+              OrderBy [481-520]
+                OrderingExpression(DESC) [490-507]
+                  PathExpression [490-502]
+                    Identifier(result_count) [490-502]
+                OrderingExpression(DESC) [509-520]
+                  PathExpression [509-515]
+                    Identifier(Rating) [509-515]
+          Alias [523-539]
+            Identifier(rating_counts) [526-539]
+        SelectColumn [594-783]
+          ExpressionSubquery(modifier=ARRAY) [594-766]
+            Query [603-764]
+              Select [603-718]
+                SelectAs(as_mode=STRUCT) [610-619]
+                SelectList [620-651]
+                  SelectColumn [620-625]
+                    PathExpression [620-625]
+                      Identifier(genre) [620-625]
+                  SelectColumn [627-651]
+                    FunctionCall [627-635]
+                      PathExpression [627-632]
+                        Identifier(COUNT) [627-632]
+                      Star(*) [633-634]
+                    Alias [636-651]
+                      Identifier(result_count) [639-651]
+                FromClause [654-701]
+                  Join [659-701]
+                    TablePathExpression [659-673]
+                      PathExpression [659-673]
+                        Identifier(search_results) [659-673]
+                    Location [676-680]
+                    TablePathExpression [681-701]
+                      UnnestExpression [681-695]
+                        ExpressionWithOptAlias [688-694]
+                          PathExpression [688-694]
+                            Identifier(Genres) [688-694]
+                      Alias [696-701]
+                        Identifier(genre) [696-701]
+                GroupBy [704-718]
+                  GroupingItem [713-718]
+                    PathExpression [713-718]
+                      Identifier(genre) [713-718]
+              OrderBy [721-754]
+                OrderingExpression(DESC) [730-747]
+                  PathExpression [730-742]
+                    Identifier(result_count) [730-742]
+                OrderingExpression(ASC) [749-754]
+                  PathExpression [749-754]
+                    Identifier(genre) [749-754]
+              LimitOffset [757-764]
+                Limit [757-764]
+                  IntLiteral(5) [763-764]
+          Alias [767-783]
+            Identifier(genres_counts) [770-783]
diff --git a/parser/testdata/spanner/guides/full-text-search.fuzzy-search.metadata.json b/parser/testdata/spanner/guides/full-text-search.fuzzy-search.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.fuzzy-search.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/full-text-search.fuzzy-search.test b/parser/testdata/spanner/guides/full-text-search.fuzzy-search.test
new file mode 100644
index 0000000..42f2b9a
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.fuzzy-search.test
@@ -0,0 +1,317 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/full-text-search/fuzzy-search
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/full-text-search/fuzzy-search#use_an_n-grams-based_approximate_search
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Albums (
+  AlbumId STRING(MAX) NOT NULL,
+  AlbumTitle STRING(MAX),
+  AlbumTitle_Tokens TOKENLIST AS (
+    TOKENIZE_SUBSTRING(AlbumTitle, ngram_size_min=>2, ngram_size_max=>3,
+                      relative_search_types=>["word_prefix", "word_suffix"])) HIDDEN
+) PRIMARY KEY(AlbumId)
+--
+CreateTableStatement [0-295]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-274]
+    ColumnDefinition [24-52]
+      Identifier(AlbumId) [24-31]
+      SimpleColumnSchema [32-52]
+        PathExpression [32-38]
+          Identifier(STRING) [32-38]
+        TypeParameterList [38-42]
+          MaxLiteral() [39-42]
+        ColumnAttributeList [44-52]
+          NotNullColumnAttribute [44-52]
+    ColumnDefinition [56-78]
+      Identifier(AlbumTitle) [56-66]
+      SimpleColumnSchema [67-78]
+        PathExpression [67-73]
+          Identifier(STRING) [67-73]
+        TypeParameterList [73-77]
+          MaxLiteral() [74-77]
+    ColumnDefinition [82-272]
+      Identifier(AlbumTitle_Tokens) [82-99]
+      SimpleColumnSchema [100-272]
+        PathExpression [100-109]
+          Identifier(TOKENLIST) [100-109]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [110-265]
+          FunctionCall [119-264]
+            PathExpression [119-137]
+              Identifier(TOKENIZE_SUBSTRING) [119-137]
+            PathExpression [138-148]
+              Identifier(AlbumTitle) [138-148]
+            NamedArgument [150-167]
+              Identifier(ngram_size_min) [150-164]
+              IntLiteral(2) [166-167]
+            NamedArgument [169-186]
+              Identifier(ngram_size_max) [169-183]
+              IntLiteral(3) [185-186]
+            NamedArgument [210-263]
+              Identifier(relative_search_types) [210-231]
+              ArrayConstructor [233-263]
+                StringLiteral [234-247]
+                  StringLiteralComponent("word_prefix") [234-247]
+                StringLiteral [249-262]
+                  StringLiteralComponent("word_suffix") [249-262]
+        ColumnAttributeList [266-272]
+          HiddenColumnAttribute [266-272]
+  SpannerTableOptions [275-295]
+    PrimaryKey(ENFORCED) [275-295]
+      PrimaryKeyElementList [286-295]
+        PrimaryKeyElement(ASC) [287-294]
+          Identifier(AlbumId) [287-294]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/fuzzy-search#use_an_n-grams-based_approximate_search
+CREATE SEARCH INDEX AlbumsIndex
+ON Albums(AlbumTitle_Tokens)
+STORING (AlbumTitle)
+--
+CreateIndexStatement(SEARCH) [0-81]
+  PathExpression [20-31]
+    Identifier(AlbumsIndex) [20-31]
+  PathExpression [35-41]
+    Identifier(Albums) [35-41]
+  IndexItemList [41-60]
+    OrderingExpression(ASC) [42-59]
+      PathExpression [42-59]
+        Identifier(AlbumTitle_Tokens) [42-59]
+  IndexStoringExpressionList [69-81]
+    PathExpression [70-80]
+      Identifier(AlbumTitle) [70-80]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/fuzzy-search#use_an_n-grams-based_approximate_search
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH_NGRAMS(AlbumTitle_Tokens, "Hatel Kaliphorn")
+ORDER BY SCORE_NGRAMS(AlbumTitle_Tokens, "Hatel Kaliphorn") DESC
+LIMIT 10
+--
+QueryStatement [0-158]
+  Query [0-158]
+    Select [0-84]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-84]
+        FunctionCall [33-84]
+          PathExpression [33-46]
+            Identifier(SEARCH_NGRAMS) [33-46]
+          PathExpression [47-64]
+            Identifier(AlbumTitle_Tokens) [47-64]
+          StringLiteral [66-83]
+            StringLiteralComponent("Hatel Kaliphorn") [66-83]
+    OrderBy [85-149]
+      OrderingExpression(DESC) [94-149]
+        FunctionCall [94-144]
+          PathExpression [94-106]
+            Identifier(SCORE_NGRAMS) [94-106]
+          PathExpression [107-124]
+            Identifier(AlbumTitle_Tokens) [107-124]
+          StringLiteral [126-143]
+            StringLiteralComponent("Hatel Kaliphorn") [126-143]
+    LimitOffset [150-158]
+      Limit [150-158]
+        IntLiteral(10) [156-158]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/fuzzy-search#optimize-n-grams
+SELECT AlbumId
+FROM (
+  SELECT AlbumId,
+        SCORE_NGRAMS(AlbumTitle_Tokens, @p) AS score
+  FROM Albums
+  WHERE SEARCH_NGRAMS(AlbumTitle_Tokens, @p)
+  LIMIT 10000  # inner limit
+)
+ORDER BY score DESC
+LIMIT 10  # outer limit
+--
+QueryStatement [0-211]
+  Query [0-211]
+    Select [0-182]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-182]
+        TableSubquery [20-182]
+          Query [24-165]
+            Select [24-151]
+              SelectList [31-92]
+                SelectColumn [31-38]
+                  PathExpression [31-38]
+                    Identifier(AlbumId) [31-38]
+                SelectColumn [48-92]
+                  FunctionCall [48-83]
+                    PathExpression [48-60]
+                      Identifier(SCORE_NGRAMS) [48-60]
+                    PathExpression [61-78]
+                      Identifier(AlbumTitle_Tokens) [61-78]
+                    ParameterExpr [80-82]
+                      Identifier(p) [81-82]
+                  Alias [84-92]
+                    Identifier(score) [87-92]
+              FromClause [95-106]
+                TablePathExpression [100-106]
+                  PathExpression [100-106]
+                    Identifier(Albums) [100-106]
+              WhereClause [109-151]
+                FunctionCall [115-151]
+                  PathExpression [115-128]
+                    Identifier(SEARCH_NGRAMS) [115-128]
+                  PathExpression [129-146]
+                    Identifier(AlbumTitle_Tokens) [129-146]
+                  ParameterExpr [148-150]
+                    Identifier(p) [149-150]
+            LimitOffset [154-165]
+              Limit [154-165]
+                IntLiteral(10000) [160-165]
+    OrderBy [183-202]
+      OrderingExpression(DESC) [192-202]
+        PathExpression [192-197]
+          Identifier(score) [192-197]
+    LimitOffset [203-211]
+      Limit [203-211]
+        IntLiteral(10) [209-211]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/fuzzy-search#perform_a_phonetic_search_with_soundex
+CREATE TABLE Singers (
+  SingerId INT64,
+  AlbumTitle STRING(MAX),
+  AlbumTitle_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN,
+  Name STRING(MAX),
+  NameSoundex STRING(MAX) AS (LOWER(SOUNDEX(Name))),
+  NameSoundex_Tokens TOKENLIST AS (TOKEN(NameSoundex)) HIDDEN
+) PRIMARY KEY(SingerId)
+--
+CreateTableStatement [0-298]
+  PathExpression [13-20]
+    Identifier(Singers) [13-20]
+  TableElementList [21-276]
+    ColumnDefinition [25-39]
+      Identifier(SingerId) [25-33]
+      SimpleColumnSchema [34-39]
+        PathExpression [34-39]
+          Identifier(INT64) [34-39]
+    ColumnDefinition [43-65]
+      Identifier(AlbumTitle) [43-53]
+      SimpleColumnSchema [54-65]
+        PathExpression [54-60]
+          Identifier(STRING) [54-60]
+        TypeParameterList [60-64]
+          MaxLiteral() [61-64]
+    ColumnDefinition [69-138]
+      Identifier(AlbumTitle_Tokens) [69-86]
+      SimpleColumnSchema [87-138]
+        PathExpression [87-96]
+          Identifier(TOKENLIST) [87-96]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [97-131]
+          FunctionCall [101-130]
+            PathExpression [101-118]
+              Identifier(TOKENIZE_FULLTEXT) [101-118]
+            PathExpression [119-129]
+              Identifier(AlbumTitle) [119-129]
+        ColumnAttributeList [132-138]
+          HiddenColumnAttribute [132-138]
+    ColumnDefinition [142-158]
+      Identifier(Name) [142-146]
+      SimpleColumnSchema [147-158]
+        PathExpression [147-153]
+          Identifier(STRING) [147-153]
+        TypeParameterList [153-157]
+          MaxLiteral() [154-157]
+    ColumnDefinition [162-211]
+      Identifier(NameSoundex) [162-173]
+      SimpleColumnSchema [174-211]
+        PathExpression [174-180]
+          Identifier(STRING) [174-180]
+        TypeParameterList [180-184]
+          MaxLiteral() [181-184]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [186-211]
+          FunctionCall [190-210]
+            PathExpression [190-195]
+              Identifier(LOWER) [190-195]
+            FunctionCall [196-209]
+              PathExpression [196-203]
+                Identifier(SOUNDEX) [196-203]
+              PathExpression [204-208]
+                Identifier(Name) [204-208]
+    ColumnDefinition [215-274]
+      Identifier(NameSoundex_Tokens) [215-233]
+      SimpleColumnSchema [234-274]
+        PathExpression [234-243]
+          Identifier(TOKENLIST) [234-243]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [244-267]
+          FunctionCall [248-266]
+            PathExpression [248-253]
+              Identifier(TOKEN) [248-253]
+            PathExpression [254-265]
+              Identifier(NameSoundex) [254-265]
+        ColumnAttributeList [268-274]
+          HiddenColumnAttribute [268-274]
+  SpannerTableOptions [277-298]
+    PrimaryKey(ENFORCED) [277-298]
+      PrimaryKeyElementList [288-298]
+        PrimaryKeyElement(ASC) [289-297]
+          Identifier(SingerId) [289-297]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/fuzzy-search#perform_a_phonetic_search_with_soundex
+CREATE SEARCH INDEX SingersPhoneticIndex ON Singers(AlbumTitle_Tokens, NameSoundex_Tokens)
+--
+CreateIndexStatement(SEARCH) [0-90]
+  PathExpression [20-40]
+    Identifier(SingersPhoneticIndex) [20-40]
+  PathExpression [44-51]
+    Identifier(Singers) [44-51]
+  IndexItemList [51-90]
+    OrderingExpression(ASC) [52-69]
+      PathExpression [52-69]
+        Identifier(AlbumTitle_Tokens) [52-69]
+    OrderingExpression(ASC) [71-89]
+      PathExpression [71-89]
+        Identifier(NameSoundex_Tokens) [71-89]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/fuzzy-search#perform_a_phonetic_search_with_soundex
+SELECT SingerId
+FROM Singers
+WHERE NameSoundex = LOWER(SOUNDEX("stefan")) AND SEARCH(AlbumTitle_Tokens, "cat")
+--
+QueryStatement [0-110]
+  Query [0-110]
+    Select [0-110]
+      SelectList [7-15]
+        SelectColumn [7-15]
+          PathExpression [7-15]
+            Identifier(SingerId) [7-15]
+      FromClause [16-28]
+        TablePathExpression [21-28]
+          PathExpression [21-28]
+            Identifier(Singers) [21-28]
+      WhereClause [29-110]
+        AndExpr [35-110]
+          BinaryExpression(=) [35-73]
+            PathExpression [35-46]
+              Identifier(NameSoundex) [35-46]
+            FunctionCall [49-73]
+              PathExpression [49-54]
+                Identifier(LOWER) [49-54]
+              FunctionCall [55-72]
+                PathExpression [55-62]
+                  Identifier(SOUNDEX) [55-62]
+                StringLiteral [63-71]
+                  StringLiteralComponent("stefan") [63-71]
+          FunctionCall [78-110]
+            PathExpression [78-84]
+              Identifier(SEARCH) [78-84]
+            PathExpression [85-102]
+              Identifier(AlbumTitle_Tokens) [85-102]
+            StringLiteral [104-109]
+              StringLiteralComponent("cat") [104-109]
diff --git a/parser/testdata/spanner/guides/full-text-search.mix-full-text-and-non-text-queries.metadata.json b/parser/testdata/spanner/guides/full-text-search.mix-full-text-and-non-text-queries.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.mix-full-text-and-non-text-queries.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/full-text-search.mix-full-text-and-non-text-queries.test b/parser/testdata/spanner/guides/full-text-search.mix-full-text-and-non-text-queries.test
new file mode 100644
index 0000000..45506d9
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.mix-full-text-and-non-text-queries.test
@@ -0,0 +1,276 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/full-text-search/mix-full-text-and-non-text-queries
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/full-text-search/mix-full-text-and-non-text-queries#perform_a_mixed_full-text_and_non-text_search
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Albums (
+  AlbumId STRING(MAX) NOT NULL,
+  Title STRING(MAX),
+  Rating FLOAT64,
+  Genres ARRAY,
+  Likes INT64,
+  Cover BYTES(MAX),
+  Title_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(Title)) HIDDEN,
+  Rating_Tokens TOKENLIST AS (TOKENIZE_NUMBER(Rating)) HIDDEN,
+  Genres_Tokens TOKENLIST AS (TOKEN(Genres)) HIDDEN
+) PRIMARY KEY(AlbumId)
+--
+CreateTableStatement [0-357]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-336]
+    ColumnDefinition [24-52]
+      Identifier(AlbumId) [24-31]
+      SimpleColumnSchema [32-52]
+        PathExpression [32-38]
+          Identifier(STRING) [32-38]
+        TypeParameterList [38-42]
+          MaxLiteral() [39-42]
+        ColumnAttributeList [44-52]
+          NotNullColumnAttribute [44-52]
+    ColumnDefinition [56-73]
+      Identifier(Title) [56-61]
+      SimpleColumnSchema [62-73]
+        PathExpression [62-68]
+          Identifier(STRING) [62-68]
+        TypeParameterList [68-72]
+          MaxLiteral() [69-72]
+    ColumnDefinition [77-91]
+      Identifier(Rating) [77-83]
+      SimpleColumnSchema [84-91]
+        PathExpression [84-91]
+          Identifier(FLOAT64) [84-91]
+    ColumnDefinition [95-120]
+      Identifier(Genres) [95-101]
+      ArrayColumnSchema [102-120]
+        SimpleColumnSchema [108-119]
+          PathExpression [108-114]
+            Identifier(STRING) [108-114]
+          TypeParameterList [114-118]
+            MaxLiteral() [115-118]
+    ColumnDefinition [124-135]
+      Identifier(Likes) [124-129]
+      SimpleColumnSchema [130-135]
+        PathExpression [130-135]
+          Identifier(INT64) [130-135]
+    ColumnDefinition [139-155]
+      Identifier(Cover) [139-144]
+      SimpleColumnSchema [145-155]
+        PathExpression [145-150]
+          Identifier(BYTES) [145-150]
+        TypeParameterList [150-154]
+          MaxLiteral() [151-154]
+    ColumnDefinition [159-218]
+      Identifier(Title_Tokens) [159-171]
+      SimpleColumnSchema [172-218]
+        PathExpression [172-181]
+          Identifier(TOKENLIST) [172-181]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [182-211]
+          FunctionCall [186-210]
+            PathExpression [186-203]
+              Identifier(TOKENIZE_FULLTEXT) [186-203]
+            PathExpression [204-209]
+              Identifier(Title) [204-209]
+        ColumnAttributeList [212-218]
+          HiddenColumnAttribute [212-218]
+    ColumnDefinition [222-281]
+      Identifier(Rating_Tokens) [222-235]
+      SimpleColumnSchema [236-281]
+        PathExpression [236-245]
+          Identifier(TOKENLIST) [236-245]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [246-274]
+          FunctionCall [250-273]
+            PathExpression [250-265]
+              Identifier(TOKENIZE_NUMBER) [250-265]
+            PathExpression [266-272]
+              Identifier(Rating) [266-272]
+        ColumnAttributeList [275-281]
+          HiddenColumnAttribute [275-281]
+    ColumnDefinition [285-334]
+      Identifier(Genres_Tokens) [285-298]
+      SimpleColumnSchema [299-334]
+        PathExpression [299-308]
+          Identifier(TOKENLIST) [299-308]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [309-327]
+          FunctionCall [313-326]
+            PathExpression [313-318]
+              Identifier(TOKEN) [313-318]
+            PathExpression [319-325]
+              Identifier(Genres) [319-325]
+        ColumnAttributeList [328-334]
+          HiddenColumnAttribute [328-334]
+  SpannerTableOptions [337-357]
+    PrimaryKey(ENFORCED) [337-357]
+      PrimaryKeyElementList [348-357]
+        PrimaryKeyElement(ASC) [349-356]
+          Identifier(AlbumId) [349-356]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/mix-full-text-and-non-text-queries#perform_a_mixed_full-text_and_non-text_search
+CREATE SEARCH INDEX AlbumsIndex
+ON Albums(Title_Tokens, Rating_Tokens, Genres_Tokens)
+STORING (Likes)
+--
+CreateIndexStatement(SEARCH) [0-101]
+  PathExpression [20-31]
+    Identifier(AlbumsIndex) [20-31]
+  PathExpression [35-41]
+    Identifier(Albums) [35-41]
+  IndexItemList [41-85]
+    OrderingExpression(ASC) [42-54]
+      PathExpression [42-54]
+        Identifier(Title_Tokens) [42-54]
+    OrderingExpression(ASC) [56-69]
+      PathExpression [56-69]
+        Identifier(Rating_Tokens) [56-69]
+    OrderingExpression(ASC) [71-84]
+      PathExpression [71-84]
+        Identifier(Genres_Tokens) [71-84]
+  IndexStoringExpressionList [94-101]
+    PathExpression [95-100]
+      Identifier(Likes) [95-100]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/mix-full-text-and-non-text-queries#perform_a_mixed_full-text_and_non-text_search
+SELECT Album
+FROM Albums
+WHERE Rating > 4
+  AND ARRAY_INCLUDES_ANY(Genres, ['jazz'])
+--
+QueryStatement [0-84]
+  Query [0-84]
+    Select [0-84]
+      SelectList [7-12]
+        SelectColumn [7-12]
+          PathExpression [7-12]
+            Identifier(Album) [7-12]
+      FromClause [13-24]
+        TablePathExpression [18-24]
+          PathExpression [18-24]
+            Identifier(Albums) [18-24]
+      WhereClause [25-84]
+        AndExpr [31-84]
+          BinaryExpression(>) [31-41]
+            PathExpression [31-37]
+              Identifier(Rating) [31-37]
+            IntLiteral(4) [40-41]
+          FunctionCall [48-84]
+            PathExpression [48-66]
+              Identifier(ARRAY_INCLUDES_ANY) [48-66]
+            PathExpression [67-73]
+              Identifier(Genres) [67-73]
+            ArrayConstructor [75-83]
+              StringLiteral [76-82]
+                StringLiteralComponent('jazz') [76-82]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/mix-full-text-and-non-text-queries#perform_a_mixed_full-text_and_non-text_search
+SELECT Album
+FROM Albums
+WHERE (SEARCH(Title_Tokens, 'car')
+      OR Rating > 4)
+  AND NOT ARRAY_INCLUDES_ANY(Genres, ['jazz'])
+--
+QueryStatement [0-127]
+  Query [0-127]
+    Select [0-127]
+      SelectList [7-12]
+        SelectColumn [7-12]
+          PathExpression [7-12]
+            Identifier(Album) [7-12]
+      FromClause [13-24]
+        TablePathExpression [18-24]
+          PathExpression [18-24]
+            Identifier(Albums) [18-24]
+      WhereClause [25-127]
+        AndExpr [31-127]
+          OrExpr [32-79]
+            FunctionCall [32-59]
+              PathExpression [32-38]
+                Identifier(SEARCH) [32-38]
+              PathExpression [39-51]
+                Identifier(Title_Tokens) [39-51]
+              StringLiteral [53-58]
+                StringLiteralComponent('car') [53-58]
+            BinaryExpression(>) [69-79]
+              PathExpression [69-75]
+                Identifier(Rating) [69-75]
+              IntLiteral(4) [78-79]
+          UnaryExpression(NOT) [87-127]
+            FunctionCall [91-127]
+              PathExpression [91-109]
+                Identifier(ARRAY_INCLUDES_ANY) [91-109]
+              PathExpression [110-116]
+                Identifier(Genres) [110-116]
+              ArrayConstructor [118-126]
+                StringLiteral [119-125]
+                  StringLiteralComponent('jazz') [119-125]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/mix-full-text-and-non-text-queries#perform_a_mixed_full-text_and_non-text_search
+SELECT Album
+FROM Albums
+WHERE SEARCH(Title_Tokens, 'car')
+  AND Rating > 4
+  AND Likes >= 1000
+--
+QueryStatement [0-95]
+  Query [0-95]
+    Select [0-95]
+      SelectList [7-12]
+        SelectColumn [7-12]
+          PathExpression [7-12]
+            Identifier(Album) [7-12]
+      FromClause [13-24]
+        TablePathExpression [18-24]
+          PathExpression [18-24]
+            Identifier(Albums) [18-24]
+      WhereClause [25-95]
+        AndExpr [31-95]
+          FunctionCall [31-58]
+            PathExpression [31-37]
+              Identifier(SEARCH) [31-37]
+            PathExpression [38-50]
+              Identifier(Title_Tokens) [38-50]
+            StringLiteral [52-57]
+              StringLiteralComponent('car') [52-57]
+          BinaryExpression(>) [65-75]
+            PathExpression [65-71]
+              Identifier(Rating) [65-71]
+            IntLiteral(4) [74-75]
+          BinaryExpression(>=) [82-95]
+            PathExpression [82-87]
+              Identifier(Likes) [82-87]
+            IntLiteral(1000) [91-95]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/mix-full-text-and-non-text-queries#perform_a_mixed_full-text_and_non-text_search
+SELECT AlbumId, Cover
+FROM Albums
+WHERE SEARCH(Title_Tokens, 'car')
+  AND Rating > 4
+--
+QueryStatement [0-84]
+  Query [0-84]
+    Select [0-84]
+      SelectList [7-21]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+        SelectColumn [16-21]
+          PathExpression [16-21]
+            Identifier(Cover) [16-21]
+      FromClause [22-33]
+        TablePathExpression [27-33]
+          PathExpression [27-33]
+            Identifier(Albums) [27-33]
+      WhereClause [34-84]
+        AndExpr [40-84]
+          FunctionCall [40-67]
+            PathExpression [40-46]
+              Identifier(SEARCH) [40-46]
+            PathExpression [47-59]
+              Identifier(Title_Tokens) [47-59]
+            StringLiteral [61-66]
+              StringLiteralComponent('car') [61-66]
+          BinaryExpression(>) [74-84]
+            PathExpression [74-80]
+              Identifier(Rating) [74-80]
+            IntLiteral(4) [83-84]
diff --git a/parser/testdata/spanner/guides/full-text-search.numeric-indexes.metadata.json b/parser/testdata/spanner/guides/full-text-search.numeric-indexes.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.numeric-indexes.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/full-text-search.numeric-indexes.test b/parser/testdata/spanner/guides/full-text-search.numeric-indexes.test
new file mode 100644
index 0000000..5909c96
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.numeric-indexes.test
@@ -0,0 +1,120 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/full-text-search/numeric-indexes
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/full-text-search/numeric-indexes#array-tokenization
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Albums (
+    AlbumId STRING(MAX) NOT NULL,
+    Ratings ARRAY,
+    Ratings_Tokens TOKENLIST
+      AS (TOKENIZE_NUMBER(Ratings, comparison_type=>"equality")) HIDDEN
+  ) PRIMARY KEY(AlbumId)
+--
+CreateTableStatement [0-207]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-186]
+    ColumnDefinition [26-54]
+      Identifier(AlbumId) [26-33]
+      SimpleColumnSchema [34-54]
+        PathExpression [34-40]
+          Identifier(STRING) [34-40]
+        TypeParameterList [40-44]
+          MaxLiteral() [41-44]
+        ColumnAttributeList [46-54]
+          NotNullColumnAttribute [46-54]
+    ColumnDefinition [60-80]
+      Identifier(Ratings) [60-67]
+      ArrayColumnSchema [68-80]
+        SimpleColumnSchema [74-79]
+          PathExpression [74-79]
+            Identifier(INT64) [74-79]
+    ColumnDefinition [86-182]
+      Identifier(Ratings_Tokens) [86-100]
+      SimpleColumnSchema [101-182]
+        PathExpression [101-110]
+          Identifier(TOKENLIST) [101-110]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [117-175]
+          FunctionCall [121-174]
+            PathExpression [121-136]
+              Identifier(TOKENIZE_NUMBER) [121-136]
+            PathExpression [137-144]
+              Identifier(Ratings) [137-144]
+            NamedArgument [146-173]
+              Identifier(comparison_type) [146-161]
+              StringLiteral [163-173]
+                StringLiteralComponent("equality") [163-173]
+        ColumnAttributeList [176-182]
+          HiddenColumnAttribute [176-182]
+  SpannerTableOptions [187-207]
+    PrimaryKey(ENFORCED) [187-207]
+      PrimaryKeyElementList [198-207]
+        PrimaryKeyElement(ASC) [199-206]
+          Identifier(AlbumId) [199-206]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/numeric-indexes#array-tokenization
+CREATE SEARCH INDEX AlbumsIndex ON Albums(Ratings_Tokens)
+--
+CreateIndexStatement(SEARCH) [0-57]
+  PathExpression [20-31]
+    Identifier(AlbumsIndex) [20-31]
+  PathExpression [35-41]
+    Identifier(Albums) [35-41]
+  IndexItemList [41-57]
+    OrderingExpression(ASC) [42-56]
+      PathExpression [42-56]
+        Identifier(Ratings_Tokens) [42-56]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/numeric-indexes#array-tokenization
+SELECT AlbumId
+  FROM Albums
+  WHERE ARRAY_INCLUDES_ANY(Ratings, [1, 2])
+--
+QueryStatement [0-72]
+  Query [0-72]
+    Select [0-72]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [17-28]
+        TablePathExpression [22-28]
+          PathExpression [22-28]
+            Identifier(Albums) [22-28]
+      WhereClause [31-72]
+        FunctionCall [37-72]
+          PathExpression [37-55]
+            Identifier(ARRAY_INCLUDES_ANY) [37-55]
+          PathExpression [56-63]
+            Identifier(Ratings) [56-63]
+          ArrayConstructor [65-71]
+            IntLiteral(1) [66-67]
+            IntLiteral(2) [69-70]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/numeric-indexes#array-tokenization
+SELECT AlbumId
+FROM Albums
+WHERE ARRAY_INCLUDES_ALL(Ratings, [1, 5])
+--
+QueryStatement [0-68]
+  Query [0-68]
+    Select [0-68]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-68]
+        FunctionCall [33-68]
+          PathExpression [33-51]
+            Identifier(ARRAY_INCLUDES_ALL) [33-51]
+          PathExpression [52-59]
+            Identifier(Ratings) [52-59]
+          ArrayConstructor [61-67]
+            IntLiteral(1) [62-63]
+            IntLiteral(5) [65-66]
diff --git a/parser/testdata/spanner/guides/full-text-search.paginate-search-results.test b/parser/testdata/spanner/guides/full-text-search.paginate-search-results.test
new file mode 100644
index 0000000..b5e6d7a
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.paginate-search-results.test
@@ -0,0 +1,145 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/full-text-search/paginate-search-results
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/full-text-search/paginate-search-results#use_key-based_pagination
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT AlbumId, ReleaseTimestamp
+FROM Albums
+WHERE SEARCH(AlbumTitle_Tokens, "fifth symphony")
+ORDER BY ReleaseTimestamp DESC, AlbumId
+LIMIT 10
+--
+QueryStatement [0-143]
+  Query [0-143]
+    Select [0-94]
+      SelectList [7-32]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+        SelectColumn [16-32]
+          PathExpression [16-32]
+            Identifier(ReleaseTimestamp) [16-32]
+      FromClause [33-44]
+        TablePathExpression [38-44]
+          PathExpression [38-44]
+            Identifier(Albums) [38-44]
+      WhereClause [45-94]
+        FunctionCall [51-94]
+          PathExpression [51-57]
+            Identifier(SEARCH) [51-57]
+          PathExpression [58-75]
+            Identifier(AlbumTitle_Tokens) [58-75]
+          StringLiteral [77-93]
+            StringLiteralComponent("fifth symphony") [77-93]
+    OrderBy [95-134]
+      OrderingExpression(DESC) [104-125]
+        PathExpression [104-120]
+          Identifier(ReleaseTimestamp) [104-120]
+      OrderingExpression(ASC) [127-134]
+        PathExpression [127-134]
+          Identifier(AlbumId) [127-134]
+    LimitOffset [135-143]
+      Limit [135-143]
+        IntLiteral(10) [141-143]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/paginate-search-results#use_key-based_pagination
+SELECT AlbumId, ReleaseTimestamp
+FROM Albums
+WHERE (ReleaseTimestamp < @last_page_release_timestamp
+    OR (ReleaseTimestamp = @last_page_release_timestamp
+        AND AlbumId > @last_page_album_id))
+    AND SEARCH(AlbumTitle_Tokens, @p)
+ORDER BY ReleaseTimestamp DESC, AlbumId ASC
+LIMIT @page_size
+--
+QueryStatement [0-298]
+  Query [0-298]
+    Select [0-237]
+      SelectList [7-32]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+        SelectColumn [16-32]
+          PathExpression [16-32]
+            Identifier(ReleaseTimestamp) [16-32]
+      FromClause [33-44]
+        TablePathExpression [38-44]
+          PathExpression [38-44]
+            Identifier(Albums) [38-44]
+      WhereClause [45-237]
+        AndExpr [51-237]
+          OrExpr [52-198]
+            BinaryExpression(<) [52-99]
+              PathExpression [52-68]
+                Identifier(ReleaseTimestamp) [52-68]
+              ParameterExpr [71-99]
+                Identifier(last_page_release_timestamp) [72-99]
+            AndExpr [108-197]
+              BinaryExpression(=) [108-155]
+                PathExpression [108-124]
+                  Identifier(ReleaseTimestamp) [108-124]
+                ParameterExpr [127-155]
+                  Identifier(last_page_release_timestamp) [128-155]
+              BinaryExpression(>) [168-197]
+                PathExpression [168-175]
+                  Identifier(AlbumId) [168-175]
+                ParameterExpr [178-197]
+                  Identifier(last_page_album_id) [179-197]
+          FunctionCall [208-237]
+            PathExpression [208-214]
+              Identifier(SEARCH) [208-214]
+            PathExpression [215-232]
+              Identifier(AlbumTitle_Tokens) [215-232]
+            ParameterExpr [234-236]
+              Identifier(p) [235-236]
+    OrderBy [238-281]
+      OrderingExpression(DESC) [247-268]
+        PathExpression [247-263]
+          Identifier(ReleaseTimestamp) [247-263]
+      OrderingExpression(ASC EXPLICITLY) [270-281]
+        PathExpression [270-277]
+          Identifier(AlbumId) [270-277]
+    LimitOffset [282-298]
+      Limit [282-298]
+        ParameterExpr [288-298]
+          Identifier(page_size) [289-298]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/paginate-search-results#use_offset-based_pagination
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH(AlbumTitle_Tokens, "fifth symphony")
+ORDER BY ReleaseTimestamp DESC, AlbumId
+LIMIT 50 OFFSET 100
+--
+QueryStatement [0-136]
+  Query [0-136]
+    Select [0-76]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-76]
+        FunctionCall [33-76]
+          PathExpression [33-39]
+            Identifier(SEARCH) [33-39]
+          PathExpression [40-57]
+            Identifier(AlbumTitle_Tokens) [40-57]
+          StringLiteral [59-75]
+            StringLiteralComponent("fifth symphony") [59-75]
+    OrderBy [77-116]
+      OrderingExpression(DESC) [86-107]
+        PathExpression [86-102]
+          Identifier(ReleaseTimestamp) [86-102]
+      OrderingExpression(ASC) [109-116]
+        PathExpression [109-116]
+          Identifier(AlbumId) [109-116]
+    LimitOffset [117-136]
+      Limit [117-125]
+        IntLiteral(50) [123-125]
+      IntLiteral(100) [133-136]
diff --git a/parser/testdata/spanner/guides/full-text-search.partition-search-index.metadata.json b/parser/testdata/spanner/guides/full-text-search.partition-search-index.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.partition-search-index.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/full-text-search.partition-search-index.test b/parser/testdata/spanner/guides/full-text-search.partition-search-index.test
new file mode 100644
index 0000000..823b5e5
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.partition-search-index.test
@@ -0,0 +1,177 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/full-text-search/partition-search-index
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/full-text-search/partition-search-index#
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Albums (
+  AlbumId STRING(MAX) NOT NULL,
+  SingerId STRING(MAX) NOT NULL,
+  ReleaseTimestamp INT64 NOT NULL,
+  AlbumTitle STRING(MAX),
+  AlbumTitle_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN,
+  SingerId_Tokens TOKENLIST AS (TOKEN(SingerId)) HIDDEN
+) PRIMARY KEY(SingerId, AlbumId)
+--
+CreateTableStatement [0-309]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-278]
+    ColumnDefinition [24-52]
+      Identifier(AlbumId) [24-31]
+      SimpleColumnSchema [32-52]
+        PathExpression [32-38]
+          Identifier(STRING) [32-38]
+        TypeParameterList [38-42]
+          MaxLiteral() [39-42]
+        ColumnAttributeList [44-52]
+          NotNullColumnAttribute [44-52]
+    ColumnDefinition [56-85]
+      Identifier(SingerId) [56-64]
+      SimpleColumnSchema [65-85]
+        PathExpression [65-71]
+          Identifier(STRING) [65-71]
+        TypeParameterList [71-75]
+          MaxLiteral() [72-75]
+        ColumnAttributeList [77-85]
+          NotNullColumnAttribute [77-85]
+    ColumnDefinition [89-120]
+      Identifier(ReleaseTimestamp) [89-105]
+      SimpleColumnSchema [106-120]
+        PathExpression [106-111]
+          Identifier(INT64) [106-111]
+        ColumnAttributeList [112-120]
+          NotNullColumnAttribute [112-120]
+    ColumnDefinition [124-146]
+      Identifier(AlbumTitle) [124-134]
+      SimpleColumnSchema [135-146]
+        PathExpression [135-141]
+          Identifier(STRING) [135-141]
+        TypeParameterList [141-145]
+          MaxLiteral() [142-145]
+    ColumnDefinition [150-219]
+      Identifier(AlbumTitle_Tokens) [150-167]
+      SimpleColumnSchema [168-219]
+        PathExpression [168-177]
+          Identifier(TOKENLIST) [168-177]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [178-212]
+          FunctionCall [182-211]
+            PathExpression [182-199]
+              Identifier(TOKENIZE_FULLTEXT) [182-199]
+            PathExpression [200-210]
+              Identifier(AlbumTitle) [200-210]
+        ColumnAttributeList [213-219]
+          HiddenColumnAttribute [213-219]
+    ColumnDefinition [223-276]
+      Identifier(SingerId_Tokens) [223-238]
+      SimpleColumnSchema [239-276]
+        PathExpression [239-248]
+          Identifier(TOKENLIST) [239-248]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [249-269]
+          FunctionCall [253-268]
+            PathExpression [253-258]
+              Identifier(TOKEN) [253-258]
+            PathExpression [259-267]
+              Identifier(SingerId) [259-267]
+        ColumnAttributeList [270-276]
+          HiddenColumnAttribute [270-276]
+  SpannerTableOptions [279-309]
+    PrimaryKey(ENFORCED) [279-309]
+      PrimaryKeyElementList [290-309]
+        PrimaryKeyElement(ASC) [291-299]
+          Identifier(SingerId) [291-299]
+        PrimaryKeyElement(ASC) [301-308]
+          Identifier(AlbumId) [301-308]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/partition-search-index#
+CREATE SEARCH INDEX AlbumsUnpartitionedIndex
+ON Albums(AlbumTitle_Tokens, SingerId_Tokens)
+--
+CreateIndexStatement(SEARCH) [0-90]
+  PathExpression [20-44]
+    Identifier(AlbumsUnpartitionedIndex) [20-44]
+  PathExpression [48-54]
+    Identifier(Albums) [48-54]
+  IndexItemList [54-90]
+    OrderingExpression(ASC) [55-72]
+      PathExpression [55-72]
+        Identifier(AlbumTitle_Tokens) [55-72]
+    OrderingExpression(ASC) [74-89]
+      PathExpression [74-89]
+        Identifier(SingerId_Tokens) [74-89]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/partition-search-index#
+CREATE SEARCH INDEX AlbumsIndexBySingerId
+ON Albums(AlbumTitle_Tokens)
+PARTITION BY SingerId
+--
+CreateIndexStatement(SEARCH) [0-92]
+  PathExpression [20-41]
+    Identifier(AlbumsIndexBySingerId) [20-41]
+  PathExpression [45-51]
+    Identifier(Albums) [45-51]
+  IndexItemList [51-70]
+    OrderingExpression(ASC) [52-69]
+      PathExpression [52-69]
+        Identifier(AlbumTitle_Tokens) [52-69]
+  PartitionBy [71-92]
+    PathExpression [84-92]
+      Identifier(SingerId) [84-92]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/partition-search-index#
+SELECT AlbumId
+FROM Albums
+WHERE SingerId = "singer1"
+AND SEARCH(AlbumTitle_Tokens, 'happy')
+--
+QueryStatement [0-92]
+  Query [0-92]
+    Select [0-92]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-92]
+        AndExpr [33-92]
+          BinaryExpression(=) [33-53]
+            PathExpression [33-41]
+              Identifier(SingerId) [33-41]
+            StringLiteral [44-53]
+              StringLiteralComponent("singer1") [44-53]
+          FunctionCall [58-92]
+            PathExpression [58-64]
+              Identifier(SEARCH) [58-64]
+            PathExpression [65-82]
+              Identifier(AlbumTitle_Tokens) [65-82]
+            StringLiteral [84-91]
+              StringLiteralComponent('happy') [84-91]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/partition-search-index#
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH(AlbumTitle_Tokens, 'piano concerto 1')
+--
+QueryStatement [0-78]
+  Query [0-78]
+    Select [0-78]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-78]
+        FunctionCall [33-78]
+          PathExpression [33-39]
+            Identifier(SEARCH) [33-39]
+          PathExpression [40-57]
+            Identifier(AlbumTitle_Tokens) [40-57]
+          StringLiteral [59-77]
+            StringLiteralComponent('piano concerto 1') [59-77]
diff --git a/parser/testdata/spanner/guides/full-text-search.pattern-matching-function-acceleration.metadata.json b/parser/testdata/spanner/guides/full-text-search.pattern-matching-function-acceleration.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.pattern-matching-function-acceleration.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/full-text-search.pattern-matching-function-acceleration.test b/parser/testdata/spanner/guides/full-text-search.pattern-matching-function-acceleration.test
new file mode 100644
index 0000000..c321176
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.pattern-matching-function-acceleration.test
@@ -0,0 +1,194 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/full-text-search/pattern-matching-function-acceleration
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/full-text-search/pattern-matching-function-acceleration#configure_an_n-gram_tokenlist_for_pattern-matching_acceleration
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Albums (
+AlbumId INT64 NOT NULL,
+AlbumTitle STRING(MAX),
+AlbumTitle_Ngram_Tokens TOKENLIST AS (
+  TOKENIZE_NGRAMS(LOWER(AlbumTitle), ngram_size_min=>3, ngram_size_max=>4)) HIDDEN,
+) PRIMARY KEY(AlbumId)
+--
+CreateTableStatement [0-215]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-194]
+    ColumnDefinition [22-44]
+      Identifier(AlbumId) [22-29]
+      SimpleColumnSchema [30-44]
+        PathExpression [30-35]
+          Identifier(INT64) [30-35]
+        ColumnAttributeList [36-44]
+          NotNullColumnAttribute [36-44]
+    ColumnDefinition [46-68]
+      Identifier(AlbumTitle) [46-56]
+      SimpleColumnSchema [57-68]
+        PathExpression [57-63]
+          Identifier(STRING) [57-63]
+        TypeParameterList [63-67]
+          MaxLiteral() [64-67]
+    ColumnDefinition [70-191]
+      Identifier(AlbumTitle_Ngram_Tokens) [70-93]
+      SimpleColumnSchema [94-191]
+        PathExpression [94-103]
+          Identifier(TOKENLIST) [94-103]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [104-184]
+          FunctionCall [111-183]
+            PathExpression [111-126]
+              Identifier(TOKENIZE_NGRAMS) [111-126]
+            FunctionCall [127-144]
+              PathExpression [127-132]
+                Identifier(LOWER) [127-132]
+              PathExpression [133-143]
+                Identifier(AlbumTitle) [133-143]
+            NamedArgument [146-163]
+              Identifier(ngram_size_min) [146-160]
+              IntLiteral(3) [162-163]
+            NamedArgument [165-182]
+              Identifier(ngram_size_max) [165-179]
+              IntLiteral(4) [181-182]
+        ColumnAttributeList [185-191]
+          HiddenColumnAttribute [185-191]
+  SpannerTableOptions [195-215]
+    PrimaryKey(ENFORCED) [195-215]
+      PrimaryKeyElementList [206-215]
+        PrimaryKeyElement(ASC) [207-214]
+          Identifier(AlbumId) [207-214]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/pattern-matching-function-acceleration#configure_an_n-gram_tokenlist_for_pattern-matching_acceleration
+CREATE SEARCH INDEX AlbumsIndex
+ON Albums(AlbumTitle_Ngram_Tokens) STORING (AlbumTitle)
+--
+CreateIndexStatement(SEARCH) [0-87]
+  PathExpression [20-31]
+    Identifier(AlbumsIndex) [20-31]
+  PathExpression [35-41]
+    Identifier(Albums) [35-41]
+  IndexItemList [41-66]
+    OrderingExpression(ASC) [42-65]
+      PathExpression [42-65]
+        Identifier(AlbumTitle_Ngram_Tokens) [42-65]
+  IndexStoringExpressionList [75-87]
+    PathExpression [76-86]
+      Identifier(AlbumTitle) [76-86]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/pattern-matching-function-acceleration#automatic_acceleration_of_queries_with_pattern-matching_predicates
+SELECT AlbumId
+FROM Albums @{FORCE_INDEX=AlbumsIndex}
+WHERE AlbumTitle LIKE "%999%"
+--
+QueryStatement [0-83]
+  Query [0-83]
+    Select [0-83]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-53]
+        TablePathExpression [20-53]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+          Hint [27-53]
+            HintEntry [29-52]
+              Identifier(FORCE_INDEX) [29-40]
+              PathExpression [41-52]
+                Identifier(AlbumsIndex) [41-52]
+      WhereClause [54-83]
+        BinaryExpression(LIKE) [60-83]
+          PathExpression [60-70]
+            Identifier(AlbumTitle) [60-70]
+          StringLiteral [76-83]
+            StringLiteralComponent("%999%") [76-83]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/pattern-matching-function-acceleration#automatic_acceleration_of_queries_with_pattern-matching_predicates
+SELECT AlbumId
+FROM Albums @{FORCE_INDEX=AlbumsIndex}
+WHERE STARTS_WITH(AlbumTitle, "apple")
+--
+QueryStatement [0-92]
+  Query [0-92]
+    Select [0-92]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-53]
+        TablePathExpression [20-53]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+          Hint [27-53]
+            HintEntry [29-52]
+              Identifier(FORCE_INDEX) [29-40]
+              PathExpression [41-52]
+                Identifier(AlbumsIndex) [41-52]
+      WhereClause [54-92]
+        FunctionCall [60-92]
+          PathExpression [60-71]
+            Identifier(STARTS_WITH) [60-71]
+          PathExpression [72-82]
+            Identifier(AlbumTitle) [72-82]
+          StringLiteral [84-91]
+            StringLiteralComponent("apple") [84-91]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/pattern-matching-function-acceleration#automatic_acceleration_of_queries_with_pattern-matching_predicates
+SELECT AlbumId
+FROM Albums @{FORCE_INDEX=AlbumsIndex}
+WHERE ENDS_WITH(AlbumTitle, "apple")
+--
+QueryStatement [0-90]
+  Query [0-90]
+    Select [0-90]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-53]
+        TablePathExpression [20-53]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+          Hint [27-53]
+            HintEntry [29-52]
+              Identifier(FORCE_INDEX) [29-40]
+              PathExpression [41-52]
+                Identifier(AlbumsIndex) [41-52]
+      WhereClause [54-90]
+        FunctionCall [60-90]
+          PathExpression [60-69]
+            Identifier(ENDS_WITH) [60-69]
+          PathExpression [70-80]
+            Identifier(AlbumTitle) [70-80]
+          StringLiteral [82-89]
+            StringLiteralComponent("apple") [82-89]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/pattern-matching-function-acceleration#automatic_acceleration_of_queries_with_pattern-matching_predicates
+SELECT AlbumId
+FROM Albums @{FORCE_INDEX=AlbumsIndex}
+WHERE REGEXP_CONTAINS(AlbumTitle, r"(good|great)[ ]+morning")
+--
+QueryStatement [0-115]
+  Query [0-115]
+    Select [0-115]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-53]
+        TablePathExpression [20-53]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+          Hint [27-53]
+            HintEntry [29-52]
+              Identifier(FORCE_INDEX) [29-40]
+              PathExpression [41-52]
+                Identifier(AlbumsIndex) [41-52]
+      WhereClause [54-115]
+        FunctionCall [60-115]
+          PathExpression [60-75]
+            Identifier(REGEXP_CONTAINS) [60-75]
+          PathExpression [76-86]
+            Identifier(AlbumTitle) [76-86]
+          StringLiteral [88-114]
+            StringLiteralComponent(r"(good|great)[ ]+morning") [88-114]
diff --git a/parser/testdata/spanner/guides/full-text-search.query-overview.metadata.json b/parser/testdata/spanner/guides/full-text-search.query-overview.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.query-overview.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/full-text-search.query-overview.test b/parser/testdata/spanner/guides/full-text-search.query-overview.test
new file mode 100644
index 0000000..d4c8f23
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.query-overview.test
@@ -0,0 +1,221 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/full-text-search/query-overview
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/full-text-search/query-overview#query_a_search_index
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH(AlbumTitle_Tokens, 'friday OR monday')
+--
+QueryStatement [0-78]
+  Query [0-78]
+    Select [0-78]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-78]
+        FunctionCall [33-78]
+          PathExpression [33-39]
+            Identifier(SEARCH) [33-39]
+          PathExpression [40-57]
+            Identifier(AlbumTitle_Tokens) [40-57]
+          StringLiteral [59-77]
+            StringLiteralComponent('friday OR monday') [59-77]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/query-overview#sql-query-requirements
+CREATE TABLE Albums (
+    AlbumId STRING(MAX) NOT NULL,
+    AlbumTitle STRING(MAX),
+    AlbumStudio STRING(MAX),
+    AlbumTitle_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN,
+    AlbumStudio_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumStudio)) HIDDEN
+) PRIMARY KEY(AlbumId)
+--
+CreateTableStatement [0-286]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-265]
+    ColumnDefinition [26-54]
+      Identifier(AlbumId) [26-33]
+      SimpleColumnSchema [34-54]
+        PathExpression [34-40]
+          Identifier(STRING) [34-40]
+        TypeParameterList [40-44]
+          MaxLiteral() [41-44]
+        ColumnAttributeList [46-54]
+          NotNullColumnAttribute [46-54]
+    ColumnDefinition [60-82]
+      Identifier(AlbumTitle) [60-70]
+      SimpleColumnSchema [71-82]
+        PathExpression [71-77]
+          Identifier(STRING) [71-77]
+        TypeParameterList [77-81]
+          MaxLiteral() [78-81]
+    ColumnDefinition [88-111]
+      Identifier(AlbumStudio) [88-99]
+      SimpleColumnSchema [100-111]
+        PathExpression [100-106]
+          Identifier(STRING) [100-106]
+        TypeParameterList [106-110]
+          MaxLiteral() [107-110]
+    ColumnDefinition [117-186]
+      Identifier(AlbumTitle_Tokens) [117-134]
+      SimpleColumnSchema [135-186]
+        PathExpression [135-144]
+          Identifier(TOKENLIST) [135-144]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [145-179]
+          FunctionCall [149-178]
+            PathExpression [149-166]
+              Identifier(TOKENIZE_FULLTEXT) [149-166]
+            PathExpression [167-177]
+              Identifier(AlbumTitle) [167-177]
+        ColumnAttributeList [180-186]
+          HiddenColumnAttribute [180-186]
+    ColumnDefinition [192-263]
+      Identifier(AlbumStudio_Tokens) [192-210]
+      SimpleColumnSchema [211-263]
+        PathExpression [211-220]
+          Identifier(TOKENLIST) [211-220]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [221-256]
+          FunctionCall [225-255]
+            PathExpression [225-242]
+              Identifier(TOKENIZE_FULLTEXT) [225-242]
+            PathExpression [243-254]
+              Identifier(AlbumStudio) [243-254]
+        ColumnAttributeList [257-263]
+          HiddenColumnAttribute [257-263]
+  SpannerTableOptions [266-286]
+    PrimaryKey(ENFORCED) [266-286]
+      PrimaryKeyElementList [277-286]
+        PrimaryKeyElement(ASC) [278-285]
+          Identifier(AlbumId) [278-285]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/query-overview#sql-query-requirements
+CREATE SEARCH INDEX AlbumsTitleIndex ON Albums(AlbumTitle_Tokens)
+--
+CreateIndexStatement(SEARCH) [0-65]
+  PathExpression [20-36]
+    Identifier(AlbumsTitleIndex) [20-36]
+  PathExpression [40-46]
+    Identifier(Albums) [40-46]
+  IndexItemList [46-65]
+    OrderingExpression(ASC) [47-64]
+      PathExpression [47-64]
+        Identifier(AlbumTitle_Tokens) [47-64]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/query-overview#sql-query-requirements
+CREATE SEARCH INDEX AlbumsStudioIndex ON Albums(AlbumStudio_Tokens)
+--
+CreateIndexStatement(SEARCH) [0-67]
+  PathExpression [20-37]
+    Identifier(AlbumsStudioIndex) [20-37]
+  PathExpression [41-47]
+    Identifier(Albums) [41-47]
+  IndexItemList [47-67]
+    OrderingExpression(ASC) [48-66]
+      PathExpression [48-66]
+        Identifier(AlbumStudio_Tokens) [48-66]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/query-overview#sql-query-requirements
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH(AlbumTitle_Tokens, @p1)
+    AND SEARCH(AlbumStudio_Tokens, @p2)
+--
+QueryStatement [0-103]
+  Query [0-103]
+    Select [0-103]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-103]
+        AndExpr [33-103]
+          FunctionCall [33-63]
+            PathExpression [33-39]
+              Identifier(SEARCH) [33-39]
+            PathExpression [40-57]
+              Identifier(AlbumTitle_Tokens) [40-57]
+            ParameterExpr [59-62]
+              Identifier(p1) [60-62]
+          FunctionCall [72-103]
+            PathExpression [72-78]
+              Identifier(SEARCH) [72-78]
+            PathExpression [79-97]
+              Identifier(AlbumStudio_Tokens) [79-97]
+            ParameterExpr [99-102]
+              Identifier(p2) [100-102]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/query-overview#index_selection
+SELECT AlbumId
+FROM Albums @{FORCE_INDEX=AlbumsIndex}
+WHERE SEARCH(AlbumTitle_Tokens, "fifth symphony")
+--
+QueryStatement [0-103]
+  Query [0-103]
+    Select [0-103]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-53]
+        TablePathExpression [20-53]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+          Hint [27-53]
+            HintEntry [29-52]
+              Identifier(FORCE_INDEX) [29-40]
+              PathExpression [41-52]
+                Identifier(AlbumsIndex) [41-52]
+      WhereClause [54-103]
+        FunctionCall [60-103]
+          PathExpression [60-66]
+            Identifier(SEARCH) [60-66]
+          PathExpression [67-84]
+            Identifier(AlbumTitle_Tokens) [67-84]
+          StringLiteral [86-102]
+            StringLiteralComponent("fifth symphony") [86-102]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/query-overview#snippets_in_search_results
+SELECT AlbumId, SNIPPET(AlbumTitle, "Fast Car")
+FROM Albums
+WHERE SEARCH(AlbumTitle_Tokens, "Fast Car")
+--
+QueryStatement [0-103]
+  Query [0-103]
+    Select [0-103]
+      SelectList [7-47]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+        SelectColumn [16-47]
+          FunctionCall [16-47]
+            PathExpression [16-23]
+              Identifier(SNIPPET) [16-23]
+            PathExpression [24-34]
+              Identifier(AlbumTitle) [24-34]
+            StringLiteral [36-46]
+              StringLiteralComponent("Fast Car") [36-46]
+      FromClause [48-59]
+        TablePathExpression [53-59]
+          PathExpression [53-59]
+            Identifier(Albums) [53-59]
+      WhereClause [60-103]
+        FunctionCall [66-103]
+          PathExpression [66-72]
+            Identifier(SEARCH) [66-72]
+          PathExpression [73-90]
+            Identifier(AlbumTitle_Tokens) [73-90]
+          StringLiteral [92-102]
+            StringLiteralComponent("Fast Car") [92-102]
diff --git a/parser/testdata/spanner/guides/full-text-search.ranked-search.test b/parser/testdata/spanner/guides/full-text-search.ranked-search.test
new file mode 100644
index 0000000..3d7db97
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.ranked-search.test
@@ -0,0 +1,406 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/full-text-search/ranked-search
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/full-text-search/ranked-search#
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH(AlbumTitle_Tokens, "fifth symphony")
+ORDER BY SCORE(AlbumTitle_Tokens, "fifth symphony") DESC
+--
+QueryStatement [0-133]
+  Query [0-133]
+    Select [0-76]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-76]
+        FunctionCall [33-76]
+          PathExpression [33-39]
+            Identifier(SEARCH) [33-39]
+          PathExpression [40-57]
+            Identifier(AlbumTitle_Tokens) [40-57]
+          StringLiteral [59-75]
+            StringLiteralComponent("fifth symphony") [59-75]
+    OrderBy [77-133]
+      OrderingExpression(DESC) [86-133]
+        FunctionCall [86-128]
+          PathExpression [86-91]
+            Identifier(SCORE) [86-91]
+          PathExpression [92-109]
+            Identifier(AlbumTitle_Tokens) [92-109]
+          StringLiteral [111-127]
+            StringLiteralComponent("fifth symphony") [111-127]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/ranked-search#score_multiple_columns
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH(Title_Tokens, @p1) OR SEARCH(Studio_Tokens, @p2)
+ORDER BY SCORE(Title_Tokens, @p1) * @titleweight + SCORE(Studio_Tokens, @p2) * @studioweight
+LIMIT 25
+--
+QueryStatement [0-190]
+  Query [0-190]
+    Select [0-88]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-88]
+        OrExpr [33-88]
+          FunctionCall [33-58]
+            PathExpression [33-39]
+              Identifier(SEARCH) [33-39]
+            PathExpression [40-52]
+              Identifier(Title_Tokens) [40-52]
+            ParameterExpr [54-57]
+              Identifier(p1) [55-57]
+          FunctionCall [62-88]
+            PathExpression [62-68]
+              Identifier(SEARCH) [62-68]
+            PathExpression [69-82]
+              Identifier(Studio_Tokens) [69-82]
+            ParameterExpr [84-87]
+              Identifier(p2) [85-87]
+    OrderBy [89-181]
+      OrderingExpression(ASC) [98-181]
+        BinaryExpression(+) [98-181]
+          BinaryExpression(*) [98-137]
+            FunctionCall [98-122]
+              PathExpression [98-103]
+                Identifier(SCORE) [98-103]
+              PathExpression [104-116]
+                Identifier(Title_Tokens) [104-116]
+              ParameterExpr [118-121]
+                Identifier(p1) [119-121]
+            ParameterExpr [125-137]
+              Identifier(titleweight) [126-137]
+          BinaryExpression(*) [140-181]
+            FunctionCall [140-165]
+              PathExpression [140-145]
+                Identifier(SCORE) [140-145]
+              PathExpression [146-159]
+                Identifier(Studio_Tokens) [146-159]
+              ParameterExpr [161-164]
+                Identifier(p2) [162-164]
+            ParameterExpr [168-181]
+              Identifier(studioweight) [169-181]
+    LimitOffset [182-190]
+      Limit [182-190]
+        IntLiteral(25) [188-190]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/ranked-search#score_multiple_columns
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH(Title_Tokens, @p1) OR SEARCH(Studio_Tokens, @p2)
+ORDER BY WITH(
+  TitleScore AS SCORE(Title_Tokens, @p1) * @titleweight,
+  StudioScore AS SCORE(Studio_Tokens, @p2) * @studioweight,
+  DaysOld AS (UNIX_MICROS(CURRENT_TIMESTAMP()) - ReleaseTimestamp) / 8.64e+10,
+  FreshnessBoost AS (1 + @freshnessweight * GREATEST(0, 30 - DaysOld) / 30),
+  PopularityBoost AS (1 + IF(HasGrammy, @grammyweight, 0)),
+  (TitleScore + StudioScore) * FreshnessBoost * PopularityBoost)
+LIMIT 25
+--
+QueryStatement [0-510]
+  Query [0-510]
+    Select [0-88]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-88]
+        OrExpr [33-88]
+          FunctionCall [33-58]
+            PathExpression [33-39]
+              Identifier(SEARCH) [33-39]
+            PathExpression [40-52]
+              Identifier(Title_Tokens) [40-52]
+            ParameterExpr [54-57]
+              Identifier(p1) [55-57]
+          FunctionCall [62-88]
+            PathExpression [62-68]
+              Identifier(SEARCH) [62-68]
+            PathExpression [69-82]
+              Identifier(Studio_Tokens) [69-82]
+            ParameterExpr [84-87]
+              Identifier(p2) [85-87]
+    OrderBy [89-501]
+      OrderingExpression(ASC) [98-501]
+        WithExpression [98-501]
+          SelectList [106-435]
+            SelectColumn [106-159]
+              BinaryExpression(*) [120-159]
+                FunctionCall [120-144]
+                  PathExpression [120-125]
+                    Identifier(SCORE) [120-125]
+                  PathExpression [126-138]
+                    Identifier(Title_Tokens) [126-138]
+                  ParameterExpr [140-143]
+                    Identifier(p1) [141-143]
+                ParameterExpr [147-159]
+                  Identifier(titleweight) [148-159]
+              Alias [106-119]
+                Identifier(TitleScore) [106-116]
+            SelectColumn [163-219]
+              BinaryExpression(*) [178-219]
+                FunctionCall [178-203]
+                  PathExpression [178-183]
+                    Identifier(SCORE) [178-183]
+                  PathExpression [184-197]
+                    Identifier(Studio_Tokens) [184-197]
+                  ParameterExpr [199-202]
+                    Identifier(p2) [200-202]
+                ParameterExpr [206-219]
+                  Identifier(studioweight) [207-219]
+              Alias [163-177]
+                Identifier(StudioScore) [163-174]
+            SelectColumn [223-298]
+              BinaryExpression(/) [234-298]
+                BinaryExpression(-) [235-286]
+                  FunctionCall [235-267]
+                    PathExpression [235-246]
+                      Identifier(UNIX_MICROS) [235-246]
+                    FunctionCall [247-266]
+                      PathExpression [247-264]
+                        Identifier(`CURRENT_TIMESTAMP`) [247-264]
+                  PathExpression [270-286]
+                    Identifier(ReleaseTimestamp) [270-286]
+                FloatLiteral(8.64e+10) [290-298]
+              Alias [223-233]
+                Identifier(DaysOld) [223-230]
+            SelectColumn [302-375]
+              BinaryExpression(+) [321-374]
+                IntLiteral(1) [321-322]
+                BinaryExpression(/) [325-374]
+                  BinaryExpression(*) [325-369]
+                    ParameterExpr [325-341]
+                      Identifier(freshnessweight) [326-341]
+                    FunctionCall [344-369]
+                      PathExpression [344-352]
+                        Identifier(GREATEST) [344-352]
+                      IntLiteral(0) [353-354]
+                      BinaryExpression(-) [356-368]
+                        IntLiteral(30) [356-358]
+                        PathExpression [361-368]
+                          Identifier(DaysOld) [361-368]
+                  IntLiteral(30) [372-374]
+              Alias [302-319]
+                Identifier(FreshnessBoost) [302-316]
+            SelectColumn [379-435]
+              BinaryExpression(+) [399-434]
+                IntLiteral(1) [399-400]
+                FunctionCall [403-434]
+                  PathExpression [403-405]
+                    Identifier(`IF`) [403-405]
+                  PathExpression [406-415]
+                    Identifier(HasGrammy) [406-415]
+                  ParameterExpr [417-430]
+                    Identifier(grammyweight) [418-430]
+                  IntLiteral(0) [432-433]
+              Alias [379-397]
+                Identifier(PopularityBoost) [379-394]
+          BinaryExpression(*) [439-500]
+            BinaryExpression(*) [439-482]
+              BinaryExpression(+) [440-464]
+                PathExpression [440-450]
+                  Identifier(TitleScore) [440-450]
+                PathExpression [453-464]
+                  Identifier(StudioScore) [453-464]
+              PathExpression [468-482]
+                Identifier(FreshnessBoost) [468-482]
+            PathExpression [485-500]
+              Identifier(PopularityBoost) [485-500]
+    LimitOffset [502-510]
+      Limit [502-510]
+        IntLiteral(25) [508-510]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/ranked-search#score_multiple_columns
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH(TOKENLIST_CONCAT([Title_Tokens, Studio_Tokens]), @p)
+ORDER BY SCORE(TOKENLIST_CONCAT([Title_Tokens, Studio_Tokens]), @p)
+LIMIT 25
+--
+QueryStatement [0-169]
+  Query [0-169]
+    Select [0-92]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-92]
+        FunctionCall [33-92]
+          PathExpression [33-39]
+            Identifier(SEARCH) [33-39]
+          FunctionCall [40-87]
+            PathExpression [40-56]
+              Identifier(TOKENLIST_CONCAT) [40-56]
+            ArrayConstructor [57-86]
+              PathExpression [58-70]
+                Identifier(Title_Tokens) [58-70]
+              PathExpression [72-85]
+                Identifier(Studio_Tokens) [72-85]
+          ParameterExpr [89-91]
+            Identifier(p) [90-91]
+    OrderBy [93-160]
+      OrderingExpression(ASC) [102-160]
+        FunctionCall [102-160]
+          PathExpression [102-107]
+            Identifier(SCORE) [102-107]
+          FunctionCall [108-155]
+            PathExpression [108-124]
+              Identifier(TOKENLIST_CONCAT) [108-124]
+            ArrayConstructor [125-154]
+              PathExpression [126-138]
+                Identifier(Title_Tokens) [126-138]
+              PathExpression [140-153]
+                Identifier(Studio_Tokens) [140-153]
+          ParameterExpr [157-159]
+            Identifier(p) [158-159]
+    LimitOffset [161-169]
+      Limit [161-169]
+        IntLiteral(25) [167-169]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/ranked-search#set_the_databases_default_scorer_version
+ALTER DATABASE database_name SET OPTIONS (score_version = 2)
+--
+AlterDatabaseStatement [0-60]
+  PathExpression [15-28]
+    Identifier(database_name) [15-28]
+  AlterActionList [29-60]
+    SetOptionsAction [29-60]
+      OptionsList [41-60]
+        OptionsEntry [42-59]
+          Identifier(score_version) [42-55]
+          IntLiteral(2) [58-59]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/ranked-search#override_the_scorer_version_per_query
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH(AlbumTitle_Tokens, @query)
+ORDER BY SCORE(
+  AlbumTitle_Tokens,
+  @query,
+  options=>JSON '{"version": 2}'
+) DESC
+--
+QueryStatement [0-153]
+  Query [0-153]
+    Select [0-66]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-66]
+        FunctionCall [33-66]
+          PathExpression [33-39]
+            Identifier(SEARCH) [33-39]
+          PathExpression [40-57]
+            Identifier(AlbumTitle_Tokens) [40-57]
+          ParameterExpr [59-65]
+            Identifier(query) [60-65]
+    OrderBy [67-153]
+      OrderingExpression(DESC) [76-153]
+        FunctionCall [76-148]
+          PathExpression [76-81]
+            Identifier(SCORE) [76-81]
+          PathExpression [85-102]
+            Identifier(AlbumTitle_Tokens) [85-102]
+          ParameterExpr [106-112]
+            Identifier(query) [107-112]
+          NamedArgument [116-146]
+            Identifier(options) [116-123]
+            JSONLiteral [125-146]
+              StringLiteral [130-146]
+                StringLiteralComponent('{"version": 2}') [130-146]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/ranked-search#limit_retrieval_depth
+SELECT AlbumId
+FROM
+  (
+    SELECT AlbumId, SCORE(Title_Tokens, @p1) AS score
+    FROM Albums
+    WHERE SEARCH(Title_Tokens, @p1)
+    ORDER BY ReleaseTimestamp DESC
+    LIMIT @retrieval_limit
+  )
+ORDER BY score DESC
+LIMIT @page_size
+--
+QueryStatement [0-232]
+  Query [0-232]
+    Select [0-195]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-195]
+        TableSubquery [22-195]
+          Query [28-191]
+            Select [28-129]
+              SelectList [35-77]
+                SelectColumn [35-42]
+                  PathExpression [35-42]
+                    Identifier(AlbumId) [35-42]
+                SelectColumn [44-77]
+                  FunctionCall [44-68]
+                    PathExpression [44-49]
+                      Identifier(SCORE) [44-49]
+                    PathExpression [50-62]
+                      Identifier(Title_Tokens) [50-62]
+                    ParameterExpr [64-67]
+                      Identifier(p1) [65-67]
+                  Alias [69-77]
+                    Identifier(score) [72-77]
+              FromClause [82-93]
+                TablePathExpression [87-93]
+                  PathExpression [87-93]
+                    Identifier(Albums) [87-93]
+              WhereClause [98-129]
+                FunctionCall [104-129]
+                  PathExpression [104-110]
+                    Identifier(SEARCH) [104-110]
+                  PathExpression [111-123]
+                    Identifier(Title_Tokens) [111-123]
+                  ParameterExpr [125-128]
+                    Identifier(p1) [126-128]
+            OrderBy [134-164]
+              OrderingExpression(DESC) [143-164]
+                PathExpression [143-159]
+                  Identifier(ReleaseTimestamp) [143-159]
+            LimitOffset [169-191]
+              Limit [169-191]
+                ParameterExpr [175-191]
+                  Identifier(retrieval_limit) [176-191]
+    OrderBy [196-215]
+      OrderingExpression(DESC) [205-215]
+        PathExpression [205-210]
+          Identifier(score) [205-210]
+    LimitOffset [216-232]
+      Limit [216-232]
+        ParameterExpr [222-232]
+          Identifier(page_size) [223-232]
diff --git a/parser/testdata/spanner/guides/full-text-search.search-indexes.metadata.json b/parser/testdata/spanner/guides/full-text-search.search-indexes.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.search-indexes.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/full-text-search.search-indexes.spanner.sql b/parser/testdata/spanner/guides/full-text-search.search-indexes.spanner.sql
new file mode 100644
index 0000000..74dfa0e
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.search-indexes.spanner.sql
@@ -0,0 +1,42 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/full-text-search/search-indexes
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-indexes#search-index-schema-definitions
+# id 866fcf6e1c19: search index clauses (ORDER BY, WHERE, INTERLEAVE IN) (full-text-search.search-indexes)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ";" or end of input but got keyword ORDER [at 4:1]
+CREATE SEARCH INDEX AlbumsIndex
+ON Albums(AlbumTitle_Tokens, Rating_Tokens)
+PARTITION BY SingerId
+ORDER BY ReleaseTimestamp DESC;
+
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-indexes#search_index_sharding
+# id 497af036fbe9: search index clauses (ORDER BY, WHERE, INTERLEAVE IN) (full-text-search.search-indexes)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ";" or end of input but got keyword ORDER [at 4:1]
+CREATE SEARCH INDEX AlbumsIndex
+ON Albums(AlbumTitle_Tokens, Rating_Tokens)
+PARTITION BY SingerId
+ORDER BY ReleaseTimestamp DESC
+OPTIONS (sort_order_sharding = true);
+
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-indexes#define_an_interleaved_search_index
+# id eef67d96745a: search index clauses (ORDER BY, WHERE, INTERLEAVE IN) (full-text-search.search-indexes)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected identifier "Singers" [at 4:15]
+CREATE SEARCH INDEX AlbumsIndex
+ON Albums(AlbumTitle_Tokens)
+PARTITION BY SingerId,
+INTERLEAVE IN Singers
+OPTIONS (sort_order_sharding = true);
+
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-indexes#null-filtered-indexes
+# id 9897bb972a57: search index clauses (ORDER BY, WHERE, INTERLEAVE IN) (full-text-search.search-indexes)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ";" or end of input but got keyword WHERE [at 4:1]
+CREATE SEARCH INDEX AlbumsIndex
+ON Albums(AlbumTitle_Tokens)
+STORING (Genre)
+WHERE Genre IS NOT NULL;
diff --git a/parser/testdata/spanner/guides/full-text-search.search-indexes.test b/parser/testdata/spanner/guides/full-text-search.search-indexes.test
new file mode 100644
index 0000000..3fabba2
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.search-indexes.test
@@ -0,0 +1,460 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/full-text-search/search-indexes
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-indexes#search_index_example
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Albums (
+  AlbumId STRING(MAX) NOT NULL,
+  AlbumTitle STRING(MAX)
+) PRIMARY KEY(AlbumId)
+--
+CreateTableStatement [0-101]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-80]
+    ColumnDefinition [24-52]
+      Identifier(AlbumId) [24-31]
+      SimpleColumnSchema [32-52]
+        PathExpression [32-38]
+          Identifier(STRING) [32-38]
+        TypeParameterList [38-42]
+          MaxLiteral() [39-42]
+        ColumnAttributeList [44-52]
+          NotNullColumnAttribute [44-52]
+    ColumnDefinition [56-78]
+      Identifier(AlbumTitle) [56-66]
+      SimpleColumnSchema [67-78]
+        PathExpression [67-73]
+          Identifier(STRING) [67-73]
+        TypeParameterList [73-77]
+          MaxLiteral() [74-77]
+  SpannerTableOptions [81-101]
+    PrimaryKey(ENFORCED) [81-101]
+      PrimaryKeyElementList [92-101]
+        PrimaryKeyElement(ASC) [93-100]
+          Identifier(AlbumId) [93-100]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-indexes#search_index_example
+ALTER TABLE Albums
+  ADD COLUMN AlbumTitle_Tokens TOKENLIST
+  AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN
+--
+AlterTableStatement [0-103]
+  PathExpression [12-18]
+    Identifier(Albums) [12-18]
+  AlterActionList [21-103]
+    AddColumnAction [21-103]
+      ColumnDefinition [32-103]
+        Identifier(AlbumTitle_Tokens) [32-49]
+        SimpleColumnSchema [50-103]
+          PathExpression [50-59]
+            Identifier(TOKENLIST) [50-59]
+          GeneratedColumnInfo(generated_mode=ALWAYS) [62-96]
+            FunctionCall [66-95]
+              PathExpression [66-83]
+                Identifier(TOKENIZE_FULLTEXT) [66-83]
+              PathExpression [84-94]
+                Identifier(AlbumTitle) [84-94]
+          ColumnAttributeList [97-103]
+            HiddenColumnAttribute [97-103]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-indexes#search_index_example
+CREATE SEARCH INDEX AlbumsIndex
+  ON Albums(AlbumTitle_Tokens)
+--
+CreateIndexStatement(SEARCH) [0-62]
+  PathExpression [20-31]
+    Identifier(AlbumsIndex) [20-31]
+  PathExpression [37-43]
+    Identifier(Albums) [37-43]
+  IndexItemList [43-62]
+    OrderingExpression(ASC) [44-61]
+      PathExpression [44-61]
+        Identifier(AlbumTitle_Tokens) [44-61]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-indexes#search_index_example
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH(AlbumTitle_Tokens, "fifth symphony")
+--
+QueryStatement [0-76]
+  Query [0-76]
+    Select [0-76]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-76]
+        FunctionCall [33-76]
+          PathExpression [33-39]
+            Identifier(SEARCH) [33-39]
+          PathExpression [40-57]
+            Identifier(AlbumTitle_Tokens) [40-57]
+          StringLiteral [59-75]
+            StringLiteralComponent("fifth symphony") [59-75]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-indexes#create_and_query_a_search_index_for_a_named_schema
+CREATE SCHEMA AlbumSchema
+--
+CreateSchemaStatement [0-25]
+  PathExpression [14-25]
+    Identifier(AlbumSchema) [14-25]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-indexes#create_and_query_a_search_index_for_a_named_schema
+CREATE TABLE AlbumSchema.Albums (
+  AlbumId STRING(MAX) NOT NULL,
+  AlbumTitle STRING(MAX),
+  AlbumTitle_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumTitle)) STORED HIDDEN,
+) PRIMARY KEY(AlbumId)
+--
+CreateTableStatement [0-194]
+  PathExpression [13-31]
+    Identifier(AlbumSchema) [13-24]
+    Identifier(Albums) [25-31]
+  TableElementList [32-173]
+    ColumnDefinition [36-64]
+      Identifier(AlbumId) [36-43]
+      SimpleColumnSchema [44-64]
+        PathExpression [44-50]
+          Identifier(STRING) [44-50]
+        TypeParameterList [50-54]
+          MaxLiteral() [51-54]
+        ColumnAttributeList [56-64]
+          NotNullColumnAttribute [56-64]
+    ColumnDefinition [68-90]
+      Identifier(AlbumTitle) [68-78]
+      SimpleColumnSchema [79-90]
+        PathExpression [79-85]
+          Identifier(STRING) [79-85]
+        TypeParameterList [85-89]
+          MaxLiteral() [86-89]
+    ColumnDefinition [94-170]
+      Identifier(AlbumTitle_Tokens) [94-111]
+      SimpleColumnSchema [112-170]
+        PathExpression [112-121]
+          Identifier(TOKENLIST) [112-121]
+        GeneratedColumnInfo(generated_mode=ALWAYS, stored_mode=STORED) [122-163]
+          FunctionCall [126-155]
+            PathExpression [126-143]
+              Identifier(TOKENIZE_FULLTEXT) [126-143]
+            PathExpression [144-154]
+              Identifier(AlbumTitle) [144-154]
+        ColumnAttributeList [164-170]
+          HiddenColumnAttribute [164-170]
+  SpannerTableOptions [174-194]
+    PrimaryKey(ENFORCED) [174-194]
+      PrimaryKeyElementList [185-194]
+        PrimaryKeyElement(ASC) [186-193]
+          Identifier(AlbumId) [186-193]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-indexes#create_and_query_a_search_index_for_a_named_schema
+CREATE SEARCH INDEX AlbumSchema.AlbumsIndex ON AlbumSchema.Albums(AlbumTitle_Tokens)
+--
+CreateIndexStatement(SEARCH) [0-84]
+  PathExpression [20-43]
+    Identifier(AlbumSchema) [20-31]
+    Identifier(AlbumsIndex) [32-43]
+  PathExpression [47-65]
+    Identifier(AlbumSchema) [47-58]
+    Identifier(Albums) [59-65]
+  IndexItemList [65-84]
+    OrderingExpression(ASC) [66-83]
+      PathExpression [66-83]
+        Identifier(AlbumTitle_Tokens) [66-83]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-indexes#create_and_query_a_search_index_for_a_named_schema
+SELECT AlbumId
+FROM AlbumSchema.Albums
+WHERE SEARCH(AlbumTitle_Tokens, "fifth symphony")
+--
+QueryStatement [0-88]
+  Query [0-88]
+    Select [0-88]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-38]
+        TablePathExpression [20-38]
+          PathExpression [20-38]
+            Identifier(AlbumSchema) [20-31]
+            Identifier(Albums) [32-38]
+      WhereClause [39-88]
+        FunctionCall [45-88]
+          PathExpression [45-51]
+            Identifier(SEARCH) [45-51]
+          PathExpression [52-69]
+            Identifier(AlbumTitle_Tokens) [52-69]
+          StringLiteral [71-87]
+            StringLiteralComponent("fifth symphony") [71-87]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-indexes#create_and_query_a_search_index_for_a_named_schema
+SELECT AlbumId
+FROM AlbumSchema.Albums@{FORCE_INDEX="AlbumSchema.AlbumsIndex"}
+WHERE SEARCH(AlbumTitle_Tokens, "fifth symphony")
+--
+QueryStatement [0-128]
+  Query [0-128]
+    Select [0-128]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-78]
+        TablePathExpression [20-78]
+          PathExpression [20-38]
+            Identifier(AlbumSchema) [20-31]
+            Identifier(Albums) [32-38]
+          Hint [38-78]
+            HintEntry [40-77]
+              Identifier(FORCE_INDEX) [40-51]
+              StringLiteral [52-77]
+                StringLiteralComponent("AlbumSchema.AlbumsIndex") [52-77]
+      WhereClause [79-128]
+        FunctionCall [85-128]
+          PathExpression [85-91]
+            Identifier(SEARCH) [85-91]
+          PathExpression [92-109]
+            Identifier(AlbumTitle_Tokens) [92-109]
+          StringLiteral [111-127]
+            StringLiteralComponent("fifth symphony") [111-127]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-indexes#search-index-schema-definitions
+CREATE TABLE Albums (
+  AlbumId STRING(MAX) NOT NULL,
+  SingerId INT64 NOT NULL,
+  ReleaseTimestamp INT64 NOT NULL,
+  AlbumTitle STRING(MAX),
+  Rating FLOAT64,
+  AlbumTitle_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN,
+  Rating_Tokens TOKENLIST AS (TOKENIZE_NUMBER(Rating)) HIDDEN
+) PRIMARY KEY(AlbumId)
+--
+CreateTableStatement [0-317]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-296]
+    ColumnDefinition [24-52]
+      Identifier(AlbumId) [24-31]
+      SimpleColumnSchema [32-52]
+        PathExpression [32-38]
+          Identifier(STRING) [32-38]
+        TypeParameterList [38-42]
+          MaxLiteral() [39-42]
+        ColumnAttributeList [44-52]
+          NotNullColumnAttribute [44-52]
+    ColumnDefinition [56-79]
+      Identifier(SingerId) [56-64]
+      SimpleColumnSchema [65-79]
+        PathExpression [65-70]
+          Identifier(INT64) [65-70]
+        ColumnAttributeList [71-79]
+          NotNullColumnAttribute [71-79]
+    ColumnDefinition [83-114]
+      Identifier(ReleaseTimestamp) [83-99]
+      SimpleColumnSchema [100-114]
+        PathExpression [100-105]
+          Identifier(INT64) [100-105]
+        ColumnAttributeList [106-114]
+          NotNullColumnAttribute [106-114]
+    ColumnDefinition [118-140]
+      Identifier(AlbumTitle) [118-128]
+      SimpleColumnSchema [129-140]
+        PathExpression [129-135]
+          Identifier(STRING) [129-135]
+        TypeParameterList [135-139]
+          MaxLiteral() [136-139]
+    ColumnDefinition [144-158]
+      Identifier(Rating) [144-150]
+      SimpleColumnSchema [151-158]
+        PathExpression [151-158]
+          Identifier(FLOAT64) [151-158]
+    ColumnDefinition [162-231]
+      Identifier(AlbumTitle_Tokens) [162-179]
+      SimpleColumnSchema [180-231]
+        PathExpression [180-189]
+          Identifier(TOKENLIST) [180-189]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [190-224]
+          FunctionCall [194-223]
+            PathExpression [194-211]
+              Identifier(TOKENIZE_FULLTEXT) [194-211]
+            PathExpression [212-222]
+              Identifier(AlbumTitle) [212-222]
+        ColumnAttributeList [225-231]
+          HiddenColumnAttribute [225-231]
+    ColumnDefinition [235-294]
+      Identifier(Rating_Tokens) [235-248]
+      SimpleColumnSchema [249-294]
+        PathExpression [249-258]
+          Identifier(TOKENLIST) [249-258]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [259-287]
+          FunctionCall [263-286]
+            PathExpression [263-278]
+              Identifier(TOKENIZE_NUMBER) [263-278]
+            PathExpression [279-285]
+              Identifier(Rating) [279-285]
+        ColumnAttributeList [288-294]
+          HiddenColumnAttribute [288-294]
+  SpannerTableOptions [297-317]
+    PrimaryKey(ENFORCED) [297-317]
+      PrimaryKeyElementList [308-317]
+        PrimaryKeyElement(ASC) [309-316]
+          Identifier(AlbumId) [309-316]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-indexes#define_an_interleaved_search_index
+CREATE TABLE Singers (
+  SingerId INT64 NOT NULL
+) PRIMARY KEY(SingerId)
+--
+CreateTableStatement [0-72]
+  PathExpression [13-20]
+    Identifier(Singers) [13-20]
+  TableElementList [21-50]
+    ColumnDefinition [25-48]
+      Identifier(SingerId) [25-33]
+      SimpleColumnSchema [34-48]
+        PathExpression [34-39]
+          Identifier(INT64) [34-39]
+        ColumnAttributeList [40-48]
+          NotNullColumnAttribute [40-48]
+  SpannerTableOptions [51-72]
+    PrimaryKey(ENFORCED) [51-72]
+      PrimaryKeyElementList [62-72]
+        PrimaryKeyElement(ASC) [63-71]
+          Identifier(SingerId) [63-71]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-indexes#define_an_interleaved_search_index
+CREATE TABLE Albums (
+  SingerId INT64 NOT NULL,
+  AlbumId STRING(MAX) NOT NULL,
+  AlbumTitle STRING(MAX),
+  AlbumTitle_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN
+) PRIMARY KEY(SingerId, AlbumId),
+INTERLEAVE IN PARENT Singers ON DELETE CASCADE
+--
+CreateTableStatement [0-259]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-180]
+    ColumnDefinition [24-47]
+      Identifier(SingerId) [24-32]
+      SimpleColumnSchema [33-47]
+        PathExpression [33-38]
+          Identifier(INT64) [33-38]
+        ColumnAttributeList [39-47]
+          NotNullColumnAttribute [39-47]
+    ColumnDefinition [51-79]
+      Identifier(AlbumId) [51-58]
+      SimpleColumnSchema [59-79]
+        PathExpression [59-65]
+          Identifier(STRING) [59-65]
+        TypeParameterList [65-69]
+          MaxLiteral() [66-69]
+        ColumnAttributeList [71-79]
+          NotNullColumnAttribute [71-79]
+    ColumnDefinition [83-105]
+      Identifier(AlbumTitle) [83-93]
+      SimpleColumnSchema [94-105]
+        PathExpression [94-100]
+          Identifier(STRING) [94-100]
+        TypeParameterList [100-104]
+          MaxLiteral() [101-104]
+    ColumnDefinition [109-178]
+      Identifier(AlbumTitle_Tokens) [109-126]
+      SimpleColumnSchema [127-178]
+        PathExpression [127-136]
+          Identifier(TOKENLIST) [127-136]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [137-171]
+          FunctionCall [141-170]
+            PathExpression [141-158]
+              Identifier(TOKENIZE_FULLTEXT) [141-158]
+            PathExpression [159-169]
+              Identifier(AlbumTitle) [159-169]
+        ColumnAttributeList [172-178]
+          HiddenColumnAttribute [172-178]
+  SpannerTableOptions [181-259]
+    PrimaryKey(ENFORCED) [181-211]
+      PrimaryKeyElementList [192-211]
+        PrimaryKeyElement(ASC) [193-201]
+          Identifier(SingerId) [193-201]
+        PrimaryKeyElement(ASC) [203-210]
+          Identifier(AlbumId) [203-210]
+    SpannerInterleaveClause [211-259]
+      PathExpression [234-241]
+        Identifier(Singers) [234-241]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-indexes#search-index-sort-order
+CREATE TABLE Albums (
+  AlbumId STRING(MAX) NOT NULL,
+  ReleaseTimestamp INT64 NOT NULL,
+  AlbumName STRING(MAX),
+  AlbumName_Token TOKENLIST AS (TOKEN(AlbumName)) HIDDEN
+) PRIMARY KEY(AlbumId)
+--
+CreateTableStatement [0-193]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-172]
+    ColumnDefinition [24-52]
+      Identifier(AlbumId) [24-31]
+      SimpleColumnSchema [32-52]
+        PathExpression [32-38]
+          Identifier(STRING) [32-38]
+        TypeParameterList [38-42]
+          MaxLiteral() [39-42]
+        ColumnAttributeList [44-52]
+          NotNullColumnAttribute [44-52]
+    ColumnDefinition [56-87]
+      Identifier(ReleaseTimestamp) [56-72]
+      SimpleColumnSchema [73-87]
+        PathExpression [73-78]
+          Identifier(INT64) [73-78]
+        ColumnAttributeList [79-87]
+          NotNullColumnAttribute [79-87]
+    ColumnDefinition [91-112]
+      Identifier(AlbumName) [91-100]
+      SimpleColumnSchema [101-112]
+        PathExpression [101-107]
+          Identifier(STRING) [101-107]
+        TypeParameterList [107-111]
+          MaxLiteral() [108-111]
+    ColumnDefinition [116-170]
+      Identifier(AlbumName_Token) [116-131]
+      SimpleColumnSchema [132-170]
+        PathExpression [132-141]
+          Identifier(TOKENLIST) [132-141]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [142-163]
+          FunctionCall [146-162]
+            PathExpression [146-151]
+              Identifier(TOKEN) [146-151]
+            PathExpression [152-161]
+              Identifier(AlbumName) [152-161]
+        ColumnAttributeList [164-170]
+          HiddenColumnAttribute [164-170]
+  SpannerTableOptions [173-193]
+    PrimaryKey(ENFORCED) [173-193]
+      PrimaryKeyElementList [184-193]
+        PrimaryKeyElement(ASC) [185-192]
+          Identifier(AlbumId) [185-192]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-indexes#search-index-sort-order
+CREATE INDEX AlbumsSecondaryIndex ON Albums(AlbumName, ReleaseTimestamp DESC)
+--
+CreateIndexStatement [0-77]
+  PathExpression [13-33]
+    Identifier(AlbumsSecondaryIndex) [13-33]
+  PathExpression [37-43]
+    Identifier(Albums) [37-43]
+  IndexItemList [43-77]
+    OrderingExpression(ASC) [44-53]
+      PathExpression [44-53]
+        Identifier(AlbumName) [44-53]
+    OrderingExpression(DESC) [55-76]
+      PathExpression [55-71]
+        Identifier(ReleaseTimestamp) [55-71]
diff --git a/parser/testdata/spanner/guides/full-text-search.search-multiple-columns.metadata.json b/parser/testdata/spanner/guides/full-text-search.search-multiple-columns.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.search-multiple-columns.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/full-text-search.search-multiple-columns.test b/parser/testdata/spanner/guides/full-text-search.search-multiple-columns.test
new file mode 100644
index 0000000..31224e7
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.search-multiple-columns.test
@@ -0,0 +1,332 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/full-text-search/search-multiple-columns
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-multiple-columns#perform_a_multi-column_search
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Albums (
+  AlbumId STRING(MAX) NOT NULL,
+  Title STRING(MAX),
+  Studio STRING(MAX),
+  Title_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(Title)) HIDDEN,
+  Studio_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(Studio)) HIDDEN
+) PRIMARY KEY(AlbumId)
+--
+CreateTableStatement [0-246]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-225]
+    ColumnDefinition [24-52]
+      Identifier(AlbumId) [24-31]
+      SimpleColumnSchema [32-52]
+        PathExpression [32-38]
+          Identifier(STRING) [32-38]
+        TypeParameterList [38-42]
+          MaxLiteral() [39-42]
+        ColumnAttributeList [44-52]
+          NotNullColumnAttribute [44-52]
+    ColumnDefinition [56-73]
+      Identifier(Title) [56-61]
+      SimpleColumnSchema [62-73]
+        PathExpression [62-68]
+          Identifier(STRING) [62-68]
+        TypeParameterList [68-72]
+          MaxLiteral() [69-72]
+    ColumnDefinition [77-95]
+      Identifier(Studio) [77-83]
+      SimpleColumnSchema [84-95]
+        PathExpression [84-90]
+          Identifier(STRING) [84-90]
+        TypeParameterList [90-94]
+          MaxLiteral() [91-94]
+    ColumnDefinition [99-158]
+      Identifier(Title_Tokens) [99-111]
+      SimpleColumnSchema [112-158]
+        PathExpression [112-121]
+          Identifier(TOKENLIST) [112-121]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [122-151]
+          FunctionCall [126-150]
+            PathExpression [126-143]
+              Identifier(TOKENIZE_FULLTEXT) [126-143]
+            PathExpression [144-149]
+              Identifier(Title) [144-149]
+        ColumnAttributeList [152-158]
+          HiddenColumnAttribute [152-158]
+    ColumnDefinition [162-223]
+      Identifier(Studio_Tokens) [162-175]
+      SimpleColumnSchema [176-223]
+        PathExpression [176-185]
+          Identifier(TOKENLIST) [176-185]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [186-216]
+          FunctionCall [190-215]
+            PathExpression [190-207]
+              Identifier(TOKENIZE_FULLTEXT) [190-207]
+            PathExpression [208-214]
+              Identifier(Studio) [208-214]
+        ColumnAttributeList [217-223]
+          HiddenColumnAttribute [217-223]
+  SpannerTableOptions [226-246]
+    PrimaryKey(ENFORCED) [226-246]
+      PrimaryKeyElementList [237-246]
+        PrimaryKeyElement(ASC) [238-245]
+          Identifier(AlbumId) [238-245]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-multiple-columns#perform_a_multi-column_search
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH(Title_Tokens, "fifth symphony")
+  AND SEARCH(Studio_Tokens, "Blue Note Studio")
+--
+QueryStatement [0-119]
+  Query [0-119]
+    Select [0-119]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-119]
+        AndExpr [33-119]
+          FunctionCall [33-71]
+            PathExpression [33-39]
+              Identifier(SEARCH) [33-39]
+            PathExpression [40-52]
+              Identifier(Title_Tokens) [40-52]
+            StringLiteral [54-70]
+              StringLiteralComponent("fifth symphony") [54-70]
+          FunctionCall [78-119]
+            PathExpression [78-84]
+              Identifier(SEARCH) [78-84]
+            PathExpression [85-98]
+              Identifier(Studio_Tokens) [85-98]
+            StringLiteral [100-118]
+              StringLiteralComponent("Blue Note Studio") [100-118]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-multiple-columns#perform_a_multi-column_search
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH(Title_Tokens, 'car') AND SEARCH(Studio_Tokens, 'sun')
+--
+QueryStatement [0-93]
+  Query [0-93]
+    Select [0-93]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-93]
+        AndExpr [33-93]
+          FunctionCall [33-60]
+            PathExpression [33-39]
+              Identifier(SEARCH) [33-39]
+            PathExpression [40-52]
+              Identifier(Title_Tokens) [40-52]
+            StringLiteral [54-59]
+              StringLiteralComponent('car') [54-59]
+          FunctionCall [65-93]
+            PathExpression [65-71]
+              Identifier(SEARCH) [65-71]
+            PathExpression [72-85]
+              Identifier(Studio_Tokens) [72-85]
+            StringLiteral [87-92]
+              StringLiteralComponent('sun') [87-92]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-multiple-columns#perform_a_multi-column_search
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH(Title_Tokens, 'car') OR SEARCH(Studio_Tokens, 'sun')
+--
+QueryStatement [0-92]
+  Query [0-92]
+    Select [0-92]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-92]
+        OrExpr [33-92]
+          FunctionCall [33-60]
+            PathExpression [33-39]
+              Identifier(SEARCH) [33-39]
+            PathExpression [40-52]
+              Identifier(Title_Tokens) [40-52]
+            StringLiteral [54-59]
+              StringLiteralComponent('car') [54-59]
+          FunctionCall [64-92]
+            PathExpression [64-70]
+              Identifier(SEARCH) [64-70]
+            PathExpression [71-84]
+              Identifier(Studio_Tokens) [71-84]
+            StringLiteral [86-91]
+              StringLiteralComponent('sun') [86-91]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-multiple-columns#perform_a_multi-column_search
+SELECT AlbumId
+FROM Albums
+WHERE NOT SEARCH(Title_Tokens, 'car')
+--
+QueryStatement [0-64]
+  Query [0-64]
+    Select [0-64]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-64]
+        UnaryExpression(NOT) [33-64]
+          FunctionCall [37-64]
+            PathExpression [37-43]
+              Identifier(SEARCH) [37-43]
+            PathExpression [44-56]
+              Identifier(Title_Tokens) [44-56]
+            StringLiteral [58-63]
+              StringLiteralComponent('car') [58-63]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-multiple-columns#perform_a_multi-column_search
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH(Title_Tokens, '-car')
+--
+QueryStatement [0-61]
+  Query [0-61]
+    Select [0-61]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-61]
+        FunctionCall [33-61]
+          PathExpression [33-39]
+            Identifier(SEARCH) [33-39]
+          PathExpression [40-52]
+            Identifier(Title_Tokens) [40-52]
+          StringLiteral [54-60]
+            StringLiteralComponent('-car') [54-60]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-multiple-columns#perform_a_multi-column_search
+SELECT AlbumId
+FROM Albums
+WHERE (SEARCH(Title_Tokens, 'car') OR SEARCH(Studio_Tokens, 'sun'))
+  AND (SEARCH(Title_Tokens, 'guy') OR SEARCH(Studio_Tokens, electric))
+--
+QueryStatement [0-165]
+  Query [0-165]
+    Select [0-165]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-165]
+        AndExpr [33-165]
+          OrExpr [34-93]
+            FunctionCall [34-61]
+              PathExpression [34-40]
+                Identifier(SEARCH) [34-40]
+              PathExpression [41-53]
+                Identifier(Title_Tokens) [41-53]
+              StringLiteral [55-60]
+                StringLiteralComponent('car') [55-60]
+            FunctionCall [65-93]
+              PathExpression [65-71]
+                Identifier(SEARCH) [65-71]
+              PathExpression [72-85]
+                Identifier(Studio_Tokens) [72-85]
+              StringLiteral [87-92]
+                StringLiteralComponent('sun') [87-92]
+          OrExpr [102-164]
+            FunctionCall [102-129]
+              PathExpression [102-108]
+                Identifier(SEARCH) [102-108]
+              PathExpression [109-121]
+                Identifier(Title_Tokens) [109-121]
+              StringLiteral [123-128]
+                StringLiteralComponent('guy') [123-128]
+            FunctionCall [133-164]
+              PathExpression [133-139]
+                Identifier(SEARCH) [133-139]
+              PathExpression [140-153]
+                Identifier(Studio_Tokens) [140-153]
+              PathExpression [155-163]
+                Identifier(electric) [155-163]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-multiple-columns#perform_a_multi-column_search
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH(Title_Tokens, 'car OR guy')
+--
+QueryStatement [0-67]
+  Query [0-67]
+    Select [0-67]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-67]
+        FunctionCall [33-67]
+          PathExpression [33-39]
+            Identifier(SEARCH) [33-39]
+          PathExpression [40-52]
+            Identifier(Title_Tokens) [40-52]
+          StringLiteral [54-66]
+            StringLiteralComponent('car OR guy') [54-66]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-multiple-columns#perform_a_multi-column_search
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH(Title_Tokens, 'car') OR SEARCH(Title_Tokens, 'guy')
+--
+QueryStatement [0-91]
+  Query [0-91]
+    Select [0-91]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-91]
+        OrExpr [33-91]
+          FunctionCall [33-60]
+            PathExpression [33-39]
+              Identifier(SEARCH) [33-39]
+            PathExpression [40-52]
+              Identifier(Title_Tokens) [40-52]
+            StringLiteral [54-59]
+              StringLiteralComponent('car') [54-59]
+          FunctionCall [64-91]
+            PathExpression [64-70]
+              Identifier(SEARCH) [64-70]
+            PathExpression [71-83]
+              Identifier(Title_Tokens) [71-83]
+            StringLiteral [85-90]
+              StringLiteralComponent('guy') [85-90]
diff --git a/parser/testdata/spanner/guides/full-text-search.search-query-enhancement.spanner.sql b/parser/testdata/spanner/guides/full-text-search.search-query-enhancement.spanner.sql
new file mode 100644
index 0000000..088651c
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.search-query-enhancement.spanner.sql
@@ -0,0 +1,29 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/full-text-search/search-query-enhancement
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-query-enhancement#create_a_custom_dictionary_table
+# id d69ff196cee4: table OPTIONS after PRIMARY KEY (full-text-search.search-query-enhancement)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected keyword INTERLEAVE but got keyword OPTIONS [at 5:1]
+CREATE TABLE MyCustomDictionary (
+  Key STRING(MAX) NOT NULL,
+  Value ARRAY NOT NULL,
+) PRIMARY KEY(Key),
+OPTIONS (fulltext_dictionary_table = true);
+
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-query-enhancement#fulltext_dictionary_staleness_table_option
+# id c77ee47eac7e: table OPTIONS after PRIMARY KEY (full-text-search.search-query-enhancement)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected keyword INTERLEAVE but got keyword OPTIONS [at 5:1]
+CREATE TABLE MyCustomDictionary (
+  Key STRING(MAX) NOT NULL,
+  Value ARRAY NOT NULL,
+) PRIMARY KEY(Key),
+OPTIONS (
+  fulltext_dictionary_table = true,
+  fulltext_dictionary_staleness = '5s'
+);
diff --git a/parser/testdata/spanner/guides/full-text-search.search-query-enhancement.test b/parser/testdata/spanner/guides/full-text-search.search-query-enhancement.test
new file mode 100644
index 0000000..b5239e1
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.search-query-enhancement.test
@@ -0,0 +1,267 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/full-text-search/search-query-enhancement
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-query-enhancement#enhanced-query
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH(AlbumTitle_Tokens, 'hotl cal', enhance_query=>true)
+--
+QueryStatement [0-91]
+  Query [0-91]
+    Select [0-91]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-91]
+        FunctionCall [33-91]
+          PathExpression [33-39]
+            Identifier(SEARCH) [33-39]
+          PathExpression [40-57]
+            Identifier(AlbumTitle_Tokens) [40-57]
+          StringLiteral [59-69]
+            StringLiteralComponent('hotl cal') [59-69]
+          NamedArgument [71-90]
+            Identifier(enhance_query) [71-84]
+            BooleanLiteral(true) [86-90]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-query-enhancement#create_a_custom_dictionary_table
+INSERT INTO MyCustomDictionary (Key, Value) VALUES
+('album', ['vinyl', 'cassette']),
+('edm', ['electronic dance music'])
+--
+InsertStatement [0-120]
+  PathExpression [12-30]
+    Identifier(MyCustomDictionary) [12-30]
+  ColumnList [31-43]
+    Identifier(Key) [32-35]
+    Identifier(Value) [37-42]
+  InsertValuesRowList [44-120]
+    InsertValuesRow [51-83]
+      StringLiteral [52-59]
+        StringLiteralComponent('album') [52-59]
+      ArrayConstructor [61-82]
+        StringLiteral [62-69]
+          StringLiteralComponent('vinyl') [62-69]
+        StringLiteral [71-81]
+          StringLiteralComponent('cassette') [71-81]
+    InsertValuesRow [85-120]
+      StringLiteral [86-91]
+        StringLiteralComponent('edm') [86-91]
+      ArrayConstructor [93-119]
+        StringLiteral [94-118]
+          StringLiteralComponent('electronic dance music') [94-118]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-query-enhancement#create_a_custom_dictionary_table
+INSERT INTO MyCustomDictionary (Key, Value)
+-- 1. Insert album -> vinyl, cassette
+SELECT 'album', ['vinyl', 'cassette']
+UNION ALL
+-- 2. Insert vinyl -> album and cassette -> album
+SELECT syn, ['album']
+FROM UNNEST(['vinyl', 'cassette']) AS syn
+--
+InsertStatement [0-243]
+  PathExpression [12-30]
+    Identifier(MyCustomDictionary) [12-30]
+  ColumnList [31-43]
+    Identifier(Key) [32-35]
+    Identifier(Value) [37-42]
+  Query [82-243]
+    SetOperation(UNION ALL) [82-243]
+      SetOperationMetadataList [120-129]
+        SetOperationMetadata [120-129]
+          SetOperationType [120-125]
+          SetOperationAllOrDistinct [126-129]
+      Select [82-119]
+        SelectList [89-119]
+          SelectColumn [89-96]
+            StringLiteral [89-96]
+              StringLiteralComponent('album') [89-96]
+          SelectColumn [98-119]
+            ArrayConstructor [98-119]
+              StringLiteral [99-106]
+                StringLiteralComponent('vinyl') [99-106]
+              StringLiteral [108-118]
+                StringLiteralComponent('cassette') [108-118]
+      Select [180-243]
+        SelectList [187-201]
+          SelectColumn [187-190]
+            PathExpression [187-190]
+              Identifier(syn) [187-190]
+          SelectColumn [192-201]
+            ArrayConstructor [192-201]
+              StringLiteral [193-200]
+                StringLiteralComponent('album') [193-200]
+        FromClause [202-243]
+          TablePathExpression [207-243]
+            UnnestExpression [207-236]
+              ExpressionWithOptAlias [214-235]
+                ArrayConstructor [214-235]
+                  StringLiteral [215-222]
+                    StringLiteralComponent('vinyl') [215-222]
+                  StringLiteral [224-234]
+                    StringLiteralComponent('cassette') [224-234]
+            Alias [237-243]
+              Identifier(syn) [240-243]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-query-enhancement#use_a_custom_dictionary_in_search_queries
+SELECT MessageId, Body
+FROM Messages
+WHERE SEARCH(Body_Tokens, 'album', dictionary=>'MyCustomDictionary')
+--
+QueryStatement [0-105]
+  Query [0-105]
+    Select [0-105]
+      SelectList [7-22]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(MessageId) [7-16]
+        SelectColumn [18-22]
+          PathExpression [18-22]
+            Identifier(Body) [18-22]
+      FromClause [23-36]
+        TablePathExpression [28-36]
+          PathExpression [28-36]
+            Identifier(Messages) [28-36]
+      WhereClause [37-105]
+        FunctionCall [43-105]
+          PathExpression [43-49]
+            Identifier(SEARCH) [43-49]
+          PathExpression [50-61]
+            Identifier(Body_Tokens) [50-61]
+          StringLiteral [63-70]
+            StringLiteralComponent('album') [63-70]
+          NamedArgument [72-104]
+            Identifier(dictionary) [72-82]
+            StringLiteral [84-104]
+              StringLiteralComponent('MyCustomDictionary') [84-104]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-query-enhancement#use_a_custom_dictionary_in_search_queries
+SELECT MessageId, Body
+FROM Messages
+WHERE SEARCH(Body_Tokens, 'edm', dictionary=>'MyCustomDictionary')
+--
+QueryStatement [0-103]
+  Query [0-103]
+    Select [0-103]
+      SelectList [7-22]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(MessageId) [7-16]
+        SelectColumn [18-22]
+          PathExpression [18-22]
+            Identifier(Body) [18-22]
+      FromClause [23-36]
+        TablePathExpression [28-36]
+          PathExpression [28-36]
+            Identifier(Messages) [28-36]
+      WhereClause [37-103]
+        FunctionCall [43-103]
+          PathExpression [43-49]
+            Identifier(SEARCH) [43-49]
+          PathExpression [50-61]
+            Identifier(Body_Tokens) [50-61]
+          StringLiteral [63-68]
+            StringLiteralComponent('edm') [63-68]
+          NamedArgument [70-102]
+            Identifier(dictionary) [70-80]
+            StringLiteral [82-102]
+              StringLiteralComponent('MyCustomDictionary') [82-102]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-query-enhancement#fulltext_dictionary_staleness_table_option
+ALTER TABLE MyCustomDictionary SET OPTIONS (
+  fulltext_dictionary_staleness = '60s'
+)
+--
+AlterTableStatement [0-86]
+  PathExpression [12-30]
+    Identifier(MyCustomDictionary) [12-30]
+  AlterActionList [31-86]
+    SetOptionsAction [31-86]
+      OptionsList [43-86]
+        OptionsEntry [47-84]
+          Identifier(fulltext_dictionary_staleness) [47-76]
+          StringLiteral [79-84]
+            StringLiteralComponent('60s') [79-84]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-query-enhancement#fulltext_dictionary_staleness_query_hint
+@{fulltext_dictionary_staleness="0s"}
+SELECT MessageId, Body
+FROM Messages
+WHERE SEARCH(Body_Tokens, 'Bill', dictionary=>'MyCustomDictionary')
+--
+HintedStatement [0-142]
+  Hint [0-37]
+    HintEntry [2-36]
+      Identifier(fulltext_dictionary_staleness) [2-31]
+      StringLiteral [32-36]
+        StringLiteralComponent("0s") [32-36]
+  QueryStatement [38-142]
+    Query [38-142]
+      Select [38-142]
+        SelectList [45-60]
+          SelectColumn [45-54]
+            PathExpression [45-54]
+              Identifier(MessageId) [45-54]
+          SelectColumn [56-60]
+            PathExpression [56-60]
+              Identifier(Body) [56-60]
+        FromClause [61-74]
+          TablePathExpression [66-74]
+            PathExpression [66-74]
+              Identifier(Messages) [66-74]
+        WhereClause [75-142]
+          FunctionCall [81-142]
+            PathExpression [81-87]
+              Identifier(SEARCH) [81-87]
+            PathExpression [88-99]
+              Identifier(Body_Tokens) [88-99]
+            StringLiteral [101-107]
+              StringLiteralComponent('Bill') [101-107]
+            NamedArgument [109-141]
+              Identifier(dictionary) [109-119]
+              StringLiteral [121-141]
+                StringLiteralComponent('MyCustomDictionary') [121-141]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/search-query-enhancement#combining_custom_dictionaries_with_enhanced_query
+SELECT MessageId, Body
+FROM Messages
+WHERE SEARCH(Body_Tokens, 'album', enhance_query=>true, dictionary=>'MyCustomDictionary')
+--
+QueryStatement [0-126]
+  Query [0-126]
+    Select [0-126]
+      SelectList [7-22]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(MessageId) [7-16]
+        SelectColumn [18-22]
+          PathExpression [18-22]
+            Identifier(Body) [18-22]
+      FromClause [23-36]
+        TablePathExpression [28-36]
+          PathExpression [28-36]
+            Identifier(Messages) [28-36]
+      WhereClause [37-126]
+        FunctionCall [43-126]
+          PathExpression [43-49]
+            Identifier(SEARCH) [43-49]
+          PathExpression [50-61]
+            Identifier(Body_Tokens) [50-61]
+          StringLiteral [63-70]
+            StringLiteralComponent('album') [63-70]
+          NamedArgument [72-91]
+            Identifier(enhance_query) [72-85]
+            BooleanLiteral(true) [87-91]
+          NamedArgument [93-125]
+            Identifier(dictionary) [93-103]
+            StringLiteral [105-125]
+              StringLiteralComponent('MyCustomDictionary') [105-125]
diff --git a/parser/testdata/spanner/guides/full-text-search.substring-search.metadata.json b/parser/testdata/spanner/guides/full-text-search.substring-search.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.substring-search.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/full-text-search.substring-search.test b/parser/testdata/spanner/guides/full-text-search.substring-search.test
new file mode 100644
index 0000000..8f1165a
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.substring-search.test
@@ -0,0 +1,76 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/full-text-search/substring-search
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/full-text-search/substring-search#
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Albums (
+AlbumId STRING(MAX) NOT NULL,
+AlbumTitle STRING(MAX),
+AlbumTitle_Tokens TOKENLIST AS (TOKENIZE_SUBSTRING(AlbumTitle)) HIDDEN
+) PRIMARY KEY(AlbumId)
+--
+CreateTableStatement [0-169]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-148]
+    ColumnDefinition [22-50]
+      Identifier(AlbumId) [22-29]
+      SimpleColumnSchema [30-50]
+        PathExpression [30-36]
+          Identifier(STRING) [30-36]
+        TypeParameterList [36-40]
+          MaxLiteral() [37-40]
+        ColumnAttributeList [42-50]
+          NotNullColumnAttribute [42-50]
+    ColumnDefinition [52-74]
+      Identifier(AlbumTitle) [52-62]
+      SimpleColumnSchema [63-74]
+        PathExpression [63-69]
+          Identifier(STRING) [63-69]
+        TypeParameterList [69-73]
+          MaxLiteral() [70-73]
+    ColumnDefinition [76-146]
+      Identifier(AlbumTitle_Tokens) [76-93]
+      SimpleColumnSchema [94-146]
+        PathExpression [94-103]
+          Identifier(TOKENLIST) [94-103]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [104-139]
+          FunctionCall [108-138]
+            PathExpression [108-126]
+              Identifier(TOKENIZE_SUBSTRING) [108-126]
+            PathExpression [127-137]
+              Identifier(AlbumTitle) [127-137]
+        ColumnAttributeList [140-146]
+          HiddenColumnAttribute [140-146]
+  SpannerTableOptions [149-169]
+    PrimaryKey(ENFORCED) [149-169]
+      PrimaryKeyElementList [160-169]
+        PrimaryKeyElement(ASC) [161-168]
+          Identifier(AlbumId) [161-168]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/substring-search#
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH_SUBSTRING(AlbumTitle_Tokens, 'happ')
+--
+QueryStatement [0-76]
+  Query [0-76]
+    Select [0-76]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-76]
+        FunctionCall [33-76]
+          PathExpression [33-49]
+            Identifier(SEARCH_SUBSTRING) [33-49]
+          PathExpression [50-67]
+            Identifier(AlbumTitle_Tokens) [50-67]
+          StringLiteral [69-75]
+            StringLiteralComponent('happ') [69-75]
diff --git a/parser/testdata/spanner/guides/full-text-search.tokenization.metadata.json b/parser/testdata/spanner/guides/full-text-search.tokenization.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.tokenization.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/full-text-search.tokenization.test b/parser/testdata/spanner/guides/full-text-search.tokenization.test
new file mode 100644
index 0000000..7d969a2
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.tokenization.test
@@ -0,0 +1,171 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/full-text-search/tokenization
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/full-text-search/tokenization#tokenizers
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+TOKENIZE_SUBSTRING('hello world', ngram_size_min=>4, ngram_size_max=>6)
+--
+FunctionCall [0-71]
+  PathExpression [0-18]
+    Identifier(TOKENIZE_SUBSTRING) [0-18]
+  StringLiteral [19-32]
+    StringLiteralComponent('hello world') [19-32]
+  NamedArgument [34-51]
+    Identifier(ngram_size_min) [34-48]
+    IntLiteral(4) [50-51]
+  NamedArgument [53-70]
+    Identifier(ngram_size_max) [53-67]
+    IntLiteral(6) [69-70]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/tokenization#tokenizers
+[mode=expression]
+TOKENIZE_NGRAMS("Big Time", ngram_size_min=>4, ngram_size_max=>4)
+--
+FunctionCall [0-65]
+  PathExpression [0-15]
+    Identifier(TOKENIZE_NGRAMS) [0-15]
+  StringLiteral [16-26]
+    StringLiteralComponent("Big Time") [16-26]
+  NamedArgument [28-45]
+    Identifier(ngram_size_min) [28-42]
+    IntLiteral(4) [44-45]
+  NamedArgument [47-64]
+    Identifier(ngram_size_max) [47-61]
+    IntLiteral(4) [63-64]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/tokenization#tokenizers
+[mode=expression]
+TOKENIZE_BOOL(true)
+--
+FunctionCall [0-19]
+  PathExpression [0-13]
+    Identifier(TOKENIZE_BOOL) [0-13]
+  BooleanLiteral(true) [14-18]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/tokenization#tokenizers
+CREATE TABLE Albums (
+  AlbumId STRING(MAX) NOT NULL,
+  AlbumTitle STRING(MAX),
+  Rating FLOAT64,
+  AlbumTitle_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN,
+  Rating_Tokens TOKENLIST AS (TOKENIZE_NUMBER(Rating)) HIDDEN
+) PRIMARY KEY(AlbumId)
+--
+CreateTableStatement [0-255]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-234]
+    ColumnDefinition [24-52]
+      Identifier(AlbumId) [24-31]
+      SimpleColumnSchema [32-52]
+        PathExpression [32-38]
+          Identifier(STRING) [32-38]
+        TypeParameterList [38-42]
+          MaxLiteral() [39-42]
+        ColumnAttributeList [44-52]
+          NotNullColumnAttribute [44-52]
+    ColumnDefinition [56-78]
+      Identifier(AlbumTitle) [56-66]
+      SimpleColumnSchema [67-78]
+        PathExpression [67-73]
+          Identifier(STRING) [67-73]
+        TypeParameterList [73-77]
+          MaxLiteral() [74-77]
+    ColumnDefinition [82-96]
+      Identifier(Rating) [82-88]
+      SimpleColumnSchema [89-96]
+        PathExpression [89-96]
+          Identifier(FLOAT64) [89-96]
+    ColumnDefinition [100-169]
+      Identifier(AlbumTitle_Tokens) [100-117]
+      SimpleColumnSchema [118-169]
+        PathExpression [118-127]
+          Identifier(TOKENLIST) [118-127]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [128-162]
+          FunctionCall [132-161]
+            PathExpression [132-149]
+              Identifier(TOKENIZE_FULLTEXT) [132-149]
+            PathExpression [150-160]
+              Identifier(AlbumTitle) [150-160]
+        ColumnAttributeList [163-169]
+          HiddenColumnAttribute [163-169]
+    ColumnDefinition [173-232]
+      Identifier(Rating_Tokens) [173-186]
+      SimpleColumnSchema [187-232]
+        PathExpression [187-196]
+          Identifier(TOKENLIST) [187-196]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [197-225]
+          FunctionCall [201-224]
+            PathExpression [201-216]
+              Identifier(TOKENIZE_NUMBER) [201-216]
+            PathExpression [217-223]
+              Identifier(Rating) [217-223]
+        ColumnAttributeList [226-232]
+          HiddenColumnAttribute [226-232]
+  SpannerTableOptions [235-255]
+    PrimaryKey(ENFORCED) [235-255]
+      PrimaryKeyElementList [246-255]
+        PrimaryKeyElement(ASC) [247-254]
+          Identifier(AlbumId) [247-254]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/tokenization#tokenize_plain_text_or_html_content
+CREATE TABLE Albums (
+  AlbumId STRING(MAX) NOT NULL,
+  AlbumTitle STRING(MAX),
+  AlbumTitle_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN
+) PRIMARY KEY(AlbumId)
+--
+CreateTableStatement [0-174]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-153]
+    ColumnDefinition [24-52]
+      Identifier(AlbumId) [24-31]
+      SimpleColumnSchema [32-52]
+        PathExpression [32-38]
+          Identifier(STRING) [32-38]
+        TypeParameterList [38-42]
+          MaxLiteral() [39-42]
+        ColumnAttributeList [44-52]
+          NotNullColumnAttribute [44-52]
+    ColumnDefinition [56-78]
+      Identifier(AlbumTitle) [56-66]
+      SimpleColumnSchema [67-78]
+        PathExpression [67-73]
+          Identifier(STRING) [67-73]
+        TypeParameterList [73-77]
+          MaxLiteral() [74-77]
+    ColumnDefinition [82-151]
+      Identifier(AlbumTitle_Tokens) [82-99]
+      SimpleColumnSchema [100-151]
+        PathExpression [100-109]
+          Identifier(TOKENLIST) [100-109]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [110-144]
+          FunctionCall [114-143]
+            PathExpression [114-131]
+              Identifier(TOKENIZE_FULLTEXT) [114-131]
+            PathExpression [132-142]
+              Identifier(AlbumTitle) [132-142]
+        ColumnAttributeList [145-151]
+          HiddenColumnAttribute [145-151]
+  SpannerTableOptions [154-174]
+    PrimaryKey(ENFORCED) [154-174]
+      PrimaryKeyElementList [165-174]
+        PrimaryKeyElement(ASC) [166-173]
+          Identifier(AlbumId) [166-173]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/tokenization#tokenize_plain_text_or_html_content
+CREATE SEARCH INDEX AlbumsIndex ON Albums(AlbumTitle_Tokens)
+--
+CreateIndexStatement(SEARCH) [0-60]
+  PathExpression [20-31]
+    Identifier(AlbumsIndex) [20-31]
+  PathExpression [35-41]
+    Identifier(Albums) [35-41]
+  IndexItemList [41-60]
+    OrderingExpression(ASC) [42-59]
+      PathExpression [42-59]
+        Identifier(AlbumTitle_Tokens) [42-59]
diff --git a/parser/testdata/spanner/guides/full-text-search.top-k-pattern-matching.metadata.json b/parser/testdata/spanner/guides/full-text-search.top-k-pattern-matching.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.top-k-pattern-matching.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/full-text-search.top-k-pattern-matching.spanner.sql b/parser/testdata/spanner/guides/full-text-search.top-k-pattern-matching.spanner.sql
new file mode 100644
index 0000000..6253ff4
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.top-k-pattern-matching.spanner.sql
@@ -0,0 +1,21 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/full-text-search/top-k-pattern-matching
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/full-text-search/top-k-pattern-matching#create_search_indexes_for_top-k_matches
+# id f7df4248d043: search index clauses (ORDER BY, WHERE, INTERLEAVE IN) (full-text-search.top-k-pattern-matching)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ";" or end of input but got keyword ORDER [at 4:1]
+CREATE SEARCH INDEX AlbumsRecordTimestampIndex
+ON Albums(AlbumTitle_Tokens, SingerId_Tokens)
+STORING (ListenTimestamp)
+ORDER BY RecordTimestamp DESC
+
+CREATE SEARCH INDEX AlbumsReleaseTimestampIndex
+ON Albums(AlbumTitle_Tokens)
+STORING (ListenTimestamp)
+ORDER BY ReleaseTimestamp DESC;
diff --git a/parser/testdata/spanner/guides/full-text-search.top-k-pattern-matching.test b/parser/testdata/spanner/guides/full-text-search.top-k-pattern-matching.test
new file mode 100644
index 0000000..7dfed8c
--- /dev/null
+++ b/parser/testdata/spanner/guides/full-text-search.top-k-pattern-matching.test
@@ -0,0 +1,211 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/full-text-search/top-k-pattern-matching
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/full-text-search/top-k-pattern-matching#create_search_indexes_for_top-k_matches
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Albums (
+  AlbumId STRING(MAX) NOT NULL,
+  RecordTimestamp INT64 NOT NULL,
+  ReleaseTimestamp INT64 NOT NULL,
+  ListenTimestamp INT64 NOT NULL,
+  AlbumTitle STRING(MAX),
+  AlbumTitle_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN
+) PRIMARY KEY(AlbumId)
+--
+CreateTableStatement [0-277]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-256]
+    ColumnDefinition [24-52]
+      Identifier(AlbumId) [24-31]
+      SimpleColumnSchema [32-52]
+        PathExpression [32-38]
+          Identifier(STRING) [32-38]
+        TypeParameterList [38-42]
+          MaxLiteral() [39-42]
+        ColumnAttributeList [44-52]
+          NotNullColumnAttribute [44-52]
+    ColumnDefinition [56-86]
+      Identifier(RecordTimestamp) [56-71]
+      SimpleColumnSchema [72-86]
+        PathExpression [72-77]
+          Identifier(INT64) [72-77]
+        ColumnAttributeList [78-86]
+          NotNullColumnAttribute [78-86]
+    ColumnDefinition [90-121]
+      Identifier(ReleaseTimestamp) [90-106]
+      SimpleColumnSchema [107-121]
+        PathExpression [107-112]
+          Identifier(INT64) [107-112]
+        ColumnAttributeList [113-121]
+          NotNullColumnAttribute [113-121]
+    ColumnDefinition [125-155]
+      Identifier(ListenTimestamp) [125-140]
+      SimpleColumnSchema [141-155]
+        PathExpression [141-146]
+          Identifier(INT64) [141-146]
+        ColumnAttributeList [147-155]
+          NotNullColumnAttribute [147-155]
+    ColumnDefinition [159-181]
+      Identifier(AlbumTitle) [159-169]
+      SimpleColumnSchema [170-181]
+        PathExpression [170-176]
+          Identifier(STRING) [170-176]
+        TypeParameterList [176-180]
+          MaxLiteral() [177-180]
+    ColumnDefinition [185-254]
+      Identifier(AlbumTitle_Tokens) [185-202]
+      SimpleColumnSchema [203-254]
+        PathExpression [203-212]
+          Identifier(TOKENLIST) [203-212]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [213-247]
+          FunctionCall [217-246]
+            PathExpression [217-234]
+              Identifier(TOKENIZE_FULLTEXT) [217-234]
+            PathExpression [235-245]
+              Identifier(AlbumTitle) [235-245]
+        ColumnAttributeList [248-254]
+          HiddenColumnAttribute [248-254]
+  SpannerTableOptions [257-277]
+    PrimaryKey(ENFORCED) [257-277]
+      PrimaryKeyElementList [268-277]
+        PrimaryKeyElement(ASC) [269-276]
+          Identifier(AlbumId) [269-276]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/top-k-pattern-matching#query_search_indexes_for_top-k_matches
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH(AlbumTitle_Tokens, 'happy')
+ORDER BY RecordTimestamp DESC
+LIMIT 10
+--
+QueryStatement [0-106]
+  Query [0-106]
+    Select [0-67]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-67]
+        FunctionCall [33-67]
+          PathExpression [33-39]
+            Identifier(SEARCH) [33-39]
+          PathExpression [40-57]
+            Identifier(AlbumTitle_Tokens) [40-57]
+          StringLiteral [59-66]
+            StringLiteralComponent('happy') [59-66]
+    OrderBy [68-97]
+      OrderingExpression(DESC) [77-97]
+        PathExpression [77-92]
+          Identifier(RecordTimestamp) [77-92]
+    LimitOffset [98-106]
+      Limit [98-106]
+        IntLiteral(10) [104-106]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/top-k-pattern-matching#query_search_indexes_for_top-k_matches
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH(AlbumTitle_Tokens, 'happy')
+ORDER BY ReleaseTimestamp DESC
+LIMIT 10
+--
+QueryStatement [0-107]
+  Query [0-107]
+    Select [0-67]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-67]
+        FunctionCall [33-67]
+          PathExpression [33-39]
+            Identifier(SEARCH) [33-39]
+          PathExpression [40-57]
+            Identifier(AlbumTitle_Tokens) [40-57]
+          StringLiteral [59-66]
+            StringLiteralComponent('happy') [59-66]
+    OrderBy [68-98]
+      OrderingExpression(DESC) [77-98]
+        PathExpression [77-93]
+          Identifier(ReleaseTimestamp) [77-93]
+    LimitOffset [99-107]
+      Limit [99-107]
+        IntLiteral(10) [105-107]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/top-k-pattern-matching#query_search_indexes_for_top-k_matches
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH(AlbumTitle_Tokens, 'happy')
+ORDER BY ListenTimestamp DESC
+LIMIT 10
+--
+QueryStatement [0-106]
+  Query [0-106]
+    Select [0-67]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-67]
+        FunctionCall [33-67]
+          PathExpression [33-39]
+            Identifier(SEARCH) [33-39]
+          PathExpression [40-57]
+            Identifier(AlbumTitle_Tokens) [40-57]
+          StringLiteral [59-66]
+            StringLiteralComponent('happy') [59-66]
+    OrderBy [68-97]
+      OrderingExpression(DESC) [77-97]
+        PathExpression [77-92]
+          Identifier(ListenTimestamp) [77-92]
+    LimitOffset [98-106]
+      Limit [98-106]
+        IntLiteral(10) [104-106]
+==
+# https://docs.cloud.google.com/spanner/docs/full-text-search/top-k-pattern-matching#query_search_indexes_for_top-k_matches
+SELECT AlbumId
+FROM Albums
+WHERE SEARCH(AlbumTitle_Tokens, 'happy')
+ORDER BY RecordTimestamp ASC
+LIMIT 10
+--
+QueryStatement [0-105]
+  Query [0-105]
+    Select [0-67]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-67]
+        FunctionCall [33-67]
+          PathExpression [33-39]
+            Identifier(SEARCH) [33-39]
+          PathExpression [40-57]
+            Identifier(AlbumTitle_Tokens) [40-57]
+          StringLiteral [59-66]
+            StringLiteralComponent('happy') [59-66]
+    OrderBy [68-96]
+      OrderingExpression(ASC EXPLICITLY) [77-96]
+        PathExpression [77-92]
+          Identifier(RecordTimestamp) [77-92]
+    LimitOffset [97-105]
+      Limit [97-105]
+        IntLiteral(10) [103-105]
diff --git a/parser/testdata/spanner/guides/generated-column.how-to.spanner.sql b/parser/testdata/spanner/guides/generated-column.how-to.spanner.sql
new file mode 100644
index 0000000..3f8c9f8
--- /dev/null
+++ b/parser/testdata/spanner/guides/generated-column.how-to.spanner.sql
@@ -0,0 +1,33 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/generated-column/how-to
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/generated-column/how-to#modify-generated-column
+# id 22b0ba359fa8: ALTER COLUMN to a non-stored generated column (generated-column.how-to)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected keyword STORED but got end of statement [at 2:67]
+ALTER TABLE Users ALTER COLUMN FullName STRING(100)
+AS (ARRAY_TO_STRING(ARRAY_TO_STRING([LastName, FirstName ], " ")));
+
+# https://docs.cloud.google.com/spanner/docs/generated-column/how-to#primary-key-generated-column
+# id adb118abcad0: generated column after NOT NULL (generated-column.how-to)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ")" or "," but got keyword AS [at 3:3]
+CREATE TABLE UserInfoLog (
+  ShardId INT64 NOT NULL
+  AS (MOD(UserId, 2048)) STORED,
+  UserId INT64 NOT NULL,
+  FullName STRING(1024) NOT NULL,
+) PRIMARY KEY (ShardId, UserId);
+
+# https://docs.cloud.google.com/spanner/docs/generated-column/how-to#primary-key-generated-column
+# id 26a8b44f4976: generated column after NOT NULL (generated-column.how-to)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ")" or "," but got keyword AS [at 3:3]
+CREATE TABLE Students (
+  StudentId INT64 NOT NULL
+  AS (INT64(StudentInfo.id)) STORED,
+  StudentInfo JSON NOT NULL,
+) PRIMARY KEY (StudentId);
diff --git a/parser/testdata/spanner/guides/generated-column.how-to.test b/parser/testdata/spanner/guides/generated-column.how-to.test
new file mode 100644
index 0000000..d154690
--- /dev/null
+++ b/parser/testdata/spanner/guides/generated-column.how-to.test
@@ -0,0 +1,459 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/generated-column/how-to
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/generated-column/how-to#create-table
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Users (
+Id STRING(20) NOT NULL,
+FirstName STRING(50),
+LastName STRING(50),
+Age INT64 NOT NULL,
+FullName STRING(100) AS (FirstName || ' ' || LastName) STORED
+) PRIMARY KEY (Id)
+--
+CreateTableStatement [0-188]
+  PathExpression [13-18]
+    Identifier(Users) [13-18]
+  TableElementList [19-171]
+    ColumnDefinition [21-43]
+      Identifier(Id) [21-23]
+      SimpleColumnSchema [24-43]
+        PathExpression [24-30]
+          Identifier(STRING) [24-30]
+        TypeParameterList [30-33]
+          IntLiteral(20) [31-33]
+        ColumnAttributeList [35-43]
+          NotNullColumnAttribute [35-43]
+    ColumnDefinition [45-65]
+      Identifier(FirstName) [45-54]
+      SimpleColumnSchema [55-65]
+        PathExpression [55-61]
+          Identifier(STRING) [55-61]
+        TypeParameterList [61-64]
+          IntLiteral(50) [62-64]
+    ColumnDefinition [67-86]
+      Identifier(LastName) [67-75]
+      SimpleColumnSchema [76-86]
+        PathExpression [76-82]
+          Identifier(STRING) [76-82]
+        TypeParameterList [82-85]
+          IntLiteral(50) [83-85]
+    ColumnDefinition [88-106]
+      Identifier(Age) [88-91]
+      SimpleColumnSchema [92-106]
+        PathExpression [92-97]
+          Identifier(INT64) [92-97]
+        ColumnAttributeList [98-106]
+          NotNullColumnAttribute [98-106]
+    ColumnDefinition [108-169]
+      Identifier(FullName) [108-116]
+      SimpleColumnSchema [117-169]
+        PathExpression [117-123]
+          Identifier(STRING) [117-123]
+        TypeParameterList [123-127]
+          IntLiteral(100) [124-127]
+        GeneratedColumnInfo(generated_mode=ALWAYS, stored_mode=STORED) [129-169]
+          BinaryExpression(||) [133-161]
+            BinaryExpression(||) [133-149]
+              PathExpression [133-142]
+                Identifier(FirstName) [133-142]
+              StringLiteral [146-149]
+                StringLiteralComponent(' ') [146-149]
+            PathExpression [153-161]
+              Identifier(LastName) [153-161]
+  SpannerTableOptions [172-188]
+    PrimaryKey(ENFORCED) [172-188]
+      PrimaryKeyElementList [184-188]
+        PrimaryKeyElement(ASC) [185-187]
+          Identifier(Id) [185-187]
+==
+# https://docs.cloud.google.com/spanner/docs/generated-column/how-to#create-table
+SELECT Id, FullName
+FROM Users
+--
+QueryStatement [0-30]
+  Query [0-30]
+    Select [0-30]
+      SelectList [7-19]
+        SelectColumn [7-9]
+          PathExpression [7-9]
+            Identifier(Id) [7-9]
+        SelectColumn [11-19]
+          PathExpression [11-19]
+            Identifier(FullName) [11-19]
+      FromClause [20-30]
+        TablePathExpression [25-30]
+          PathExpression [25-30]
+            Identifier(Users) [25-30]
+==
+# https://docs.cloud.google.com/spanner/docs/generated-column/how-to#create-table
+SELECT Id, ARRAY_TO_STRING([FirstName, LastName], " ") as FullName
+FROM Users
+--
+QueryStatement [0-77]
+  Query [0-77]
+    Select [0-77]
+      SelectList [7-66]
+        SelectColumn [7-9]
+          PathExpression [7-9]
+            Identifier(Id) [7-9]
+        SelectColumn [11-66]
+          FunctionCall [11-54]
+            PathExpression [11-26]
+              Identifier(ARRAY_TO_STRING) [11-26]
+            ArrayConstructor [27-48]
+              PathExpression [28-37]
+                Identifier(FirstName) [28-37]
+              PathExpression [39-47]
+                Identifier(LastName) [39-47]
+            StringLiteral [50-53]
+              StringLiteralComponent(" ") [50-53]
+          Alias [55-66]
+            Identifier(FullName) [58-66]
+      FromClause [67-77]
+        TablePathExpression [72-77]
+          PathExpression [72-77]
+            Identifier(Users) [72-77]
+==
+# https://docs.cloud.google.com/spanner/docs/generated-column/how-to#create-index
+CREATE INDEX UsersByFullName ON Users (FullName)
+--
+CreateIndexStatement [0-48]
+  PathExpression [13-28]
+    Identifier(UsersByFullName) [13-28]
+  PathExpression [32-37]
+    Identifier(Users) [32-37]
+  IndexItemList [38-48]
+    OrderingExpression(ASC) [39-47]
+      PathExpression [39-47]
+        Identifier(FullName) [39-47]
+==
+# https://docs.cloud.google.com/spanner/docs/generated-column/how-to#expression-index
+CREATE TABLE Venues (
+  Id INT64 NOT NULL,
+  VenueData JSON
+) PRIMARY KEY (Id)
+--
+CreateTableStatement [0-78]
+  PathExpression [13-19]
+    Identifier(Venues) [13-19]
+  TableElementList [20-61]
+    ColumnDefinition [24-41]
+      Identifier(Id) [24-26]
+      SimpleColumnSchema [27-41]
+        PathExpression [27-32]
+          Identifier(INT64) [27-32]
+        ColumnAttributeList [33-41]
+          NotNullColumnAttribute [33-41]
+    ColumnDefinition [45-59]
+      Identifier(VenueData) [45-54]
+      SimpleColumnSchema [55-59]
+        PathExpression [55-59]
+          Identifier(JSON) [55-59]
+  SpannerTableOptions [62-78]
+    PrimaryKey(ENFORCED) [62-78]
+      PrimaryKeyElementList [74-78]
+        PrimaryKeyElement(ASC) [75-77]
+          Identifier(Id) [75-77]
+==
+# https://docs.cloud.google.com/spanner/docs/generated-column/how-to#expression-index
+CREATE INDEX VenuesByCity ON Venues((JSON_VALUE(VenueData.address.city)))
+--
+CreateIndexStatement [0-73]
+  PathExpression [13-25]
+    Identifier(VenuesByCity) [13-25]
+  PathExpression [29-35]
+    Identifier(Venues) [29-35]
+  IndexItemList [35-73]
+    OrderingExpression(ASC) [36-72]
+      FunctionCall [37-71]
+        PathExpression [37-47]
+          Identifier(JSON_VALUE) [37-47]
+        PathExpression [48-70]
+          Identifier(VenueData) [48-57]
+          Identifier(address) [58-65]
+          Identifier(city) [66-70]
+==
+# https://docs.cloud.google.com/spanner/docs/generated-column/how-to#alter-table
+ALTER TABLE Users ADD COLUMN Initials STRING(2)
+AS (ARRAY_TO_STRING([SUBSTR(FirstName, 0, 1), SUBSTR(LastName, 0, 1)], "")) STORED
+--
+AlterTableStatement [0-130]
+  PathExpression [12-17]
+    Identifier(Users) [12-17]
+  AlterActionList [18-130]
+    AddColumnAction [18-130]
+      ColumnDefinition [29-130]
+        Identifier(Initials) [29-37]
+        SimpleColumnSchema [38-130]
+          PathExpression [38-44]
+            Identifier(STRING) [38-44]
+          TypeParameterList [44-46]
+            IntLiteral(2) [45-46]
+          GeneratedColumnInfo(generated_mode=ALWAYS, stored_mode=STORED) [48-130]
+            FunctionCall [52-122]
+              PathExpression [52-67]
+                Identifier(ARRAY_TO_STRING) [52-67]
+              ArrayConstructor [68-117]
+                FunctionCall [69-92]
+                  PathExpression [69-75]
+                    Identifier(SUBSTR) [69-75]
+                  PathExpression [76-85]
+                    Identifier(FirstName) [76-85]
+                  IntLiteral(0) [87-88]
+                  IntLiteral(1) [90-91]
+                FunctionCall [94-116]
+                  PathExpression [94-100]
+                    Identifier(SUBSTR) [94-100]
+                  PathExpression [101-109]
+                    Identifier(LastName) [101-109]
+                  IntLiteral(0) [111-112]
+                  IntLiteral(1) [114-115]
+              StringLiteral [119-121]
+                StringLiteralComponent("") [119-121]
+==
+# https://docs.cloud.google.com/spanner/docs/generated-column/how-to#create_a_partial_index_using_a_generated_column
+ALTER TABLE Users ADD COLUMN AgeAbove18 INT64
+AS (IF(Age > 18, Age, NULL))
+--
+AlterTableStatement [0-74]
+  PathExpression [12-17]
+    Identifier(Users) [12-17]
+  AlterActionList [18-74]
+    AddColumnAction [18-74]
+      ColumnDefinition [29-74]
+        Identifier(AgeAbove18) [29-39]
+        SimpleColumnSchema [40-74]
+          PathExpression [40-45]
+            Identifier(INT64) [40-45]
+          GeneratedColumnInfo(generated_mode=ALWAYS) [46-74]
+            FunctionCall [50-73]
+              PathExpression [50-52]
+                Identifier(`IF`) [50-52]
+              BinaryExpression(>) [53-61]
+                PathExpression [53-56]
+                  Identifier(Age) [53-56]
+                IntLiteral(18) [59-61]
+              PathExpression [63-66]
+                Identifier(Age) [63-66]
+              NullLiteral(NULL) [68-72]
+==
+# https://docs.cloud.google.com/spanner/docs/generated-column/how-to#create_a_partial_index_using_a_generated_column
+CREATE NULL_FILTERED INDEX UsersAbove18ByAge
+ON Users (AgeAbove18)
+--
+CreateIndexStatement [0-66]
+  PathExpression [27-44]
+    Identifier(UsersAbove18ByAge) [27-44]
+  PathExpression [48-53]
+    Identifier(Users) [48-53]
+  IndexItemList [54-66]
+    OrderingExpression(ASC) [55-65]
+      PathExpression [55-65]
+        Identifier(AgeAbove18) [55-65]
+==
+# https://docs.cloud.google.com/spanner/docs/generated-column/how-to#create_a_partial_index_using_a_generated_column
+SELECT Id, Age
+FROM Users@{FORCE_INDEX=UsersAbove18ByAge}
+WHERE AgeAbove18 IS NOT NULL
+--
+QueryStatement [0-86]
+  Query [0-86]
+    Select [0-86]
+      SelectList [7-14]
+        SelectColumn [7-9]
+          PathExpression [7-9]
+            Identifier(Id) [7-9]
+        SelectColumn [11-14]
+          PathExpression [11-14]
+            Identifier(Age) [11-14]
+      FromClause [15-57]
+        TablePathExpression [20-57]
+          PathExpression [20-25]
+            Identifier(Users) [20-25]
+          Hint [25-57]
+            HintEntry [27-56]
+              Identifier(FORCE_INDEX) [27-38]
+              PathExpression [39-56]
+                Identifier(UsersAbove18ByAge) [39-56]
+      WhereClause [58-86]
+        BinaryExpression(IS NOT) [64-86]
+          PathExpression [64-74]
+            Identifier(AgeAbove18) [64-74]
+          NullLiteral(NULL) [82-86]
+==
+# https://docs.cloud.google.com/spanner/docs/generated-column/how-to#create_a_partial_index_using_a_generated_column
+SELECT Id, Age
+FROM Users@{FORCE_INDEX=UsersAbove18ByAge}
+WHERE AgeAbove18 > 21
+--
+QueryStatement [0-79]
+  Query [0-79]
+    Select [0-79]
+      SelectList [7-14]
+        SelectColumn [7-9]
+          PathExpression [7-9]
+            Identifier(Id) [7-9]
+        SelectColumn [11-14]
+          PathExpression [11-14]
+            Identifier(Age) [11-14]
+      FromClause [15-57]
+        TablePathExpression [20-57]
+          PathExpression [20-25]
+            Identifier(Users) [20-25]
+          Hint [25-57]
+            HintEntry [27-56]
+              Identifier(FORCE_INDEX) [27-38]
+              PathExpression [39-56]
+                Identifier(UsersAbove18ByAge) [39-56]
+      WhereClause [58-79]
+        BinaryExpression(>) [64-79]
+          PathExpression [64-74]
+            Identifier(AgeAbove18) [64-74]
+          IntLiteral(21) [77-79]
+==
+# https://docs.cloud.google.com/spanner/docs/generated-column/how-to#drop-generated-column
+ALTER TABLE Users DROP COLUMN Initials
+--
+AlterTableStatement [0-38]
+  PathExpression [12-17]
+    Identifier(Users) [12-17]
+  AlterActionList [18-38]
+    DropColumnAction [18-38]
+      Identifier(Initials) [30-38]
+==
+# https://docs.cloud.google.com/spanner/docs/generated-column/how-to#primary-key-generated-column
+SELECT * FROM UserInfoLog
+AS T WHERE T.UserId=1
+--
+QueryStatement [0-47]
+  Query [0-47]
+    Select [0-47]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-30]
+        TablePathExpression [14-30]
+          PathExpression [14-25]
+            Identifier(UserInfoLog) [14-25]
+          Alias [26-30]
+            Identifier(T) [29-30]
+      WhereClause [31-47]
+        BinaryExpression(=) [37-47]
+          PathExpression [37-45]
+            Identifier(T) [37-38]
+            Identifier(UserId) [39-45]
+          IntLiteral(1) [46-47]
+==
+# https://docs.cloud.google.com/spanner/docs/generated-column/how-to#primary-key-generated-column
+SELECT * FROM UserInfoLog
+AS T WHERE T.ShardId = MOD(1, 2048)
+AND T.UserId=1
+--
+QueryStatement [0-76]
+  Query [0-76]
+    Select [0-76]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-30]
+        TablePathExpression [14-30]
+          PathExpression [14-25]
+            Identifier(UserInfoLog) [14-25]
+          Alias [26-30]
+            Identifier(T) [29-30]
+      WhereClause [31-76]
+        AndExpr [37-76]
+          BinaryExpression(=) [37-61]
+            PathExpression [37-46]
+              Identifier(T) [37-38]
+              Identifier(ShardId) [39-46]
+            FunctionCall [49-61]
+              PathExpression [49-52]
+                Identifier(MOD) [49-52]
+              IntLiteral(1) [53-54]
+              IntLiteral(2048) [56-60]
+          BinaryExpression(=) [66-76]
+            PathExpression [66-74]
+              Identifier(T) [66-67]
+              Identifier(UserId) [68-74]
+            IntLiteral(1) [75-76]
+==
+# https://docs.cloud.google.com/spanner/docs/generated-column/how-to#view_props
+SELECT c.TABLE_NAME, c.COLUMN_NAME, C.IS_STORED
+FROM INFORMATION_SCHEMA.COLUMNS as c
+WHERE c.GENERATION_EXPRESSION IS NOT NULL
+--
+QueryStatement [0-126]
+  Query [0-126]
+    Select [0-126]
+      SelectList [7-47]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(c) [7-8]
+            Identifier(TABLE_NAME) [9-19]
+        SelectColumn [21-34]
+          PathExpression [21-34]
+            Identifier(c) [21-22]
+            Identifier(COLUMN_NAME) [23-34]
+        SelectColumn [36-47]
+          PathExpression [36-47]
+            Identifier(C) [36-37]
+            Identifier(IS_STORED) [38-47]
+      FromClause [48-84]
+        TablePathExpression [53-84]
+          PathExpression [53-79]
+            Identifier(INFORMATION_SCHEMA) [53-71]
+            Identifier(COLUMNS) [72-79]
+          Alias [80-84]
+            Identifier(c) [83-84]
+      WhereClause [85-126]
+        BinaryExpression(IS NOT) [91-126]
+          PathExpression [91-114]
+            Identifier(c) [91-92]
+            Identifier(GENERATION_EXPRESSION) [93-114]
+          NullLiteral(NULL) [122-126]
+==
+# https://docs.cloud.google.com/spanner/docs/generated-column/how-to#view_props
+SELECT c.TABLE_NAME, c.COLUMN_NAME, c.SPANNER_STATE
+FROM INFORMATION_SCHEMA.COLUMNS AS c
+WHERE c.TABLE_NAME="Users" AND c.GENERATION_EXPRESSION IS NOT NULL
+--
+QueryStatement [0-155]
+  Query [0-155]
+    Select [0-155]
+      SelectList [7-51]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(c) [7-8]
+            Identifier(TABLE_NAME) [9-19]
+        SelectColumn [21-34]
+          PathExpression [21-34]
+            Identifier(c) [21-22]
+            Identifier(COLUMN_NAME) [23-34]
+        SelectColumn [36-51]
+          PathExpression [36-51]
+            Identifier(c) [36-37]
+            Identifier(SPANNER_STATE) [38-51]
+      FromClause [52-88]
+        TablePathExpression [57-88]
+          PathExpression [57-83]
+            Identifier(INFORMATION_SCHEMA) [57-75]
+            Identifier(COLUMNS) [76-83]
+          Alias [84-88]
+            Identifier(c) [87-88]
+      WhereClause [89-155]
+        AndExpr [95-155]
+          BinaryExpression(=) [95-115]
+            PathExpression [95-107]
+              Identifier(c) [95-96]
+              Identifier(TABLE_NAME) [97-107]
+            StringLiteral [108-115]
+              StringLiteralComponent("Users") [108-115]
+          BinaryExpression(IS NOT) [120-155]
+            PathExpression [120-143]
+              Identifier(c) [120-121]
+              Identifier(GENERATION_EXPRESSION) [122-143]
+            NullLiteral(NULL) [151-155]
diff --git a/parser/testdata/spanner/guides/getting-started.jdbc.test b/parser/testdata/spanner/guides/getting-started.jdbc.test
new file mode 100644
index 0000000..c1c20b7
--- /dev/null
+++ b/parser/testdata/spanner/guides/getting-started.jdbc.test
@@ -0,0 +1,25 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/getting-started/jdbc
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/getting-started/jdbc#create_a_database
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+System.out.println("Created database [" + db.getName() + "]")
+--
+FunctionCall [0-61]
+  PathExpression [0-18]
+    Identifier(System) [0-6]
+    Identifier(`out`) [7-10]
+    Identifier(println) [11-18]
+  BinaryExpression(+) [19-60]
+    BinaryExpression(+) [19-54]
+      StringLiteral [19-39]
+        StringLiteralComponent("Created database [") [19-39]
+      FunctionCall [42-54]
+        PathExpression [42-52]
+          Identifier(db) [42-44]
+          Identifier(getName) [45-52]
+    StringLiteral [57-60]
+      StringLiteralComponent("]") [57-60]
diff --git a/parser/testdata/spanner/guides/graph.algorithm-best-practices.metadata.json b/parser/testdata/spanner/guides/graph.algorithm-best-practices.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.algorithm-best-practices.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/graph.algorithm-best-practices.test b/parser/testdata/spanner/guides/graph.algorithm-best-practices.test
new file mode 100644
index 0000000..5ca6bdc
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.algorithm-best-practices.test
@@ -0,0 +1,950 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/algorithm-best-practices
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/graph/algorithm-best-practices#predefined-flexible-schema
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+-- Create `AccountAlgoProperty` as a child table of `Account`, to store all
+-- properties produced by algorithms for `Account`.
+-- `algo_run_id`: a unique ID for a given algorithm run.
+-- `int_val`: column to store integer algorithm output.
+-- `float_val`: column to store float algorithm output.
+CREATE TABLE AccountAlgoProperty (
+ id INT64 NOT NULL,
+ algo_run_id STRING(200) NOT NULL,
+ int_val INT64,
+ float_val FLOAT64
+) PRIMARY KEY(id, algo_run_id),
+ INTERLEAVE IN PARENT Account
+--
+CreateTableStatement [297-483]
+  PathExpression [310-329]
+    Identifier(AccountAlgoProperty) [310-329]
+  TableElementList [330-423]
+    ColumnDefinition [333-350]
+      Identifier(id) [333-335]
+      SimpleColumnSchema [336-350]
+        PathExpression [336-341]
+          Identifier(INT64) [336-341]
+        ColumnAttributeList [342-350]
+          NotNullColumnAttribute [342-350]
+    ColumnDefinition [353-385]
+      Identifier(algo_run_id) [353-364]
+      SimpleColumnSchema [365-385]
+        PathExpression [365-371]
+          Identifier(STRING) [365-371]
+        TypeParameterList [371-375]
+          IntLiteral(200) [372-375]
+        ColumnAttributeList [377-385]
+          NotNullColumnAttribute [377-385]
+    ColumnDefinition [388-401]
+      Identifier(int_val) [388-395]
+      SimpleColumnSchema [396-401]
+        PathExpression [396-401]
+          Identifier(INT64) [396-401]
+    ColumnDefinition [404-421]
+      Identifier(float_val) [404-413]
+      SimpleColumnSchema [414-421]
+        PathExpression [414-421]
+          Identifier(FLOAT64) [414-421]
+  SpannerTableOptions [424-483]
+    PrimaryKey(ENFORCED) [424-452]
+      PrimaryKeyElementList [435-452]
+        PrimaryKeyElement(ASC) [436-438]
+          Identifier(id) [436-438]
+        PrimaryKeyElement(ASC) [440-451]
+          Identifier(algo_run_id) [440-451]
+    SpannerInterleaveClause [452-483]
+      PathExpression [476-483]
+        Identifier(Account) [476-483]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/algorithm-best-practices#predefined-flexible-schema
+-- Create `AccountAlgoEdge` as a child table of `Account`, to store all
+-- outgoing edges produced by algorithms for `Account`.
+-- `algo_run_id`: a unique ID for a given algorithm run.
+-- `to_id`: destination `Account` id.
+-- `int_val`: column to store integer algorithm output.
+-- `float_val`: column to store float algorithm output.
+CREATE TABLE AccountAlgoEdge (
+ id INT64 NOT NULL,
+ algo_run_id STRING(200) NOT NULL,
+ to_id INT64 NOT NULL,
+ int_val INT64,
+ float_val FLOAT64,
+ CONSTRAINT FK_AccountId FOREIGN KEY (to_id) REFERENCES Account (id) NOT ENFORCED,
+) PRIMARY KEY(id, algo_run_id, to_id),
+ INTERLEAVE IN PARENT Account
+--
+CreateTableStatement [335-631]
+  PathExpression [348-363]
+    Identifier(AccountAlgoEdge) [348-363]
+  TableElementList [364-564]
+    ColumnDefinition [367-384]
+      Identifier(id) [367-369]
+      SimpleColumnSchema [370-384]
+        PathExpression [370-375]
+          Identifier(INT64) [370-375]
+        ColumnAttributeList [376-384]
+          NotNullColumnAttribute [376-384]
+    ColumnDefinition [387-419]
+      Identifier(algo_run_id) [387-398]
+      SimpleColumnSchema [399-419]
+        PathExpression [399-405]
+          Identifier(STRING) [399-405]
+        TypeParameterList [405-409]
+          IntLiteral(200) [406-409]
+        ColumnAttributeList [411-419]
+          NotNullColumnAttribute [411-419]
+    ColumnDefinition [422-442]
+      Identifier(to_id) [422-427]
+      SimpleColumnSchema [428-442]
+        PathExpression [428-433]
+          Identifier(INT64) [428-433]
+        ColumnAttributeList [434-442]
+          NotNullColumnAttribute [434-442]
+    ColumnDefinition [445-458]
+      Identifier(int_val) [445-452]
+      SimpleColumnSchema [453-458]
+        PathExpression [453-458]
+          Identifier(INT64) [453-458]
+    ColumnDefinition [461-478]
+      Identifier(float_val) [461-470]
+      SimpleColumnSchema [471-478]
+        PathExpression [471-478]
+          Identifier(FLOAT64) [471-478]
+    ForeignKey [481-561]
+      ColumnList [517-524]
+        Identifier(to_id) [518-523]
+      ForeignKeyReference(MATCH SIMPLE NOT ENFORCED) [525-548]
+        PathExpression [536-543]
+          Identifier(Account) [536-543]
+        ColumnList [544-548]
+          Identifier(id) [545-547]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [548-548]
+      Identifier(FK_AccountId) [492-504]
+  SpannerTableOptions [565-631]
+    PrimaryKey(ENFORCED) [565-600]
+      PrimaryKeyElementList [576-600]
+        PrimaryKeyElement(ASC) [577-579]
+          Identifier(id) [577-579]
+        PrimaryKeyElement(ASC) [581-592]
+          Identifier(algo_run_id) [581-592]
+        PrimaryKeyElement(ASC) [594-599]
+          Identifier(to_id) [594-599]
+    SpannerInterleaveClause [600-631]
+      PathExpression [624-631]
+        Identifier(Account) [624-631]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/algorithm-best-practices#predefined-flexible-schema
+-- Store PageRank score as property in child table.
+EXPORT DATA
+ OPTIONS (format = "CLOUD_SPANNER",
+          table = "AccountAlgoProperty",
+          write_mode = 'upsert_ignore_all' ) AS
+GRAPH FinGraph
+CALL PageRank(node_labels => ['Account'], edge_labels => ['Transfers'])
+RETURN node.id, "page_rank_123" AS algo_run_id, score As float_val
+--
+ExportDataStatement [52-342]
+  OptionsList [73-185]
+    OptionsEntry [74-98]
+      Identifier(format) [74-80]
+      StringLiteral [83-98]
+        StringLiteralComponent("CLOUD_SPANNER") [83-98]
+    OptionsEntry [110-139]
+      Identifier(table) [110-115]
+      StringLiteral [118-139]
+        StringLiteralComponent("AccountAlgoProperty") [118-139]
+    OptionsEntry [151-183]
+      Identifier(write_mode) [151-161]
+      StringLiteral [164-183]
+        StringLiteralComponent('upsert_ignore_all') [164-183]
+  Query [189-342]
+    GqlQuery [189-342]
+      GraphTableQuery [189-342]
+        PathExpression [195-203]
+          Identifier(FinGraph) [195-203]
+        GqlOperatorList [204-342]
+          GqlOperatorList [204-342]
+            GqlNamedCall [204-275]
+              TVF [209-275]
+                PathExpression [209-217]
+                  Identifier(PageRank) [209-217]
+                TVFArgument [218-244]
+                  NamedArgument [218-244]
+                    Identifier(node_labels) [218-229]
+                    ArrayConstructor [233-244]
+                      StringLiteral [234-243]
+                        StringLiteralComponent('Account') [234-243]
+                TVFArgument [246-274]
+                  NamedArgument [246-274]
+                    Identifier(edge_labels) [246-257]
+                    ArrayConstructor [261-274]
+                      StringLiteral [262-273]
+                        StringLiteralComponent('Transfers') [262-273]
+            GqlReturn [276-342]
+              Select [283-342]
+                SelectList [283-342]
+                  SelectColumn [283-290]
+                    PathExpression [283-290]
+                      Identifier(node) [283-287]
+                      Identifier(id) [288-290]
+                  SelectColumn [292-322]
+                    StringLiteral [292-307]
+                      StringLiteralComponent("page_rank_123") [292-307]
+                    Alias [308-322]
+                      Identifier(algo_run_id) [311-322]
+                  SelectColumn [324-342]
+                    PathExpression [324-329]
+                      Identifier(score) [324-329]
+                    Alias [330-342]
+                      Identifier(float_val) [333-342]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/algorithm-best-practices#predefined-flexible-schema
+-- Store ShortestPath output as edge in child table.
+EXPORT DATA
+ OPTIONS (format = "CLOUD_SPANNER",
+          table = "AccountAlgoEdge",
+          write_mode = 'upsert_ignore_all' ) AS
+GRAPH FinGraph
+CALL ShortestPath(
+    source_nodes => ARRAY {
+                      MATCH (n:Account {id: 7})
+                      RETURN n
+                    },
+    target_nodes => ARRAY {
+                      MATCH (n:Account {id: 16})
+                      RETURN n
+                    }
+  ) YIELD source_node, target_node, path, cost
+RETURN source_node.id AS id, "shortest_path_456" AS algo_run_id,
+  target_node.id AS to_id, PATH_LENGTH(path) AS int_val, cost AS float_val
+--
+ExportDataStatement [53-666]
+  OptionsList [74-182]
+    OptionsEntry [75-99]
+      Identifier(format) [75-81]
+      StringLiteral [84-99]
+        StringLiteralComponent("CLOUD_SPANNER") [84-99]
+    OptionsEntry [111-136]
+      Identifier(table) [111-116]
+      StringLiteral [119-136]
+        StringLiteralComponent("AccountAlgoEdge") [119-136]
+    OptionsEntry [148-180]
+      Identifier(write_mode) [148-158]
+      StringLiteral [161-180]
+        StringLiteralComponent('upsert_ignore_all') [161-180]
+  Query [186-666]
+    GqlQuery [186-666]
+      GraphTableQuery [186-666]
+        PathExpression [192-200]
+          Identifier(FinGraph) [192-200]
+        GqlOperatorList [201-666]
+          GqlOperatorList [201-666]
+            GqlNamedCall [201-526]
+              TVF [206-483]
+                PathExpression [206-218]
+                  Identifier(ShortestPath) [206-218]
+                TVFArgument [224-348]
+                  NamedArgument [224-348]
+                    Identifier(source_nodes) [224-236]
+                    ExpressionSubquery(modifier=ARRAY) [240-348]
+                      Query [246-348]
+                        GqlQuery [246-348]
+                          GraphTableQuery [246-348]
+                            GqlOperatorList [270-326]
+                              GqlOperatorList [270-326]
+                                GqlMatch [270-295]
+                                  GraphPattern [276-295]
+                                    GraphPathPattern [276-295]
+                                      GraphNodePattern [276-295]
+                                        GraphElementPatternFiller [277-294]
+                                          Identifier(n) [277-278]
+                                          GraphLabelFilter [278-286]
+                                            GraphElementLabel [279-286]
+                                              Identifier(Account) [279-286]
+                                          GraphPropertySpecification [287-294]
+                                            GraphPropertyNameAndValue [288-293]
+                                              Identifier(id) [288-290]
+                                              IntLiteral(7) [292-293]
+                                GqlReturn [318-326]
+                                  Select [325-326]
+                                    SelectList [325-326]
+                                      SelectColumn [325-326]
+                                        PathExpression [325-326]
+                                          Identifier(n) [325-326]
+                TVFArgument [354-479]
+                  NamedArgument [354-479]
+                    Identifier(target_nodes) [354-366]
+                    ExpressionSubquery(modifier=ARRAY) [370-479]
+                      Query [376-479]
+                        GqlQuery [376-479]
+                          GraphTableQuery [376-479]
+                            GqlOperatorList [400-457]
+                              GqlOperatorList [400-457]
+                                GqlMatch [400-426]
+                                  GraphPattern [406-426]
+                                    GraphPathPattern [406-426]
+                                      GraphNodePattern [406-426]
+                                        GraphElementPatternFiller [407-425]
+                                          Identifier(n) [407-408]
+                                          GraphLabelFilter [408-416]
+                                            GraphElementLabel [409-416]
+                                              Identifier(Account) [409-416]
+                                          GraphPropertySpecification [417-425]
+                                            GraphPropertyNameAndValue [418-424]
+                                              Identifier(id) [418-420]
+                                              IntLiteral(16) [422-424]
+                                GqlReturn [449-457]
+                                  Select [456-457]
+                                    SelectList [456-457]
+                                      SelectColumn [456-457]
+                                        PathExpression [456-457]
+                                          Identifier(n) [456-457]
+              YieldItemList [484-526]
+                ExpressionWithOptAlias [490-501]
+                  Identifier(source_node) [490-501]
+                ExpressionWithOptAlias [503-514]
+                  Identifier(target_node) [503-514]
+                ExpressionWithOptAlias [516-520]
+                  Identifier(path) [516-520]
+                ExpressionWithOptAlias [522-526]
+                  Identifier(cost) [522-526]
+            GqlReturn [527-666]
+              Select [534-666]
+                SelectList [534-666]
+                  SelectColumn [534-554]
+                    PathExpression [534-548]
+                      Identifier(source_node) [534-545]
+                      Identifier(id) [546-548]
+                    Alias [549-554]
+                      Identifier(id) [552-554]
+                  SelectColumn [556-590]
+                    StringLiteral [556-575]
+                      StringLiteralComponent("shortest_path_456") [556-575]
+                    Alias [576-590]
+                      Identifier(algo_run_id) [579-590]
+                  SelectColumn [594-617]
+                    PathExpression [594-608]
+                      Identifier(target_node) [594-605]
+                      Identifier(id) [606-608]
+                    Alias [609-617]
+                      Identifier(to_id) [612-617]
+                  SelectColumn [619-647]
+                    FunctionCall [619-636]
+                      PathExpression [619-630]
+                        Identifier(PATH_LENGTH) [619-630]
+                      PathExpression [631-635]
+                        Identifier(path) [631-635]
+                    Alias [637-647]
+                      Identifier(int_val) [640-647]
+                  SelectColumn [649-666]
+                    PathExpression [649-653]
+                      Identifier(cost) [649-653]
+                    Alias [654-666]
+                      Identifier(float_val) [657-666]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/algorithm-best-practices#predefined-flexible-schema
+SELECT acct.id, prop.float_val AS page_rank_score
+FROM GRAPH_TABLE(
+  FinGraph
+  MATCH (n:Account)
+  RETURN n.id
+) acct JOIN AccountAlgoProperty prop ON acct.id = prop.id
+  AND prop.algo_run_id = 'page_rank_123'
+--
+QueryStatement [0-211]
+  Query [0-211]
+    Select [0-211]
+      SelectList [7-49]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(acct) [7-11]
+            Identifier(id) [12-14]
+        SelectColumn [16-49]
+          PathExpression [16-30]
+            Identifier(prop) [16-20]
+            Identifier(float_val) [21-30]
+          Alias [31-49]
+            Identifier(page_rank_score) [34-49]
+      FromClause [50-211]
+        Join [55-211]
+          GraphTableQuery [55-119]
+            PathExpression [70-78]
+              Identifier(FinGraph) [70-78]
+            GqlOperatorList [81-112]
+              GqlOperatorList [81-112]
+                GqlMatch [81-98]
+                  GraphPattern [87-98]
+                    GraphPathPattern [87-98]
+                      GraphNodePattern [87-98]
+                        GraphElementPatternFiller [88-97]
+                          Identifier(n) [88-89]
+                          GraphLabelFilter [89-97]
+                            GraphElementLabel [90-97]
+                              Identifier(Account) [90-97]
+                GqlReturn [101-112]
+                  Select [108-112]
+                    SelectList [108-112]
+                      SelectColumn [108-112]
+                        PathExpression [108-112]
+                          Identifier(n) [108-109]
+                          Identifier(id) [110-112]
+            Alias [115-119]
+              Identifier(acct) [115-119]
+          Location [120-124]
+          TablePathExpression [125-149]
+            PathExpression [125-144]
+              Identifier(AccountAlgoProperty) [125-144]
+            Alias [145-149]
+              Identifier(prop) [145-149]
+          OnClause [150-211]
+            AndExpr [153-211]
+              BinaryExpression(=) [153-170]
+                PathExpression [153-160]
+                  Identifier(acct) [153-157]
+                  Identifier(id) [158-160]
+                PathExpression [163-170]
+                  Identifier(prop) [163-167]
+                  Identifier(id) [168-170]
+              BinaryExpression(=) [177-211]
+                PathExpression [177-193]
+                  Identifier(prop) [177-181]
+                  Identifier(algo_run_id) [182-193]
+                StringLiteral [196-211]
+                  StringLiteralComponent('page_rank_123') [196-211]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/algorithm-best-practices#predefined-flexible-schema
+SELECT acct.id, edge.to_id, edge.int_val AS path_len, edge.float_val AS cost
+FROM GRAPH_TABLE(
+  FinGraph
+  MATCH (n:Account)
+  RETURN n.id
+) acct JOIN AccountAlgoEdge edge ON acct.id = edge.id
+  AND edge.algo_run_id = 'shortest_path_456'
+--
+QueryStatement [0-238]
+  Query [0-238]
+    Select [0-238]
+      SelectList [7-76]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(acct) [7-11]
+            Identifier(id) [12-14]
+        SelectColumn [16-26]
+          PathExpression [16-26]
+            Identifier(edge) [16-20]
+            Identifier(to_id) [21-26]
+        SelectColumn [28-52]
+          PathExpression [28-40]
+            Identifier(edge) [28-32]
+            Identifier(int_val) [33-40]
+          Alias [41-52]
+            Identifier(path_len) [44-52]
+        SelectColumn [54-76]
+          PathExpression [54-68]
+            Identifier(edge) [54-58]
+            Identifier(float_val) [59-68]
+          Alias [69-76]
+            Identifier(cost) [72-76]
+      FromClause [77-238]
+        Join [82-238]
+          GraphTableQuery [82-146]
+            PathExpression [97-105]
+              Identifier(FinGraph) [97-105]
+            GqlOperatorList [108-139]
+              GqlOperatorList [108-139]
+                GqlMatch [108-125]
+                  GraphPattern [114-125]
+                    GraphPathPattern [114-125]
+                      GraphNodePattern [114-125]
+                        GraphElementPatternFiller [115-124]
+                          Identifier(n) [115-116]
+                          GraphLabelFilter [116-124]
+                            GraphElementLabel [117-124]
+                              Identifier(Account) [117-124]
+                GqlReturn [128-139]
+                  Select [135-139]
+                    SelectList [135-139]
+                      SelectColumn [135-139]
+                        PathExpression [135-139]
+                          Identifier(n) [135-136]
+                          Identifier(id) [137-139]
+            Alias [142-146]
+              Identifier(acct) [142-146]
+          Location [147-151]
+          TablePathExpression [152-172]
+            PathExpression [152-167]
+              Identifier(AccountAlgoEdge) [152-167]
+            Alias [168-172]
+              Identifier(edge) [168-172]
+          OnClause [173-238]
+            AndExpr [176-238]
+              BinaryExpression(=) [176-193]
+                PathExpression [176-183]
+                  Identifier(acct) [176-180]
+                  Identifier(id) [181-183]
+                PathExpression [186-193]
+                  Identifier(edge) [186-190]
+                  Identifier(id) [191-193]
+              BinaryExpression(=) [200-238]
+                PathExpression [200-216]
+                  Identifier(edge) [200-204]
+                  Identifier(algo_run_id) [205-216]
+                StringLiteral [219-238]
+                  StringLiteralComponent('shortest_path_456') [219-238]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/algorithm-best-practices#predefined-flexible-schema
+-- Create a view that aggregates non-null algorithm properties per node into
+-- name-value pairs in JSON.
+CREATE OR REPLACE VIEW AccountWithAlgoProperties SQL SECURITY INVOKER AS
+SELECT
+  n.id, ANY_VALUE(n.create_time) AS create_time,
+  ANY_VALUE(n.is_blocked) AS is_blocked, ANY_VALUE(n.nick_name) AS nick_name,
+  JSON_OBJECT(
+    IF(COUNT(c.algo_run_id) = 0, [], ARRAY_AGG(c.algo_run_id)),
+    IF(COUNT(c.algo_run_id) = 0, [], ARRAY_AGG(
+      COALESCE(
+        IF (c.int_val IS NULL, NULL, TO_JSON(c.int_val)),
+        IF (c.float_val IS NULL, NULL, TO_JSON(c.float_val))
+      )))) AS algo_props
+FROM Account AS n LEFT JOIN AccountAlgoProperty AS c ON n.id = c.id
+GROUP BY n.id
+--
+CreateViewStatement(is_or_replace, SQL SECURITY INVOKER) [106-681]
+  PathExpression [129-154]
+    Identifier(AccountWithAlgoProperties) [129-154]
+  Query [179-681]
+    Select [179-681]
+      SelectList [188-599]
+        SelectColumn [188-192]
+          PathExpression [188-192]
+            Identifier(n) [188-189]
+            Identifier(id) [190-192]
+        SelectColumn [194-233]
+          FunctionCall [194-218]
+            PathExpression [194-203]
+              Identifier(ANY_VALUE) [194-203]
+            PathExpression [204-217]
+              Identifier(n) [204-205]
+              Identifier(create_time) [206-217]
+          Alias [219-233]
+            Identifier(create_time) [222-233]
+        SelectColumn [237-274]
+          FunctionCall [237-260]
+            PathExpression [237-246]
+              Identifier(ANY_VALUE) [237-246]
+            PathExpression [247-259]
+              Identifier(n) [247-248]
+              Identifier(is_blocked) [249-259]
+          Alias [261-274]
+            Identifier(is_blocked) [264-274]
+        SelectColumn [276-311]
+          FunctionCall [276-298]
+            PathExpression [276-285]
+              Identifier(ANY_VALUE) [276-285]
+            PathExpression [286-297]
+              Identifier(n) [286-287]
+              Identifier(nick_name) [288-297]
+          Alias [299-311]
+            Identifier(nick_name) [302-311]
+        SelectColumn [315-599]
+          FunctionCall [315-585]
+            PathExpression [315-326]
+              Identifier(JSON_OBJECT) [315-326]
+            FunctionCall [332-390]
+              PathExpression [332-334]
+                Identifier(`IF`) [332-334]
+              BinaryExpression(=) [335-359]
+                FunctionCall [335-355]
+                  PathExpression [335-340]
+                    Identifier(COUNT) [335-340]
+                  PathExpression [341-354]
+                    Identifier(c) [341-342]
+                    Identifier(algo_run_id) [343-354]
+                IntLiteral(0) [358-359]
+              ArrayConstructor [361-363]
+              FunctionCall [365-389]
+                PathExpression [365-374]
+                  Identifier(ARRAY_AGG) [365-374]
+                PathExpression [375-388]
+                  Identifier(c) [375-376]
+                  Identifier(algo_run_id) [377-388]
+            FunctionCall [396-584]
+              PathExpression [396-398]
+                Identifier(`IF`) [396-398]
+              BinaryExpression(=) [399-423]
+                FunctionCall [399-419]
+                  PathExpression [399-404]
+                    Identifier(COUNT) [399-404]
+                  PathExpression [405-418]
+                    Identifier(c) [405-406]
+                    Identifier(algo_run_id) [407-418]
+                IntLiteral(0) [422-423]
+              ArrayConstructor [425-427]
+              FunctionCall [429-583]
+                PathExpression [429-438]
+                  Identifier(ARRAY_AGG) [429-438]
+                FunctionCall [446-582]
+                  PathExpression [446-454]
+                    Identifier(COALESCE) [446-454]
+                  FunctionCall [464-512]
+                    PathExpression [464-466]
+                      Identifier(`IF`) [464-466]
+                    BinaryExpression(IS) [468-485]
+                      PathExpression [468-477]
+                        Identifier(c) [468-469]
+                        Identifier(int_val) [470-477]
+                      NullLiteral(NULL) [481-485]
+                    NullLiteral(NULL) [487-491]
+                    FunctionCall [493-511]
+                      PathExpression [493-500]
+                        Identifier(TO_JSON) [493-500]
+                      PathExpression [501-510]
+                        Identifier(c) [501-502]
+                        Identifier(int_val) [503-510]
+                  FunctionCall [522-574]
+                    PathExpression [522-524]
+                      Identifier(`IF`) [522-524]
+                    BinaryExpression(IS) [526-545]
+                      PathExpression [526-537]
+                        Identifier(c) [526-527]
+                        Identifier(float_val) [528-537]
+                      NullLiteral(NULL) [541-545]
+                    NullLiteral(NULL) [547-551]
+                    FunctionCall [553-573]
+                      PathExpression [553-560]
+                        Identifier(TO_JSON) [553-560]
+                      PathExpression [561-572]
+                        Identifier(c) [561-562]
+                        Identifier(float_val) [563-572]
+          Alias [586-599]
+            Identifier(algo_props) [589-599]
+      FromClause [600-667]
+        Join(LEFT) [605-667]
+          TablePathExpression [605-617]
+            PathExpression [605-612]
+              Identifier(Account) [605-612]
+            Alias [613-617]
+              Identifier(n) [616-617]
+          Location [618-627]
+          TablePathExpression [628-652]
+            PathExpression [628-647]
+              Identifier(AccountAlgoProperty) [628-647]
+            Alias [648-652]
+              Identifier(c) [651-652]
+          OnClause [653-667]
+            BinaryExpression(=) [656-667]
+              PathExpression [656-660]
+                Identifier(n) [656-657]
+                Identifier(id) [658-660]
+              PathExpression [663-667]
+                Identifier(c) [663-664]
+                Identifier(id) [665-667]
+      GroupBy [668-681]
+        GroupingItem [677-681]
+          PathExpression [677-681]
+            Identifier(n) [677-678]
+            Identifier(id) [679-681]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/algorithm-best-practices#predefined-flexible-schema
+-- Create FinGraphAugmented with algorithm generated properties and edges.
+CREATE OR REPLACE PROPERTY GRAPH FinGraphAugmented
+  NODE TABLES (
+    AccountWithAlgoProperties AS Account
+      KEY(id)
+      LABEL Account PROPERTIES(
+        create_time,
+        id,
+        is_blocked,
+        nick_name,
+        algo_props),
+    Person
+  )
+  EDGE TABLES (
+    PersonOwnAccount
+      SOURCE KEY (id) REFERENCES Person (id)
+      DESTINATION KEY (account_id) REFERENCES Account (id)
+      LABEL Owns,
+    AccountTransferAccount
+      SOURCE KEY (id) REFERENCES Account (id)
+      DESTINATION KEY (to_id) REFERENCES Account (id)
+      LABEL Transfers,
+    AccountAlgoEdge
+      SOURCE KEY (id) REFERENCES Account (id)
+      DESTINATION KEY (to_id) REFERENCES Account (id)
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [75-769]
+  PathExpression [108-125]
+    Identifier(FinGraphAugmented) [108-125]
+  GraphElementTableList [140-336]
+    GraphElementTable [146-320]
+      PathExpression [146-171]
+        Identifier(AccountWithAlgoProperties) [146-171]
+      Alias [172-182]
+        Identifier(Account) [175-182]
+      ColumnList [192-196]
+        Identifier(id) [193-195]
+      GraphElementLabelAndPropertiesList [203-320]
+        GraphElementLabelAndProperties [203-320]
+          Identifier(Account) [209-216]
+          GraphProperties [217-320]
+            GraphDerivedPropertyList [237-319]
+              GraphDerivedProperty [237-248]
+                PathExpression [237-248]
+                  Identifier(create_time) [237-248]
+              GraphDerivedProperty [258-260]
+                PathExpression [258-260]
+                  Identifier(id) [258-260]
+              GraphDerivedProperty [270-280]
+                PathExpression [270-280]
+                  Identifier(is_blocked) [270-280]
+              GraphDerivedProperty [290-299]
+                PathExpression [290-299]
+                  Identifier(nick_name) [290-299]
+              GraphDerivedProperty [309-319]
+                PathExpression [309-319]
+                  Identifier(algo_props) [309-319]
+    GraphElementTable [326-332]
+      PathExpression [326-332]
+        Identifier(Person) [326-332]
+      GraphElementLabelAndPropertiesList [332-332]
+        GraphElementLabelAndProperties [332-332]
+          GraphProperties [332-332]
+  GraphElementTableList [351-769]
+    GraphElementTable [357-494]
+      PathExpression [357-373]
+        Identifier(PersonOwnAccount) [357-373]
+      GraphNodeTableReference(SOURCE) [380-418]
+        Identifier(Person) [407-413]
+        ColumnList [391-395]
+          Identifier(id) [392-394]
+        ColumnList [414-418]
+          Identifier(id) [415-417]
+      GraphNodeTableReference(DESTINATION) [425-477]
+        Identifier(Account) [465-472]
+        ColumnList [441-453]
+          Identifier(account_id) [442-452]
+        ColumnList [473-477]
+          Identifier(id) [474-476]
+      GraphElementLabelAndPropertiesList [484-494]
+        GraphElementLabelAndProperties [484-494]
+          Identifier(Owns) [490-494]
+          GraphProperties [494-494]
+    GraphElementTable [500-644]
+      PathExpression [500-522]
+        Identifier(AccountTransferAccount) [500-522]
+      GraphNodeTableReference(SOURCE) [529-568]
+        Identifier(Account) [556-563]
+        ColumnList [540-544]
+          Identifier(id) [541-543]
+        ColumnList [564-568]
+          Identifier(id) [565-567]
+      GraphNodeTableReference(DESTINATION) [575-622]
+        Identifier(Account) [610-617]
+        ColumnList [591-598]
+          Identifier(to_id) [592-597]
+        ColumnList [618-622]
+          Identifier(id) [619-621]
+      GraphElementLabelAndPropertiesList [629-644]
+        GraphElementLabelAndProperties [629-644]
+          Identifier(Transfers) [635-644]
+          GraphProperties [644-644]
+    GraphElementTable [650-765]
+      PathExpression [650-665]
+        Identifier(AccountAlgoEdge) [650-665]
+      GraphNodeTableReference(SOURCE) [672-711]
+        Identifier(Account) [699-706]
+        ColumnList [683-687]
+          Identifier(id) [684-686]
+        ColumnList [707-711]
+          Identifier(id) [708-710]
+      GraphNodeTableReference(DESTINATION) [718-765]
+        Identifier(Account) [753-760]
+        ColumnList [734-741]
+          Identifier(to_id) [735-740]
+        ColumnList [761-765]
+          Identifier(id) [762-764]
+      GraphElementLabelAndPropertiesList [765-765]
+        GraphElementLabelAndProperties [765-765]
+          GraphProperties [765-765]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/algorithm-best-practices#predefined-flexible-schema
+-- Retrieve PageRank score property in graph query.
+GRAPH FinGraphAugmented
+MATCH (a:Account)
+RETURN a.id, a.algo_props.page_rank_123 AS page_rank
+ORDER BY page_rank DESC
+LIMIT 5
+--
+QueryStatement [52-178]
+  Query [52-178]
+    GqlQuery [52-178]
+      GraphTableQuery [52-178]
+        PathExpression [58-75]
+          Identifier(FinGraphAugmented) [58-75]
+        GqlOperatorList [76-178]
+          GqlOperatorList [76-178]
+            GqlMatch [76-93]
+              GraphPattern [82-93]
+                GraphPathPattern [82-93]
+                  GraphNodePattern [82-93]
+                    GraphElementPatternFiller [83-92]
+                      Identifier(a) [83-84]
+                      GraphLabelFilter [84-92]
+                        GraphElementLabel [85-92]
+                          Identifier(Account) [85-92]
+            GqlReturn [94-178]
+              Select [101-146]
+                SelectList [101-146]
+                  SelectColumn [101-105]
+                    PathExpression [101-105]
+                      Identifier(a) [101-102]
+                      Identifier(id) [103-105]
+                  SelectColumn [107-146]
+                    PathExpression [107-133]
+                      Identifier(a) [107-108]
+                      Identifier(algo_props) [109-119]
+                      Identifier(page_rank_123) [120-133]
+                    Alias [134-146]
+                      Identifier(page_rank) [137-146]
+              GqlOrderByAndPage [147-178]
+                OrderBy [147-170]
+                  OrderingExpression(DESC) [156-170]
+                    PathExpression [156-165]
+                      Identifier(page_rank) [156-165]
+                GqlPage [171-178]
+                  GqlPageLimit [171-178]
+                    IntLiteral(5) [177-178]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/algorithm-best-practices#predefined-flexible-schema
+-- Navigate through ShortestPath edge in graph query.
+GRAPH FinGraphAugmented
+MATCH (s:Account {id: 7})-[e:AccountAlgoEdge {algo_run_id : 'shortest_path_456'}]->(d:Account)
+RETURN s.id AS src, d.id AS dst, e.int_val AS path_len, e.float_val AS cost
+--
+QueryStatement [54-248]
+  Query [54-248]
+    GqlQuery [54-248]
+      GraphTableQuery [54-248]
+        PathExpression [60-77]
+          Identifier(FinGraphAugmented) [60-77]
+        GqlOperatorList [78-248]
+          GqlOperatorList [78-248]
+            GqlMatch [78-172]
+              GraphPattern [84-172]
+                GraphPathPattern [84-172]
+                  GraphNodePattern [84-103]
+                    GraphElementPatternFiller [85-102]
+                      Identifier(s) [85-86]
+                      GraphLabelFilter [86-94]
+                        GraphElementLabel [87-94]
+                          Identifier(Account) [87-94]
+                      GraphPropertySpecification [95-102]
+                        GraphPropertyNameAndValue [96-101]
+                          Identifier(id) [96-98]
+                          IntLiteral(7) [100-101]
+                  GraphEdgePattern [103-161]
+                    GraphElementPatternFiller [105-158]
+                      Identifier(e) [105-106]
+                      GraphLabelFilter [106-122]
+                        GraphElementLabel [107-122]
+                          Identifier(AccountAlgoEdge) [107-122]
+                      GraphPropertySpecification [123-158]
+                        GraphPropertyNameAndValue [124-157]
+                          Identifier(algo_run_id) [124-135]
+                          StringLiteral [138-157]
+                            StringLiteralComponent('shortest_path_456') [138-157]
+                  GraphNodePattern [161-172]
+                    GraphElementPatternFiller [162-171]
+                      Identifier(d) [162-163]
+                      GraphLabelFilter [163-171]
+                        GraphElementLabel [164-171]
+                          Identifier(Account) [164-171]
+            GqlReturn [173-248]
+              Select [180-248]
+                SelectList [180-248]
+                  SelectColumn [180-191]
+                    PathExpression [180-184]
+                      Identifier(s) [180-181]
+                      Identifier(id) [182-184]
+                    Alias [185-191]
+                      Identifier(src) [188-191]
+                  SelectColumn [193-204]
+                    PathExpression [193-197]
+                      Identifier(d) [193-194]
+                      Identifier(id) [195-197]
+                    Alias [198-204]
+                      Identifier(dst) [201-204]
+                  SelectColumn [206-227]
+                    PathExpression [206-215]
+                      Identifier(e) [206-207]
+                      Identifier(int_val) [208-215]
+                    Alias [216-227]
+                      Identifier(path_len) [219-227]
+                  SelectColumn [229-248]
+                    PathExpression [229-240]
+                      Identifier(e) [229-230]
+                      Identifier(float_val) [231-240]
+                    Alias [241-248]
+                      Identifier(cost) [244-248]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/algorithm-best-practices#diff-element-types
+EXPORT DATA OPTIONS (
+  uri = "gs://bucket-name/page_rank.csv",
+  format = "csv",
+  overwrite = TRUE) AS
+GRAPH FinGraph
+CALL PageRank()
+YIELD node, score
+RETURN ELEMENT_DEFINITION_NAME(node) AS node_type, node.id, score
+--
+ExportDataStatement [0-219]
+  OptionsList [20-101]
+    OptionsEntry [24-62]
+      Identifier(uri) [24-27]
+      StringLiteral [30-62]
+        StringLiteralComponent("gs://bucket-name/page_rank.csv") [30-62]
+    OptionsEntry [66-80]
+      Identifier(format) [66-72]
+      StringLiteral [75-80]
+        StringLiteralComponent("csv") [75-80]
+    OptionsEntry [84-100]
+      Identifier(overwrite) [84-93]
+      BooleanLiteral(TRUE) [96-100]
+  Query [105-219]
+    GqlQuery [105-219]
+      GraphTableQuery [105-219]
+        PathExpression [111-119]
+          Identifier(FinGraph) [111-119]
+        GqlOperatorList [120-219]
+          GqlOperatorList [120-219]
+            GqlNamedCall [120-153]
+              TVF [125-135]
+                PathExpression [125-133]
+                  Identifier(PageRank) [125-133]
+              YieldItemList [136-153]
+                ExpressionWithOptAlias [142-146]
+                  Identifier(node) [142-146]
+                ExpressionWithOptAlias [148-153]
+                  Identifier(score) [148-153]
+            GqlReturn [154-219]
+              Select [161-219]
+                SelectList [161-219]
+                  SelectColumn [161-203]
+                    FunctionCall [161-190]
+                      PathExpression [161-184]
+                        Identifier(ELEMENT_DEFINITION_NAME) [161-184]
+                      PathExpression [185-189]
+                        Identifier(node) [185-189]
+                    Alias [191-203]
+                      Identifier(node_type) [194-203]
+                  SelectColumn [205-212]
+                    PathExpression [205-212]
+                      Identifier(node) [205-209]
+                      Identifier(id) [210-212]
+                  SelectColumn [214-219]
+                    PathExpression [214-219]
+                      Identifier(score) [214-219]
diff --git a/parser/testdata/spanner/guides/graph.algorithms.test b/parser/testdata/spanner/guides/graph.algorithms.test
new file mode 100644
index 0000000..ef38c1e
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.algorithms.test
@@ -0,0 +1,806 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/algorithms
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/graph/algorithms#example
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+EXPORT DATA OPTIONS (
+  uri = "gs://my-bucket-name/my-output.csv",
+  format = "csv"
+) AS
+GRAPH FinGraph
+CALL PageRank(
+    node_labels => ['Account'], edge_labels => ['Transfers'],
+    source_nodes => ARRAY {
+                      MATCH (n:Account {id:7})
+                      RETURN n
+                    }
+  ) YIELD node, score
+RETURN node.id, score
+--
+ExportDataStatement [0-352]
+  OptionsList [20-85]
+    OptionsEntry [24-65]
+      Identifier(uri) [24-27]
+      StringLiteral [30-65]
+        StringLiteralComponent("gs://my-bucket-name/my-output.csv") [30-65]
+    OptionsEntry [69-83]
+      Identifier(format) [69-75]
+      StringLiteral [78-83]
+        StringLiteralComponent("csv") [78-83]
+  Query [89-352]
+    GqlQuery [89-352]
+      GraphTableQuery [89-352]
+        PathExpression [95-103]
+          Identifier(FinGraph) [95-103]
+        GqlOperatorList [104-352]
+          GqlOperatorList [104-352]
+            GqlNamedCall [104-330]
+              TVF [109-312]
+                PathExpression [109-117]
+                  Identifier(PageRank) [109-117]
+                TVFArgument [123-149]
+                  NamedArgument [123-149]
+                    Identifier(node_labels) [123-134]
+                    ArrayConstructor [138-149]
+                      StringLiteral [139-148]
+                        StringLiteralComponent('Account') [139-148]
+                TVFArgument [151-179]
+                  NamedArgument [151-179]
+                    Identifier(edge_labels) [151-162]
+                    ArrayConstructor [166-179]
+                      StringLiteral [167-178]
+                        StringLiteralComponent('Transfers') [167-178]
+                TVFArgument [185-308]
+                  NamedArgument [185-308]
+                    Identifier(source_nodes) [185-197]
+                    ExpressionSubquery(modifier=ARRAY) [201-308]
+                      Query [207-308]
+                        GqlQuery [207-308]
+                          GraphTableQuery [207-308]
+                            GqlOperatorList [231-286]
+                              GqlOperatorList [231-286]
+                                GqlMatch [231-255]
+                                  GraphPattern [237-255]
+                                    GraphPathPattern [237-255]
+                                      GraphNodePattern [237-255]
+                                        GraphElementPatternFiller [238-254]
+                                          Identifier(n) [238-239]
+                                          GraphLabelFilter [239-247]
+                                            GraphElementLabel [240-247]
+                                              Identifier(Account) [240-247]
+                                          GraphPropertySpecification [248-254]
+                                            GraphPropertyNameAndValue [249-253]
+                                              Identifier(id) [249-251]
+                                              IntLiteral(7) [252-253]
+                                GqlReturn [278-286]
+                                  Select [285-286]
+                                    SelectList [285-286]
+                                      SelectColumn [285-286]
+                                        PathExpression [285-286]
+                                          Identifier(n) [285-286]
+              YieldItemList [313-330]
+                ExpressionWithOptAlias [319-323]
+                  Identifier(node) [319-323]
+                ExpressionWithOptAlias [325-330]
+                  Identifier(score) [325-330]
+            GqlReturn [331-352]
+              Select [338-352]
+                SelectList [338-352]
+                  SelectColumn [338-345]
+                    PathExpression [338-345]
+                      Identifier(node) [338-342]
+                      Identifier(id) [343-345]
+                  SelectColumn [347-352]
+                    PathExpression [347-352]
+                      Identifier(score) [347-352]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/algorithms#example-2
+EXPORT DATA OPTIONS (
+  uri = "gs://my-bucket-name/my-output.csv",
+  format = "csv"
+) AS
+GRAPH FinGraph
+CALL BetweennessCentrality(
+    node_labels => ['Account'], edge_labels => ['Transfers'], num_source_nodes => 5
+  ) YIELD node, centrality
+RETURN node.id, centrality
+--
+ExportDataStatement [0-269]
+  OptionsList [20-85]
+    OptionsEntry [24-65]
+      Identifier(uri) [24-27]
+      StringLiteral [30-65]
+        StringLiteralComponent("gs://my-bucket-name/my-output.csv") [30-65]
+    OptionsEntry [69-83]
+      Identifier(format) [69-75]
+      StringLiteral [78-83]
+        StringLiteralComponent("csv") [78-83]
+  Query [89-269]
+    GqlQuery [89-269]
+      GraphTableQuery [89-269]
+        PathExpression [95-103]
+          Identifier(FinGraph) [95-103]
+        GqlOperatorList [104-269]
+          GqlOperatorList [104-269]
+            GqlNamedCall [104-242]
+              TVF [109-219]
+                PathExpression [109-130]
+                  Identifier(BetweennessCentrality) [109-130]
+                TVFArgument [136-162]
+                  NamedArgument [136-162]
+                    Identifier(node_labels) [136-147]
+                    ArrayConstructor [151-162]
+                      StringLiteral [152-161]
+                        StringLiteralComponent('Account') [152-161]
+                TVFArgument [164-192]
+                  NamedArgument [164-192]
+                    Identifier(edge_labels) [164-175]
+                    ArrayConstructor [179-192]
+                      StringLiteral [180-191]
+                        StringLiteralComponent('Transfers') [180-191]
+                TVFArgument [194-215]
+                  NamedArgument [194-215]
+                    Identifier(num_source_nodes) [194-210]
+                    IntLiteral(5) [214-215]
+              YieldItemList [220-242]
+                ExpressionWithOptAlias [226-230]
+                  Identifier(node) [226-230]
+                ExpressionWithOptAlias [232-242]
+                  Identifier(centrality) [232-242]
+            GqlReturn [243-269]
+              Select [250-269]
+                SelectList [250-269]
+                  SelectColumn [250-257]
+                    PathExpression [250-257]
+                      Identifier(node) [250-254]
+                      Identifier(id) [255-257]
+                  SelectColumn [259-269]
+                    PathExpression [259-269]
+                      Identifier(centrality) [259-269]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/algorithms#example-3
+EXPORT DATA OPTIONS (
+  uri = "gs://my-bucket-name/my-output.csv",
+  format = "csv"
+) AS
+GRAPH FinGraph
+CALL ClosenessCentrality(
+    node_labels => ['Account'], edge_labels => ['Transfers'],
+    mode => 'EXACT'
+  ) YIELD node, centrality
+RETURN node.id, centrality
+--
+ExportDataStatement [0-265]
+  OptionsList [20-85]
+    OptionsEntry [24-65]
+      Identifier(uri) [24-27]
+      StringLiteral [30-65]
+        StringLiteralComponent("gs://my-bucket-name/my-output.csv") [30-65]
+    OptionsEntry [69-83]
+      Identifier(format) [69-75]
+      StringLiteral [78-83]
+        StringLiteralComponent("csv") [78-83]
+  Query [89-265]
+    GqlQuery [89-265]
+      GraphTableQuery [89-265]
+        PathExpression [95-103]
+          Identifier(FinGraph) [95-103]
+        GqlOperatorList [104-265]
+          GqlOperatorList [104-265]
+            GqlNamedCall [104-238]
+              TVF [109-215]
+                PathExpression [109-128]
+                  Identifier(ClosenessCentrality) [109-128]
+                TVFArgument [134-160]
+                  NamedArgument [134-160]
+                    Identifier(node_labels) [134-145]
+                    ArrayConstructor [149-160]
+                      StringLiteral [150-159]
+                        StringLiteralComponent('Account') [150-159]
+                TVFArgument [162-190]
+                  NamedArgument [162-190]
+                    Identifier(edge_labels) [162-173]
+                    ArrayConstructor [177-190]
+                      StringLiteral [178-189]
+                        StringLiteralComponent('Transfers') [178-189]
+                TVFArgument [196-211]
+                  NamedArgument [196-211]
+                    Identifier(mode) [196-200]
+                    StringLiteral [204-211]
+                      StringLiteralComponent('EXACT') [204-211]
+              YieldItemList [216-238]
+                ExpressionWithOptAlias [222-226]
+                  Identifier(node) [222-226]
+                ExpressionWithOptAlias [228-238]
+                  Identifier(centrality) [228-238]
+            GqlReturn [239-265]
+              Select [246-265]
+                SelectList [246-265]
+                  SelectColumn [246-253]
+                    PathExpression [246-253]
+                      Identifier(node) [246-250]
+                      Identifier(id) [251-253]
+                  SelectColumn [255-265]
+                    PathExpression [255-265]
+                      Identifier(centrality) [255-265]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/algorithms#example-4
+EXPORT DATA OPTIONS (
+  uri = "gs://my-bucket-name/wcc-output.csv",
+  format = "csv"
+) AS
+GRAPH FinGraph
+CALL WeaklyConnectedComponents(
+    node_labels => ['Account'], edge_labels => ['Transfers']
+  ) YIELD node, cluster
+RETURN node.id, cluster
+--
+ExportDataStatement [0-245]
+  OptionsList [20-86]
+    OptionsEntry [24-66]
+      Identifier(uri) [24-27]
+      StringLiteral [30-66]
+        StringLiteralComponent("gs://my-bucket-name/wcc-output.csv") [30-66]
+    OptionsEntry [70-84]
+      Identifier(format) [70-76]
+      StringLiteral [79-84]
+        StringLiteralComponent("csv") [79-84]
+  Query [90-245]
+    GqlQuery [90-245]
+      GraphTableQuery [90-245]
+        PathExpression [96-104]
+          Identifier(FinGraph) [96-104]
+        GqlOperatorList [105-245]
+          GqlOperatorList [105-245]
+            GqlNamedCall [105-221]
+              TVF [110-201]
+                PathExpression [110-135]
+                  Identifier(WeaklyConnectedComponents) [110-135]
+                TVFArgument [141-167]
+                  NamedArgument [141-167]
+                    Identifier(node_labels) [141-152]
+                    ArrayConstructor [156-167]
+                      StringLiteral [157-166]
+                        StringLiteralComponent('Account') [157-166]
+                TVFArgument [169-197]
+                  NamedArgument [169-197]
+                    Identifier(edge_labels) [169-180]
+                    ArrayConstructor [184-197]
+                      StringLiteral [185-196]
+                        StringLiteralComponent('Transfers') [185-196]
+              YieldItemList [202-221]
+                ExpressionWithOptAlias [208-212]
+                  Identifier(node) [208-212]
+                ExpressionWithOptAlias [214-221]
+                  Identifier(cluster) [214-221]
+            GqlReturn [222-245]
+              Select [229-245]
+                SelectList [229-245]
+                  SelectColumn [229-236]
+                    PathExpression [229-236]
+                      Identifier(node) [229-233]
+                      Identifier(id) [234-236]
+                  SelectColumn [238-245]
+                    PathExpression [238-245]
+                      Identifier(cluster) [238-245]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/algorithms#example-5
+EXPORT DATA OPTIONS (
+  uri = "gs://my-bucket-name/modularity-output.csv",
+  format = "csv"
+) AS
+GRAPH FinGraph
+CALL ModularityClustering(
+    node_labels => ['Account'], edge_labels => ['Transfers'],
+    resolution => 1.0, max_iterations => 10
+  ) YIELD node, cluster
+RETURN node.id, cluster
+--
+ExportDataStatement [0-292]
+  OptionsList [20-93]
+    OptionsEntry [24-73]
+      Identifier(uri) [24-27]
+      StringLiteral [30-73]
+        StringLiteralComponent("gs://my-bucket-name/modularity-output.csv") [30-73]
+    OptionsEntry [77-91]
+      Identifier(format) [77-83]
+      StringLiteral [86-91]
+        StringLiteralComponent("csv") [86-91]
+  Query [97-292]
+    GqlQuery [97-292]
+      GraphTableQuery [97-292]
+        PathExpression [103-111]
+          Identifier(FinGraph) [103-111]
+        GqlOperatorList [112-292]
+          GqlOperatorList [112-292]
+            GqlNamedCall [112-268]
+              TVF [117-248]
+                PathExpression [117-137]
+                  Identifier(ModularityClustering) [117-137]
+                TVFArgument [143-169]
+                  NamedArgument [143-169]
+                    Identifier(node_labels) [143-154]
+                    ArrayConstructor [158-169]
+                      StringLiteral [159-168]
+                        StringLiteralComponent('Account') [159-168]
+                TVFArgument [171-199]
+                  NamedArgument [171-199]
+                    Identifier(edge_labels) [171-182]
+                    ArrayConstructor [186-199]
+                      StringLiteral [187-198]
+                        StringLiteralComponent('Transfers') [187-198]
+                TVFArgument [205-222]
+                  NamedArgument [205-222]
+                    Identifier(resolution) [205-215]
+                    FloatLiteral(1.0) [219-222]
+                TVFArgument [224-244]
+                  NamedArgument [224-244]
+                    Identifier(max_iterations) [224-238]
+                    IntLiteral(10) [242-244]
+              YieldItemList [249-268]
+                ExpressionWithOptAlias [255-259]
+                  Identifier(node) [255-259]
+                ExpressionWithOptAlias [261-268]
+                  Identifier(cluster) [261-268]
+            GqlReturn [269-292]
+              Select [276-292]
+                SelectList [276-292]
+                  SelectColumn [276-283]
+                    PathExpression [276-283]
+                      Identifier(node) [276-280]
+                      Identifier(id) [281-283]
+                  SelectColumn [285-292]
+                    PathExpression [285-292]
+                      Identifier(cluster) [285-292]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/algorithms#example-6
+EXPORT DATA OPTIONS (
+  uri = "gs://my-bucket-name/correlation-output.csv",
+  format = "csv"
+) AS
+GRAPH FinGraph
+CALL CorrelationClustering(
+    node_labels => ['Account'], edge_labels => ['Transfers'],
+    resolution => 0.5
+  ) YIELD node, cluster
+RETURN node.id, cluster
+--
+ExportDataStatement [0-272]
+  OptionsList [20-94]
+    OptionsEntry [24-74]
+      Identifier(uri) [24-27]
+      StringLiteral [30-74]
+        StringLiteralComponent("gs://my-bucket-name/correlation-output.csv") [30-74]
+    OptionsEntry [78-92]
+      Identifier(format) [78-84]
+      StringLiteral [87-92]
+        StringLiteralComponent("csv") [87-92]
+  Query [98-272]
+    GqlQuery [98-272]
+      GraphTableQuery [98-272]
+        PathExpression [104-112]
+          Identifier(FinGraph) [104-112]
+        GqlOperatorList [113-272]
+          GqlOperatorList [113-272]
+            GqlNamedCall [113-248]
+              TVF [118-228]
+                PathExpression [118-139]
+                  Identifier(CorrelationClustering) [118-139]
+                TVFArgument [145-171]
+                  NamedArgument [145-171]
+                    Identifier(node_labels) [145-156]
+                    ArrayConstructor [160-171]
+                      StringLiteral [161-170]
+                        StringLiteralComponent('Account') [161-170]
+                TVFArgument [173-201]
+                  NamedArgument [173-201]
+                    Identifier(edge_labels) [173-184]
+                    ArrayConstructor [188-201]
+                      StringLiteral [189-200]
+                        StringLiteralComponent('Transfers') [189-200]
+                TVFArgument [207-224]
+                  NamedArgument [207-224]
+                    Identifier(resolution) [207-217]
+                    FloatLiteral(0.5) [221-224]
+              YieldItemList [229-248]
+                ExpressionWithOptAlias [235-239]
+                  Identifier(node) [235-239]
+                ExpressionWithOptAlias [241-248]
+                  Identifier(cluster) [241-248]
+            GqlReturn [249-272]
+              Select [256-272]
+                SelectList [256-272]
+                  SelectColumn [256-263]
+                    PathExpression [256-263]
+                      Identifier(node) [256-260]
+                      Identifier(id) [261-263]
+                  SelectColumn [265-272]
+                    PathExpression [265-272]
+                      Identifier(cluster) [265-272]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/algorithms#example-7
+EXPORT DATA OPTIONS (
+  uri = "gs://my-bucket-name/lp-output.csv",
+  format = "csv"
+) AS
+GRAPH FinGraph
+CALL LabelPropagation(
+    node_labels => ['Account'], edge_labels => ['Transfers'],
+    max_iterations => 10
+  ) YIELD node, cluster
+RETURN node.id, cluster
+--
+ExportDataStatement [0-261]
+  OptionsList [20-85]
+    OptionsEntry [24-65]
+      Identifier(uri) [24-27]
+      StringLiteral [30-65]
+        StringLiteralComponent("gs://my-bucket-name/lp-output.csv") [30-65]
+    OptionsEntry [69-83]
+      Identifier(format) [69-75]
+      StringLiteral [78-83]
+        StringLiteralComponent("csv") [78-83]
+  Query [89-261]
+    GqlQuery [89-261]
+      GraphTableQuery [89-261]
+        PathExpression [95-103]
+          Identifier(FinGraph) [95-103]
+        GqlOperatorList [104-261]
+          GqlOperatorList [104-261]
+            GqlNamedCall [104-237]
+              TVF [109-217]
+                PathExpression [109-125]
+                  Identifier(LabelPropagation) [109-125]
+                TVFArgument [131-157]
+                  NamedArgument [131-157]
+                    Identifier(node_labels) [131-142]
+                    ArrayConstructor [146-157]
+                      StringLiteral [147-156]
+                        StringLiteralComponent('Account') [147-156]
+                TVFArgument [159-187]
+                  NamedArgument [159-187]
+                    Identifier(edge_labels) [159-170]
+                    ArrayConstructor [174-187]
+                      StringLiteral [175-186]
+                        StringLiteralComponent('Transfers') [175-186]
+                TVFArgument [193-213]
+                  NamedArgument [193-213]
+                    Identifier(max_iterations) [193-207]
+                    IntLiteral(10) [211-213]
+              YieldItemList [218-237]
+                ExpressionWithOptAlias [224-228]
+                  Identifier(node) [224-228]
+                ExpressionWithOptAlias [230-237]
+                  Identifier(cluster) [230-237]
+            GqlReturn [238-261]
+              Select [245-261]
+                SelectList [245-261]
+                  SelectColumn [245-252]
+                    PathExpression [245-252]
+                      Identifier(node) [245-249]
+                      Identifier(id) [250-252]
+                  SelectColumn [254-261]
+                    PathExpression [254-261]
+                      Identifier(cluster) [254-261]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/algorithms#example-8
+EXPORT DATA OPTIONS (
+  uri = "gs://my-bucket-name/clique-output.csv",
+  format = "csv"
+) AS
+GRAPH FinGraph
+CALL CliqueFinding(
+    node_labels => ['Account'], edge_labels => ['Transfers'],
+    min_density => 0.9
+  ) YIELD node, clique
+RETURN node.id, clique
+--
+ExportDataStatement [0-258]
+  OptionsList [20-89]
+    OptionsEntry [24-69]
+      Identifier(uri) [24-27]
+      StringLiteral [30-69]
+        StringLiteralComponent("gs://my-bucket-name/clique-output.csv") [30-69]
+    OptionsEntry [73-87]
+      Identifier(format) [73-79]
+      StringLiteral [82-87]
+        StringLiteralComponent("csv") [82-87]
+  Query [93-258]
+    GqlQuery [93-258]
+      GraphTableQuery [93-258]
+        PathExpression [99-107]
+          Identifier(FinGraph) [99-107]
+        GqlOperatorList [108-258]
+          GqlOperatorList [108-258]
+            GqlNamedCall [108-235]
+              TVF [113-216]
+                PathExpression [113-126]
+                  Identifier(CliqueFinding) [113-126]
+                TVFArgument [132-158]
+                  NamedArgument [132-158]
+                    Identifier(node_labels) [132-143]
+                    ArrayConstructor [147-158]
+                      StringLiteral [148-157]
+                        StringLiteralComponent('Account') [148-157]
+                TVFArgument [160-188]
+                  NamedArgument [160-188]
+                    Identifier(edge_labels) [160-171]
+                    ArrayConstructor [175-188]
+                      StringLiteral [176-187]
+                        StringLiteralComponent('Transfers') [176-187]
+                TVFArgument [194-212]
+                  NamedArgument [194-212]
+                    Identifier(min_density) [194-205]
+                    FloatLiteral(0.9) [209-212]
+              YieldItemList [217-235]
+                ExpressionWithOptAlias [223-227]
+                  Identifier(node) [223-227]
+                ExpressionWithOptAlias [229-235]
+                  Identifier(clique) [229-235]
+            GqlReturn [236-258]
+              Select [243-258]
+                SelectList [243-258]
+                  SelectColumn [243-250]
+                    PathExpression [243-250]
+                      Identifier(node) [243-247]
+                      Identifier(id) [248-250]
+                  SelectColumn [252-258]
+                    PathExpression [252-258]
+                      Identifier(clique) [252-258]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/algorithms#example-9
+EXPORT DATA OPTIONS (
+  uri = "gs://my-bucket-name/jaccard-output.csv",
+  format = "csv"
+) AS
+GRAPH FinGraph
+CALL JaccardSimilarity(
+    source_nodes => ARRAY {
+                      MATCH (n:Account {id: 7})
+                      RETURN n
+                    },
+    target_nodes => ARRAY {
+                      MATCH (n:Account)
+                      WHERE n.id != 7
+                      RETURN n
+                    }
+  ) YIELD source_node, target_node, similarity
+RETURN source_node.id AS source_id, target_node.id AS target_id, similarity
+--
+ExportDataStatement [0-544]
+  OptionsList [20-90]
+    OptionsEntry [24-70]
+      Identifier(uri) [24-27]
+      StringLiteral [30-70]
+        StringLiteralComponent("gs://my-bucket-name/jaccard-output.csv") [30-70]
+    OptionsEntry [74-88]
+      Identifier(format) [74-80]
+      StringLiteral [83-88]
+        StringLiteralComponent("csv") [83-88]
+  Query [94-544]
+    GqlQuery [94-544]
+      GraphTableQuery [94-544]
+        PathExpression [100-108]
+          Identifier(FinGraph) [100-108]
+        GqlOperatorList [109-544]
+          GqlOperatorList [109-544]
+            GqlNamedCall [109-468]
+              TVF [114-425]
+                PathExpression [114-131]
+                  Identifier(JaccardSimilarity) [114-131]
+                TVFArgument [137-261]
+                  NamedArgument [137-261]
+                    Identifier(source_nodes) [137-149]
+                    ExpressionSubquery(modifier=ARRAY) [153-261]
+                      Query [159-261]
+                        GqlQuery [159-261]
+                          GraphTableQuery [159-261]
+                            GqlOperatorList [183-239]
+                              GqlOperatorList [183-239]
+                                GqlMatch [183-208]
+                                  GraphPattern [189-208]
+                                    GraphPathPattern [189-208]
+                                      GraphNodePattern [189-208]
+                                        GraphElementPatternFiller [190-207]
+                                          Identifier(n) [190-191]
+                                          GraphLabelFilter [191-199]
+                                            GraphElementLabel [192-199]
+                                              Identifier(Account) [192-199]
+                                          GraphPropertySpecification [200-207]
+                                            GraphPropertyNameAndValue [201-206]
+                                              Identifier(id) [201-203]
+                                              IntLiteral(7) [205-206]
+                                GqlReturn [231-239]
+                                  Select [238-239]
+                                    SelectList [238-239]
+                                      SelectColumn [238-239]
+                                        PathExpression [238-239]
+                                          Identifier(n) [238-239]
+                TVFArgument [267-421]
+                  NamedArgument [267-421]
+                    Identifier(target_nodes) [267-279]
+                    ExpressionSubquery(modifier=ARRAY) [283-421]
+                      Query [289-421]
+                        GqlQuery [289-421]
+                          GraphTableQuery [289-421]
+                            GqlOperatorList [313-399]
+                              GqlOperatorList [313-399]
+                                GqlMatch [313-368]
+                                  GraphPattern [319-368]
+                                    GraphPathPattern [319-330]
+                                      GraphNodePattern [319-330]
+                                        GraphElementPatternFiller [320-329]
+                                          Identifier(n) [320-321]
+                                          GraphLabelFilter [321-329]
+                                            GraphElementLabel [322-329]
+                                              Identifier(Account) [322-329]
+                                    WhereClause [353-368]
+                                      BinaryExpression(!=) [359-368]
+                                        PathExpression [359-363]
+                                          Identifier(n) [359-360]
+                                          Identifier(id) [361-363]
+                                        IntLiteral(7) [367-368]
+                                GqlReturn [391-399]
+                                  Select [398-399]
+                                    SelectList [398-399]
+                                      SelectColumn [398-399]
+                                        PathExpression [398-399]
+                                          Identifier(n) [398-399]
+              YieldItemList [426-468]
+                ExpressionWithOptAlias [432-443]
+                  Identifier(source_node) [432-443]
+                ExpressionWithOptAlias [445-456]
+                  Identifier(target_node) [445-456]
+                ExpressionWithOptAlias [458-468]
+                  Identifier(similarity) [458-468]
+            GqlReturn [469-544]
+              Select [476-544]
+                SelectList [476-544]
+                  SelectColumn [476-503]
+                    PathExpression [476-490]
+                      Identifier(source_node) [476-487]
+                      Identifier(id) [488-490]
+                    Alias [491-503]
+                      Identifier(source_id) [494-503]
+                  SelectColumn [505-532]
+                    PathExpression [505-519]
+                      Identifier(target_node) [505-516]
+                      Identifier(id) [517-519]
+                    Alias [520-532]
+                      Identifier(target_id) [523-532]
+                  SelectColumn [534-544]
+                    PathExpression [534-544]
+                      Identifier(similarity) [534-544]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/algorithms#example-10
+EXPORT DATA OPTIONS (
+  uri = "gs://my-bucket-name/shortest-path-output.csv",
+  format = "csv"
+) AS
+GRAPH FinGraph
+CALL ShortestPath(
+    source_nodes => ARRAY {
+                      MATCH (n:Account {id: 7})
+                      RETURN n
+                    },
+    target_nodes => ARRAY {
+                      MATCH (n:Account {id: 16})
+                      RETURN n
+                    }
+  ) YIELD source_node, target_node, path, cost
+RETURN source_node.id AS source_id, target_node.id AS target_id, PATH_LENGTH(path) AS length, cost
+--
+ExportDataStatement [0-539]
+  OptionsList [20-96]
+    OptionsEntry [24-76]
+      Identifier(uri) [24-27]
+      StringLiteral [30-76]
+        StringLiteralComponent("gs://my-bucket-name/shortest-path-output.csv") [30-76]
+    OptionsEntry [80-94]
+      Identifier(format) [80-86]
+      StringLiteral [89-94]
+        StringLiteralComponent("csv") [89-94]
+  Query [100-539]
+    GqlQuery [100-539]
+      GraphTableQuery [100-539]
+        PathExpression [106-114]
+          Identifier(FinGraph) [106-114]
+        GqlOperatorList [115-539]
+          GqlOperatorList [115-539]
+            GqlNamedCall [115-440]
+              TVF [120-397]
+                PathExpression [120-132]
+                  Identifier(ShortestPath) [120-132]
+                TVFArgument [138-262]
+                  NamedArgument [138-262]
+                    Identifier(source_nodes) [138-150]
+                    ExpressionSubquery(modifier=ARRAY) [154-262]
+                      Query [160-262]
+                        GqlQuery [160-262]
+                          GraphTableQuery [160-262]
+                            GqlOperatorList [184-240]
+                              GqlOperatorList [184-240]
+                                GqlMatch [184-209]
+                                  GraphPattern [190-209]
+                                    GraphPathPattern [190-209]
+                                      GraphNodePattern [190-209]
+                                        GraphElementPatternFiller [191-208]
+                                          Identifier(n) [191-192]
+                                          GraphLabelFilter [192-200]
+                                            GraphElementLabel [193-200]
+                                              Identifier(Account) [193-200]
+                                          GraphPropertySpecification [201-208]
+                                            GraphPropertyNameAndValue [202-207]
+                                              Identifier(id) [202-204]
+                                              IntLiteral(7) [206-207]
+                                GqlReturn [232-240]
+                                  Select [239-240]
+                                    SelectList [239-240]
+                                      SelectColumn [239-240]
+                                        PathExpression [239-240]
+                                          Identifier(n) [239-240]
+                TVFArgument [268-393]
+                  NamedArgument [268-393]
+                    Identifier(target_nodes) [268-280]
+                    ExpressionSubquery(modifier=ARRAY) [284-393]
+                      Query [290-393]
+                        GqlQuery [290-393]
+                          GraphTableQuery [290-393]
+                            GqlOperatorList [314-371]
+                              GqlOperatorList [314-371]
+                                GqlMatch [314-340]
+                                  GraphPattern [320-340]
+                                    GraphPathPattern [320-340]
+                                      GraphNodePattern [320-340]
+                                        GraphElementPatternFiller [321-339]
+                                          Identifier(n) [321-322]
+                                          GraphLabelFilter [322-330]
+                                            GraphElementLabel [323-330]
+                                              Identifier(Account) [323-330]
+                                          GraphPropertySpecification [331-339]
+                                            GraphPropertyNameAndValue [332-338]
+                                              Identifier(id) [332-334]
+                                              IntLiteral(16) [336-338]
+                                GqlReturn [363-371]
+                                  Select [370-371]
+                                    SelectList [370-371]
+                                      SelectColumn [370-371]
+                                        PathExpression [370-371]
+                                          Identifier(n) [370-371]
+              YieldItemList [398-440]
+                ExpressionWithOptAlias [404-415]
+                  Identifier(source_node) [404-415]
+                ExpressionWithOptAlias [417-428]
+                  Identifier(target_node) [417-428]
+                ExpressionWithOptAlias [430-434]
+                  Identifier(path) [430-434]
+                ExpressionWithOptAlias [436-440]
+                  Identifier(cost) [436-440]
+            GqlReturn [441-539]
+              Select [448-539]
+                SelectList [448-539]
+                  SelectColumn [448-475]
+                    PathExpression [448-462]
+                      Identifier(source_node) [448-459]
+                      Identifier(id) [460-462]
+                    Alias [463-475]
+                      Identifier(source_id) [466-475]
+                  SelectColumn [477-504]
+                    PathExpression [477-491]
+                      Identifier(target_node) [477-488]
+                      Identifier(id) [489-491]
+                    Alias [492-504]
+                      Identifier(target_id) [495-504]
+                  SelectColumn [506-533]
+                    FunctionCall [506-523]
+                      PathExpression [506-517]
+                        Identifier(PATH_LENGTH) [506-517]
+                      PathExpression [518-522]
+                        Identifier(path) [518-522]
+                    Alias [524-533]
+                      Identifier(length) [527-533]
+                  SelectColumn [535-539]
+                    PathExpression [535-539]
+                      Identifier(cost) [535-539]
diff --git a/parser/testdata/spanner/guides/graph.best-practices-designing-schema.spanner.sql b/parser/testdata/spanner/guides/graph.best-practices-designing-schema.spanner.sql
new file mode 100644
index 0000000..06a2c75
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.best-practices-designing-schema.spanner.sql
@@ -0,0 +1,31 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#ttl-on-nodes-and-edges
+# id af7e3c0e2324: row deletion policy (TTL) (graph.best-practices-designing-schema)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected keyword INTERLEAVE but got keyword ROW [at 6:5]
+CREATE TABLE Account (
+    id               INT64 NOT NULL,
+    create_time      TIMESTAMP,
+    close_time       TIMESTAMP,
+  ) PRIMARY KEY (id),
+    ROW DELETION POLICY (OLDER_THAN(close_time, INTERVAL 90 DAY));
+
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#ttl-on-nodes-and-edges
+# id 9c7464fea28c: row deletion policy (TTL) (graph.best-practices-designing-schema)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ";" or end of input but got "," [at 8:49]
+CREATE TABLE AccountTransferAccount (
+  id               INT64 NOT NULL,
+  to_id            INT64 NOT NULL,
+  amount           FLOAT64,
+  create_time      TIMESTAMP NOT NULL,
+  order_number     STRING(MAX),
+) PRIMARY KEY (id, to_id),
+  INTERLEAVE IN PARENT Account ON DELETE CASCADE,
+  ROW DELETION POLICY (OLDER_THAN(create_time, INTERVAL 3650 DAY));
diff --git a/parser/testdata/spanner/guides/graph.best-practices-designing-schema.test b/parser/testdata/spanner/guides/graph.best-practices-designing-schema.test
new file mode 100644
index 0000000..f6590fb
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.best-practices-designing-schema.test
@@ -0,0 +1,1058 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#choose-design-based-on-queries
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+GRAPH FinGraph
+MATCH p = (:Account {id:1})-[:Transfer|Withdraw]->{1,3}(:Account)
+RETURN TO_JSON(p) AS p
+--
+QueryStatement [0-103]
+  Query [0-103]
+    GqlQuery [0-103]
+      GraphTableQuery [0-103]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-103]
+          GqlOperatorList [15-103]
+            GqlMatch [15-80]
+              GraphPattern [21-80]
+                GraphPathPattern [21-80]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [25-42]
+                    GraphElementPatternFiller [26-41]
+                      GraphLabelFilter [26-34]
+                        GraphElementLabel [27-34]
+                          Identifier(Account) [27-34]
+                      GraphPropertySpecification [35-41]
+                        GraphPropertyNameAndValue [36-40]
+                          Identifier(id) [36-38]
+                          IntLiteral(1) [39-40]
+                  ParenthesizedGraphPathPattern [42-70]
+                    BoundedQuantifier [65-70]
+                      QuantifierBound [66-67]
+                        IntLiteral(1) [66-67]
+                      QuantifierBound [68-69]
+                        IntLiteral(3) [68-69]
+                    GraphEdgePattern [42-65]
+                      GraphElementPatternFiller [44-62]
+                        GraphLabelFilter [44-62]
+                          GraphLabelOperation(OR) [45-62]
+                            GraphElementLabel [45-53]
+                              Identifier(Transfer) [45-53]
+                            GraphElementLabel [54-62]
+                              Identifier(Withdraw) [54-62]
+                  GraphNodePattern [70-80]
+                    GraphElementPatternFiller [71-79]
+                      GraphLabelFilter [71-79]
+                        GraphElementLabel [72-79]
+                          Identifier(Account) [72-79]
+            GqlReturn [81-103]
+              Select [88-103]
+                SelectList [88-103]
+                  SelectColumn [88-103]
+                    FunctionCall [88-98]
+                      PathExpression [88-95]
+                        Identifier(TO_JSON) [88-95]
+                      PathExpression [96-97]
+                        Identifier(p) [96-97]
+                    Alias [99-103]
+                      Identifier(p) [102-103]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#choose-design-based-on-queries
+GRAPH FinGraph
+MATCH p = (:Person {id:1})-[:Owns|Transfers]->{1,3}(:Account)
+RETURN TO_JSON(p) AS p
+--
+QueryStatement [0-99]
+  Query [0-99]
+    GqlQuery [0-99]
+      GraphTableQuery [0-99]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-99]
+          GqlOperatorList [15-99]
+            GqlMatch [15-76]
+              GraphPattern [21-76]
+                GraphPathPattern [21-76]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [25-41]
+                    GraphElementPatternFiller [26-40]
+                      GraphLabelFilter [26-33]
+                        GraphElementLabel [27-33]
+                          Identifier(Person) [27-33]
+                      GraphPropertySpecification [34-40]
+                        GraphPropertyNameAndValue [35-39]
+                          Identifier(id) [35-37]
+                          IntLiteral(1) [38-39]
+                  ParenthesizedGraphPathPattern [41-66]
+                    BoundedQuantifier [61-66]
+                      QuantifierBound [62-63]
+                        IntLiteral(1) [62-63]
+                      QuantifierBound [64-65]
+                        IntLiteral(3) [64-65]
+                    GraphEdgePattern [41-61]
+                      GraphElementPatternFiller [43-58]
+                        GraphLabelFilter [43-58]
+                          GraphLabelOperation(OR) [44-58]
+                            GraphElementLabel [44-48]
+                              Identifier(Owns) [44-48]
+                            GraphElementLabel [49-58]
+                              Identifier(Transfers) [49-58]
+                  GraphNodePattern [66-76]
+                    GraphElementPatternFiller [67-75]
+                      GraphLabelFilter [67-75]
+                        GraphElementLabel [68-75]
+                          Identifier(Account) [68-75]
+            GqlReturn [77-99]
+              Select [84-99]
+                SelectList [84-99]
+                  SelectColumn [84-99]
+                    FunctionCall [84-94]
+                      PathExpression [84-91]
+                        Identifier(TO_JSON) [84-91]
+                      PathExpression [92-93]
+                        Identifier(p) [92-93]
+                    Alias [95-99]
+                      Identifier(p) [98-99]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#choose-design-based-on-queries
+GRAPH FinGraph
+MATCH p = (:Person {id:1})-[]->{1,3}(:Account)
+RETURN TO_JSON(p) AS p
+--
+QueryStatement [0-84]
+  Query [0-84]
+    GqlQuery [0-84]
+      GraphTableQuery [0-84]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-84]
+          GqlOperatorList [15-84]
+            GqlMatch [15-61]
+              GraphPattern [21-61]
+                GraphPathPattern [21-61]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [25-41]
+                    GraphElementPatternFiller [26-40]
+                      GraphLabelFilter [26-33]
+                        GraphElementLabel [27-33]
+                          Identifier(Person) [27-33]
+                      GraphPropertySpecification [34-40]
+                        GraphPropertyNameAndValue [35-39]
+                          Identifier(id) [35-37]
+                          IntLiteral(1) [38-39]
+                  ParenthesizedGraphPathPattern [41-51]
+                    BoundedQuantifier [46-51]
+                      QuantifierBound [47-48]
+                        IntLiteral(1) [47-48]
+                      QuantifierBound [49-50]
+                        IntLiteral(3) [49-50]
+                    GraphEdgePattern [41-46]
+                      GraphElementPatternFiller [43-43]
+                  GraphNodePattern [51-61]
+                    GraphElementPatternFiller [52-60]
+                      GraphLabelFilter [52-60]
+                        GraphElementLabel [53-60]
+                          Identifier(Account) [53-60]
+            GqlReturn [62-84]
+              Select [69-84]
+                SelectList [69-84]
+                  SelectColumn [69-84]
+                    FunctionCall [69-79]
+                      PathExpression [69-76]
+                        Identifier(TO_JSON) [69-76]
+                      PathExpression [77-78]
+                        Identifier(p) [77-78]
+                    Alias [80-84]
+                      Identifier(p) [83-84]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#choose-design-based-on-queries
+GRAPH FinGraph
+MATCH p = (:Account {id:1})-[:Owns]-{1,3}(:Account)
+RETURN TO_JSON(p) AS p
+--
+QueryStatement [0-89]
+  Query [0-89]
+    GqlQuery [0-89]
+      GraphTableQuery [0-89]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-89]
+          GqlOperatorList [15-89]
+            GqlMatch [15-66]
+              GraphPattern [21-66]
+                GraphPathPattern [21-66]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [25-42]
+                    GraphElementPatternFiller [26-41]
+                      GraphLabelFilter [26-34]
+                        GraphElementLabel [27-34]
+                          Identifier(Account) [27-34]
+                      GraphPropertySpecification [35-41]
+                        GraphPropertyNameAndValue [36-40]
+                          Identifier(id) [36-38]
+                          IntLiteral(1) [39-40]
+                  ParenthesizedGraphPathPattern [42-56]
+                    BoundedQuantifier [51-56]
+                      QuantifierBound [52-53]
+                        IntLiteral(1) [52-53]
+                      QuantifierBound [54-55]
+                        IntLiteral(3) [54-55]
+                    GraphEdgePattern [42-51]
+                      GraphElementPatternFiller [44-49]
+                        GraphLabelFilter [44-49]
+                          GraphElementLabel [45-49]
+                            Identifier(Owns) [45-49]
+                  GraphNodePattern [56-66]
+                    GraphElementPatternFiller [57-65]
+                      GraphLabelFilter [57-65]
+                        GraphElementLabel [58-65]
+                          Identifier(Account) [58-65]
+            GqlReturn [67-89]
+              Select [74-89]
+                SelectList [74-89]
+                  SelectColumn [74-89]
+                    FunctionCall [74-84]
+                      PathExpression [74-81]
+                        Identifier(TO_JSON) [74-81]
+                      PathExpression [82-83]
+                        Identifier(p) [82-83]
+                    Alias [85-89]
+                      Identifier(p) [88-89]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#choose-design-based-on-queries
+GRAPH FinGraph
+MATCH p = (:Person {id:1})-[:Owns]->(:Account)-[:Transfer]->{1,3}(:Account)
+RETURN TO_JSON(p) AS p
+--
+QueryStatement [0-113]
+  Query [0-113]
+    GqlQuery [0-113]
+      GraphTableQuery [0-113]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-113]
+          GqlOperatorList [15-113]
+            GqlMatch [15-90]
+              GraphPattern [21-90]
+                GraphPathPattern [21-90]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [25-41]
+                    GraphElementPatternFiller [26-40]
+                      GraphLabelFilter [26-33]
+                        GraphElementLabel [27-33]
+                          Identifier(Person) [27-33]
+                      GraphPropertySpecification [34-40]
+                        GraphPropertyNameAndValue [35-39]
+                          Identifier(id) [35-37]
+                          IntLiteral(1) [38-39]
+                  GraphEdgePattern [41-51]
+                    GraphElementPatternFiller [43-48]
+                      GraphLabelFilter [43-48]
+                        GraphElementLabel [44-48]
+                          Identifier(Owns) [44-48]
+                  GraphNodePattern [51-61]
+                    GraphElementPatternFiller [52-60]
+                      GraphLabelFilter [52-60]
+                        GraphElementLabel [53-60]
+                          Identifier(Account) [53-60]
+                  ParenthesizedGraphPathPattern [61-80]
+                    BoundedQuantifier [75-80]
+                      QuantifierBound [76-77]
+                        IntLiteral(1) [76-77]
+                      QuantifierBound [78-79]
+                        IntLiteral(3) [78-79]
+                    GraphEdgePattern [61-75]
+                      GraphElementPatternFiller [63-72]
+                        GraphLabelFilter [63-72]
+                          GraphElementLabel [64-72]
+                            Identifier(Transfer) [64-72]
+                  GraphNodePattern [80-90]
+                    GraphElementPatternFiller [81-89]
+                      GraphLabelFilter [81-89]
+                        GraphElementLabel [82-89]
+                          Identifier(Account) [82-89]
+            GqlReturn [91-113]
+              Select [98-113]
+                SelectList [98-113]
+                  SelectColumn [98-113]
+                    FunctionCall [98-108]
+                      PathExpression [98-105]
+                        Identifier(TO_JSON) [98-105]
+                      PathExpression [106-107]
+                        Identifier(p) [106-107]
+                    Alias [109-113]
+                      Identifier(p) [112-113]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#forward_and_reverse_edge_traversal_query_examples
+GRAPH FinGraph
+MATCH (person:Person {id: 1})-[owns:Owns]->(accnt:Account)
+RETURN accnt.id
+--
+QueryStatement [0-89]
+  Query [0-89]
+    GqlQuery [0-89]
+      GraphTableQuery [0-89]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-89]
+          GqlOperatorList [15-89]
+            GqlMatch [15-73]
+              GraphPattern [21-73]
+                GraphPathPattern [21-73]
+                  GraphNodePattern [21-44]
+                    GraphElementPatternFiller [22-43]
+                      Identifier(person) [22-28]
+                      GraphLabelFilter [28-35]
+                        GraphElementLabel [29-35]
+                          Identifier(Person) [29-35]
+                      GraphPropertySpecification [36-43]
+                        GraphPropertyNameAndValue [37-42]
+                          Identifier(id) [37-39]
+                          IntLiteral(1) [41-42]
+                  GraphEdgePattern [44-58]
+                    GraphElementPatternFiller [46-55]
+                      Identifier(owns) [46-50]
+                      GraphLabelFilter [50-55]
+                        GraphElementLabel [51-55]
+                          Identifier(Owns) [51-55]
+                  GraphNodePattern [58-73]
+                    GraphElementPatternFiller [59-72]
+                      Identifier(accnt) [59-64]
+                      GraphLabelFilter [64-72]
+                        GraphElementLabel [65-72]
+                          Identifier(Account) [65-72]
+            GqlReturn [74-89]
+              Select [81-89]
+                SelectList [81-89]
+                  SelectColumn [81-89]
+                    PathExpression [81-89]
+                      Identifier(accnt) [81-86]
+                      Identifier(id) [87-89]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#forward_and_reverse_edge_traversal_query_examples
+GRAPH FinGraph
+MATCH (accnt:Account {id: 1})<-[owns:Owns]-(person:Person)
+RETURN person.name
+--
+QueryStatement [0-92]
+  Query [0-92]
+    GqlQuery [0-92]
+      GraphTableQuery [0-92]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-92]
+          GqlOperatorList [15-92]
+            GqlMatch [15-73]
+              GraphPattern [21-73]
+                GraphPathPattern [21-73]
+                  GraphNodePattern [21-44]
+                    GraphElementPatternFiller [22-43]
+                      Identifier(accnt) [22-27]
+                      GraphLabelFilter [27-35]
+                        GraphElementLabel [28-35]
+                          Identifier(Account) [28-35]
+                      GraphPropertySpecification [36-43]
+                        GraphPropertyNameAndValue [37-42]
+                          Identifier(id) [37-39]
+                          IntLiteral(1) [41-42]
+                  GraphEdgePattern [44-58]
+                    GraphElementPatternFiller [47-56]
+                      Identifier(owns) [47-51]
+                      GraphLabelFilter [51-56]
+                        GraphElementLabel [52-56]
+                          Identifier(Owns) [52-56]
+                  GraphNodePattern [58-73]
+                    GraphElementPatternFiller [59-72]
+                      Identifier(person) [59-65]
+                      GraphLabelFilter [65-72]
+                        GraphElementLabel [66-72]
+                          Identifier(Person) [66-72]
+            GqlReturn [74-92]
+              Select [81-92]
+                SelectList [81-92]
+                  SelectColumn [81-92]
+                    PathExpression [81-92]
+                      Identifier(person) [81-87]
+                      Identifier(name) [88-92]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#optimize-forward-edge-traversal
+CREATE TABLE Person (
+  id               INT64 NOT NULL,
+  name             STRING(MAX),
+) PRIMARY KEY (id)
+--
+CreateTableStatement [0-107]
+  PathExpression [13-19]
+    Identifier(Person) [13-19]
+  TableElementList [20-90]
+    ColumnDefinition [24-55]
+      Identifier(id) [24-26]
+      SimpleColumnSchema [41-55]
+        PathExpression [41-46]
+          Identifier(INT64) [41-46]
+        ColumnAttributeList [47-55]
+          NotNullColumnAttribute [47-55]
+    ColumnDefinition [59-87]
+      Identifier(name) [59-63]
+      SimpleColumnSchema [76-87]
+        PathExpression [76-82]
+          Identifier(STRING) [76-82]
+        TypeParameterList [82-86]
+          MaxLiteral() [83-86]
+  SpannerTableOptions [91-107]
+    PrimaryKey(ENFORCED) [91-107]
+      PrimaryKeyElementList [103-107]
+        PrimaryKeyElement(ASC) [104-106]
+          Identifier(id) [104-106]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#optimize-forward-edge-traversal
+CREATE TABLE Account (
+  id               INT64 NOT NULL,
+  create_time      TIMESTAMP,
+  close_time       TIMESTAMP,
+) PRIMARY KEY (id)
+--
+CreateTableStatement [0-136]
+  PathExpression [13-20]
+    Identifier(Account) [13-20]
+  TableElementList [21-119]
+    ColumnDefinition [25-56]
+      Identifier(id) [25-27]
+      SimpleColumnSchema [42-56]
+        PathExpression [42-47]
+          Identifier(INT64) [42-47]
+        ColumnAttributeList [48-56]
+          NotNullColumnAttribute [48-56]
+    ColumnDefinition [60-86]
+      Identifier(create_time) [60-71]
+      SimpleColumnSchema [77-86]
+        PathExpression [77-86]
+          Identifier(TIMESTAMP) [77-86]
+    ColumnDefinition [90-116]
+      Identifier(close_time) [90-100]
+      SimpleColumnSchema [107-116]
+        PathExpression [107-116]
+          Identifier(TIMESTAMP) [107-116]
+  SpannerTableOptions [120-136]
+    PrimaryKey(ENFORCED) [120-136]
+      PrimaryKeyElementList [132-136]
+        PrimaryKeyElement(ASC) [133-135]
+          Identifier(id) [133-135]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#optimize-forward-edge-traversal
+CREATE TABLE PersonOwnAccount (
+  id               INT64 NOT NULL,
+  account_id       INT64 NOT NULL,
+  create_time      TIMESTAMP,
+  CONSTRAINT FK_Account FOREIGN KEY (account_id)
+    REFERENCES Account (id)
+) PRIMARY KEY (id, account_id),
+  INTERLEAVE IN PARENT Person ON DELETE CASCADE
+--
+CreateTableStatement [0-288]
+  PathExpression [13-29]
+    Identifier(PersonOwnAccount) [13-29]
+  TableElementList [30-210]
+    ColumnDefinition [34-65]
+      Identifier(id) [34-36]
+      SimpleColumnSchema [51-65]
+        PathExpression [51-56]
+          Identifier(INT64) [51-56]
+        ColumnAttributeList [57-65]
+          NotNullColumnAttribute [57-65]
+    ColumnDefinition [69-100]
+      Identifier(account_id) [69-79]
+      SimpleColumnSchema [86-100]
+        PathExpression [86-91]
+          Identifier(INT64) [86-91]
+        ColumnAttributeList [92-100]
+          NotNullColumnAttribute [92-100]
+    ColumnDefinition [104-130]
+      Identifier(create_time) [104-115]
+      SimpleColumnSchema [121-130]
+        PathExpression [121-130]
+          Identifier(TIMESTAMP) [121-130]
+    ForeignKey [134-208]
+      ColumnList [168-180]
+        Identifier(account_id) [169-179]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [185-208]
+        PathExpression [196-203]
+          Identifier(Account) [196-203]
+        ColumnList [204-208]
+          Identifier(id) [205-207]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [208-208]
+      Identifier(FK_Account) [145-155]
+  SpannerTableOptions [211-288]
+    PrimaryKey(ENFORCED) [211-239]
+      PrimaryKeyElementList [223-239]
+        PrimaryKeyElement(ASC) [224-226]
+          Identifier(id) [224-226]
+        PrimaryKeyElement(ASC) [228-238]
+          Identifier(account_id) [228-238]
+    SpannerInterleaveClause [239-288]
+      PathExpression [264-270]
+        Identifier(Person) [264-270]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#optimize-forward-edge-traversal
+CREATE TABLE PersonOwnAccount (
+  id               INT64 NOT NULL,
+  account_id       INT64 NOT NULL,
+  create_time      TIMESTAMP,
+  CONSTRAINT FK_Account FOREIGN KEY (account_id)
+    REFERENCES Account (id) NOT ENFORCED
+) PRIMARY KEY (id, account_id),
+  INTERLEAVE IN PARENT Person ON DELETE CASCADE
+--
+CreateTableStatement [0-301]
+  PathExpression [13-29]
+    Identifier(PersonOwnAccount) [13-29]
+  TableElementList [30-223]
+    ColumnDefinition [34-65]
+      Identifier(id) [34-36]
+      SimpleColumnSchema [51-65]
+        PathExpression [51-56]
+          Identifier(INT64) [51-56]
+        ColumnAttributeList [57-65]
+          NotNullColumnAttribute [57-65]
+    ColumnDefinition [69-100]
+      Identifier(account_id) [69-79]
+      SimpleColumnSchema [86-100]
+        PathExpression [86-91]
+          Identifier(INT64) [86-91]
+        ColumnAttributeList [92-100]
+          NotNullColumnAttribute [92-100]
+    ColumnDefinition [104-130]
+      Identifier(create_time) [104-115]
+      SimpleColumnSchema [121-130]
+        PathExpression [121-130]
+          Identifier(TIMESTAMP) [121-130]
+    ForeignKey [134-221]
+      ColumnList [168-180]
+        Identifier(account_id) [169-179]
+      ForeignKeyReference(MATCH SIMPLE NOT ENFORCED) [185-208]
+        PathExpression [196-203]
+          Identifier(Account) [196-203]
+        ColumnList [204-208]
+          Identifier(id) [205-207]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [208-208]
+      Identifier(FK_Account) [145-155]
+  SpannerTableOptions [224-301]
+    PrimaryKey(ENFORCED) [224-252]
+      PrimaryKeyElementList [236-252]
+        PrimaryKeyElement(ASC) [237-239]
+          Identifier(id) [237-239]
+        PrimaryKeyElement(ASC) [241-251]
+          Identifier(account_id) [241-251]
+    SpannerInterleaveClause [252-301]
+      PathExpression [277-283]
+        Identifier(Person) [277-283]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#optimize-reverse-edge-traversal-secondary-index
+CREATE TABLE PersonOwnAccount (
+  id               INT64 NOT NULL,
+  account_id       INT64 NOT NULL,
+  create_time      TIMESTAMP,
+) PRIMARY KEY (id, account_id),
+  INTERLEAVE IN PARENT Person ON DELETE CASCADE
+--
+CreateTableStatement [0-211]
+  PathExpression [13-29]
+    Identifier(PersonOwnAccount) [13-29]
+  TableElementList [30-133]
+    ColumnDefinition [34-65]
+      Identifier(id) [34-36]
+      SimpleColumnSchema [51-65]
+        PathExpression [51-56]
+          Identifier(INT64) [51-56]
+        ColumnAttributeList [57-65]
+          NotNullColumnAttribute [57-65]
+    ColumnDefinition [69-100]
+      Identifier(account_id) [69-79]
+      SimpleColumnSchema [86-100]
+        PathExpression [86-91]
+          Identifier(INT64) [86-91]
+        ColumnAttributeList [92-100]
+          NotNullColumnAttribute [92-100]
+    ColumnDefinition [104-130]
+      Identifier(create_time) [104-115]
+      SimpleColumnSchema [121-130]
+        PathExpression [121-130]
+          Identifier(TIMESTAMP) [121-130]
+  SpannerTableOptions [134-211]
+    PrimaryKey(ENFORCED) [134-162]
+      PrimaryKeyElementList [146-162]
+        PrimaryKeyElement(ASC) [147-149]
+          Identifier(id) [147-149]
+        PrimaryKeyElement(ASC) [151-161]
+          Identifier(account_id) [151-161]
+    SpannerInterleaveClause [162-211]
+      PathExpression [187-193]
+        Identifier(Person) [187-193]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#optimize-reverse-edge-traversal-secondary-index
+CREATE INDEX AccountOwnedByPerson
+ON PersonOwnAccount (account_id)
+STORING (create_time),
+INTERLEAVE IN Account
+--
+CreateIndexStatement [0-111]
+  PathExpression [13-33]
+    Identifier(AccountOwnedByPerson) [13-33]
+  PathExpression [37-53]
+    Identifier(PersonOwnAccount) [37-53]
+  IndexItemList [54-66]
+    OrderingExpression(ASC) [55-65]
+      PathExpression [55-65]
+        Identifier(account_id) [55-65]
+  IndexStoringExpressionList [75-88]
+    PathExpression [76-87]
+      Identifier(create_time) [76-87]
+  SpannerInterleaveClause [88-111]
+    PathExpression [104-111]
+      Identifier(Account) [104-111]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#filter-nodes-by-property
+GRAPH FinGraph
+MATCH (acct:Account)
+WHERE acct.nick_name = "abcd"
+RETURN acct.id
+--
+QueryStatement [0-80]
+  Query [0-80]
+    GqlQuery [0-80]
+      GraphTableQuery [0-80]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-80]
+          GqlOperatorList [15-80]
+            GqlMatch [15-65]
+              GraphPattern [21-65]
+                GraphPathPattern [21-35]
+                  GraphNodePattern [21-35]
+                    GraphElementPatternFiller [22-34]
+                      Identifier(acct) [22-26]
+                      GraphLabelFilter [26-34]
+                        GraphElementLabel [27-34]
+                          Identifier(Account) [27-34]
+                WhereClause [36-65]
+                  BinaryExpression(=) [42-65]
+                    PathExpression [42-56]
+                      Identifier(acct) [42-46]
+                      Identifier(nick_name) [47-56]
+                    StringLiteral [59-65]
+                      StringLiteralComponent("abcd") [59-65]
+            GqlReturn [66-80]
+              Select [73-80]
+                SelectList [73-80]
+                  SelectColumn [73-80]
+                    PathExpression [73-80]
+                      Identifier(acct) [73-77]
+                      Identifier(id) [78-80]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#filter-nodes-by-property
+CREATE TABLE Account (
+  id               INT64 NOT NULL,
+  create_time      TIMESTAMP,
+  is_blocked       BOOL,
+  nick_name        STRING(MAX),
+) PRIMARY KEY (id)
+--
+CreateTableStatement [0-163]
+  PathExpression [13-20]
+    Identifier(Account) [13-20]
+  TableElementList [21-146]
+    ColumnDefinition [25-56]
+      Identifier(id) [25-27]
+      SimpleColumnSchema [42-56]
+        PathExpression [42-47]
+          Identifier(INT64) [42-47]
+        ColumnAttributeList [48-56]
+          NotNullColumnAttribute [48-56]
+    ColumnDefinition [60-86]
+      Identifier(create_time) [60-71]
+      SimpleColumnSchema [77-86]
+        PathExpression [77-86]
+          Identifier(TIMESTAMP) [77-86]
+    ColumnDefinition [90-111]
+      Identifier(is_blocked) [90-100]
+      SimpleColumnSchema [107-111]
+        PathExpression [107-111]
+          Identifier(BOOL) [107-111]
+    ColumnDefinition [115-143]
+      Identifier(nick_name) [115-124]
+      SimpleColumnSchema [132-143]
+        PathExpression [132-138]
+          Identifier(STRING) [132-138]
+        TypeParameterList [138-142]
+          MaxLiteral() [139-142]
+  SpannerTableOptions [147-163]
+    PrimaryKey(ENFORCED) [147-163]
+      PrimaryKeyElementList [159-163]
+        PrimaryKeyElement(ASC) [160-162]
+          Identifier(id) [160-162]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#filter-nodes-by-property
+CREATE INDEX AccountByNickName
+ON Account (nick_name)
+--
+CreateIndexStatement [0-53]
+  PathExpression [13-30]
+    Identifier(AccountByNickName) [13-30]
+  PathExpression [34-41]
+    Identifier(Account) [34-41]
+  IndexItemList [42-53]
+    OrderingExpression(ASC) [43-52]
+      PathExpression [43-52]
+        Identifier(nick_name) [43-52]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#filter-on-edge-property
+GRAPH FinGraph
+MATCH (person:Person)-[owns:Owns]->(acct:Account)
+WHERE person.id = 1
+  AND owns.create_time >= PARSE_TIMESTAMP("%c", "Thu Dec 25 07:30:00 2008")
+RETURN acct.id
+--
+QueryStatement [0-175]
+  Query [0-175]
+    GqlQuery [0-175]
+      GraphTableQuery [0-175]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-175]
+          GqlOperatorList [15-175]
+            GqlMatch [15-160]
+              GraphPattern [21-160]
+                GraphPathPattern [21-64]
+                  GraphNodePattern [21-36]
+                    GraphElementPatternFiller [22-35]
+                      Identifier(person) [22-28]
+                      GraphLabelFilter [28-35]
+                        GraphElementLabel [29-35]
+                          Identifier(Person) [29-35]
+                  GraphEdgePattern [36-50]
+                    GraphElementPatternFiller [38-47]
+                      Identifier(owns) [38-42]
+                      GraphLabelFilter [42-47]
+                        GraphElementLabel [43-47]
+                          Identifier(Owns) [43-47]
+                  GraphNodePattern [50-64]
+                    GraphElementPatternFiller [51-63]
+                      Identifier(acct) [51-55]
+                      GraphLabelFilter [55-63]
+                        GraphElementLabel [56-63]
+                          Identifier(Account) [56-63]
+                WhereClause [65-160]
+                  AndExpr [71-160]
+                    BinaryExpression(=) [71-84]
+                      PathExpression [71-80]
+                        Identifier(person) [71-77]
+                        Identifier(id) [78-80]
+                      IntLiteral(1) [83-84]
+                    BinaryExpression(>=) [91-160]
+                      PathExpression [91-107]
+                        Identifier(owns) [91-95]
+                        Identifier(create_time) [96-107]
+                      FunctionCall [111-160]
+                        PathExpression [111-126]
+                          Identifier(PARSE_TIMESTAMP) [111-126]
+                        StringLiteral [127-131]
+                          StringLiteralComponent("%c") [127-131]
+                        StringLiteral [133-159]
+                          StringLiteralComponent("Thu Dec 25 07:30:00 2008") [133-159]
+            GqlReturn [161-175]
+              Select [168-175]
+                SelectList [168-175]
+                  SelectColumn [168-175]
+                    PathExpression [168-175]
+                      Identifier(acct) [168-172]
+                      Identifier(id) [173-175]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#filter-on-edge-property
+CREATE INDEX PersonOwnAccountByCreateTime
+ON PersonOwnAccount (id, create_time),
+INTERLEAVE IN Person
+--
+CreateIndexStatement [0-101]
+  PathExpression [13-41]
+    Identifier(PersonOwnAccountByCreateTime) [13-41]
+  PathExpression [45-61]
+    Identifier(PersonOwnAccount) [45-61]
+  IndexItemList [62-79]
+    OrderingExpression(ASC) [63-65]
+      PathExpression [63-65]
+        Identifier(id) [63-65]
+    OrderingExpression(ASC) [67-78]
+      PathExpression [67-78]
+        Identifier(create_time) [67-78]
+  SpannerInterleaveClause [79-101]
+    PathExpression [95-101]
+      Identifier(Person) [95-101]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#filter-on-edge-property
+GRAPH FinGraph
+MATCH (acct:Account)<-[owns:Owns]-(person:Person)
+WHERE acct.id = 1
+  AND owns.create_time >= PARSE_TIMESTAMP("%c", "Thu Dec 25 07:30:00 2008")
+RETURN person.id
+--
+QueryStatement [0-175]
+  Query [0-175]
+    GqlQuery [0-175]
+      GraphTableQuery [0-175]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-175]
+          GqlOperatorList [15-175]
+            GqlMatch [15-158]
+              GraphPattern [21-158]
+                GraphPathPattern [21-64]
+                  GraphNodePattern [21-35]
+                    GraphElementPatternFiller [22-34]
+                      Identifier(acct) [22-26]
+                      GraphLabelFilter [26-34]
+                        GraphElementLabel [27-34]
+                          Identifier(Account) [27-34]
+                  GraphEdgePattern [35-49]
+                    GraphElementPatternFiller [38-47]
+                      Identifier(owns) [38-42]
+                      GraphLabelFilter [42-47]
+                        GraphElementLabel [43-47]
+                          Identifier(Owns) [43-47]
+                  GraphNodePattern [49-64]
+                    GraphElementPatternFiller [50-63]
+                      Identifier(person) [50-56]
+                      GraphLabelFilter [56-63]
+                        GraphElementLabel [57-63]
+                          Identifier(Person) [57-63]
+                WhereClause [65-158]
+                  AndExpr [71-158]
+                    BinaryExpression(=) [71-82]
+                      PathExpression [71-78]
+                        Identifier(acct) [71-75]
+                        Identifier(id) [76-78]
+                      IntLiteral(1) [81-82]
+                    BinaryExpression(>=) [89-158]
+                      PathExpression [89-105]
+                        Identifier(owns) [89-93]
+                        Identifier(create_time) [94-105]
+                      FunctionCall [109-158]
+                        PathExpression [109-124]
+                          Identifier(PARSE_TIMESTAMP) [109-124]
+                        StringLiteral [125-129]
+                          StringLiteralComponent("%c") [125-129]
+                        StringLiteral [131-157]
+                          StringLiteralComponent("Thu Dec 25 07:30:00 2008") [131-157]
+            GqlReturn [159-175]
+              Select [166-175]
+                SelectList [166-175]
+                  SelectColumn [166-175]
+                    PathExpression [166-175]
+                      Identifier(person) [166-172]
+                      Identifier(id) [173-175]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#filter-on-edge-property
+CREATE INDEX AccountOwnedByPersonByCreateTime
+ON PersonOwnAccount (account_id, create_time),
+INTERLEAVE IN Account
+--
+CreateIndexStatement [0-114]
+  PathExpression [13-45]
+    Identifier(AccountOwnedByPersonByCreateTime) [13-45]
+  PathExpression [49-65]
+    Identifier(PersonOwnAccount) [49-65]
+  IndexItemList [66-91]
+    OrderingExpression(ASC) [67-77]
+      PathExpression [67-77]
+        Identifier(account_id) [67-77]
+    OrderingExpression(ASC) [79-90]
+      PathExpression [79-90]
+        Identifier(create_time) [79-90]
+  SpannerInterleaveClause [91-114]
+    PathExpression [107-114]
+      Identifier(Account) [107-114]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#use-ref-constraints
+CREATE TABLE PersonOwnAccount (
+    id               INT64 NOT NULL,
+    account_id       INT64 NOT NULL,
+    create_time      TIMESTAMP,
+    CONSTRAINT FK_Account FOREIGN KEY (account_id) REFERENCES Account (id) ON DELETE CASCADE,
+  ) PRIMARY KEY (id, account_id),
+    INTERLEAVE IN PARENT Person ON DELETE CASCADE
+--
+CreateTableStatement [0-315]
+  PathExpression [13-29]
+    Identifier(PersonOwnAccount) [13-29]
+  TableElementList [30-235]
+    ColumnDefinition [36-67]
+      Identifier(id) [36-38]
+      SimpleColumnSchema [53-67]
+        PathExpression [53-58]
+          Identifier(INT64) [53-58]
+        ColumnAttributeList [59-67]
+          NotNullColumnAttribute [59-67]
+    ColumnDefinition [73-104]
+      Identifier(account_id) [73-83]
+      SimpleColumnSchema [90-104]
+        PathExpression [90-95]
+          Identifier(INT64) [90-95]
+        ColumnAttributeList [96-104]
+          NotNullColumnAttribute [96-104]
+    ColumnDefinition [110-136]
+      Identifier(create_time) [110-121]
+      SimpleColumnSchema [127-136]
+        PathExpression [127-136]
+          Identifier(TIMESTAMP) [127-136]
+    ForeignKey [142-230]
+      ColumnList [176-188]
+        Identifier(account_id) [177-187]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [189-230]
+        PathExpression [200-207]
+          Identifier(Account) [200-207]
+        ColumnList [208-212]
+          Identifier(id) [209-211]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE CASCADE) [213-230]
+      Identifier(FK_Account) [153-163]
+  SpannerTableOptions [236-315]
+    PrimaryKey(ENFORCED) [236-264]
+      PrimaryKeyElementList [248-264]
+        PrimaryKeyElement(ASC) [249-251]
+          Identifier(id) [249-251]
+        PrimaryKeyElement(ASC) [253-263]
+          Identifier(account_id) [253-263]
+    SpannerInterleaveClause [264-315]
+      PathExpression [291-297]
+        Identifier(Person) [291-297]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#delete-cascade-edges-connecting-different-types-nodes
+CREATE TABLE PersonOwnAccount (
+  id               INT64 NOT NULL,
+  account_id       INT64 NOT NULL,
+  create_time      TIMESTAMP,
+  CONSTRAINT FK_Account FOREIGN KEY (account_id) REFERENCES Account (id) ON DELETE CASCADE,
+) PRIMARY KEY (id, account_id),
+  INTERLEAVE IN PARENT Person ON DELETE CASCADE
+--
+CreateTableStatement [0-303]
+  PathExpression [13-29]
+    Identifier(PersonOwnAccount) [13-29]
+  TableElementList [30-225]
+    ColumnDefinition [34-65]
+      Identifier(id) [34-36]
+      SimpleColumnSchema [51-65]
+        PathExpression [51-56]
+          Identifier(INT64) [51-56]
+        ColumnAttributeList [57-65]
+          NotNullColumnAttribute [57-65]
+    ColumnDefinition [69-100]
+      Identifier(account_id) [69-79]
+      SimpleColumnSchema [86-100]
+        PathExpression [86-91]
+          Identifier(INT64) [86-91]
+        ColumnAttributeList [92-100]
+          NotNullColumnAttribute [92-100]
+    ColumnDefinition [104-130]
+      Identifier(create_time) [104-115]
+      SimpleColumnSchema [121-130]
+        PathExpression [121-130]
+          Identifier(TIMESTAMP) [121-130]
+    ForeignKey [134-222]
+      ColumnList [168-180]
+        Identifier(account_id) [169-179]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [181-222]
+        PathExpression [192-199]
+          Identifier(Account) [192-199]
+        ColumnList [200-204]
+          Identifier(id) [201-203]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE CASCADE) [205-222]
+      Identifier(FK_Account) [145-155]
+  SpannerTableOptions [226-303]
+    PrimaryKey(ENFORCED) [226-254]
+      PrimaryKeyElementList [238-254]
+        PrimaryKeyElement(ASC) [239-241]
+          Identifier(id) [239-241]
+        PrimaryKeyElement(ASC) [243-253]
+          Identifier(account_id) [243-253]
+    SpannerInterleaveClause [254-303]
+      PathExpression [279-285]
+        Identifier(Person) [279-285]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-designing-schema#delete-cascade-edges-connecting-the-same-type-nodes
+CREATE TABLE AccountTransferAccount (
+  id               INT64 NOT NULL,
+  to_id            INT64 NOT NULL,
+  amount           FLOAT64,
+  create_time      TIMESTAMP NOT NULL,
+  order_number     STRING(MAX),
+  CONSTRAINT FK_FromAccount FOREIGN KEY (id) REFERENCES Account (id) ON DELETE CASCADE,
+  CONSTRAINT FK_ToAccount FOREIGN KEY (to_id) REFERENCES Account (id) ON DELETE CASCADE,
+) PRIMARY KEY (id, to_id)
+--
+CreateTableStatement [0-409]
+  PathExpression [13-35]
+    Identifier(AccountTransferAccount) [13-35]
+  TableElementList [36-385]
+    ColumnDefinition [40-71]
+      Identifier(id) [40-42]
+      SimpleColumnSchema [57-71]
+        PathExpression [57-62]
+          Identifier(INT64) [57-62]
+        ColumnAttributeList [63-71]
+          NotNullColumnAttribute [63-71]
+    ColumnDefinition [75-106]
+      Identifier(to_id) [75-80]
+      SimpleColumnSchema [92-106]
+        PathExpression [92-97]
+          Identifier(INT64) [92-97]
+        ColumnAttributeList [98-106]
+          NotNullColumnAttribute [98-106]
+    ColumnDefinition [110-134]
+      Identifier(amount) [110-116]
+      SimpleColumnSchema [127-134]
+        PathExpression [127-134]
+          Identifier(FLOAT64) [127-134]
+    ColumnDefinition [138-173]
+      Identifier(create_time) [138-149]
+      SimpleColumnSchema [155-173]
+        PathExpression [155-164]
+          Identifier(TIMESTAMP) [155-164]
+        ColumnAttributeList [165-173]
+          NotNullColumnAttribute [165-173]
+    ColumnDefinition [177-205]
+      Identifier(order_number) [177-189]
+      SimpleColumnSchema [194-205]
+        PathExpression [194-200]
+          Identifier(STRING) [194-200]
+        TypeParameterList [200-204]
+          MaxLiteral() [201-204]
+    ForeignKey [209-293]
+      ColumnList [247-251]
+        Identifier(id) [248-250]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [252-293]
+        PathExpression [263-270]
+          Identifier(Account) [263-270]
+        ColumnList [271-275]
+          Identifier(id) [272-274]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE CASCADE) [276-293]
+      Identifier(FK_FromAccount) [220-234]
+    ForeignKey [297-382]
+      ColumnList [333-340]
+        Identifier(to_id) [334-339]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [341-382]
+        PathExpression [352-359]
+          Identifier(Account) [352-359]
+        ColumnList [360-364]
+          Identifier(id) [361-363]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE CASCADE) [365-382]
+      Identifier(FK_ToAccount) [308-320]
+  SpannerTableOptions [386-409]
+    PrimaryKey(ENFORCED) [386-409]
+      PrimaryKeyElementList [398-409]
+        PrimaryKeyElement(ASC) [399-401]
+          Identifier(id) [399-401]
+        PrimaryKeyElement(ASC) [403-408]
+          Identifier(to_id) [403-408]
diff --git a/parser/testdata/spanner/guides/graph.best-practices-tuning-queries.test b/parser/testdata/spanner/guides/graph.best-practices-tuning-queries.test
new file mode 100644
index 0000000..27a9ded
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.best-practices-tuning-queries.test
@@ -0,0 +1,1055 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/best-practices-tuning-queries
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-tuning-queries#start-lower-cardinality-nodes
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+GRAPH FinGraph
+MATCH (p:Person {name:"Alex"})-[:Owns]->(a:Account {is_blocked: true})
+RETURN p.id AS person_id, a.id AS account_id
+--
+QueryStatement [0-130]
+  Query [0-130]
+    GqlQuery [0-130]
+      GraphTableQuery [0-130]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-130]
+          GqlOperatorList [15-130]
+            GqlMatch [15-85]
+              GraphPattern [21-85]
+                GraphPathPattern [21-85]
+                  GraphNodePattern [21-45]
+                    GraphElementPatternFiller [22-44]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                      GraphPropertySpecification [31-44]
+                        GraphPropertyNameAndValue [32-43]
+                          Identifier(name) [32-36]
+                          StringLiteral [37-43]
+                            StringLiteralComponent("Alex") [37-43]
+                  GraphEdgePattern [45-55]
+                    GraphElementPatternFiller [47-52]
+                      GraphLabelFilter [47-52]
+                        GraphElementLabel [48-52]
+                          Identifier(Owns) [48-52]
+                  GraphNodePattern [55-85]
+                    GraphElementPatternFiller [56-84]
+                      Identifier(a) [56-57]
+                      GraphLabelFilter [57-65]
+                        GraphElementLabel [58-65]
+                          Identifier(Account) [58-65]
+                      GraphPropertySpecification [66-84]
+                        GraphPropertyNameAndValue [67-83]
+                          Identifier(is_blocked) [67-77]
+                          BooleanLiteral(true) [79-83]
+            GqlReturn [86-130]
+              Select [93-130]
+                SelectList [93-130]
+                  SelectColumn [93-110]
+                    PathExpression [93-97]
+                      Identifier(p) [93-94]
+                      Identifier(id) [95-97]
+                    Alias [98-110]
+                      Identifier(person_id) [101-110]
+                  SelectColumn [112-130]
+                    PathExpression [112-116]
+                      Identifier(a) [112-113]
+                      Identifier(id) [114-116]
+                    Alias [117-130]
+                      Identifier(account_id) [120-130]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-tuning-queries#start-lower-cardinality-nodes
+GRAPH FinGraph
+MATCH (a:Account {is_blocked:true})<-[:Owns]-(p:Person {name: "Alex"})
+RETURN p.id AS person_id, a.id AS account_id
+--
+QueryStatement [0-130]
+  Query [0-130]
+    GqlQuery [0-130]
+      GraphTableQuery [0-130]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-130]
+          GqlOperatorList [15-130]
+            GqlMatch [15-85]
+              GraphPattern [21-85]
+                GraphPathPattern [21-85]
+                  GraphNodePattern [21-50]
+                    GraphElementPatternFiller [22-49]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                      GraphPropertySpecification [32-49]
+                        GraphPropertyNameAndValue [33-48]
+                          Identifier(is_blocked) [33-43]
+                          BooleanLiteral(true) [44-48]
+                  GraphEdgePattern [50-60]
+                    GraphElementPatternFiller [53-58]
+                      GraphLabelFilter [53-58]
+                        GraphElementLabel [54-58]
+                          Identifier(Owns) [54-58]
+                  GraphNodePattern [60-85]
+                    GraphElementPatternFiller [61-84]
+                      Identifier(p) [61-62]
+                      GraphLabelFilter [62-69]
+                        GraphElementLabel [63-69]
+                          Identifier(Person) [63-69]
+                      GraphPropertySpecification [70-84]
+                        GraphPropertyNameAndValue [71-83]
+                          Identifier(name) [71-75]
+                          StringLiteral [77-83]
+                            StringLiteralComponent("Alex") [77-83]
+            GqlReturn [86-130]
+              Select [93-130]
+                SelectList [93-130]
+                  SelectColumn [93-110]
+                    PathExpression [93-97]
+                      Identifier(p) [93-94]
+                      Identifier(id) [95-97]
+                    Alias [98-110]
+                      Identifier(person_id) [101-110]
+                  SelectColumn [112-130]
+                    PathExpression [112-116]
+                      Identifier(a) [112-113]
+                      Identifier(id) [114-116]
+                    Alias [117-130]
+                      Identifier(account_id) [120-130]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-tuning-queries#start-lower-cardinality-nodes
+GRAPH FinGraph
+MATCH (:Account {id: 7})-[:Transfers]->{1,3}(a:Account)
+RETURN a.id
+--
+QueryStatement [0-82]
+  Query [0-82]
+    GqlQuery [0-82]
+      GraphTableQuery [0-82]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-82]
+          GqlOperatorList [15-82]
+            GqlMatch [15-70]
+              GraphPattern [21-70]
+                GraphPathPattern [21-70]
+                  GraphNodePattern [21-39]
+                    GraphElementPatternFiller [22-38]
+                      GraphLabelFilter [22-30]
+                        GraphElementLabel [23-30]
+                          Identifier(Account) [23-30]
+                      GraphPropertySpecification [31-38]
+                        GraphPropertyNameAndValue [32-37]
+                          Identifier(id) [32-34]
+                          IntLiteral(7) [36-37]
+                  ParenthesizedGraphPathPattern [39-59]
+                    BoundedQuantifier [54-59]
+                      QuantifierBound [55-56]
+                        IntLiteral(1) [55-56]
+                      QuantifierBound [57-58]
+                        IntLiteral(3) [57-58]
+                    GraphEdgePattern [39-54]
+                      GraphElementPatternFiller [41-51]
+                        GraphLabelFilter [41-51]
+                          GraphElementLabel [42-51]
+                            Identifier(Transfers) [42-51]
+                  GraphNodePattern [59-70]
+                    GraphElementPatternFiller [60-69]
+                      Identifier(a) [60-61]
+                      GraphLabelFilter [61-69]
+                        GraphElementLabel [62-69]
+                          Identifier(Account) [62-69]
+            GqlReturn [71-82]
+              Select [78-82]
+                SelectList [78-82]
+                  SelectColumn [78-82]
+                    PathExpression [78-82]
+                      Identifier(a) [78-79]
+                      Identifier(id) [80-82]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-tuning-queries#single-match-statement
+GRAPH FinGraph
+MATCH (src:Account {id: 7})-[:Transfers]->{1,3}(dst:Account)
+RETURN dst.id
+--
+QueryStatement [0-89]
+  Query [0-89]
+    GqlQuery [0-89]
+      GraphTableQuery [0-89]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-89]
+          GqlOperatorList [15-89]
+            GqlMatch [15-75]
+              GraphPattern [21-75]
+                GraphPathPattern [21-75]
+                  GraphNodePattern [21-42]
+                    GraphElementPatternFiller [22-41]
+                      Identifier(src) [22-25]
+                      GraphLabelFilter [25-33]
+                        GraphElementLabel [26-33]
+                          Identifier(Account) [26-33]
+                      GraphPropertySpecification [34-41]
+                        GraphPropertyNameAndValue [35-40]
+                          Identifier(id) [35-37]
+                          IntLiteral(7) [39-40]
+                  ParenthesizedGraphPathPattern [42-62]
+                    BoundedQuantifier [57-62]
+                      QuantifierBound [58-59]
+                        IntLiteral(1) [58-59]
+                      QuantifierBound [60-61]
+                        IntLiteral(3) [60-61]
+                    GraphEdgePattern [42-57]
+                      GraphElementPatternFiller [44-54]
+                        GraphLabelFilter [44-54]
+                          GraphElementLabel [45-54]
+                            Identifier(Transfers) [45-54]
+                  GraphNodePattern [62-75]
+                    GraphElementPatternFiller [63-74]
+                      Identifier(dst) [63-66]
+                      GraphLabelFilter [66-74]
+                        GraphElementLabel [67-74]
+                          Identifier(Account) [67-74]
+            GqlReturn [76-89]
+              Select [83-89]
+                SelectList [83-89]
+                  SelectColumn [83-89]
+                    PathExpression [83-89]
+                      Identifier(dst) [83-86]
+                      Identifier(id) [87-89]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-tuning-queries#across-match-statements
+GRAPH FinGraph
+MATCH (acct:Account {id: 7})-[:Transfers]->{1,3}(other_acct:Account)
+RETURN acct, COUNT(DISTINCT other_acct) AS related_accts
+GROUP BY acct
+
+NEXT
+
+MATCH (acct:Account)<-[:Owns]-(p:Person)
+RETURN p.id AS person, acct.id AS acct, related_accts
+--
+QueryStatement [0-256]
+  Query [0-256]
+    GqlQuery [0-256]
+      GraphTableQuery [0-256]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-256]
+          GqlOperatorList [15-154]
+            GqlMatch [15-83]
+              GraphPattern [21-83]
+                GraphPathPattern [21-83]
+                  GraphNodePattern [21-43]
+                    GraphElementPatternFiller [22-42]
+                      Identifier(acct) [22-26]
+                      GraphLabelFilter [26-34]
+                        GraphElementLabel [27-34]
+                          Identifier(Account) [27-34]
+                      GraphPropertySpecification [35-42]
+                        GraphPropertyNameAndValue [36-41]
+                          Identifier(id) [36-38]
+                          IntLiteral(7) [40-41]
+                  ParenthesizedGraphPathPattern [43-63]
+                    BoundedQuantifier [58-63]
+                      QuantifierBound [59-60]
+                        IntLiteral(1) [59-60]
+                      QuantifierBound [61-62]
+                        IntLiteral(3) [61-62]
+                    GraphEdgePattern [43-58]
+                      GraphElementPatternFiller [45-55]
+                        GraphLabelFilter [45-55]
+                          GraphElementLabel [46-55]
+                            Identifier(Transfers) [46-55]
+                  GraphNodePattern [63-83]
+                    GraphElementPatternFiller [64-82]
+                      Identifier(other_acct) [64-74]
+                      GraphLabelFilter [74-82]
+                        GraphElementLabel [75-82]
+                          Identifier(Account) [75-82]
+            GqlReturn [84-154]
+              Select [91-154]
+                SelectList [91-140]
+                  SelectColumn [91-95]
+                    PathExpression [91-95]
+                      Identifier(acct) [91-95]
+                  SelectColumn [97-140]
+                    FunctionCall(distinct=true) [97-123]
+                      PathExpression [97-102]
+                        Identifier(COUNT) [97-102]
+                      PathExpression [112-122]
+                        Identifier(other_acct) [112-122]
+                    Alias [124-140]
+                      Identifier(related_accts) [127-140]
+                GroupBy [141-154]
+                  GroupingItem [150-154]
+                    PathExpression [150-154]
+                      Identifier(acct) [150-154]
+          GqlOperatorList [162-256]
+            GqlMatch [162-202]
+              GraphPattern [168-202]
+                GraphPathPattern [168-202]
+                  GraphNodePattern [168-182]
+                    GraphElementPatternFiller [169-181]
+                      Identifier(acct) [169-173]
+                      GraphLabelFilter [173-181]
+                        GraphElementLabel [174-181]
+                          Identifier(Account) [174-181]
+                  GraphEdgePattern [182-192]
+                    GraphElementPatternFiller [185-190]
+                      GraphLabelFilter [185-190]
+                        GraphElementLabel [186-190]
+                          Identifier(Owns) [186-190]
+                  GraphNodePattern [192-202]
+                    GraphElementPatternFiller [193-201]
+                      Identifier(p) [193-194]
+                      GraphLabelFilter [194-201]
+                        GraphElementLabel [195-201]
+                          Identifier(Person) [195-201]
+            GqlReturn [203-256]
+              Select [210-256]
+                SelectList [210-256]
+                  SelectColumn [210-224]
+                    PathExpression [210-214]
+                      Identifier(p) [210-211]
+                      Identifier(id) [212-214]
+                    Alias [215-224]
+                      Identifier(person) [218-224]
+                  SelectColumn [226-241]
+                    PathExpression [226-233]
+                      Identifier(acct) [226-230]
+                      Identifier(id) [231-233]
+                    Alias [234-241]
+                      Identifier(acct) [237-241]
+                  SelectColumn [243-256]
+                    PathExpression [243-256]
+                      Identifier(related_accts) [243-256]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-tuning-queries#limit-traversed-edges
+GRAPH FinGraph
+MATCH
+(a1:Account {is_blocked: true})
+-[e:Transfers WHERE e IN
+  {
+    MATCH -[selected_e:Transfers]->
+    FILTER IS_FIRST(@max_transfers_per_account) OVER (
+      PARTITION BY SOURCE_NODE_ID(selected_e)
+      ORDER BY selected_e.create_time DESC)
+    RETURN selected_e
+  }
+]->{1,5}
+(a2:Account)
+RETURN a1.id AS src_id, a2.id AS dst_id
+--
+QueryStatement [0-350]
+  Query [0-350]
+    GqlQuery [0-350]
+      GraphTableQuery [0-350]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-350]
+          GqlOperatorList [15-350]
+            GqlMatch [15-310]
+              GraphPattern [21-310]
+                GraphPathPattern [21-310]
+                  GraphNodePattern [21-52]
+                    GraphElementPatternFiller [22-51]
+                      Identifier(a1) [22-24]
+                      GraphLabelFilter [24-32]
+                        GraphElementLabel [25-32]
+                          Identifier(Account) [25-32]
+                      GraphPropertySpecification [33-51]
+                        GraphPropertyNameAndValue [34-50]
+                          Identifier(is_blocked) [34-44]
+                          BooleanLiteral(true) [46-50]
+                  ParenthesizedGraphPathPattern [53-297]
+                    BoundedQuantifier [292-297]
+                      QuantifierBound [293-294]
+                        IntLiteral(1) [293-294]
+                      QuantifierBound [295-296]
+                        IntLiteral(5) [295-296]
+                    GraphEdgePattern [53-292]
+                      GraphElementPatternFiller [55-288]
+                        Identifier(e) [55-56]
+                        GraphLabelFilter [56-66]
+                          GraphElementLabel [57-66]
+                            Identifier(Transfers) [57-66]
+                        WhereClause [67-288]
+                          InExpression(IN) [73-288]
+                            PathExpression [73-74]
+                              Identifier(e) [73-74]
+                            Location [75-77]
+                            Query [80-288]
+                              GqlQuery [80-288]
+                                GraphTableQuery [80-288]
+                                  GqlOperatorList [86-284]
+                                    GqlOperatorList [86-284]
+                                      GqlMatch [86-117]
+                                        GraphPattern [92-117]
+                                          GraphPathPattern [92-117]
+                                            GraphEdgePattern [92-117]
+                                              GraphElementPatternFiller [94-114]
+                                                Identifier(selected_e) [94-104]
+                                                GraphLabelFilter [104-114]
+                                                  GraphElementLabel [105-114]
+                                                    Identifier(Transfers) [105-114]
+                                      GqlFilter [122-262]
+                                        WhereClause [122-262]
+                                          AnalyticFunctionCall [129-262]
+                                            FunctionCall [129-165]
+                                              PathExpression [129-137]
+                                                Identifier(IS_FIRST) [129-137]
+                                              ParameterExpr [138-164]
+                                                Identifier(max_transfers_per_account) [139-164]
+                                            WindowSpecification [171-262]
+                                              PartitionBy [179-218]
+                                                FunctionCall [192-218]
+                                                  PathExpression [192-206]
+                                                    Identifier(SOURCE_NODE_ID) [192-206]
+                                                  PathExpression [207-217]
+                                                    Identifier(selected_e) [207-217]
+                                              OrderBy [225-261]
+                                                OrderingExpression(DESC) [234-261]
+                                                  PathExpression [234-256]
+                                                    Identifier(selected_e) [234-244]
+                                                    Identifier(create_time) [245-256]
+                                      GqlReturn [267-284]
+                                        Select [274-284]
+                                          SelectList [274-284]
+                                            SelectColumn [274-284]
+                                              PathExpression [274-284]
+                                                Identifier(selected_e) [274-284]
+                  GraphNodePattern [298-310]
+                    GraphElementPatternFiller [299-309]
+                      Identifier(a2) [299-301]
+                      GraphLabelFilter [301-309]
+                        GraphElementLabel [302-309]
+                          Identifier(Account) [302-309]
+            GqlReturn [311-350]
+              Select [318-350]
+                SelectList [318-350]
+                  SelectColumn [318-333]
+                    PathExpression [318-323]
+                      Identifier(a1) [318-320]
+                      Identifier(id) [321-323]
+                    Alias [324-333]
+                      Identifier(src_id) [327-333]
+                  SelectColumn [335-350]
+                    PathExpression [335-340]
+                      Identifier(a2) [335-337]
+                      Identifier(id) [338-340]
+                    Alias [341-350]
+                      Identifier(dst_id) [344-350]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-tuning-queries#sample-intermediate-nodes
+GRAPH FinGraph
+MATCH (a1:Account {is_blocked: true})-[e1:Transfers]->(a2:Account)
+FILTER IS_FIRST(1) OVER (PARTITION BY a2)
+RETURN a1, a2
+
+NEXT
+
+MATCH (a2)-[e2:Transfers]->(a3:Account)
+RETURN a1.id AS src_id, a2.id AS mid_id, a3.id AS dst_id
+--
+QueryStatement [0-241]
+  Query [0-241]
+    GqlQuery [0-241]
+      GraphTableQuery [0-241]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-241]
+          GqlOperatorList [15-137]
+            GqlMatch [15-81]
+              GraphPattern [21-81]
+                GraphPathPattern [21-81]
+                  GraphNodePattern [21-52]
+                    GraphElementPatternFiller [22-51]
+                      Identifier(a1) [22-24]
+                      GraphLabelFilter [24-32]
+                        GraphElementLabel [25-32]
+                          Identifier(Account) [25-32]
+                      GraphPropertySpecification [33-51]
+                        GraphPropertyNameAndValue [34-50]
+                          Identifier(is_blocked) [34-44]
+                          BooleanLiteral(true) [46-50]
+                  GraphEdgePattern [52-69]
+                    GraphElementPatternFiller [54-66]
+                      Identifier(e1) [54-56]
+                      GraphLabelFilter [56-66]
+                        GraphElementLabel [57-66]
+                          Identifier(Transfers) [57-66]
+                  GraphNodePattern [69-81]
+                    GraphElementPatternFiller [70-80]
+                      Identifier(a2) [70-72]
+                      GraphLabelFilter [72-80]
+                        GraphElementLabel [73-80]
+                          Identifier(Account) [73-80]
+            GqlFilter [82-123]
+              WhereClause [82-123]
+                AnalyticFunctionCall [89-123]
+                  FunctionCall [89-100]
+                    PathExpression [89-97]
+                      Identifier(IS_FIRST) [89-97]
+                    IntLiteral(1) [98-99]
+                  WindowSpecification [106-123]
+                    PartitionBy [107-122]
+                      PathExpression [120-122]
+                        Identifier(a2) [120-122]
+            GqlReturn [124-137]
+              Select [131-137]
+                SelectList [131-137]
+                  SelectColumn [131-133]
+                    PathExpression [131-133]
+                      Identifier(a1) [131-133]
+                  SelectColumn [135-137]
+                    PathExpression [135-137]
+                      Identifier(a2) [135-137]
+          GqlOperatorList [145-241]
+            GqlMatch [145-184]
+              GraphPattern [151-184]
+                GraphPathPattern [151-184]
+                  GraphNodePattern [151-155]
+                    GraphElementPatternFiller [152-154]
+                      Identifier(a2) [152-154]
+                  GraphEdgePattern [155-172]
+                    GraphElementPatternFiller [157-169]
+                      Identifier(e2) [157-159]
+                      GraphLabelFilter [159-169]
+                        GraphElementLabel [160-169]
+                          Identifier(Transfers) [160-169]
+                  GraphNodePattern [172-184]
+                    GraphElementPatternFiller [173-183]
+                      Identifier(a3) [173-175]
+                      GraphLabelFilter [175-183]
+                        GraphElementLabel [176-183]
+                          Identifier(Account) [176-183]
+            GqlReturn [185-241]
+              Select [192-241]
+                SelectList [192-241]
+                  SelectColumn [192-207]
+                    PathExpression [192-197]
+                      Identifier(a1) [192-194]
+                      Identifier(id) [195-197]
+                    Alias [198-207]
+                      Identifier(src_id) [201-207]
+                  SelectColumn [209-224]
+                    PathExpression [209-214]
+                      Identifier(a2) [209-211]
+                      Identifier(id) [212-214]
+                    Alias [215-224]
+                      Identifier(mid_id) [218-224]
+                  SelectColumn [226-241]
+                    PathExpression [226-231]
+                      Identifier(a3) [226-228]
+                      Identifier(id) [229-231]
+                    Alias [232-241]
+                      Identifier(dst_id) [235-241]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-tuning-queries#reduce-intermediate-result
+GRAPH FinGraph
+MATCH (a1:Account {is_blocked: true})-[e1:Transfers]->{1,3}@{factorized_mode=factorize_left}(a2:Account)
+LET total_amount = SUM(e1.amount)
+RETURN a2.id, a2.create_time, total_amount
+--
+QueryStatement [0-196]
+  Query [0-196]
+    GqlQuery [0-196]
+      GraphTableQuery [0-196]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-196]
+          GqlOperatorList [15-196]
+            GqlMatch [15-119]
+              GraphPattern [21-119]
+                GraphPathPattern [21-119]
+                  GraphNodePattern [21-52]
+                    GraphElementPatternFiller [22-51]
+                      Identifier(a1) [22-24]
+                      GraphLabelFilter [24-32]
+                        GraphElementLabel [25-32]
+                          Identifier(Account) [25-32]
+                      GraphPropertySpecification [33-51]
+                        GraphPropertyNameAndValue [34-50]
+                          Identifier(is_blocked) [34-44]
+                          BooleanLiteral(true) [46-50]
+                  ParenthesizedGraphPathPattern [52-74]
+                    BoundedQuantifier [69-74]
+                      QuantifierBound [70-71]
+                        IntLiteral(1) [70-71]
+                      QuantifierBound [72-73]
+                        IntLiteral(3) [72-73]
+                    GraphEdgePattern [52-69]
+                      GraphElementPatternFiller [54-66]
+                        Identifier(e1) [54-56]
+                        GraphLabelFilter [56-66]
+                          GraphElementLabel [57-66]
+                            Identifier(Transfers) [57-66]
+                  GraphPathPattern [74-119]
+                    Hint [74-107]
+                      HintEntry [76-106]
+                        Identifier(factorized_mode) [76-91]
+                        PathExpression [92-106]
+                          Identifier(factorize_left) [92-106]
+                    GraphNodePattern [107-119]
+                      GraphElementPatternFiller [108-118]
+                        Identifier(a2) [108-110]
+                        GraphLabelFilter [110-118]
+                          GraphElementLabel [111-118]
+                            Identifier(Account) [111-118]
+            GqlLet [120-153]
+              GqlLetVariableDefinitionList [124-153]
+                GqlLetVariableDefinition [124-153]
+                  Identifier(total_amount) [124-136]
+                  FunctionCall [139-153]
+                    PathExpression [139-142]
+                      Identifier(SUM) [139-142]
+                    PathExpression [143-152]
+                      Identifier(e1) [143-145]
+                      Identifier(amount) [146-152]
+            GqlReturn [154-196]
+              Select [161-196]
+                SelectList [161-196]
+                  SelectColumn [161-166]
+                    PathExpression [161-166]
+                      Identifier(a2) [161-163]
+                      Identifier(id) [164-166]
+                  SelectColumn [168-182]
+                    PathExpression [168-182]
+                      Identifier(a2) [168-170]
+                      Identifier(create_time) [171-182]
+                  SelectColumn [184-196]
+                    PathExpression [184-196]
+                      Identifier(total_amount) [184-196]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-tuning-queries#reduce-intermediate-result
+GRAPH FinGraph
+MATCH (a1:Account {is_blocked: true})-[e1:Transfers]->{1,3}(a2:Account)
+LET total_amount = SUM(e1.amount)
+RETURN a2.id, a2.create_time, total_amount
+--
+QueryStatement [0-163]
+  Query [0-163]
+    GqlQuery [0-163]
+      GraphTableQuery [0-163]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-163]
+          GqlOperatorList [15-163]
+            GqlMatch [15-86]
+              GraphPattern [21-86]
+                GraphPathPattern [21-86]
+                  GraphNodePattern [21-52]
+                    GraphElementPatternFiller [22-51]
+                      Identifier(a1) [22-24]
+                      GraphLabelFilter [24-32]
+                        GraphElementLabel [25-32]
+                          Identifier(Account) [25-32]
+                      GraphPropertySpecification [33-51]
+                        GraphPropertyNameAndValue [34-50]
+                          Identifier(is_blocked) [34-44]
+                          BooleanLiteral(true) [46-50]
+                  ParenthesizedGraphPathPattern [52-74]
+                    BoundedQuantifier [69-74]
+                      QuantifierBound [70-71]
+                        IntLiteral(1) [70-71]
+                      QuantifierBound [72-73]
+                        IntLiteral(3) [72-73]
+                    GraphEdgePattern [52-69]
+                      GraphElementPatternFiller [54-66]
+                        Identifier(e1) [54-56]
+                        GraphLabelFilter [56-66]
+                          GraphElementLabel [57-66]
+                            Identifier(Transfers) [57-66]
+                  GraphNodePattern [74-86]
+                    GraphElementPatternFiller [75-85]
+                      Identifier(a2) [75-77]
+                      GraphLabelFilter [77-85]
+                        GraphElementLabel [78-85]
+                          Identifier(Account) [78-85]
+            GqlLet [87-120]
+              GqlLetVariableDefinitionList [91-120]
+                GqlLetVariableDefinition [91-120]
+                  Identifier(total_amount) [91-103]
+                  FunctionCall [106-120]
+                    PathExpression [106-109]
+                      Identifier(SUM) [106-109]
+                    PathExpression [110-119]
+                      Identifier(e1) [110-112]
+                      Identifier(amount) [113-119]
+            GqlReturn [121-163]
+              Select [128-163]
+                SelectList [128-163]
+                  SelectColumn [128-133]
+                    PathExpression [128-133]
+                      Identifier(a2) [128-130]
+                      Identifier(id) [131-133]
+                  SelectColumn [135-149]
+                    PathExpression [135-149]
+                      Identifier(a2) [135-137]
+                      Identifier(create_time) [138-149]
+                  SelectColumn [151-163]
+                    PathExpression [151-163]
+                      Identifier(total_amount) [151-163]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-tuning-queries#defer-intermediate-result
+GRAPH FinGraph
+MATCH (a:Account {id: 1})-[e1:Transfers]->(a1:Account),
+  @{factorized_mode=factorize_both}(a:Account)-[e2:Transfers]->(a2:Account),
+  (a:Account)-[e3:Transfers]->(a3:Account)
+WHERE e1.create_time < TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 90 DAY)
+  AND e2.create_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 90 DAY)
+  AND e2.create_time < TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
+  AND e3.create_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
+LET total_amount = e1.amount + e2.amount + e3.amount
+RETURN a1.id as a1_id, a2.id as a2_id, a3.id as a3_id, total_amount
+--
+QueryStatement [0-613]
+  Query [0-613]
+    GqlQuery [0-613]
+      GraphTableQuery [0-613]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-613]
+          GqlOperatorList [15-613]
+            GqlMatch [15-492]
+              GraphPattern [21-492]
+                GraphPathPattern [21-69]
+                  GraphNodePattern [21-40]
+                    GraphElementPatternFiller [22-39]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                      GraphPropertySpecification [32-39]
+                        GraphPropertyNameAndValue [33-38]
+                          Identifier(id) [33-35]
+                          IntLiteral(1) [37-38]
+                  GraphEdgePattern [40-57]
+                    GraphElementPatternFiller [42-54]
+                      Identifier(e1) [42-44]
+                      GraphLabelFilter [44-54]
+                        GraphElementLabel [45-54]
+                          Identifier(Transfers) [45-54]
+                  GraphNodePattern [57-69]
+                    GraphElementPatternFiller [58-68]
+                      Identifier(a1) [58-60]
+                      GraphLabelFilter [60-68]
+                        GraphElementLabel [61-68]
+                          Identifier(Account) [61-68]
+                GraphPathPattern [73-146]
+                  Hint [73-106]
+                    HintEntry [75-105]
+                      Identifier(factorized_mode) [75-90]
+                      PathExpression [91-105]
+                        Identifier(factorize_both) [91-105]
+                  GraphNodePattern [106-117]
+                    GraphElementPatternFiller [107-116]
+                      Identifier(a) [107-108]
+                      GraphLabelFilter [108-116]
+                        GraphElementLabel [109-116]
+                          Identifier(Account) [109-116]
+                  GraphEdgePattern [117-134]
+                    GraphElementPatternFiller [119-131]
+                      Identifier(e2) [119-121]
+                      GraphLabelFilter [121-131]
+                        GraphElementLabel [122-131]
+                          Identifier(Transfers) [122-131]
+                  GraphNodePattern [134-146]
+                    GraphElementPatternFiller [135-145]
+                      Identifier(a2) [135-137]
+                      GraphLabelFilter [137-145]
+                        GraphElementLabel [138-145]
+                          Identifier(Account) [138-145]
+                GraphPathPattern [150-190]
+                  GraphNodePattern [150-161]
+                    GraphElementPatternFiller [151-160]
+                      Identifier(a) [151-152]
+                      GraphLabelFilter [152-160]
+                        GraphElementLabel [153-160]
+                          Identifier(Account) [153-160]
+                  GraphEdgePattern [161-178]
+                    GraphElementPatternFiller [163-175]
+                      Identifier(e3) [163-165]
+                      GraphLabelFilter [165-175]
+                        GraphElementLabel [166-175]
+                          Identifier(Transfers) [166-175]
+                  GraphNodePattern [178-190]
+                    GraphElementPatternFiller [179-189]
+                      Identifier(a3) [179-181]
+                      GraphLabelFilter [181-189]
+                        GraphElementLabel [182-189]
+                          Identifier(Account) [182-189]
+                WhereClause [191-492]
+                  AndExpr [197-492]
+                    BinaryExpression(<) [197-265]
+                      PathExpression [197-211]
+                        Identifier(e1) [197-199]
+                        Identifier(create_time) [200-211]
+                      FunctionCall [214-265]
+                        PathExpression [214-227]
+                          Identifier(TIMESTAMP_SUB) [214-227]
+                        FunctionCall [228-247]
+                          PathExpression [228-245]
+                            Identifier(`CURRENT_TIMESTAMP`) [228-245]
+                        IntervalExpr [249-264]
+                          IntLiteral(90) [258-260]
+                          Identifier(DAY) [261-264]
+                    BinaryExpression(>=) [272-341]
+                      PathExpression [272-286]
+                        Identifier(e2) [272-274]
+                        Identifier(create_time) [275-286]
+                      FunctionCall [290-341]
+                        PathExpression [290-303]
+                          Identifier(TIMESTAMP_SUB) [290-303]
+                        FunctionCall [304-323]
+                          PathExpression [304-321]
+                            Identifier(`CURRENT_TIMESTAMP`) [304-321]
+                        IntervalExpr [325-340]
+                          IntLiteral(90) [334-336]
+                          Identifier(DAY) [337-340]
+                    BinaryExpression(<) [348-416]
+                      PathExpression [348-362]
+                        Identifier(e2) [348-350]
+                        Identifier(create_time) [351-362]
+                      FunctionCall [365-416]
+                        PathExpression [365-378]
+                          Identifier(TIMESTAMP_SUB) [365-378]
+                        FunctionCall [379-398]
+                          PathExpression [379-396]
+                            Identifier(`CURRENT_TIMESTAMP`) [379-396]
+                        IntervalExpr [400-415]
+                          IntLiteral(30) [409-411]
+                          Identifier(DAY) [412-415]
+                    BinaryExpression(>=) [423-492]
+                      PathExpression [423-437]
+                        Identifier(e3) [423-425]
+                        Identifier(create_time) [426-437]
+                      FunctionCall [441-492]
+                        PathExpression [441-454]
+                          Identifier(TIMESTAMP_SUB) [441-454]
+                        FunctionCall [455-474]
+                          PathExpression [455-472]
+                            Identifier(`CURRENT_TIMESTAMP`) [455-472]
+                        IntervalExpr [476-491]
+                          IntLiteral(30) [485-487]
+                          Identifier(DAY) [488-491]
+            GqlLet [493-545]
+              GqlLetVariableDefinitionList [497-545]
+                GqlLetVariableDefinition [497-545]
+                  Identifier(total_amount) [497-509]
+                  BinaryExpression(+) [512-545]
+                    BinaryExpression(+) [512-533]
+                      PathExpression [512-521]
+                        Identifier(e1) [512-514]
+                        Identifier(amount) [515-521]
+                      PathExpression [524-533]
+                        Identifier(e2) [524-526]
+                        Identifier(amount) [527-533]
+                    PathExpression [536-545]
+                      Identifier(e3) [536-538]
+                      Identifier(amount) [539-545]
+            GqlReturn [546-613]
+              Select [553-613]
+                SelectList [553-613]
+                  SelectColumn [553-567]
+                    PathExpression [553-558]
+                      Identifier(a1) [553-555]
+                      Identifier(id) [556-558]
+                    Alias [559-567]
+                      Identifier(a1_id) [562-567]
+                  SelectColumn [569-583]
+                    PathExpression [569-574]
+                      Identifier(a2) [569-571]
+                      Identifier(id) [572-574]
+                    Alias [575-583]
+                      Identifier(a2_id) [578-583]
+                  SelectColumn [585-599]
+                    PathExpression [585-590]
+                      Identifier(a3) [585-587]
+                      Identifier(id) [588-590]
+                    Alias [591-599]
+                      Identifier(a3_id) [594-599]
+                  SelectColumn [601-613]
+                    PathExpression [601-613]
+                      Identifier(total_amount) [601-613]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/best-practices-tuning-queries#defer-intermediate-result
+GRAPH FinGraph
+MATCH (a:Account {id: 1})-[e1:Transfers]->(a1:Account),
+  (a:Account)-[e2:Transfers]->(a2:Account),
+  (a:Account)-[e3:Transfers]->(a3:Account)
+WHERE e1.create_time < TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 90 DAY)
+  AND e2.create_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 90 DAY)
+  AND e2.create_time < TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
+  AND e3.create_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
+LET total_amount = e1.amount + e2.amount + e3.amount
+RETURN a1.id as a1_id, a2.id as a2_id, a3.id as a3_id, total_amount
+--
+QueryStatement [0-580]
+  Query [0-580]
+    GqlQuery [0-580]
+      GraphTableQuery [0-580]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-580]
+          GqlOperatorList [15-580]
+            GqlMatch [15-459]
+              GraphPattern [21-459]
+                GraphPathPattern [21-69]
+                  GraphNodePattern [21-40]
+                    GraphElementPatternFiller [22-39]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                      GraphPropertySpecification [32-39]
+                        GraphPropertyNameAndValue [33-38]
+                          Identifier(id) [33-35]
+                          IntLiteral(1) [37-38]
+                  GraphEdgePattern [40-57]
+                    GraphElementPatternFiller [42-54]
+                      Identifier(e1) [42-44]
+                      GraphLabelFilter [44-54]
+                        GraphElementLabel [45-54]
+                          Identifier(Transfers) [45-54]
+                  GraphNodePattern [57-69]
+                    GraphElementPatternFiller [58-68]
+                      Identifier(a1) [58-60]
+                      GraphLabelFilter [60-68]
+                        GraphElementLabel [61-68]
+                          Identifier(Account) [61-68]
+                GraphPathPattern [73-113]
+                  GraphNodePattern [73-84]
+                    GraphElementPatternFiller [74-83]
+                      Identifier(a) [74-75]
+                      GraphLabelFilter [75-83]
+                        GraphElementLabel [76-83]
+                          Identifier(Account) [76-83]
+                  GraphEdgePattern [84-101]
+                    GraphElementPatternFiller [86-98]
+                      Identifier(e2) [86-88]
+                      GraphLabelFilter [88-98]
+                        GraphElementLabel [89-98]
+                          Identifier(Transfers) [89-98]
+                  GraphNodePattern [101-113]
+                    GraphElementPatternFiller [102-112]
+                      Identifier(a2) [102-104]
+                      GraphLabelFilter [104-112]
+                        GraphElementLabel [105-112]
+                          Identifier(Account) [105-112]
+                GraphPathPattern [117-157]
+                  GraphNodePattern [117-128]
+                    GraphElementPatternFiller [118-127]
+                      Identifier(a) [118-119]
+                      GraphLabelFilter [119-127]
+                        GraphElementLabel [120-127]
+                          Identifier(Account) [120-127]
+                  GraphEdgePattern [128-145]
+                    GraphElementPatternFiller [130-142]
+                      Identifier(e3) [130-132]
+                      GraphLabelFilter [132-142]
+                        GraphElementLabel [133-142]
+                          Identifier(Transfers) [133-142]
+                  GraphNodePattern [145-157]
+                    GraphElementPatternFiller [146-156]
+                      Identifier(a3) [146-148]
+                      GraphLabelFilter [148-156]
+                        GraphElementLabel [149-156]
+                          Identifier(Account) [149-156]
+                WhereClause [158-459]
+                  AndExpr [164-459]
+                    BinaryExpression(<) [164-232]
+                      PathExpression [164-178]
+                        Identifier(e1) [164-166]
+                        Identifier(create_time) [167-178]
+                      FunctionCall [181-232]
+                        PathExpression [181-194]
+                          Identifier(TIMESTAMP_SUB) [181-194]
+                        FunctionCall [195-214]
+                          PathExpression [195-212]
+                            Identifier(`CURRENT_TIMESTAMP`) [195-212]
+                        IntervalExpr [216-231]
+                          IntLiteral(90) [225-227]
+                          Identifier(DAY) [228-231]
+                    BinaryExpression(>=) [239-308]
+                      PathExpression [239-253]
+                        Identifier(e2) [239-241]
+                        Identifier(create_time) [242-253]
+                      FunctionCall [257-308]
+                        PathExpression [257-270]
+                          Identifier(TIMESTAMP_SUB) [257-270]
+                        FunctionCall [271-290]
+                          PathExpression [271-288]
+                            Identifier(`CURRENT_TIMESTAMP`) [271-288]
+                        IntervalExpr [292-307]
+                          IntLiteral(90) [301-303]
+                          Identifier(DAY) [304-307]
+                    BinaryExpression(<) [315-383]
+                      PathExpression [315-329]
+                        Identifier(e2) [315-317]
+                        Identifier(create_time) [318-329]
+                      FunctionCall [332-383]
+                        PathExpression [332-345]
+                          Identifier(TIMESTAMP_SUB) [332-345]
+                        FunctionCall [346-365]
+                          PathExpression [346-363]
+                            Identifier(`CURRENT_TIMESTAMP`) [346-363]
+                        IntervalExpr [367-382]
+                          IntLiteral(30) [376-378]
+                          Identifier(DAY) [379-382]
+                    BinaryExpression(>=) [390-459]
+                      PathExpression [390-404]
+                        Identifier(e3) [390-392]
+                        Identifier(create_time) [393-404]
+                      FunctionCall [408-459]
+                        PathExpression [408-421]
+                          Identifier(TIMESTAMP_SUB) [408-421]
+                        FunctionCall [422-441]
+                          PathExpression [422-439]
+                            Identifier(`CURRENT_TIMESTAMP`) [422-439]
+                        IntervalExpr [443-458]
+                          IntLiteral(30) [452-454]
+                          Identifier(DAY) [455-458]
+            GqlLet [460-512]
+              GqlLetVariableDefinitionList [464-512]
+                GqlLetVariableDefinition [464-512]
+                  Identifier(total_amount) [464-476]
+                  BinaryExpression(+) [479-512]
+                    BinaryExpression(+) [479-500]
+                      PathExpression [479-488]
+                        Identifier(e1) [479-481]
+                        Identifier(amount) [482-488]
+                      PathExpression [491-500]
+                        Identifier(e2) [491-493]
+                        Identifier(amount) [494-500]
+                    PathExpression [503-512]
+                      Identifier(e3) [503-505]
+                      Identifier(amount) [506-512]
+            GqlReturn [513-580]
+              Select [520-580]
+                SelectList [520-580]
+                  SelectColumn [520-534]
+                    PathExpression [520-525]
+                      Identifier(a1) [520-522]
+                      Identifier(id) [523-525]
+                    Alias [526-534]
+                      Identifier(a1_id) [529-534]
+                  SelectColumn [536-550]
+                    PathExpression [536-541]
+                      Identifier(a2) [536-538]
+                      Identifier(id) [539-541]
+                    Alias [542-550]
+                      Identifier(a2_id) [545-550]
+                  SelectColumn [552-566]
+                    PathExpression [552-557]
+                      Identifier(a3) [552-554]
+                      Identifier(id) [555-557]
+                    Alias [558-566]
+                      Identifier(a3_id) [561-566]
+                  SelectColumn [568-580]
+                    PathExpression [568-580]
+                      Identifier(total_amount) [568-580]
diff --git a/parser/testdata/spanner/guides/graph.create-update-drop-schema.metadata.json b/parser/testdata/spanner/guides/graph.create-update-drop-schema.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.create-update-drop-schema.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/graph.create-update-drop-schema.test b/parser/testdata/spanner/guides/graph.create-update-drop-schema.test
new file mode 100644
index 0000000..02a0974
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.create-update-drop-schema.test
@@ -0,0 +1,801 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/create-update-drop-schema
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/graph/create-update-drop-schema#create-node-input-tables
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Person (
+    id               INT64 NOT NULL,
+    name             STRING(MAX),
+    birthday         TIMESTAMP,
+    country          STRING(MAX),
+    city             STRING(MAX),
+  ) PRIMARY KEY (id)
+--
+CreateTableStatement [0-213]
+  PathExpression [13-19]
+    Identifier(Person) [13-19]
+  TableElementList [20-196]
+    ColumnDefinition [26-57]
+      Identifier(id) [26-28]
+      SimpleColumnSchema [43-57]
+        PathExpression [43-48]
+          Identifier(INT64) [43-48]
+        ColumnAttributeList [49-57]
+          NotNullColumnAttribute [49-57]
+    ColumnDefinition [63-91]
+      Identifier(name) [63-67]
+      SimpleColumnSchema [80-91]
+        PathExpression [80-86]
+          Identifier(STRING) [80-86]
+        TypeParameterList [86-90]
+          MaxLiteral() [87-90]
+    ColumnDefinition [97-123]
+      Identifier(birthday) [97-105]
+      SimpleColumnSchema [114-123]
+        PathExpression [114-123]
+          Identifier(TIMESTAMP) [114-123]
+    ColumnDefinition [129-157]
+      Identifier(country) [129-136]
+      SimpleColumnSchema [146-157]
+        PathExpression [146-152]
+          Identifier(STRING) [146-152]
+        TypeParameterList [152-156]
+          MaxLiteral() [153-156]
+    ColumnDefinition [163-191]
+      Identifier(city) [163-167]
+      SimpleColumnSchema [180-191]
+        PathExpression [180-186]
+          Identifier(STRING) [180-186]
+        TypeParameterList [186-190]
+          MaxLiteral() [187-190]
+  SpannerTableOptions [197-213]
+    PrimaryKey(ENFORCED) [197-213]
+      PrimaryKeyElementList [209-213]
+        PrimaryKeyElement(ASC) [210-212]
+          Identifier(id) [210-212]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/create-update-drop-schema#create-node-input-tables
+CREATE TABLE Account (
+    id               INT64 NOT NULL,
+    create_time      TIMESTAMP,
+    is_blocked       BOOL,
+    nick_name        STRING(MAX),
+  ) PRIMARY KEY (id)
+--
+CreateTableStatement [0-173]
+  PathExpression [13-20]
+    Identifier(Account) [13-20]
+  TableElementList [21-156]
+    ColumnDefinition [27-58]
+      Identifier(id) [27-29]
+      SimpleColumnSchema [44-58]
+        PathExpression [44-49]
+          Identifier(INT64) [44-49]
+        ColumnAttributeList [50-58]
+          NotNullColumnAttribute [50-58]
+    ColumnDefinition [64-90]
+      Identifier(create_time) [64-75]
+      SimpleColumnSchema [81-90]
+        PathExpression [81-90]
+          Identifier(TIMESTAMP) [81-90]
+    ColumnDefinition [96-117]
+      Identifier(is_blocked) [96-106]
+      SimpleColumnSchema [113-117]
+        PathExpression [113-117]
+          Identifier(BOOL) [113-117]
+    ColumnDefinition [123-151]
+      Identifier(nick_name) [123-132]
+      SimpleColumnSchema [140-151]
+        PathExpression [140-146]
+          Identifier(STRING) [140-146]
+        TypeParameterList [146-150]
+          MaxLiteral() [147-150]
+  SpannerTableOptions [157-173]
+    PrimaryKey(ENFORCED) [157-173]
+      PrimaryKeyElementList [169-173]
+        PrimaryKeyElement(ASC) [170-172]
+          Identifier(id) [170-172]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/create-update-drop-schema#create-edge-input-tables
+CREATE TABLE PersonOwnAccount (
+    id               INT64 NOT NULL,
+    account_id       INT64 NOT NULL,
+    create_time      TIMESTAMP,
+    FOREIGN KEY (account_id) REFERENCES Account (id)
+  ) PRIMARY KEY (id, account_id),
+    INTERLEAVE IN PARENT Person ON DELETE CASCADE
+--
+CreateTableStatement [0-274]
+  PathExpression [13-29]
+    Identifier(PersonOwnAccount) [13-29]
+  TableElementList [30-194]
+    ColumnDefinition [36-67]
+      Identifier(id) [36-38]
+      SimpleColumnSchema [53-67]
+        PathExpression [53-58]
+          Identifier(INT64) [53-58]
+        ColumnAttributeList [59-67]
+          NotNullColumnAttribute [59-67]
+    ColumnDefinition [73-104]
+      Identifier(account_id) [73-83]
+      SimpleColumnSchema [90-104]
+        PathExpression [90-95]
+          Identifier(INT64) [90-95]
+        ColumnAttributeList [96-104]
+          NotNullColumnAttribute [96-104]
+    ColumnDefinition [110-136]
+      Identifier(create_time) [110-121]
+      SimpleColumnSchema [127-136]
+        PathExpression [127-136]
+          Identifier(TIMESTAMP) [127-136]
+    ForeignKey [142-190]
+      ColumnList [154-166]
+        Identifier(account_id) [155-165]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [167-190]
+        PathExpression [178-185]
+          Identifier(Account) [178-185]
+        ColumnList [186-190]
+          Identifier(id) [187-189]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [190-190]
+  SpannerTableOptions [195-274]
+    PrimaryKey(ENFORCED) [195-223]
+      PrimaryKeyElementList [207-223]
+        PrimaryKeyElement(ASC) [208-210]
+          Identifier(id) [208-210]
+        PrimaryKeyElement(ASC) [212-222]
+          Identifier(account_id) [212-222]
+    SpannerInterleaveClause [223-274]
+      PathExpression [250-256]
+        Identifier(Person) [250-256]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/create-update-drop-schema#create-edge-input-tables
+CREATE TABLE AccountTransferAccount (
+    id               INT64 NOT NULL,
+    to_id            INT64 NOT NULL,
+    amount           FLOAT64,
+    create_time      TIMESTAMP NOT NULL,
+    order_number     STRING(MAX),
+    FOREIGN KEY (to_id) REFERENCES Account (id)
+  ) PRIMARY KEY (id, to_id, create_time),
+    INTERLEAVE IN PARENT Account ON DELETE CASCADE
+--
+CreateTableStatement [0-357]
+  PathExpression [13-35]
+    Identifier(AccountTransferAccount) [13-35]
+  TableElementList [36-268]
+    ColumnDefinition [42-73]
+      Identifier(id) [42-44]
+      SimpleColumnSchema [59-73]
+        PathExpression [59-64]
+          Identifier(INT64) [59-64]
+        ColumnAttributeList [65-73]
+          NotNullColumnAttribute [65-73]
+    ColumnDefinition [79-110]
+      Identifier(to_id) [79-84]
+      SimpleColumnSchema [96-110]
+        PathExpression [96-101]
+          Identifier(INT64) [96-101]
+        ColumnAttributeList [102-110]
+          NotNullColumnAttribute [102-110]
+    ColumnDefinition [116-140]
+      Identifier(amount) [116-122]
+      SimpleColumnSchema [133-140]
+        PathExpression [133-140]
+          Identifier(FLOAT64) [133-140]
+    ColumnDefinition [146-181]
+      Identifier(create_time) [146-157]
+      SimpleColumnSchema [163-181]
+        PathExpression [163-172]
+          Identifier(TIMESTAMP) [163-172]
+        ColumnAttributeList [173-181]
+          NotNullColumnAttribute [173-181]
+    ColumnDefinition [187-215]
+      Identifier(order_number) [187-199]
+      SimpleColumnSchema [204-215]
+        PathExpression [204-210]
+          Identifier(STRING) [204-210]
+        TypeParameterList [210-214]
+          MaxLiteral() [211-214]
+    ForeignKey [221-264]
+      ColumnList [233-240]
+        Identifier(to_id) [234-239]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [241-264]
+        PathExpression [252-259]
+          Identifier(Account) [252-259]
+        ColumnList [260-264]
+          Identifier(id) [261-263]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [264-264]
+  SpannerTableOptions [269-357]
+    PrimaryKey(ENFORCED) [269-305]
+      PrimaryKeyElementList [281-305]
+        PrimaryKeyElement(ASC) [282-284]
+          Identifier(id) [282-284]
+        PrimaryKeyElement(ASC) [286-291]
+          Identifier(to_id) [286-291]
+        PrimaryKeyElement(ASC) [293-304]
+          Identifier(create_time) [293-304]
+    SpannerInterleaveClause [305-357]
+      PathExpression [332-339]
+        Identifier(Account) [332-339]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/create-update-drop-schema#define-property-graph
+CREATE PROPERTY GRAPH FinGraph
+    NODE TABLES (
+      Account,
+      Person
+    )
+    EDGE TABLES (
+      PersonOwnAccount
+        SOURCE KEY (id) REFERENCES Person (id)
+        DESTINATION KEY (account_id) REFERENCES Account (id)
+        LABEL Owns,
+      AccountTransferAccount
+        SOURCE KEY (id) REFERENCES Account (id)
+        DESTINATION KEY (to_id) REFERENCES Account (id)
+        LABEL Transfers
+    )
+--
+CreatePropertyGraphStatement [0-414]
+  PathExpression [22-30]
+    Identifier(FinGraph) [22-30]
+  GraphElementTableList [47-82]
+    GraphElementTable [55-62]
+      PathExpression [55-62]
+        Identifier(Account) [55-62]
+      GraphElementLabelAndPropertiesList [62-62]
+        GraphElementLabelAndProperties [62-62]
+          GraphProperties [62-62]
+    GraphElementTable [70-76]
+      PathExpression [70-76]
+        Identifier(Person) [70-76]
+      GraphElementLabelAndPropertiesList [76-76]
+        GraphElementLabelAndProperties [76-76]
+          GraphProperties [76-76]
+  GraphElementTableList [99-414]
+    GraphElementTable [107-250]
+      PathExpression [107-123]
+        Identifier(PersonOwnAccount) [107-123]
+      GraphNodeTableReference(SOURCE) [132-170]
+        Identifier(Person) [159-165]
+        ColumnList [143-147]
+          Identifier(id) [144-146]
+        ColumnList [166-170]
+          Identifier(id) [167-169]
+      GraphNodeTableReference(DESTINATION) [179-231]
+        Identifier(Account) [219-226]
+        ColumnList [195-207]
+          Identifier(account_id) [196-206]
+        ColumnList [227-231]
+          Identifier(id) [228-230]
+      GraphElementLabelAndPropertiesList [240-250]
+        GraphElementLabelAndProperties [240-250]
+          Identifier(Owns) [246-250]
+          GraphProperties [250-250]
+    GraphElementTable [258-408]
+      PathExpression [258-280]
+        Identifier(AccountTransferAccount) [258-280]
+      GraphNodeTableReference(SOURCE) [289-328]
+        Identifier(Account) [316-323]
+        ColumnList [300-304]
+          Identifier(id) [301-303]
+        ColumnList [324-328]
+          Identifier(id) [325-327]
+      GraphNodeTableReference(DESTINATION) [337-384]
+        Identifier(Account) [372-379]
+        ColumnList [353-360]
+          Identifier(to_id) [354-359]
+        ColumnList [380-384]
+          Identifier(id) [381-383]
+      GraphElementLabelAndPropertiesList [393-408]
+        GraphElementLabelAndProperties [393-408]
+          Identifier(Transfers) [399-408]
+          GraphProperties [408-408]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/create-update-drop-schema#add-new-node-or-edge
+CREATE TABLE Company (
+  id               INT64 NOT NULL,
+  name             STRING(MAX),
+) PRIMARY KEY (id)
+--
+CreateTableStatement [0-108]
+  PathExpression [13-20]
+    Identifier(Company) [13-20]
+  TableElementList [21-91]
+    ColumnDefinition [25-56]
+      Identifier(id) [25-27]
+      SimpleColumnSchema [42-56]
+        PathExpression [42-47]
+          Identifier(INT64) [42-47]
+        ColumnAttributeList [48-56]
+          NotNullColumnAttribute [48-56]
+    ColumnDefinition [60-88]
+      Identifier(name) [60-64]
+      SimpleColumnSchema [77-88]
+        PathExpression [77-83]
+          Identifier(STRING) [77-83]
+        TypeParameterList [83-87]
+          MaxLiteral() [84-87]
+  SpannerTableOptions [92-108]
+    PrimaryKey(ENFORCED) [92-108]
+      PrimaryKeyElementList [104-108]
+        PrimaryKeyElement(ASC) [105-107]
+          Identifier(id) [105-107]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/create-update-drop-schema#add-new-node-or-edge
+CREATE TABLE PersonInvestCompany (
+  id               INT64 NOT NULL,
+  company_id       INT64 NOT NULL,
+  FOREIGN KEY (company_id) REFERENCES Company (id)
+) PRIMARY KEY (id, company_id),
+  INTERLEAVE IN PARENT Person ON DELETE CASCADE
+--
+CreateTableStatement [0-235]
+  PathExpression [13-32]
+    Identifier(PersonInvestCompany) [13-32]
+  TableElementList [33-157]
+    ColumnDefinition [37-68]
+      Identifier(id) [37-39]
+      SimpleColumnSchema [54-68]
+        PathExpression [54-59]
+          Identifier(INT64) [54-59]
+        ColumnAttributeList [60-68]
+          NotNullColumnAttribute [60-68]
+    ColumnDefinition [72-103]
+      Identifier(company_id) [72-82]
+      SimpleColumnSchema [89-103]
+        PathExpression [89-94]
+          Identifier(INT64) [89-94]
+        ColumnAttributeList [95-103]
+          NotNullColumnAttribute [95-103]
+    ForeignKey [107-155]
+      ColumnList [119-131]
+        Identifier(company_id) [120-130]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [132-155]
+        PathExpression [143-150]
+          Identifier(Company) [143-150]
+        ColumnList [151-155]
+          Identifier(id) [152-154]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [155-155]
+  SpannerTableOptions [158-235]
+    PrimaryKey(ENFORCED) [158-186]
+      PrimaryKeyElementList [170-186]
+        PrimaryKeyElement(ASC) [171-173]
+          Identifier(id) [171-173]
+        PrimaryKeyElement(ASC) [175-185]
+          Identifier(company_id) [175-185]
+    SpannerInterleaveClause [186-235]
+      PathExpression [211-217]
+        Identifier(Person) [211-217]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/create-update-drop-schema#add-new-node-or-edge
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Person,
+    Account,
+    Company
+  )
+  EDGE TABLES (
+    AccountTransferAccount
+      SOURCE KEY (id) REFERENCES Account
+      DESTINATION KEY (to_id) REFERENCES Account
+      LABEL Transfers,
+    PersonOwnAccount
+      SOURCE KEY (id) REFERENCES Person
+      DESTINATION KEY (account_id) REFERENCES Account
+      LABEL Owns,
+    PersonInvestCompany
+      SOURCE KEY (id) REFERENCES Person
+      DESTINATION KEY (company_id) REFERENCES Company
+      LABEL Invests
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [0-529]
+  PathExpression [33-41]
+    Identifier(FinGraph) [33-41]
+  GraphElementTableList [56-98]
+    GraphElementTable [62-68]
+      PathExpression [62-68]
+        Identifier(Person) [62-68]
+      GraphElementLabelAndPropertiesList [68-68]
+        GraphElementLabelAndProperties [68-68]
+          GraphProperties [68-68]
+    GraphElementTable [74-81]
+      PathExpression [74-81]
+        Identifier(Account) [74-81]
+      GraphElementLabelAndPropertiesList [81-81]
+        GraphElementLabelAndProperties [81-81]
+          GraphProperties [81-81]
+    GraphElementTable [87-94]
+      PathExpression [87-94]
+        Identifier(Company) [87-94]
+      GraphElementLabelAndPropertiesList [94-94]
+        GraphElementLabelAndProperties [94-94]
+          GraphProperties [94-94]
+  GraphElementTableList [113-529]
+    GraphElementTable [119-253]
+      PathExpression [119-141]
+        Identifier(AccountTransferAccount) [119-141]
+      GraphNodeTableReference(SOURCE) [148-182]
+        Identifier(Account) [175-182]
+        ColumnList [159-163]
+          Identifier(id) [160-162]
+      GraphNodeTableReference(DESTINATION) [189-231]
+        Identifier(Account) [224-231]
+        ColumnList [205-212]
+          Identifier(to_id) [206-211]
+      GraphElementLabelAndPropertiesList [238-253]
+        GraphElementLabelAndProperties [238-253]
+          Identifier(Transfers) [244-253]
+          GraphProperties [253-253]
+    GraphElementTable [259-386]
+      PathExpression [259-275]
+        Identifier(PersonOwnAccount) [259-275]
+      GraphNodeTableReference(SOURCE) [282-315]
+        Identifier(Person) [309-315]
+        ColumnList [293-297]
+          Identifier(id) [294-296]
+      GraphNodeTableReference(DESTINATION) [322-369]
+        Identifier(Account) [362-369]
+        ColumnList [338-350]
+          Identifier(account_id) [339-349]
+      GraphElementLabelAndPropertiesList [376-386]
+        GraphElementLabelAndProperties [376-386]
+          Identifier(Owns) [382-386]
+          GraphProperties [386-386]
+    GraphElementTable [392-525]
+      PathExpression [392-411]
+        Identifier(PersonInvestCompany) [392-411]
+      GraphNodeTableReference(SOURCE) [418-451]
+        Identifier(Person) [445-451]
+        ColumnList [429-433]
+          Identifier(id) [430-432]
+      GraphNodeTableReference(DESTINATION) [458-505]
+        Identifier(Company) [498-505]
+        ColumnList [474-486]
+          Identifier(company_id) [475-485]
+      GraphElementLabelAndPropertiesList [512-525]
+        GraphElementLabelAndProperties [512-525]
+          Identifier(Invests) [518-525]
+          GraphProperties [525-525]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/create-update-drop-schema#update-node-or-edge
+ALTER TABLE Person
+ADD COLUMN mailing_address STRING(MAX)
+--
+AlterTableStatement [0-57]
+  PathExpression [12-18]
+    Identifier(Person) [12-18]
+  AlterActionList [19-57]
+    AddColumnAction [19-57]
+      ColumnDefinition [30-57]
+        Identifier(mailing_address) [30-45]
+        SimpleColumnSchema [46-57]
+          PathExpression [46-52]
+            Identifier(STRING) [46-52]
+          TypeParameterList [52-56]
+            MaxLiteral() [53-56]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/create-update-drop-schema#update-node-or-edge
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Person,
+    Account
+  )
+  EDGE TABLES (
+    AccountTransferAccount
+      SOURCE KEY (id) REFERENCES Account
+      DESTINATION KEY (to_id) REFERENCES Account
+      LABEL Transfers,
+    PersonOwnAccount
+      SOURCE KEY (id) REFERENCES Person
+      DESTINATION KEY (account_id) REFERENCES Account
+      LABEL Owns
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [0-377]
+  PathExpression [33-41]
+    Identifier(FinGraph) [33-41]
+  GraphElementTableList [56-85]
+    GraphElementTable [62-68]
+      PathExpression [62-68]
+        Identifier(Person) [62-68]
+      GraphElementLabelAndPropertiesList [68-68]
+        GraphElementLabelAndProperties [68-68]
+          GraphProperties [68-68]
+    GraphElementTable [74-81]
+      PathExpression [74-81]
+        Identifier(Account) [74-81]
+      GraphElementLabelAndPropertiesList [81-81]
+        GraphElementLabelAndProperties [81-81]
+          GraphProperties [81-81]
+  GraphElementTableList [100-377]
+    GraphElementTable [106-240]
+      PathExpression [106-128]
+        Identifier(AccountTransferAccount) [106-128]
+      GraphNodeTableReference(SOURCE) [135-169]
+        Identifier(Account) [162-169]
+        ColumnList [146-150]
+          Identifier(id) [147-149]
+      GraphNodeTableReference(DESTINATION) [176-218]
+        Identifier(Account) [211-218]
+        ColumnList [192-199]
+          Identifier(to_id) [193-198]
+      GraphElementLabelAndPropertiesList [225-240]
+        GraphElementLabelAndProperties [225-240]
+          Identifier(Transfers) [231-240]
+          GraphProperties [240-240]
+    GraphElementTable [246-373]
+      PathExpression [246-262]
+        Identifier(PersonOwnAccount) [246-262]
+      GraphNodeTableReference(SOURCE) [269-302]
+        Identifier(Person) [296-302]
+        ColumnList [280-284]
+          Identifier(id) [281-283]
+      GraphNodeTableReference(DESTINATION) [309-356]
+        Identifier(Account) [349-356]
+        ColumnList [325-337]
+          Identifier(account_id) [326-336]
+      GraphElementLabelAndPropertiesList [363-373]
+        GraphElementLabelAndProperties [363-373]
+          Identifier(Owns) [369-373]
+          GraphProperties [373-373]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/create-update-drop-schema#remove-node-or-edge
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+    NODE TABLES (
+      Account
+    )
+    EDGE TABLES (
+      AccountTransferAccount
+        SOURCE KEY (id) REFERENCES Account
+        DESTINATION KEY (to_id) REFERENCES Account
+        LABEL Transfers
+    )
+--
+CreatePropertyGraphStatement(is_or_replace) [0-250]
+  PathExpression [33-41]
+    Identifier(FinGraph) [33-41]
+  GraphElementTableList [58-79]
+    GraphElementTable [66-73]
+      PathExpression [66-73]
+        Identifier(Account) [66-73]
+      GraphElementLabelAndPropertiesList [73-73]
+        GraphElementLabelAndProperties [73-73]
+          GraphProperties [73-73]
+  GraphElementTableList [96-250]
+    GraphElementTable [104-244]
+      PathExpression [104-126]
+        Identifier(AccountTransferAccount) [104-126]
+      GraphNodeTableReference(SOURCE) [135-169]
+        Identifier(Account) [162-169]
+        ColumnList [146-150]
+          Identifier(id) [147-149]
+      GraphNodeTableReference(DESTINATION) [178-220]
+        Identifier(Account) [213-220]
+        ColumnList [194-201]
+          Identifier(to_id) [195-200]
+      GraphElementLabelAndPropertiesList [229-244]
+        GraphElementLabelAndProperties [229-244]
+          Identifier(Transfers) [235-244]
+          GraphProperties [244-244]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/create-update-drop-schema#create-property-graph-named-schema
+CREATE SCHEMA sch1
+--
+CreateSchemaStatement [0-18]
+  PathExpression [14-18]
+    Identifier(sch1) [14-18]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/create-update-drop-schema#create-property-graph-named-schema
+CREATE SCHEMA sch2
+--
+CreateSchemaStatement [0-18]
+  PathExpression [14-18]
+    Identifier(sch2) [14-18]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/create-update-drop-schema#create-property-graph-named-schema
+CREATE TABLE sch1.Person (
+  id               INT64 NOT NULL,
+  name             STRING(MAX),
+  birthday         TIMESTAMP,
+  country          STRING(MAX),
+  city             STRING(MAX),
+) PRIMARY KEY (id)
+--
+CreateTableStatement [0-206]
+  PathExpression [13-24]
+    Identifier(sch1) [13-17]
+    Identifier(Person) [18-24]
+  TableElementList [25-189]
+    ColumnDefinition [29-60]
+      Identifier(id) [29-31]
+      SimpleColumnSchema [46-60]
+        PathExpression [46-51]
+          Identifier(INT64) [46-51]
+        ColumnAttributeList [52-60]
+          NotNullColumnAttribute [52-60]
+    ColumnDefinition [64-92]
+      Identifier(name) [64-68]
+      SimpleColumnSchema [81-92]
+        PathExpression [81-87]
+          Identifier(STRING) [81-87]
+        TypeParameterList [87-91]
+          MaxLiteral() [88-91]
+    ColumnDefinition [96-122]
+      Identifier(birthday) [96-104]
+      SimpleColumnSchema [113-122]
+        PathExpression [113-122]
+          Identifier(TIMESTAMP) [113-122]
+    ColumnDefinition [126-154]
+      Identifier(country) [126-133]
+      SimpleColumnSchema [143-154]
+        PathExpression [143-149]
+          Identifier(STRING) [143-149]
+        TypeParameterList [149-153]
+          MaxLiteral() [150-153]
+    ColumnDefinition [158-186]
+      Identifier(city) [158-162]
+      SimpleColumnSchema [175-186]
+        PathExpression [175-181]
+          Identifier(STRING) [175-181]
+        TypeParameterList [181-185]
+          MaxLiteral() [182-185]
+  SpannerTableOptions [190-206]
+    PrimaryKey(ENFORCED) [190-206]
+      PrimaryKeyElementList [202-206]
+        PrimaryKeyElement(ASC) [203-205]
+          Identifier(id) [203-205]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/create-update-drop-schema#create-property-graph-named-schema
+CREATE TABLE sch2.Account (
+  id               INT64 NOT NULL,
+  create_time      TIMESTAMP,
+  is_blocked       BOOL,
+  nick_name        STRING(MAX),
+) PRIMARY KEY (id)
+--
+CreateTableStatement [0-168]
+  PathExpression [13-25]
+    Identifier(sch2) [13-17]
+    Identifier(Account) [18-25]
+  TableElementList [26-151]
+    ColumnDefinition [30-61]
+      Identifier(id) [30-32]
+      SimpleColumnSchema [47-61]
+        PathExpression [47-52]
+          Identifier(INT64) [47-52]
+        ColumnAttributeList [53-61]
+          NotNullColumnAttribute [53-61]
+    ColumnDefinition [65-91]
+      Identifier(create_time) [65-76]
+      SimpleColumnSchema [82-91]
+        PathExpression [82-91]
+          Identifier(TIMESTAMP) [82-91]
+    ColumnDefinition [95-116]
+      Identifier(is_blocked) [95-105]
+      SimpleColumnSchema [112-116]
+        PathExpression [112-116]
+          Identifier(BOOL) [112-116]
+    ColumnDefinition [120-148]
+      Identifier(nick_name) [120-129]
+      SimpleColumnSchema [137-148]
+        PathExpression [137-143]
+          Identifier(STRING) [137-143]
+        TypeParameterList [143-147]
+          MaxLiteral() [144-147]
+  SpannerTableOptions [152-168]
+    PrimaryKey(ENFORCED) [152-168]
+      PrimaryKeyElementList [164-168]
+        PrimaryKeyElement(ASC) [165-167]
+          Identifier(id) [165-167]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/create-update-drop-schema#create-property-graph-named-schema
+CREATE TABLE sch1.PersonOwnAccount (
+  id               INT64 NOT NULL,
+  account_id       INT64 NOT NULL,
+  create_time      TIMESTAMP,
+  FOREIGN KEY (account_id) REFERENCES sch2.Account (id)
+) PRIMARY KEY (id, account_id),
+  INTERLEAVE IN PARENT sch1.Person ON DELETE CASCADE
+--
+CreateTableStatement [0-277]
+  PathExpression [13-34]
+    Identifier(sch1) [13-17]
+    Identifier(PersonOwnAccount) [18-34]
+  TableElementList [35-194]
+    ColumnDefinition [39-70]
+      Identifier(id) [39-41]
+      SimpleColumnSchema [56-70]
+        PathExpression [56-61]
+          Identifier(INT64) [56-61]
+        ColumnAttributeList [62-70]
+          NotNullColumnAttribute [62-70]
+    ColumnDefinition [74-105]
+      Identifier(account_id) [74-84]
+      SimpleColumnSchema [91-105]
+        PathExpression [91-96]
+          Identifier(INT64) [91-96]
+        ColumnAttributeList [97-105]
+          NotNullColumnAttribute [97-105]
+    ColumnDefinition [109-135]
+      Identifier(create_time) [109-120]
+      SimpleColumnSchema [126-135]
+        PathExpression [126-135]
+          Identifier(TIMESTAMP) [126-135]
+    ForeignKey [139-192]
+      ColumnList [151-163]
+        Identifier(account_id) [152-162]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [164-192]
+        PathExpression [175-187]
+          Identifier(sch2) [175-179]
+          Identifier(Account) [180-187]
+        ColumnList [188-192]
+          Identifier(id) [189-191]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [192-192]
+  SpannerTableOptions [195-277]
+    PrimaryKey(ENFORCED) [195-223]
+      PrimaryKeyElementList [207-223]
+        PrimaryKeyElement(ASC) [208-210]
+          Identifier(id) [208-210]
+        PrimaryKeyElement(ASC) [212-222]
+          Identifier(account_id) [212-222]
+    SpannerInterleaveClause [223-277]
+      PathExpression [248-259]
+        Identifier(sch1) [248-252]
+        Identifier(Person) [253-259]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/create-update-drop-schema#create-property-graph-named-schema
+CREATE OR REPLACE PROPERTY GRAPH sch2.FinGraph
+  NODE TABLES (
+    sch1.Person,
+    sch2.Account
+  )
+  EDGE TABLES (
+    sch1.PersonOwnAccount
+      SOURCE KEY (id) REFERENCES Person (id)
+      DESTINATION KEY (account_id) REFERENCES Account (id)
+      LABEL Owns
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [0-267]
+  PathExpression [33-46]
+    Identifier(sch2) [33-37]
+    Identifier(FinGraph) [38-46]
+  GraphElementTableList [61-100]
+    GraphElementTable [67-78]
+      PathExpression [67-78]
+        Identifier(sch1) [67-71]
+        Identifier(Person) [72-78]
+      GraphElementLabelAndPropertiesList [78-78]
+        GraphElementLabelAndProperties [78-78]
+          GraphProperties [78-78]
+    GraphElementTable [84-96]
+      PathExpression [84-96]
+        Identifier(sch2) [84-88]
+        Identifier(Account) [89-96]
+      GraphElementLabelAndPropertiesList [96-96]
+        GraphElementLabelAndProperties [96-96]
+          GraphProperties [96-96]
+  GraphElementTableList [115-267]
+    GraphElementTable [121-263]
+      PathExpression [121-142]
+        Identifier(sch1) [121-125]
+        Identifier(PersonOwnAccount) [126-142]
+      GraphNodeTableReference(SOURCE) [149-187]
+        Identifier(Person) [176-182]
+        ColumnList [160-164]
+          Identifier(id) [161-163]
+        ColumnList [183-187]
+          Identifier(id) [184-186]
+      GraphNodeTableReference(DESTINATION) [194-246]
+        Identifier(Account) [234-241]
+        ColumnList [210-222]
+          Identifier(account_id) [211-221]
+        ColumnList [242-246]
+          Identifier(id) [243-245]
+      GraphElementLabelAndPropertiesList [253-263]
+        GraphElementLabelAndProperties [253-263]
+          Identifier(Owns) [259-263]
+          GraphProperties [263-263]
diff --git a/parser/testdata/spanner/guides/graph.full-text-search-and-graph.metadata.json b/parser/testdata/spanner/guides/graph.full-text-search-and-graph.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.full-text-search-and-graph.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/graph.full-text-search-and-graph.test b/parser/testdata/spanner/guides/graph.full-text-search-and-graph.test
new file mode 100644
index 0000000..fd277f3
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.full-text-search-and-graph.test
@@ -0,0 +1,431 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/full-text-search-and-graph
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/graph/full-text-search-and-graph#create_tokens_and_search_indexes
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+ALTER TABLE Account
+ADD COLUMN nick_name_token TOKENLIST
+AS (TOKENIZE_FULLTEXT(nick_name)) STORED HIDDEN
+--
+AlterTableStatement [0-104]
+  PathExpression [12-19]
+    Identifier(Account) [12-19]
+  AlterActionList [20-104]
+    AddColumnAction [20-104]
+      ColumnDefinition [31-104]
+        Identifier(nick_name_token) [31-46]
+        SimpleColumnSchema [47-104]
+          PathExpression [47-56]
+            Identifier(TOKENLIST) [47-56]
+          GeneratedColumnInfo(generated_mode=ALWAYS, stored_mode=STORED) [57-97]
+            FunctionCall [61-89]
+              PathExpression [61-78]
+                Identifier(TOKENIZE_FULLTEXT) [61-78]
+              PathExpression [79-88]
+                Identifier(nick_name) [79-88]
+          ColumnAttributeList [98-104]
+            HiddenColumnAttribute [98-104]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/full-text-search-and-graph#create_tokens_and_search_indexes
+CREATE SEARCH INDEX AccountTextSearchIndex
+ON Account(nick_name_token) STORING (nick_name)
+--
+CreateIndexStatement(SEARCH) [0-90]
+  PathExpression [20-42]
+    Identifier(AccountTextSearchIndex) [20-42]
+  PathExpression [46-53]
+    Identifier(Account) [46-53]
+  IndexItemList [53-70]
+    OrderingExpression(ASC) [54-69]
+      PathExpression [54-69]
+        Identifier(nick_name_token) [54-69]
+  IndexStoringExpressionList [79-90]
+    PathExpression [80-89]
+      Identifier(nick_name) [80-89]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/full-text-search-and-graph#create_tokens_and_search_indexes
+ALTER TABLE AccountTransferAccount
+ADD COLUMN notes STRING(MAX)
+--
+AlterTableStatement [0-63]
+  PathExpression [12-34]
+    Identifier(AccountTransferAccount) [12-34]
+  AlterActionList [35-63]
+    AddColumnAction [35-63]
+      ColumnDefinition [46-63]
+        Identifier(notes) [46-51]
+        SimpleColumnSchema [52-63]
+          PathExpression [52-58]
+            Identifier(STRING) [52-58]
+          TypeParameterList [58-62]
+            MaxLiteral() [59-62]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/full-text-search-and-graph#create_tokens_and_search_indexes
+ALTER TABLE AccountTransferAccount
+ADD COLUMN notes_token TOKENLIST AS (TOKENIZE_FULLTEXT(notes)) STORED HIDDEN
+--
+AlterTableStatement [0-111]
+  PathExpression [12-34]
+    Identifier(AccountTransferAccount) [12-34]
+  AlterActionList [35-111]
+    AddColumnAction [35-111]
+      ColumnDefinition [46-111]
+        Identifier(notes_token) [46-57]
+        SimpleColumnSchema [58-111]
+          PathExpression [58-67]
+            Identifier(TOKENLIST) [58-67]
+          GeneratedColumnInfo(generated_mode=ALWAYS, stored_mode=STORED) [68-104]
+            FunctionCall [72-96]
+              PathExpression [72-89]
+                Identifier(TOKENIZE_FULLTEXT) [72-89]
+              PathExpression [90-95]
+                Identifier(notes) [90-95]
+          ColumnAttributeList [105-111]
+            HiddenColumnAttribute [105-111]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/full-text-search-and-graph#create_tokens_and_search_indexes
+CREATE SEARCH INDEX TransferTextSearchIndex
+ON AccountTransferAccount(notes_token) STORING (notes)
+--
+CreateIndexStatement(SEARCH) [0-98]
+  PathExpression [20-43]
+    Identifier(TransferTextSearchIndex) [20-43]
+  PathExpression [47-69]
+    Identifier(AccountTransferAccount) [47-69]
+  IndexItemList [69-82]
+    OrderingExpression(ASC) [70-81]
+      PathExpression [70-81]
+        Identifier(notes_token) [70-81]
+  IndexStoringExpressionList [91-98]
+    PathExpression [92-97]
+      Identifier(notes) [92-97]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/full-text-search-and-graph#create_tokens_and_search_indexes
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (Account, Person)
+  EDGE TABLES (
+    PersonOwnAccount
+      SOURCE KEY (id) REFERENCES Person (id)
+      DESTINATION KEY (account_id) REFERENCES Account (id)
+      LABEL Owns,
+    AccountTransferAccount
+      SOURCE KEY (id) REFERENCES Account (id)
+      DESTINATION KEY (to_id) REFERENCES Account (id)
+      LABEL Transfers
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [0-385]
+  PathExpression [33-41]
+    Identifier(FinGraph) [33-41]
+  GraphElementTableList [56-73]
+    GraphElementTable [57-64]
+      PathExpression [57-64]
+        Identifier(Account) [57-64]
+      GraphElementLabelAndPropertiesList [64-64]
+        GraphElementLabelAndProperties [64-64]
+          GraphProperties [64-64]
+    GraphElementTable [66-72]
+      PathExpression [66-72]
+        Identifier(Person) [66-72]
+      GraphElementLabelAndPropertiesList [72-72]
+        GraphElementLabelAndProperties [72-72]
+          GraphProperties [72-72]
+  GraphElementTableList [88-385]
+    GraphElementTable [94-231]
+      PathExpression [94-110]
+        Identifier(PersonOwnAccount) [94-110]
+      GraphNodeTableReference(SOURCE) [117-155]
+        Identifier(Person) [144-150]
+        ColumnList [128-132]
+          Identifier(id) [129-131]
+        ColumnList [151-155]
+          Identifier(id) [152-154]
+      GraphNodeTableReference(DESTINATION) [162-214]
+        Identifier(Account) [202-209]
+        ColumnList [178-190]
+          Identifier(account_id) [179-189]
+        ColumnList [210-214]
+          Identifier(id) [211-213]
+      GraphElementLabelAndPropertiesList [221-231]
+        GraphElementLabelAndProperties [221-231]
+          Identifier(Owns) [227-231]
+          GraphProperties [231-231]
+    GraphElementTable [237-381]
+      PathExpression [237-259]
+        Identifier(AccountTransferAccount) [237-259]
+      GraphNodeTableReference(SOURCE) [266-305]
+        Identifier(Account) [293-300]
+        ColumnList [277-281]
+          Identifier(id) [278-280]
+        ColumnList [301-305]
+          Identifier(id) [302-304]
+      GraphNodeTableReference(DESTINATION) [312-359]
+        Identifier(Account) [347-354]
+        ColumnList [328-335]
+          Identifier(to_id) [329-334]
+        ColumnList [355-359]
+          Identifier(id) [356-358]
+      GraphElementLabelAndPropertiesList [366-381]
+        GraphElementLabelAndProperties [366-381]
+          Identifier(Transfers) [372-381]
+          GraphProperties [381-381]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/full-text-search-and-graph#search_graph_node_property
+UPDATE Account SET nick_name = "Fund for vacation at the north pole" WHERE id = 7
+--
+UpdateStatement [0-81]
+  PathExpression [7-14]
+    Identifier(Account) [7-14]
+  UpdateItemList [19-68]
+    UpdateItem [19-68]
+      UpdateSetValue [19-68]
+        PathExpression [19-28]
+          Identifier(nick_name) [19-28]
+        StringLiteral [31-68]
+          StringLiteralComponent("Fund for vacation at the north pole") [31-68]
+  BinaryExpression(=) [75-81]
+    PathExpression [75-77]
+      Identifier(id) [75-77]
+    IntLiteral(7) [80-81]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/full-text-search-and-graph#search_graph_node_property
+UPDATE Account SET nick_name = "Fund -- thrill rides!" WHERE id = 16
+--
+UpdateStatement [0-68]
+  PathExpression [7-14]
+    Identifier(Account) [7-14]
+  UpdateItemList [19-54]
+    UpdateItem [19-54]
+      UpdateSetValue [19-54]
+        PathExpression [19-28]
+          Identifier(nick_name) [19-28]
+        StringLiteral [31-54]
+          StringLiteralComponent("Fund -- thrill rides!") [31-54]
+  BinaryExpression(=) [61-68]
+    PathExpression [61-63]
+      Identifier(id) [61-63]
+    IntLiteral(16) [66-68]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/full-text-search-and-graph#search_graph_node_property
+UPDATE Account SET nick_name = "Rainy day fund for the things I still want to do" WHERE id = 20
+--
+UpdateStatement [0-95]
+  PathExpression [7-14]
+    Identifier(Account) [7-14]
+  UpdateItemList [19-81]
+    UpdateItem [19-81]
+      UpdateSetValue [19-81]
+        PathExpression [19-28]
+          Identifier(nick_name) [19-28]
+        StringLiteral [31-81]
+          StringLiteralComponent("Rainy day fund for the things I still want to do") [31-81]
+  BinaryExpression(=) [88-95]
+    PathExpression [88-90]
+      Identifier(id) [88-90]
+    IntLiteral(20) [93-95]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/full-text-search-and-graph#search_graph_node_property
+GRAPH FinGraph
+MATCH (n:Account)<-[e:Transfers]-(:Account)
+WHERE SEARCH(n.nick_name_token, '"rainy day" | vacation')
+RETURN n.nick_name, e.amount AS amount_added
+ORDER BY SCORE(n.nick_name_token, '"rainy day" | vacation') DESC
+--
+QueryStatement [0-226]
+  Query [0-226]
+    GqlQuery [0-226]
+      GraphTableQuery [0-226]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-226]
+          GqlOperatorList [15-226]
+            GqlMatch [15-116]
+              GraphPattern [21-116]
+                GraphPathPattern [21-58]
+                  GraphNodePattern [21-32]
+                    GraphElementPatternFiller [22-31]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                  GraphEdgePattern [32-48]
+                    GraphElementPatternFiller [35-46]
+                      Identifier(e) [35-36]
+                      GraphLabelFilter [36-46]
+                        GraphElementLabel [37-46]
+                          Identifier(Transfers) [37-46]
+                  GraphNodePattern [48-58]
+                    GraphElementPatternFiller [49-57]
+                      GraphLabelFilter [49-57]
+                        GraphElementLabel [50-57]
+                          Identifier(Account) [50-57]
+                WhereClause [59-116]
+                  FunctionCall [65-116]
+                    PathExpression [65-71]
+                      Identifier(SEARCH) [65-71]
+                    PathExpression [72-89]
+                      Identifier(n) [72-73]
+                      Identifier(nick_name_token) [74-89]
+                    StringLiteral [91-115]
+                      StringLiteralComponent('"rainy day" | vacation') [91-115]
+            GqlReturn [117-226]
+              Select [124-161]
+                SelectList [124-161]
+                  SelectColumn [124-135]
+                    PathExpression [124-135]
+                      Identifier(n) [124-125]
+                      Identifier(nick_name) [126-135]
+                  SelectColumn [137-161]
+                    PathExpression [137-145]
+                      Identifier(e) [137-138]
+                      Identifier(amount) [139-145]
+                    Alias [146-161]
+                      Identifier(amount_added) [149-161]
+              GqlOrderByAndPage [162-226]
+                OrderBy [162-226]
+                  OrderingExpression(DESC) [171-226]
+                    FunctionCall [171-221]
+                      PathExpression [171-176]
+                        Identifier(SCORE) [171-176]
+                      PathExpression [177-194]
+                        Identifier(n) [177-178]
+                        Identifier(nick_name_token) [179-194]
+                      StringLiteral [196-220]
+                        StringLiteralComponent('"rainy day" | vacation') [196-220]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/full-text-search-and-graph#search_graph_edge_property
+UPDATE AccountTransferAccount SET notes = 'for trip fund'
+WHERE id = 16 AND to_id = 20
+--
+UpdateStatement [0-86]
+  PathExpression [7-29]
+    Identifier(AccountTransferAccount) [7-29]
+  UpdateItemList [34-57]
+    UpdateItem [34-57]
+      UpdateSetValue [34-57]
+        PathExpression [34-39]
+          Identifier(notes) [34-39]
+        StringLiteral [42-57]
+          StringLiteralComponent('for trip fund') [42-57]
+  AndExpr [64-86]
+    BinaryExpression(=) [64-71]
+      PathExpression [64-66]
+        Identifier(id) [64-66]
+      IntLiteral(16) [69-71]
+    BinaryExpression(=) [76-86]
+      PathExpression [76-81]
+        Identifier(to_id) [76-81]
+      IntLiteral(20) [84-86]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/full-text-search-and-graph#search_graph_edge_property
+UPDATE AccountTransferAccount SET notes = '<trip's very fun!>'
+WHERE id = 20 AND to_id = 7
+--
+UpdateStatement [0-100]
+  PathExpression [7-29]
+    Identifier(AccountTransferAccount) [7-29]
+  UpdateItemList [34-72]
+    UpdateItem [34-72]
+      UpdateSetValue [34-72]
+        PathExpression [34-39]
+          Identifier(notes) [34-39]
+        StringLiteral [42-72]
+          StringLiteralComponent('<trip's very fun!>') [42-72]
+  AndExpr [79-100]
+    BinaryExpression(=) [79-86]
+      PathExpression [79-81]
+        Identifier(id) [79-81]
+      IntLiteral(20) [84-86]
+    BinaryExpression(=) [91-100]
+      PathExpression [91-96]
+        Identifier(to_id) [91-96]
+      IntLiteral(7) [99-100]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/full-text-search-and-graph#search_graph_edge_property
+UPDATE AccountTransferAccount SET notes = 'book fee'
+WHERE id = 20 AND to_id = 16
+--
+UpdateStatement [0-81]
+  PathExpression [7-29]
+    Identifier(AccountTransferAccount) [7-29]
+  UpdateItemList [34-52]
+    UpdateItem [34-52]
+      UpdateSetValue [34-52]
+        PathExpression [34-39]
+          Identifier(notes) [34-39]
+        StringLiteral [42-52]
+          StringLiteralComponent('book fee') [42-52]
+  AndExpr [59-81]
+    BinaryExpression(=) [59-66]
+      PathExpression [59-61]
+        Identifier(id) [59-61]
+      IntLiteral(20) [64-66]
+    BinaryExpression(=) [71-81]
+      PathExpression [71-76]
+        Identifier(to_id) [71-76]
+      IntLiteral(16) [79-81]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/full-text-search-and-graph#search_graph_edge_property
+GRAPH FinGraph
+MATCH (a:Account)-[e:Transfers WHERE SEARCH(e.notes_token, 'trip')]->(b:Account)
+RETURN a.id AS src_id, b.id AS dst_id, e.notes
+--
+QueryStatement [0-142]
+  Query [0-142]
+    GqlQuery [0-142]
+      GraphTableQuery [0-142]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-142]
+          GqlOperatorList [15-142]
+            GqlMatch [15-95]
+              GraphPattern [21-95]
+                GraphPathPattern [21-95]
+                  GraphNodePattern [21-32]
+                    GraphElementPatternFiller [22-31]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                  GraphEdgePattern [32-84]
+                    GraphElementPatternFiller [34-81]
+                      Identifier(e) [34-35]
+                      GraphLabelFilter [35-45]
+                        GraphElementLabel [36-45]
+                          Identifier(Transfers) [36-45]
+                      WhereClause [46-81]
+                        FunctionCall [52-81]
+                          PathExpression [52-58]
+                            Identifier(SEARCH) [52-58]
+                          PathExpression [59-72]
+                            Identifier(e) [59-60]
+                            Identifier(notes_token) [61-72]
+                          StringLiteral [74-80]
+                            StringLiteralComponent('trip') [74-80]
+                  GraphNodePattern [84-95]
+                    GraphElementPatternFiller [85-94]
+                      Identifier(b) [85-86]
+                      GraphLabelFilter [86-94]
+                        GraphElementLabel [87-94]
+                          Identifier(Account) [87-94]
+            GqlReturn [96-142]
+              Select [103-142]
+                SelectList [103-142]
+                  SelectColumn [103-117]
+                    PathExpression [103-107]
+                      Identifier(a) [103-104]
+                      Identifier(id) [105-107]
+                    Alias [108-117]
+                      Identifier(src_id) [111-117]
+                  SelectColumn [119-133]
+                    PathExpression [119-123]
+                      Identifier(b) [119-120]
+                      Identifier(id) [121-123]
+                    Alias [124-133]
+                      Identifier(dst_id) [127-133]
+                  SelectColumn [135-142]
+                    PathExpression [135-142]
+                      Identifier(e) [135-136]
+                      Identifier(notes) [137-142]
diff --git a/parser/testdata/spanner/guides/graph.graph-algorithms-overview.test b/parser/testdata/spanner/guides/graph.graph-algorithms-overview.test
new file mode 100644
index 0000000..6a127b9
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.graph-algorithms-overview.test
@@ -0,0 +1,63 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/graph-algorithms-overview
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/graph/graph-algorithms-overview#gql-integration
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+EXPORT DATA OPTIONS (
+  uri = "gs://my-bucket-name/my-output.csv",
+  format = "csv"
+) AS
+GRAPH FinGraph
+CALL WeaklyConnectedComponents(node_labels => ['Account'], edge_labels => ['Transfers']) YIELD node, cluster
+RETURN node.id, cluster
+--
+ExportDataStatement [0-236]
+  OptionsList [20-85]
+    OptionsEntry [24-65]
+      Identifier(uri) [24-27]
+      StringLiteral [30-65]
+        StringLiteralComponent("gs://my-bucket-name/my-output.csv") [30-65]
+    OptionsEntry [69-83]
+      Identifier(format) [69-75]
+      StringLiteral [78-83]
+        StringLiteralComponent("csv") [78-83]
+  Query [89-236]
+    GqlQuery [89-236]
+      GraphTableQuery [89-236]
+        PathExpression [95-103]
+          Identifier(FinGraph) [95-103]
+        GqlOperatorList [104-236]
+          GqlOperatorList [104-236]
+            GqlNamedCall [104-212]
+              TVF [109-192]
+                PathExpression [109-134]
+                  Identifier(WeaklyConnectedComponents) [109-134]
+                TVFArgument [135-161]
+                  NamedArgument [135-161]
+                    Identifier(node_labels) [135-146]
+                    ArrayConstructor [150-161]
+                      StringLiteral [151-160]
+                        StringLiteralComponent('Account') [151-160]
+                TVFArgument [163-191]
+                  NamedArgument [163-191]
+                    Identifier(edge_labels) [163-174]
+                    ArrayConstructor [178-191]
+                      StringLiteral [179-190]
+                        StringLiteralComponent('Transfers') [179-190]
+              YieldItemList [193-212]
+                ExpressionWithOptAlias [199-203]
+                  Identifier(node) [199-203]
+                ExpressionWithOptAlias [205-212]
+                  Identifier(cluster) [205-212]
+            GqlReturn [213-236]
+              Select [220-236]
+                SelectList [220-236]
+                  SelectColumn [220-227]
+                    PathExpression [220-227]
+                      Identifier(node) [220-224]
+                      Identifier(id) [225-227]
+                  SelectColumn [229-236]
+                    PathExpression [229-236]
+                      Identifier(cluster) [229-236]
diff --git a/parser/testdata/spanner/guides/graph.graph-with-views-how-to.metadata.json b/parser/testdata/spanner/guides/graph.graph-with-views-how-to.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.graph-with-views-how-to.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/graph.graph-with-views-how-to.spanner.sql b/parser/testdata/spanner/guides/graph.graph-with-views-how-to.spanner.sql
new file mode 100644
index 0000000..c31fb8d
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.graph-with-views-how-to.spanner.sql
@@ -0,0 +1,19 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to#fine-grained-graph-access
+# id bfb46504a7f2: CREATE ROLE (graph.graph-with-views-how-to)
+# execute_query: INVALID_ARGUMENT: ROLE is not a supported object type [at 2:8]
+-- The role that is granted to read the view.
+CREATE ROLE engineering_data_reader;
+
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to#fine-grained-graph-access
+# id 6d9fa1b755f8: GRANT/REVOKE for fine-grained access control (graph.graph-with-views-how-to)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:46]
+GRANT SELECT ON VIEW EngineerEmployeeView TO ROLE engineering_data_reader;
diff --git a/parser/testdata/spanner/guides/graph.graph-with-views-how-to.test b/parser/testdata/spanner/guides/graph.graph-with-views-how-to.test
new file mode 100644
index 0000000..63a8ad8
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.graph-with-views-how-to.test
@@ -0,0 +1,966 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to#create-graph-views
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE PROPERTY GRAPH MyGraph
+  NODE TABLES (
+    CustomerViewTrusted KEY(id)
+  ) OPTIONS (validate_element_key_uniqueness = false)
+--
+CreatePropertyGraphStatement [0-131]
+  PathExpression [22-29]
+    Identifier(MyGraph) [22-29]
+  GraphElementTableList [44-81]
+    GraphElementTable [50-77]
+      PathExpression [50-69]
+        Identifier(CustomerViewTrusted) [50-69]
+      ColumnList [73-77]
+        Identifier(id) [74-76]
+      GraphElementLabelAndPropertiesList [77-77]
+        GraphElementLabelAndProperties [77-77]
+          GraphProperties [77-77]
+  OptionsList [90-131]
+    OptionsEntry [91-130]
+      Identifier(validate_element_key_uniqueness) [91-122]
+      BooleanLiteral(false) [125-130]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to#create-tables
+CREATE TABLE Customer (
+  customer_id INT64 NOT NULL,
+  name STRING(MAX),
+  address_continent STRING(MAX),
+  address_country STRING(MAX),
+) PRIMARY KEY(customer_id)
+--
+CreateTableStatement [0-164]
+  PathExpression [13-21]
+    Identifier(Customer) [13-21]
+  TableElementList [22-139]
+    ColumnDefinition [26-52]
+      Identifier(customer_id) [26-37]
+      SimpleColumnSchema [38-52]
+        PathExpression [38-43]
+          Identifier(INT64) [38-43]
+        ColumnAttributeList [44-52]
+          NotNullColumnAttribute [44-52]
+    ColumnDefinition [56-72]
+      Identifier(name) [56-60]
+      SimpleColumnSchema [61-72]
+        PathExpression [61-67]
+          Identifier(STRING) [61-67]
+        TypeParameterList [67-71]
+          MaxLiteral() [68-71]
+    ColumnDefinition [76-105]
+      Identifier(address_continent) [76-93]
+      SimpleColumnSchema [94-105]
+        PathExpression [94-100]
+          Identifier(STRING) [94-100]
+        TypeParameterList [100-104]
+          MaxLiteral() [101-104]
+    ColumnDefinition [109-136]
+      Identifier(address_country) [109-124]
+      SimpleColumnSchema [125-136]
+        PathExpression [125-131]
+          Identifier(STRING) [125-131]
+        TypeParameterList [131-135]
+          MaxLiteral() [132-135]
+  SpannerTableOptions [140-164]
+    PrimaryKey(ENFORCED) [140-164]
+      PrimaryKeyElementList [151-164]
+        PrimaryKeyElement(ASC) [152-163]
+          Identifier(customer_id) [152-163]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to#create-tables
+CREATE TABLE Account (
+  account_id INT64 NOT NULL,
+  customer_id INT64 NOT NULL,
+  account_type STRING(MAX),
+  balance INT64,
+  create_time TIMESTAMP,
+  address_continent STRING(MAX),
+  address_country STRING(MAX),
+  CONSTRAINT FK_CustomerId FOREIGN KEY (customer_id)
+    REFERENCES Customer (customer_id)
+) PRIMARY KEY(account_id)
+--
+CreateTableStatement [0-332]
+  PathExpression [13-20]
+    Identifier(Account) [13-20]
+  TableElementList [21-308]
+    ColumnDefinition [25-50]
+      Identifier(account_id) [25-35]
+      SimpleColumnSchema [36-50]
+        PathExpression [36-41]
+          Identifier(INT64) [36-41]
+        ColumnAttributeList [42-50]
+          NotNullColumnAttribute [42-50]
+    ColumnDefinition [54-80]
+      Identifier(customer_id) [54-65]
+      SimpleColumnSchema [66-80]
+        PathExpression [66-71]
+          Identifier(INT64) [66-71]
+        ColumnAttributeList [72-80]
+          NotNullColumnAttribute [72-80]
+    ColumnDefinition [84-108]
+      Identifier(account_type) [84-96]
+      SimpleColumnSchema [97-108]
+        PathExpression [97-103]
+          Identifier(STRING) [97-103]
+        TypeParameterList [103-107]
+          MaxLiteral() [104-107]
+    ColumnDefinition [112-125]
+      Identifier(balance) [112-119]
+      SimpleColumnSchema [120-125]
+        PathExpression [120-125]
+          Identifier(INT64) [120-125]
+    ColumnDefinition [129-150]
+      Identifier(create_time) [129-140]
+      SimpleColumnSchema [141-150]
+        PathExpression [141-150]
+          Identifier(TIMESTAMP) [141-150]
+    ColumnDefinition [154-183]
+      Identifier(address_continent) [154-171]
+      SimpleColumnSchema [172-183]
+        PathExpression [172-178]
+          Identifier(STRING) [172-178]
+        TypeParameterList [178-182]
+          MaxLiteral() [179-182]
+    ColumnDefinition [187-214]
+      Identifier(address_country) [187-202]
+      SimpleColumnSchema [203-214]
+        PathExpression [203-209]
+          Identifier(STRING) [203-209]
+        TypeParameterList [209-213]
+          MaxLiteral() [210-213]
+    ForeignKey [218-306]
+      ColumnList [255-268]
+        Identifier(customer_id) [256-267]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [273-306]
+        PathExpression [284-292]
+          Identifier(Customer) [284-292]
+        ColumnList [293-306]
+          Identifier(customer_id) [294-305]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [306-306]
+      Identifier(FK_CustomerId) [229-242]
+  SpannerTableOptions [309-332]
+    PrimaryKey(ENFORCED) [309-332]
+      PrimaryKeyElementList [320-332]
+        PrimaryKeyElement(ASC) [321-331]
+          Identifier(account_id) [321-331]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to#create-views
+-- View for 'Customer' nodes, filtered for Asia
+CREATE VIEW AsiaCustomer
+  SQL SECURITY INVOKER AS
+    SELECT customer.customer_id, customer.name
+    FROM Customer customer
+    WHERE LOWER(customer.address_continent) = "asia"
+--
+CreateViewStatement(SQL SECURITY INVOKER) [48-225]
+  PathExpression [60-72]
+    Identifier(AsiaCustomer) [60-72]
+  Query [103-225]
+    Select [103-225]
+      SelectList [110-145]
+        SelectColumn [110-130]
+          PathExpression [110-130]
+            Identifier(customer) [110-118]
+            Identifier(customer_id) [119-130]
+        SelectColumn [132-145]
+          PathExpression [132-145]
+            Identifier(customer) [132-140]
+            Identifier(name) [141-145]
+      FromClause [150-172]
+        TablePathExpression [155-172]
+          PathExpression [155-163]
+            Identifier(Customer) [155-163]
+          Alias [164-172]
+            Identifier(customer) [164-172]
+      WhereClause [177-225]
+        BinaryExpression(=) [183-225]
+          FunctionCall [183-216]
+            PathExpression [183-188]
+              Identifier(LOWER) [183-188]
+            PathExpression [189-215]
+              Identifier(customer) [189-197]
+              Identifier(address_continent) [198-215]
+          StringLiteral [219-225]
+            StringLiteralComponent("asia") [219-225]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to#create-views
+-- View for 'Account' nodes, filtered for Asia.
+CREATE VIEW AsiaBankAccount
+  SQL SECURITY INVOKER AS
+    SELECT account.account_id, account.balance, account.account_type, account.create_time
+    FROM Account account
+    WHERE LOWER(account.address_continent) = "asia"
+--
+CreateViewStatement(SQL SECURITY INVOKER) [48-268]
+  PathExpression [60-75]
+    Identifier(AsiaBankAccount) [60-75]
+  Query [106-268]
+    Select [106-268]
+      SelectList [113-191]
+        SelectColumn [113-131]
+          PathExpression [113-131]
+            Identifier(account) [113-120]
+            Identifier(account_id) [121-131]
+        SelectColumn [133-148]
+          PathExpression [133-148]
+            Identifier(account) [133-140]
+            Identifier(balance) [141-148]
+        SelectColumn [150-170]
+          PathExpression [150-170]
+            Identifier(account) [150-157]
+            Identifier(account_type) [158-170]
+        SelectColumn [172-191]
+          PathExpression [172-191]
+            Identifier(account) [172-179]
+            Identifier(create_time) [180-191]
+      FromClause [196-216]
+        TablePathExpression [201-216]
+          PathExpression [201-208]
+            Identifier(Account) [201-208]
+          Alias [209-216]
+            Identifier(account) [209-216]
+      WhereClause [221-268]
+        BinaryExpression(=) [227-268]
+          FunctionCall [227-259]
+            PathExpression [227-232]
+              Identifier(LOWER) [227-232]
+            PathExpression [233-258]
+              Identifier(account) [233-240]
+              Identifier(address_continent) [241-258]
+          StringLiteral [262-268]
+            StringLiteralComponent("asia") [262-268]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to#create-views
+-- View for 'Owns' edges, connecting customers to accounts in Asia.
+CREATE VIEW AsiaAccountsOwnership
+  SQL SECURITY INVOKER AS
+    SELECT account.customer_id, account.account_id
+    FROM Account account
+    WHERE LOWER(account.address_continent) = "asia"
+--
+CreateViewStatement(SQL SECURITY INVOKER) [68-255]
+  PathExpression [80-101]
+    Identifier(AsiaAccountsOwnership) [80-101]
+  Query [132-255]
+    Select [132-255]
+      SelectList [139-178]
+        SelectColumn [139-158]
+          PathExpression [139-158]
+            Identifier(account) [139-146]
+            Identifier(customer_id) [147-158]
+        SelectColumn [160-178]
+          PathExpression [160-178]
+            Identifier(account) [160-167]
+            Identifier(account_id) [168-178]
+      FromClause [183-203]
+        TablePathExpression [188-203]
+          PathExpression [188-195]
+            Identifier(Account) [188-195]
+          Alias [196-203]
+            Identifier(account) [196-203]
+      WhereClause [208-255]
+        BinaryExpression(=) [214-255]
+          FunctionCall [214-246]
+            PathExpression [214-219]
+              Identifier(LOWER) [214-219]
+            PathExpression [220-245]
+              Identifier(account) [220-227]
+              Identifier(address_continent) [228-245]
+          StringLiteral [249-255]
+            StringLiteralComponent("asia") [249-255]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to#create-property-graph
+CREATE PROPERTY GRAPH AsiaFinGraph
+  NODE TABLES (
+    AsiaCustomer AS Customer KEY(customer_id),
+    AsiaBankAccount AS Account KEY(account_id)
+  )
+  EDGE TABLES (
+    AsiaAccountsOwnership AS Owns
+      KEY(customer_id, account_id)
+      SOURCE KEY (customer_id) REFERENCES Customer (customer_id)
+      DESTINATION KEY (account_id) REFERENCES Account (account_id)
+  )
+--
+CreatePropertyGraphStatement [0-369]
+  PathExpression [22-34]
+    Identifier(AsiaFinGraph) [22-34]
+  GraphElementTableList [49-148]
+    GraphElementTable [55-96]
+      PathExpression [55-67]
+        Identifier(AsiaCustomer) [55-67]
+      Alias [68-79]
+        Identifier(Customer) [71-79]
+      ColumnList [83-96]
+        Identifier(customer_id) [84-95]
+      GraphElementLabelAndPropertiesList [96-96]
+        GraphElementLabelAndProperties [96-96]
+          GraphProperties [96-96]
+    GraphElementTable [102-144]
+      PathExpression [102-117]
+        Identifier(AsiaBankAccount) [102-117]
+      Alias [118-128]
+        Identifier(Account) [121-128]
+      ColumnList [132-144]
+        Identifier(account_id) [133-143]
+      GraphElementLabelAndPropertiesList [144-144]
+        GraphElementLabelAndProperties [144-144]
+          GraphProperties [144-144]
+  GraphElementTableList [163-369]
+    GraphElementTable [169-365]
+      PathExpression [169-190]
+        Identifier(AsiaAccountsOwnership) [169-190]
+      Alias [191-198]
+        Identifier(Owns) [194-198]
+      ColumnList [208-233]
+        Identifier(customer_id) [209-220]
+        Identifier(account_id) [222-232]
+      GraphNodeTableReference(SOURCE) [240-298]
+        Identifier(Customer) [276-284]
+        ColumnList [251-264]
+          Identifier(customer_id) [252-263]
+        ColumnList [285-298]
+          Identifier(customer_id) [286-297]
+      GraphNodeTableReference(DESTINATION) [305-365]
+        Identifier(Account) [345-352]
+        ColumnList [321-333]
+          Identifier(account_id) [322-332]
+        ColumnList [353-365]
+          Identifier(account_id) [354-364]
+      GraphElementLabelAndPropertiesList [365-365]
+        GraphElementLabelAndProperties [365-365]
+          GraphProperties [365-365]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to#fine-grained-graph-access
+-- The table containing all employee data.
+CREATE TABLE Employee (
+  id INT64 NOT NULL,
+  cost_center STRING(MAX),
+  job_title STRING(MAX),
+  office STRING(MAX)
+) PRIMARY KEY (id)
+--
+CreateTableStatement [43-179]
+  PathExpression [56-64]
+    Identifier(Employee) [56-64]
+  TableElementList [65-162]
+    ColumnDefinition [69-86]
+      Identifier(id) [69-71]
+      SimpleColumnSchema [72-86]
+        PathExpression [72-77]
+          Identifier(INT64) [72-77]
+        ColumnAttributeList [78-86]
+          NotNullColumnAttribute [78-86]
+    ColumnDefinition [90-113]
+      Identifier(cost_center) [90-101]
+      SimpleColumnSchema [102-113]
+        PathExpression [102-108]
+          Identifier(STRING) [102-108]
+        TypeParameterList [108-112]
+          MaxLiteral() [109-112]
+    ColumnDefinition [117-138]
+      Identifier(job_title) [117-126]
+      SimpleColumnSchema [127-138]
+        PathExpression [127-133]
+          Identifier(STRING) [127-133]
+        TypeParameterList [133-137]
+          MaxLiteral() [134-137]
+    ColumnDefinition [142-160]
+      Identifier(office) [142-148]
+      SimpleColumnSchema [149-160]
+        PathExpression [149-155]
+          Identifier(STRING) [149-155]
+        TypeParameterList [155-159]
+          MaxLiteral() [156-159]
+  SpannerTableOptions [163-179]
+    PrimaryKey(ENFORCED) [163-179]
+      PrimaryKeyElementList [175-179]
+        PrimaryKeyElement(ASC) [176-178]
+          Identifier(id) [176-178]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to#fine-grained-graph-access
+-- The definer's rights view that filters for engineering employees.
+CREATE VIEW EngineerEmployeeView SQL SECURITY DEFINER AS
+  SELECT e.id, e.cost_center, e.job_title, e.office
+  FROM Employee e
+  WHERE LOWER(e.cost_center) = "engineering"
+--
+CreateViewStatement(SQL SECURITY DEFINER) [69-240]
+  PathExpression [81-101]
+    Identifier(EngineerEmployeeView) [81-101]
+  Query [128-240]
+    Select [128-240]
+      SelectList [135-177]
+        SelectColumn [135-139]
+          PathExpression [135-139]
+            Identifier(e) [135-136]
+            Identifier(id) [137-139]
+        SelectColumn [141-154]
+          PathExpression [141-154]
+            Identifier(e) [141-142]
+            Identifier(cost_center) [143-154]
+        SelectColumn [156-167]
+          PathExpression [156-167]
+            Identifier(e) [156-157]
+            Identifier(job_title) [158-167]
+        SelectColumn [169-177]
+          PathExpression [169-177]
+            Identifier(e) [169-170]
+            Identifier(office) [171-177]
+      FromClause [180-195]
+        TablePathExpression [185-195]
+          PathExpression [185-193]
+            Identifier(Employee) [185-193]
+          Alias [194-195]
+            Identifier(e) [194-195]
+      WhereClause [198-240]
+        BinaryExpression(=) [204-240]
+          FunctionCall [204-224]
+            PathExpression [204-209]
+              Identifier(LOWER) [204-209]
+            PathExpression [210-223]
+              Identifier(e) [210-211]
+              Identifier(cost_center) [212-223]
+          StringLiteral [227-240]
+            StringLiteralComponent("engineering") [227-240]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to#fine-grained-graph-access
+-- The graph that uses definer's rights view.
+CREATE PROPERTY GRAPH EngineeringGraph
+  NODE TABLES (
+    EngineerEmployeeView KEY(id)
+  )
+--
+CreatePropertyGraphStatement [46-137]
+  PathExpression [68-84]
+    Identifier(EngineeringGraph) [68-84]
+  GraphElementTableList [99-137]
+    GraphElementTable [105-133]
+      PathExpression [105-125]
+        Identifier(EngineerEmployeeView) [105-125]
+      ColumnList [129-133]
+        Identifier(id) [130-132]
+      GraphElementLabelAndPropertiesList [133-133]
+        GraphElementLabelAndProperties [133-133]
+          GraphProperties [133-133]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to#model-derived-graph
+-- Parts table with an ARRAY of dependent parts.
+CREATE TABLE Parts (
+  part_id INT64 NOT NULL,
+  dependent_parts ARRAY
+) PRIMARY KEY (part_id)
+--
+CreateTableStatement [49-150]
+  PathExpression [62-67]
+    Identifier(Parts) [62-67]
+  TableElementList [68-128]
+    ColumnDefinition [72-94]
+      Identifier(part_id) [72-79]
+      SimpleColumnSchema [80-94]
+        PathExpression [80-85]
+          Identifier(INT64) [80-85]
+        ColumnAttributeList [86-94]
+          NotNullColumnAttribute [86-94]
+    ColumnDefinition [98-126]
+      Identifier(dependent_parts) [98-113]
+      ArrayColumnSchema [114-126]
+        SimpleColumnSchema [120-125]
+          PathExpression [120-125]
+            Identifier(INT64) [120-125]
+  SpannerTableOptions [129-150]
+    PrimaryKey(ENFORCED) [129-150]
+      PrimaryKeyElementList [141-150]
+        PrimaryKeyElement(ASC) [142-149]
+          Identifier(part_id) [142-149]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to#model-derived-graph
+-- A view that unnests the dependent_parts array.
+-- GROUP BY ensures uniqueness for the graph element KEY.
+CREATE VIEW PartDependsOnPart SQL SECURITY INVOKER AS
+  SELECT p.part_id, dependent_part_id
+  FROM Parts AS p,
+    UNNEST(p.dependent_parts) AS dependent_part_id
+  GROUP BY p.part_id, dependent_part_id
+--
+CreateViewStatement(SQL SECURITY INVOKER) [108-309]
+  PathExpression [120-137]
+    Identifier(PartDependsOnPart) [120-137]
+  Query [164-309]
+    Select [164-309]
+      SelectList [171-199]
+        SelectColumn [171-180]
+          PathExpression [171-180]
+            Identifier(p) [171-172]
+            Identifier(part_id) [173-180]
+        SelectColumn [182-199]
+          PathExpression [182-199]
+            Identifier(dependent_part_id) [182-199]
+      FromClause [202-269]
+        Join(COMMA) [207-269]
+          TablePathExpression [207-217]
+            PathExpression [207-212]
+              Identifier(Parts) [207-212]
+            Alias [213-217]
+              Identifier(p) [216-217]
+          Location [217-218]
+          TablePathExpression [223-269]
+            UnnestExpression [223-248]
+              ExpressionWithOptAlias [230-247]
+                PathExpression [230-247]
+                  Identifier(p) [230-231]
+                  Identifier(dependent_parts) [232-247]
+            Alias [249-269]
+              Identifier(dependent_part_id) [252-269]
+      GroupBy [272-309]
+        GroupingItem [281-290]
+          PathExpression [281-290]
+            Identifier(p) [281-282]
+            Identifier(part_id) [283-290]
+        GroupingItem [292-309]
+          PathExpression [292-309]
+            Identifier(dependent_part_id) [292-309]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to#model-derived-graph
+-- Graph modeling the part dependency relationship.
+CREATE PROPERTY GRAPH SupplyChainGraph
+  NODE TABLES (
+    Parts
+  )
+  EDGE TABLES (
+    PartDependsOnPart KEY (part_id, dependent_part_id)
+      SOURCE KEY (part_id) REFERENCES Parts(part_id)
+      DESTINATION KEY (dependent_part_id) REFERENCES Parts(part_id)
+  )
+--
+CreatePropertyGraphStatement [52-316]
+  PathExpression [74-90]
+    Identifier(SupplyChainGraph) [74-90]
+  GraphElementTableList [105-120]
+    GraphElementTable [111-116]
+      PathExpression [111-116]
+        Identifier(Parts) [111-116]
+      GraphElementLabelAndPropertiesList [116-116]
+        GraphElementLabelAndProperties [116-116]
+          GraphProperties [116-116]
+  GraphElementTableList [135-316]
+    GraphElementTable [141-312]
+      PathExpression [141-158]
+        Identifier(PartDependsOnPart) [141-158]
+      ColumnList [163-191]
+        Identifier(part_id) [164-171]
+        Identifier(dependent_part_id) [173-190]
+      GraphNodeTableReference(SOURCE) [198-244]
+        Identifier(Parts) [230-235]
+        ColumnList [209-218]
+          Identifier(part_id) [210-217]
+        ColumnList [235-244]
+          Identifier(part_id) [236-243]
+      GraphNodeTableReference(DESTINATION) [251-312]
+        Identifier(Parts) [298-303]
+        ColumnList [267-286]
+          Identifier(dependent_part_id) [268-285]
+        ColumnList [303-312]
+          Identifier(part_id) [304-311]
+      GraphElementLabelAndPropertiesList [312-312]
+        GraphElementLabelAndProperties [312-312]
+          GraphProperties [312-312]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to#schemaless-transition
+-- 1. Create the canonical tables for a schemaless model.
+CREATE TABLE GraphNode (
+  id INT64 NOT NULL,
+  label STRING(MAX) NOT NULL,
+  properties JSON
+) PRIMARY KEY (id)
+--
+CreateTableStatement [58-170]
+  PathExpression [71-80]
+    Identifier(GraphNode) [71-80]
+  TableElementList [81-153]
+    ColumnDefinition [85-102]
+      Identifier(id) [85-87]
+      SimpleColumnSchema [88-102]
+        PathExpression [88-93]
+          Identifier(INT64) [88-93]
+        ColumnAttributeList [94-102]
+          NotNullColumnAttribute [94-102]
+    ColumnDefinition [106-132]
+      Identifier(label) [106-111]
+      SimpleColumnSchema [112-132]
+        PathExpression [112-118]
+          Identifier(STRING) [112-118]
+        TypeParameterList [118-122]
+          MaxLiteral() [119-122]
+        ColumnAttributeList [124-132]
+          NotNullColumnAttribute [124-132]
+    ColumnDefinition [136-151]
+      Identifier(properties) [136-146]
+      SimpleColumnSchema [147-151]
+        PathExpression [147-151]
+          Identifier(JSON) [147-151]
+  SpannerTableOptions [154-170]
+    PrimaryKey(ENFORCED) [154-170]
+      PrimaryKeyElementList [166-170]
+        PrimaryKeyElement(ASC) [167-169]
+          Identifier(id) [167-169]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to#schemaless-transition
+CREATE TABLE GraphEdge (
+  id INT64 NOT NULL,
+  dest_id INT64 NOT NULL,
+  edge_id INT64 NOT NULL,
+  label STRING(MAX) NOT NULL,
+  properties JSON
+) PRIMARY KEY (id, dest_id, edge_id),
+  INTERLEAVE IN PARENT GraphNode
+--
+CreateTableStatement [0-216]
+  PathExpression [13-22]
+    Identifier(GraphEdge) [13-22]
+  TableElementList [23-147]
+    ColumnDefinition [27-44]
+      Identifier(id) [27-29]
+      SimpleColumnSchema [30-44]
+        PathExpression [30-35]
+          Identifier(INT64) [30-35]
+        ColumnAttributeList [36-44]
+          NotNullColumnAttribute [36-44]
+    ColumnDefinition [48-70]
+      Identifier(dest_id) [48-55]
+      SimpleColumnSchema [56-70]
+        PathExpression [56-61]
+          Identifier(INT64) [56-61]
+        ColumnAttributeList [62-70]
+          NotNullColumnAttribute [62-70]
+    ColumnDefinition [74-96]
+      Identifier(edge_id) [74-81]
+      SimpleColumnSchema [82-96]
+        PathExpression [82-87]
+          Identifier(INT64) [82-87]
+        ColumnAttributeList [88-96]
+          NotNullColumnAttribute [88-96]
+    ColumnDefinition [100-126]
+      Identifier(label) [100-105]
+      SimpleColumnSchema [106-126]
+        PathExpression [106-112]
+          Identifier(STRING) [106-112]
+        TypeParameterList [112-116]
+          MaxLiteral() [113-116]
+        ColumnAttributeList [118-126]
+          NotNullColumnAttribute [118-126]
+    ColumnDefinition [130-145]
+      Identifier(properties) [130-140]
+      SimpleColumnSchema [141-145]
+        PathExpression [141-145]
+          Identifier(JSON) [141-145]
+  SpannerTableOptions [148-216]
+    PrimaryKey(ENFORCED) [148-182]
+      PrimaryKeyElementList [160-182]
+        PrimaryKeyElement(ASC) [161-163]
+          Identifier(id) [161-163]
+        PrimaryKeyElement(ASC) [165-172]
+          Identifier(dest_id) [165-172]
+        PrimaryKeyElement(ASC) [174-181]
+          Identifier(edge_id) [174-181]
+    SpannerInterleaveClause [182-216]
+      PathExpression [207-216]
+        Identifier(GraphNode) [207-216]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to#schemaless-transition
+-- 2. Define a schemaless graph.
+CREATE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    GraphNode
+      DYNAMIC LABEL (label)
+      DYNAMIC PROPERTIES (properties)
+  )
+  EDGE TABLES (
+    GraphEdge
+      SOURCE KEY (id) REFERENCES GraphNode(id)
+      DESTINATION KEY (dest_id) REFERENCES GraphNode(id)
+      DYNAMIC LABEL (label)
+      DYNAMIC PROPERTIES (properties)
+  )
+--
+CreatePropertyGraphStatement [33-367]
+  PathExpression [55-63]
+    Identifier(FinGraph) [55-63]
+  GraphElementTableList [78-163]
+    GraphElementTable [84-159]
+      PathExpression [84-93]
+        Identifier(GraphNode) [84-93]
+      GraphElementLabelAndPropertiesList [93-93]
+        GraphElementLabelAndProperties [93-93]
+          GraphProperties [93-93]
+      GraphDynamicLabel [115-120]
+        PathExpression [115-120]
+          Identifier(label) [115-120]
+      GraphDynamicProperties [148-158]
+        PathExpression [148-158]
+          Identifier(properties) [148-158]
+  GraphElementTableList [178-367]
+    GraphElementTable [184-363]
+      PathExpression [184-193]
+        Identifier(GraphEdge) [184-193]
+      GraphNodeTableReference(SOURCE) [200-240]
+        Identifier(GraphNode) [227-236]
+        ColumnList [211-215]
+          Identifier(id) [212-214]
+        ColumnList [236-240]
+          Identifier(id) [237-239]
+      GraphNodeTableReference(DESTINATION) [247-297]
+        Identifier(GraphNode) [284-293]
+        ColumnList [263-272]
+          Identifier(dest_id) [264-271]
+        ColumnList [293-297]
+          Identifier(id) [294-296]
+      GraphElementLabelAndPropertiesList [297-297]
+        GraphElementLabelAndProperties [297-297]
+          GraphProperties [297-297]
+      GraphDynamicLabel [319-324]
+        PathExpression [319-324]
+          Identifier(label) [319-324]
+      GraphDynamicProperties [352-362]
+        PathExpression [352-362]
+          Identifier(properties) [352-362]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to#schemaless-transition
+-- 3. Define typed views that extract and formalize the data.
+--    Convert JSON fields to primitive types (for example, INT64, STRING) to
+--    ensure type safety.
+CREATE VIEW Person SQL SECURITY INVOKER AS
+  SELECT n.id, STRING(n.properties.name) AS name, INT64(n.properties.age) AS age
+  FROM GraphNode n WHERE n.label = "person"
+--
+CreateViewStatement(SQL SECURITY INVOKER) [165-332]
+  PathExpression [177-183]
+    Identifier(Person) [177-183]
+  Query [210-332]
+    Select [210-332]
+      SelectList [217-288]
+        SelectColumn [217-221]
+          PathExpression [217-221]
+            Identifier(n) [217-218]
+            Identifier(id) [219-221]
+        SelectColumn [223-256]
+          FunctionCall [223-248]
+            PathExpression [223-229]
+              Identifier(STRING) [223-229]
+            PathExpression [230-247]
+              Identifier(n) [230-231]
+              Identifier(properties) [232-242]
+              Identifier(name) [243-247]
+          Alias [249-256]
+            Identifier(name) [252-256]
+        SelectColumn [258-288]
+          FunctionCall [258-281]
+            PathExpression [258-263]
+              Identifier(INT64) [258-263]
+            PathExpression [264-280]
+              Identifier(n) [264-265]
+              Identifier(properties) [266-276]
+              Identifier(age) [277-280]
+          Alias [282-288]
+            Identifier(age) [285-288]
+      FromClause [291-307]
+        TablePathExpression [296-307]
+          PathExpression [296-305]
+            Identifier(GraphNode) [296-305]
+          Alias [306-307]
+            Identifier(n) [306-307]
+      WhereClause [308-332]
+        BinaryExpression(=) [314-332]
+          PathExpression [314-321]
+            Identifier(n) [314-315]
+            Identifier(label) [316-321]
+          StringLiteral [324-332]
+            StringLiteralComponent("person") [324-332]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to#schemaless-transition
+CREATE VIEW Company SQL SECURITY INVOKER AS
+  SELECT n.id, STRING(n.properties.name) AS company_name, BOOL(n.properties.is_public) AS is_public
+  FROM GraphNode n WHERE n.label = "company"
+--
+CreateViewStatement(SQL SECURITY INVOKER) [0-188]
+  PathExpression [12-19]
+    Identifier(Company) [12-19]
+  Query [46-188]
+    Select [46-188]
+      SelectList [53-143]
+        SelectColumn [53-57]
+          PathExpression [53-57]
+            Identifier(n) [53-54]
+            Identifier(id) [55-57]
+        SelectColumn [59-100]
+          FunctionCall [59-84]
+            PathExpression [59-65]
+              Identifier(STRING) [59-65]
+            PathExpression [66-83]
+              Identifier(n) [66-67]
+              Identifier(properties) [68-78]
+              Identifier(name) [79-83]
+          Alias [85-100]
+            Identifier(company_name) [88-100]
+        SelectColumn [102-143]
+          FunctionCall [102-130]
+            PathExpression [102-106]
+              Identifier(BOOL) [102-106]
+            PathExpression [107-129]
+              Identifier(n) [107-108]
+              Identifier(properties) [109-119]
+              Identifier(is_public) [120-129]
+          Alias [131-143]
+            Identifier(is_public) [134-143]
+      FromClause [146-162]
+        TablePathExpression [151-162]
+          PathExpression [151-160]
+            Identifier(GraphNode) [151-160]
+          Alias [161-162]
+            Identifier(n) [161-162]
+      WhereClause [163-188]
+        BinaryExpression(=) [169-188]
+          PathExpression [169-176]
+            Identifier(n) [169-170]
+            Identifier(label) [171-176]
+          StringLiteral [179-188]
+            StringLiteralComponent("company") [179-188]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to#schemaless-transition
+CREATE VIEW WorksFor SQL SECURITY INVOKER AS
+  SELECT e.id AS person_id, e.dest_id AS company_id, e.edge_id AS edge_id, STRING(e.properties.since) AS since
+  FROM GraphEdge e
+  WHERE e.label = "worksfor"
+--
+CreateViewStatement(SQL SECURITY INVOKER) [0-203]
+  PathExpression [12-20]
+    Identifier(WorksFor) [12-20]
+  Query [47-203]
+    Select [47-203]
+      SelectList [54-155]
+        SelectColumn [54-71]
+          PathExpression [54-58]
+            Identifier(e) [54-55]
+            Identifier(id) [56-58]
+          Alias [59-71]
+            Identifier(person_id) [62-71]
+        SelectColumn [73-96]
+          PathExpression [73-82]
+            Identifier(e) [73-74]
+            Identifier(dest_id) [75-82]
+          Alias [83-96]
+            Identifier(company_id) [86-96]
+        SelectColumn [98-118]
+          PathExpression [98-107]
+            Identifier(e) [98-99]
+            Identifier(edge_id) [100-107]
+          Alias [108-118]
+            Identifier(edge_id) [111-118]
+        SelectColumn [120-155]
+          FunctionCall [120-146]
+            PathExpression [120-126]
+              Identifier(STRING) [120-126]
+            PathExpression [127-145]
+              Identifier(e) [127-128]
+              Identifier(properties) [129-139]
+              Identifier(since) [140-145]
+          Alias [147-155]
+            Identifier(since) [150-155]
+      FromClause [158-174]
+        TablePathExpression [163-174]
+          PathExpression [163-172]
+            Identifier(GraphEdge) [163-172]
+          Alias [173-174]
+            Identifier(e) [173-174]
+      WhereClause [177-203]
+        BinaryExpression(=) [183-203]
+          PathExpression [183-190]
+            Identifier(e) [183-184]
+            Identifier(label) [185-190]
+          StringLiteral [193-203]
+            StringLiteralComponent("worksfor") [193-203]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-how-to#schemaless-transition
+-- 4. Create the final, formalized graph from the typed views.
+CREATE PROPERTY GRAPH typed_formalized_graph
+  NODE TABLES (
+    Person KEY(id)
+      PROPERTIES (name, age),
+    Company KEY(id)
+      PROPERTIES (company_name, is_public)
+  )
+  EDGE TABLES(
+    WorksFor KEY(person_id, company_id, edge_id)
+      SOURCE KEY (person_id) REFERENCES Person(id)
+      DESTINATION KEY (company_id) REFERENCES Company(id)
+      PROPERTIES (since)
+  )
+--
+CreatePropertyGraphStatement [63-441]
+  PathExpression [85-107]
+    Identifier(typed_formalized_graph) [85-107]
+  GraphElementTableList [122-239]
+    GraphElementTable [128-171]
+      PathExpression [128-134]
+        Identifier(Person) [128-134]
+      ColumnList [138-142]
+        Identifier(id) [139-141]
+      GraphElementLabelAndPropertiesList [149-171]
+        GraphElementLabelAndProperties [149-171]
+          GraphProperties [149-171]
+            GraphDerivedPropertyList [161-170]
+              GraphDerivedProperty [161-165]
+                PathExpression [161-165]
+                  Identifier(name) [161-165]
+              GraphDerivedProperty [167-170]
+                PathExpression [167-170]
+                  Identifier(age) [167-170]
+    GraphElementTable [177-235]
+      PathExpression [177-184]
+        Identifier(Company) [177-184]
+      ColumnList [188-192]
+        Identifier(id) [189-191]
+      GraphElementLabelAndPropertiesList [199-235]
+        GraphElementLabelAndProperties [199-235]
+          GraphProperties [199-235]
+            GraphDerivedPropertyList [211-234]
+              GraphDerivedProperty [211-223]
+                PathExpression [211-223]
+                  Identifier(company_name) [211-223]
+              GraphDerivedProperty [225-234]
+                PathExpression [225-234]
+                  Identifier(is_public) [225-234]
+  GraphElementTableList [253-441]
+    GraphElementTable [259-437]
+      PathExpression [259-267]
+        Identifier(WorksFor) [259-267]
+      ColumnList [271-303]
+        Identifier(person_id) [272-281]
+        Identifier(company_id) [283-293]
+        Identifier(edge_id) [295-302]
+      GraphNodeTableReference(SOURCE) [310-354]
+        Identifier(Person) [344-350]
+        ColumnList [321-332]
+          Identifier(person_id) [322-331]
+        ColumnList [350-354]
+          Identifier(id) [351-353]
+      GraphNodeTableReference(DESTINATION) [361-412]
+        Identifier(Company) [401-408]
+        ColumnList [377-389]
+          Identifier(company_id) [378-388]
+        ColumnList [408-412]
+          Identifier(id) [409-411]
+      GraphElementLabelAndPropertiesList [419-437]
+        GraphElementLabelAndProperties [419-437]
+          GraphProperties [419-437]
+            GraphDerivedPropertyList [431-436]
+              GraphDerivedProperty [431-436]
+                PathExpression [431-436]
+                  Identifier(since) [431-436]
diff --git a/parser/testdata/spanner/guides/graph.graph-with-views-overview.metadata.json b/parser/testdata/spanner/guides/graph.graph-with-views-overview.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.graph-with-views-overview.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/graph.graph-with-views-overview.test b/parser/testdata/spanner/guides/graph.graph-with-views-overview.test
new file mode 100644
index 0000000..3d4edf8
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.graph-with-views-overview.test
@@ -0,0 +1,461 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-overview
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-overview#strict-key-validation
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+-- Table has PRIMARY KEY(account_id).
+CREATE TABLE Account (
+  account_id INT64 NOT NULL,
+  customer_id INT64 NOT NULL,
+  account_type STRING(MAX),
+  balance INT64
+) PRIMARY KEY(account_id)
+--
+CreateTableStatement [38-189]
+  PathExpression [51-58]
+    Identifier(Account) [51-58]
+  TableElementList [59-165]
+    ColumnDefinition [63-88]
+      Identifier(account_id) [63-73]
+      SimpleColumnSchema [74-88]
+        PathExpression [74-79]
+          Identifier(INT64) [74-79]
+        ColumnAttributeList [80-88]
+          NotNullColumnAttribute [80-88]
+    ColumnDefinition [92-118]
+      Identifier(customer_id) [92-103]
+      SimpleColumnSchema [104-118]
+        PathExpression [104-109]
+          Identifier(INT64) [104-109]
+        ColumnAttributeList [110-118]
+          NotNullColumnAttribute [110-118]
+    ColumnDefinition [122-146]
+      Identifier(account_type) [122-134]
+      SimpleColumnSchema [135-146]
+        PathExpression [135-141]
+          Identifier(STRING) [135-141]
+        TypeParameterList [141-145]
+          MaxLiteral() [142-145]
+    ColumnDefinition [150-163]
+      Identifier(balance) [150-157]
+      SimpleColumnSchema [158-163]
+        PathExpression [158-163]
+          Identifier(INT64) [158-163]
+  SpannerTableOptions [166-189]
+    PrimaryKey(ENFORCED) [166-189]
+      PrimaryKeyElementList [177-189]
+        PrimaryKeyElement(ASC) [178-188]
+          Identifier(account_id) [178-188]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-overview#strict-key-validation
+-- Pattern 1: View uses the primary key from a single table.
+CREATE VIEW SavingAccount
+  SQL SECURITY INVOKER AS
+    SELECT accnt.account_id, accnt.customer_id, accnt.balance
+    FROM Account accnt
+    WHERE accnt.account_type = 'saving'
+--
+CreateViewStatement(SQL SECURITY INVOKER) [61-237]
+  PathExpression [73-86]
+    Identifier(SavingAccount) [73-86]
+  Query [117-237]
+    Select [117-237]
+      SelectList [124-174]
+        SelectColumn [124-140]
+          PathExpression [124-140]
+            Identifier(accnt) [124-129]
+            Identifier(account_id) [130-140]
+        SelectColumn [142-159]
+          PathExpression [142-159]
+            Identifier(accnt) [142-147]
+            Identifier(customer_id) [148-159]
+        SelectColumn [161-174]
+          PathExpression [161-174]
+            Identifier(accnt) [161-166]
+            Identifier(balance) [167-174]
+      FromClause [179-197]
+        TablePathExpression [184-197]
+          PathExpression [184-191]
+            Identifier(Account) [184-191]
+          Alias [192-197]
+            Identifier(accnt) [192-197]
+      WhereClause [202-237]
+        BinaryExpression(=) [208-237]
+          PathExpression [208-226]
+            Identifier(accnt) [208-213]
+            Identifier(account_type) [214-226]
+          StringLiteral [229-237]
+            StringLiteralComponent('saving') [229-237]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-overview#strict-key-validation
+CREATE PROPERTY GRAPH SavingAccountGraph
+  NODE TABLES (
+    -- The element KEY(account_id) matches the table's primary key.
+    SavingAccount KEY(account_id)
+  )
+--
+CreatePropertyGraphStatement [0-162]
+  PathExpression [22-40]
+    Identifier(SavingAccountGraph) [22-40]
+  GraphElementTableList [55-162]
+    GraphElementTable [129-158]
+      PathExpression [129-142]
+        Identifier(SavingAccount) [129-142]
+      ColumnList [146-158]
+        Identifier(account_id) [147-157]
+      GraphElementLabelAndPropertiesList [158-158]
+        GraphElementLabelAndProperties [158-158]
+          GraphProperties [158-158]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-overview#strict-key-validation
+CREATE TABLE Customer (
+  customer_id INT64,
+  name STRING(MAX)
+) PRIMARY KEY (customer_id)
+--
+CreateTableStatement [0-91]
+  PathExpression [13-21]
+    Identifier(Customer) [13-21]
+  TableElementList [22-65]
+    ColumnDefinition [26-43]
+      Identifier(customer_id) [26-37]
+      SimpleColumnSchema [38-43]
+        PathExpression [38-43]
+          Identifier(INT64) [38-43]
+    ColumnDefinition [47-63]
+      Identifier(name) [47-51]
+      SimpleColumnSchema [52-63]
+        PathExpression [52-58]
+          Identifier(STRING) [52-58]
+        TypeParameterList [58-62]
+          MaxLiteral() [59-62]
+  SpannerTableOptions [66-91]
+    PrimaryKey(ENFORCED) [66-91]
+      PrimaryKeyElementList [78-91]
+        PrimaryKeyElement(ASC) [79-90]
+          Identifier(customer_id) [79-90]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-overview#strict-key-validation
+CREATE TABLE SaleOrder (
+  order_id INT64,
+  customer_id INT64,
+  amount INT64
+) PRIMARY KEY (order_id)
+--
+CreateTableStatement [0-103]
+  PathExpression [13-22]
+    Identifier(SaleOrder) [13-22]
+  TableElementList [23-80]
+    ColumnDefinition [27-41]
+      Identifier(order_id) [27-35]
+      SimpleColumnSchema [36-41]
+        PathExpression [36-41]
+          Identifier(INT64) [36-41]
+    ColumnDefinition [45-62]
+      Identifier(customer_id) [45-56]
+      SimpleColumnSchema [57-62]
+        PathExpression [57-62]
+          Identifier(INT64) [57-62]
+    ColumnDefinition [66-78]
+      Identifier(amount) [66-72]
+      SimpleColumnSchema [73-78]
+        PathExpression [73-78]
+          Identifier(INT64) [73-78]
+  SpannerTableOptions [81-103]
+    PrimaryKey(ENFORCED) [81-103]
+      PrimaryKeyElementList [93-103]
+        PrimaryKeyElement(ASC) [94-102]
+          Identifier(order_id) [94-102]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-overview#strict-key-validation
+CREATE VIEW CustomerOrder
+  SQL SECURITY INVOKER AS
+    SELECT
+      s.order_id,
+      ANY_VALUE(c.customer_id) AS customer_id,
+      ANY_VALUE(c.name) AS customer_name
+    FROM Customer c JOIN SaleOrder s ON c.customer_id = s.customer_id
+    GROUP BY s.order_id
+--
+CreateViewStatement(SQL SECURITY INVOKER) [0-262]
+  PathExpression [12-25]
+    Identifier(CustomerOrder) [12-25]
+  Query [56-262]
+    Select [56-262]
+      SelectList [69-168]
+        SelectColumn [69-79]
+          PathExpression [69-79]
+            Identifier(s) [69-70]
+            Identifier(order_id) [71-79]
+        SelectColumn [87-126]
+          FunctionCall [87-111]
+            PathExpression [87-96]
+              Identifier(ANY_VALUE) [87-96]
+            PathExpression [97-110]
+              Identifier(c) [97-98]
+              Identifier(customer_id) [99-110]
+          Alias [112-126]
+            Identifier(customer_id) [115-126]
+        SelectColumn [134-168]
+          FunctionCall [134-151]
+            PathExpression [134-143]
+              Identifier(ANY_VALUE) [134-143]
+            PathExpression [144-150]
+              Identifier(c) [144-145]
+              Identifier(name) [146-150]
+          Alias [152-168]
+            Identifier(customer_name) [155-168]
+      FromClause [173-238]
+        Join [178-238]
+          TablePathExpression [178-188]
+            PathExpression [178-186]
+              Identifier(Customer) [178-186]
+            Alias [187-188]
+              Identifier(c) [187-188]
+          Location [189-193]
+          TablePathExpression [194-205]
+            PathExpression [194-203]
+              Identifier(SaleOrder) [194-203]
+            Alias [204-205]
+              Identifier(s) [204-205]
+          OnClause [206-238]
+            BinaryExpression(=) [209-238]
+              PathExpression [209-222]
+                Identifier(c) [209-210]
+                Identifier(customer_id) [211-222]
+              PathExpression [225-238]
+                Identifier(s) [225-226]
+                Identifier(customer_id) [227-238]
+      GroupBy [243-262]
+        GroupingItem [252-262]
+          PathExpression [252-262]
+            Identifier(s) [252-253]
+            Identifier(order_id) [254-262]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-overview#strict-key-validation
+CREATE PROPERTY GRAPH OrderGraph
+  NODE TABLES (
+    -- The KEY(order_id) matches the GROUP BY column in view definition.
+    CustomerOrder KEY(order_id)
+  )
+--
+CreatePropertyGraphStatement [0-157]
+  PathExpression [22-32]
+    Identifier(OrderGraph) [22-32]
+  GraphElementTableList [47-157]
+    GraphElementTable [126-153]
+      PathExpression [126-139]
+        Identifier(CustomerOrder) [126-139]
+      ColumnList [143-153]
+        Identifier(order_id) [144-152]
+      GraphElementLabelAndPropertiesList [153-153]
+        GraphElementLabelAndProperties [153-153]
+          GraphProperties [153-153]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-overview#strict-key-validation
+CREATE VIEW KeyCustomer SQL SECURITY INVOKER AS
+  SELECT DISTINCT s.customer_id, s.amount
+  FROM SaleOrder s
+  WHERE s.amount > 1000
+--
+CreateViewStatement(SQL SECURITY INVOKER) [0-132]
+  PathExpression [12-23]
+    Identifier(KeyCustomer) [12-23]
+  Query [50-132]
+    Select(distinct=true) [50-132]
+      SelectList [66-89]
+        SelectColumn [66-79]
+          PathExpression [66-79]
+            Identifier(s) [66-67]
+            Identifier(customer_id) [68-79]
+        SelectColumn [81-89]
+          PathExpression [81-89]
+            Identifier(s) [81-82]
+            Identifier(amount) [83-89]
+      FromClause [92-108]
+        TablePathExpression [97-108]
+          PathExpression [97-106]
+            Identifier(SaleOrder) [97-106]
+          Alias [107-108]
+            Identifier(s) [107-108]
+      WhereClause [111-132]
+        BinaryExpression(>) [117-132]
+          PathExpression [117-125]
+            Identifier(s) [117-118]
+            Identifier(amount) [119-125]
+          IntLiteral(1000) [128-132]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-overview#strict-key-validation
+CREATE PROPERTY GRAPH KeyCustomersGraph
+  NODE TABLES (
+    -- The KEY(customer_id, amount) matches the DISTINCT columns.
+    KeyCustomer KEY(customer_id, amount)
+  )
+--
+CreatePropertyGraphStatement [0-166]
+  PathExpression [22-39]
+    Identifier(KeyCustomersGraph) [22-39]
+  GraphElementTableList [54-166]
+    GraphElementTable [126-162]
+      PathExpression [126-137]
+        Identifier(KeyCustomer) [126-137]
+      ColumnList [141-162]
+        Identifier(customer_id) [142-153]
+        Identifier(amount) [155-161]
+      GraphElementLabelAndPropertiesList [162-162]
+        GraphElementLabelAndProperties [162-162]
+          GraphProperties [162-162]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-overview#disabled-key-validation
+-- View uses a JOIN without GROUP BY or DISTINCT.
+CREATE VIEW CustomerOrderTrusted
+  SQL SECURITY INVOKER AS
+    SELECT
+      s.order_id,
+      c.customer_id,
+      c.name AS customer_name
+    FROM Customer c JOIN SaleOrder s ON c.customer_id = s.customer_id
+--
+CreateViewStatement(SQL SECURITY INVOKER) [50-258]
+  PathExpression [62-82]
+    Identifier(CustomerOrderTrusted) [62-82]
+  Query [113-258]
+    Select [113-258]
+      SelectList [126-188]
+        SelectColumn [126-136]
+          PathExpression [126-136]
+            Identifier(s) [126-127]
+            Identifier(order_id) [128-136]
+        SelectColumn [144-157]
+          PathExpression [144-157]
+            Identifier(c) [144-145]
+            Identifier(customer_id) [146-157]
+        SelectColumn [165-188]
+          PathExpression [165-171]
+            Identifier(c) [165-166]
+            Identifier(name) [167-171]
+          Alias [172-188]
+            Identifier(customer_name) [175-188]
+      FromClause [193-258]
+        Join [198-258]
+          TablePathExpression [198-208]
+            PathExpression [198-206]
+              Identifier(Customer) [198-206]
+            Alias [207-208]
+              Identifier(c) [207-208]
+          Location [209-213]
+          TablePathExpression [214-225]
+            PathExpression [214-223]
+              Identifier(SaleOrder) [214-223]
+            Alias [224-225]
+              Identifier(s) [224-225]
+          OnClause [226-258]
+            BinaryExpression(=) [229-258]
+              PathExpression [229-242]
+                Identifier(c) [229-230]
+                Identifier(customer_id) [231-242]
+              PathExpression [245-258]
+                Identifier(s) [245-246]
+                Identifier(customer_id) [247-258]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-overview#disabled-key-validation
+-- Property graph disables element key uniqueness validation.
+CREATE PROPERTY GRAPH OrderGraphTrusted
+  NODE TABLES (
+    CustomerOrderTrusted
+      KEY(order_id)
+      LABEL CustomerOrder PROPERTIES(
+        order_id,
+        customer_id,
+        customer_name)
+  ) OPTIONS (validate_element_key_uniqueness = false)
+--
+CreatePropertyGraphStatement [62-316]
+  PathExpression [84-101]
+    Identifier(OrderGraphTrusted) [84-101]
+  GraphElementTableList [116-266]
+    GraphElementTable [122-262]
+      PathExpression [122-142]
+        Identifier(CustomerOrderTrusted) [122-142]
+      ColumnList [152-162]
+        Identifier(order_id) [153-161]
+      GraphElementLabelAndPropertiesList [169-262]
+        GraphElementLabelAndProperties [169-262]
+          Identifier(CustomerOrder) [175-188]
+          GraphProperties [189-262]
+            GraphDerivedPropertyList [209-261]
+              GraphDerivedProperty [209-217]
+                PathExpression [209-217]
+                  Identifier(order_id) [209-217]
+              GraphDerivedProperty [227-238]
+                PathExpression [227-238]
+                  Identifier(customer_id) [227-238]
+              GraphDerivedProperty [248-261]
+                PathExpression [248-261]
+                  Identifier(customer_name) [248-261]
+  OptionsList [275-316]
+    OptionsEntry [276-315]
+      Identifier(validate_element_key_uniqueness) [276-307]
+      BooleanLiteral(false) [310-315]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-overview#error-handling
+-- Enforce that the 'name' property exists for nodes with the 'person' label.
+ALTER TABLE GraphNode
+ADD CONSTRAINT NameMustExistForPersonConstraint
+CHECK (IF(label = 'person', properties.name IS NOT NULL, TRUE))
+--
+AlterTableStatement [78-211]
+  PathExpression [90-99]
+    Identifier(GraphNode) [90-99]
+  AlterActionList [100-211]
+    AddConstraintAction [100-211]
+      CheckConstraint(ENFORCED) [115-211]
+        FunctionCall [155-210]
+          PathExpression [155-157]
+            Identifier(`IF`) [155-157]
+          BinaryExpression(=) [158-174]
+            PathExpression [158-163]
+              Identifier(label) [158-163]
+            StringLiteral [166-174]
+              StringLiteralComponent('person') [166-174]
+          BinaryExpression(IS NOT) [176-203]
+            PathExpression [176-191]
+              Identifier(properties) [176-186]
+              Identifier(name) [187-191]
+            NullLiteral(NULL) [199-203]
+          BooleanLiteral(TRUE) [205-209]
+        Identifier(NameMustExistForPersonConstraint) [115-147]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/graph-with-views-overview#error-handling
+-- Enforce that the 'name' property is a string for nodes with the 'person' label.
+ALTER TABLE GraphNode
+ADD CONSTRAINT PersonNameMustBeStringTypeConstraint
+CHECK (IF(label = 'person', JSON_TYPE(properties.name) = 'string', TRUE))
+--
+AlterTableStatement [83-230]
+  PathExpression [95-104]
+    Identifier(GraphNode) [95-104]
+  AlterActionList [105-230]
+    AddConstraintAction [105-230]
+      CheckConstraint(ENFORCED) [120-230]
+        FunctionCall [164-229]
+          PathExpression [164-166]
+            Identifier(`IF`) [164-166]
+          BinaryExpression(=) [167-183]
+            PathExpression [167-172]
+              Identifier(label) [167-172]
+            StringLiteral [175-183]
+              StringLiteralComponent('person') [175-183]
+          BinaryExpression(=) [185-222]
+            FunctionCall [185-211]
+              PathExpression [185-194]
+                Identifier(JSON_TYPE) [185-194]
+              PathExpression [195-210]
+                Identifier(properties) [195-205]
+                Identifier(name) [206-210]
+            StringLiteral [214-222]
+              StringLiteralComponent('string') [214-222]
+          BooleanLiteral(TRUE) [224-228]
+        Identifier(PersonNameMustBeStringTypeConstraint) [120-156]
diff --git a/parser/testdata/spanner/guides/graph.insert-update-delete-data.test b/parser/testdata/spanner/guides/graph.insert-update-delete-data.test
new file mode 100644
index 0000000..5cf038c
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.insert-update-delete-data.test
@@ -0,0 +1,298 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/insert-update-delete-data
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/graph/insert-update-delete-data#insert-nodes-or-edges
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+-- Insert 2 Account nodes.
+INSERT INTO Account (id, create_time, is_blocked)
+VALUES (1, CAST('2000-08-10 08:18:48.463959-07:52' AS TIMESTAMP), false)
+--
+InsertStatement [27-149]
+  PathExpression [39-46]
+    Identifier(Account) [39-46]
+  ColumnList [47-76]
+    Identifier(id) [48-50]
+    Identifier(create_time) [52-63]
+    Identifier(is_blocked) [65-75]
+  InsertValuesRowList [77-149]
+    InsertValuesRow [84-149]
+      IntLiteral(1) [85-86]
+      CastExpression [88-141]
+        StringLiteral [93-127]
+          StringLiteralComponent('2000-08-10 08:18:48.463959-07:52') [93-127]
+        SimpleType [131-140]
+          PathExpression [131-140]
+            Identifier(TIMESTAMP) [131-140]
+      BooleanLiteral(false) [143-148]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/insert-update-delete-data#insert-nodes-or-edges
+INSERT INTO Account (id, create_time, is_blocked)
+VALUES (2, CAST('2000-08-12 07:13:16.463959-03:41' AS TIMESTAMP), true)
+--
+InsertStatement [0-121]
+  PathExpression [12-19]
+    Identifier(Account) [12-19]
+  ColumnList [20-49]
+    Identifier(id) [21-23]
+    Identifier(create_time) [25-36]
+    Identifier(is_blocked) [38-48]
+  InsertValuesRowList [50-121]
+    InsertValuesRow [57-121]
+      IntLiteral(2) [58-59]
+      CastExpression [61-114]
+        StringLiteral [66-100]
+          StringLiteralComponent('2000-08-12 07:13:16.463959-03:41') [66-100]
+        SimpleType [104-113]
+          PathExpression [104-113]
+            Identifier(TIMESTAMP) [104-113]
+      BooleanLiteral(true) [116-120]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/insert-update-delete-data#insert-nodes-or-edges
+-- Insert 2 Transfer edges.
+INSERT INTO AccountTransferAccount (id, to_id, create_time, amount)
+VALUES (1, 2, CAST('2000-09-11 03:11:18.463959-06:36' AS TIMESTAMP), 100)
+--
+InsertStatement [28-169]
+  PathExpression [40-62]
+    Identifier(AccountTransferAccount) [40-62]
+  ColumnList [63-95]
+    Identifier(id) [64-66]
+    Identifier(to_id) [68-73]
+    Identifier(create_time) [75-86]
+    Identifier(amount) [88-94]
+  InsertValuesRowList [96-169]
+    InsertValuesRow [103-169]
+      IntLiteral(1) [104-105]
+      IntLiteral(2) [107-108]
+      CastExpression [110-163]
+        StringLiteral [115-149]
+          StringLiteralComponent('2000-09-11 03:11:18.463959-06:36') [115-149]
+        SimpleType [153-162]
+          PathExpression [153-162]
+            Identifier(TIMESTAMP) [153-162]
+      IntLiteral(100) [165-168]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/insert-update-delete-data#insert-nodes-or-edges
+INSERT INTO AccountTransferAccount (id, to_id, create_time, amount)
+VALUES (1, 1, CAST('2000-09-12 04:09:34.463959-05:12' AS TIMESTAMP), 200)
+--
+InsertStatement [0-141]
+  PathExpression [12-34]
+    Identifier(AccountTransferAccount) [12-34]
+  ColumnList [35-67]
+    Identifier(id) [36-38]
+    Identifier(to_id) [40-45]
+    Identifier(create_time) [47-58]
+    Identifier(amount) [60-66]
+  InsertValuesRowList [68-141]
+    InsertValuesRow [75-141]
+      IntLiteral(1) [76-77]
+      IntLiteral(1) [79-80]
+      CastExpression [82-135]
+        StringLiteral [87-121]
+          StringLiteralComponent('2000-09-12 04:09:34.463959-05:12') [87-121]
+        SimpleType [125-134]
+          PathExpression [125-134]
+            Identifier(TIMESTAMP) [125-134]
+      IntLiteral(200) [137-140]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/insert-update-delete-data#update_nodes_or_edges_with_dml
+-- Update Account node
+UPDATE Account SET is_blocked = false WHERE id = 2
+--
+UpdateStatement [23-73]
+  PathExpression [30-37]
+    Identifier(Account) [30-37]
+  UpdateItemList [42-60]
+    UpdateItem [42-60]
+      UpdateSetValue [42-60]
+        PathExpression [42-52]
+          Identifier(is_blocked) [42-52]
+        BooleanLiteral(false) [55-60]
+  BinaryExpression(=) [67-73]
+    PathExpression [67-69]
+      Identifier(id) [67-69]
+    IntLiteral(2) [72-73]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/insert-update-delete-data#update_nodes_or_edges_with_dml
+-- Update Transfer edge
+UPDATE AccountTransferAccount
+SET amount = 300
+WHERE id = 1 AND to_id = 2
+--
+UpdateStatement [24-97]
+  PathExpression [31-53]
+    Identifier(AccountTransferAccount) [31-53]
+  UpdateItemList [58-70]
+    UpdateItem [58-70]
+      UpdateSetValue [58-70]
+        PathExpression [58-64]
+          Identifier(amount) [58-64]
+        IntLiteral(300) [67-70]
+  AndExpr [77-97]
+    BinaryExpression(=) [77-83]
+      PathExpression [77-79]
+        Identifier(id) [77-79]
+      IntLiteral(1) [82-83]
+    BinaryExpression(=) [88-97]
+      PathExpression [88-93]
+        Identifier(to_id) [88-93]
+      IntLiteral(2) [96-97]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/insert-update-delete-data#update_nodes_or_edges_with_graph_queries_and_dml
+-- Use Graph pattern matching to identify Account nodes to update:
+UPDATE Account SET is_blocked = false
+WHERE id IN {
+  GRAPH FinGraph
+  MATCH (a:Account WHERE a.id = 1)-[:Transfers]->{1,2}(b:Account)
+  RETURN b.id
+}
+--
+UpdateStatement [67-217]
+  PathExpression [74-81]
+    Identifier(Account) [74-81]
+  UpdateItemList [86-104]
+    UpdateItem [86-104]
+      UpdateSetValue [86-104]
+        PathExpression [86-96]
+          Identifier(is_blocked) [86-96]
+        BooleanLiteral(false) [99-104]
+  InExpression(IN) [111-217]
+    PathExpression [111-113]
+      Identifier(id) [111-113]
+    Location [114-116]
+    Query [117-217]
+      GqlQuery [117-217]
+        GraphTableQuery [117-217]
+          PathExpression [127-135]
+            Identifier(FinGraph) [127-135]
+          GqlOperatorList [138-215]
+            GqlOperatorList [138-215]
+              GqlMatch [138-201]
+                GraphPattern [144-201]
+                  GraphPathPattern [144-201]
+                    GraphNodePattern [144-170]
+                      GraphElementPatternFiller [145-169]
+                        Identifier(a) [145-146]
+                        GraphLabelFilter [146-154]
+                          GraphElementLabel [147-154]
+                            Identifier(Account) [147-154]
+                        WhereClause [155-169]
+                          BinaryExpression(=) [161-169]
+                            PathExpression [161-165]
+                              Identifier(a) [161-162]
+                              Identifier(id) [163-165]
+                            IntLiteral(1) [168-169]
+                    ParenthesizedGraphPathPattern [170-190]
+                      BoundedQuantifier [185-190]
+                        QuantifierBound [186-187]
+                          IntLiteral(1) [186-187]
+                        QuantifierBound [188-189]
+                          IntLiteral(2) [188-189]
+                      GraphEdgePattern [170-185]
+                        GraphElementPatternFiller [172-182]
+                          GraphLabelFilter [172-182]
+                            GraphElementLabel [173-182]
+                              Identifier(Transfers) [173-182]
+                    GraphNodePattern [190-201]
+                      GraphElementPatternFiller [191-200]
+                        Identifier(b) [191-192]
+                        GraphLabelFilter [192-200]
+                          GraphElementLabel [193-200]
+                            Identifier(Account) [193-200]
+              GqlReturn [204-215]
+                Select [211-215]
+                  SelectList [211-215]
+                    SelectColumn [211-215]
+                      PathExpression [211-215]
+                        Identifier(b) [211-212]
+                        Identifier(id) [213-215]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/insert-update-delete-data#delete-nodes-or-edges
+-- Delete Transfer edge
+DELETE FROM AccountTransferAccount
+WHERE id = 1 AND to_id = 2
+--
+DeleteStatement [24-85]
+  PathExpression [36-58]
+    Identifier(AccountTransferAccount) [36-58]
+  AndExpr [65-85]
+    BinaryExpression(=) [65-71]
+      PathExpression [65-67]
+        Identifier(id) [65-67]
+      IntLiteral(1) [70-71]
+    BinaryExpression(=) [76-85]
+      PathExpression [76-81]
+        Identifier(to_id) [76-81]
+      IntLiteral(2) [84-85]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/insert-update-delete-data#delete-nodes-or-edges
+-- Delete Account node
+DELETE FROM Account WHERE id = 2
+--
+DeleteStatement [23-55]
+  PathExpression [35-42]
+    Identifier(Account) [35-42]
+  BinaryExpression(=) [49-55]
+    PathExpression [49-51]
+      Identifier(id) [49-51]
+    IntLiteral(2) [54-55]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/insert-update-delete-data#delete-nodes-or-edges
+-- Use Graph pattern matching to identify Account nodes to delete:
+  DELETE FROM AccountTransferAccount
+  WHERE id IN {
+    GRAPH FinGraph
+    MATCH (a:Account WHERE a.id = 1)-[:Transfers]->(b:Account)
+    RETURN b.id
+  }
+--
+DeleteStatement [69-221]
+  PathExpression [81-103]
+    Identifier(AccountTransferAccount) [81-103]
+  InExpression(IN) [112-221]
+    PathExpression [112-114]
+      Identifier(id) [112-114]
+    Location [115-117]
+    Query [118-221]
+      GqlQuery [118-221]
+        GraphTableQuery [118-221]
+          PathExpression [130-138]
+            Identifier(FinGraph) [130-138]
+          GqlOperatorList [143-217]
+            GqlOperatorList [143-217]
+              GqlMatch [143-201]
+                GraphPattern [149-201]
+                  GraphPathPattern [149-201]
+                    GraphNodePattern [149-175]
+                      GraphElementPatternFiller [150-174]
+                        Identifier(a) [150-151]
+                        GraphLabelFilter [151-159]
+                          GraphElementLabel [152-159]
+                            Identifier(Account) [152-159]
+                        WhereClause [160-174]
+                          BinaryExpression(=) [166-174]
+                            PathExpression [166-170]
+                              Identifier(a) [166-167]
+                              Identifier(id) [168-170]
+                            IntLiteral(1) [173-174]
+                    GraphEdgePattern [175-190]
+                      GraphElementPatternFiller [177-187]
+                        GraphLabelFilter [177-187]
+                          GraphElementLabel [178-187]
+                            Identifier(Transfers) [178-187]
+                    GraphNodePattern [190-201]
+                      GraphElementPatternFiller [191-200]
+                        Identifier(b) [191-192]
+                        GraphLabelFilter [192-200]
+                          GraphElementLabel [193-200]
+                            Identifier(Account) [193-200]
+              GqlReturn [206-217]
+                Select [213-217]
+                  SelectList [213-217]
+                    SelectColumn [213-217]
+                      PathExpression [213-217]
+                        Identifier(b) [213-214]
+                        Identifier(id) [215-217]
diff --git a/parser/testdata/spanner/guides/graph.manage-schemaless-data.metadata.json b/parser/testdata/spanner/guides/graph.manage-schemaless-data.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.manage-schemaless-data.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/graph.manage-schemaless-data.test b/parser/testdata/spanner/guides/graph.manage-schemaless-data.test
new file mode 100644
index 0000000..af1acef
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.manage-schemaless-data.test
@@ -0,0 +1,2066 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#create-input-tables
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE GraphNode (
+  id INT64 NOT NULL,
+  label STRING(MAX) NOT NULL,
+  properties JSON,
+) PRIMARY KEY (id)
+--
+CreateTableStatement [0-113]
+  PathExpression [13-22]
+    Identifier(GraphNode) [13-22]
+  TableElementList [23-96]
+    ColumnDefinition [27-44]
+      Identifier(id) [27-29]
+      SimpleColumnSchema [30-44]
+        PathExpression [30-35]
+          Identifier(INT64) [30-35]
+        ColumnAttributeList [36-44]
+          NotNullColumnAttribute [36-44]
+    ColumnDefinition [48-74]
+      Identifier(label) [48-53]
+      SimpleColumnSchema [54-74]
+        PathExpression [54-60]
+          Identifier(STRING) [54-60]
+        TypeParameterList [60-64]
+          MaxLiteral() [61-64]
+        ColumnAttributeList [66-74]
+          NotNullColumnAttribute [66-74]
+    ColumnDefinition [78-93]
+      Identifier(properties) [78-88]
+      SimpleColumnSchema [89-93]
+        PathExpression [89-93]
+          Identifier(JSON) [89-93]
+  SpannerTableOptions [97-113]
+    PrimaryKey(ENFORCED) [97-113]
+      PrimaryKeyElementList [109-113]
+        PrimaryKeyElement(ASC) [110-112]
+          Identifier(id) [110-112]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#create-input-tables
+CREATE TABLE GraphEdge (
+  id INT64 NOT NULL,
+  dest_id INT64 NOT NULL,
+  edge_id INT64 NOT NULL,
+  label STRING(MAX) NOT NULL,
+  properties JSON,
+) PRIMARY KEY (id, dest_id, edge_id),
+  INTERLEAVE IN PARENT GraphNode
+--
+CreateTableStatement [0-217]
+  PathExpression [13-22]
+    Identifier(GraphEdge) [13-22]
+  TableElementList [23-148]
+    ColumnDefinition [27-44]
+      Identifier(id) [27-29]
+      SimpleColumnSchema [30-44]
+        PathExpression [30-35]
+          Identifier(INT64) [30-35]
+        ColumnAttributeList [36-44]
+          NotNullColumnAttribute [36-44]
+    ColumnDefinition [48-70]
+      Identifier(dest_id) [48-55]
+      SimpleColumnSchema [56-70]
+        PathExpression [56-61]
+          Identifier(INT64) [56-61]
+        ColumnAttributeList [62-70]
+          NotNullColumnAttribute [62-70]
+    ColumnDefinition [74-96]
+      Identifier(edge_id) [74-81]
+      SimpleColumnSchema [82-96]
+        PathExpression [82-87]
+          Identifier(INT64) [82-87]
+        ColumnAttributeList [88-96]
+          NotNullColumnAttribute [88-96]
+    ColumnDefinition [100-126]
+      Identifier(label) [100-105]
+      SimpleColumnSchema [106-126]
+        PathExpression [106-112]
+          Identifier(STRING) [106-112]
+        TypeParameterList [112-116]
+          MaxLiteral() [113-116]
+        ColumnAttributeList [118-126]
+          NotNullColumnAttribute [118-126]
+    ColumnDefinition [130-145]
+      Identifier(properties) [130-140]
+      SimpleColumnSchema [141-145]
+        PathExpression [141-145]
+          Identifier(JSON) [141-145]
+  SpannerTableOptions [149-217]
+    PrimaryKey(ENFORCED) [149-183]
+      PrimaryKeyElementList [161-183]
+        PrimaryKeyElement(ASC) [162-164]
+          Identifier(id) [162-164]
+        PrimaryKeyElement(ASC) [166-173]
+          Identifier(dest_id) [166-173]
+        PrimaryKeyElement(ASC) [175-182]
+          Identifier(edge_id) [175-182]
+    SpannerInterleaveClause [183-217]
+      PathExpression [208-217]
+        Identifier(GraphNode) [208-217]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#create-a-property-graph
+CREATE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    GraphNode
+      DYNAMIC LABEL (label)
+      DYNAMIC PROPERTIES (properties)
+  )
+  EDGE TABLES (
+    GraphEdge
+      SOURCE KEY (id) REFERENCES GraphNode(id)
+      DESTINATION KEY (dest_id) REFERENCES GraphNode(id)
+      DYNAMIC LABEL (label)
+      DYNAMIC PROPERTIES (properties)
+  )
+--
+CreatePropertyGraphStatement [0-334]
+  PathExpression [22-30]
+    Identifier(FinGraph) [22-30]
+  GraphElementTableList [45-130]
+    GraphElementTable [51-126]
+      PathExpression [51-60]
+        Identifier(GraphNode) [51-60]
+      GraphElementLabelAndPropertiesList [60-60]
+        GraphElementLabelAndProperties [60-60]
+          GraphProperties [60-60]
+      GraphDynamicLabel [82-87]
+        PathExpression [82-87]
+          Identifier(label) [82-87]
+      GraphDynamicProperties [115-125]
+        PathExpression [115-125]
+          Identifier(properties) [115-125]
+  GraphElementTableList [145-334]
+    GraphElementTable [151-330]
+      PathExpression [151-160]
+        Identifier(GraphEdge) [151-160]
+      GraphNodeTableReference(SOURCE) [167-207]
+        Identifier(GraphNode) [194-203]
+        ColumnList [178-182]
+          Identifier(id) [179-181]
+        ColumnList [203-207]
+          Identifier(id) [204-206]
+      GraphNodeTableReference(DESTINATION) [214-264]
+        Identifier(GraphNode) [251-260]
+        ColumnList [230-239]
+          Identifier(dest_id) [231-238]
+        ColumnList [260-264]
+          Identifier(id) [261-263]
+      GraphElementLabelAndPropertiesList [264-264]
+        GraphElementLabelAndProperties [264-264]
+          GraphProperties [264-264]
+      GraphDynamicLabel [286-291]
+        PathExpression [286-291]
+          Identifier(label) [286-291]
+      GraphDynamicProperties [319-329]
+        PathExpression [319-329]
+          Identifier(properties) [319-329]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#match-nodes-and-edges-using-labels
+GRAPH FinGraph
+MATCH (a:Account {id: 1})-[t:Transfers]->(d:Account)
+RETURN COUNT(*) AS result_count
+--
+QueryStatement [0-99]
+  Query [0-99]
+    GqlQuery [0-99]
+      GraphTableQuery [0-99]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-99]
+          GqlOperatorList [15-99]
+            GqlMatch [15-67]
+              GraphPattern [21-67]
+                GraphPathPattern [21-67]
+                  GraphNodePattern [21-40]
+                    GraphElementPatternFiller [22-39]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                      GraphPropertySpecification [32-39]
+                        GraphPropertyNameAndValue [33-38]
+                          Identifier(id) [33-35]
+                          IntLiteral(1) [37-38]
+                  GraphEdgePattern [40-56]
+                    GraphElementPatternFiller [42-53]
+                      Identifier(t) [42-43]
+                      GraphLabelFilter [43-53]
+                        GraphElementLabel [44-53]
+                          Identifier(Transfers) [44-53]
+                  GraphNodePattern [56-67]
+                    GraphElementPatternFiller [57-66]
+                      Identifier(d) [57-58]
+                      GraphLabelFilter [58-66]
+                        GraphElementLabel [59-66]
+                          Identifier(Account) [59-66]
+            GqlReturn [68-99]
+              Select [75-99]
+                SelectList [75-99]
+                  SelectColumn [75-99]
+                    FunctionCall [75-83]
+                      PathExpression [75-80]
+                        Identifier(COUNT) [75-80]
+                      Star(*) [81-82]
+                    Alias [84-99]
+                      Identifier(result_count) [87-99]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#access-properties
+GRAPH FinGraph
+MATCH (person:Person {id: 1})
+RETURN person.name
+--
+QueryStatement [0-63]
+  Query [0-63]
+    GqlQuery [0-63]
+      GraphTableQuery [0-63]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-63]
+          GqlOperatorList [15-63]
+            GqlMatch [15-44]
+              GraphPattern [21-44]
+                GraphPathPattern [21-44]
+                  GraphNodePattern [21-44]
+                    GraphElementPatternFiller [22-43]
+                      Identifier(person) [22-28]
+                      GraphLabelFilter [28-35]
+                        GraphElementLabel [29-35]
+                          Identifier(Person) [29-35]
+                      GraphPropertySpecification [36-43]
+                        GraphPropertyNameAndValue [37-42]
+                          Identifier(id) [37-39]
+                          IntLiteral(1) [41-42]
+            GqlReturn [45-63]
+              Select [52-63]
+                SelectList [52-63]
+                  SelectColumn [52-63]
+                    PathExpression [52-63]
+                      Identifier(person) [52-58]
+                      Identifier(name) [59-63]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#access-properties
+[mode=expression]
+JSON"Tom"
+--
+JSONLiteral [0-9]
+  StringLiteral [4-9]
+    StringLiteralComponent("Tom") [4-9]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#convert-property-data-types
+GRAPH FinGraph
+MATCH (a:Account)-[t:Transfers]->()
+WHERE BOOL(a.is_blocked) AND STRING(t.order_number_str) = "302290001255747"
+RETURN LAX_INT64(t.order_number_str) AS order_number_as_int64
+--
+QueryStatement [0-188]
+  Query [0-188]
+    GqlQuery [0-188]
+      GraphTableQuery [0-188]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-188]
+          GqlOperatorList [15-188]
+            GqlMatch [15-126]
+              GraphPattern [21-126]
+                GraphPathPattern [21-50]
+                  GraphNodePattern [21-32]
+                    GraphElementPatternFiller [22-31]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                  GraphEdgePattern [32-48]
+                    GraphElementPatternFiller [34-45]
+                      Identifier(t) [34-35]
+                      GraphLabelFilter [35-45]
+                        GraphElementLabel [36-45]
+                          Identifier(Transfers) [36-45]
+                  GraphNodePattern [48-50]
+                    GraphElementPatternFiller [49-49]
+                WhereClause [51-126]
+                  AndExpr [57-126]
+                    FunctionCall [57-75]
+                      PathExpression [57-61]
+                        Identifier(BOOL) [57-61]
+                      PathExpression [62-74]
+                        Identifier(a) [62-63]
+                        Identifier(is_blocked) [64-74]
+                    BinaryExpression(=) [80-126]
+                      FunctionCall [80-106]
+                        PathExpression [80-86]
+                          Identifier(STRING) [80-86]
+                        PathExpression [87-105]
+                          Identifier(t) [87-88]
+                          Identifier(order_number_str) [89-105]
+                      StringLiteral [109-126]
+                        StringLiteralComponent("302290001255747") [109-126]
+            GqlReturn [127-188]
+              Select [134-188]
+                SelectList [134-188]
+                  SelectColumn [134-188]
+                    FunctionCall [134-163]
+                      PathExpression [134-143]
+                        Identifier(LAX_INT64) [134-143]
+                      PathExpression [144-162]
+                        Identifier(t) [144-145]
+                        Identifier(order_number_str) [146-162]
+                    Alias [164-188]
+                      Identifier(order_number_as_int64) [167-188]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#convert-property-data-types
+GRAPH FinGraph
+MATCH (person:Person {country: "South Korea"})
+RETURN STRING(person.city) as person_city, COUNT(*) as cnt
+LIMIT 10
+--
+QueryStatement [0-129]
+  Query [0-129]
+    GqlQuery [0-129]
+      GraphTableQuery [0-129]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-129]
+          GqlOperatorList [15-129]
+            GqlMatch [15-61]
+              GraphPattern [21-61]
+                GraphPathPattern [21-61]
+                  GraphNodePattern [21-61]
+                    GraphElementPatternFiller [22-60]
+                      Identifier(person) [22-28]
+                      GraphLabelFilter [28-35]
+                        GraphElementLabel [29-35]
+                          Identifier(Person) [29-35]
+                      GraphPropertySpecification [36-60]
+                        GraphPropertyNameAndValue [37-59]
+                          Identifier(country) [37-44]
+                          StringLiteral [46-59]
+                            StringLiteralComponent("South Korea") [46-59]
+            GqlReturn [62-129]
+              Select [69-120]
+                SelectList [69-120]
+                  SelectColumn [69-103]
+                    FunctionCall [69-88]
+                      PathExpression [69-75]
+                        Identifier(STRING) [69-75]
+                      PathExpression [76-87]
+                        Identifier(person) [76-82]
+                        Identifier(city) [83-87]
+                    Alias [89-103]
+                      Identifier(person_city) [92-103]
+                  SelectColumn [105-120]
+                    FunctionCall [105-113]
+                      PathExpression [105-110]
+                        Identifier(COUNT) [105-110]
+                      Star(*) [111-112]
+                    Alias [114-120]
+                      Identifier(cnt) [117-120]
+              GqlOrderByAndPage [120-129]
+                GqlPage [121-129]
+                  GqlPageLimit [121-129]
+                    IntLiteral(10) [127-129]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#filter-property-values
+GRAPH FinGraph
+MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str:"302290001255747"}]->()
+RETURN a.id AS account_id
+--
+QueryStatement [0-133]
+  Query [0-133]
+    GqlQuery [0-133]
+      GraphTableQuery [0-133]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-133]
+          GqlOperatorList [15-133]
+            GqlMatch [15-107]
+              GraphPattern [21-107]
+                GraphPathPattern [21-107]
+                  GraphNodePattern [21-52]
+                    GraphElementPatternFiller [22-51]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                      GraphPropertySpecification [32-51]
+                        GraphPropertyNameAndValue [33-50]
+                          Identifier(is_blocked) [33-43]
+                          BooleanLiteral(false) [45-50]
+                  GraphEdgePattern [52-105]
+                    GraphElementPatternFiller [54-102]
+                      Identifier(t) [54-55]
+                      GraphLabelFilter [55-65]
+                        GraphElementLabel [56-65]
+                          Identifier(Transfers) [56-65]
+                      GraphPropertySpecification [66-102]
+                        GraphPropertyNameAndValue [67-101]
+                          Identifier(order_number_str) [67-83]
+                          StringLiteral [84-101]
+                            StringLiteralComponent("302290001255747") [84-101]
+                  GraphNodePattern [105-107]
+                    GraphElementPatternFiller [106-106]
+            GqlReturn [108-133]
+              Select [115-133]
+                SelectList [115-133]
+                  SelectColumn [115-133]
+                    PathExpression [115-119]
+                      Identifier(a) [115-116]
+                      Identifier(id) [117-119]
+                    Alias [120-133]
+                      Identifier(account_id) [123-133]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#filter-property-values
+GRAPH FinGraph
+MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str: 302290001255747}]->()
+RETURN t.order_number_str
+--
+QueryStatement [0-132]
+  Query [0-132]
+    GqlQuery [0-132]
+      GraphTableQuery [0-132]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-132]
+          GqlOperatorList [15-132]
+            GqlMatch [15-106]
+              GraphPattern [21-106]
+                GraphPathPattern [21-106]
+                  GraphNodePattern [21-52]
+                    GraphElementPatternFiller [22-51]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                      GraphPropertySpecification [32-51]
+                        GraphPropertyNameAndValue [33-50]
+                          Identifier(is_blocked) [33-43]
+                          BooleanLiteral(false) [45-50]
+                  GraphEdgePattern [52-104]
+                    GraphElementPatternFiller [54-101]
+                      Identifier(t) [54-55]
+                      GraphLabelFilter [55-65]
+                        GraphElementLabel [56-65]
+                          Identifier(Transfers) [56-65]
+                      GraphPropertySpecification [66-101]
+                        GraphPropertyNameAndValue [67-100]
+                          Identifier(order_number_str) [67-83]
+                          IntLiteral(302290001255747) [85-100]
+                  GraphNodePattern [104-106]
+                    GraphElementPatternFiller [105-105]
+            GqlReturn [107-132]
+              Select [114-132]
+                SelectList [114-132]
+                  SelectColumn [114-132]
+                    PathExpression [114-132]
+                      Identifier(t) [114-115]
+                      Identifier(order_number_str) [116-132]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#access_nested_json_properties
+GRAPH FinGraph
+MATCH (person:Person {id: 1})
+RETURN STRING(person.location.city)
+--
+QueryStatement [0-80]
+  Query [0-80]
+    GqlQuery [0-80]
+      GraphTableQuery [0-80]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-80]
+          GqlOperatorList [15-80]
+            GqlMatch [15-44]
+              GraphPattern [21-44]
+                GraphPathPattern [21-44]
+                  GraphNodePattern [21-44]
+                    GraphElementPatternFiller [22-43]
+                      Identifier(person) [22-28]
+                      GraphLabelFilter [28-35]
+                        GraphElementLabel [29-35]
+                          Identifier(Person) [29-35]
+                      GraphPropertySpecification [36-43]
+                        GraphPropertyNameAndValue [37-42]
+                          Identifier(id) [37-39]
+                          IntLiteral(1) [41-42]
+            GqlReturn [45-80]
+              Select [52-80]
+                SelectList [52-80]
+                  SelectColumn [52-80]
+                    FunctionCall [52-80]
+                      PathExpression [52-58]
+                        Identifier(STRING) [52-58]
+                      PathExpression [59-79]
+                        Identifier(person) [59-65]
+                        Identifier(location) [66-74]
+                        Identifier(city) [75-79]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#access_nested_json_properties
+[mode=expression]
+"New York"
+--
+StringLiteral [0-10]
+  StringLiteralComponent("New York") [0-10]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#create-graph-data
+INSERT INTO GraphNode (id, label, properties)
+VALUES (4, "person", JSON'{"name": "David", "age": 43}')
+--
+InsertStatement [0-102]
+  PathExpression [12-21]
+    Identifier(GraphNode) [12-21]
+  ColumnList [22-45]
+    Identifier(id) [23-25]
+    Identifier(label) [27-32]
+    Identifier(properties) [34-44]
+  InsertValuesRowList [46-102]
+    InsertValuesRow [53-102]
+      IntLiteral(4) [54-55]
+      StringLiteral [57-65]
+        StringLiteralComponent("person") [57-65]
+      JSONLiteral [67-101]
+        StringLiteral [71-101]
+          StringLiteralComponent('{"name": "David", "age": 43}') [71-101]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#update-graph-data
+UPDATE GraphNode
+SET properties = JSON_SET(
+  properties,
+  '$.is_blocked', false
+)
+WHERE label = "account" AND id = 16
+--
+UpdateStatement [0-119]
+  PathExpression [7-16]
+    Identifier(GraphNode) [7-16]
+  UpdateItemList [21-83]
+    UpdateItem [21-83]
+      UpdateSetValue [21-83]
+        PathExpression [21-31]
+          Identifier(properties) [21-31]
+        FunctionCall [34-83]
+          PathExpression [34-42]
+            Identifier(JSON_SET) [34-42]
+          PathExpression [46-56]
+            Identifier(properties) [46-56]
+          StringLiteral [60-74]
+            StringLiteralComponent('$.is_blocked') [60-74]
+          BooleanLiteral(false) [76-81]
+  AndExpr [90-119]
+    BinaryExpression(=) [90-107]
+      PathExpression [90-95]
+        Identifier(label) [90-95]
+      StringLiteral [98-107]
+        StringLiteralComponent("account") [98-107]
+    BinaryExpression(=) [112-119]
+      PathExpression [112-114]
+        Identifier(id) [112-114]
+      IntLiteral(16) [117-119]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#update-graph-data
+UPDATE GraphNode
+SET properties = JSON'{"name": "David", "age": 43}'
+WHERE label = "person" AND id = 4
+--
+UpdateStatement [0-102]
+  PathExpression [7-16]
+    Identifier(GraphNode) [7-16]
+  UpdateItemList [21-68]
+    UpdateItem [21-68]
+      UpdateSetValue [21-68]
+        PathExpression [21-31]
+          Identifier(properties) [21-31]
+        JSONLiteral [34-68]
+          StringLiteral [38-68]
+            StringLiteralComponent('{"name": "David", "age": 43}') [38-68]
+  AndExpr [75-102]
+    BinaryExpression(=) [75-91]
+      PathExpression [75-80]
+        Identifier(label) [75-80]
+      StringLiteral [83-91]
+        StringLiteralComponent("person") [83-91]
+    BinaryExpression(=) [96-102]
+      PathExpression [96-98]
+        Identifier(id) [96-98]
+      IntLiteral(4) [101-102]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#update-graph-data
+UPDATE GraphNode
+SET properties = JSON_REMOVE(
+  properties,
+  '$.is_blocked'
+)
+WHERE label = "account" AND id = 16
+--
+UpdateStatement [0-115]
+  PathExpression [7-16]
+    Identifier(GraphNode) [7-16]
+  UpdateItemList [21-79]
+    UpdateItem [21-79]
+      UpdateSetValue [21-79]
+        PathExpression [21-31]
+          Identifier(properties) [21-31]
+        FunctionCall [34-79]
+          PathExpression [34-45]
+            Identifier(JSON_REMOVE) [34-45]
+          PathExpression [49-59]
+            Identifier(properties) [49-59]
+          StringLiteral [63-77]
+            StringLiteralComponent('$.is_blocked') [63-77]
+  AndExpr [86-115]
+    BinaryExpression(=) [86-103]
+      PathExpression [86-91]
+        Identifier(label) [86-91]
+      StringLiteral [94-103]
+        StringLiteralComponent("account") [94-103]
+    BinaryExpression(=) [108-115]
+      PathExpression [108-110]
+        Identifier(id) [108-110]
+      IntLiteral(16) [113-115]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#delete-graph-data
+DELETE FROM GraphEdge
+WHERE label = "transfers" AND id IN {
+  GRAPH FinGraph
+  MATCH (a:Account)-[:Transfers]->{1,2}(:Account {is_blocked: TRUE})
+  RETURN a.id
+}
+--
+DeleteStatement [0-161]
+  PathExpression [12-21]
+    Identifier(GraphEdge) [12-21]
+  AndExpr [28-161]
+    BinaryExpression(=) [28-47]
+      PathExpression [28-33]
+        Identifier(label) [28-33]
+      StringLiteral [36-47]
+        StringLiteralComponent("transfers") [36-47]
+    InExpression(IN) [52-161]
+      PathExpression [52-54]
+        Identifier(id) [52-54]
+      Location [55-57]
+      Query [58-161]
+        GqlQuery [58-161]
+          GraphTableQuery [58-161]
+            PathExpression [68-76]
+              Identifier(FinGraph) [68-76]
+            GqlOperatorList [79-159]
+              GqlOperatorList [79-159]
+                GqlMatch [79-145]
+                  GraphPattern [85-145]
+                    GraphPathPattern [85-145]
+                      GraphNodePattern [85-96]
+                        GraphElementPatternFiller [86-95]
+                          Identifier(a) [86-87]
+                          GraphLabelFilter [87-95]
+                            GraphElementLabel [88-95]
+                              Identifier(Account) [88-95]
+                      ParenthesizedGraphPathPattern [96-116]
+                        BoundedQuantifier [111-116]
+                          QuantifierBound [112-113]
+                            IntLiteral(1) [112-113]
+                          QuantifierBound [114-115]
+                            IntLiteral(2) [114-115]
+                        GraphEdgePattern [96-111]
+                          GraphElementPatternFiller [98-108]
+                            GraphLabelFilter [98-108]
+                              GraphElementLabel [99-108]
+                                Identifier(Transfers) [99-108]
+                      GraphNodePattern [116-145]
+                        GraphElementPatternFiller [117-144]
+                          GraphLabelFilter [117-125]
+                            GraphElementLabel [118-125]
+                              Identifier(Account) [118-125]
+                          GraphPropertySpecification [126-144]
+                            GraphPropertyNameAndValue [127-143]
+                              Identifier(is_blocked) [127-137]
+                              BooleanLiteral(TRUE) [139-143]
+                GqlReturn [148-159]
+                  Select [155-159]
+                    SelectList [155-159]
+                      SelectColumn [155-159]
+                        PathExpression [155-159]
+                          Identifier(a) [155-156]
+                          Identifier(id) [157-159]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#labels-must-be-lowercase
+INSERT INTO GraphNode (id, label) VALUES (1, "account")
+--
+InsertStatement [0-55]
+  PathExpression [12-21]
+    Identifier(GraphNode) [12-21]
+  ColumnList [22-33]
+    Identifier(id) [23-25]
+    Identifier(label) [27-32]
+  InsertValuesRowList [34-55]
+    InsertValuesRow [41-55]
+      IntLiteral(1) [42-43]
+      StringLiteral [45-54]
+        StringLiteralComponent("account") [45-54]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#labels-must-be-lowercase
+INSERT INTO GraphNode (id, label) VALUES (2, "account")
+--
+InsertStatement [0-55]
+  PathExpression [12-21]
+    Identifier(GraphNode) [12-21]
+  ColumnList [22-33]
+    Identifier(id) [23-25]
+    Identifier(label) [27-32]
+  InsertValuesRowList [34-55]
+    InsertValuesRow [41-55]
+      IntLiteral(2) [42-43]
+      StringLiteral [45-54]
+        StringLiteralComponent("account") [45-54]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#labels-must-be-lowercase
+GRAPH FinGraph
+MATCH (accnt:Account {id: 1})-[:Transfers]->(dest_accnt:Account)
+RETURN dest_accnt.id
+--
+QueryStatement [0-100]
+  Query [0-100]
+    GqlQuery [0-100]
+      GraphTableQuery [0-100]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-100]
+          GqlOperatorList [15-100]
+            GqlMatch [15-79]
+              GraphPattern [21-79]
+                GraphPathPattern [21-79]
+                  GraphNodePattern [21-44]
+                    GraphElementPatternFiller [22-43]
+                      Identifier(accnt) [22-27]
+                      GraphLabelFilter [27-35]
+                        GraphElementLabel [28-35]
+                          Identifier(Account) [28-35]
+                      GraphPropertySpecification [36-43]
+                        GraphPropertyNameAndValue [37-42]
+                          Identifier(id) [37-39]
+                          IntLiteral(1) [41-42]
+                  GraphEdgePattern [44-59]
+                    GraphElementPatternFiller [46-56]
+                      GraphLabelFilter [46-56]
+                        GraphElementLabel [47-56]
+                          Identifier(Transfers) [47-56]
+                  GraphNodePattern [59-79]
+                    GraphElementPatternFiller [60-78]
+                      Identifier(dest_accnt) [60-70]
+                      GraphLabelFilter [70-78]
+                        GraphElementLabel [71-78]
+                          Identifier(Account) [71-78]
+            GqlReturn [80-100]
+              Select [87-100]
+                SelectList [87-100]
+                  SelectColumn [87-100]
+                    PathExpression [87-100]
+                      Identifier(dest_accnt) [87-97]
+                      Identifier(id) [98-100]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#property-names-must-be-lowercase
+INSERT INTO GraphNode (id, label, properties)
+VALUES (25, "person", JSON '{"name": "Kim", "age": 27}')
+--
+InsertStatement [0-102]
+  PathExpression [12-21]
+    Identifier(GraphNode) [12-21]
+  ColumnList [22-45]
+    Identifier(id) [23-25]
+    Identifier(label) [27-32]
+    Identifier(properties) [34-44]
+  InsertValuesRowList [46-102]
+    InsertValuesRow [53-102]
+      IntLiteral(25) [54-56]
+      StringLiteral [58-66]
+        StringLiteralComponent("person") [58-66]
+      JSONLiteral [68-101]
+        StringLiteral [73-101]
+          StringLiteralComponent('{"name": "Kim", "age": 27}') [73-101]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#property-names-must-be-lowercase
+GRAPH FinGraph
+MATCH (n:Person {Age: 27})
+RETURN n.id
+--
+QueryStatement [0-53]
+  Query [0-53]
+    GqlQuery [0-53]
+      GraphTableQuery [0-53]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-53]
+          GqlOperatorList [15-53]
+            GqlMatch [15-41]
+              GraphPattern [21-41]
+                GraphPathPattern [21-41]
+                  GraphNodePattern [21-41]
+                    GraphElementPatternFiller [22-40]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                      GraphPropertySpecification [31-40]
+                        GraphPropertyNameAndValue [32-39]
+                          Identifier(Age) [32-35]
+                          IntLiteral(27) [37-39]
+            GqlReturn [42-53]
+              Select [49-53]
+                SelectList [49-53]
+                  SelectColumn [49-53]
+                    PathExpression [49-53]
+                      Identifier(n) [49-50]
+                      Identifier(id) [51-53]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#property-indexes
+ALTER TABLE GraphNode
+ADD COLUMN person_age INT64 AS
+(IF (label = "person", LAX_INT64(properties.age), NULL))
+--
+AlterTableStatement [0-109]
+  PathExpression [12-21]
+    Identifier(GraphNode) [12-21]
+  AlterActionList [22-109]
+    AddColumnAction [22-109]
+      ColumnDefinition [33-109]
+        Identifier(person_age) [33-43]
+        SimpleColumnSchema [44-109]
+          PathExpression [44-49]
+            Identifier(INT64) [44-49]
+          GeneratedColumnInfo(generated_mode=ALWAYS) [50-109]
+            FunctionCall [54-108]
+              PathExpression [54-56]
+                Identifier(`IF`) [54-56]
+              BinaryExpression(=) [58-74]
+                PathExpression [58-63]
+                  Identifier(label) [58-63]
+                StringLiteral [66-74]
+                  StringLiteralComponent("person") [66-74]
+              FunctionCall [76-101]
+                PathExpression [76-85]
+                  Identifier(LAX_INT64) [76-85]
+                PathExpression [86-100]
+                  Identifier(properties) [86-96]
+                  Identifier(age) [97-100]
+              NullLiteral(NULL) [103-107]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#property-indexes
+CREATE NULL_FILTERED INDEX IdxPersonAge
+ON GraphNode(id, label, person_age), INTERLEAVE IN GraphNode
+--
+CreateIndexStatement [0-100]
+  PathExpression [27-39]
+    Identifier(IdxPersonAge) [27-39]
+  PathExpression [43-52]
+    Identifier(GraphNode) [43-52]
+  IndexItemList [52-75]
+    OrderingExpression(ASC) [53-55]
+      PathExpression [53-55]
+        Identifier(id) [53-55]
+    OrderingExpression(ASC) [57-62]
+      PathExpression [57-62]
+        Identifier(label) [57-62]
+    OrderingExpression(ASC) [64-74]
+      PathExpression [64-74]
+        Identifier(person_age) [64-74]
+  SpannerInterleaveClause [75-100]
+    PathExpression [91-100]
+      Identifier(GraphNode) [91-100]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#property-indexes
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    GraphNode
+      DYNAMIC LABEL (label)
+      DYNAMIC PROPERTIES (properties)
+  )
+  EDGE TABLES (
+    GraphEdge
+      SOURCE KEY (id) REFERENCES GraphNode (id)
+      DESTINATION KEY (dest_id) REFERENCES GraphNode (id)
+      DYNAMIC LABEL (label)
+      DYNAMIC PROPERTIES (properties)
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [0-347]
+  PathExpression [33-41]
+    Identifier(FinGraph) [33-41]
+  GraphElementTableList [56-141]
+    GraphElementTable [62-137]
+      PathExpression [62-71]
+        Identifier(GraphNode) [62-71]
+      GraphElementLabelAndPropertiesList [71-71]
+        GraphElementLabelAndProperties [71-71]
+          GraphProperties [71-71]
+      GraphDynamicLabel [93-98]
+        PathExpression [93-98]
+          Identifier(label) [93-98]
+      GraphDynamicProperties [126-136]
+        PathExpression [126-136]
+          Identifier(properties) [126-136]
+  GraphElementTableList [156-347]
+    GraphElementTable [162-343]
+      PathExpression [162-171]
+        Identifier(GraphEdge) [162-171]
+      GraphNodeTableReference(SOURCE) [178-219]
+        Identifier(GraphNode) [205-214]
+        ColumnList [189-193]
+          Identifier(id) [190-192]
+        ColumnList [215-219]
+          Identifier(id) [216-218]
+      GraphNodeTableReference(DESTINATION) [226-277]
+        Identifier(GraphNode) [263-272]
+        ColumnList [242-251]
+          Identifier(dest_id) [243-250]
+        ColumnList [273-277]
+          Identifier(id) [274-276]
+      GraphElementLabelAndPropertiesList [277-277]
+        GraphElementLabelAndProperties [277-277]
+          GraphProperties [277-277]
+      GraphDynamicLabel [299-304]
+        PathExpression [299-304]
+          Identifier(label) [299-304]
+      GraphDynamicProperties [332-342]
+        PathExpression [332-342]
+          Identifier(properties) [332-342]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#property-indexes
+GRAPH FinGraph
+MATCH (person:Person {person_age: 43})
+RETURN person.id, person.name
+--
+QueryStatement [0-83]
+  Query [0-83]
+    GqlQuery [0-83]
+      GraphTableQuery [0-83]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-83]
+          GqlOperatorList [15-83]
+            GqlMatch [15-53]
+              GraphPattern [21-53]
+                GraphPathPattern [21-53]
+                  GraphNodePattern [21-53]
+                    GraphElementPatternFiller [22-52]
+                      Identifier(person) [22-28]
+                      GraphLabelFilter [28-35]
+                        GraphElementLabel [29-35]
+                          Identifier(Person) [29-35]
+                      GraphPropertySpecification [36-52]
+                        GraphPropertyNameAndValue [37-51]
+                          Identifier(person_age) [37-47]
+                          IntLiteral(43) [49-51]
+            GqlReturn [54-83]
+              Select [61-83]
+                SelectList [61-83]
+                  SelectColumn [61-70]
+                    PathExpression [61-70]
+                      Identifier(person) [61-67]
+                      Identifier(id) [68-70]
+                  SelectColumn [72-83]
+                    PathExpression [72-83]
+                      Identifier(person) [72-78]
+                      Identifier(name) [79-83]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#enforce-lowercase-label-values-and-property-names
+ALTER TABLE GraphNode ADD CONSTRAINT NodeLabelLowerCaseCheck
+CHECK(LOWER(label) = label)
+--
+AlterTableStatement [0-88]
+  PathExpression [12-21]
+    Identifier(GraphNode) [12-21]
+  AlterActionList [22-88]
+    AddConstraintAction [22-88]
+      CheckConstraint(ENFORCED) [37-88]
+        BinaryExpression(=) [67-87]
+          FunctionCall [67-79]
+            PathExpression [67-72]
+              Identifier(LOWER) [67-72]
+            PathExpression [73-78]
+              Identifier(label) [73-78]
+          PathExpression [82-87]
+            Identifier(label) [82-87]
+        Identifier(NodeLabelLowerCaseCheck) [37-60]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#enforce-lowercase-label-values-and-property-names
+ALTER TABLE GraphEdge ADD CONSTRAINT EdgePropertiesLowerCaseCheck
+CHECK(NOT array_includes(COALESCE(JSON_KEYS(properties, 1), []), key->key<>LOWER(key)))
+--
+AlterTableStatement [0-153]
+  PathExpression [12-21]
+    Identifier(GraphEdge) [12-21]
+  AlterActionList [22-153]
+    AddConstraintAction [22-153]
+      CheckConstraint(ENFORCED) [37-153]
+        UnaryExpression(NOT) [72-152]
+          FunctionCall [76-152]
+            PathExpression [76-90]
+              Identifier(array_includes) [76-90]
+            FunctionCall [91-129]
+              PathExpression [91-99]
+                Identifier(COALESCE) [91-99]
+              FunctionCall [100-124]
+                PathExpression [100-109]
+                  Identifier(JSON_KEYS) [100-109]
+                PathExpression [110-120]
+                  Identifier(properties) [110-120]
+                IntLiteral(1) [122-123]
+              ArrayConstructor [126-128]
+            Lambda [131-151]
+              PathExpression [131-134]
+                Identifier(key) [131-134]
+              BinaryExpression(<>) [136-151]
+                PathExpression [136-139]
+                  Identifier(key) [136-139]
+                FunctionCall [141-151]
+                  PathExpression [141-146]
+                    Identifier(LOWER) [141-146]
+                  PathExpression [147-150]
+                    Identifier(key) [147-150]
+        Identifier(EdgePropertiesLowerCaseCheck) [37-65]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#enforce-property-existence
+ALTER TABLE GraphNode
+ADD CONSTRAINT NameMustExistForPersonConstraint
+CHECK (IF(label = 'person', properties.name IS NOT NULL, TRUE))
+--
+AlterTableStatement [0-133]
+  PathExpression [12-21]
+    Identifier(GraphNode) [12-21]
+  AlterActionList [22-133]
+    AddConstraintAction [22-133]
+      CheckConstraint(ENFORCED) [37-133]
+        FunctionCall [77-132]
+          PathExpression [77-79]
+            Identifier(`IF`) [77-79]
+          BinaryExpression(=) [80-96]
+            PathExpression [80-85]
+              Identifier(label) [80-85]
+            StringLiteral [88-96]
+              StringLiteralComponent('person') [88-96]
+          BinaryExpression(IS NOT) [98-125]
+            PathExpression [98-113]
+              Identifier(properties) [98-108]
+              Identifier(name) [109-113]
+            NullLiteral(NULL) [121-125]
+          BooleanLiteral(TRUE) [127-131]
+        Identifier(NameMustExistForPersonConstraint) [37-69]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#enforce-property-uniqueness
+ALTER TABLE GraphNode
+ADD COLUMN person_name STRING(MAX)
+AS (IF(label = 'person', STRING(properties.name), NULL)) Hidden
+--
+AlterTableStatement [0-120]
+  PathExpression [12-21]
+    Identifier(GraphNode) [12-21]
+  AlterActionList [22-120]
+    AddColumnAction [22-120]
+      ColumnDefinition [33-120]
+        Identifier(person_name) [33-44]
+        SimpleColumnSchema [45-120]
+          PathExpression [45-51]
+            Identifier(STRING) [45-51]
+          TypeParameterList [51-55]
+            MaxLiteral() [52-55]
+          GeneratedColumnInfo(generated_mode=ALWAYS) [57-113]
+            FunctionCall [61-112]
+              PathExpression [61-63]
+                Identifier(`IF`) [61-63]
+              BinaryExpression(=) [64-80]
+                PathExpression [64-69]
+                  Identifier(label) [64-69]
+                StringLiteral [72-80]
+                  StringLiteralComponent('person') [72-80]
+              FunctionCall [82-105]
+                PathExpression [82-88]
+                  Identifier(STRING) [82-88]
+                PathExpression [89-104]
+                  Identifier(properties) [89-99]
+                  Identifier(name) [100-104]
+              NullLiteral(NULL) [107-111]
+          ColumnAttributeList [114-120]
+            HiddenColumnAttribute [114-120]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#enforce-property-uniqueness
+ALTER TABLE GraphNode
+ADD COLUMN person_country STRING(MAX)
+AS (IF(label = 'person', STRING(properties.country), NULL)) Hidden
+--
+AlterTableStatement [0-126]
+  PathExpression [12-21]
+    Identifier(GraphNode) [12-21]
+  AlterActionList [22-126]
+    AddColumnAction [22-126]
+      ColumnDefinition [33-126]
+        Identifier(person_country) [33-47]
+        SimpleColumnSchema [48-126]
+          PathExpression [48-54]
+            Identifier(STRING) [48-54]
+          TypeParameterList [54-58]
+            MaxLiteral() [55-58]
+          GeneratedColumnInfo(generated_mode=ALWAYS) [60-119]
+            FunctionCall [64-118]
+              PathExpression [64-66]
+                Identifier(`IF`) [64-66]
+              BinaryExpression(=) [67-83]
+                PathExpression [67-72]
+                  Identifier(label) [67-72]
+                StringLiteral [75-83]
+                  StringLiteralComponent('person') [75-83]
+              FunctionCall [85-111]
+                PathExpression [85-91]
+                  Identifier(STRING) [85-91]
+                PathExpression [92-110]
+                  Identifier(properties) [92-102]
+                  Identifier(country) [103-110]
+              NullLiteral(NULL) [113-117]
+          ColumnAttributeList [120-126]
+            HiddenColumnAttribute [120-126]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#enforce-property-uniqueness
+CREATE UNIQUE NULL_FILTERED INDEX NameAndCountryMustBeUniqueForPerson
+ON GraphNode (person_name, person_country)
+--
+CreateIndexStatement(UNIQUE) [0-112]
+  PathExpression [34-69]
+    Identifier(NameAndCountryMustBeUniqueForPerson) [34-69]
+  PathExpression [73-82]
+    Identifier(GraphNode) [73-82]
+  IndexItemList [83-112]
+    OrderingExpression(ASC) [84-95]
+      PathExpression [84-95]
+        Identifier(person_name) [84-95]
+    OrderingExpression(ASC) [97-111]
+      PathExpression [97-111]
+        Identifier(person_country) [97-111]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#enforce-property-data-type
+ALTER TABLE GraphNode
+ADD CONSTRAINT PersonNameMustBeStringTypeConstraint
+CHECK (IF(label = 'person', JSON_TYPE(properties.name) = 'string', TRUE))
+--
+AlterTableStatement [0-147]
+  PathExpression [12-21]
+    Identifier(GraphNode) [12-21]
+  AlterActionList [22-147]
+    AddConstraintAction [22-147]
+      CheckConstraint(ENFORCED) [37-147]
+        FunctionCall [81-146]
+          PathExpression [81-83]
+            Identifier(`IF`) [81-83]
+          BinaryExpression(=) [84-100]
+            PathExpression [84-89]
+              Identifier(label) [84-89]
+            StringLiteral [92-100]
+              StringLiteralComponent('person') [92-100]
+          BinaryExpression(=) [102-139]
+            FunctionCall [102-128]
+              PathExpression [102-111]
+                Identifier(JSON_TYPE) [102-111]
+              PathExpression [112-127]
+                Identifier(properties) [112-122]
+                Identifier(name) [123-127]
+            StringLiteral [131-139]
+              StringLiteralComponent('string') [131-139]
+          BooleanLiteral(TRUE) [141-145]
+        Identifier(PersonNameMustBeStringTypeConstraint) [37-73]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#combine-defined-and-dynamic-labels
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    GraphNode
+      LABEL Entity -- Defined label
+      DYNAMIC LABEL (label) -- Dynamic label from data column 'label'
+      DYNAMIC PROPERTIES (properties)
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [0-219]
+  PathExpression [33-41]
+    Identifier(FinGraph) [33-41]
+  GraphElementTableList [56-219]
+    GraphElementTable [62-215]
+      PathExpression [62-71]
+        Identifier(GraphNode) [62-71]
+      GraphElementLabelAndPropertiesList [78-90]
+        GraphElementLabelAndProperties [78-90]
+          Identifier(Entity) [84-90]
+          GraphProperties [90-90]
+      GraphDynamicLabel [129-134]
+        PathExpression [129-134]
+          Identifier(label) [129-134]
+      GraphDynamicProperties [204-214]
+        PathExpression [204-214]
+          Identifier(properties) [204-214]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#combine-defined-and-dynamic-labels
+GRAPH FinGraph
+MATCH (node:Entity {id: 1}) -- Querying by the defined label
+RETURN node.name
+--
+QueryStatement [0-92]
+  Query [0-92]
+    GqlQuery [0-92]
+      GraphTableQuery [0-92]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-92]
+          GqlOperatorList [15-92]
+            GqlMatch [15-42]
+              GraphPattern [21-42]
+                GraphPathPattern [21-42]
+                  GraphNodePattern [21-42]
+                    GraphElementPatternFiller [22-41]
+                      Identifier(node) [22-26]
+                      GraphLabelFilter [26-33]
+                        GraphElementLabel [27-33]
+                          Identifier(Entity) [27-33]
+                      GraphPropertySpecification [34-41]
+                        GraphPropertyNameAndValue [35-40]
+                          Identifier(id) [35-37]
+                          IntLiteral(1) [39-40]
+            GqlReturn [76-92]
+              Select [83-92]
+                SelectList [83-92]
+                  SelectColumn [83-92]
+                    PathExpression [83-92]
+                      Identifier(node) [83-87]
+                      Identifier(name) [88-92]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#schema-example
+CREATE TABLE GraphNode (
+  id INT64 NOT NULL,
+  label STRING(MAX) NOT NULL,
+  properties JSON
+) PRIMARY KEY (id)
+--
+CreateTableStatement [0-112]
+  PathExpression [13-22]
+    Identifier(GraphNode) [13-22]
+  TableElementList [23-95]
+    ColumnDefinition [27-44]
+      Identifier(id) [27-29]
+      SimpleColumnSchema [30-44]
+        PathExpression [30-35]
+          Identifier(INT64) [30-35]
+        ColumnAttributeList [36-44]
+          NotNullColumnAttribute [36-44]
+    ColumnDefinition [48-74]
+      Identifier(label) [48-53]
+      SimpleColumnSchema [54-74]
+        PathExpression [54-60]
+          Identifier(STRING) [54-60]
+        TypeParameterList [60-64]
+          MaxLiteral() [61-64]
+        ColumnAttributeList [66-74]
+          NotNullColumnAttribute [66-74]
+    ColumnDefinition [78-93]
+      Identifier(properties) [78-88]
+      SimpleColumnSchema [89-93]
+        PathExpression [89-93]
+          Identifier(JSON) [89-93]
+  SpannerTableOptions [96-112]
+    PrimaryKey(ENFORCED) [96-112]
+      PrimaryKeyElementList [108-112]
+        PrimaryKeyElement(ASC) [109-111]
+          Identifier(id) [109-111]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#schema-example
+CREATE INDEX R_EDGE ON GraphEdge (dest_id, id, edge_id) STORING (properties),
+INTERLEAVE IN GraphNode
+--
+CreateIndexStatement [0-101]
+  PathExpression [13-19]
+    Identifier(R_EDGE) [13-19]
+  PathExpression [23-32]
+    Identifier(GraphEdge) [23-32]
+  IndexItemList [33-55]
+    OrderingExpression(ASC) [34-41]
+      PathExpression [34-41]
+        Identifier(dest_id) [34-41]
+    OrderingExpression(ASC) [43-45]
+      PathExpression [43-45]
+        Identifier(id) [43-45]
+    OrderingExpression(ASC) [47-54]
+      PathExpression [47-54]
+        Identifier(edge_id) [47-54]
+  IndexStoringExpressionList [64-76]
+    PathExpression [65-75]
+      Identifier(properties) [65-75]
+  SpannerInterleaveClause [76-101]
+    PathExpression [92-101]
+      Identifier(GraphNode) [92-101]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#schema-example
+CREATE INDEX IDX_NODE_LABEL ON GraphNode (label)
+--
+CreateIndexStatement [0-48]
+  PathExpression [13-27]
+    Identifier(IDX_NODE_LABEL) [13-27]
+  PathExpression [31-40]
+    Identifier(GraphNode) [31-40]
+  IndexItemList [41-48]
+    OrderingExpression(ASC) [42-47]
+      PathExpression [42-47]
+        Identifier(label) [42-47]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#schema-example
+ALTER TABLE GraphNode ADD CONSTRAINT node_label_lower_case
+CHECK(LOWER(label) = label)
+--
+AlterTableStatement [0-86]
+  PathExpression [12-21]
+    Identifier(GraphNode) [12-21]
+  AlterActionList [22-86]
+    AddConstraintAction [22-86]
+      CheckConstraint(ENFORCED) [37-86]
+        BinaryExpression(=) [65-85]
+          FunctionCall [65-77]
+            PathExpression [65-70]
+              Identifier(LOWER) [65-70]
+            PathExpression [71-76]
+              Identifier(label) [71-76]
+          PathExpression [80-85]
+            Identifier(label) [80-85]
+        Identifier(node_label_lower_case) [37-58]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#schema-example
+ALTER TABLE GraphEdge ADD CONSTRAINT edge_label_lower_case
+CHECK(LOWER(label) = label)
+--
+AlterTableStatement [0-86]
+  PathExpression [12-21]
+    Identifier(GraphEdge) [12-21]
+  AlterActionList [22-86]
+    AddConstraintAction [22-86]
+      CheckConstraint(ENFORCED) [37-86]
+        BinaryExpression(=) [65-85]
+          FunctionCall [65-77]
+            PathExpression [65-70]
+              Identifier(LOWER) [65-70]
+            PathExpression [71-76]
+              Identifier(label) [71-76]
+          PathExpression [80-85]
+            Identifier(label) [80-85]
+        Identifier(edge_label_lower_case) [37-58]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#schema-example
+ALTER TABLE GraphNode ADD CONSTRAINT node_property_keys_lower_case
+CHECK(
+  NOT array_includes(COALESCE(JSON_KEYS(properties, 1), []), key->key<>LOWER(key)))
+--
+AlterTableStatement [0-157]
+  PathExpression [12-21]
+    Identifier(GraphNode) [12-21]
+  AlterActionList [22-157]
+    AddConstraintAction [22-157]
+      CheckConstraint(ENFORCED) [37-157]
+        UnaryExpression(NOT) [76-156]
+          FunctionCall [80-156]
+            PathExpression [80-94]
+              Identifier(array_includes) [80-94]
+            FunctionCall [95-133]
+              PathExpression [95-103]
+                Identifier(COALESCE) [95-103]
+              FunctionCall [104-128]
+                PathExpression [104-113]
+                  Identifier(JSON_KEYS) [104-113]
+                PathExpression [114-124]
+                  Identifier(properties) [114-124]
+                IntLiteral(1) [126-127]
+              ArrayConstructor [130-132]
+            Lambda [135-155]
+              PathExpression [135-138]
+                Identifier(key) [135-138]
+              BinaryExpression(<>) [140-155]
+                PathExpression [140-143]
+                  Identifier(key) [140-143]
+                FunctionCall [145-155]
+                  PathExpression [145-150]
+                    Identifier(LOWER) [145-150]
+                  PathExpression [151-154]
+                    Identifier(key) [151-154]
+        Identifier(node_property_keys_lower_case) [37-66]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#schema-example
+ALTER TABLE GraphEdge ADD CONSTRAINT edge_property_keys_lower_case
+CHECK(
+  NOT array_includes(COALESCE(JSON_KEYS(properties, 1), []), key->key<>LOWER(key)))
+--
+AlterTableStatement [0-157]
+  PathExpression [12-21]
+    Identifier(GraphEdge) [12-21]
+  AlterActionList [22-157]
+    AddConstraintAction [22-157]
+      CheckConstraint(ENFORCED) [37-157]
+        UnaryExpression(NOT) [76-156]
+          FunctionCall [80-156]
+            PathExpression [80-94]
+              Identifier(array_includes) [80-94]
+            FunctionCall [95-133]
+              PathExpression [95-103]
+                Identifier(COALESCE) [95-103]
+              FunctionCall [104-128]
+                PathExpression [104-113]
+                  Identifier(JSON_KEYS) [104-113]
+                PathExpression [114-124]
+                  Identifier(properties) [114-124]
+                IntLiteral(1) [126-127]
+              ArrayConstructor [130-132]
+            Lambda [135-155]
+              PathExpression [135-138]
+                Identifier(key) [135-138]
+              BinaryExpression(<>) [140-155]
+                PathExpression [140-143]
+                  Identifier(key) [140-143]
+                FunctionCall [145-155]
+                  PathExpression [145-150]
+                    Identifier(LOWER) [145-150]
+                  PathExpression [151-154]
+                    Identifier(key) [151-154]
+        Identifier(edge_property_keys_lower_case) [37-66]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#property_appears_multiple_times_in_the_to_json_result
+GRAPH FinGraph
+MATCH (n: Person {id: 14})
+RETURN SAFE_TO_JSON(n) AS n
+--
+QueryStatement [0-69]
+  Query [0-69]
+    GqlQuery [0-69]
+      GraphTableQuery [0-69]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-69]
+          GqlOperatorList [15-69]
+            GqlMatch [15-41]
+              GraphPattern [21-41]
+                GraphPathPattern [21-41]
+                  GraphNodePattern [21-41]
+                    GraphElementPatternFiller [22-40]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [25-31]
+                          Identifier(Person) [25-31]
+                      GraphPropertySpecification [32-40]
+                        GraphPropertyNameAndValue [33-39]
+                          Identifier(id) [33-35]
+                          IntLiteral(14) [37-39]
+            GqlReturn [42-69]
+              Select [49-69]
+                SelectList [49-69]
+                  SelectColumn [49-69]
+                    FunctionCall [49-64]
+                      PathExpression [49-61]
+                        Identifier(SAFE_TO_JSON) [49-61]
+                      PathExpression [62-63]
+                        Identifier(n) [62-63]
+                    Alias [65-69]
+                      Identifier(n) [68-69]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#property_appears_multiple_times_in_the_to_json_result
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    GraphNode
+      PROPERTIES ALL COLUMNS EXCEPT (properties)
+      DYNAMIC LABEL (label)
+      DYNAMIC PROPERTIES (properties)
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [0-190]
+  PathExpression [33-41]
+    Identifier(FinGraph) [33-41]
+  GraphElementTableList [56-190]
+    GraphElementTable [62-186]
+      PathExpression [62-71]
+        Identifier(GraphNode) [62-71]
+      GraphElementLabelAndPropertiesList [78-120]
+        GraphElementLabelAndProperties [78-120]
+          GraphProperties [78-120]
+            ColumnList [108-120]
+              Identifier(properties) [109-119]
+      GraphDynamicLabel [142-147]
+        PathExpression [142-147]
+          Identifier(label) [142-147]
+      GraphDynamicProperties [175-185]
+        PathExpression [175-185]
+          Identifier(properties) [175-185]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#property_appears_multiple_times_in_the_to_json_result
+GRAPH FinGraph
+MATCH (n: Person {id: 1})
+RETURN TO_JSON(n) AS n
+--
+QueryStatement [0-63]
+  Query [0-63]
+    GqlQuery [0-63]
+      GraphTableQuery [0-63]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-63]
+          GqlOperatorList [15-63]
+            GqlMatch [15-40]
+              GraphPattern [21-40]
+                GraphPathPattern [21-40]
+                  GraphNodePattern [21-40]
+                    GraphElementPatternFiller [22-39]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [25-31]
+                          Identifier(Person) [25-31]
+                      GraphPropertySpecification [32-39]
+                        GraphPropertyNameAndValue [33-38]
+                          Identifier(id) [33-35]
+                          IntLiteral(1) [37-38]
+            GqlReturn [41-63]
+              Select [48-63]
+                SelectList [48-63]
+                  SelectColumn [48-63]
+                    FunctionCall [48-58]
+                      PathExpression [48-55]
+                        Identifier(TO_JSON) [48-55]
+                      PathExpression [56-57]
+                        Identifier(n) [56-57]
+                    Alias [59-63]
+                      Identifier(n) [62-63]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#comparison-on-property-value-without-conversion
+GRAPH FinGraph
+MATCH (p:Person)
+WHERE p.name = "Alex"
+RETURN p.id
+--
+QueryStatement [0-65]
+  Query [0-65]
+    GqlQuery [0-65]
+      GraphTableQuery [0-65]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-65]
+          GqlOperatorList [15-65]
+            GqlMatch [15-53]
+              GraphPattern [21-53]
+                GraphPathPattern [21-31]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                WhereClause [32-53]
+                  BinaryExpression(=) [38-53]
+                    PathExpression [38-44]
+                      Identifier(p) [38-39]
+                      Identifier(name) [40-44]
+                    StringLiteral [47-53]
+                      StringLiteralComponent("Alex") [47-53]
+            GqlReturn [54-65]
+              Select [61-65]
+                SelectList [61-65]
+                  SelectColumn [61-65]
+                    PathExpression [61-65]
+                      Identifier(p) [61-62]
+                      Identifier(id) [63-65]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#comparison-on-property-value-without-conversion
+GRAPH FinGraph
+MATCH (p:Person)
+WHERE STRING(p.name) = "Alex"
+RETURN p.id
+--
+QueryStatement [0-73]
+  Query [0-73]
+    GqlQuery [0-73]
+      GraphTableQuery [0-73]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-73]
+          GqlOperatorList [15-73]
+            GqlMatch [15-61]
+              GraphPattern [21-61]
+                GraphPathPattern [21-31]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                WhereClause [32-61]
+                  BinaryExpression(=) [38-61]
+                    FunctionCall [38-52]
+                      PathExpression [38-44]
+                        Identifier(STRING) [38-44]
+                      PathExpression [45-51]
+                        Identifier(p) [45-46]
+                        Identifier(name) [47-51]
+                    StringLiteral [55-61]
+                      StringLiteralComponent("Alex") [55-61]
+            GqlReturn [62-73]
+              Select [69-73]
+                SelectList [69-73]
+                  SelectColumn [69-73]
+                    PathExpression [69-73]
+                      Identifier(p) [69-70]
+                      Identifier(id) [71-73]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#comparison-on-property-value-without-conversion
+GRAPH FinGraph
+MATCH (p:Person {name: 'Alex'})
+RETURN p.id
+--
+QueryStatement [0-58]
+  Query [0-58]
+    GqlQuery [0-58]
+      GraphTableQuery [0-58]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-58]
+          GqlOperatorList [15-58]
+            GqlMatch [15-46]
+              GraphPattern [21-46]
+                GraphPathPattern [21-46]
+                  GraphNodePattern [21-46]
+                    GraphElementPatternFiller [22-45]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                      GraphPropertySpecification [31-45]
+                        GraphPropertyNameAndValue [32-44]
+                          Identifier(name) [32-36]
+                          StringLiteral [38-44]
+                            StringLiteralComponent('Alex') [38-44]
+            GqlReturn [47-58]
+              Select [54-58]
+                SelectList [54-58]
+                  SelectColumn [54-58]
+                    PathExpression [54-58]
+                      Identifier(p) [54-55]
+                      Identifier(id) [56-58]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#return-distinct-property-value-without-conversion
+GRAPH FinGraph
+MATCH -[t:Transfers]->
+RETURN DISTINCT t.order_number_str AS order_number_str
+--
+QueryStatement [0-92]
+  Query [0-92]
+    GqlQuery [0-92]
+      GraphTableQuery [0-92]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-92]
+          GqlOperatorList [15-92]
+            GqlMatch [15-37]
+              GraphPattern [21-37]
+                GraphPathPattern [21-37]
+                  GraphEdgePattern [21-37]
+                    GraphElementPatternFiller [23-34]
+                      Identifier(t) [23-24]
+                      GraphLabelFilter [24-34]
+                        GraphElementLabel [25-34]
+                          Identifier(Transfers) [25-34]
+            GqlReturn [38-92]
+              Select(distinct=true) [45-92]
+                SelectList [54-92]
+                  SelectColumn [54-92]
+                    PathExpression [54-72]
+                      Identifier(t) [54-55]
+                      Identifier(order_number_str) [56-72]
+                    Alias [73-92]
+                      Identifier(order_number_str) [76-92]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#return-distinct-property-value-without-conversion
+GRAPH FinGraph
+MATCH -[t:Transfers]->
+RETURN DISTINCT STRING(t.order_number_str) AS order_number_str
+--
+QueryStatement [0-100]
+  Query [0-100]
+    GqlQuery [0-100]
+      GraphTableQuery [0-100]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-100]
+          GqlOperatorList [15-100]
+            GqlMatch [15-37]
+              GraphPattern [21-37]
+                GraphPathPattern [21-37]
+                  GraphEdgePattern [21-37]
+                    GraphElementPatternFiller [23-34]
+                      Identifier(t) [23-24]
+                      GraphLabelFilter [24-34]
+                        GraphElementLabel [25-34]
+                          Identifier(Transfers) [25-34]
+            GqlReturn [38-100]
+              Select(distinct=true) [45-100]
+                SelectList [54-100]
+                  SelectColumn [54-100]
+                    FunctionCall [54-80]
+                      PathExpression [54-60]
+                        Identifier(STRING) [54-60]
+                      PathExpression [61-79]
+                        Identifier(t) [61-62]
+                        Identifier(order_number_str) [63-79]
+                    Alias [81-100]
+                      Identifier(order_number_str) [84-100]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#use-property-as-grouping-key-without-conversion
+GRAPH FinGraph
+MATCH (a:Account)-[t:Transfers]->(b:Account)
+RETURN t.order_number_str, COUNT(*) AS total_transfers
+--
+QueryStatement [0-114]
+  Query [0-114]
+    GqlQuery [0-114]
+      GraphTableQuery [0-114]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-114]
+          GqlOperatorList [15-114]
+            GqlMatch [15-59]
+              GraphPattern [21-59]
+                GraphPathPattern [21-59]
+                  GraphNodePattern [21-32]
+                    GraphElementPatternFiller [22-31]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                  GraphEdgePattern [32-48]
+                    GraphElementPatternFiller [34-45]
+                      Identifier(t) [34-35]
+                      GraphLabelFilter [35-45]
+                        GraphElementLabel [36-45]
+                          Identifier(Transfers) [36-45]
+                  GraphNodePattern [48-59]
+                    GraphElementPatternFiller [49-58]
+                      Identifier(b) [49-50]
+                      GraphLabelFilter [50-58]
+                        GraphElementLabel [51-58]
+                          Identifier(Account) [51-58]
+            GqlReturn [60-114]
+              Select [67-114]
+                SelectList [67-114]
+                  SelectColumn [67-85]
+                    PathExpression [67-85]
+                      Identifier(t) [67-68]
+                      Identifier(order_number_str) [69-85]
+                  SelectColumn [87-114]
+                    FunctionCall [87-95]
+                      PathExpression [87-92]
+                        Identifier(COUNT) [87-92]
+                      Star(*) [93-94]
+                    Alias [96-114]
+                      Identifier(total_transfers) [99-114]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#use-property-as-grouping-key-without-conversion
+GRAPH FinGraph
+MATCH (a:Account)-[t:Transfers]->(b:Account)
+RETURN STRING(t.order_number_str) AS order_number_str, COUNT(*) AS total_transfers
+--
+QueryStatement [0-142]
+  Query [0-142]
+    GqlQuery [0-142]
+      GraphTableQuery [0-142]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-142]
+          GqlOperatorList [15-142]
+            GqlMatch [15-59]
+              GraphPattern [21-59]
+                GraphPathPattern [21-59]
+                  GraphNodePattern [21-32]
+                    GraphElementPatternFiller [22-31]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                  GraphEdgePattern [32-48]
+                    GraphElementPatternFiller [34-45]
+                      Identifier(t) [34-35]
+                      GraphLabelFilter [35-45]
+                        GraphElementLabel [36-45]
+                          Identifier(Transfers) [36-45]
+                  GraphNodePattern [48-59]
+                    GraphElementPatternFiller [49-58]
+                      Identifier(b) [49-50]
+                      GraphLabelFilter [50-58]
+                        GraphElementLabel [51-58]
+                          Identifier(Account) [51-58]
+            GqlReturn [60-142]
+              Select [67-142]
+                SelectList [67-142]
+                  SelectColumn [67-113]
+                    FunctionCall [67-93]
+                      PathExpression [67-73]
+                        Identifier(STRING) [67-73]
+                      PathExpression [74-92]
+                        Identifier(t) [74-75]
+                        Identifier(order_number_str) [76-92]
+                    Alias [94-113]
+                      Identifier(order_number_str) [97-113]
+                  SelectColumn [115-142]
+                    FunctionCall [115-123]
+                      PathExpression [115-120]
+                        Identifier(COUNT) [115-120]
+                      Star(*) [121-122]
+                    Alias [124-142]
+                      Identifier(total_transfers) [127-142]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#use-property-as-ordering-key-without-conversion
+GRAPH FinGraph
+MATCH (a:Account)-[t:Transfers]->(b:Account)
+RETURN a.Id AS from_account, b.Id AS to_account, t.amount
+ORDER BY t.amount DESC
+LIMIT 1
+--
+QueryStatement [0-148]
+  Query [0-148]
+    GqlQuery [0-148]
+      GraphTableQuery [0-148]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-148]
+          GqlOperatorList [15-148]
+            GqlMatch [15-59]
+              GraphPattern [21-59]
+                GraphPathPattern [21-59]
+                  GraphNodePattern [21-32]
+                    GraphElementPatternFiller [22-31]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                  GraphEdgePattern [32-48]
+                    GraphElementPatternFiller [34-45]
+                      Identifier(t) [34-35]
+                      GraphLabelFilter [35-45]
+                        GraphElementLabel [36-45]
+                          Identifier(Transfers) [36-45]
+                  GraphNodePattern [48-59]
+                    GraphElementPatternFiller [49-58]
+                      Identifier(b) [49-50]
+                      GraphLabelFilter [50-58]
+                        GraphElementLabel [51-58]
+                          Identifier(Account) [51-58]
+            GqlReturn [60-148]
+              Select [67-117]
+                SelectList [67-117]
+                  SelectColumn [67-87]
+                    PathExpression [67-71]
+                      Identifier(a) [67-68]
+                      Identifier(Id) [69-71]
+                    Alias [72-87]
+                      Identifier(from_account) [75-87]
+                  SelectColumn [89-107]
+                    PathExpression [89-93]
+                      Identifier(b) [89-90]
+                      Identifier(Id) [91-93]
+                    Alias [94-107]
+                      Identifier(to_account) [97-107]
+                  SelectColumn [109-117]
+                    PathExpression [109-117]
+                      Identifier(t) [109-110]
+                      Identifier(amount) [111-117]
+              GqlOrderByAndPage [118-148]
+                OrderBy [118-140]
+                  OrderingExpression(DESC) [127-140]
+                    PathExpression [127-135]
+                      Identifier(t) [127-128]
+                      Identifier(amount) [129-135]
+                GqlPage [141-148]
+                  GqlPageLimit [141-148]
+                    IntLiteral(1) [147-148]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#use-property-as-ordering-key-without-conversion
+GRAPH FinGraph
+MATCH (a:Account)-[t:Transfers]->(b:Account)
+RETURN a.Id AS from_account, b.Id AS to_account, t.amount
+ORDER BY DOUBLE(t.amount) DESC
+LIMIT 1
+--
+QueryStatement [0-156]
+  Query [0-156]
+    GqlQuery [0-156]
+      GraphTableQuery [0-156]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-156]
+          GqlOperatorList [15-156]
+            GqlMatch [15-59]
+              GraphPattern [21-59]
+                GraphPathPattern [21-59]
+                  GraphNodePattern [21-32]
+                    GraphElementPatternFiller [22-31]
+                      Identifier(a) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                  GraphEdgePattern [32-48]
+                    GraphElementPatternFiller [34-45]
+                      Identifier(t) [34-35]
+                      GraphLabelFilter [35-45]
+                        GraphElementLabel [36-45]
+                          Identifier(Transfers) [36-45]
+                  GraphNodePattern [48-59]
+                    GraphElementPatternFiller [49-58]
+                      Identifier(b) [49-50]
+                      GraphLabelFilter [50-58]
+                        GraphElementLabel [51-58]
+                          Identifier(Account) [51-58]
+            GqlReturn [60-156]
+              Select [67-117]
+                SelectList [67-117]
+                  SelectColumn [67-87]
+                    PathExpression [67-71]
+                      Identifier(a) [67-68]
+                      Identifier(Id) [69-71]
+                    Alias [72-87]
+                      Identifier(from_account) [75-87]
+                  SelectColumn [89-107]
+                    PathExpression [89-93]
+                      Identifier(b) [89-90]
+                      Identifier(Id) [91-93]
+                    Alias [94-107]
+                      Identifier(to_account) [97-107]
+                  SelectColumn [109-117]
+                    PathExpression [109-117]
+                      Identifier(t) [109-110]
+                      Identifier(amount) [111-117]
+              GqlOrderByAndPage [118-156]
+                OrderBy [118-148]
+                  OrderingExpression(DESC) [127-148]
+                    FunctionCall [127-143]
+                      PathExpression [127-133]
+                        Identifier(DOUBLE) [127-133]
+                      PathExpression [134-142]
+                        Identifier(t) [134-135]
+                        Identifier(amount) [136-142]
+                GqlPage [149-156]
+                  GqlPageLimit [149-156]
+                    IntLiteral(1) [155-156]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#type-mismatch-during-conversion
+GRAPH FinGraph
+MATCH -[e:Transfers]->
+WHERE INT64(e.order_number_str) = 302290001255747
+RETURN e.amount
+--
+QueryStatement [0-103]
+  Query [0-103]
+    GqlQuery [0-103]
+      GraphTableQuery [0-103]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-103]
+          GqlOperatorList [15-103]
+            GqlMatch [15-87]
+              GraphPattern [21-87]
+                GraphPathPattern [21-37]
+                  GraphEdgePattern [21-37]
+                    GraphElementPatternFiller [23-34]
+                      Identifier(e) [23-24]
+                      GraphLabelFilter [24-34]
+                        GraphElementLabel [25-34]
+                          Identifier(Transfers) [25-34]
+                WhereClause [38-87]
+                  BinaryExpression(=) [44-87]
+                    FunctionCall [44-69]
+                      PathExpression [44-49]
+                        Identifier(INT64) [44-49]
+                      PathExpression [50-68]
+                        Identifier(e) [50-51]
+                        Identifier(order_number_str) [52-68]
+                    IntLiteral(302290001255747) [72-87]
+            GqlReturn [88-103]
+              Select [95-103]
+                SelectList [95-103]
+                  SelectColumn [95-103]
+                    PathExpression [95-103]
+                      Identifier(e) [95-96]
+                      Identifier(amount) [97-103]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#type-mismatch-during-conversion
+GRAPH FinGraph
+MATCH -[e:Transfers]->
+WHERE STRING(e.order_number_str) = "302290001255747"
+RETURN e.amount
+--
+QueryStatement [0-106]
+  Query [0-106]
+    GqlQuery [0-106]
+      GraphTableQuery [0-106]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-106]
+          GqlOperatorList [15-106]
+            GqlMatch [15-90]
+              GraphPattern [21-90]
+                GraphPathPattern [21-37]
+                  GraphEdgePattern [21-37]
+                    GraphElementPatternFiller [23-34]
+                      Identifier(e) [23-24]
+                      GraphLabelFilter [24-34]
+                        GraphElementLabel [25-34]
+                          Identifier(Transfers) [25-34]
+                WhereClause [38-90]
+                  BinaryExpression(=) [44-90]
+                    FunctionCall [44-70]
+                      PathExpression [44-50]
+                        Identifier(STRING) [44-50]
+                      PathExpression [51-69]
+                        Identifier(e) [51-52]
+                        Identifier(order_number_str) [53-69]
+                    StringLiteral [73-90]
+                      StringLiteralComponent("302290001255747") [73-90]
+            GqlReturn [91-106]
+              Select [98-106]
+                SelectList [98-106]
+                  SelectColumn [98-106]
+                    PathExpression [98-106]
+                      Identifier(e) [98-99]
+                      Identifier(amount) [100-106]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/manage-schemaless-data#type-mismatch-during-conversion
+GRAPH FinGraph
+MATCH -[e:Transfers]->
+WHERE LAX_INT64(e.order_number_str) = 302290001255747
+RETURN e.amount
+--
+QueryStatement [0-107]
+  Query [0-107]
+    GqlQuery [0-107]
+      GraphTableQuery [0-107]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-107]
+          GqlOperatorList [15-107]
+            GqlMatch [15-91]
+              GraphPattern [21-91]
+                GraphPathPattern [21-37]
+                  GraphEdgePattern [21-37]
+                    GraphElementPatternFiller [23-34]
+                      Identifier(e) [23-24]
+                      GraphLabelFilter [24-34]
+                        GraphElementLabel [25-34]
+                          Identifier(Transfers) [25-34]
+                WhereClause [38-91]
+                  BinaryExpression(=) [44-91]
+                    FunctionCall [44-73]
+                      PathExpression [44-53]
+                        Identifier(LAX_INT64) [44-53]
+                      PathExpression [54-72]
+                        Identifier(e) [54-55]
+                        Identifier(order_number_str) [56-72]
+                    IntLiteral(302290001255747) [76-91]
+            GqlReturn [92-107]
+              Select [99-107]
+                SelectList [99-107]
+                  SelectColumn [99-107]
+                    PathExpression [99-107]
+                      Identifier(e) [99-100]
+                      Identifier(amount) [101-107]
diff --git a/parser/testdata/spanner/guides/graph.perform-vector-similarity-search.metadata.json b/parser/testdata/spanner/guides/graph.perform-vector-similarity-search.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.perform-vector-similarity-search.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/graph.perform-vector-similarity-search.spanner.sql b/parser/testdata/spanner/guides/graph.perform-vector-similarity-search.spanner.sql
new file mode 100644
index 0000000..2b6e5e2
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.perform-vector-similarity-search.spanner.sql
@@ -0,0 +1,28 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/perform-vector-similarity-search
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/graph/perform-vector-similarity-search#insert-additional-vector-data
+# id d8d4d1cfa615: ARRAY(vector_length=>N) (graph.perform-vector-similarity-search)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected identifier "vector_length" [at 2:48]
+ALTER TABLE Account
+ADD COLUMN nick_name_embeddings ARRAY(vector_length=>4);
+
+# https://docs.cloud.google.com/spanner/docs/graph/perform-vector-similarity-search#insert-additional-vector-data
+# id 747de5553744: ARRAY(vector_length=>N) (graph.perform-vector-similarity-search)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected identifier "vector_length" [at 2:44]
+ALTER TABLE AccountTransferAccount
+ADD COLUMN notes_embeddings ARRAY(vector_length=>4);
+
+# https://docs.cloud.google.com/spanner/docs/graph/perform-vector-similarity-search#create-vector-index-find-ann
+# id 31c46e52a9b5: vector index clauses (graph.perform-vector-similarity-search)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ";" or end of input but got keyword WHERE [at 3:1]
+CREATE VECTOR INDEX NickNameEmbeddingIndex
+ON Account(nick_name_embeddings)
+WHERE nick_name_embeddings IS NOT NULL
+OPTIONS (distance_type = 'EUCLIDEAN', tree_depth = 2, num_leaves = 1000);
diff --git a/parser/testdata/spanner/guides/graph.perform-vector-similarity-search.test b/parser/testdata/spanner/guides/graph.perform-vector-similarity-search.test
new file mode 100644
index 0000000..dd6940c
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.perform-vector-similarity-search.test
@@ -0,0 +1,589 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/perform-vector-similarity-search
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/graph/perform-vector-similarity-search#insert-additional-vector-data
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+UPDATE Account SET nick_name = "Fund for a refreshing tropical vacation" WHERE id = 7
+--
+UpdateStatement [0-85]
+  PathExpression [7-14]
+    Identifier(Account) [7-14]
+  UpdateItemList [19-72]
+    UpdateItem [19-72]
+      UpdateSetValue [19-72]
+        PathExpression [19-28]
+          Identifier(nick_name) [19-28]
+        StringLiteral [31-72]
+          StringLiteralComponent("Fund for a refreshing tropical vacation") [31-72]
+  BinaryExpression(=) [79-85]
+    PathExpression [79-81]
+      Identifier(id) [79-81]
+    IntLiteral(7) [84-85]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/perform-vector-similarity-search#insert-additional-vector-data
+UPDATE Account SET nick_name = "Fund for a rainy day!" WHERE id = 16
+--
+UpdateStatement [0-68]
+  PathExpression [7-14]
+    Identifier(Account) [7-14]
+  UpdateItemList [19-54]
+    UpdateItem [19-54]
+      UpdateSetValue [19-54]
+        PathExpression [19-28]
+          Identifier(nick_name) [19-28]
+        StringLiteral [31-54]
+          StringLiteralComponent("Fund for a rainy day!") [31-54]
+  BinaryExpression(=) [61-68]
+    PathExpression [61-63]
+      Identifier(id) [61-63]
+    IntLiteral(16) [66-68]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/perform-vector-similarity-search#insert-additional-vector-data
+UPDATE Account SET nick_name = "Saving up for travel" WHERE id = 20
+--
+UpdateStatement [0-67]
+  PathExpression [7-14]
+    Identifier(Account) [7-14]
+  UpdateItemList [19-53]
+    UpdateItem [19-53]
+      UpdateSetValue [19-53]
+        PathExpression [19-28]
+          Identifier(nick_name) [19-28]
+        StringLiteral [31-53]
+          StringLiteralComponent("Saving up for travel") [31-53]
+  BinaryExpression(=) [60-67]
+    PathExpression [60-62]
+      Identifier(id) [60-62]
+    IntLiteral(20) [65-67]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/perform-vector-similarity-search#insert-additional-vector-data
+UPDATE Account SET nick_name_embeddings = ARRAY[0.3, 0.5, 0.8, 0.7] WHERE id = 7
+--
+UpdateStatement [0-89]
+  PathExpression [7-14]
+    Identifier(Account) [7-14]
+  UpdateItemList [19-76]
+    UpdateItem [19-76]
+      UpdateSetValue [19-76]
+        PathExpression [19-39]
+          Identifier(nick_name_embeddings) [19-39]
+        ArrayConstructor [42-76]
+          ArrayType [42-56]
+            SimpleType [48-55]
+              PathExpression [48-55]
+                Identifier(FLOAT32) [48-55]
+          FloatLiteral(0.3) [57-60]
+          FloatLiteral(0.5) [62-65]
+          FloatLiteral(0.8) [67-70]
+          FloatLiteral(0.7) [72-75]
+  BinaryExpression(=) [83-89]
+    PathExpression [83-85]
+      Identifier(id) [83-85]
+    IntLiteral(7) [88-89]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/perform-vector-similarity-search#insert-additional-vector-data
+UPDATE Account SET nick_name_embeddings = ARRAY[0.4, 0.9, 0.7, 0.1] WHERE id = 16
+--
+UpdateStatement [0-90]
+  PathExpression [7-14]
+    Identifier(Account) [7-14]
+  UpdateItemList [19-76]
+    UpdateItem [19-76]
+      UpdateSetValue [19-76]
+        PathExpression [19-39]
+          Identifier(nick_name_embeddings) [19-39]
+        ArrayConstructor [42-76]
+          ArrayType [42-56]
+            SimpleType [48-55]
+              PathExpression [48-55]
+                Identifier(FLOAT32) [48-55]
+          FloatLiteral(0.4) [57-60]
+          FloatLiteral(0.9) [62-65]
+          FloatLiteral(0.7) [67-70]
+          FloatLiteral(0.1) [72-75]
+  BinaryExpression(=) [83-90]
+    PathExpression [83-85]
+      Identifier(id) [83-85]
+    IntLiteral(16) [88-90]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/perform-vector-similarity-search#insert-additional-vector-data
+UPDATE Account SET nick_name_embeddings = ARRAY[0.2, 0.5, 0.6, 0.6] WHERE id = 20
+--
+UpdateStatement [0-90]
+  PathExpression [7-14]
+    Identifier(Account) [7-14]
+  UpdateItemList [19-76]
+    UpdateItem [19-76]
+      UpdateSetValue [19-76]
+        PathExpression [19-39]
+          Identifier(nick_name_embeddings) [19-39]
+        ArrayConstructor [42-76]
+          ArrayType [42-56]
+            SimpleType [48-55]
+              PathExpression [48-55]
+                Identifier(FLOAT32) [48-55]
+          FloatLiteral(0.2) [57-60]
+          FloatLiteral(0.5) [62-65]
+          FloatLiteral(0.6) [67-70]
+          FloatLiteral(0.6) [72-75]
+  BinaryExpression(=) [83-90]
+    PathExpression [83-85]
+      Identifier(id) [83-85]
+    IntLiteral(20) [88-90]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/perform-vector-similarity-search#insert-additional-vector-data
+UPDATE AccountTransferAccount
+SET notes = "for shared cost of dinner",
+  notes_embeddings = ARRAY[0.3, 0.5, 0.8, 0.7]
+WHERE id = 16 AND to_id = 20
+--
+UpdateStatement [0-155]
+  PathExpression [7-29]
+    Identifier(AccountTransferAccount) [7-29]
+  UpdateItemList [34-126]
+    UpdateItem [34-69]
+      UpdateSetValue [34-69]
+        PathExpression [34-39]
+          Identifier(notes) [34-39]
+        StringLiteral [42-69]
+          StringLiteralComponent("for shared cost of dinner") [42-69]
+    UpdateItem [73-126]
+      UpdateSetValue [73-126]
+        PathExpression [73-89]
+          Identifier(notes_embeddings) [73-89]
+        ArrayConstructor [92-126]
+          ArrayType [92-106]
+            SimpleType [98-105]
+              PathExpression [98-105]
+                Identifier(FLOAT32) [98-105]
+          FloatLiteral(0.3) [107-110]
+          FloatLiteral(0.5) [112-115]
+          FloatLiteral(0.8) [117-120]
+          FloatLiteral(0.7) [122-125]
+  AndExpr [133-155]
+    BinaryExpression(=) [133-140]
+      PathExpression [133-135]
+        Identifier(id) [133-135]
+      IntLiteral(16) [138-140]
+    BinaryExpression(=) [145-155]
+      PathExpression [145-150]
+        Identifier(to_id) [145-150]
+      IntLiteral(20) [153-155]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/perform-vector-similarity-search#insert-additional-vector-data
+UPDATE AccountTransferAccount
+SET notes = "fees for tuition",
+  notes_embeddings = ARRAY[0.1, 0.9, 0.1, 0.7]
+WHERE id = 20 AND to_id = 7
+--
+UpdateStatement [0-145]
+  PathExpression [7-29]
+    Identifier(AccountTransferAccount) [7-29]
+  UpdateItemList [34-117]
+    UpdateItem [34-60]
+      UpdateSetValue [34-60]
+        PathExpression [34-39]
+          Identifier(notes) [34-39]
+        StringLiteral [42-60]
+          StringLiteralComponent("fees for tuition") [42-60]
+    UpdateItem [64-117]
+      UpdateSetValue [64-117]
+        PathExpression [64-80]
+          Identifier(notes_embeddings) [64-80]
+        ArrayConstructor [83-117]
+          ArrayType [83-97]
+            SimpleType [89-96]
+              PathExpression [89-96]
+                Identifier(FLOAT32) [89-96]
+          FloatLiteral(0.1) [98-101]
+          FloatLiteral(0.9) [103-106]
+          FloatLiteral(0.1) [108-111]
+          FloatLiteral(0.7) [113-116]
+  AndExpr [124-145]
+    BinaryExpression(=) [124-131]
+      PathExpression [124-126]
+        Identifier(id) [124-126]
+      IntLiteral(20) [129-131]
+    BinaryExpression(=) [136-145]
+      PathExpression [136-141]
+        Identifier(to_id) [136-141]
+      IntLiteral(7) [144-145]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/perform-vector-similarity-search#insert-additional-vector-data
+UPDATE AccountTransferAccount
+SET notes = 'loved the lunch',
+  notes_embeddings = ARRAY[0.4, 0.5, 0.7, 0.9]
+WHERE id = 20 AND to_id = 16
+--
+UpdateStatement [0-145]
+  PathExpression [7-29]
+    Identifier(AccountTransferAccount) [7-29]
+  UpdateItemList [34-116]
+    UpdateItem [34-59]
+      UpdateSetValue [34-59]
+        PathExpression [34-39]
+          Identifier(notes) [34-39]
+        StringLiteral [42-59]
+          StringLiteralComponent('loved the lunch') [42-59]
+    UpdateItem [63-116]
+      UpdateSetValue [63-116]
+        PathExpression [63-79]
+          Identifier(notes_embeddings) [63-79]
+        ArrayConstructor [82-116]
+          ArrayType [82-96]
+            SimpleType [88-95]
+              PathExpression [88-95]
+                Identifier(FLOAT32) [88-95]
+          FloatLiteral(0.4) [97-100]
+          FloatLiteral(0.5) [102-105]
+          FloatLiteral(0.7) [107-110]
+          FloatLiteral(0.9) [112-115]
+  AndExpr [123-145]
+    BinaryExpression(=) [123-130]
+      PathExpression [123-125]
+        Identifier(id) [123-125]
+      IntLiteral(20) [128-130]
+    BinaryExpression(=) [135-145]
+      PathExpression [135-140]
+        Identifier(to_id) [135-140]
+      IntLiteral(16) [143-145]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/perform-vector-similarity-search#insert-additional-vector-data
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+NODE TABLES (Account, Person)
+EDGE TABLES (
+  PersonOwnAccount
+    SOURCE KEY (id) REFERENCES Person (id)
+    DESTINATION KEY (account_id) REFERENCES Account (id)
+    LABEL Owns,
+  AccountTransferAccount
+    SOURCE KEY (id) REFERENCES Account (id)
+    DESTINATION KEY (to_id) REFERENCES Account (id)
+    LABEL Transfers
+)
+--
+CreatePropertyGraphStatement(is_or_replace) [0-363]
+  PathExpression [33-41]
+    Identifier(FinGraph) [33-41]
+  GraphElementTableList [54-71]
+    GraphElementTable [55-62]
+      PathExpression [55-62]
+        Identifier(Account) [55-62]
+      GraphElementLabelAndPropertiesList [62-62]
+        GraphElementLabelAndProperties [62-62]
+          GraphProperties [62-62]
+    GraphElementTable [64-70]
+      PathExpression [64-70]
+        Identifier(Person) [64-70]
+      GraphElementLabelAndPropertiesList [70-70]
+        GraphElementLabelAndProperties [70-70]
+          GraphProperties [70-70]
+  GraphElementTableList [84-363]
+    GraphElementTable [88-219]
+      PathExpression [88-104]
+        Identifier(PersonOwnAccount) [88-104]
+      GraphNodeTableReference(SOURCE) [109-147]
+        Identifier(Person) [136-142]
+        ColumnList [120-124]
+          Identifier(id) [121-123]
+        ColumnList [143-147]
+          Identifier(id) [144-146]
+      GraphNodeTableReference(DESTINATION) [152-204]
+        Identifier(Account) [192-199]
+        ColumnList [168-180]
+          Identifier(account_id) [169-179]
+        ColumnList [200-204]
+          Identifier(id) [201-203]
+      GraphElementLabelAndPropertiesList [209-219]
+        GraphElementLabelAndProperties [209-219]
+          Identifier(Owns) [215-219]
+          GraphProperties [219-219]
+    GraphElementTable [223-361]
+      PathExpression [223-245]
+        Identifier(AccountTransferAccount) [223-245]
+      GraphNodeTableReference(SOURCE) [250-289]
+        Identifier(Account) [277-284]
+        ColumnList [261-265]
+          Identifier(id) [262-264]
+        ColumnList [285-289]
+          Identifier(id) [286-288]
+      GraphNodeTableReference(DESTINATION) [294-341]
+        Identifier(Account) [329-336]
+        ColumnList [310-317]
+          Identifier(to_id) [311-316]
+        ColumnList [337-341]
+          Identifier(id) [338-340]
+      GraphElementLabelAndPropertiesList [346-361]
+        GraphElementLabelAndProperties [346-361]
+          Identifier(Transfers) [352-361]
+          GraphProperties [361-361]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/perform-vector-similarity-search#perform-knn-graph-nodes
+GRAPH FinGraph
+MATCH (p:Person)-[:Owns]->(a:Account)
+RETURN p.name, a.nick_name
+ORDER BY EUCLIDEAN_DISTANCE(a.nick_name_embeddings,
+  -- An illustrative embedding for 'accounts for leisure travel and vacation'
+  ARRAY[0.2, 0.4, 0.9, 0.6])
+LIMIT 2
+--
+QueryStatement [0-255]
+  Query [0-255]
+    GqlQuery [0-255]
+      GraphTableQuery [0-255]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-255]
+          GqlOperatorList [15-255]
+            GqlMatch [15-52]
+              GraphPattern [21-52]
+                GraphPathPattern [21-52]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                  GraphEdgePattern [31-41]
+                    GraphElementPatternFiller [33-38]
+                      GraphLabelFilter [33-38]
+                        GraphElementLabel [34-38]
+                          Identifier(Owns) [34-38]
+                  GraphNodePattern [41-52]
+                    GraphElementPatternFiller [42-51]
+                      Identifier(a) [42-43]
+                      GraphLabelFilter [43-51]
+                        GraphElementLabel [44-51]
+                          Identifier(Account) [44-51]
+            GqlReturn [53-255]
+              Select [60-79]
+                SelectList [60-79]
+                  SelectColumn [60-66]
+                    PathExpression [60-66]
+                      Identifier(p) [60-61]
+                      Identifier(name) [62-66]
+                  SelectColumn [68-79]
+                    PathExpression [68-79]
+                      Identifier(a) [68-69]
+                      Identifier(nick_name) [70-79]
+              GqlOrderByAndPage [80-255]
+                OrderBy [80-247]
+                  OrderingExpression(ASC) [89-247]
+                    FunctionCall [89-247]
+                      PathExpression [89-107]
+                        Identifier(EUCLIDEAN_DISTANCE) [89-107]
+                      PathExpression [108-130]
+                        Identifier(a) [108-109]
+                        Identifier(nick_name_embeddings) [110-130]
+                      ArrayConstructor [212-246]
+                        ArrayType [212-226]
+                          SimpleType [218-225]
+                            PathExpression [218-225]
+                              Identifier(FLOAT32) [218-225]
+                        FloatLiteral(0.2) [227-230]
+                        FloatLiteral(0.4) [232-235]
+                        FloatLiteral(0.9) [237-240]
+                        FloatLiteral(0.6) [242-245]
+                GqlPage [248-255]
+                  GqlPageLimit [248-255]
+                    IntLiteral(2) [254-255]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/perform-vector-similarity-search#perform-knn-graph-edges
+GRAPH FinGraph
+MATCH (p:Person)-[:Owns]->(:Account)-[t:Transfers]->(:Account)
+WHERE t.notes_embeddings IS NOT NULL
+RETURN p.name, t.notes
+ORDER BY EUCLIDEAN_DISTANCE(t.notes_embeddings,
+  -- An illustrative vector embedding for 'food expenses'
+  ARRAY[0.2, 0.4, 0.9, 0.6])
+LIMIT 2
+--
+QueryStatement [0-289]
+  Query [0-289]
+    GqlQuery [0-289]
+      GraphTableQuery [0-289]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-289]
+          GqlOperatorList [15-289]
+            GqlMatch [15-114]
+              GraphPattern [21-114]
+                GraphPathPattern [21-77]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                  GraphEdgePattern [31-41]
+                    GraphElementPatternFiller [33-38]
+                      GraphLabelFilter [33-38]
+                        GraphElementLabel [34-38]
+                          Identifier(Owns) [34-38]
+                  GraphNodePattern [41-51]
+                    GraphElementPatternFiller [42-50]
+                      GraphLabelFilter [42-50]
+                        GraphElementLabel [43-50]
+                          Identifier(Account) [43-50]
+                  GraphEdgePattern [51-67]
+                    GraphElementPatternFiller [53-64]
+                      Identifier(t) [53-54]
+                      GraphLabelFilter [54-64]
+                        GraphElementLabel [55-64]
+                          Identifier(Transfers) [55-64]
+                  GraphNodePattern [67-77]
+                    GraphElementPatternFiller [68-76]
+                      GraphLabelFilter [68-76]
+                        GraphElementLabel [69-76]
+                          Identifier(Account) [69-76]
+                WhereClause [78-114]
+                  BinaryExpression(IS NOT) [84-114]
+                    PathExpression [84-102]
+                      Identifier(t) [84-85]
+                      Identifier(notes_embeddings) [86-102]
+                    NullLiteral(NULL) [110-114]
+            GqlReturn [115-289]
+              Select [122-137]
+                SelectList [122-137]
+                  SelectColumn [122-128]
+                    PathExpression [122-128]
+                      Identifier(p) [122-123]
+                      Identifier(name) [124-128]
+                  SelectColumn [130-137]
+                    PathExpression [130-137]
+                      Identifier(t) [130-131]
+                      Identifier(notes) [132-137]
+              GqlOrderByAndPage [138-289]
+                OrderBy [138-281]
+                  OrderingExpression(ASC) [147-281]
+                    FunctionCall [147-281]
+                      PathExpression [147-165]
+                        Identifier(EUCLIDEAN_DISTANCE) [147-165]
+                      PathExpression [166-184]
+                        Identifier(t) [166-167]
+                        Identifier(notes_embeddings) [168-184]
+                      ArrayConstructor [246-280]
+                        ArrayType [246-260]
+                          SimpleType [252-259]
+                            PathExpression [252-259]
+                              Identifier(FLOAT32) [252-259]
+                        FloatLiteral(0.2) [261-264]
+                        FloatLiteral(0.4) [266-269]
+                        FloatLiteral(0.9) [271-274]
+                        FloatLiteral(0.6) [276-279]
+                GqlPage [282-289]
+                  GqlPageLimit [282-289]
+                    IntLiteral(2) [288-289]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/perform-vector-similarity-search#perform-ann
+GRAPH FinGraph
+MATCH (@{FORCE_INDEX=NickNameEmbeddingIndex} a:Account)
+WHERE a.nick_name_embeddings IS NOT NULL
+RETURN a, APPROX_EUCLIDEAN_DISTANCE(a.nick_name_embeddings,
+  -- An illustrative embedding for 'accounts for leisure travel and vacation'
+  ARRAY[0.2, 0.4, 0.9, 0.6],
+  options => JSON '{"num_leaves_to_search": 10}') AS distance
+ORDER BY distance
+LIMIT 2
+
+NEXT
+
+MATCH (p:Person)-[:Owns]->(a)
+RETURN p.name, a.nick_name
+--
+QueryStatement [0-439]
+  Query [0-439]
+    GqlQuery [0-439]
+      GraphTableQuery [0-439]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-439]
+          GqlOperatorList [15-375]
+            GqlMatch [15-111]
+              GraphPattern [21-111]
+                GraphPathPattern [21-70]
+                  GraphNodePattern [21-70]
+                    GraphElementPatternFiller [22-69]
+                      Identifier(a) [60-61]
+                      GraphLabelFilter [61-69]
+                        GraphElementLabel [62-69]
+                          Identifier(Account) [62-69]
+                      Hint [22-59]
+                        HintEntry [24-58]
+                          Identifier(FORCE_INDEX) [24-35]
+                          PathExpression [36-58]
+                            Identifier(NickNameEmbeddingIndex) [36-58]
+                WhereClause [71-111]
+                  BinaryExpression(IS NOT) [77-111]
+                    PathExpression [77-99]
+                      Identifier(a) [77-78]
+                      Identifier(nick_name_embeddings) [79-99]
+                    NullLiteral(NULL) [107-111]
+            GqlReturn [112-375]
+              Select [119-349]
+                SelectList [119-349]
+                  SelectColumn [119-120]
+                    PathExpression [119-120]
+                      Identifier(a) [119-120]
+                  SelectColumn [122-349]
+                    FunctionCall [122-337]
+                      PathExpression [122-147]
+                        Identifier(APPROX_EUCLIDEAN_DISTANCE) [122-147]
+                      PathExpression [148-170]
+                        Identifier(a) [148-149]
+                        Identifier(nick_name_embeddings) [150-170]
+                      ArrayConstructor [252-286]
+                        ArrayType [252-266]
+                          SimpleType [258-265]
+                            PathExpression [258-265]
+                              Identifier(FLOAT32) [258-265]
+                        FloatLiteral(0.2) [267-270]
+                        FloatLiteral(0.4) [272-275]
+                        FloatLiteral(0.9) [277-280]
+                        FloatLiteral(0.6) [282-285]
+                      NamedArgument [290-336]
+                        Identifier(options) [290-297]
+                        JSONLiteral [301-336]
+                          StringLiteral [306-336]
+                            StringLiteralComponent('{"num_leaves_to_search": 10}') [306-336]
+                    Alias [338-349]
+                      Identifier(distance) [341-349]
+              GqlOrderByAndPage [350-375]
+                OrderBy [350-367]
+                  OrderingExpression(ASC) [359-367]
+                    PathExpression [359-367]
+                      Identifier(distance) [359-367]
+                GqlPage [368-375]
+                  GqlPageLimit [368-375]
+                    IntLiteral(2) [374-375]
+          GqlOperatorList [383-439]
+            GqlMatch [383-412]
+              GraphPattern [389-412]
+                GraphPathPattern [389-412]
+                  GraphNodePattern [389-399]
+                    GraphElementPatternFiller [390-398]
+                      Identifier(p) [390-391]
+                      GraphLabelFilter [391-398]
+                        GraphElementLabel [392-398]
+                          Identifier(Person) [392-398]
+                  GraphEdgePattern [399-409]
+                    GraphElementPatternFiller [401-406]
+                      GraphLabelFilter [401-406]
+                        GraphElementLabel [402-406]
+                          Identifier(Owns) [402-406]
+                  GraphNodePattern [409-412]
+                    GraphElementPatternFiller [410-411]
+                      Identifier(a) [410-411]
+            GqlReturn [413-439]
+              Select [420-439]
+                SelectList [420-439]
+                  SelectColumn [420-426]
+                    PathExpression [420-426]
+                      Identifier(p) [420-421]
+                      Identifier(name) [422-426]
+                  SelectColumn [428-439]
+                    PathExpression [428-439]
+                      Identifier(a) [428-429]
+                      Identifier(nick_name) [430-439]
diff --git a/parser/testdata/spanner/guides/graph.queries-overview.test b/parser/testdata/spanner/guides/graph.queries-overview.test
new file mode 100644
index 0000000..27ab78d
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.queries-overview.test
@@ -0,0 +1,1770 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/queries-overview
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#find-all-nodes
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+GRAPH FinGraph
+MATCH (n)
+RETURN LABELS(n) AS label, n.id
+--
+QueryStatement [0-56]
+  Query [0-56]
+    GqlQuery [0-56]
+      GraphTableQuery [0-56]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-56]
+          GqlOperatorList [15-56]
+            GqlMatch [15-24]
+              GraphPattern [21-24]
+                GraphPathPattern [21-24]
+                  GraphNodePattern [21-24]
+                    GraphElementPatternFiller [22-23]
+                      Identifier(n) [22-23]
+            GqlReturn [25-56]
+              Select [32-56]
+                SelectList [32-56]
+                  SelectColumn [32-50]
+                    FunctionCall [32-41]
+                      PathExpression [32-38]
+                        Identifier(LABELS) [32-38]
+                      PathExpression [39-40]
+                        Identifier(n) [39-40]
+                    Alias [42-50]
+                      Identifier(label) [45-50]
+                  SelectColumn [52-56]
+                    PathExpression [52-56]
+                      Identifier(n) [52-53]
+                      Identifier(id) [54-56]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#find-all-nodes-specific-label
+GRAPH FinGraph
+MATCH (p:Person)
+RETURN LABELS(p) AS label, p.id, p.name
+--
+QueryStatement [0-71]
+  Query [0-71]
+    GqlQuery [0-71]
+      GraphTableQuery [0-71]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-71]
+          GqlOperatorList [15-71]
+            GqlMatch [15-31]
+              GraphPattern [21-31]
+                GraphPathPattern [21-31]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+            GqlReturn [32-71]
+              Select [39-71]
+                SelectList [39-71]
+                  SelectColumn [39-57]
+                    FunctionCall [39-48]
+                      PathExpression [39-45]
+                        Identifier(LABELS) [39-45]
+                      PathExpression [46-47]
+                        Identifier(p) [46-47]
+                    Alias [49-57]
+                      Identifier(label) [52-57]
+                  SelectColumn [59-63]
+                    PathExpression [59-63]
+                      Identifier(p) [59-60]
+                      Identifier(id) [61-63]
+                  SelectColumn [65-71]
+                    PathExpression [65-71]
+                      Identifier(p) [65-66]
+                      Identifier(name) [67-71]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#find-all-nodes-matching-label-expression
+GRAPH FinGraph
+MATCH (n:Person|Account)
+RETURN LABELS(n) AS label, n.id, n.birthday, n.create_time
+--
+QueryStatement [0-98]
+  Query [0-98]
+    GqlQuery [0-98]
+      GraphTableQuery [0-98]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-98]
+          GqlOperatorList [15-98]
+            GqlMatch [15-39]
+              GraphPattern [21-39]
+                GraphPathPattern [21-39]
+                  GraphNodePattern [21-39]
+                    GraphElementPatternFiller [22-38]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-38]
+                        GraphLabelOperation(OR) [24-38]
+                          GraphElementLabel [24-30]
+                            Identifier(Person) [24-30]
+                          GraphElementLabel [31-38]
+                            Identifier(Account) [31-38]
+            GqlReturn [40-98]
+              Select [47-98]
+                SelectList [47-98]
+                  SelectColumn [47-65]
+                    FunctionCall [47-56]
+                      PathExpression [47-53]
+                        Identifier(LABELS) [47-53]
+                      PathExpression [54-55]
+                        Identifier(n) [54-55]
+                    Alias [57-65]
+                      Identifier(label) [60-65]
+                  SelectColumn [67-71]
+                    PathExpression [67-71]
+                      Identifier(n) [67-68]
+                      Identifier(id) [69-71]
+                  SelectColumn [73-83]
+                    PathExpression [73-83]
+                      Identifier(n) [73-74]
+                      Identifier(birthday) [75-83]
+                  SelectColumn [85-98]
+                    PathExpression [85-98]
+                      Identifier(n) [85-86]
+                      Identifier(create_time) [87-98]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#find-all-nodes-matching-label-expr-property-filter
+GRAPH FinGraph
+MATCH (p:Person {id: 1})
+RETURN LABELS(p) AS label, p.id, p.name, p.birthday
+--
+QueryStatement [0-91]
+  Query [0-91]
+    GqlQuery [0-91]
+      GraphTableQuery [0-91]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-91]
+          GqlOperatorList [15-91]
+            GqlMatch [15-39]
+              GraphPattern [21-39]
+                GraphPathPattern [21-39]
+                  GraphNodePattern [21-39]
+                    GraphElementPatternFiller [22-38]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                      GraphPropertySpecification [31-38]
+                        GraphPropertyNameAndValue [32-37]
+                          Identifier(id) [32-34]
+                          IntLiteral(1) [36-37]
+            GqlReturn [40-91]
+              Select [47-91]
+                SelectList [47-91]
+                  SelectColumn [47-65]
+                    FunctionCall [47-56]
+                      PathExpression [47-53]
+                        Identifier(LABELS) [47-53]
+                      PathExpression [54-55]
+                        Identifier(p) [54-55]
+                    Alias [57-65]
+                      Identifier(label) [60-65]
+                  SelectColumn [67-71]
+                    PathExpression [67-71]
+                      Identifier(p) [67-68]
+                      Identifier(id) [69-71]
+                  SelectColumn [73-79]
+                    PathExpression [73-79]
+                      Identifier(p) [73-74]
+                      Identifier(name) [75-79]
+                  SelectColumn [81-91]
+                    PathExpression [81-91]
+                      Identifier(p) [81-82]
+                      Identifier(birthday) [83-91]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#find-all-nodes-matching-label-expr-property-filter
+GRAPH FinGraph
+MATCH (p:Person WHERE p.birthday < '1990-01-10')
+RETURN LABELS(p) AS label, p.name, p.birthday
+--
+QueryStatement [0-109]
+  Query [0-109]
+    GqlQuery [0-109]
+      GraphTableQuery [0-109]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-109]
+          GqlOperatorList [15-109]
+            GqlMatch [15-63]
+              GraphPattern [21-63]
+                GraphPathPattern [21-63]
+                  GraphNodePattern [21-63]
+                    GraphElementPatternFiller [22-62]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                      WhereClause [31-62]
+                        BinaryExpression(<) [37-62]
+                          PathExpression [37-47]
+                            Identifier(p) [37-38]
+                            Identifier(birthday) [39-47]
+                          StringLiteral [50-62]
+                            StringLiteralComponent('1990-01-10') [50-62]
+            GqlReturn [64-109]
+              Select [71-109]
+                SelectList [71-109]
+                  SelectColumn [71-89]
+                    FunctionCall [71-80]
+                      PathExpression [71-77]
+                        Identifier(LABELS) [71-77]
+                      PathExpression [78-79]
+                        Identifier(p) [78-79]
+                    Alias [81-89]
+                      Identifier(label) [84-89]
+                  SelectColumn [91-97]
+                    PathExpression [91-97]
+                      Identifier(p) [91-92]
+                      Identifier(name) [93-97]
+                  SelectColumn [99-109]
+                    PathExpression [99-109]
+                      Identifier(p) [99-100]
+                      Identifier(birthday) [101-109]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#find-all-edges-matching-label-expression
+GRAPH FinGraph
+MATCH -[e:Transfers]->
+RETURN e.Id as src_account, e.order_number
+--
+QueryStatement [0-80]
+  Query [0-80]
+    GqlQuery [0-80]
+      GraphTableQuery [0-80]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-80]
+          GqlOperatorList [15-80]
+            GqlMatch [15-37]
+              GraphPattern [21-37]
+                GraphPathPattern [21-37]
+                  GraphEdgePattern [21-37]
+                    GraphElementPatternFiller [23-34]
+                      Identifier(e) [23-24]
+                      GraphLabelFilter [24-34]
+                        GraphElementLabel [25-34]
+                          Identifier(Transfers) [25-34]
+            GqlReturn [38-80]
+              Select [45-80]
+                SelectList [45-80]
+                  SelectColumn [45-64]
+                    PathExpression [45-49]
+                      Identifier(e) [45-46]
+                      Identifier(Id) [47-49]
+                    Alias [50-64]
+                      Identifier(src_account) [53-64]
+                  SelectColumn [66-80]
+                    PathExpression [66-80]
+                      Identifier(e) [66-67]
+                      Identifier(order_number) [68-80]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#find-all-edges-matching-label-expr-property-filter
+GRAPH FinGraph
+MATCH -[e:Transfers {order_number: "304120005529714"}]->
+RETURN e.Id AS src_account, e.order_number
+--
+QueryStatement [0-114]
+  Query [0-114]
+    GqlQuery [0-114]
+      GraphTableQuery [0-114]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-114]
+          GqlOperatorList [15-114]
+            GqlMatch [15-71]
+              GraphPattern [21-71]
+                GraphPathPattern [21-71]
+                  GraphEdgePattern [21-71]
+                    GraphElementPatternFiller [23-68]
+                      Identifier(e) [23-24]
+                      GraphLabelFilter [24-34]
+                        GraphElementLabel [25-34]
+                          Identifier(Transfers) [25-34]
+                      GraphPropertySpecification [35-68]
+                        GraphPropertyNameAndValue [36-67]
+                          Identifier(order_number) [36-48]
+                          StringLiteral [50-67]
+                            StringLiteralComponent("304120005529714") [50-67]
+            GqlReturn [72-114]
+              Select [79-114]
+                SelectList [79-114]
+                  SelectColumn [79-98]
+                    PathExpression [79-83]
+                      Identifier(e) [79-80]
+                      Identifier(Id) [81-83]
+                    Alias [84-98]
+                      Identifier(src_account) [87-98]
+                  SelectColumn [100-114]
+                    PathExpression [100-114]
+                      Identifier(e) [100-101]
+                      Identifier(order_number) [102-114]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#find-all-paths-node-specified-label-property-filters-path-pattern
+GRAPH FinGraph
+MATCH
+  (p:Person {id: 2})-[:Owns]->(account:Account)-[t:Transfers]->
+  (to_account:Account)
+RETURN
+  p.id AS sender_id, account.id AS from_id, to_account.id AS to_id
+--
+QueryStatement [0-181]
+  Query [0-181]
+    GqlQuery [0-181]
+      GraphTableQuery [0-181]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-181]
+          GqlOperatorList [15-181]
+            GqlMatch [15-107]
+              GraphPattern [23-107]
+                GraphPathPattern [23-107]
+                  GraphNodePattern [23-41]
+                    GraphElementPatternFiller [24-40]
+                      Identifier(p) [24-25]
+                      GraphLabelFilter [25-32]
+                        GraphElementLabel [26-32]
+                          Identifier(Person) [26-32]
+                      GraphPropertySpecification [33-40]
+                        GraphPropertyNameAndValue [34-39]
+                          Identifier(id) [34-36]
+                          IntLiteral(2) [38-39]
+                  GraphEdgePattern [41-51]
+                    GraphElementPatternFiller [43-48]
+                      GraphLabelFilter [43-48]
+                        GraphElementLabel [44-48]
+                          Identifier(Owns) [44-48]
+                  GraphNodePattern [51-68]
+                    GraphElementPatternFiller [52-67]
+                      Identifier(account) [52-59]
+                      GraphLabelFilter [59-67]
+                        GraphElementLabel [60-67]
+                          Identifier(Account) [60-67]
+                  GraphEdgePattern [68-84]
+                    GraphElementPatternFiller [70-81]
+                      Identifier(t) [70-71]
+                      GraphLabelFilter [71-81]
+                        GraphElementLabel [72-81]
+                          Identifier(Transfers) [72-81]
+                  GraphNodePattern [87-107]
+                    GraphElementPatternFiller [88-106]
+                      Identifier(to_account) [88-98]
+                      GraphLabelFilter [98-106]
+                        GraphElementLabel [99-106]
+                          Identifier(Account) [99-106]
+            GqlReturn [108-181]
+              Select [117-181]
+                SelectList [117-181]
+                  SelectColumn [117-134]
+                    PathExpression [117-121]
+                      Identifier(p) [117-118]
+                      Identifier(id) [119-121]
+                    Alias [122-134]
+                      Identifier(sender_id) [125-134]
+                  SelectColumn [136-157]
+                    PathExpression [136-146]
+                      Identifier(account) [136-143]
+                      Identifier(id) [144-146]
+                    Alias [147-157]
+                      Identifier(from_id) [150-157]
+                  SelectColumn [159-181]
+                    PathExpression [159-172]
+                      Identifier(to_account) [159-169]
+                      Identifier(id) [170-172]
+                    Alias [173-181]
+                      Identifier(to_id) [176-181]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#quantified-edge-pattern
+GRAPH FinGraph
+MATCH (src:Account {id: 7})-[e:Transfers]->{1, 3}(dst:Account)
+WHERE src != dst
+RETURN src.id AS src_account_id, ARRAY_LENGTH(e) AS path_length, dst.id AS dst_account_id
+--
+QueryStatement [0-184]
+  Query [0-184]
+    GqlQuery [0-184]
+      GraphTableQuery [0-184]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-184]
+          GqlOperatorList [15-184]
+            GqlMatch [15-94]
+              GraphPattern [21-94]
+                GraphPathPattern [21-77]
+                  GraphNodePattern [21-42]
+                    GraphElementPatternFiller [22-41]
+                      Identifier(src) [22-25]
+                      GraphLabelFilter [25-33]
+                        GraphElementLabel [26-33]
+                          Identifier(Account) [26-33]
+                      GraphPropertySpecification [34-41]
+                        GraphPropertyNameAndValue [35-40]
+                          Identifier(id) [35-37]
+                          IntLiteral(7) [39-40]
+                  ParenthesizedGraphPathPattern [42-64]
+                    BoundedQuantifier [58-64]
+                      QuantifierBound [59-60]
+                        IntLiteral(1) [59-60]
+                      QuantifierBound [62-63]
+                        IntLiteral(3) [62-63]
+                    GraphEdgePattern [42-58]
+                      GraphElementPatternFiller [44-55]
+                        Identifier(e) [44-45]
+                        GraphLabelFilter [45-55]
+                          GraphElementLabel [46-55]
+                            Identifier(Transfers) [46-55]
+                  GraphNodePattern [64-77]
+                    GraphElementPatternFiller [65-76]
+                      Identifier(dst) [65-68]
+                      GraphLabelFilter [68-76]
+                        GraphElementLabel [69-76]
+                          Identifier(Account) [69-76]
+                WhereClause [78-94]
+                  BinaryExpression(!=) [84-94]
+                    PathExpression [84-87]
+                      Identifier(src) [84-87]
+                    PathExpression [91-94]
+                      Identifier(dst) [91-94]
+            GqlReturn [95-184]
+              Select [102-184]
+                SelectList [102-184]
+                  SelectColumn [102-126]
+                    PathExpression [102-108]
+                      Identifier(src) [102-105]
+                      Identifier(id) [106-108]
+                    Alias [109-126]
+                      Identifier(src_account_id) [112-126]
+                  SelectColumn [128-158]
+                    FunctionCall [128-143]
+                      PathExpression [128-140]
+                        Identifier(ARRAY_LENGTH) [128-140]
+                      PathExpression [141-142]
+                        Identifier(e) [141-142]
+                    Alias [144-158]
+                      Identifier(path_length) [147-158]
+                  SelectColumn [160-184]
+                    PathExpression [160-166]
+                      Identifier(dst) [160-163]
+                      Identifier(id) [164-166]
+                    Alias [167-184]
+                      Identifier(dst_account_id) [170-184]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#match-quantified-path-pattern
+GRAPH FinGraph
+MATCH
+  (src:Account)
+  ((a:Account)-[:Transfers]->(b:Account {is_blocked:true}) WHERE a != b){1,2}
+    -[:Transfers]->(dst:Account)
+RETURN src.id AS src_account_id, dst.id AS dst_account_id
+--
+QueryStatement [0-205]
+  Query [0-205]
+    GqlQuery [0-205]
+      GraphTableQuery [0-205]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-205]
+          GqlOperatorList [15-205]
+            GqlMatch [15-147]
+              GraphPattern [23-147]
+                GraphPathPattern [23-147]
+                  GraphNodePattern [23-36]
+                    GraphElementPatternFiller [24-35]
+                      Identifier(src) [24-27]
+                      GraphLabelFilter [27-35]
+                        GraphElementLabel [28-35]
+                          Identifier(Account) [28-35]
+                  ParenthesizedGraphPathPattern [39-114]
+                    BoundedQuantifier [109-114]
+                      QuantifierBound [110-111]
+                        IntLiteral(1) [110-111]
+                      QuantifierBound [112-113]
+                        IntLiteral(2) [112-113]
+                    WhereClause [96-108]
+                      BinaryExpression(!=) [102-108]
+                        PathExpression [102-103]
+                          Identifier(a) [102-103]
+                        PathExpression [107-108]
+                          Identifier(b) [107-108]
+                    GraphNodePattern [40-51]
+                      GraphElementPatternFiller [41-50]
+                        Identifier(a) [41-42]
+                        GraphLabelFilter [42-50]
+                          GraphElementLabel [43-50]
+                            Identifier(Account) [43-50]
+                    GraphEdgePattern [51-66]
+                      GraphElementPatternFiller [53-63]
+                        GraphLabelFilter [53-63]
+                          GraphElementLabel [54-63]
+                            Identifier(Transfers) [54-63]
+                    GraphNodePattern [66-95]
+                      GraphElementPatternFiller [67-94]
+                        Identifier(b) [67-68]
+                        GraphLabelFilter [68-76]
+                          GraphElementLabel [69-76]
+                            Identifier(Account) [69-76]
+                        GraphPropertySpecification [77-94]
+                          GraphPropertyNameAndValue [78-93]
+                            Identifier(is_blocked) [78-88]
+                            BooleanLiteral(true) [89-93]
+                  GraphEdgePattern [119-134]
+                    GraphElementPatternFiller [121-131]
+                      GraphLabelFilter [121-131]
+                        GraphElementLabel [122-131]
+                          Identifier(Transfers) [122-131]
+                  GraphNodePattern [134-147]
+                    GraphElementPatternFiller [135-146]
+                      Identifier(dst) [135-138]
+                      GraphLabelFilter [138-146]
+                        GraphElementLabel [139-146]
+                          Identifier(Account) [139-146]
+            GqlReturn [148-205]
+              Select [155-205]
+                SelectList [155-205]
+                  SelectColumn [155-179]
+                    PathExpression [155-161]
+                      Identifier(src) [155-158]
+                      Identifier(id) [159-161]
+                    Alias [162-179]
+                      Identifier(src_account_id) [165-179]
+                  SelectColumn [181-205]
+                    PathExpression [181-187]
+                      Identifier(dst) [181-184]
+                      Identifier(id) [185-187]
+                    Alias [188-205]
+                      Identifier(dst_account_id) [191-205]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#access-group-variable
+GRAPH FinGraph
+MATCH
+  (src:Account {id: 7})-[e:Transfers WHERE e.amount > 100]->{0,2}
+  (dst:Account)
+WHERE src.id != dst.id
+LET total_amount = SUM(e.amount)
+RETURN
+  src.id AS src_account_id, ARRAY_LENGTH(e) AS path_length,
+  total_amount, dst.id AS dst_account_id
+--
+QueryStatement [0-266]
+  Query [0-266]
+    GqlQuery [0-266]
+      GraphTableQuery [0-266]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-266]
+          GqlOperatorList [15-266]
+            GqlMatch [15-125]
+              GraphPattern [23-125]
+                GraphPathPattern [23-102]
+                  GraphNodePattern [23-44]
+                    GraphElementPatternFiller [24-43]
+                      Identifier(src) [24-27]
+                      GraphLabelFilter [27-35]
+                        GraphElementLabel [28-35]
+                          Identifier(Account) [28-35]
+                      GraphPropertySpecification [36-43]
+                        GraphPropertyNameAndValue [37-42]
+                          Identifier(id) [37-39]
+                          IntLiteral(7) [41-42]
+                  ParenthesizedGraphPathPattern [44-86]
+                    BoundedQuantifier [81-86]
+                      QuantifierBound [82-83]
+                        IntLiteral(0) [82-83]
+                      QuantifierBound [84-85]
+                        IntLiteral(2) [84-85]
+                    GraphEdgePattern [44-81]
+                      GraphElementPatternFiller [46-78]
+                        Identifier(e) [46-47]
+                        GraphLabelFilter [47-57]
+                          GraphElementLabel [48-57]
+                            Identifier(Transfers) [48-57]
+                        WhereClause [58-78]
+                          BinaryExpression(>) [64-78]
+                            PathExpression [64-72]
+                              Identifier(e) [64-65]
+                              Identifier(amount) [66-72]
+                            IntLiteral(100) [75-78]
+                  GraphNodePattern [89-102]
+                    GraphElementPatternFiller [90-101]
+                      Identifier(dst) [90-93]
+                      GraphLabelFilter [93-101]
+                        GraphElementLabel [94-101]
+                          Identifier(Account) [94-101]
+                WhereClause [103-125]
+                  BinaryExpression(!=) [109-125]
+                    PathExpression [109-115]
+                      Identifier(src) [109-112]
+                      Identifier(id) [113-115]
+                    PathExpression [119-125]
+                      Identifier(dst) [119-122]
+                      Identifier(id) [123-125]
+            GqlLet [126-158]
+              GqlLetVariableDefinitionList [130-158]
+                GqlLetVariableDefinition [130-158]
+                  Identifier(total_amount) [130-142]
+                  FunctionCall [145-158]
+                    PathExpression [145-148]
+                      Identifier(SUM) [145-148]
+                    PathExpression [149-157]
+                      Identifier(e) [149-150]
+                      Identifier(amount) [151-157]
+            GqlReturn [159-266]
+              Select [168-266]
+                SelectList [168-266]
+                  SelectColumn [168-192]
+                    PathExpression [168-174]
+                      Identifier(src) [168-171]
+                      Identifier(id) [172-174]
+                    Alias [175-192]
+                      Identifier(src_account_id) [178-192]
+                  SelectColumn [194-224]
+                    FunctionCall [194-209]
+                      PathExpression [194-206]
+                        Identifier(ARRAY_LENGTH) [194-206]
+                      PathExpression [207-208]
+                        Identifier(e) [207-208]
+                    Alias [210-224]
+                      Identifier(path_length) [213-224]
+                  SelectColumn [228-240]
+                    PathExpression [228-240]
+                      Identifier(total_amount) [228-240]
+                  SelectColumn [242-266]
+                    PathExpression [242-248]
+                      Identifier(dst) [242-245]
+                      Identifier(id) [246-248]
+                    Alias [249-266]
+                      Identifier(dst_account_id) [252-266]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#match-using-any
+GRAPH FinGraph
+MATCH ANY (src:Account {id: 7})-[e:Transfers]->{1,2}(dst:Account)
+LET ids_in_path = ARRAY_CONCAT(ARRAY_AGG(e.Id), [dst.Id])
+RETURN src.id AS src_account_id, dst.id AS dst_account_id, ids_in_path
+--
+QueryStatement [0-209]
+  Query [0-209]
+    GqlQuery [0-209]
+      GraphTableQuery [0-209]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-209]
+          GqlOperatorList [15-209]
+            GqlMatch [15-80]
+              GraphPattern [21-80]
+                GraphPathPattern [21-80]
+                  GraphPathSearchPrefix [21-24]
+                  GraphNodePattern [25-46]
+                    GraphElementPatternFiller [26-45]
+                      Identifier(src) [26-29]
+                      GraphLabelFilter [29-37]
+                        GraphElementLabel [30-37]
+                          Identifier(Account) [30-37]
+                      GraphPropertySpecification [38-45]
+                        GraphPropertyNameAndValue [39-44]
+                          Identifier(id) [39-41]
+                          IntLiteral(7) [43-44]
+                  ParenthesizedGraphPathPattern [46-67]
+                    BoundedQuantifier [62-67]
+                      QuantifierBound [63-64]
+                        IntLiteral(1) [63-64]
+                      QuantifierBound [65-66]
+                        IntLiteral(2) [65-66]
+                    GraphEdgePattern [46-62]
+                      GraphElementPatternFiller [48-59]
+                        Identifier(e) [48-49]
+                        GraphLabelFilter [49-59]
+                          GraphElementLabel [50-59]
+                            Identifier(Transfers) [50-59]
+                  GraphNodePattern [67-80]
+                    GraphElementPatternFiller [68-79]
+                      Identifier(dst) [68-71]
+                      GraphLabelFilter [71-79]
+                        GraphElementLabel [72-79]
+                          Identifier(Account) [72-79]
+            GqlLet [81-138]
+              GqlLetVariableDefinitionList [85-138]
+                GqlLetVariableDefinition [85-138]
+                  Identifier(ids_in_path) [85-96]
+                  FunctionCall [99-138]
+                    PathExpression [99-111]
+                      Identifier(ARRAY_CONCAT) [99-111]
+                    FunctionCall [112-127]
+                      PathExpression [112-121]
+                        Identifier(ARRAY_AGG) [112-121]
+                      PathExpression [122-126]
+                        Identifier(e) [122-123]
+                        Identifier(Id) [124-126]
+                    ArrayConstructor [129-137]
+                      PathExpression [130-136]
+                        Identifier(dst) [130-133]
+                        Identifier(Id) [134-136]
+            GqlReturn [139-209]
+              Select [146-209]
+                SelectList [146-209]
+                  SelectColumn [146-170]
+                    PathExpression [146-152]
+                      Identifier(src) [146-149]
+                      Identifier(id) [150-152]
+                    Alias [153-170]
+                      Identifier(src_account_id) [156-170]
+                  SelectColumn [172-196]
+                    PathExpression [172-178]
+                      Identifier(dst) [172-175]
+                      Identifier(id) [176-178]
+                    Alias [179-196]
+                      Identifier(dst_account_id) [182-196]
+                  SelectColumn [198-209]
+                    PathExpression [198-209]
+                      Identifier(ids_in_path) [198-209]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#match-using-any-shortest
+GRAPH FinGraph
+MATCH ANY SHORTEST (src:Account {id: 7})-[e:Transfers]->{1, 3}(dst:Account {id: 20})
+RETURN src.id AS src_account_id, dst.id AS dst_account_id, ARRAY_LENGTH(e) AS path_length
+--
+QueryStatement [0-189]
+  Query [0-189]
+    GqlQuery [0-189]
+      GraphTableQuery [0-189]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-189]
+          GqlOperatorList [15-189]
+            GqlMatch [15-99]
+              GraphPattern [21-99]
+                GraphPathPattern [21-99]
+                  GraphPathSearchPrefix [21-33]
+                  GraphNodePattern [34-55]
+                    GraphElementPatternFiller [35-54]
+                      Identifier(src) [35-38]
+                      GraphLabelFilter [38-46]
+                        GraphElementLabel [39-46]
+                          Identifier(Account) [39-46]
+                      GraphPropertySpecification [47-54]
+                        GraphPropertyNameAndValue [48-53]
+                          Identifier(id) [48-50]
+                          IntLiteral(7) [52-53]
+                  ParenthesizedGraphPathPattern [55-77]
+                    BoundedQuantifier [71-77]
+                      QuantifierBound [72-73]
+                        IntLiteral(1) [72-73]
+                      QuantifierBound [75-76]
+                        IntLiteral(3) [75-76]
+                    GraphEdgePattern [55-71]
+                      GraphElementPatternFiller [57-68]
+                        Identifier(e) [57-58]
+                        GraphLabelFilter [58-68]
+                          GraphElementLabel [59-68]
+                            Identifier(Transfers) [59-68]
+                  GraphNodePattern [77-99]
+                    GraphElementPatternFiller [78-98]
+                      Identifier(dst) [78-81]
+                      GraphLabelFilter [81-89]
+                        GraphElementLabel [82-89]
+                          Identifier(Account) [82-89]
+                      GraphPropertySpecification [90-98]
+                        GraphPropertyNameAndValue [91-97]
+                          Identifier(id) [91-93]
+                          IntLiteral(20) [95-97]
+            GqlReturn [100-189]
+              Select [107-189]
+                SelectList [107-189]
+                  SelectColumn [107-131]
+                    PathExpression [107-113]
+                      Identifier(src) [107-110]
+                      Identifier(id) [111-113]
+                    Alias [114-131]
+                      Identifier(src_account_id) [117-131]
+                  SelectColumn [133-157]
+                    PathExpression [133-139]
+                      Identifier(dst) [133-136]
+                      Identifier(id) [137-139]
+                    Alias [140-157]
+                      Identifier(dst_account_id) [143-157]
+                  SelectColumn [159-189]
+                    FunctionCall [159-174]
+                      PathExpression [159-171]
+                        Identifier(ARRAY_LENGTH) [159-171]
+                      PathExpression [172-173]
+                        Identifier(e) [172-173]
+                    Alias [175-189]
+                      Identifier(path_length) [178-189]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#match-using-any-cheapest
+GRAPH FinGraph
+MATCH ANY CHEAPEST (src:Account)-[e:Transfers COST e.amount]->{1,3}(dst:Account)
+LET total_cost = sum(e.amount)
+RETURN src.id AS src_account_id, dst.id AS dst_account_id, total_cost
+--
+QueryStatement [0-196]
+  Query [0-196]
+    GqlQuery [0-196]
+      GraphTableQuery [0-196]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-196]
+          GqlOperatorList [15-196]
+            GqlMatch [15-95]
+              GraphPattern [21-95]
+                GraphPathPattern [21-95]
+                  GraphPathSearchPrefix [21-33]
+                  GraphNodePattern [34-47]
+                    GraphElementPatternFiller [35-46]
+                      Identifier(src) [35-38]
+                      GraphLabelFilter [38-46]
+                        GraphElementLabel [39-46]
+                          Identifier(Account) [39-46]
+                  ParenthesizedGraphPathPattern [47-82]
+                    BoundedQuantifier [77-82]
+                      QuantifierBound [78-79]
+                        IntLiteral(1) [78-79]
+                      QuantifierBound [80-81]
+                        IntLiteral(3) [80-81]
+                    GraphEdgePattern [47-77]
+                      GraphElementPatternFiller [49-74]
+                        Identifier(e) [49-50]
+                        GraphLabelFilter [50-60]
+                          GraphElementLabel [51-60]
+                            Identifier(Transfers) [51-60]
+                        PathExpression [66-74]
+                          Identifier(e) [66-67]
+                          Identifier(amount) [68-74]
+                  GraphNodePattern [82-95]
+                    GraphElementPatternFiller [83-94]
+                      Identifier(dst) [83-86]
+                      GraphLabelFilter [86-94]
+                        GraphElementLabel [87-94]
+                          Identifier(Account) [87-94]
+            GqlLet [96-126]
+              GqlLetVariableDefinitionList [100-126]
+                GqlLetVariableDefinition [100-126]
+                  Identifier(total_cost) [100-110]
+                  FunctionCall [113-126]
+                    PathExpression [113-116]
+                      Identifier(sum) [113-116]
+                    PathExpression [117-125]
+                      Identifier(e) [117-118]
+                      Identifier(amount) [119-125]
+            GqlReturn [127-196]
+              Select [134-196]
+                SelectList [134-196]
+                  SelectColumn [134-158]
+                    PathExpression [134-140]
+                      Identifier(src) [134-137]
+                      Identifier(id) [138-140]
+                    Alias [141-158]
+                      Identifier(src_account_id) [144-158]
+                  SelectColumn [160-184]
+                    PathExpression [160-166]
+                      Identifier(dst) [160-163]
+                      Identifier(id) [164-166]
+                    Alias [167-184]
+                      Identifier(dst_account_id) [170-184]
+                  SelectColumn [186-196]
+                    PathExpression [186-196]
+                      Identifier(total_cost) [186-196]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#match-using-graph-patterns
+GRAPH FinGraph
+MATCH
+  (src:Account)-[t1:Transfers]->(interm:Account)-[t2:Transfers]->(dst:Account),
+  (interm)<-[:Owns]-(p:Person)
+WHERE dst.is_blocked = TRUE AND t1.amount > 200 AND t2.amount > 200
+RETURN
+  src.id AS src_account_id, dst.id AS dst_account_id,
+  interm.id AS interm_account_id, p.id AS owner_id
+--
+QueryStatement [0-311]
+  Query [0-311]
+    GqlQuery [0-311]
+      GraphTableQuery [0-311]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-311]
+          GqlOperatorList [15-311]
+            GqlMatch [15-199]
+              GraphPattern [23-199]
+                GraphPathPattern [23-99]
+                  GraphNodePattern [23-36]
+                    GraphElementPatternFiller [24-35]
+                      Identifier(src) [24-27]
+                      GraphLabelFilter [27-35]
+                        GraphElementLabel [28-35]
+                          Identifier(Account) [28-35]
+                  GraphEdgePattern [36-53]
+                    GraphElementPatternFiller [38-50]
+                      Identifier(t1) [38-40]
+                      GraphLabelFilter [40-50]
+                        GraphElementLabel [41-50]
+                          Identifier(Transfers) [41-50]
+                  GraphNodePattern [53-69]
+                    GraphElementPatternFiller [54-68]
+                      Identifier(interm) [54-60]
+                      GraphLabelFilter [60-68]
+                        GraphElementLabel [61-68]
+                          Identifier(Account) [61-68]
+                  GraphEdgePattern [69-86]
+                    GraphElementPatternFiller [71-83]
+                      Identifier(t2) [71-73]
+                      GraphLabelFilter [73-83]
+                        GraphElementLabel [74-83]
+                          Identifier(Transfers) [74-83]
+                  GraphNodePattern [86-99]
+                    GraphElementPatternFiller [87-98]
+                      Identifier(dst) [87-90]
+                      GraphLabelFilter [90-98]
+                        GraphElementLabel [91-98]
+                          Identifier(Account) [91-98]
+                GraphPathPattern [103-131]
+                  GraphNodePattern [103-111]
+                    GraphElementPatternFiller [104-110]
+                      Identifier(interm) [104-110]
+                  GraphEdgePattern [111-121]
+                    GraphElementPatternFiller [114-119]
+                      GraphLabelFilter [114-119]
+                        GraphElementLabel [115-119]
+                          Identifier(Owns) [115-119]
+                  GraphNodePattern [121-131]
+                    GraphElementPatternFiller [122-130]
+                      Identifier(p) [122-123]
+                      GraphLabelFilter [123-130]
+                        GraphElementLabel [124-130]
+                          Identifier(Person) [124-130]
+                WhereClause [132-199]
+                  AndExpr [138-199]
+                    BinaryExpression(=) [138-159]
+                      PathExpression [138-152]
+                        Identifier(dst) [138-141]
+                        Identifier(is_blocked) [142-152]
+                      BooleanLiteral(TRUE) [155-159]
+                    BinaryExpression(>) [164-179]
+                      PathExpression [164-173]
+                        Identifier(t1) [164-166]
+                        Identifier(amount) [167-173]
+                      IntLiteral(200) [176-179]
+                    BinaryExpression(>) [184-199]
+                      PathExpression [184-193]
+                        Identifier(t2) [184-186]
+                        Identifier(amount) [187-193]
+                      IntLiteral(200) [196-199]
+            GqlReturn [200-311]
+              Select [209-311]
+                SelectList [209-311]
+                  SelectColumn [209-233]
+                    PathExpression [209-215]
+                      Identifier(src) [209-212]
+                      Identifier(id) [213-215]
+                    Alias [216-233]
+                      Identifier(src_account_id) [219-233]
+                  SelectColumn [235-259]
+                    PathExpression [235-241]
+                      Identifier(dst) [235-238]
+                      Identifier(id) [239-241]
+                    Alias [242-259]
+                      Identifier(dst_account_id) [245-259]
+                  SelectColumn [263-293]
+                    PathExpression [263-272]
+                      Identifier(interm) [263-269]
+                      Identifier(id) [270-272]
+                    Alias [273-293]
+                      Identifier(interm_account_id) [276-293]
+                  SelectColumn [295-311]
+                    PathExpression [295-299]
+                      Identifier(p) [295-296]
+                      Identifier(id) [297-299]
+                    Alias [300-311]
+                      Identifier(owner_id) [303-311]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#graph-linear-query-statements
+GRAPH FinGraph
+MATCH (src_account:Account)-[transfer:Transfers]->(dst_account:Account {is_blocked:true})
+ORDER BY transfer.amount DESC
+LIMIT 1
+MATCH (src_account:Account)<-[owns:Owns]-(owner:Person)
+RETURN src_account.id AS account_id, owner.name AS owner_name
+--
+QueryStatement [0-260]
+  Query [0-260]
+    GqlQuery [0-260]
+      GraphTableQuery [0-260]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-260]
+          GqlOperatorList [15-260]
+            GqlMatch [15-104]
+              GraphPattern [21-104]
+                GraphPathPattern [21-104]
+                  GraphNodePattern [21-42]
+                    GraphElementPatternFiller [22-41]
+                      Identifier(src_account) [22-33]
+                      GraphLabelFilter [33-41]
+                        GraphElementLabel [34-41]
+                          Identifier(Account) [34-41]
+                  GraphEdgePattern [42-65]
+                    GraphElementPatternFiller [44-62]
+                      Identifier(transfer) [44-52]
+                      GraphLabelFilter [52-62]
+                        GraphElementLabel [53-62]
+                          Identifier(Transfers) [53-62]
+                  GraphNodePattern [65-104]
+                    GraphElementPatternFiller [66-103]
+                      Identifier(dst_account) [66-77]
+                      GraphLabelFilter [77-85]
+                        GraphElementLabel [78-85]
+                          Identifier(Account) [78-85]
+                      GraphPropertySpecification [86-103]
+                        GraphPropertyNameAndValue [87-102]
+                          Identifier(is_blocked) [87-97]
+                          BooleanLiteral(true) [98-102]
+            GqlOrderByAndPage [105-134]
+              OrderBy [105-134]
+                OrderingExpression(DESC) [114-134]
+                  PathExpression [114-129]
+                    Identifier(transfer) [114-122]
+                    Identifier(amount) [123-129]
+            GqlOrderByAndPage [135-142]
+              GqlPage [135-142]
+                GqlPageLimit [135-142]
+                  IntLiteral(1) [141-142]
+            GqlMatch [143-198]
+              GraphPattern [149-198]
+                GraphPathPattern [149-198]
+                  GraphNodePattern [149-170]
+                    GraphElementPatternFiller [150-169]
+                      Identifier(src_account) [150-161]
+                      GraphLabelFilter [161-169]
+                        GraphElementLabel [162-169]
+                          Identifier(Account) [162-169]
+                  GraphEdgePattern [170-184]
+                    GraphElementPatternFiller [173-182]
+                      Identifier(owns) [173-177]
+                      GraphLabelFilter [177-182]
+                        GraphElementLabel [178-182]
+                          Identifier(Owns) [178-182]
+                  GraphNodePattern [184-198]
+                    GraphElementPatternFiller [185-197]
+                      Identifier(owner) [185-190]
+                      GraphLabelFilter [190-197]
+                        GraphElementLabel [191-197]
+                          Identifier(Person) [191-197]
+            GqlReturn [199-260]
+              Select [206-260]
+                SelectList [206-260]
+                  SelectColumn [206-234]
+                    PathExpression [206-220]
+                      Identifier(src_account) [206-217]
+                      Identifier(id) [218-220]
+                    Alias [221-234]
+                      Identifier(account_id) [224-234]
+                  SelectColumn [236-260]
+                    PathExpression [236-246]
+                      Identifier(owner) [236-241]
+                      Identifier(name) [242-246]
+                    Alias [247-260]
+                      Identifier(owner_name) [250-260]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#return-graph-elements-json
+GRAPH FinGraph
+MATCH (n:Account {id: 7})
+-- Returning a graph element in the final results is NOT allowed. Instead, use
+-- the TO_JSON function or explicitly return the graph element's properties.
+RETURN TO_JSON(n) AS n
+--
+QueryStatement [0-219]
+  Query [0-219]
+    GqlQuery [0-219]
+      GraphTableQuery [0-219]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-219]
+          GqlOperatorList [15-219]
+            GqlMatch [15-40]
+              GraphPattern [21-40]
+                GraphPathPattern [21-40]
+                  GraphNodePattern [21-40]
+                    GraphElementPatternFiller [22-39]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                      GraphPropertySpecification [32-39]
+                        GraphPropertyNameAndValue [33-38]
+                          Identifier(id) [33-35]
+                          IntLiteral(7) [37-38]
+            GqlReturn [197-219]
+              Select [204-219]
+                SelectList [204-219]
+                  SelectColumn [204-219]
+                    FunctionCall [204-214]
+                      PathExpression [204-211]
+                        Identifier(TO_JSON) [204-211]
+                      PathExpression [212-213]
+                        Identifier(n) [212-213]
+                    Alias [215-219]
+                      Identifier(n) [218-219]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#return-graph-elements-json
+GRAPH FinGraph
+MATCH (n:Account {id: 7})
+-- Certain fields in the graph elements, such as TOKENLIST, can't be returned
+-- in the TO_JSON function. In those cases, use the SAFE_TO_JSON function instead.
+RETURN SAFE_TO_JSON(n) AS n
+--
+QueryStatement [0-229]
+  Query [0-229]
+    GqlQuery [0-229]
+      GraphTableQuery [0-229]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-229]
+          GqlOperatorList [15-229]
+            GqlMatch [15-40]
+              GraphPattern [21-40]
+                GraphPathPattern [21-40]
+                  GraphNodePattern [21-40]
+                    GraphElementPatternFiller [22-39]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                      GraphPropertySpecification [32-39]
+                        GraphPropertyNameAndValue [33-38]
+                          Identifier(id) [33-35]
+                          IntLiteral(7) [37-38]
+            GqlReturn [202-229]
+              Select [209-229]
+                SelectList [209-229]
+                  SelectColumn [209-229]
+                    FunctionCall [209-224]
+                      PathExpression [209-221]
+                        Identifier(SAFE_TO_JSON) [209-221]
+                      PathExpression [222-223]
+                        Identifier(n) [222-223]
+                    Alias [225-229]
+                      Identifier(n) [228-229]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#composing-larger-queries-next-keyword
+GRAPH FinGraph
+MATCH (:Account)-[:Transfers]->(account:Account)
+RETURN account, COUNT(*) AS num_incoming_transfers
+GROUP BY account
+ORDER BY num_incoming_transfers DESC
+LIMIT 1
+
+NEXT
+
+MATCH (account:Account)<-[:Owns]-(owner:Person)
+RETURN account.id AS account_id, owner.name AS owner_name, num_incoming_transfers
+--
+QueryStatement [0-313]
+  Query [0-313]
+    GqlQuery [0-313]
+      GraphTableQuery [0-313]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-313]
+          GqlOperatorList [15-176]
+            GqlMatch [15-63]
+              GraphPattern [21-63]
+                GraphPathPattern [21-63]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      GraphLabelFilter [22-30]
+                        GraphElementLabel [23-30]
+                          Identifier(Account) [23-30]
+                  GraphEdgePattern [31-46]
+                    GraphElementPatternFiller [33-43]
+                      GraphLabelFilter [33-43]
+                        GraphElementLabel [34-43]
+                          Identifier(Transfers) [34-43]
+                  GraphNodePattern [46-63]
+                    GraphElementPatternFiller [47-62]
+                      Identifier(account) [47-54]
+                      GraphLabelFilter [54-62]
+                        GraphElementLabel [55-62]
+                          Identifier(Account) [55-62]
+            GqlReturn [64-176]
+              Select [71-131]
+                SelectList [71-114]
+                  SelectColumn [71-78]
+                    PathExpression [71-78]
+                      Identifier(account) [71-78]
+                  SelectColumn [80-114]
+                    FunctionCall [80-88]
+                      PathExpression [80-85]
+                        Identifier(COUNT) [80-85]
+                      Star(*) [86-87]
+                    Alias [89-114]
+                      Identifier(num_incoming_transfers) [92-114]
+                GroupBy [115-131]
+                  GroupingItem [124-131]
+                    PathExpression [124-131]
+                      Identifier(account) [124-131]
+              GqlOrderByAndPage [132-176]
+                OrderBy [132-168]
+                  OrderingExpression(DESC) [141-168]
+                    PathExpression [141-163]
+                      Identifier(num_incoming_transfers) [141-163]
+                GqlPage [169-176]
+                  GqlPageLimit [169-176]
+                    IntLiteral(1) [175-176]
+          GqlOperatorList [184-313]
+            GqlMatch [184-231]
+              GraphPattern [190-231]
+                GraphPathPattern [190-231]
+                  GraphNodePattern [190-207]
+                    GraphElementPatternFiller [191-206]
+                      Identifier(account) [191-198]
+                      GraphLabelFilter [198-206]
+                        GraphElementLabel [199-206]
+                          Identifier(Account) [199-206]
+                  GraphEdgePattern [207-217]
+                    GraphElementPatternFiller [210-215]
+                      GraphLabelFilter [210-215]
+                        GraphElementLabel [211-215]
+                          Identifier(Owns) [211-215]
+                  GraphNodePattern [217-231]
+                    GraphElementPatternFiller [218-230]
+                      Identifier(owner) [218-223]
+                      GraphLabelFilter [223-230]
+                        GraphElementLabel [224-230]
+                          Identifier(Person) [224-230]
+            GqlReturn [232-313]
+              Select [239-313]
+                SelectList [239-313]
+                  SelectColumn [239-263]
+                    PathExpression [239-249]
+                      Identifier(account) [239-246]
+                      Identifier(id) [247-249]
+                    Alias [250-263]
+                      Identifier(account_id) [253-263]
+                  SelectColumn [265-289]
+                    PathExpression [265-275]
+                      Identifier(owner) [265-270]
+                      Identifier(name) [271-275]
+                    Alias [276-289]
+                      Identifier(owner_name) [279-289]
+                  SelectColumn [291-313]
+                    PathExpression [291-313]
+                      Identifier(num_incoming_transfers) [291-313]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#built-in-functions-operators
+GRAPH FinGraph
+MATCH (person:Person)-[:Owns]->(account:Account)
+RETURN person, ARRAY_AGG(account.nick_name) AS accounts
+GROUP BY person
+
+NEXT
+
+RETURN
+  LABELS(person) AS labels,
+  TO_JSON(person) AS person,
+  accounts,
+  CONCAT(person.city, ", ", person.country) AS location,
+  PROPERTY_EXISTS(person, is_blocked) AS is_blocked_property_exists,
+  PROPERTY_EXISTS(person, name) AS name_property_exists
+LIMIT 1
+--
+QueryStatement [0-408]
+  Query [0-408]
+    GqlQuery [0-408]
+      GraphTableQuery [0-408]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-408]
+          GqlOperatorList [15-135]
+            GqlMatch [15-63]
+              GraphPattern [21-63]
+                GraphPathPattern [21-63]
+                  GraphNodePattern [21-36]
+                    GraphElementPatternFiller [22-35]
+                      Identifier(person) [22-28]
+                      GraphLabelFilter [28-35]
+                        GraphElementLabel [29-35]
+                          Identifier(Person) [29-35]
+                  GraphEdgePattern [36-46]
+                    GraphElementPatternFiller [38-43]
+                      GraphLabelFilter [38-43]
+                        GraphElementLabel [39-43]
+                          Identifier(Owns) [39-43]
+                  GraphNodePattern [46-63]
+                    GraphElementPatternFiller [47-62]
+                      Identifier(account) [47-54]
+                      GraphLabelFilter [54-62]
+                        GraphElementLabel [55-62]
+                          Identifier(Account) [55-62]
+            GqlReturn [64-135]
+              Select [71-135]
+                SelectList [71-119]
+                  SelectColumn [71-77]
+                    PathExpression [71-77]
+                      Identifier(person) [71-77]
+                  SelectColumn [79-119]
+                    FunctionCall [79-107]
+                      PathExpression [79-88]
+                        Identifier(ARRAY_AGG) [79-88]
+                      PathExpression [89-106]
+                        Identifier(account) [89-96]
+                        Identifier(nick_name) [97-106]
+                    Alias [108-119]
+                      Identifier(accounts) [111-119]
+                GroupBy [120-135]
+                  GroupingItem [129-135]
+                    PathExpression [129-135]
+                      Identifier(person) [129-135]
+          GqlOperatorList [143-408]
+            GqlReturn [143-408]
+              Select [152-400]
+                SelectList [152-400]
+                  SelectColumn [152-176]
+                    FunctionCall [152-166]
+                      PathExpression [152-158]
+                        Identifier(LABELS) [152-158]
+                      PathExpression [159-165]
+                        Identifier(person) [159-165]
+                    Alias [167-176]
+                      Identifier(labels) [170-176]
+                  SelectColumn [180-205]
+                    FunctionCall [180-195]
+                      PathExpression [180-187]
+                        Identifier(TO_JSON) [180-187]
+                      PathExpression [188-194]
+                        Identifier(person) [188-194]
+                    Alias [196-205]
+                      Identifier(person) [199-205]
+                  SelectColumn [209-217]
+                    PathExpression [209-217]
+                      Identifier(accounts) [209-217]
+                  SelectColumn [221-274]
+                    FunctionCall [221-262]
+                      PathExpression [221-227]
+                        Identifier(CONCAT) [221-227]
+                      PathExpression [228-239]
+                        Identifier(person) [228-234]
+                        Identifier(city) [235-239]
+                      StringLiteral [241-245]
+                        StringLiteralComponent(", ") [241-245]
+                      PathExpression [247-261]
+                        Identifier(person) [247-253]
+                        Identifier(country) [254-261]
+                    Alias [263-274]
+                      Identifier(location) [266-274]
+                  SelectColumn [278-343]
+                    FunctionCall [278-313]
+                      PathExpression [278-293]
+                        Identifier(PROPERTY_EXISTS) [278-293]
+                      PathExpression [294-300]
+                        Identifier(person) [294-300]
+                      PathExpression [302-312]
+                        Identifier(is_blocked) [302-312]
+                    Alias [314-343]
+                      Identifier(is_blocked_property_exists) [317-343]
+                  SelectColumn [347-400]
+                    FunctionCall [347-376]
+                      PathExpression [347-362]
+                        Identifier(PROPERTY_EXISTS) [347-362]
+                      PathExpression [363-369]
+                        Identifier(person) [363-369]
+                      PathExpression [371-375]
+                        Identifier(name) [371-375]
+                    Alias [377-400]
+                      Identifier(name_property_exists) [380-400]
+              GqlOrderByAndPage [400-408]
+                GqlPage [401-408]
+                  GqlPageLimit [401-408]
+                    IntLiteral(1) [407-408]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#subquery-find-total-transfers-account
+GRAPH FinGraph
+MATCH (p:Person)-[:Owns]->(account:Account)
+RETURN p.name, account.id AS account_id, VALUE {
+  MATCH (a:Account)-[transfer:Transfers]->(:Account)
+  WHERE a = account
+  RETURN COUNT(transfer) AS num_transfers
+} AS num_transfers
+--
+QueryStatement [0-241]
+  Query [0-241]
+    GqlQuery [0-241]
+      GraphTableQuery [0-241]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-241]
+          GqlOperatorList [15-241]
+            GqlMatch [15-58]
+              GraphPattern [21-58]
+                GraphPathPattern [21-58]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+                  GraphEdgePattern [31-41]
+                    GraphElementPatternFiller [33-38]
+                      GraphLabelFilter [33-38]
+                        GraphElementLabel [34-38]
+                          Identifier(Owns) [34-38]
+                  GraphNodePattern [41-58]
+                    GraphElementPatternFiller [42-57]
+                      Identifier(account) [42-49]
+                      GraphLabelFilter [49-57]
+                        GraphElementLabel [50-57]
+                          Identifier(Account) [50-57]
+            GqlReturn [59-241]
+              Select [66-241]
+                SelectList [66-241]
+                  SelectColumn [66-72]
+                    PathExpression [66-72]
+                      Identifier(p) [66-67]
+                      Identifier(name) [68-72]
+                  SelectColumn [74-98]
+                    PathExpression [74-84]
+                      Identifier(account) [74-81]
+                      Identifier(id) [82-84]
+                    Alias [85-98]
+                      Identifier(account_id) [88-98]
+                  SelectColumn [100-241]
+                    ExpressionSubquery(modifier=VALUE) [100-224]
+                      Query [106-224]
+                        GqlQuery [106-224]
+                          GraphTableQuery [106-224]
+                            GqlOperatorList [110-222]
+                              GqlOperatorList [110-222]
+                                GqlMatch [110-180]
+                                  GraphPattern [116-180]
+                                    GraphPathPattern [116-160]
+                                      GraphNodePattern [116-127]
+                                        GraphElementPatternFiller [117-126]
+                                          Identifier(a) [117-118]
+                                          GraphLabelFilter [118-126]
+                                            GraphElementLabel [119-126]
+                                              Identifier(Account) [119-126]
+                                      GraphEdgePattern [127-150]
+                                        GraphElementPatternFiller [129-147]
+                                          Identifier(transfer) [129-137]
+                                          GraphLabelFilter [137-147]
+                                            GraphElementLabel [138-147]
+                                              Identifier(Transfers) [138-147]
+                                      GraphNodePattern [150-160]
+                                        GraphElementPatternFiller [151-159]
+                                          GraphLabelFilter [151-159]
+                                            GraphElementLabel [152-159]
+                                              Identifier(Account) [152-159]
+                                    WhereClause [163-180]
+                                      BinaryExpression(=) [169-180]
+                                        PathExpression [169-170]
+                                          Identifier(a) [169-170]
+                                        PathExpression [173-180]
+                                          Identifier(account) [173-180]
+                                GqlReturn [183-222]
+                                  Select [190-222]
+                                    SelectList [190-222]
+                                      SelectColumn [190-222]
+                                        FunctionCall [190-205]
+                                          PathExpression [190-195]
+                                            Identifier(COUNT) [190-195]
+                                          PathExpression [196-204]
+                                            Identifier(transfer) [196-204]
+                                        Alias [206-222]
+                                          Identifier(num_transfers) [209-222]
+                    Alias [225-241]
+                      Identifier(num_transfers) [228-241]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#call-subquery-find-accounts
+GRAPH FinGraph
+MATCH (p:Person)
+CALL (p) {
+  MATCH (p)-[:Owns]->(a:Account)
+  RETURN a.Id AS account_Id
+  ORDER BY account_Id
+}
+RETURN p.name AS person_name, account_Id
+ORDER BY person_name, account_Id
+--
+QueryStatement [0-201]
+  Query [0-201]
+    GqlQuery [0-201]
+      GraphTableQuery [0-201]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-201]
+          GqlOperatorList [15-201]
+            GqlMatch [15-31]
+              GraphPattern [21-31]
+                GraphPathPattern [21-31]
+                  GraphNodePattern [21-31]
+                    GraphElementPatternFiller [22-30]
+                      Identifier(p) [22-23]
+                      GraphLabelFilter [23-30]
+                        GraphElementLabel [24-30]
+                          Identifier(Person) [24-30]
+            GqlInlineSubqueryCall [32-127]
+              Query [41-127]
+                GqlQuery [41-127]
+                  GraphTableQuery [41-127]
+                    GqlOperatorList [45-125]
+                      GqlOperatorList [45-125]
+                        GqlMatch [45-75]
+                          GraphPattern [51-75]
+                            GraphPathPattern [51-75]
+                              GraphNodePattern [51-54]
+                                GraphElementPatternFiller [52-53]
+                                  Identifier(p) [52-53]
+                              GraphEdgePattern [54-64]
+                                GraphElementPatternFiller [56-61]
+                                  GraphLabelFilter [56-61]
+                                    GraphElementLabel [57-61]
+                                      Identifier(Owns) [57-61]
+                              GraphNodePattern [64-75]
+                                GraphElementPatternFiller [65-74]
+                                  Identifier(a) [65-66]
+                                  GraphLabelFilter [66-74]
+                                    GraphElementLabel [67-74]
+                                      Identifier(Account) [67-74]
+                        GqlReturn [78-125]
+                          Select [85-103]
+                            SelectList [85-103]
+                              SelectColumn [85-103]
+                                PathExpression [85-89]
+                                  Identifier(a) [85-86]
+                                  Identifier(Id) [87-89]
+                                Alias [90-103]
+                                  Identifier(account_Id) [93-103]
+                          GqlOrderByAndPage [106-125]
+                            OrderBy [106-125]
+                              OrderingExpression(ASC) [115-125]
+                                PathExpression [115-125]
+                                  Identifier(account_Id) [115-125]
+              IdentifierList [37-40]
+                Identifier(p) [38-39]
+            GqlReturn [128-201]
+              Select [135-168]
+                SelectList [135-168]
+                  SelectColumn [135-156]
+                    PathExpression [135-141]
+                      Identifier(p) [135-136]
+                      Identifier(name) [137-141]
+                    Alias [142-156]
+                      Identifier(person_name) [145-156]
+                  SelectColumn [158-168]
+                    PathExpression [158-168]
+                      Identifier(account_Id) [158-168]
+              GqlOrderByAndPage [169-201]
+                OrderBy [169-201]
+                  OrderingExpression(ASC) [178-189]
+                    PathExpression [178-189]
+                      Identifier(person_name) [178-189]
+                  OrderingExpression(ASC) [191-201]
+                    PathExpression [191-201]
+                      Identifier(account_Id) [191-201]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#query-parameters
+GRAPH FinGraph
+MATCH (person:Person {id: @id})
+RETURN person.name
+--
+QueryStatement [0-65]
+  Query [0-65]
+    GqlQuery [0-65]
+      GraphTableQuery [0-65]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-65]
+          GqlOperatorList [15-65]
+            GqlMatch [15-46]
+              GraphPattern [21-46]
+                GraphPathPattern [21-46]
+                  GraphNodePattern [21-46]
+                    GraphElementPatternFiller [22-45]
+                      Identifier(person) [22-28]
+                      GraphLabelFilter [28-35]
+                        GraphElementLabel [29-35]
+                          Identifier(Person) [29-35]
+                      GraphPropertySpecification [36-45]
+                        GraphPropertyNameAndValue [37-44]
+                          Identifier(id) [37-39]
+                          ParameterExpr [41-44]
+                            Identifier(id) [42-44]
+            GqlReturn [47-65]
+              Select [54-65]
+                SelectList [54-65]
+                  SelectColumn [54-65]
+                    PathExpression [54-65]
+                      Identifier(person) [54-60]
+                      Identifier(name) [61-65]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#query-graphs-and-tables-together
+CREATE TABLE CreditReports (
+  person_id     INT64 NOT NULL,
+  create_time   TIMESTAMP NOT NULL,
+  score         INT64 NOT NULL,
+) PRIMARY KEY (person_id, create_time)
+--
+CreateTableStatement [0-167]
+  PathExpression [13-26]
+    Identifier(CreditReports) [13-26]
+  TableElementList [27-130]
+    ColumnDefinition [31-59]
+      Identifier(person_id) [31-40]
+      SimpleColumnSchema [45-59]
+        PathExpression [45-50]
+          Identifier(INT64) [45-50]
+        ColumnAttributeList [51-59]
+          NotNullColumnAttribute [51-59]
+    ColumnDefinition [63-95]
+      Identifier(create_time) [63-74]
+      SimpleColumnSchema [77-95]
+        PathExpression [77-86]
+          Identifier(TIMESTAMP) [77-86]
+        ColumnAttributeList [87-95]
+          NotNullColumnAttribute [87-95]
+    ColumnDefinition [99-127]
+      Identifier(score) [99-104]
+      SimpleColumnSchema [113-127]
+        PathExpression [113-118]
+          Identifier(INT64) [113-118]
+        ColumnAttributeList [119-127]
+          NotNullColumnAttribute [119-127]
+  SpannerTableOptions [131-167]
+    PrimaryKey(ENFORCED) [131-167]
+      PrimaryKeyElementList [143-167]
+        PrimaryKeyElement(ASC) [144-153]
+          Identifier(person_id) [144-153]
+        PrimaryKeyElement(ASC) [155-166]
+          Identifier(create_time) [155-166]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#query-graphs-and-tables-together
+INSERT INTO CreditReports (person_id, create_time, score)
+VALUES
+  (1,"2020-01-10 06:22:20.222", 700),
+  (2,"2020-02-10 06:22:20.222", 800),
+  (3,"2020-03-10 06:22:20.222", 750)
+--
+InsertStatement [0-177]
+  PathExpression [12-25]
+    Identifier(CreditReports) [12-25]
+  ColumnList [26-57]
+    Identifier(person_id) [27-36]
+    Identifier(create_time) [38-49]
+    Identifier(score) [51-56]
+  InsertValuesRowList [58-177]
+    InsertValuesRow [67-101]
+      IntLiteral(1) [68-69]
+      StringLiteral [70-95]
+        StringLiteralComponent("2020-01-10 06:22:20.222") [70-95]
+      IntLiteral(700) [97-100]
+    InsertValuesRow [105-139]
+      IntLiteral(2) [106-107]
+      StringLiteral [108-133]
+        StringLiteralComponent("2020-02-10 06:22:20.222") [108-133]
+      IntLiteral(800) [135-138]
+    InsertValuesRow [143-177]
+      IntLiteral(3) [144-145]
+      StringLiteral [146-171]
+        StringLiteralComponent("2020-03-10 06:22:20.222") [146-171]
+      IntLiteral(750) [173-176]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/queries-overview#query-graphs-and-tables-together
+SELECT
+  gt.person.id,
+  credit.score AS latest_credit_score
+FROM GRAPH_TABLE(
+  FinGraph
+  MATCH (person:Person)-[:Owns]->(:Account)-[:Transfers]->(account:Account {is_blocked:true})
+  RETURN DISTINCT person
+) AS gt
+JOIN CreditReports AS credit
+  ON gt.person.id = credit.person_id
+ORDER BY credit.create_time
+--
+QueryStatement [0-310]
+  Query [0-310]
+    Select [0-282]
+      SelectList [9-60]
+        SelectColumn [9-21]
+          PathExpression [9-21]
+            Identifier(gt) [9-11]
+            Identifier(person) [12-18]
+            Identifier(id) [19-21]
+        SelectColumn [25-60]
+          PathExpression [25-37]
+            Identifier(credit) [25-31]
+            Identifier(score) [32-37]
+          Alias [38-60]
+            Identifier(latest_credit_score) [41-60]
+      FromClause [61-282]
+        Join [66-282]
+          GraphTableQuery [66-216]
+            PathExpression [81-89]
+              Identifier(FinGraph) [81-89]
+            GqlOperatorList [92-208]
+              GqlOperatorList [92-208]
+                GqlMatch [92-183]
+                  GraphPattern [98-183]
+                    GraphPathPattern [98-183]
+                      GraphNodePattern [98-113]
+                        GraphElementPatternFiller [99-112]
+                          Identifier(person) [99-105]
+                          GraphLabelFilter [105-112]
+                            GraphElementLabel [106-112]
+                              Identifier(Person) [106-112]
+                      GraphEdgePattern [113-123]
+                        GraphElementPatternFiller [115-120]
+                          GraphLabelFilter [115-120]
+                            GraphElementLabel [116-120]
+                              Identifier(Owns) [116-120]
+                      GraphNodePattern [123-133]
+                        GraphElementPatternFiller [124-132]
+                          GraphLabelFilter [124-132]
+                            GraphElementLabel [125-132]
+                              Identifier(Account) [125-132]
+                      GraphEdgePattern [133-148]
+                        GraphElementPatternFiller [135-145]
+                          GraphLabelFilter [135-145]
+                            GraphElementLabel [136-145]
+                              Identifier(Transfers) [136-145]
+                      GraphNodePattern [148-183]
+                        GraphElementPatternFiller [149-182]
+                          Identifier(account) [149-156]
+                          GraphLabelFilter [156-164]
+                            GraphElementLabel [157-164]
+                              Identifier(Account) [157-164]
+                          GraphPropertySpecification [165-182]
+                            GraphPropertyNameAndValue [166-181]
+                              Identifier(is_blocked) [166-176]
+                              BooleanLiteral(true) [177-181]
+                GqlReturn [186-208]
+                  Select(distinct=true) [193-208]
+                    SelectList [202-208]
+                      SelectColumn [202-208]
+                        PathExpression [202-208]
+                          Identifier(person) [202-208]
+            Alias [211-216]
+              Identifier(gt) [214-216]
+          Location [217-221]
+          TablePathExpression [222-245]
+            PathExpression [222-235]
+              Identifier(CreditReports) [222-235]
+            Alias [236-245]
+              Identifier(credit) [239-245]
+          OnClause [248-282]
+            BinaryExpression(=) [251-282]
+              PathExpression [251-263]
+                Identifier(gt) [251-253]
+                Identifier(person) [254-260]
+                Identifier(id) [261-263]
+              PathExpression [266-282]
+                Identifier(credit) [266-272]
+                Identifier(person_id) [273-282]
+    OrderBy [283-310]
+      OrderingExpression(ASC) [292-310]
+        PathExpression [292-310]
+          Identifier(credit) [292-298]
+          Identifier(create_time) [299-310]
diff --git a/parser/testdata/spanner/guides/graph.reverse-etl.metadata.json b/parser/testdata/spanner/guides/graph.reverse-etl.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.reverse-etl.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/graph.reverse-etl.spanner.sql b/parser/testdata/spanner/guides/graph.reverse-etl.spanner.sql
new file mode 100644
index 0000000..1f84bab
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.reverse-etl.spanner.sql
@@ -0,0 +1,31 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/reverse-etl
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/graph/reverse-etl#relax_referential_integrity
+# id 4c161277f464: INTERLEAVE IN without PARENT (graph.reverse-etl)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected keyword PARENT but got identifier "Person" [at 7:15]
+CREATE TABLE PersonOwnAccount (
+  id          INT64 NOT NULL,
+  account_id  INT64 NOT NULL,
+  create_time TIMESTAMP,
+  CONSTRAINT FK_Account FOREIGN KEY (account_id) REFERENCES Account (id) NOT ENFORCED
+) PRIMARY KEY (id, account_id),
+INTERLEAVE IN Person;
+
+# https://docs.cloud.google.com/spanner/docs/graph/reverse-etl#perform_a_logical_or_soft_delete_in_the_source
+# id 27727b8671c2: row deletion policy (TTL) (graph.reverse-etl)
+# execute_query: INVALID_ARGUMENT: ADD ROW DELETION POLICY clause is not supported. [at 2:7]
+ALTER TABLE PersonOwnAccount
+  ADD ROW DELETION POLICY (OLDER_THAN(expired_ts_generated, INTERVAL 0 DAY));
+
+# https://docs.cloud.google.com/spanner/docs/graph/reverse-etl#bq-change-history
+# id c2e5bc718928: row deletion policy (TTL) (graph.reverse-etl)
+# execute_query: INVALID_ARGUMENT: ADD ROW DELETION POLICY clause is not supported. [at 2:7]
+ALTER TABLE PersonOwnAccount
+  ADD ROW DELETION POLICY (OLDER_THAN(expired_ts, INTERVAL 1 DAY));
diff --git a/parser/testdata/spanner/guides/graph.reverse-etl.test b/parser/testdata/spanner/guides/graph.reverse-etl.test
new file mode 100644
index 0000000..4c59d2e
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.reverse-etl.test
@@ -0,0 +1,241 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/reverse-etl
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/graph/reverse-etl#insert-data
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Person (
+  id    INT64 NOT NULL,
+  name  STRING(MAX)
+) PRIMARY KEY (id)
+--
+CreateTableStatement [0-84]
+  PathExpression [13-19]
+    Identifier(Person) [13-19]
+  TableElementList [20-67]
+    ColumnDefinition [24-44]
+      Identifier(id) [24-26]
+      SimpleColumnSchema [30-44]
+        PathExpression [30-35]
+          Identifier(INT64) [30-35]
+        ColumnAttributeList [36-44]
+          NotNullColumnAttribute [36-44]
+    ColumnDefinition [48-65]
+      Identifier(name) [48-52]
+      SimpleColumnSchema [54-65]
+        PathExpression [54-60]
+          Identifier(STRING) [54-60]
+        TypeParameterList [60-64]
+          MaxLiteral() [61-64]
+  SpannerTableOptions [68-84]
+    PrimaryKey(ENFORCED) [68-84]
+      PrimaryKeyElementList [80-84]
+        PrimaryKeyElement(ASC) [81-83]
+          Identifier(id) [81-83]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/reverse-etl#insert-data
+CREATE TABLE Account (
+  id           INT64 NOT NULL,
+  create_time  TIMESTAMP,
+  is_blocked   BOOL,
+  type        STRING(MAX)
+) PRIMARY KEY (id)
+--
+CreateTableStatement [0-145]
+  PathExpression [13-20]
+    Identifier(Account) [13-20]
+  TableElementList [21-128]
+    ColumnDefinition [25-52]
+      Identifier(id) [25-27]
+      SimpleColumnSchema [38-52]
+        PathExpression [38-43]
+          Identifier(INT64) [38-43]
+        ColumnAttributeList [44-52]
+          NotNullColumnAttribute [44-52]
+    ColumnDefinition [56-78]
+      Identifier(create_time) [56-67]
+      SimpleColumnSchema [69-78]
+        PathExpression [69-78]
+          Identifier(TIMESTAMP) [69-78]
+    ColumnDefinition [82-99]
+      Identifier(is_blocked) [82-92]
+      SimpleColumnSchema [95-99]
+        PathExpression [95-99]
+          Identifier(BOOL) [95-99]
+    ColumnDefinition [103-126]
+      Identifier(type) [103-107]
+      SimpleColumnSchema [115-126]
+        PathExpression [115-121]
+          Identifier(STRING) [115-121]
+        TypeParameterList [121-125]
+          MaxLiteral() [122-125]
+  SpannerTableOptions [129-145]
+    PrimaryKey(ENFORCED) [129-145]
+      PrimaryKeyElementList [141-145]
+        PrimaryKeyElement(ASC) [142-144]
+          Identifier(id) [142-144]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/reverse-etl#insert-data
+CREATE TABLE PersonOwnAccount (
+  id           INT64 NOT NULL,
+  account_id   INT64 NOT NULL,
+  create_time  TIMESTAMP,
+  CONSTRAINT FK_Account FOREIGN KEY (account_id) REFERENCES Account (id)
+) PRIMARY KEY (id, account_id),
+  INTERLEAVE IN PARENT Person ON DELETE CASCADE
+--
+CreateTableStatement [0-272]
+  PathExpression [13-29]
+    Identifier(PersonOwnAccount) [13-29]
+  TableElementList [30-194]
+    ColumnDefinition [34-61]
+      Identifier(id) [34-36]
+      SimpleColumnSchema [47-61]
+        PathExpression [47-52]
+          Identifier(INT64) [47-52]
+        ColumnAttributeList [53-61]
+          NotNullColumnAttribute [53-61]
+    ColumnDefinition [65-92]
+      Identifier(account_id) [65-75]
+      SimpleColumnSchema [78-92]
+        PathExpression [78-83]
+          Identifier(INT64) [78-83]
+        ColumnAttributeList [84-92]
+          NotNullColumnAttribute [84-92]
+    ColumnDefinition [96-118]
+      Identifier(create_time) [96-107]
+      SimpleColumnSchema [109-118]
+        PathExpression [109-118]
+          Identifier(TIMESTAMP) [109-118]
+    ForeignKey [122-192]
+      ColumnList [156-168]
+        Identifier(account_id) [157-167]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [169-192]
+        PathExpression [180-187]
+          Identifier(Account) [180-187]
+        ColumnList [188-192]
+          Identifier(id) [189-191]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [192-192]
+      Identifier(FK_Account) [133-143]
+  SpannerTableOptions [195-272]
+    PrimaryKey(ENFORCED) [195-223]
+      PrimaryKeyElementList [207-223]
+        PrimaryKeyElement(ASC) [208-210]
+          Identifier(id) [208-210]
+        PrimaryKeyElement(ASC) [212-222]
+          Identifier(account_id) [212-222]
+    SpannerInterleaveClause [223-272]
+      PathExpression [248-254]
+        Identifier(Person) [248-254]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/reverse-etl#insert-data
+CREATE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Person,
+    Account
+  )
+  EDGE TABLES (
+    PersonOwnAccount
+      SOURCE KEY (id) REFERENCES Person
+      DESTINATION KEY (account_id) REFERENCES Account
+      LABEL Owns
+  )
+--
+CreatePropertyGraphStatement [0-226]
+  PathExpression [22-30]
+    Identifier(FinGraph) [22-30]
+  GraphElementTableList [45-74]
+    GraphElementTable [51-57]
+      PathExpression [51-57]
+        Identifier(Person) [51-57]
+      GraphElementLabelAndPropertiesList [57-57]
+        GraphElementLabelAndProperties [57-57]
+          GraphProperties [57-57]
+    GraphElementTable [63-70]
+      PathExpression [63-70]
+        Identifier(Account) [63-70]
+      GraphElementLabelAndPropertiesList [70-70]
+        GraphElementLabelAndProperties [70-70]
+          GraphProperties [70-70]
+  GraphElementTableList [89-226]
+    GraphElementTable [95-222]
+      PathExpression [95-111]
+        Identifier(PersonOwnAccount) [95-111]
+      GraphNodeTableReference(SOURCE) [118-151]
+        Identifier(Person) [145-151]
+        ColumnList [129-133]
+          Identifier(id) [130-132]
+      GraphNodeTableReference(DESTINATION) [158-205]
+        Identifier(Account) [198-205]
+        ColumnList [174-186]
+          Identifier(account_id) [175-185]
+      GraphElementLabelAndPropertiesList [212-222]
+        GraphElementLabelAndProperties [212-222]
+          Identifier(Owns) [218-222]
+          GraphProperties [222-222]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/reverse-etl#perform_a_logical_or_soft_delete_in_the_source
+ALTER TABLE PersonOwnAccount
+  ADD COLUMN is_deleted BOOL DEFAULT (FALSE)
+--
+AlterTableStatement [0-73]
+  PathExpression [12-28]
+    Identifier(PersonOwnAccount) [12-28]
+  AlterActionList [31-73]
+    AddColumnAction [31-73]
+      ColumnDefinition [42-73]
+        Identifier(is_deleted) [42-52]
+        SimpleColumnSchema [53-73]
+          PathExpression [53-57]
+            Identifier(BOOL) [53-57]
+          BooleanLiteral(FALSE) [67-72]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/reverse-etl#perform_a_logical_or_soft_delete_in_the_source
+ALTER TABLE PersonOwnAccount ADD COLUMN
+  expired_ts_generated TIMESTAMP AS (IF(is_deleted,
+    TIMESTAMP("1970-01-01 00:00:00+00"),
+    TIMESTAMP("9999-01-01 00:00:00+00"))) STORED HIDDEN
+--
+AlterTableStatement [0-188]
+  PathExpression [12-28]
+    Identifier(PersonOwnAccount) [12-28]
+  AlterActionList [29-188]
+    AddColumnAction [29-188]
+      ColumnDefinition [42-188]
+        Identifier(expired_ts_generated) [42-62]
+        SimpleColumnSchema [63-188]
+          PathExpression [63-72]
+            Identifier(TIMESTAMP) [63-72]
+          GeneratedColumnInfo(generated_mode=ALWAYS, stored_mode=STORED) [73-181]
+            FunctionCall [77-173]
+              PathExpression [77-79]
+                Identifier(`IF`) [77-79]
+              PathExpression [80-90]
+                Identifier(is_deleted) [80-90]
+              FunctionCall [96-131]
+                PathExpression [96-105]
+                  Identifier(TIMESTAMP) [96-105]
+                StringLiteral [106-130]
+                  StringLiteralComponent("1970-01-01 00:00:00+00") [106-130]
+              FunctionCall [137-172]
+                PathExpression [137-146]
+                  Identifier(TIMESTAMP) [137-146]
+                StringLiteral [147-171]
+                  StringLiteralComponent("9999-01-01 00:00:00+00") [147-171]
+          ColumnAttributeList [182-188]
+            HiddenColumnAttribute [182-188]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/reverse-etl#bq-change-history
+ALTER TABLE PersonOwnAccount ADD COLUMN expired_ts TIMESTAMP
+--
+AlterTableStatement [0-60]
+  PathExpression [12-28]
+    Identifier(PersonOwnAccount) [12-28]
+  AlterActionList [29-60]
+    AddColumnAction [29-60]
+      ColumnDefinition [40-60]
+        Identifier(expired_ts) [40-50]
+        SimpleColumnSchema [51-60]
+          PathExpression [51-60]
+            Identifier(TIMESTAMP) [51-60]
diff --git a/parser/testdata/spanner/guides/graph.run-algorithms.metadata.json b/parser/testdata/spanner/guides/graph.run-algorithms.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.run-algorithms.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/graph.run-algorithms.test b/parser/testdata/spanner/guides/graph.run-algorithms.test
new file mode 100644
index 0000000..3940006
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.run-algorithms.test
@@ -0,0 +1,361 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/run-algorithms
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/graph/run-algorithms#before-you-begin
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+-- Add `page_rank` as a column. Data type of this column matches the data type defined in `PageRank` output signature.
+ALTER TABLE Account ADD COLUMN page_rank FLOAT64
+--
+AlterTableStatement [119-167]
+  PathExpression [131-138]
+    Identifier(Account) [131-138]
+  AlterActionList [139-167]
+    AddColumnAction [139-167]
+      ColumnDefinition [150-167]
+        Identifier(page_rank) [150-159]
+        SimpleColumnSchema [160-167]
+          PathExpression [160-167]
+            Identifier(FLOAT64) [160-167]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/run-algorithms#before-you-begin
+-- Rerun the graph definition DDL to pickup `page_rank` as a new property.
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (`Account`, `Person`)
+  EDGE TABLES (
+    `PersonOwnAccount`
+      SOURCE KEY (id) REFERENCES `Person` (id)
+      DESTINATION KEY (account_id) REFERENCES `Account` (id)
+      LABEL `Owns`,
+    `AccountTransferAccount`
+      SOURCE KEY (id) REFERENCES `Account` (id)
+      DESTINATION KEY (to_id) REFERENCES `Account` (id)
+      LABEL `Transfers`
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [75-480]
+  PathExpression [108-116]
+    Identifier(FinGraph) [108-116]
+  GraphElementTableList [131-152]
+    GraphElementTable [132-141]
+      PathExpression [132-141]
+        Identifier(Account) [132-141]
+      GraphElementLabelAndPropertiesList [141-141]
+        GraphElementLabelAndProperties [141-141]
+          GraphProperties [141-141]
+    GraphElementTable [143-151]
+      PathExpression [143-151]
+        Identifier(Person) [143-151]
+      GraphElementLabelAndPropertiesList [151-151]
+        GraphElementLabelAndProperties [151-151]
+          GraphProperties [151-151]
+  GraphElementTableList [167-480]
+    GraphElementTable [173-318]
+      PathExpression [173-191]
+        Identifier(PersonOwnAccount) [173-191]
+      GraphNodeTableReference(SOURCE) [198-238]
+        Identifier(Person) [225-233]
+        ColumnList [209-213]
+          Identifier(id) [210-212]
+        ColumnList [234-238]
+          Identifier(id) [235-237]
+      GraphNodeTableReference(DESTINATION) [245-299]
+        Identifier(Account) [285-294]
+        ColumnList [261-273]
+          Identifier(account_id) [262-272]
+        ColumnList [295-299]
+          Identifier(id) [296-298]
+      GraphElementLabelAndPropertiesList [306-318]
+        GraphElementLabelAndProperties [306-318]
+          Identifier(Owns) [312-318]
+          GraphProperties [318-318]
+    GraphElementTable [324-476]
+      PathExpression [324-348]
+        Identifier(AccountTransferAccount) [324-348]
+      GraphNodeTableReference(SOURCE) [355-396]
+        Identifier(Account) [382-391]
+        ColumnList [366-370]
+          Identifier(id) [367-369]
+        ColumnList [392-396]
+          Identifier(id) [393-395]
+      GraphNodeTableReference(DESTINATION) [403-452]
+        Identifier(Account) [438-447]
+        ColumnList [419-426]
+          Identifier(to_id) [420-425]
+        ColumnList [448-452]
+          Identifier(id) [449-451]
+      GraphElementLabelAndPropertiesList [459-476]
+        GraphElementLabelAndProperties [459-476]
+          Identifier(Transfers) [465-476]
+          GraphProperties [476-476]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/run-algorithms#full-graph-label-filter-gcs
+EXPORT DATA OPTIONS (
+  uri = "gs://my-bucket-name/my-output.csv",
+  format = "csv"
+) AS
+GRAPH FinGraph
+CALL PageRank(node_labels => ['Account'], edge_labels => ['Transfers']) YIELD node, score
+RETURN node.id, score AS page_rank
+--
+ExportDataStatement [0-228]
+  OptionsList [20-85]
+    OptionsEntry [24-65]
+      Identifier(uri) [24-27]
+      StringLiteral [30-65]
+        StringLiteralComponent("gs://my-bucket-name/my-output.csv") [30-65]
+    OptionsEntry [69-83]
+      Identifier(format) [69-75]
+      StringLiteral [78-83]
+        StringLiteralComponent("csv") [78-83]
+  Query [89-228]
+    GqlQuery [89-228]
+      GraphTableQuery [89-228]
+        PathExpression [95-103]
+          Identifier(FinGraph) [95-103]
+        GqlOperatorList [104-228]
+          GqlOperatorList [104-228]
+            GqlNamedCall [104-193]
+              TVF [109-175]
+                PathExpression [109-117]
+                  Identifier(PageRank) [109-117]
+                TVFArgument [118-144]
+                  NamedArgument [118-144]
+                    Identifier(node_labels) [118-129]
+                    ArrayConstructor [133-144]
+                      StringLiteral [134-143]
+                        StringLiteralComponent('Account') [134-143]
+                TVFArgument [146-174]
+                  NamedArgument [146-174]
+                    Identifier(edge_labels) [146-157]
+                    ArrayConstructor [161-174]
+                      StringLiteral [162-173]
+                        StringLiteralComponent('Transfers') [162-173]
+              YieldItemList [176-193]
+                ExpressionWithOptAlias [182-186]
+                  Identifier(node) [182-186]
+                ExpressionWithOptAlias [188-193]
+                  Identifier(score) [188-193]
+            GqlReturn [194-228]
+              Select [201-228]
+                SelectList [201-228]
+                  SelectColumn [201-208]
+                    PathExpression [201-208]
+                      Identifier(node) [201-205]
+                      Identifier(id) [206-208]
+                  SelectColumn [210-228]
+                    PathExpression [210-215]
+                      Identifier(score) [210-215]
+                    Alias [216-228]
+                      Identifier(page_rank) [219-228]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/run-algorithms#subgraph-spanner
+EXPORT DATA OPTIONS (
+  format = "CLOUD_SPANNER",
+  table = "Account",
+  write_mode = 'update_ignore_all'
+) AS
+GRAPH FinGraph
+MATCH (n:Account)
+RETURN n
+FULL UNION ALL
+MATCH -[e:Transfers WHERE e.amount < 500]->
+RETURN e
+NEXT
+CALL PER () PageRank() YIELD node, score
+RETURN node.id, score AS page_rank
+--
+ExportDataStatement [0-301]
+  OptionsList [20-107]
+    OptionsEntry [24-48]
+      Identifier(format) [24-30]
+      StringLiteral [33-48]
+        StringLiteralComponent("CLOUD_SPANNER") [33-48]
+    OptionsEntry [52-69]
+      Identifier(table) [52-57]
+      StringLiteral [60-69]
+        StringLiteralComponent("Account") [60-69]
+    OptionsEntry [73-105]
+      Identifier(write_mode) [73-83]
+      StringLiteral [86-105]
+        StringLiteralComponent('update_ignore_all') [86-105]
+  Query [111-301]
+    GqlQuery [111-301]
+      GraphTableQuery [111-301]
+        PathExpression [117-125]
+          Identifier(FinGraph) [117-125]
+        GqlOperatorList [126-301]
+          GqlSetOperation [126-220]
+            SetOperationMetadataList [153-167]
+              SetOperationMetadata [153-167]
+                SetOperationType [158-163]
+                SetOperationAllOrDistinct [164-167]
+                SetOperationColumnPropagationMode [153-157]
+            GqlOperatorList [126-152]
+              GqlMatch [126-143]
+                GraphPattern [132-143]
+                  GraphPathPattern [132-143]
+                    GraphNodePattern [132-143]
+                      GraphElementPatternFiller [133-142]
+                        Identifier(n) [133-134]
+                        GraphLabelFilter [134-142]
+                          GraphElementLabel [135-142]
+                            Identifier(Account) [135-142]
+              GqlReturn [144-152]
+                Select [151-152]
+                  SelectList [151-152]
+                    SelectColumn [151-152]
+                      PathExpression [151-152]
+                        Identifier(n) [151-152]
+            GqlOperatorList [168-220]
+              GqlMatch [168-211]
+                GraphPattern [174-211]
+                  GraphPathPattern [174-211]
+                    GraphEdgePattern [174-211]
+                      GraphElementPatternFiller [176-208]
+                        Identifier(e) [176-177]
+                        GraphLabelFilter [177-187]
+                          GraphElementLabel [178-187]
+                            Identifier(Transfers) [178-187]
+                        WhereClause [188-208]
+                          BinaryExpression(<) [194-208]
+                            PathExpression [194-202]
+                              Identifier(e) [194-195]
+                              Identifier(amount) [196-202]
+                            IntLiteral(500) [205-208]
+              GqlReturn [212-220]
+                Select [219-220]
+                  SelectList [219-220]
+                    SelectColumn [219-220]
+                      PathExpression [219-220]
+                        Identifier(e) [219-220]
+          GqlOperatorList [226-301]
+            GqlNamedCall [226-266]
+              TVF [238-248]
+                PathExpression [238-246]
+                  Identifier(PageRank) [238-246]
+              YieldItemList [249-266]
+                ExpressionWithOptAlias [255-259]
+                  Identifier(node) [255-259]
+                ExpressionWithOptAlias [261-266]
+                  Identifier(score) [261-266]
+              IdentifierList [235-237]
+            GqlReturn [267-301]
+              Select [274-301]
+                SelectList [274-301]
+                  SelectColumn [274-281]
+                    PathExpression [274-281]
+                      Identifier(node) [274-278]
+                      Identifier(id) [279-281]
+                  SelectColumn [283-301]
+                    PathExpression [283-288]
+                      Identifier(score) [283-288]
+                    Alias [289-301]
+                      Identifier(page_rank) [292-301]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/run-algorithms#subgraph-spanner
+GRAPH FinGraph
+MATCH (n:Account)
+RETURN n.id, ROUND(n.page_rank, 2) AS page_rank
+ORDER BY page_rank DESC, id ASC
+--
+QueryStatement [0-112]
+  Query [0-112]
+    GqlQuery [0-112]
+      GraphTableQuery [0-112]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-112]
+          GqlOperatorList [15-112]
+            GqlMatch [15-32]
+              GraphPattern [21-32]
+                GraphPathPattern [21-32]
+                  GraphNodePattern [21-32]
+                    GraphElementPatternFiller [22-31]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+            GqlReturn [33-112]
+              Select [40-80]
+                SelectList [40-80]
+                  SelectColumn [40-44]
+                    PathExpression [40-44]
+                      Identifier(n) [40-41]
+                      Identifier(id) [42-44]
+                  SelectColumn [46-80]
+                    FunctionCall [46-67]
+                      PathExpression [46-51]
+                        Identifier(ROUND) [46-51]
+                      PathExpression [52-63]
+                        Identifier(n) [52-53]
+                        Identifier(page_rank) [54-63]
+                      IntLiteral(2) [65-66]
+                    Alias [68-80]
+                      Identifier(page_rank) [71-80]
+              GqlOrderByAndPage [81-112]
+                OrderBy [81-112]
+                  OrderingExpression(DESC) [90-104]
+                    PathExpression [90-99]
+                      Identifier(page_rank) [90-99]
+                  OrderingExpression(ASC EXPLICITLY) [106-112]
+                    PathExpression [106-108]
+                      Identifier(id) [106-108]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/run-algorithms#graph-operation-execution-status-schema
+SELECT
+  query_id,
+  query_text,
+  start_timestamp,
+  last_update_timestamp,
+  progress,
+  status,
+  error_message
+FROM
+  SPANNER_SYS.GRAPH_OPERATION_EXECUTION_STATUS
+WHERE
+  status != "OK"
+ORDER BY
+  start_timestamp DESC
+--
+QueryStatement [0-221]
+  Query [0-221]
+    Select [0-189]
+      SelectList [9-114]
+        SelectColumn [9-17]
+          PathExpression [9-17]
+            Identifier(query_id) [9-17]
+        SelectColumn [21-31]
+          PathExpression [21-31]
+            Identifier(query_text) [21-31]
+        SelectColumn [35-50]
+          PathExpression [35-50]
+            Identifier(start_timestamp) [35-50]
+        SelectColumn [54-75]
+          PathExpression [54-75]
+            Identifier(last_update_timestamp) [54-75]
+        SelectColumn [79-87]
+          PathExpression [79-87]
+            Identifier(progress) [79-87]
+        SelectColumn [91-97]
+          PathExpression [91-97]
+            Identifier(status) [91-97]
+        SelectColumn [101-114]
+          PathExpression [101-114]
+            Identifier(error_message) [101-114]
+      FromClause [115-166]
+        TablePathExpression [122-166]
+          PathExpression [122-166]
+            Identifier(SPANNER_SYS) [122-133]
+            Identifier(GRAPH_OPERATION_EXECUTION_STATUS) [134-166]
+      WhereClause [167-189]
+        BinaryExpression(!=) [175-189]
+          PathExpression [175-181]
+            Identifier(status) [175-181]
+          StringLiteral [185-189]
+            StringLiteralComponent("OK") [185-189]
+    OrderBy [190-221]
+      OrderingExpression(DESC) [201-221]
+        PathExpression [201-216]
+          Identifier(start_timestamp) [201-216]
diff --git a/parser/testdata/spanner/guides/graph.schema-overview.metadata.json b/parser/testdata/spanner/guides/graph.schema-overview.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.schema-overview.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/graph.schema-overview.test b/parser/testdata/spanner/guides/graph.schema-overview.test
new file mode 100644
index 0000000..2749817
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.schema-overview.test
@@ -0,0 +1,559 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/schema-overview
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/graph/schema-overview#defining-node-property-graph
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+-- First, create an Account table.
+CREATE TABLE Account (
+  id           INT64 NOT NULL,
+  create_time  TIMESTAMP,
+) PRIMARY KEY (id)
+--
+CreateTableStatement [35-133]
+  PathExpression [48-55]
+    Identifier(Account) [48-55]
+  TableElementList [56-116]
+    ColumnDefinition [60-87]
+      Identifier(id) [60-62]
+      SimpleColumnSchema [73-87]
+        PathExpression [73-78]
+          Identifier(INT64) [73-78]
+        ColumnAttributeList [79-87]
+          NotNullColumnAttribute [79-87]
+    ColumnDefinition [91-113]
+      Identifier(create_time) [91-102]
+      SimpleColumnSchema [104-113]
+        PathExpression [104-113]
+          Identifier(TIMESTAMP) [104-113]
+  SpannerTableOptions [117-133]
+    PrimaryKey(ENFORCED) [117-133]
+      PrimaryKeyElementList [129-133]
+        PrimaryKeyElement(ASC) [130-132]
+          Identifier(id) [130-132]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/schema-overview#defining-node-property-graph
+-- Next, use the Account table as input table of Account node definition.
+CREATE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Account
+  )
+--
+CreatePropertyGraphStatement [74-136]
+  PathExpression [96-104]
+    Identifier(FinGraph) [96-104]
+  GraphElementTableList [119-136]
+    GraphElementTable [125-132]
+      PathExpression [125-132]
+        Identifier(Account) [125-132]
+      GraphElementLabelAndPropertiesList [132-132]
+        GraphElementLabelAndProperties [132-132]
+          GraphProperties [132-132]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/schema-overview#element-key
+CREATE TABLE Person (
+  id           INT64 NOT NULL,
+  name         STRING(MAX),
+) PRIMARY KEY (id)
+--
+CreateTableStatement [0-99]
+  PathExpression [13-19]
+    Identifier(Person) [13-19]
+  TableElementList [20-82]
+    ColumnDefinition [24-51]
+      Identifier(id) [24-26]
+      SimpleColumnSchema [37-51]
+        PathExpression [37-42]
+          Identifier(INT64) [37-42]
+        ColumnAttributeList [43-51]
+          NotNullColumnAttribute [43-51]
+    ColumnDefinition [55-79]
+      Identifier(name) [55-59]
+      SimpleColumnSchema [68-79]
+        PathExpression [68-74]
+          Identifier(STRING) [68-74]
+        TypeParameterList [74-78]
+          MaxLiteral() [75-78]
+  SpannerTableOptions [83-99]
+    PrimaryKey(ENFORCED) [83-99]
+      PrimaryKeyElementList [95-99]
+        PrimaryKeyElement(ASC) [96-98]
+          Identifier(id) [96-98]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/schema-overview#element-key
+CREATE TABLE Account (
+  id           INT64 NOT NULL,
+  create_time  TIMESTAMP,
+) PRIMARY KEY (id)
+--
+CreateTableStatement [0-98]
+  PathExpression [13-20]
+    Identifier(Account) [13-20]
+  TableElementList [21-81]
+    ColumnDefinition [25-52]
+      Identifier(id) [25-27]
+      SimpleColumnSchema [38-52]
+        PathExpression [38-43]
+          Identifier(INT64) [38-43]
+        ColumnAttributeList [44-52]
+          NotNullColumnAttribute [44-52]
+    ColumnDefinition [56-78]
+      Identifier(create_time) [56-67]
+      SimpleColumnSchema [69-78]
+        PathExpression [69-78]
+          Identifier(TIMESTAMP) [69-78]
+  SpannerTableOptions [82-98]
+    PrimaryKey(ENFORCED) [82-98]
+      PrimaryKeyElementList [94-98]
+        PrimaryKeyElement(ASC) [95-97]
+          Identifier(id) [95-97]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/schema-overview#element-key
+CREATE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Person KEY (id),
+    Account
+  )
+--
+CreatePropertyGraphStatement [0-83]
+  PathExpression [22-30]
+    Identifier(FinGraph) [22-30]
+  GraphElementTableList [45-83]
+    GraphElementTable [51-66]
+      PathExpression [51-57]
+        Identifier(Person) [51-57]
+      ColumnList [62-66]
+        Identifier(id) [63-65]
+      GraphElementLabelAndPropertiesList [66-66]
+        GraphElementLabelAndProperties [66-66]
+          GraphProperties [66-66]
+    GraphElementTable [72-79]
+      PathExpression [72-79]
+        Identifier(Account) [72-79]
+      GraphElementLabelAndPropertiesList [79-79]
+        GraphElementLabelAndProperties [79-79]
+          GraphProperties [79-79]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/schema-overview#source-destination-node-reference
+CREATE TABLE Person (
+  id            INT64 NOT NULL,
+  name          STRING(MAX),
+) PRIMARY KEY (id)
+--
+CreateTableStatement [0-101]
+  PathExpression [13-19]
+    Identifier(Person) [13-19]
+  TableElementList [20-84]
+    ColumnDefinition [24-52]
+      Identifier(id) [24-26]
+      SimpleColumnSchema [38-52]
+        PathExpression [38-43]
+          Identifier(INT64) [38-43]
+        ColumnAttributeList [44-52]
+          NotNullColumnAttribute [44-52]
+    ColumnDefinition [56-81]
+      Identifier(name) [56-60]
+      SimpleColumnSchema [70-81]
+        PathExpression [70-76]
+          Identifier(STRING) [70-76]
+        TypeParameterList [76-80]
+          MaxLiteral() [77-80]
+  SpannerTableOptions [85-101]
+    PrimaryKey(ENFORCED) [85-101]
+      PrimaryKeyElementList [97-101]
+        PrimaryKeyElement(ASC) [98-100]
+          Identifier(id) [98-100]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/schema-overview#source-destination-node-reference
+CREATE TABLE Account (
+  id            INT64 NOT NULL,
+  create_time   TIMESTAMP,
+) PRIMARY KEY (id)
+--
+CreateTableStatement [0-100]
+  PathExpression [13-20]
+    Identifier(Account) [13-20]
+  TableElementList [21-83]
+    ColumnDefinition [25-53]
+      Identifier(id) [25-27]
+      SimpleColumnSchema [39-53]
+        PathExpression [39-44]
+          Identifier(INT64) [39-44]
+        ColumnAttributeList [45-53]
+          NotNullColumnAttribute [45-53]
+    ColumnDefinition [57-80]
+      Identifier(create_time) [57-68]
+      SimpleColumnSchema [71-80]
+        PathExpression [71-80]
+          Identifier(TIMESTAMP) [71-80]
+  SpannerTableOptions [84-100]
+    PrimaryKey(ENFORCED) [84-100]
+      PrimaryKeyElementList [96-100]
+        PrimaryKeyElement(ASC) [97-99]
+          Identifier(id) [97-99]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/schema-overview#source-destination-node-reference
+CREATE TABLE PersonOwnAccount (
+  id            INT64 NOT NULL,
+  account_id    INT64 NOT NULL,
+  create_time   TIMESTAMP,
+  FOREIGN KEY (account_id) REFERENCES Account (id)
+) PRIMARY KEY (id, account_id),
+  INTERLEAVE IN PARENT Person
+--
+CreateTableStatement [0-235]
+  PathExpression [13-29]
+    Identifier(PersonOwnAccount) [13-29]
+  TableElementList [30-175]
+    ColumnDefinition [34-62]
+      Identifier(id) [34-36]
+      SimpleColumnSchema [48-62]
+        PathExpression [48-53]
+          Identifier(INT64) [48-53]
+        ColumnAttributeList [54-62]
+          NotNullColumnAttribute [54-62]
+    ColumnDefinition [66-94]
+      Identifier(account_id) [66-76]
+      SimpleColumnSchema [80-94]
+        PathExpression [80-85]
+          Identifier(INT64) [80-85]
+        ColumnAttributeList [86-94]
+          NotNullColumnAttribute [86-94]
+    ColumnDefinition [98-121]
+      Identifier(create_time) [98-109]
+      SimpleColumnSchema [112-121]
+        PathExpression [112-121]
+          Identifier(TIMESTAMP) [112-121]
+    ForeignKey [125-173]
+      ColumnList [137-149]
+        Identifier(account_id) [138-148]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [150-173]
+        PathExpression [161-168]
+          Identifier(Account) [161-168]
+        ColumnList [169-173]
+          Identifier(id) [170-172]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [173-173]
+  SpannerTableOptions [176-235]
+    PrimaryKey(ENFORCED) [176-204]
+      PrimaryKeyElementList [188-204]
+        PrimaryKeyElement(ASC) [189-191]
+          Identifier(id) [189-191]
+        PrimaryKeyElement(ASC) [193-203]
+          Identifier(account_id) [193-203]
+    SpannerInterleaveClause [204-235]
+      PathExpression [229-235]
+        Identifier(Person) [229-235]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/schema-overview#source-destination-node-reference
+CREATE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Person,
+    Account
+  )
+  EDGE TABLES (
+    PersonOwnAccount
+      SOURCE KEY (id) REFERENCES Person (id)
+      DESTINATION KEY (account_id) REFERENCES Account (id)
+  )
+--
+CreatePropertyGraphStatement [0-219]
+  PathExpression [22-30]
+    Identifier(FinGraph) [22-30]
+  GraphElementTableList [45-74]
+    GraphElementTable [51-57]
+      PathExpression [51-57]
+        Identifier(Person) [51-57]
+      GraphElementLabelAndPropertiesList [57-57]
+        GraphElementLabelAndProperties [57-57]
+          GraphProperties [57-57]
+    GraphElementTable [63-70]
+      PathExpression [63-70]
+        Identifier(Account) [63-70]
+      GraphElementLabelAndPropertiesList [70-70]
+        GraphElementLabelAndProperties [70-70]
+          GraphProperties [70-70]
+  GraphElementTableList [89-219]
+    GraphElementTable [95-215]
+      PathExpression [95-111]
+        Identifier(PersonOwnAccount) [95-111]
+      GraphNodeTableReference(SOURCE) [118-156]
+        Identifier(Person) [145-151]
+        ColumnList [129-133]
+          Identifier(id) [130-132]
+        ColumnList [152-156]
+          Identifier(id) [153-155]
+      GraphNodeTableReference(DESTINATION) [163-215]
+        Identifier(Account) [203-210]
+        ColumnList [179-191]
+          Identifier(account_id) [180-190]
+        ColumnList [211-215]
+          Identifier(id) [212-214]
+      GraphElementLabelAndPropertiesList [215-215]
+        GraphElementLabelAndProperties [215-215]
+          GraphProperties [215-215]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/schema-overview#define-nodes-and-edges-single-table
+CREATE TABLE Person (
+    id INT64 NOT NULL,
+  ) PRIMARY KEY (id)
+--
+CreateTableStatement [0-65]
+  PathExpression [13-19]
+    Identifier(Person) [13-19]
+  TableElementList [20-48]
+    ColumnDefinition [26-43]
+      Identifier(id) [26-28]
+      SimpleColumnSchema [29-43]
+        PathExpression [29-34]
+          Identifier(INT64) [29-34]
+        ColumnAttributeList [35-43]
+          NotNullColumnAttribute [35-43]
+  SpannerTableOptions [49-65]
+    PrimaryKey(ENFORCED) [49-65]
+      PrimaryKeyElementList [61-65]
+        PrimaryKeyElement(ASC) [62-64]
+          Identifier(id) [62-64]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/schema-overview#define-nodes-and-edges-single-table
+-- Assume each account has exactly one owner.
+  CREATE TABLE Account (
+    owner_id INT64 NOT NULL,
+    account_id INT64 NOT NULL,
+    FOREIGN KEY (owner_id) REFERENCES Person(id)
+  ) PRIMARY KEY (owner_id, account_id)
+--
+CreateTableStatement [48-218]
+  PathExpression [61-68]
+    Identifier(Account) [61-68]
+  TableElementList [69-183]
+    ColumnDefinition [75-98]
+      Identifier(owner_id) [75-83]
+      SimpleColumnSchema [84-98]
+        PathExpression [84-89]
+          Identifier(INT64) [84-89]
+        ColumnAttributeList [90-98]
+          NotNullColumnAttribute [90-98]
+    ColumnDefinition [104-129]
+      Identifier(account_id) [104-114]
+      SimpleColumnSchema [115-129]
+        PathExpression [115-120]
+          Identifier(INT64) [115-120]
+        ColumnAttributeList [121-129]
+          NotNullColumnAttribute [121-129]
+    ForeignKey [135-179]
+      ColumnList [147-157]
+        Identifier(owner_id) [148-156]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [158-179]
+        PathExpression [169-175]
+          Identifier(Person) [169-175]
+        ColumnList [175-179]
+          Identifier(id) [176-178]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [179-179]
+  SpannerTableOptions [184-218]
+    PrimaryKey(ENFORCED) [184-218]
+      PrimaryKeyElementList [196-218]
+        PrimaryKeyElement(ASC) [197-205]
+          Identifier(owner_id) [197-205]
+        PrimaryKeyElement(ASC) [207-217]
+          Identifier(account_id) [207-217]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/schema-overview#define-nodes-and-edges-single-table
+CREATE PROPERTY GRAPH FinGraph
+    NODE TABLES (
+      Person,
+      Account
+    )
+    EDGE TABLES (
+      Account AS Owns
+        SOURCE KEY (owner_id) REFERENCES Person
+        DESTINATION KEY (owner_id, account_id) REFERENCES Account
+    )
+--
+CreatePropertyGraphStatement [0-242]
+  PathExpression [22-30]
+    Identifier(FinGraph) [22-30]
+  GraphElementTableList [47-82]
+    GraphElementTable [55-61]
+      PathExpression [55-61]
+        Identifier(Person) [55-61]
+      GraphElementLabelAndPropertiesList [61-61]
+        GraphElementLabelAndProperties [61-61]
+          GraphProperties [61-61]
+    GraphElementTable [69-76]
+      PathExpression [69-76]
+        Identifier(Account) [69-76]
+      GraphElementLabelAndPropertiesList [76-76]
+        GraphElementLabelAndProperties [76-76]
+          GraphProperties [76-76]
+  GraphElementTableList [99-242]
+    GraphElementTable [107-236]
+      PathExpression [107-114]
+        Identifier(Account) [107-114]
+      Alias [115-122]
+        Identifier(Owns) [118-122]
+      GraphNodeTableReference(SOURCE) [131-170]
+        Identifier(Person) [164-170]
+        ColumnList [142-152]
+          Identifier(owner_id) [143-151]
+      GraphNodeTableReference(DESTINATION) [179-236]
+        Identifier(Account) [229-236]
+        ColumnList [195-217]
+          Identifier(owner_id) [196-204]
+          Identifier(account_id) [206-216]
+      GraphElementLabelAndPropertiesList [236-236]
+        GraphElementLabelAndProperties [236-236]
+          GraphProperties [236-236]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/schema-overview#customize-labels-properties
+CREATE TABLE Person (
+  id               INT64 NOT NULL,
+  name             STRING(MAX),
+  birthday         TIMESTAMP,
+  country          STRING(MAX),
+  city             STRING(MAX),
+) PRIMARY KEY (id)
+--
+CreateTableStatement [0-201]
+  PathExpression [13-19]
+    Identifier(Person) [13-19]
+  TableElementList [20-184]
+    ColumnDefinition [24-55]
+      Identifier(id) [24-26]
+      SimpleColumnSchema [41-55]
+        PathExpression [41-46]
+          Identifier(INT64) [41-46]
+        ColumnAttributeList [47-55]
+          NotNullColumnAttribute [47-55]
+    ColumnDefinition [59-87]
+      Identifier(name) [59-63]
+      SimpleColumnSchema [76-87]
+        PathExpression [76-82]
+          Identifier(STRING) [76-82]
+        TypeParameterList [82-86]
+          MaxLiteral() [83-86]
+    ColumnDefinition [91-117]
+      Identifier(birthday) [91-99]
+      SimpleColumnSchema [108-117]
+        PathExpression [108-117]
+          Identifier(TIMESTAMP) [108-117]
+    ColumnDefinition [121-149]
+      Identifier(country) [121-128]
+      SimpleColumnSchema [138-149]
+        PathExpression [138-144]
+          Identifier(STRING) [138-144]
+        TypeParameterList [144-148]
+          MaxLiteral() [145-148]
+    ColumnDefinition [153-181]
+      Identifier(city) [153-157]
+      SimpleColumnSchema [170-181]
+        PathExpression [170-176]
+          Identifier(STRING) [170-176]
+        TypeParameterList [176-180]
+          MaxLiteral() [177-180]
+  SpannerTableOptions [185-201]
+    PrimaryKey(ENFORCED) [185-201]
+      PrimaryKeyElementList [197-201]
+        PrimaryKeyElement(ASC) [198-200]
+          Identifier(id) [198-200]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/schema-overview#customize-labels-properties
+CREATE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Person KEY (id)
+      LABEL Customer
+        PROPERTIES (CONCAT(city, ", ", country) AS address)
+      LABEL Entity PROPERTIES (id, name),
+    Account KEY (id)
+      LABEL Account PROPERTIES (id, create_time)
+      LABEL Entity PROPERTIES (id, nick_name AS name)
+  )
+--
+CreatePropertyGraphStatement [0-317]
+  PathExpression [22-30]
+    Identifier(FinGraph) [22-30]
+  GraphElementTableList [45-317]
+    GraphElementTable [51-188]
+      PathExpression [51-57]
+        Identifier(Person) [51-57]
+      ColumnList [62-66]
+        Identifier(id) [63-65]
+      GraphElementLabelAndPropertiesList [73-188]
+        GraphElementLabelAndProperties [73-147]
+          Identifier(Customer) [79-87]
+          GraphProperties [96-147]
+            GraphDerivedPropertyList [108-146]
+              GraphDerivedProperty [108-146]
+                FunctionCall [108-135]
+                  PathExpression [108-114]
+                    Identifier(CONCAT) [108-114]
+                  PathExpression [115-119]
+                    Identifier(city) [115-119]
+                  StringLiteral [121-125]
+                    StringLiteralComponent(", ") [121-125]
+                  PathExpression [127-134]
+                    Identifier(country) [127-134]
+                Alias [136-146]
+                  Identifier(address) [139-146]
+        GraphElementLabelAndProperties [154-188]
+          Identifier(Entity) [160-166]
+          GraphProperties [167-188]
+            GraphDerivedPropertyList [179-187]
+              GraphDerivedProperty [179-181]
+                PathExpression [179-181]
+                  Identifier(id) [179-181]
+              GraphDerivedProperty [183-187]
+                PathExpression [183-187]
+                  Identifier(name) [183-187]
+    GraphElementTable [194-313]
+      PathExpression [194-201]
+        Identifier(Account) [194-201]
+      ColumnList [206-210]
+        Identifier(id) [207-209]
+      GraphElementLabelAndPropertiesList [217-313]
+        GraphElementLabelAndProperties [217-259]
+          Identifier(Account) [223-230]
+          GraphProperties [231-259]
+            GraphDerivedPropertyList [243-258]
+              GraphDerivedProperty [243-245]
+                PathExpression [243-245]
+                  Identifier(id) [243-245]
+              GraphDerivedProperty [247-258]
+                PathExpression [247-258]
+                  Identifier(create_time) [247-258]
+        GraphElementLabelAndProperties [266-313]
+          Identifier(Entity) [272-278]
+          GraphProperties [279-313]
+            GraphDerivedPropertyList [291-312]
+              GraphDerivedProperty [291-293]
+                PathExpression [291-293]
+                  Identifier(id) [291-293]
+              GraphDerivedProperty [295-312]
+                PathExpression [295-304]
+                  Identifier(nick_name) [295-304]
+                Alias [305-312]
+                  Identifier(name) [308-312]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/schema-overview#dependencies-graphs-schema-objects
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Account PROPERTIES (id, create_time)
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [0-102]
+  PathExpression [33-41]
+    Identifier(FinGraph) [33-41]
+  GraphElementTableList [56-102]
+    GraphElementTable [62-98]
+      PathExpression [62-69]
+        Identifier(Account) [62-69]
+      GraphElementLabelAndPropertiesList [70-98]
+        GraphElementLabelAndProperties [70-98]
+          GraphProperties [70-98]
+            GraphDerivedPropertyList [82-97]
+              GraphDerivedProperty [82-84]
+                PathExpression [82-84]
+                  Identifier(id) [82-84]
+              GraphDerivedProperty [86-97]
+                PathExpression [86-97]
+                  Identifier(create_time) [86-97]
diff --git a/parser/testdata/spanner/guides/graph.set-up.test b/parser/testdata/spanner/guides/graph.set-up.test
new file mode 100644
index 0000000..8fe98ed
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.set-up.test
@@ -0,0 +1,411 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/set-up
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/graph/set-up#create-database
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE PersonOwnAccount (
+  id               INT64 NOT NULL,
+  account_id       INT64 NOT NULL,
+  create_time      TIMESTAMP,
+  FOREIGN KEY (account_id) REFERENCES Account (id)
+) PRIMARY KEY (id, account_id),
+  INTERLEAVE IN PARENT Person ON DELETE CASCADE
+--
+CreateTableStatement [0-262]
+  PathExpression [13-29]
+    Identifier(PersonOwnAccount) [13-29]
+  TableElementList [30-184]
+    ColumnDefinition [34-65]
+      Identifier(id) [34-36]
+      SimpleColumnSchema [51-65]
+        PathExpression [51-56]
+          Identifier(INT64) [51-56]
+        ColumnAttributeList [57-65]
+          NotNullColumnAttribute [57-65]
+    ColumnDefinition [69-100]
+      Identifier(account_id) [69-79]
+      SimpleColumnSchema [86-100]
+        PathExpression [86-91]
+          Identifier(INT64) [86-91]
+        ColumnAttributeList [92-100]
+          NotNullColumnAttribute [92-100]
+    ColumnDefinition [104-130]
+      Identifier(create_time) [104-115]
+      SimpleColumnSchema [121-130]
+        PathExpression [121-130]
+          Identifier(TIMESTAMP) [121-130]
+    ForeignKey [134-182]
+      ColumnList [146-158]
+        Identifier(account_id) [147-157]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [159-182]
+        PathExpression [170-177]
+          Identifier(Account) [170-177]
+        ColumnList [178-182]
+          Identifier(id) [179-181]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [182-182]
+  SpannerTableOptions [185-262]
+    PrimaryKey(ENFORCED) [185-213]
+      PrimaryKeyElementList [197-213]
+        PrimaryKeyElement(ASC) [198-200]
+          Identifier(id) [198-200]
+        PrimaryKeyElement(ASC) [202-212]
+          Identifier(account_id) [202-212]
+    SpannerInterleaveClause [213-262]
+      PathExpression [238-244]
+        Identifier(Person) [238-244]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/set-up#create-database
+CREATE TABLE AccountTransferAccount (
+  id               INT64 NOT NULL,
+  to_id            INT64 NOT NULL,
+  amount           FLOAT64,
+  create_time      TIMESTAMP NOT NULL,
+  order_number     STRING(MAX),
+  FOREIGN KEY (to_id) REFERENCES Account (id)
+) PRIMARY KEY (id, to_id, create_time),
+  INTERLEAVE IN PARENT Account ON DELETE CASCADE
+--
+CreateTableStatement [0-341]
+  PathExpression [13-35]
+    Identifier(AccountTransferAccount) [13-35]
+  TableElementList [36-254]
+    ColumnDefinition [40-71]
+      Identifier(id) [40-42]
+      SimpleColumnSchema [57-71]
+        PathExpression [57-62]
+          Identifier(INT64) [57-62]
+        ColumnAttributeList [63-71]
+          NotNullColumnAttribute [63-71]
+    ColumnDefinition [75-106]
+      Identifier(to_id) [75-80]
+      SimpleColumnSchema [92-106]
+        PathExpression [92-97]
+          Identifier(INT64) [92-97]
+        ColumnAttributeList [98-106]
+          NotNullColumnAttribute [98-106]
+    ColumnDefinition [110-134]
+      Identifier(amount) [110-116]
+      SimpleColumnSchema [127-134]
+        PathExpression [127-134]
+          Identifier(FLOAT64) [127-134]
+    ColumnDefinition [138-173]
+      Identifier(create_time) [138-149]
+      SimpleColumnSchema [155-173]
+        PathExpression [155-164]
+          Identifier(TIMESTAMP) [155-164]
+        ColumnAttributeList [165-173]
+          NotNullColumnAttribute [165-173]
+    ColumnDefinition [177-205]
+      Identifier(order_number) [177-189]
+      SimpleColumnSchema [194-205]
+        PathExpression [194-200]
+          Identifier(STRING) [194-200]
+        TypeParameterList [200-204]
+          MaxLiteral() [201-204]
+    ForeignKey [209-252]
+      ColumnList [221-228]
+        Identifier(to_id) [222-227]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [229-252]
+        PathExpression [240-247]
+          Identifier(Account) [240-247]
+        ColumnList [248-252]
+          Identifier(id) [249-251]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [252-252]
+  SpannerTableOptions [255-341]
+    PrimaryKey(ENFORCED) [255-291]
+      PrimaryKeyElementList [267-291]
+        PrimaryKeyElement(ASC) [268-270]
+          Identifier(id) [268-270]
+        PrimaryKeyElement(ASC) [272-277]
+          Identifier(to_id) [272-277]
+        PrimaryKeyElement(ASC) [279-290]
+          Identifier(create_time) [279-290]
+    SpannerInterleaveClause [291-341]
+      PathExpression [316-323]
+        Identifier(Account) [316-323]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/set-up#insert-graph-data
+INSERT INTO Account
+  (id, create_time, is_blocked, nick_name)
+VALUES
+  (7,"2020-01-10 06:22:20.222",false,"Vacation Fund"),
+  (16,"2020-01-27 17:55:09.206",true,"Vacation Fund"),
+  (20,"2020-02-18 05:44:20.655",false,"Rainy Day Fund")
+--
+InsertStatement [0-235]
+  PathExpression [12-19]
+    Identifier(Account) [12-19]
+  ColumnList [22-62]
+    Identifier(id) [23-25]
+    Identifier(create_time) [27-38]
+    Identifier(is_blocked) [40-50]
+    Identifier(nick_name) [52-61]
+  InsertValuesRowList [63-235]
+    InsertValuesRow [72-123]
+      IntLiteral(7) [73-74]
+      StringLiteral [75-100]
+        StringLiteralComponent("2020-01-10 06:22:20.222") [75-100]
+      BooleanLiteral(false) [101-106]
+      StringLiteral [107-122]
+        StringLiteralComponent("Vacation Fund") [107-122]
+    InsertValuesRow [127-178]
+      IntLiteral(16) [128-130]
+      StringLiteral [131-156]
+        StringLiteralComponent("2020-01-27 17:55:09.206") [131-156]
+      BooleanLiteral(true) [157-161]
+      StringLiteral [162-177]
+        StringLiteralComponent("Vacation Fund") [162-177]
+    InsertValuesRow [182-235]
+      IntLiteral(20) [183-185]
+      StringLiteral [186-211]
+        StringLiteralComponent("2020-02-18 05:44:20.655") [186-211]
+      BooleanLiteral(false) [212-217]
+      StringLiteral [218-234]
+        StringLiteralComponent("Rainy Day Fund") [218-234]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/set-up#insert-graph-data
+INSERT INTO Person
+  (id, name, birthday, country, city)
+VALUES
+  (1,"Alex","1991-12-21 00:00:00","Australia","Adelaide"),
+  (2,"Dana","1980-10-31 00:00:00","Czech_Republic","Moravia"),
+  (3,"Lee","1986-12-07 00:00:00","India","Kollam")
+--
+InsertStatement [0-236]
+  PathExpression [12-18]
+    Identifier(Person) [12-18]
+  ColumnList [21-56]
+    Identifier(id) [22-24]
+    Identifier(name) [26-30]
+    Identifier(birthday) [32-40]
+    Identifier(country) [42-49]
+    Identifier(city) [51-55]
+  InsertValuesRowList [57-236]
+    InsertValuesRow [66-121]
+      IntLiteral(1) [67-68]
+      StringLiteral [69-75]
+        StringLiteralComponent("Alex") [69-75]
+      StringLiteral [76-97]
+        StringLiteralComponent("1991-12-21 00:00:00") [76-97]
+      StringLiteral [98-109]
+        StringLiteralComponent("Australia") [98-109]
+      StringLiteral [110-120]
+        StringLiteralComponent("Adelaide") [110-120]
+    InsertValuesRow [125-184]
+      IntLiteral(2) [126-127]
+      StringLiteral [128-134]
+        StringLiteralComponent("Dana") [128-134]
+      StringLiteral [135-156]
+        StringLiteralComponent("1980-10-31 00:00:00") [135-156]
+      StringLiteral [157-173]
+        StringLiteralComponent("Czech_Republic") [157-173]
+      StringLiteral [174-183]
+        StringLiteralComponent("Moravia") [174-183]
+    InsertValuesRow [188-236]
+      IntLiteral(3) [189-190]
+      StringLiteral [191-196]
+        StringLiteralComponent("Lee") [191-196]
+      StringLiteral [197-218]
+        StringLiteralComponent("1986-12-07 00:00:00") [197-218]
+      StringLiteral [219-226]
+        StringLiteralComponent("India") [219-226]
+      StringLiteral [227-235]
+        StringLiteralComponent("Kollam") [227-235]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/set-up#insert-graph-data
+INSERT INTO AccountTransferAccount
+  (id, to_id, amount, create_time, order_number)
+VALUES
+  (7,16,300,"2020-08-29 15:28:58.647","304330008004315"),
+  (7,16,100,"2020-10-04 16:55:05.342","304120005529714"),
+  (16,20,300,"2020-09-25 02:36:14.926","103650009791820"),
+  (20,7,500,"2020-10-04 16:55:05.342","304120005529714"),
+  (20,16,200,"2020-10-17 03:59:40.247","302290001255747")
+--
+InsertStatement [0-381]
+  PathExpression [12-34]
+    Identifier(AccountTransferAccount) [12-34]
+  ColumnList [37-83]
+    Identifier(id) [38-40]
+    Identifier(to_id) [42-47]
+    Identifier(amount) [49-55]
+    Identifier(create_time) [57-68]
+    Identifier(order_number) [70-82]
+  InsertValuesRowList [84-381]
+    InsertValuesRow [93-147]
+      IntLiteral(7) [94-95]
+      IntLiteral(16) [96-98]
+      IntLiteral(300) [99-102]
+      StringLiteral [103-128]
+        StringLiteralComponent("2020-08-29 15:28:58.647") [103-128]
+      StringLiteral [129-146]
+        StringLiteralComponent("304330008004315") [129-146]
+    InsertValuesRow [151-205]
+      IntLiteral(7) [152-153]
+      IntLiteral(16) [154-156]
+      IntLiteral(100) [157-160]
+      StringLiteral [161-186]
+        StringLiteralComponent("2020-10-04 16:55:05.342") [161-186]
+      StringLiteral [187-204]
+        StringLiteralComponent("304120005529714") [187-204]
+    InsertValuesRow [209-264]
+      IntLiteral(16) [210-212]
+      IntLiteral(20) [213-215]
+      IntLiteral(300) [216-219]
+      StringLiteral [220-245]
+        StringLiteralComponent("2020-09-25 02:36:14.926") [220-245]
+      StringLiteral [246-263]
+        StringLiteralComponent("103650009791820") [246-263]
+    InsertValuesRow [268-322]
+      IntLiteral(20) [269-271]
+      IntLiteral(7) [272-273]
+      IntLiteral(500) [274-277]
+      StringLiteral [278-303]
+        StringLiteralComponent("2020-10-04 16:55:05.342") [278-303]
+      StringLiteral [304-321]
+        StringLiteralComponent("304120005529714") [304-321]
+    InsertValuesRow [326-381]
+      IntLiteral(20) [327-329]
+      IntLiteral(16) [330-332]
+      IntLiteral(200) [333-336]
+      StringLiteral [337-362]
+        StringLiteralComponent("2020-10-17 03:59:40.247") [337-362]
+      StringLiteral [363-380]
+        StringLiteralComponent("302290001255747") [363-380]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/set-up#insert-graph-data
+INSERT INTO PersonOwnAccount
+  (id, account_id, create_time)
+VALUES
+  (1,7,"2020-01-10 06:22:20.222"),
+  (2,20,"2020-01-27 17:55:09.206"),
+  (3,16,"2020-02-18 05:44:20.655")
+--
+InsertStatement [0-173]
+  PathExpression [12-28]
+    Identifier(PersonOwnAccount) [12-28]
+  ColumnList [31-60]
+    Identifier(id) [32-34]
+    Identifier(account_id) [36-46]
+    Identifier(create_time) [48-59]
+  InsertValuesRowList [61-173]
+    InsertValuesRow [70-101]
+      IntLiteral(1) [71-72]
+      IntLiteral(7) [73-74]
+      StringLiteral [75-100]
+        StringLiteralComponent("2020-01-10 06:22:20.222") [75-100]
+    InsertValuesRow [105-137]
+      IntLiteral(2) [106-107]
+      IntLiteral(20) [108-110]
+      StringLiteral [111-136]
+        StringLiteralComponent("2020-01-27 17:55:09.206") [111-136]
+    InsertValuesRow [141-173]
+      IntLiteral(3) [142-143]
+      IntLiteral(16) [144-146]
+      StringLiteral [147-172]
+        StringLiteralComponent("2020-02-18 05:44:20.655") [147-172]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/set-up#run-graph-query
+GRAPH FinGraph
+MATCH
+  (from_person:Person {name: "Dana"})-[:Owns]->
+  (from_account:Account)-[transfer:Transfers]->
+  (to_account:Account)<-[:Owns]-(to_person:Person)
+RETURN
+  from_person.name AS from_account_owner,
+  from_account.id AS from_account_id,
+  to_person.name AS to_account_owner,
+  to_account.id AS to_account_id,
+  transfer.amount AS amount
+--
+QueryStatement [0-354]
+  Query [0-354]
+    GqlQuery [0-354]
+      GraphTableQuery [0-354]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-354]
+          GqlOperatorList [15-354]
+            GqlMatch [15-167]
+              GraphPattern [23-167]
+                GraphPathPattern [23-167]
+                  GraphNodePattern [23-58]
+                    GraphElementPatternFiller [24-57]
+                      Identifier(from_person) [24-35]
+                      GraphLabelFilter [35-42]
+                        GraphElementLabel [36-42]
+                          Identifier(Person) [36-42]
+                      GraphPropertySpecification [43-57]
+                        GraphPropertyNameAndValue [44-56]
+                          Identifier(name) [44-48]
+                          StringLiteral [50-56]
+                            StringLiteralComponent("Dana") [50-56]
+                  GraphEdgePattern [58-68]
+                    GraphElementPatternFiller [60-65]
+                      GraphLabelFilter [60-65]
+                        GraphElementLabel [61-65]
+                          Identifier(Owns) [61-65]
+                  GraphNodePattern [71-93]
+                    GraphElementPatternFiller [72-92]
+                      Identifier(from_account) [72-84]
+                      GraphLabelFilter [84-92]
+                        GraphElementLabel [85-92]
+                          Identifier(Account) [85-92]
+                  GraphEdgePattern [93-116]
+                    GraphElementPatternFiller [95-113]
+                      Identifier(transfer) [95-103]
+                      GraphLabelFilter [103-113]
+                        GraphElementLabel [104-113]
+                          Identifier(Transfers) [104-113]
+                  GraphNodePattern [119-139]
+                    GraphElementPatternFiller [120-138]
+                      Identifier(to_account) [120-130]
+                      GraphLabelFilter [130-138]
+                        GraphElementLabel [131-138]
+                          Identifier(Account) [131-138]
+                  GraphEdgePattern [139-149]
+                    GraphElementPatternFiller [142-147]
+                      GraphLabelFilter [142-147]
+                        GraphElementLabel [143-147]
+                          Identifier(Owns) [143-147]
+                  GraphNodePattern [149-167]
+                    GraphElementPatternFiller [150-166]
+                      Identifier(to_person) [150-159]
+                      GraphLabelFilter [159-166]
+                        GraphElementLabel [160-166]
+                          Identifier(Person) [160-166]
+            GqlReturn [168-354]
+              Select [177-354]
+                SelectList [177-354]
+                  SelectColumn [177-215]
+                    PathExpression [177-193]
+                      Identifier(from_person) [177-188]
+                      Identifier(name) [189-193]
+                    Alias [194-215]
+                      Identifier(from_account_owner) [197-215]
+                  SelectColumn [219-253]
+                    PathExpression [219-234]
+                      Identifier(from_account) [219-231]
+                      Identifier(id) [232-234]
+                    Alias [235-253]
+                      Identifier(from_account_id) [238-253]
+                  SelectColumn [257-291]
+                    PathExpression [257-271]
+                      Identifier(to_person) [257-266]
+                      Identifier(name) [267-271]
+                    Alias [272-291]
+                      Identifier(to_account_owner) [275-291]
+                  SelectColumn [295-325]
+                    PathExpression [295-308]
+                      Identifier(to_account) [295-305]
+                      Identifier(id) [306-308]
+                    Alias [309-325]
+                      Identifier(to_account_id) [312-325]
+                  SelectColumn [329-354]
+                    PathExpression [329-344]
+                      Identifier(transfer) [329-337]
+                      Identifier(amount) [338-344]
+                    Alias [345-354]
+                      Identifier(amount) [348-354]
diff --git a/parser/testdata/spanner/guides/graph.troubleshoot.metadata.json b/parser/testdata/spanner/guides/graph.troubleshoot.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.troubleshoot.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/graph.troubleshoot.test b/parser/testdata/spanner/guides/graph.troubleshoot.test
new file mode 100644
index 0000000..a4498c9
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.troubleshoot.test
@@ -0,0 +1,1315 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/troubleshoot
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#example_error
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Person KEY (name)
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [0-83]
+  PathExpression [33-41]
+    Identifier(FinGraph) [33-41]
+  GraphElementTableList [56-83]
+    GraphElementTable [62-79]
+      PathExpression [62-68]
+        Identifier(Person) [62-68]
+      ColumnList [73-79]
+        Identifier(name) [74-78]
+      GraphElementLabelAndPropertiesList [79-79]
+        GraphElementLabelAndProperties [79-79]
+          GraphProperties [79-79]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#recommended_fix
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Person KEY (id)
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [0-81]
+  PathExpression [33-41]
+    Identifier(FinGraph) [33-41]
+  GraphElementTableList [56-81]
+    GraphElementTable [62-77]
+      PathExpression [62-68]
+        Identifier(Person) [62-68]
+      ColumnList [73-77]
+        Identifier(id) [74-76]
+      GraphElementLabelAndPropertiesList [77-77]
+        GraphElementLabelAndProperties [77-77]
+          GraphProperties [77-77]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#recommended_fix
+CREATE UNIQUE INDEX PersonNameIndex ON Person(name)
+--
+CreateIndexStatement(UNIQUE) [0-51]
+  PathExpression [20-35]
+    Identifier(PersonNameIndex) [20-35]
+  PathExpression [39-45]
+    Identifier(Person) [39-45]
+  IndexItemList [45-51]
+    OrderingExpression(ASC) [46-50]
+      PathExpression [46-50]
+        Identifier(name) [46-50]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#example_error_2
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Account,
+    Person
+  )
+  EDGE TABLES (
+    Account
+      SOURCE KEY(owner_id) REFERENCES Person
+      DESTINATION KEY(account_id) REFERENCES Account
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [0-215]
+  PathExpression [33-41]
+    Identifier(FinGraph) [33-41]
+  GraphElementTableList [56-85]
+    GraphElementTable [62-69]
+      PathExpression [62-69]
+        Identifier(Account) [62-69]
+      GraphElementLabelAndPropertiesList [69-69]
+        GraphElementLabelAndProperties [69-69]
+          GraphProperties [69-69]
+    GraphElementTable [75-81]
+      PathExpression [75-81]
+        Identifier(Person) [75-81]
+      GraphElementLabelAndPropertiesList [81-81]
+        GraphElementLabelAndProperties [81-81]
+          GraphProperties [81-81]
+  GraphElementTableList [100-215]
+    GraphElementTable [106-211]
+      PathExpression [106-113]
+        Identifier(Account) [106-113]
+      GraphNodeTableReference(SOURCE) [120-158]
+        Identifier(Person) [152-158]
+        ColumnList [130-140]
+          Identifier(owner_id) [131-139]
+      GraphNodeTableReference(DESTINATION) [165-211]
+        Identifier(Account) [204-211]
+        ColumnList [180-192]
+          Identifier(account_id) [181-191]
+      GraphElementLabelAndPropertiesList [211-211]
+        GraphElementLabelAndProperties [211-211]
+          GraphProperties [211-211]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#recommended_fix_2
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Account,
+    Person
+  )
+  EDGE TABLES (
+    Account AS Owns
+      SOURCE KEY(owner_id) REFERENCES Person
+      DESTINATION KEY(account_id) REFERENCES Account
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [0-223]
+  PathExpression [33-41]
+    Identifier(FinGraph) [33-41]
+  GraphElementTableList [56-85]
+    GraphElementTable [62-69]
+      PathExpression [62-69]
+        Identifier(Account) [62-69]
+      GraphElementLabelAndPropertiesList [69-69]
+        GraphElementLabelAndProperties [69-69]
+          GraphProperties [69-69]
+    GraphElementTable [75-81]
+      PathExpression [75-81]
+        Identifier(Person) [75-81]
+      GraphElementLabelAndPropertiesList [81-81]
+        GraphElementLabelAndProperties [81-81]
+          GraphProperties [81-81]
+  GraphElementTableList [100-223]
+    GraphElementTable [106-219]
+      PathExpression [106-113]
+        Identifier(Account) [106-113]
+      Alias [114-121]
+        Identifier(Owns) [117-121]
+      GraphNodeTableReference(SOURCE) [128-166]
+        Identifier(Person) [160-166]
+        ColumnList [138-148]
+          Identifier(owner_id) [139-147]
+      GraphNodeTableReference(DESTINATION) [173-219]
+        Identifier(Account) [212-219]
+        ColumnList [188-200]
+          Identifier(account_id) [189-199]
+      GraphElementLabelAndPropertiesList [219-219]
+        GraphElementLabelAndProperties [219-219]
+          GraphProperties [219-219]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#example_error_3
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Person LABEL Entity PROPERTIES (name),
+    Account LABEL Entity PROPERTIES (id)
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [0-145]
+  PathExpression [33-41]
+    Identifier(FinGraph) [33-41]
+  GraphElementTableList [56-145]
+    GraphElementTable [62-99]
+      PathExpression [62-68]
+        Identifier(Person) [62-68]
+      GraphElementLabelAndPropertiesList [69-99]
+        GraphElementLabelAndProperties [69-99]
+          Identifier(Entity) [75-81]
+          GraphProperties [82-99]
+            GraphDerivedPropertyList [94-98]
+              GraphDerivedProperty [94-98]
+                PathExpression [94-98]
+                  Identifier(name) [94-98]
+    GraphElementTable [105-141]
+      PathExpression [105-112]
+        Identifier(Account) [105-112]
+      GraphElementLabelAndPropertiesList [113-141]
+        GraphElementLabelAndProperties [113-141]
+          Identifier(Entity) [119-125]
+          GraphProperties [126-141]
+            GraphDerivedPropertyList [138-140]
+              GraphDerivedProperty [138-140]
+                PathExpression [138-140]
+                  Identifier(id) [138-140]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#recommended_fix_3
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Person LABEL Entity PROPERTIES (id, name),
+    Account LABEL Entity PROPERTIES (id, name)
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [0-155]
+  PathExpression [33-41]
+    Identifier(FinGraph) [33-41]
+  GraphElementTableList [56-155]
+    GraphElementTable [62-103]
+      PathExpression [62-68]
+        Identifier(Person) [62-68]
+      GraphElementLabelAndPropertiesList [69-103]
+        GraphElementLabelAndProperties [69-103]
+          Identifier(Entity) [75-81]
+          GraphProperties [82-103]
+            GraphDerivedPropertyList [94-102]
+              GraphDerivedProperty [94-96]
+                PathExpression [94-96]
+                  Identifier(id) [94-96]
+              GraphDerivedProperty [98-102]
+                PathExpression [98-102]
+                  Identifier(name) [98-102]
+    GraphElementTable [109-151]
+      PathExpression [109-116]
+        Identifier(Account) [109-116]
+      GraphElementLabelAndPropertiesList [117-151]
+        GraphElementLabelAndProperties [117-151]
+          Identifier(Entity) [123-129]
+          GraphProperties [130-151]
+            GraphDerivedPropertyList [142-150]
+              GraphDerivedProperty [142-144]
+                PathExpression [142-144]
+                  Identifier(id) [142-144]
+              GraphDerivedProperty [146-150]
+                PathExpression [146-150]
+                  Identifier(name) [146-150]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#example_error_4
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Person PROPERTIES (name),
+    Account PROPERTIES (id AS name)
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [0-127]
+  PathExpression [33-41]
+    Identifier(FinGraph) [33-41]
+  GraphElementTableList [56-127]
+    GraphElementTable [62-86]
+      PathExpression [62-68]
+        Identifier(Person) [62-68]
+      GraphElementLabelAndPropertiesList [69-86]
+        GraphElementLabelAndProperties [69-86]
+          GraphProperties [69-86]
+            GraphDerivedPropertyList [81-85]
+              GraphDerivedProperty [81-85]
+                PathExpression [81-85]
+                  Identifier(name) [81-85]
+    GraphElementTable [92-123]
+      PathExpression [92-99]
+        Identifier(Account) [92-99]
+      GraphElementLabelAndPropertiesList [100-123]
+        GraphElementLabelAndProperties [100-123]
+          GraphProperties [100-123]
+            GraphDerivedPropertyList [112-122]
+              GraphDerivedProperty [112-122]
+                PathExpression [112-114]
+                  Identifier(id) [112-114]
+                Alias [115-122]
+                  Identifier(name) [118-122]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#recommended_fix_4
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Person PROPERTIES (name),
+    Account PROPERTIES (CAST(id AS STRING) AS name)
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [0-143]
+  PathExpression [33-41]
+    Identifier(FinGraph) [33-41]
+  GraphElementTableList [56-143]
+    GraphElementTable [62-86]
+      PathExpression [62-68]
+        Identifier(Person) [62-68]
+      GraphElementLabelAndPropertiesList [69-86]
+        GraphElementLabelAndProperties [69-86]
+          GraphProperties [69-86]
+            GraphDerivedPropertyList [81-85]
+              GraphDerivedProperty [81-85]
+                PathExpression [81-85]
+                  Identifier(name) [81-85]
+    GraphElementTable [92-139]
+      PathExpression [92-99]
+        Identifier(Account) [92-99]
+      GraphElementLabelAndPropertiesList [100-139]
+        GraphElementLabelAndProperties [100-139]
+          GraphProperties [100-139]
+            GraphDerivedPropertyList [112-138]
+              GraphDerivedProperty [112-138]
+                CastExpression [112-130]
+                  PathExpression [117-119]
+                    Identifier(id) [117-119]
+                  SimpleType [123-129]
+                    PathExpression [123-129]
+                      Identifier(STRING) [123-129]
+                Alias [131-138]
+                  Identifier(name) [134-138]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#example_error_5
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Person PROPERTIES ((SELECT COUNT(*) FROM Person) AS count)
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [0-124]
+  PathExpression [33-41]
+    Identifier(FinGraph) [33-41]
+  GraphElementTableList [56-124]
+    GraphElementTable [62-120]
+      PathExpression [62-68]
+        Identifier(Person) [62-68]
+      GraphElementLabelAndPropertiesList [69-120]
+        GraphElementLabelAndProperties [69-120]
+          GraphProperties [69-120]
+            GraphDerivedPropertyList [81-119]
+              GraphDerivedProperty [81-119]
+                ExpressionSubquery [81-110]
+                  Query [82-109]
+                    Select [82-109]
+                      SelectList [89-97]
+                        SelectColumn [89-97]
+                          FunctionCall [89-97]
+                            PathExpression [89-94]
+                              Identifier(COUNT) [89-94]
+                            Star(*) [95-96]
+                      FromClause [98-109]
+                        TablePathExpression [103-109]
+                          PathExpression [103-109]
+                            Identifier(Person) [103-109]
+                Alias [111-119]
+                  Identifier(count) [114-119]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#example_error_6
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Person
+      LABEL Person PROPERTIES (country AS location)
+      LABEL Entity PROPERTIES (city AS location)
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [0-173]
+  PathExpression [33-41]
+    Identifier(FinGraph) [33-41]
+  GraphElementTableList [56-173]
+    GraphElementTable [62-169]
+      PathExpression [62-68]
+        Identifier(Person) [62-68]
+      GraphElementLabelAndPropertiesList [75-169]
+        GraphElementLabelAndProperties [75-120]
+          Identifier(Person) [81-87]
+          GraphProperties [88-120]
+            GraphDerivedPropertyList [100-119]
+              GraphDerivedProperty [100-119]
+                PathExpression [100-107]
+                  Identifier(country) [100-107]
+                Alias [108-119]
+                  Identifier(location) [111-119]
+        GraphElementLabelAndProperties [127-169]
+          Identifier(Entity) [133-139]
+          GraphProperties [140-169]
+            GraphDerivedPropertyList [152-168]
+              GraphDerivedProperty [152-168]
+                PathExpression [152-156]
+                  Identifier(city) [152-156]
+                Alias [157-168]
+                  Identifier(location) [160-168]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#recommended_fix_6
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Person
+      LABEL Person PROPERTIES (country AS location)
+      LABEL Entity PROPERTIES (country AS location)
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [0-176]
+  PathExpression [33-41]
+    Identifier(FinGraph) [33-41]
+  GraphElementTableList [56-176]
+    GraphElementTable [62-172]
+      PathExpression [62-68]
+        Identifier(Person) [62-68]
+      GraphElementLabelAndPropertiesList [75-172]
+        GraphElementLabelAndProperties [75-120]
+          Identifier(Person) [81-87]
+          GraphProperties [88-120]
+            GraphDerivedPropertyList [100-119]
+              GraphDerivedProperty [100-119]
+                PathExpression [100-107]
+                  Identifier(country) [100-107]
+                Alias [108-119]
+                  Identifier(location) [111-119]
+        GraphElementLabelAndProperties [127-172]
+          Identifier(Entity) [133-139]
+          GraphProperties [140-172]
+            GraphDerivedPropertyList [152-171]
+              GraphDerivedProperty [152-171]
+                PathExpression [152-159]
+                  Identifier(country) [152-159]
+                Alias [160-171]
+                  Identifier(location) [163-171]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#recommended_fix_6
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Person
+      LABEL Person PROPERTIES (country AS location)
+      LABEL Entity PROPERTIES (city AS city)
+  )
+--
+CreatePropertyGraphStatement(is_or_replace) [0-169]
+  PathExpression [33-41]
+    Identifier(FinGraph) [33-41]
+  GraphElementTableList [56-169]
+    GraphElementTable [62-165]
+      PathExpression [62-68]
+        Identifier(Person) [62-68]
+      GraphElementLabelAndPropertiesList [75-165]
+        GraphElementLabelAndProperties [75-120]
+          Identifier(Person) [81-87]
+          GraphProperties [88-120]
+            GraphDerivedPropertyList [100-119]
+              GraphDerivedProperty [100-119]
+                PathExpression [100-107]
+                  Identifier(country) [100-107]
+                Alias [108-119]
+                  Identifier(location) [111-119]
+        GraphElementLabelAndProperties [127-165]
+          Identifier(Entity) [133-139]
+          GraphProperties [140-165]
+            GraphDerivedPropertyList [152-164]
+              GraphDerivedProperty [152-164]
+                PathExpression [152-156]
+                  Identifier(city) [152-156]
+                Alias [157-164]
+                  Identifier(city) [160-164]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#example_error_7
+GRAPH FinGraph
+MATCH (n:Account)
+RETURN n
+--
+QueryStatement [0-41]
+  Query [0-41]
+    GqlQuery [0-41]
+      GraphTableQuery [0-41]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-41]
+          GqlOperatorList [15-41]
+            GqlMatch [15-32]
+              GraphPattern [21-32]
+                GraphPathPattern [21-32]
+                  GraphNodePattern [21-32]
+                    GraphElementPatternFiller [22-31]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+            GqlReturn [33-41]
+              Select [40-41]
+                SelectList [40-41]
+                  SelectColumn [40-41]
+                    PathExpression [40-41]
+                      Identifier(n) [40-41]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#recommended_fix_7
+GRAPH FinGraph
+MATCH (n:Account)
+RETURN TO_JSON(n) AS n
+--
+QueryStatement [0-55]
+  Query [0-55]
+    GqlQuery [0-55]
+      GraphTableQuery [0-55]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-55]
+          GqlOperatorList [15-55]
+            GqlMatch [15-32]
+              GraphPattern [21-32]
+                GraphPathPattern [21-32]
+                  GraphNodePattern [21-32]
+                    GraphElementPatternFiller [22-31]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+            GqlReturn [33-55]
+              Select [40-55]
+                SelectList [40-55]
+                  SelectColumn [40-55]
+                    FunctionCall [40-50]
+                      PathExpression [40-47]
+                        Identifier(TO_JSON) [40-47]
+                      PathExpression [48-49]
+                        Identifier(n) [48-49]
+                    Alias [51-55]
+                      Identifier(n) [54-55]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#recommended_fix_8
+GRAPH FinGraph
+MATCH (n:Account {id: 1})
+WHERE n.is_blocked
+RETURN n.id
+--
+QueryStatement [0-71]
+  Query [0-71]
+    GqlQuery [0-71]
+      GraphTableQuery [0-71]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-71]
+          GqlOperatorList [15-71]
+            GqlMatch [15-59]
+              GraphPattern [21-59]
+                GraphPathPattern [21-40]
+                  GraphNodePattern [21-40]
+                    GraphElementPatternFiller [22-39]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                      GraphPropertySpecification [32-39]
+                        GraphPropertyNameAndValue [33-38]
+                          Identifier(id) [33-35]
+                          IntLiteral(1) [37-38]
+                WhereClause [41-59]
+                  PathExpression [47-59]
+                    Identifier(n) [47-48]
+                    Identifier(is_blocked) [49-59]
+            GqlReturn [60-71]
+              Select [67-71]
+                SelectList [67-71]
+                  SelectColumn [67-71]
+                    PathExpression [67-71]
+                      Identifier(n) [67-68]
+                      Identifier(id) [69-71]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#recommended_fix_8
+GRAPH FinGraph
+MATCH (n:Account WHERE n.id = 1 AND n.is_blocked )
+RETURN n.id
+--
+QueryStatement [0-77]
+  Query [0-77]
+    GqlQuery [0-77]
+      GraphTableQuery [0-77]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-77]
+          GqlOperatorList [15-77]
+            GqlMatch [15-65]
+              GraphPattern [21-65]
+                GraphPathPattern [21-65]
+                  GraphNodePattern [21-65]
+                    GraphElementPatternFiller [22-63]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                      WhereClause [32-63]
+                        AndExpr [38-63]
+                          BinaryExpression(=) [38-46]
+                            PathExpression [38-42]
+                              Identifier(n) [38-39]
+                              Identifier(id) [40-42]
+                            IntLiteral(1) [45-46]
+                          PathExpression [51-63]
+                            Identifier(n) [51-52]
+                            Identifier(is_blocked) [53-63]
+            GqlReturn [66-77]
+              Select [73-77]
+                SelectList [73-77]
+                  SelectColumn [73-77]
+                    PathExpression [73-77]
+                      Identifier(n) [73-74]
+                      Identifier(id) [75-77]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#recommended_fix_8
+GRAPH FinGraph
+MATCH (n:Account {id: 1, is_blocked: TRUE})
+RETURN n.id
+--
+QueryStatement [0-70]
+  Query [0-70]
+    GqlQuery [0-70]
+      GraphTableQuery [0-70]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-70]
+          GqlOperatorList [15-70]
+            GqlMatch [15-58]
+              GraphPattern [21-58]
+                GraphPathPattern [21-58]
+                  GraphNodePattern [21-58]
+                    GraphElementPatternFiller [22-57]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                      GraphPropertySpecification [32-57]
+                        GraphPropertyNameAndValue [33-38]
+                          Identifier(id) [33-35]
+                          IntLiteral(1) [37-38]
+                        GraphPropertyNameAndValue [40-56]
+                          Identifier(is_blocked) [40-50]
+                          BooleanLiteral(TRUE) [52-56]
+            GqlReturn [59-70]
+              Select [66-70]
+                SelectList [66-70]
+                  SelectColumn [66-70]
+                    PathExpression [66-70]
+                      Identifier(n) [66-67]
+                      Identifier(id) [68-70]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#example_error_9
+GRAPH FinGraph
+LET account_id = 1
+MATCH (n:Account {id: account_id})
+RETURN n.id
+--
+QueryStatement [0-80]
+  Query [0-80]
+    GqlQuery [0-80]
+      GraphTableQuery [0-80]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-80]
+          GqlOperatorList [15-80]
+            GqlLet [15-33]
+              GqlLetVariableDefinitionList [19-33]
+                GqlLetVariableDefinition [19-33]
+                  Identifier(account_id) [19-29]
+                  IntLiteral(1) [32-33]
+            GqlMatch [34-68]
+              GraphPattern [40-68]
+                GraphPathPattern [40-68]
+                  GraphNodePattern [40-68]
+                    GraphElementPatternFiller [41-67]
+                      Identifier(n) [41-42]
+                      GraphLabelFilter [42-50]
+                        GraphElementLabel [43-50]
+                          Identifier(Account) [43-50]
+                      GraphPropertySpecification [51-67]
+                        GraphPropertyNameAndValue [52-66]
+                          Identifier(id) [52-54]
+                          PathExpression [56-66]
+                            Identifier(account_id) [56-66]
+            GqlReturn [69-80]
+              Select [76-80]
+                SelectList [76-80]
+                  SelectColumn [76-80]
+                    PathExpression [76-80]
+                      Identifier(n) [76-77]
+                      Identifier(id) [78-80]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#recommended_fix_9
+GRAPH FinGraph
+LET account_id = 1
+MATCH (n:Account)
+WHERE n.id = account_id
+RETURN n.id
+--
+QueryStatement [0-87]
+  Query [0-87]
+    GqlQuery [0-87]
+      GraphTableQuery [0-87]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-87]
+          GqlOperatorList [15-87]
+            GqlLet [15-33]
+              GqlLetVariableDefinitionList [19-33]
+                GqlLetVariableDefinition [19-33]
+                  Identifier(account_id) [19-29]
+                  IntLiteral(1) [32-33]
+            GqlMatch [34-75]
+              GraphPattern [40-75]
+                GraphPathPattern [40-51]
+                  GraphNodePattern [40-51]
+                    GraphElementPatternFiller [41-50]
+                      Identifier(n) [41-42]
+                      GraphLabelFilter [42-50]
+                        GraphElementLabel [43-50]
+                          Identifier(Account) [43-50]
+                WhereClause [52-75]
+                  BinaryExpression(=) [58-75]
+                    PathExpression [58-62]
+                      Identifier(n) [58-59]
+                      Identifier(id) [60-62]
+                    PathExpression [65-75]
+                      Identifier(account_id) [65-75]
+            GqlReturn [76-87]
+              Select [83-87]
+                SelectList [83-87]
+                  SelectColumn [83-87]
+                    PathExpression [83-87]
+                      Identifier(n) [83-84]
+                      Identifier(id) [85-87]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#example_error_10
+GRAPH FinGraph
+MATCH (account:Account)
+RETURN account.id AS account_id, VALUE {
+  MATCH (account:Account)-[transfer:Transfers]->(:Account)
+  RETURN SUM(transfer.amount) AS total_transfer
+} AS total_transfer
+--
+QueryStatement [0-206]
+  Query [0-206]
+    GqlQuery [0-206]
+      GraphTableQuery [0-206]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-206]
+          GqlOperatorList [15-206]
+            GqlMatch [15-38]
+              GraphPattern [21-38]
+                GraphPathPattern [21-38]
+                  GraphNodePattern [21-38]
+                    GraphElementPatternFiller [22-37]
+                      Identifier(account) [22-29]
+                      GraphLabelFilter [29-37]
+                        GraphElementLabel [30-37]
+                          Identifier(Account) [30-37]
+            GqlReturn [39-206]
+              Select [46-206]
+                SelectList [46-206]
+                  SelectColumn [46-70]
+                    PathExpression [46-56]
+                      Identifier(account) [46-53]
+                      Identifier(id) [54-56]
+                    Alias [57-70]
+                      Identifier(account_id) [60-70]
+                  SelectColumn [72-206]
+                    ExpressionSubquery(modifier=VALUE) [72-188]
+                      Query [78-188]
+                        GqlQuery [78-188]
+                          GraphTableQuery [78-188]
+                            GqlOperatorList [82-186]
+                              GqlOperatorList [82-186]
+                                GqlMatch [82-138]
+                                  GraphPattern [88-138]
+                                    GraphPathPattern [88-138]
+                                      GraphNodePattern [88-105]
+                                        GraphElementPatternFiller [89-104]
+                                          Identifier(account) [89-96]
+                                          GraphLabelFilter [96-104]
+                                            GraphElementLabel [97-104]
+                                              Identifier(Account) [97-104]
+                                      GraphEdgePattern [105-128]
+                                        GraphElementPatternFiller [107-125]
+                                          Identifier(transfer) [107-115]
+                                          GraphLabelFilter [115-125]
+                                            GraphElementLabel [116-125]
+                                              Identifier(Transfers) [116-125]
+                                      GraphNodePattern [128-138]
+                                        GraphElementPatternFiller [129-137]
+                                          GraphLabelFilter [129-137]
+                                            GraphElementLabel [130-137]
+                                              Identifier(Account) [130-137]
+                                GqlReturn [141-186]
+                                  Select [148-186]
+                                    SelectList [148-186]
+                                      SelectColumn [148-186]
+                                        FunctionCall [148-168]
+                                          PathExpression [148-151]
+                                            Identifier(SUM) [148-151]
+                                          PathExpression [152-167]
+                                            Identifier(transfer) [152-160]
+                                            Identifier(amount) [161-167]
+                                        Alias [169-186]
+                                          Identifier(total_transfer) [172-186]
+                    Alias [189-206]
+                      Identifier(total_transfer) [192-206]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#recommended_fix_10
+GRAPH FinGraph
+MATCH (account:Account)
+RETURN account.id AS account_id, VALUE {
+  MATCH (a:Account)-[transfer:Transfers]->(:Account)
+  WHERE a = account
+  RETURN SUM(transfer.amount) AS total_transfer
+} AS total_transfer
+--
+QueryStatement [0-220]
+  Query [0-220]
+    GqlQuery [0-220]
+      GraphTableQuery [0-220]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-220]
+          GqlOperatorList [15-220]
+            GqlMatch [15-38]
+              GraphPattern [21-38]
+                GraphPathPattern [21-38]
+                  GraphNodePattern [21-38]
+                    GraphElementPatternFiller [22-37]
+                      Identifier(account) [22-29]
+                      GraphLabelFilter [29-37]
+                        GraphElementLabel [30-37]
+                          Identifier(Account) [30-37]
+            GqlReturn [39-220]
+              Select [46-220]
+                SelectList [46-220]
+                  SelectColumn [46-70]
+                    PathExpression [46-56]
+                      Identifier(account) [46-53]
+                      Identifier(id) [54-56]
+                    Alias [57-70]
+                      Identifier(account_id) [60-70]
+                  SelectColumn [72-220]
+                    ExpressionSubquery(modifier=VALUE) [72-202]
+                      Query [78-202]
+                        GqlQuery [78-202]
+                          GraphTableQuery [78-202]
+                            GqlOperatorList [82-200]
+                              GqlOperatorList [82-200]
+                                GqlMatch [82-152]
+                                  GraphPattern [88-152]
+                                    GraphPathPattern [88-132]
+                                      GraphNodePattern [88-99]
+                                        GraphElementPatternFiller [89-98]
+                                          Identifier(a) [89-90]
+                                          GraphLabelFilter [90-98]
+                                            GraphElementLabel [91-98]
+                                              Identifier(Account) [91-98]
+                                      GraphEdgePattern [99-122]
+                                        GraphElementPatternFiller [101-119]
+                                          Identifier(transfer) [101-109]
+                                          GraphLabelFilter [109-119]
+                                            GraphElementLabel [110-119]
+                                              Identifier(Transfers) [110-119]
+                                      GraphNodePattern [122-132]
+                                        GraphElementPatternFiller [123-131]
+                                          GraphLabelFilter [123-131]
+                                            GraphElementLabel [124-131]
+                                              Identifier(Account) [124-131]
+                                    WhereClause [135-152]
+                                      BinaryExpression(=) [141-152]
+                                        PathExpression [141-142]
+                                          Identifier(a) [141-142]
+                                        PathExpression [145-152]
+                                          Identifier(account) [145-152]
+                                GqlReturn [155-200]
+                                  Select [162-200]
+                                    SelectList [162-200]
+                                      SelectColumn [162-200]
+                                        FunctionCall [162-182]
+                                          PathExpression [162-165]
+                                            Identifier(SUM) [162-165]
+                                          PathExpression [166-181]
+                                            Identifier(transfer) [166-174]
+                                            Identifier(amount) [175-181]
+                                        Alias [183-200]
+                                          Identifier(total_transfer) [186-200]
+                    Alias [203-220]
+                      Identifier(total_transfer) [206-220]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#example_issue
+GRAPH FinGraph
+MATCH (n:Account {id: 7})
+OPTIONAL MATCH (m:Account)
+WHERE FALSE
+RETURN n.id AS n_id, m.id AS m_id
+--
+QueryStatement [0-113]
+  Query [0-113]
+    GqlQuery [0-113]
+      GraphTableQuery [0-113]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-113]
+          GqlOperatorList [15-113]
+            GqlMatch [15-40]
+              GraphPattern [21-40]
+                GraphPathPattern [21-40]
+                  GraphNodePattern [21-40]
+                    GraphElementPatternFiller [22-39]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                      GraphPropertySpecification [32-39]
+                        GraphPropertyNameAndValue [33-38]
+                          Identifier(id) [33-35]
+                          IntLiteral(7) [37-38]
+            GqlMatch (optional) [41-79]
+              GraphPattern [56-79]
+                GraphPathPattern [56-67]
+                  GraphNodePattern [56-67]
+                    GraphElementPatternFiller [57-66]
+                      Identifier(m) [57-58]
+                      GraphLabelFilter [58-66]
+                        GraphElementLabel [59-66]
+                          Identifier(Account) [59-66]
+                WhereClause [68-79]
+                  BooleanLiteral(FALSE) [74-79]
+            GqlReturn [80-113]
+              Select [87-113]
+                SelectList [87-113]
+                  SelectColumn [87-99]
+                    PathExpression [87-91]
+                      Identifier(n) [87-88]
+                      Identifier(id) [89-91]
+                    Alias [92-99]
+                      Identifier(n_id) [95-99]
+                  SelectColumn [101-113]
+                    PathExpression [101-105]
+                      Identifier(m) [101-102]
+                      Identifier(id) [103-105]
+                    Alias [106-113]
+                      Identifier(m_id) [109-113]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#example_issue
+GRAPH FinGraph
+MATCH (n:Account {id: 7})
+OPTIONAL MATCH (m:Account)
+FILTER FALSE
+RETURN n.id AS n_id, m.id AS m_id
+--
+QueryStatement [0-114]
+  Query [0-114]
+    GqlQuery [0-114]
+      GraphTableQuery [0-114]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-114]
+          GqlOperatorList [15-114]
+            GqlMatch [15-40]
+              GraphPattern [21-40]
+                GraphPathPattern [21-40]
+                  GraphNodePattern [21-40]
+                    GraphElementPatternFiller [22-39]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                      GraphPropertySpecification [32-39]
+                        GraphPropertyNameAndValue [33-38]
+                          Identifier(id) [33-35]
+                          IntLiteral(7) [37-38]
+            GqlMatch (optional) [41-67]
+              GraphPattern [56-67]
+                GraphPathPattern [56-67]
+                  GraphNodePattern [56-67]
+                    GraphElementPatternFiller [57-66]
+                      Identifier(m) [57-58]
+                      GraphLabelFilter [58-66]
+                        GraphElementLabel [59-66]
+                          Identifier(Account) [59-66]
+            GqlFilter [68-80]
+              WhereClause [68-80]
+                BooleanLiteral(FALSE) [75-80]
+            GqlReturn [81-114]
+              Select [88-114]
+                SelectList [88-114]
+                  SelectColumn [88-100]
+                    PathExpression [88-92]
+                      Identifier(n) [88-89]
+                      Identifier(id) [90-92]
+                    Alias [93-100]
+                      Identifier(n_id) [96-100]
+                  SelectColumn [102-114]
+                    PathExpression [102-106]
+                      Identifier(m) [102-103]
+                      Identifier(id) [104-106]
+                    Alias [107-114]
+                      Identifier(m_id) [110-114]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#example_issue_2
+GRAPH FinGraph
+MATCH (n:Account {id: 7})
+RETURN n
+
+NEXT
+
+MATCH (n:Account {id: 16})
+RETURN n.id AS n_id
+--
+QueryStatement [0-103]
+  Query [0-103]
+    GqlQuery [0-103]
+      GraphTableQuery [0-103]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-103]
+          GqlOperatorList [15-49]
+            GqlMatch [15-40]
+              GraphPattern [21-40]
+                GraphPathPattern [21-40]
+                  GraphNodePattern [21-40]
+                    GraphElementPatternFiller [22-39]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                      GraphPropertySpecification [32-39]
+                        GraphPropertyNameAndValue [33-38]
+                          Identifier(id) [33-35]
+                          IntLiteral(7) [37-38]
+            GqlReturn [41-49]
+              Select [48-49]
+                SelectList [48-49]
+                  SelectColumn [48-49]
+                    PathExpression [48-49]
+                      Identifier(n) [48-49]
+          GqlOperatorList [57-103]
+            GqlMatch [57-83]
+              GraphPattern [63-83]
+                GraphPathPattern [63-83]
+                  GraphNodePattern [63-83]
+                    GraphElementPatternFiller [64-82]
+                      Identifier(n) [64-65]
+                      GraphLabelFilter [65-73]
+                        GraphElementLabel [66-73]
+                          Identifier(Account) [66-73]
+                      GraphPropertySpecification [74-82]
+                        GraphPropertyNameAndValue [75-81]
+                          Identifier(id) [75-77]
+                          IntLiteral(16) [79-81]
+            GqlReturn [84-103]
+              Select [91-103]
+                SelectList [91-103]
+                  SelectColumn [91-103]
+                    PathExpression [91-95]
+                      Identifier(n) [91-92]
+                      Identifier(id) [93-95]
+                    Alias [96-103]
+                      Identifier(n_id) [99-103]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#example_issue_2
+GRAPH FinGraph
+MATCH (n:Account {id: 7})
+RETURN n.id AS id
+
+NEXT
+
+MATCH (n:Account {id: 16})
+RETURN n.id AS n_id
+--
+QueryStatement [0-112]
+  Query [0-112]
+    GqlQuery [0-112]
+      GraphTableQuery [0-112]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-112]
+          GqlOperatorList [15-58]
+            GqlMatch [15-40]
+              GraphPattern [21-40]
+                GraphPathPattern [21-40]
+                  GraphNodePattern [21-40]
+                    GraphElementPatternFiller [22-39]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                      GraphPropertySpecification [32-39]
+                        GraphPropertyNameAndValue [33-38]
+                          Identifier(id) [33-35]
+                          IntLiteral(7) [37-38]
+            GqlReturn [41-58]
+              Select [48-58]
+                SelectList [48-58]
+                  SelectColumn [48-58]
+                    PathExpression [48-52]
+                      Identifier(n) [48-49]
+                      Identifier(id) [50-52]
+                    Alias [53-58]
+                      Identifier(id) [56-58]
+          GqlOperatorList [66-112]
+            GqlMatch [66-92]
+              GraphPattern [72-92]
+                GraphPathPattern [72-92]
+                  GraphNodePattern [72-92]
+                    GraphElementPatternFiller [73-91]
+                      Identifier(n) [73-74]
+                      GraphLabelFilter [74-82]
+                        GraphElementLabel [75-82]
+                          Identifier(Account) [75-82]
+                      GraphPropertySpecification [83-91]
+                        GraphPropertyNameAndValue [84-90]
+                          Identifier(id) [84-86]
+                          IntLiteral(16) [88-90]
+            GqlReturn [93-112]
+              Select [100-112]
+                SelectList [100-112]
+                  SelectColumn [100-112]
+                    PathExpression [100-104]
+                      Identifier(n) [100-101]
+                      Identifier(id) [102-104]
+                    Alias [105-112]
+                      Identifier(n_id) [108-112]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#example_issue_3
+GRAPH FinGraph
+MATCH (n:Account)
+ORDER BY n.id DESC
+RETURN n.id
+LIMIT 3
+--
+QueryStatement [0-71]
+  Query [0-71]
+    GqlQuery [0-71]
+      GraphTableQuery [0-71]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-71]
+          GqlOperatorList [15-71]
+            GqlMatch [15-32]
+              GraphPattern [21-32]
+                GraphPathPattern [21-32]
+                  GraphNodePattern [21-32]
+                    GraphElementPatternFiller [22-31]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+            GqlOrderByAndPage [33-51]
+              OrderBy [33-51]
+                OrderingExpression(DESC) [42-51]
+                  PathExpression [42-46]
+                    Identifier(n) [42-43]
+                    Identifier(id) [44-46]
+            GqlReturn [52-71]
+              Select [59-63]
+                SelectList [59-63]
+                  SelectColumn [59-63]
+                    PathExpression [59-63]
+                      Identifier(n) [59-60]
+                      Identifier(id) [61-63]
+              GqlOrderByAndPage [63-71]
+                GqlPage [64-71]
+                  GqlPageLimit [64-71]
+                    IntLiteral(3) [70-71]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#example_issue_3
+GRAPH FinGraph
+MATCH (n:Account)
+ORDER BY n.id DESC
+LIMIT 3
+RETURN n.id
+--
+QueryStatement [0-71]
+  Query [0-71]
+    GqlQuery [0-71]
+      GraphTableQuery [0-71]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-71]
+          GqlOperatorList [15-71]
+            GqlMatch [15-32]
+              GraphPattern [21-32]
+                GraphPathPattern [21-32]
+                  GraphNodePattern [21-32]
+                    GraphElementPatternFiller [22-31]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+            GqlOrderByAndPage [33-51]
+              OrderBy [33-51]
+                OrderingExpression(DESC) [42-51]
+                  PathExpression [42-46]
+                    Identifier(n) [42-43]
+                    Identifier(id) [44-46]
+            GqlOrderByAndPage [52-59]
+              GqlPage [52-59]
+                GqlPageLimit [52-59]
+                  IntLiteral(3) [58-59]
+            GqlReturn [60-71]
+              Select [67-71]
+                SelectList [67-71]
+                  SelectColumn [67-71]
+                    PathExpression [67-71]
+                      Identifier(n) [67-68]
+                      Identifier(id) [69-71]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#example_issue_4
+GRAPH FinGraph
+MATCH (n:Account)-[:Transfers]-(m:Account)
+RETURN COUNT(*) AS num_transfer_edges
+--
+QueryStatement [0-95]
+  Query [0-95]
+    GqlQuery [0-95]
+      GraphTableQuery [0-95]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-95]
+          GqlOperatorList [15-95]
+            GqlMatch [15-57]
+              GraphPattern [21-57]
+                GraphPathPattern [21-57]
+                  GraphNodePattern [21-32]
+                    GraphElementPatternFiller [22-31]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                  GraphEdgePattern [32-46]
+                    GraphElementPatternFiller [34-44]
+                      GraphLabelFilter [34-44]
+                        GraphElementLabel [35-44]
+                          Identifier(Transfers) [35-44]
+                  GraphNodePattern [46-57]
+                    GraphElementPatternFiller [47-56]
+                      Identifier(m) [47-48]
+                      GraphLabelFilter [48-56]
+                        GraphElementLabel [49-56]
+                          Identifier(Account) [49-56]
+            GqlReturn [58-95]
+              Select [65-95]
+                SelectList [65-95]
+                  SelectColumn [65-95]
+                    FunctionCall [65-73]
+                      PathExpression [65-70]
+                        Identifier(COUNT) [65-70]
+                      Star(*) [71-72]
+                    Alias [74-95]
+                      Identifier(num_transfer_edges) [77-95]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#example_issue_4
+GRAPH FinGraph
+MATCH (n:Account)-[:Transfers]->(m:Account)
+RETURN COUNT(*) AS num_transfer_edges
+--
+QueryStatement [0-96]
+  Query [0-96]
+    GqlQuery [0-96]
+      GraphTableQuery [0-96]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-96]
+          GqlOperatorList [15-96]
+            GqlMatch [15-58]
+              GraphPattern [21-58]
+                GraphPathPattern [21-58]
+                  GraphNodePattern [21-32]
+                    GraphElementPatternFiller [22-31]
+                      Identifier(n) [22-23]
+                      GraphLabelFilter [23-31]
+                        GraphElementLabel [24-31]
+                          Identifier(Account) [24-31]
+                  GraphEdgePattern [32-47]
+                    GraphElementPatternFiller [34-44]
+                      GraphLabelFilter [34-44]
+                        GraphElementLabel [35-44]
+                          Identifier(Transfers) [35-44]
+                  GraphNodePattern [47-58]
+                    GraphElementPatternFiller [48-57]
+                      Identifier(m) [48-49]
+                      GraphLabelFilter [49-57]
+                        GraphElementLabel [50-57]
+                          Identifier(Account) [50-57]
+            GqlReturn [59-96]
+              Select [66-96]
+                SelectList [66-96]
+                  SelectColumn [66-96]
+                    FunctionCall [66-74]
+                      PathExpression [66-71]
+                        Identifier(COUNT) [66-71]
+                      Star(*) [72-73]
+                    Alias [75-96]
+                      Identifier(num_transfer_edges) [78-96]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#example_error_11
+INSERT INTO AccountTransferAccount (id, to_id, create_time, amount)
+VALUES (100, 1, PENDING_COMMIT_TIMESTAMP(), 200)
+--
+InsertStatement [0-116]
+  PathExpression [12-34]
+    Identifier(AccountTransferAccount) [12-34]
+  ColumnList [35-67]
+    Identifier(id) [36-38]
+    Identifier(to_id) [40-45]
+    Identifier(create_time) [47-58]
+    Identifier(amount) [60-66]
+  InsertValuesRowList [68-116]
+    InsertValuesRow [75-116]
+      IntLiteral(100) [76-79]
+      IntLiteral(1) [81-82]
+      FunctionCall [84-110]
+        PathExpression [84-108]
+          Identifier(PENDING_COMMIT_TIMESTAMP) [84-108]
+      IntLiteral(200) [112-115]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#example_error_12
+INSERT INTO AccountTransferAccount (id, to_id, create_time, amount)
+VALUES (1, 100, PENDING_COMMIT_TIMESTAMP(), 200)
+--
+InsertStatement [0-116]
+  PathExpression [12-34]
+    Identifier(AccountTransferAccount) [12-34]
+  ColumnList [35-67]
+    Identifier(id) [36-38]
+    Identifier(to_id) [40-45]
+    Identifier(create_time) [47-58]
+    Identifier(amount) [60-66]
+  InsertValuesRowList [68-116]
+    InsertValuesRow [75-116]
+      IntLiteral(1) [76-77]
+      IntLiteral(100) [79-82]
+      FunctionCall [84-110]
+        PathExpression [84-108]
+          Identifier(PENDING_COMMIT_TIMESTAMP) [84-108]
+      IntLiteral(200) [112-115]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/troubleshoot#example_error_13
+DELETE FROM Account WHERE id = 1
+--
+DeleteStatement [0-32]
+  PathExpression [12-19]
+    Identifier(Account) [12-19]
+  BinaryExpression(=) [26-32]
+    PathExpression [26-28]
+      Identifier(id) [26-28]
+    IntLiteral(1) [31-32]
diff --git a/parser/testdata/spanner/guides/graph.work-with-paths.test b/parser/testdata/spanner/guides/graph.work-with-paths.test
new file mode 100644
index 0000000..b9a9192
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.work-with-paths.test
@@ -0,0 +1,1404 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/work-with-paths
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-paths#example
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+GRAPH FinGraph
+MATCH p = (src:Account {id: 16})-[t:Transfers]->{2}(dst:Account {id: 7})
+RETURN TO_JSON(p) AS full_path
+--
+QueryStatement [0-118]
+  Query [0-118]
+    GqlQuery [0-118]
+      GraphTableQuery [0-118]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-118]
+          GqlOperatorList [15-118]
+            GqlMatch [15-87]
+              GraphPattern [21-87]
+                GraphPathPattern [21-87]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [25-47]
+                    GraphElementPatternFiller [26-46]
+                      Identifier(src) [26-29]
+                      GraphLabelFilter [29-37]
+                        GraphElementLabel [30-37]
+                          Identifier(Account) [30-37]
+                      GraphPropertySpecification [38-46]
+                        GraphPropertyNameAndValue [39-45]
+                          Identifier(id) [39-41]
+                          IntLiteral(16) [43-45]
+                  ParenthesizedGraphPathPattern [47-66]
+                    FixedQuantifier [64-65]
+                      IntLiteral(2) [64-65]
+                    GraphEdgePattern [47-63]
+                      GraphElementPatternFiller [49-60]
+                        Identifier(t) [49-50]
+                        GraphLabelFilter [50-60]
+                          GraphElementLabel [51-60]
+                            Identifier(Transfers) [51-60]
+                  GraphNodePattern [66-87]
+                    GraphElementPatternFiller [67-86]
+                      Identifier(dst) [67-70]
+                      GraphLabelFilter [70-78]
+                        GraphElementLabel [71-78]
+                          Identifier(Account) [71-78]
+                      GraphPropertySpecification [79-86]
+                        GraphPropertyNameAndValue [80-85]
+                          Identifier(id) [80-82]
+                          IntLiteral(7) [84-85]
+            GqlReturn [88-118]
+              Select [95-118]
+                SelectList [95-118]
+                  SelectColumn [95-118]
+                    FunctionCall [95-105]
+                      PathExpression [95-102]
+                        Identifier(TO_JSON) [95-102]
+                      PathExpression [103-104]
+                        Identifier(p) [103-104]
+                    Alias [106-118]
+                      Identifier(full_path) [109-118]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-paths#example_2
+GRAPH FinGraph
+MATCH p = (src:Account {id: 7})-[t1:Transfers]->{1,3}(mid:Account)-[t2:Transfers]->
+  {1,3}(dst:Account {id: 16})
+LET second_edge = EDGES(p)[1]
+RETURN DISTINCT src.id AS src, dst.id AS dst, second_edge.amount AS second_edge_amount
+--
+QueryStatement [0-245]
+  Query [0-245]
+    GqlQuery [0-245]
+      GraphTableQuery [0-245]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-245]
+          GqlOperatorList [15-245]
+            GqlMatch [15-128]
+              GraphPattern [21-128]
+                GraphPathPattern [21-128]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [25-46]
+                    GraphElementPatternFiller [26-45]
+                      Identifier(src) [26-29]
+                      GraphLabelFilter [29-37]
+                        GraphElementLabel [30-37]
+                          Identifier(Account) [30-37]
+                      GraphPropertySpecification [38-45]
+                        GraphPropertyNameAndValue [39-44]
+                          Identifier(id) [39-41]
+                          IntLiteral(7) [43-44]
+                  ParenthesizedGraphPathPattern [46-68]
+                    BoundedQuantifier [63-68]
+                      QuantifierBound [64-65]
+                        IntLiteral(1) [64-65]
+                      QuantifierBound [66-67]
+                        IntLiteral(3) [66-67]
+                    GraphEdgePattern [46-63]
+                      GraphElementPatternFiller [48-60]
+                        Identifier(t1) [48-50]
+                        GraphLabelFilter [50-60]
+                          GraphElementLabel [51-60]
+                            Identifier(Transfers) [51-60]
+                  GraphNodePattern [68-81]
+                    GraphElementPatternFiller [69-80]
+                      Identifier(mid) [69-72]
+                      GraphLabelFilter [72-80]
+                        GraphElementLabel [73-80]
+                          Identifier(Account) [73-80]
+                  ParenthesizedGraphPathPattern [81-106]
+                    BoundedQuantifier [101-106]
+                      QuantifierBound [102-103]
+                        IntLiteral(1) [102-103]
+                      QuantifierBound [104-105]
+                        IntLiteral(3) [104-105]
+                    GraphEdgePattern [81-98]
+                      GraphElementPatternFiller [83-95]
+                        Identifier(t2) [83-85]
+                        GraphLabelFilter [85-95]
+                          GraphElementLabel [86-95]
+                            Identifier(Transfers) [86-95]
+                  GraphNodePattern [106-128]
+                    GraphElementPatternFiller [107-127]
+                      Identifier(dst) [107-110]
+                      GraphLabelFilter [110-118]
+                        GraphElementLabel [111-118]
+                          Identifier(Account) [111-118]
+                      GraphPropertySpecification [119-127]
+                        GraphPropertyNameAndValue [120-126]
+                          Identifier(id) [120-122]
+                          IntLiteral(16) [124-126]
+            GqlLet [129-158]
+              GqlLetVariableDefinitionList [133-158]
+                GqlLetVariableDefinition [133-158]
+                  Identifier(second_edge) [133-144]
+                  ArrayElement [147-158]
+                    FunctionCall [147-155]
+                      PathExpression [147-152]
+                        Identifier(EDGES) [147-152]
+                      PathExpression [153-154]
+                        Identifier(p) [153-154]
+                    Location [155-156]
+                    IntLiteral(1) [156-157]
+            GqlReturn [159-245]
+              Select(distinct=true) [166-245]
+                SelectList [175-245]
+                  SelectColumn [175-188]
+                    PathExpression [175-181]
+                      Identifier(src) [175-178]
+                      Identifier(id) [179-181]
+                    Alias [182-188]
+                      Identifier(src) [185-188]
+                  SelectColumn [190-203]
+                    PathExpression [190-196]
+                      Identifier(dst) [190-193]
+                      Identifier(id) [194-196]
+                    Alias [197-203]
+                      Identifier(dst) [200-203]
+                  SelectColumn [205-245]
+                    PathExpression [205-223]
+                      Identifier(second_edge) [205-216]
+                      Identifier(amount) [217-223]
+                    Alias [224-245]
+                      Identifier(second_edge_amount) [227-245]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-paths#example_3
+GRAPH FinGraph
+MATCH p = (src:Account)-[t:Transfers]->{2}(dst:Account)
+RETURN TO_JSON(NODES(p)) AS nodes
+--
+QueryStatement [0-104]
+  Query [0-104]
+    GqlQuery [0-104]
+      GraphTableQuery [0-104]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-104]
+          GqlOperatorList [15-104]
+            GqlMatch [15-70]
+              GraphPattern [21-70]
+                GraphPathPattern [21-70]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [25-38]
+                    GraphElementPatternFiller [26-37]
+                      Identifier(src) [26-29]
+                      GraphLabelFilter [29-37]
+                        GraphElementLabel [30-37]
+                          Identifier(Account) [30-37]
+                  ParenthesizedGraphPathPattern [38-57]
+                    FixedQuantifier [55-56]
+                      IntLiteral(2) [55-56]
+                    GraphEdgePattern [38-54]
+                      GraphElementPatternFiller [40-51]
+                        Identifier(t) [40-41]
+                        GraphLabelFilter [41-51]
+                          GraphElementLabel [42-51]
+                            Identifier(Transfers) [42-51]
+                  GraphNodePattern [57-70]
+                    GraphElementPatternFiller [58-69]
+                      Identifier(dst) [58-61]
+                      GraphLabelFilter [61-69]
+                        GraphElementLabel [62-69]
+                          Identifier(Account) [62-69]
+            GqlReturn [71-104]
+              Select [78-104]
+                SelectList [78-104]
+                  SelectColumn [78-104]
+                    FunctionCall [78-95]
+                      PathExpression [78-85]
+                        Identifier(TO_JSON) [78-85]
+                      FunctionCall [86-94]
+                        PathExpression [86-91]
+                          Identifier(NODES) [86-91]
+                        PathExpression [92-93]
+                          Identifier(p) [92-93]
+                    Alias [96-104]
+                      Identifier(nodes) [99-104]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-paths#example_4
+GRAPH FinGraph
+MATCH p = -[:Transfers]->{1,3}(dst:Account{id: 7})
+RETURN DISTINCT PATH_FIRST(p).id AS can_reach_target
+--
+QueryStatement [0-118]
+  Query [0-118]
+    GqlQuery [0-118]
+      GraphTableQuery [0-118]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-118]
+          GqlOperatorList [15-118]
+            GqlMatch [15-65]
+              GraphPattern [21-65]
+                GraphPathPattern [21-65]
+                  Identifier(p) [21-22]
+                  ParenthesizedGraphPathPattern [25-45]
+                    BoundedQuantifier [40-45]
+                      QuantifierBound [41-42]
+                        IntLiteral(1) [41-42]
+                      QuantifierBound [43-44]
+                        IntLiteral(3) [43-44]
+                    GraphEdgePattern [25-40]
+                      GraphElementPatternFiller [27-37]
+                        GraphLabelFilter [27-37]
+                          GraphElementLabel [28-37]
+                            Identifier(Transfers) [28-37]
+                  GraphNodePattern [45-65]
+                    GraphElementPatternFiller [46-64]
+                      Identifier(dst) [46-49]
+                      GraphLabelFilter [49-57]
+                        GraphElementLabel [50-57]
+                          Identifier(Account) [50-57]
+                      GraphPropertySpecification [57-64]
+                        GraphPropertyNameAndValue [58-63]
+                          Identifier(id) [58-60]
+                          IntLiteral(7) [62-63]
+            GqlReturn [66-118]
+              Select(distinct=true) [73-118]
+                SelectList [82-118]
+                  SelectColumn [82-118]
+                    DotIdentifier [82-98]
+                      FunctionCall [82-95]
+                        PathExpression [82-92]
+                          Identifier(PATH_FIRST) [82-92]
+                        PathExpression [93-94]
+                          Identifier(p) [93-94]
+                      Identifier(id) [96-98]
+                    Alias [99-118]
+                      Identifier(can_reach_target) [102-118]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-paths#example_5
+GRAPH FinGraph
+MATCH p =(start:Account{id: 7})-[:Transfers]->{1,3}
+RETURN DISTINCT PATH_LAST(p).id as can_reach_target
+--
+QueryStatement [0-118]
+  Query [0-118]
+    GqlQuery [0-118]
+      GraphTableQuery [0-118]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-118]
+          GqlOperatorList [15-118]
+            GqlMatch [15-66]
+              GraphPattern [21-66]
+                GraphPathPattern [21-66]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [24-46]
+                    GraphElementPatternFiller [25-45]
+                      Identifier(start) [25-30]
+                      GraphLabelFilter [30-38]
+                        GraphElementLabel [31-38]
+                          Identifier(Account) [31-38]
+                      GraphPropertySpecification [38-45]
+                        GraphPropertyNameAndValue [39-44]
+                          Identifier(id) [39-41]
+                          IntLiteral(7) [43-44]
+                  ParenthesizedGraphPathPattern [46-66]
+                    BoundedQuantifier [61-66]
+                      QuantifierBound [62-63]
+                        IntLiteral(1) [62-63]
+                      QuantifierBound [64-65]
+                        IntLiteral(3) [64-65]
+                    GraphEdgePattern [46-61]
+                      GraphElementPatternFiller [48-58]
+                        GraphLabelFilter [48-58]
+                          GraphElementLabel [49-58]
+                            Identifier(Transfers) [49-58]
+            GqlReturn [67-118]
+              Select(distinct=true) [74-118]
+                SelectList [83-118]
+                  SelectColumn [83-118]
+                    DotIdentifier [83-98]
+                      FunctionCall [83-95]
+                        PathExpression [83-92]
+                          Identifier(PATH_LAST) [83-92]
+                        PathExpression [93-94]
+                          Identifier(p) [93-94]
+                      Identifier(id) [96-98]
+                    Alias [99-118]
+                      Identifier(can_reach_target) [102-118]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-paths#example_6
+GRAPH FinGraph
+MATCH p = (src:Account)-[e:Transfers]->{1,3}(dst:Account)
+RETURN PATH_LENGTH(p) AS num_transfers, COUNT(*) AS num_paths
+--
+QueryStatement [0-134]
+  Query [0-134]
+    GqlQuery [0-134]
+      GraphTableQuery [0-134]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-134]
+          GqlOperatorList [15-134]
+            GqlMatch [15-72]
+              GraphPattern [21-72]
+                GraphPathPattern [21-72]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [25-38]
+                    GraphElementPatternFiller [26-37]
+                      Identifier(src) [26-29]
+                      GraphLabelFilter [29-37]
+                        GraphElementLabel [30-37]
+                          Identifier(Account) [30-37]
+                  ParenthesizedGraphPathPattern [38-59]
+                    BoundedQuantifier [54-59]
+                      QuantifierBound [55-56]
+                        IntLiteral(1) [55-56]
+                      QuantifierBound [57-58]
+                        IntLiteral(3) [57-58]
+                    GraphEdgePattern [38-54]
+                      GraphElementPatternFiller [40-51]
+                        Identifier(e) [40-41]
+                        GraphLabelFilter [41-51]
+                          GraphElementLabel [42-51]
+                            Identifier(Transfers) [42-51]
+                  GraphNodePattern [59-72]
+                    GraphElementPatternFiller [60-71]
+                      Identifier(dst) [60-63]
+                      GraphLabelFilter [63-71]
+                        GraphElementLabel [64-71]
+                          Identifier(Account) [64-71]
+            GqlReturn [73-134]
+              Select [80-134]
+                SelectList [80-134]
+                  SelectColumn [80-111]
+                    FunctionCall [80-94]
+                      PathExpression [80-91]
+                        Identifier(PATH_LENGTH) [80-91]
+                      PathExpression [92-93]
+                        Identifier(p) [92-93]
+                    Alias [95-111]
+                      Identifier(num_transfers) [98-111]
+                  SelectColumn [113-134]
+                    FunctionCall [113-121]
+                      PathExpression [113-118]
+                        Identifier(COUNT) [113-118]
+                      Star(*) [119-120]
+                    Alias [122-134]
+                      Identifier(num_paths) [125-134]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-paths#example_7
+GRAPH FinGraph
+MATCH p = (src:Account)-[t:Transfers]->{2}(dst:Account)
+RETURN IS_ACYCLIC(p) AS is_acyclic_path,
+       ARRAY_TRANSFORM(NODES(p), n->n.id) AS account_ids
+--
+QueryStatement [0-168]
+  Query [0-168]
+    GqlQuery [0-168]
+      GraphTableQuery [0-168]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-168]
+          GqlOperatorList [15-168]
+            GqlMatch [15-70]
+              GraphPattern [21-70]
+                GraphPathPattern [21-70]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [25-38]
+                    GraphElementPatternFiller [26-37]
+                      Identifier(src) [26-29]
+                      GraphLabelFilter [29-37]
+                        GraphElementLabel [30-37]
+                          Identifier(Account) [30-37]
+                  ParenthesizedGraphPathPattern [38-57]
+                    FixedQuantifier [55-56]
+                      IntLiteral(2) [55-56]
+                    GraphEdgePattern [38-54]
+                      GraphElementPatternFiller [40-51]
+                        Identifier(t) [40-41]
+                        GraphLabelFilter [41-51]
+                          GraphElementLabel [42-51]
+                            Identifier(Transfers) [42-51]
+                  GraphNodePattern [57-70]
+                    GraphElementPatternFiller [58-69]
+                      Identifier(dst) [58-61]
+                      GraphLabelFilter [61-69]
+                        GraphElementLabel [62-69]
+                          Identifier(Account) [62-69]
+            GqlReturn [71-168]
+              Select [78-168]
+                SelectList [78-168]
+                  SelectColumn [78-110]
+                    FunctionCall [78-91]
+                      PathExpression [78-88]
+                        Identifier(IS_ACYCLIC) [78-88]
+                      PathExpression [89-90]
+                        Identifier(p) [89-90]
+                    Alias [92-110]
+                      Identifier(is_acyclic_path) [95-110]
+                  SelectColumn [119-168]
+                    FunctionCall [119-153]
+                      PathExpression [119-134]
+                        Identifier(ARRAY_TRANSFORM) [119-134]
+                      FunctionCall [135-143]
+                        PathExpression [135-140]
+                          Identifier(NODES) [135-140]
+                        PathExpression [141-142]
+                          Identifier(p) [141-142]
+                      Lambda [145-152]
+                        PathExpression [145-146]
+                          Identifier(n) [145-146]
+                        PathExpression [148-152]
+                          Identifier(n) [148-149]
+                          Identifier(id) [150-152]
+                    Alias [154-168]
+                      Identifier(account_ids) [157-168]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-paths#example_8
+GRAPH FinGraph
+MATCH p = (src:Account)-[t:Transfers]->{3}(dst:Account)
+WHERE src.id < dst.id
+RETURN IS_TRAIL(p) AS is_trail_path,
+       ARRAY_TRANSFORM(t, t->t.id) AS transfer_ids
+--
+QueryStatement [0-180]
+  Query [0-180]
+    GqlQuery [0-180]
+      GraphTableQuery [0-180]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-180]
+          GqlOperatorList [15-180]
+            GqlMatch [15-92]
+              GraphPattern [21-92]
+                GraphPathPattern [21-70]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [25-38]
+                    GraphElementPatternFiller [26-37]
+                      Identifier(src) [26-29]
+                      GraphLabelFilter [29-37]
+                        GraphElementLabel [30-37]
+                          Identifier(Account) [30-37]
+                  ParenthesizedGraphPathPattern [38-57]
+                    FixedQuantifier [55-56]
+                      IntLiteral(3) [55-56]
+                    GraphEdgePattern [38-54]
+                      GraphElementPatternFiller [40-51]
+                        Identifier(t) [40-41]
+                        GraphLabelFilter [41-51]
+                          GraphElementLabel [42-51]
+                            Identifier(Transfers) [42-51]
+                  GraphNodePattern [57-70]
+                    GraphElementPatternFiller [58-69]
+                      Identifier(dst) [58-61]
+                      GraphLabelFilter [61-69]
+                        GraphElementLabel [62-69]
+                          Identifier(Account) [62-69]
+                WhereClause [71-92]
+                  BinaryExpression(<) [77-92]
+                    PathExpression [77-83]
+                      Identifier(src) [77-80]
+                      Identifier(id) [81-83]
+                    PathExpression [86-92]
+                      Identifier(dst) [86-89]
+                      Identifier(id) [90-92]
+            GqlReturn [93-180]
+              Select [100-180]
+                SelectList [100-180]
+                  SelectColumn [100-128]
+                    FunctionCall [100-111]
+                      PathExpression [100-108]
+                        Identifier(IS_TRAIL) [100-108]
+                      PathExpression [109-110]
+                        Identifier(p) [109-110]
+                    Alias [112-128]
+                      Identifier(is_trail_path) [115-128]
+                  SelectColumn [137-180]
+                    FunctionCall [137-164]
+                      PathExpression [137-152]
+                        Identifier(ARRAY_TRANSFORM) [137-152]
+                      PathExpression [153-154]
+                        Identifier(t) [153-154]
+                      Lambda [156-163]
+                        PathExpression [156-157]
+                          Identifier(t) [156-157]
+                        PathExpression [159-163]
+                          Identifier(t) [159-160]
+                          Identifier(id) [161-163]
+                    Alias [165-180]
+                      Identifier(transfer_ids) [168-180]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-paths#example_9
+GRAPH FinGraph
+MATCH p = WALK (src:Account)-[t:Transfers]->{3}(dst:Account)
+WHERE src.id < dst.id
+RETURN ARRAY_TRANSFORM(t, t->t.id) AS transfer_ids
+--
+QueryStatement [0-148]
+  Query [0-148]
+    GqlQuery [0-148]
+      GraphTableQuery [0-148]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-148]
+          GqlOperatorList [15-148]
+            GqlMatch [15-97]
+              GraphPattern [21-97]
+                GraphPathPattern [21-75]
+                  Identifier(p) [21-22]
+                  GraphPathMode [25-29]
+                  GraphNodePattern [30-43]
+                    GraphElementPatternFiller [31-42]
+                      Identifier(src) [31-34]
+                      GraphLabelFilter [34-42]
+                        GraphElementLabel [35-42]
+                          Identifier(Account) [35-42]
+                  ParenthesizedGraphPathPattern [43-62]
+                    FixedQuantifier [60-61]
+                      IntLiteral(3) [60-61]
+                    GraphEdgePattern [43-59]
+                      GraphElementPatternFiller [45-56]
+                        Identifier(t) [45-46]
+                        GraphLabelFilter [46-56]
+                          GraphElementLabel [47-56]
+                            Identifier(Transfers) [47-56]
+                  GraphNodePattern [62-75]
+                    GraphElementPatternFiller [63-74]
+                      Identifier(dst) [63-66]
+                      GraphLabelFilter [66-74]
+                        GraphElementLabel [67-74]
+                          Identifier(Account) [67-74]
+                WhereClause [76-97]
+                  BinaryExpression(<) [82-97]
+                    PathExpression [82-88]
+                      Identifier(src) [82-85]
+                      Identifier(id) [86-88]
+                    PathExpression [91-97]
+                      Identifier(dst) [91-94]
+                      Identifier(id) [95-97]
+            GqlReturn [98-148]
+              Select [105-148]
+                SelectList [105-148]
+                  SelectColumn [105-148]
+                    FunctionCall [105-132]
+                      PathExpression [105-120]
+                        Identifier(ARRAY_TRANSFORM) [105-120]
+                      PathExpression [121-122]
+                        Identifier(t) [121-122]
+                      Lambda [124-131]
+                        PathExpression [124-125]
+                          Identifier(t) [124-125]
+                        PathExpression [127-131]
+                          Identifier(t) [127-128]
+                          Identifier(id) [129-131]
+                    Alias [133-148]
+                      Identifier(transfer_ids) [136-148]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-paths#example_10
+GRAPH FinGraph
+MATCH p = ACYCLIC (src:Account)-[t:Transfers]->{2}(dst:Account)
+RETURN ARRAY_TRANSFORM(NODES(p), n->n.id) AS account_ids
+--
+QueryStatement [0-135]
+  Query [0-135]
+    GqlQuery [0-135]
+      GraphTableQuery [0-135]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-135]
+          GqlOperatorList [15-135]
+            GqlMatch [15-78]
+              GraphPattern [21-78]
+                GraphPathPattern [21-78]
+                  Identifier(p) [21-22]
+                  GraphPathMode [25-32]
+                  GraphNodePattern [33-46]
+                    GraphElementPatternFiller [34-45]
+                      Identifier(src) [34-37]
+                      GraphLabelFilter [37-45]
+                        GraphElementLabel [38-45]
+                          Identifier(Account) [38-45]
+                  ParenthesizedGraphPathPattern [46-65]
+                    FixedQuantifier [63-64]
+                      IntLiteral(2) [63-64]
+                    GraphEdgePattern [46-62]
+                      GraphElementPatternFiller [48-59]
+                        Identifier(t) [48-49]
+                        GraphLabelFilter [49-59]
+                          GraphElementLabel [50-59]
+                            Identifier(Transfers) [50-59]
+                  GraphNodePattern [65-78]
+                    GraphElementPatternFiller [66-77]
+                      Identifier(dst) [66-69]
+                      GraphLabelFilter [69-77]
+                        GraphElementLabel [70-77]
+                          Identifier(Account) [70-77]
+            GqlReturn [79-135]
+              Select [86-135]
+                SelectList [86-135]
+                  SelectColumn [86-135]
+                    FunctionCall [86-120]
+                      PathExpression [86-101]
+                        Identifier(ARRAY_TRANSFORM) [86-101]
+                      FunctionCall [102-110]
+                        PathExpression [102-107]
+                          Identifier(NODES) [102-107]
+                        PathExpression [108-109]
+                          Identifier(p) [108-109]
+                      Lambda [112-119]
+                        PathExpression [112-113]
+                          Identifier(n) [112-113]
+                        PathExpression [115-119]
+                          Identifier(n) [115-116]
+                          Identifier(id) [117-119]
+                    Alias [121-135]
+                      Identifier(account_ids) [124-135]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-paths#example_11
+GRAPH FinGraph
+MATCH p = TRAIL (src:Account)-[t:Transfers]->{3}(dst:Account)
+WHERE src.id < dst.id
+RETURN ARRAY_TRANSFORM(t, t->t.id) AS transfer_ids
+--
+QueryStatement [0-149]
+  Query [0-149]
+    GqlQuery [0-149]
+      GraphTableQuery [0-149]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-149]
+          GqlOperatorList [15-149]
+            GqlMatch [15-98]
+              GraphPattern [21-98]
+                GraphPathPattern [21-76]
+                  Identifier(p) [21-22]
+                  GraphPathMode [25-30]
+                  GraphNodePattern [31-44]
+                    GraphElementPatternFiller [32-43]
+                      Identifier(src) [32-35]
+                      GraphLabelFilter [35-43]
+                        GraphElementLabel [36-43]
+                          Identifier(Account) [36-43]
+                  ParenthesizedGraphPathPattern [44-63]
+                    FixedQuantifier [61-62]
+                      IntLiteral(3) [61-62]
+                    GraphEdgePattern [44-60]
+                      GraphElementPatternFiller [46-57]
+                        Identifier(t) [46-47]
+                        GraphLabelFilter [47-57]
+                          GraphElementLabel [48-57]
+                            Identifier(Transfers) [48-57]
+                  GraphNodePattern [63-76]
+                    GraphElementPatternFiller [64-75]
+                      Identifier(dst) [64-67]
+                      GraphLabelFilter [67-75]
+                        GraphElementLabel [68-75]
+                          Identifier(Account) [68-75]
+                WhereClause [77-98]
+                  BinaryExpression(<) [83-98]
+                    PathExpression [83-89]
+                      Identifier(src) [83-86]
+                      Identifier(id) [87-89]
+                    PathExpression [92-98]
+                      Identifier(dst) [92-95]
+                      Identifier(id) [96-98]
+            GqlReturn [99-149]
+              Select [106-149]
+                SelectList [106-149]
+                  SelectColumn [106-149]
+                    FunctionCall [106-133]
+                      PathExpression [106-121]
+                        Identifier(ARRAY_TRANSFORM) [106-121]
+                      PathExpression [122-123]
+                        Identifier(t) [122-123]
+                      Lambda [125-132]
+                        PathExpression [125-126]
+                          Identifier(t) [125-126]
+                        PathExpression [128-132]
+                          Identifier(t) [128-129]
+                          Identifier(id) [130-132]
+                    Alias [134-149]
+                      Identifier(transfer_ids) [137-149]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-paths#example_1
+GRAPH FinGraph
+MATCH p = ANY SHORTEST (a:Account {is_blocked:true})-[t:Transfers]->{1,4}(b:Account)
+LET total_amount = SUM(t.amount)
+RETURN a.id AS account1_id, total_amount, b.id AS account2_id
+--
+QueryStatement [0-194]
+  Query [0-194]
+    GqlQuery [0-194]
+      GraphTableQuery [0-194]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-194]
+          GqlOperatorList [15-194]
+            GqlMatch [15-99]
+              GraphPattern [21-99]
+                GraphPathPattern [21-99]
+                  Identifier(p) [21-22]
+                  GraphPathSearchPrefix [25-37]
+                  GraphNodePattern [38-67]
+                    GraphElementPatternFiller [39-66]
+                      Identifier(a) [39-40]
+                      GraphLabelFilter [40-48]
+                        GraphElementLabel [41-48]
+                          Identifier(Account) [41-48]
+                      GraphPropertySpecification [49-66]
+                        GraphPropertyNameAndValue [50-65]
+                          Identifier(is_blocked) [50-60]
+                          BooleanLiteral(true) [61-65]
+                  ParenthesizedGraphPathPattern [67-88]
+                    BoundedQuantifier [83-88]
+                      QuantifierBound [84-85]
+                        IntLiteral(1) [84-85]
+                      QuantifierBound [86-87]
+                        IntLiteral(4) [86-87]
+                    GraphEdgePattern [67-83]
+                      GraphElementPatternFiller [69-80]
+                        Identifier(t) [69-70]
+                        GraphLabelFilter [70-80]
+                          GraphElementLabel [71-80]
+                            Identifier(Transfers) [71-80]
+                  GraphNodePattern [88-99]
+                    GraphElementPatternFiller [89-98]
+                      Identifier(b) [89-90]
+                      GraphLabelFilter [90-98]
+                        GraphElementLabel [91-98]
+                          Identifier(Account) [91-98]
+            GqlLet [100-132]
+              GqlLetVariableDefinitionList [104-132]
+                GqlLetVariableDefinition [104-132]
+                  Identifier(total_amount) [104-116]
+                  FunctionCall [119-132]
+                    PathExpression [119-122]
+                      Identifier(SUM) [119-122]
+                    PathExpression [123-131]
+                      Identifier(t) [123-124]
+                      Identifier(amount) [125-131]
+            GqlReturn [133-194]
+              Select [140-194]
+                SelectList [140-194]
+                  SelectColumn [140-159]
+                    PathExpression [140-144]
+                      Identifier(a) [140-141]
+                      Identifier(id) [142-144]
+                    Alias [145-159]
+                      Identifier(account1_id) [148-159]
+                  SelectColumn [161-173]
+                    PathExpression [161-173]
+                      Identifier(total_amount) [161-173]
+                  SelectColumn [175-194]
+                    PathExpression [175-179]
+                      Identifier(b) [175-176]
+                      Identifier(id) [177-179]
+                    Alias [180-194]
+                      Identifier(account2_id) [183-194]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-paths#example_2
+GRAPH FinGraph
+MATCH p = ANY SHORTEST (a:Account {id: 7})-[:Transfers]->{1,3}(d:Account {id: 20})
+RETURN TO_JSON(p) AS results
+--
+QueryStatement [0-126]
+  Query [0-126]
+    GqlQuery [0-126]
+      GraphTableQuery [0-126]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-126]
+          GqlOperatorList [15-126]
+            GqlMatch [15-97]
+              GraphPattern [21-97]
+                GraphPathPattern [21-97]
+                  Identifier(p) [21-22]
+                  GraphPathSearchPrefix [25-37]
+                  GraphNodePattern [38-57]
+                    GraphElementPatternFiller [39-56]
+                      Identifier(a) [39-40]
+                      GraphLabelFilter [40-48]
+                        GraphElementLabel [41-48]
+                          Identifier(Account) [41-48]
+                      GraphPropertySpecification [49-56]
+                        GraphPropertyNameAndValue [50-55]
+                          Identifier(id) [50-52]
+                          IntLiteral(7) [54-55]
+                  ParenthesizedGraphPathPattern [57-77]
+                    BoundedQuantifier [72-77]
+                      QuantifierBound [73-74]
+                        IntLiteral(1) [73-74]
+                      QuantifierBound [75-76]
+                        IntLiteral(3) [75-76]
+                    GraphEdgePattern [57-72]
+                      GraphElementPatternFiller [59-69]
+                        GraphLabelFilter [59-69]
+                          GraphElementLabel [60-69]
+                            Identifier(Transfers) [60-69]
+                  GraphNodePattern [77-97]
+                    GraphElementPatternFiller [78-96]
+                      Identifier(d) [78-79]
+                      GraphLabelFilter [79-87]
+                        GraphElementLabel [80-87]
+                          Identifier(Account) [80-87]
+                      GraphPropertySpecification [88-96]
+                        GraphPropertyNameAndValue [89-95]
+                          Identifier(id) [89-91]
+                          IntLiteral(20) [93-95]
+            GqlReturn [98-126]
+              Select [105-126]
+                SelectList [105-126]
+                  SelectColumn [105-126]
+                    FunctionCall [105-115]
+                      PathExpression [105-112]
+                        Identifier(TO_JSON) [105-112]
+                      PathExpression [113-114]
+                        Identifier(p) [113-114]
+                    Alias [116-126]
+                      Identifier(results) [119-126]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-paths#example_12
+GRAPH FinGraph
+MATCH ANY CHEAPEST (a:Account)-[t:Transfers COST t.amount]->{1,3}(b:Account)
+LET total_cost = sum(t.amount)
+RETURN a.id AS account1_id, b.id AS account2_id, total_cost
+--
+QueryStatement [0-182]
+  Query [0-182]
+    GqlQuery [0-182]
+      GraphTableQuery [0-182]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-182]
+          GqlOperatorList [15-182]
+            GqlMatch [15-91]
+              GraphPattern [21-91]
+                GraphPathPattern [21-91]
+                  GraphPathSearchPrefix [21-33]
+                  GraphNodePattern [34-45]
+                    GraphElementPatternFiller [35-44]
+                      Identifier(a) [35-36]
+                      GraphLabelFilter [36-44]
+                        GraphElementLabel [37-44]
+                          Identifier(Account) [37-44]
+                  ParenthesizedGraphPathPattern [45-80]
+                    BoundedQuantifier [75-80]
+                      QuantifierBound [76-77]
+                        IntLiteral(1) [76-77]
+                      QuantifierBound [78-79]
+                        IntLiteral(3) [78-79]
+                    GraphEdgePattern [45-75]
+                      GraphElementPatternFiller [47-72]
+                        Identifier(t) [47-48]
+                        GraphLabelFilter [48-58]
+                          GraphElementLabel [49-58]
+                            Identifier(Transfers) [49-58]
+                        PathExpression [64-72]
+                          Identifier(t) [64-65]
+                          Identifier(amount) [66-72]
+                  GraphNodePattern [80-91]
+                    GraphElementPatternFiller [81-90]
+                      Identifier(b) [81-82]
+                      GraphLabelFilter [82-90]
+                        GraphElementLabel [83-90]
+                          Identifier(Account) [83-90]
+            GqlLet [92-122]
+              GqlLetVariableDefinitionList [96-122]
+                GqlLetVariableDefinition [96-122]
+                  Identifier(total_cost) [96-106]
+                  FunctionCall [109-122]
+                    PathExpression [109-112]
+                      Identifier(sum) [109-112]
+                    PathExpression [113-121]
+                      Identifier(t) [113-114]
+                      Identifier(amount) [115-121]
+            GqlReturn [123-182]
+              Select [130-182]
+                SelectList [130-182]
+                  SelectColumn [130-149]
+                    PathExpression [130-134]
+                      Identifier(a) [130-131]
+                      Identifier(id) [132-134]
+                    Alias [135-149]
+                      Identifier(account1_id) [138-149]
+                  SelectColumn [151-170]
+                    PathExpression [151-155]
+                      Identifier(b) [151-152]
+                      Identifier(id) [153-155]
+                    Alias [156-170]
+                      Identifier(account2_id) [159-170]
+                  SelectColumn [172-182]
+                    PathExpression [172-182]
+                      Identifier(total_cost) [172-182]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-paths#use_case_example
+GRAPH FinGraph
+MATCH p = (start:Account {id: 20})-[:Transfers]->{1,3}(dst:Account)
+RETURN DISTINCT dst.id AS dst
+--
+QueryStatement [0-112]
+  Query [0-112]
+    GqlQuery [0-112]
+      GraphTableQuery [0-112]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-112]
+          GqlOperatorList [15-112]
+            GqlMatch [15-82]
+              GraphPattern [21-82]
+                GraphPathPattern [21-82]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [25-49]
+                    GraphElementPatternFiller [26-48]
+                      Identifier(start) [26-31]
+                      GraphLabelFilter [31-39]
+                        GraphElementLabel [32-39]
+                          Identifier(Account) [32-39]
+                      GraphPropertySpecification [40-48]
+                        GraphPropertyNameAndValue [41-47]
+                          Identifier(id) [41-43]
+                          IntLiteral(20) [45-47]
+                  ParenthesizedGraphPathPattern [49-69]
+                    BoundedQuantifier [64-69]
+                      QuantifierBound [65-66]
+                        IntLiteral(1) [65-66]
+                      QuantifierBound [67-68]
+                        IntLiteral(3) [67-68]
+                    GraphEdgePattern [49-64]
+                      GraphElementPatternFiller [51-61]
+                        GraphLabelFilter [51-61]
+                          GraphElementLabel [52-61]
+                            Identifier(Transfers) [52-61]
+                  GraphNodePattern [69-82]
+                    GraphElementPatternFiller [70-81]
+                      Identifier(dst) [70-73]
+                      GraphLabelFilter [73-81]
+                        GraphElementLabel [74-81]
+                          Identifier(Account) [74-81]
+            GqlReturn [83-112]
+              Select(distinct=true) [90-112]
+                SelectList [99-112]
+                  SelectColumn [99-112]
+                    PathExpression [99-105]
+                      Identifier(dst) [99-102]
+                      Identifier(id) [103-105]
+                    Alias [106-112]
+                      Identifier(dst) [109-112]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-paths#use_case_example
+GRAPH FinGraph
+MATCH p = ACYCLIC (start:Account {id: 20})-[:Transfers]->{1,3}(dst:Account)
+RETURN DISTINCT dst.id AS dst
+--
+QueryStatement [0-120]
+  Query [0-120]
+    GqlQuery [0-120]
+      GraphTableQuery [0-120]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-120]
+          GqlOperatorList [15-120]
+            GqlMatch [15-90]
+              GraphPattern [21-90]
+                GraphPathPattern [21-90]
+                  Identifier(p) [21-22]
+                  GraphPathMode [25-32]
+                  GraphNodePattern [33-57]
+                    GraphElementPatternFiller [34-56]
+                      Identifier(start) [34-39]
+                      GraphLabelFilter [39-47]
+                        GraphElementLabel [40-47]
+                          Identifier(Account) [40-47]
+                      GraphPropertySpecification [48-56]
+                        GraphPropertyNameAndValue [49-55]
+                          Identifier(id) [49-51]
+                          IntLiteral(20) [53-55]
+                  ParenthesizedGraphPathPattern [57-77]
+                    BoundedQuantifier [72-77]
+                      QuantifierBound [73-74]
+                        IntLiteral(1) [73-74]
+                      QuantifierBound [75-76]
+                        IntLiteral(3) [75-76]
+                    GraphEdgePattern [57-72]
+                      GraphElementPatternFiller [59-69]
+                        GraphLabelFilter [59-69]
+                          GraphElementLabel [60-69]
+                            Identifier(Transfers) [60-69]
+                  GraphNodePattern [77-90]
+                    GraphElementPatternFiller [78-89]
+                      Identifier(dst) [78-81]
+                      GraphLabelFilter [81-89]
+                        GraphElementLabel [82-89]
+                          Identifier(Account) [82-89]
+            GqlReturn [91-120]
+              Select(distinct=true) [98-120]
+                SelectList [107-120]
+                  SelectColumn [107-120]
+                    PathExpression [107-113]
+                      Identifier(dst) [107-110]
+                      Identifier(id) [111-113]
+                    Alias [114-120]
+                      Identifier(dst) [117-120]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-paths#use_case_example
+GRAPH FinGraph
+MATCH p = ACYCLIC (start:Account {id: 20})(-[:Transfers]->
+  (nexts:Account)){1,3}(dst:Account)
+RETURN dst.id AS dst, ARRAY_AGG(DISTINCT nexts[0].id) AS unique_starts
+--
+QueryStatement [0-181]
+  Query [0-181]
+    GqlQuery [0-181]
+      GraphTableQuery [0-181]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-181]
+          GqlOperatorList [15-181]
+            GqlMatch [15-110]
+              GraphPattern [21-110]
+                GraphPathPattern [21-110]
+                  Identifier(p) [21-22]
+                  GraphPathMode [25-32]
+                  GraphNodePattern [33-57]
+                    GraphElementPatternFiller [34-56]
+                      Identifier(start) [34-39]
+                      GraphLabelFilter [39-47]
+                        GraphElementLabel [40-47]
+                          Identifier(Account) [40-47]
+                      GraphPropertySpecification [48-56]
+                        GraphPropertyNameAndValue [49-55]
+                          Identifier(id) [49-51]
+                          IntLiteral(20) [53-55]
+                  ParenthesizedGraphPathPattern [57-97]
+                    BoundedQuantifier [92-97]
+                      QuantifierBound [93-94]
+                        IntLiteral(1) [93-94]
+                      QuantifierBound [95-96]
+                        IntLiteral(3) [95-96]
+                    GraphEdgePattern [58-73]
+                      GraphElementPatternFiller [60-70]
+                        GraphLabelFilter [60-70]
+                          GraphElementLabel [61-70]
+                            Identifier(Transfers) [61-70]
+                    GraphNodePattern [76-91]
+                      GraphElementPatternFiller [77-90]
+                        Identifier(nexts) [77-82]
+                        GraphLabelFilter [82-90]
+                          GraphElementLabel [83-90]
+                            Identifier(Account) [83-90]
+                  GraphNodePattern [97-110]
+                    GraphElementPatternFiller [98-109]
+                      Identifier(dst) [98-101]
+                      GraphLabelFilter [101-109]
+                        GraphElementLabel [102-109]
+                          Identifier(Account) [102-109]
+            GqlReturn [111-181]
+              Select [118-181]
+                SelectList [118-181]
+                  SelectColumn [118-131]
+                    PathExpression [118-124]
+                      Identifier(dst) [118-121]
+                      Identifier(id) [122-124]
+                    Alias [125-131]
+                      Identifier(dst) [128-131]
+                  SelectColumn [133-181]
+                    FunctionCall(distinct=true) [133-164]
+                      PathExpression [133-142]
+                        Identifier(ARRAY_AGG) [133-142]
+                      DotIdentifier [152-163]
+                        ArrayElement [152-160]
+                          PathExpression [152-157]
+                            Identifier(nexts) [152-157]
+                          Location [157-158]
+                          IntLiteral(0) [158-159]
+                        Identifier(id) [161-163]
+                    Alias [165-181]
+                      Identifier(unique_starts) [168-181]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-paths#use_case_example
+GRAPH FinGraph
+MATCH p = ACYCLIC (start:Account {id: 20})-[:Transfers]->{1,3}(dst:Account)
+RETURN dst.id AS dst, ARRAY_AGG(DISTINCT NODES(p)[OFFSET(1)].id) AS unique_starts
+--
+QueryStatement [0-172]
+  Query [0-172]
+    GqlQuery [0-172]
+      GraphTableQuery [0-172]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-172]
+          GqlOperatorList [15-172]
+            GqlMatch [15-90]
+              GraphPattern [21-90]
+                GraphPathPattern [21-90]
+                  Identifier(p) [21-22]
+                  GraphPathMode [25-32]
+                  GraphNodePattern [33-57]
+                    GraphElementPatternFiller [34-56]
+                      Identifier(start) [34-39]
+                      GraphLabelFilter [39-47]
+                        GraphElementLabel [40-47]
+                          Identifier(Account) [40-47]
+                      GraphPropertySpecification [48-56]
+                        GraphPropertyNameAndValue [49-55]
+                          Identifier(id) [49-51]
+                          IntLiteral(20) [53-55]
+                  ParenthesizedGraphPathPattern [57-77]
+                    BoundedQuantifier [72-77]
+                      QuantifierBound [73-74]
+                        IntLiteral(1) [73-74]
+                      QuantifierBound [75-76]
+                        IntLiteral(3) [75-76]
+                    GraphEdgePattern [57-72]
+                      GraphElementPatternFiller [59-69]
+                        GraphLabelFilter [59-69]
+                          GraphElementLabel [60-69]
+                            Identifier(Transfers) [60-69]
+                  GraphNodePattern [77-90]
+                    GraphElementPatternFiller [78-89]
+                      Identifier(dst) [78-81]
+                      GraphLabelFilter [81-89]
+                        GraphElementLabel [82-89]
+                          Identifier(Account) [82-89]
+            GqlReturn [91-172]
+              Select [98-172]
+                SelectList [98-172]
+                  SelectColumn [98-111]
+                    PathExpression [98-104]
+                      Identifier(dst) [98-101]
+                      Identifier(id) [102-104]
+                    Alias [105-111]
+                      Identifier(dst) [108-111]
+                  SelectColumn [113-172]
+                    FunctionCall(distinct=true) [113-155]
+                      PathExpression [113-122]
+                        Identifier(ARRAY_AGG) [113-122]
+                      DotIdentifier [132-154]
+                        ArrayElement [132-151]
+                          FunctionCall [132-140]
+                            PathExpression [132-137]
+                              Identifier(NODES) [132-137]
+                            PathExpression [138-139]
+                              Identifier(p) [138-139]
+                          Location [140-141]
+                          FunctionCall [141-150]
+                            PathExpression [141-147]
+                              Identifier(OFFSET) [141-147]
+                            IntLiteral(1) [148-149]
+                        Identifier(id) [152-154]
+                    Alias [156-172]
+                      Identifier(unique_starts) [159-172]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-paths#use_case_example
+GRAPH FinGraph
+MATCH p = ACYCLIC (start:Account {id: 20})-[:Transfers]->
+  {1,3}(mid:Account {id: 7})-[:Transfers]->{1,3}(dst:Account)
+RETURN dst.id AS dst,
+  ARRAY_AGG(DISTINCT NODES(p)[OFFSET(1)].id) AS unique_starts
+--
+QueryStatement [0-218]
+  Query [0-218]
+    GqlQuery [0-218]
+      GraphTableQuery [0-218]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-218]
+          GqlOperatorList [15-218]
+            GqlMatch [15-134]
+              GraphPattern [21-134]
+                GraphPathPattern [21-134]
+                  Identifier(p) [21-22]
+                  GraphPathMode [25-32]
+                  GraphNodePattern [33-57]
+                    GraphElementPatternFiller [34-56]
+                      Identifier(start) [34-39]
+                      GraphLabelFilter [39-47]
+                        GraphElementLabel [40-47]
+                          Identifier(Account) [40-47]
+                      GraphPropertySpecification [48-56]
+                        GraphPropertyNameAndValue [49-55]
+                          Identifier(id) [49-51]
+                          IntLiteral(20) [53-55]
+                  ParenthesizedGraphPathPattern [57-80]
+                    BoundedQuantifier [75-80]
+                      QuantifierBound [76-77]
+                        IntLiteral(1) [76-77]
+                      QuantifierBound [78-79]
+                        IntLiteral(3) [78-79]
+                    GraphEdgePattern [57-72]
+                      GraphElementPatternFiller [59-69]
+                        GraphLabelFilter [59-69]
+                          GraphElementLabel [60-69]
+                            Identifier(Transfers) [60-69]
+                  GraphNodePattern [80-101]
+                    GraphElementPatternFiller [81-100]
+                      Identifier(mid) [81-84]
+                      GraphLabelFilter [84-92]
+                        GraphElementLabel [85-92]
+                          Identifier(Account) [85-92]
+                      GraphPropertySpecification [93-100]
+                        GraphPropertyNameAndValue [94-99]
+                          Identifier(id) [94-96]
+                          IntLiteral(7) [98-99]
+                  ParenthesizedGraphPathPattern [101-121]
+                    BoundedQuantifier [116-121]
+                      QuantifierBound [117-118]
+                        IntLiteral(1) [117-118]
+                      QuantifierBound [119-120]
+                        IntLiteral(3) [119-120]
+                    GraphEdgePattern [101-116]
+                      GraphElementPatternFiller [103-113]
+                        GraphLabelFilter [103-113]
+                          GraphElementLabel [104-113]
+                            Identifier(Transfers) [104-113]
+                  GraphNodePattern [121-134]
+                    GraphElementPatternFiller [122-133]
+                      Identifier(dst) [122-125]
+                      GraphLabelFilter [125-133]
+                        GraphElementLabel [126-133]
+                          Identifier(Account) [126-133]
+            GqlReturn [135-218]
+              Select [142-218]
+                SelectList [142-218]
+                  SelectColumn [142-155]
+                    PathExpression [142-148]
+                      Identifier(dst) [142-145]
+                      Identifier(id) [146-148]
+                    Alias [149-155]
+                      Identifier(dst) [152-155]
+                  SelectColumn [159-218]
+                    FunctionCall(distinct=true) [159-201]
+                      PathExpression [159-168]
+                        Identifier(ARRAY_AGG) [159-168]
+                      DotIdentifier [178-200]
+                        ArrayElement [178-197]
+                          FunctionCall [178-186]
+                            PathExpression [178-183]
+                              Identifier(NODES) [178-183]
+                            PathExpression [184-185]
+                              Identifier(p) [184-185]
+                          Location [186-187]
+                          FunctionCall [187-196]
+                            PathExpression [187-193]
+                              Identifier(OFFSET) [187-193]
+                            IntLiteral(1) [194-195]
+                        Identifier(id) [198-200]
+                    Alias [202-218]
+                      Identifier(unique_starts) [205-218]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-paths#use_case_example
+GRAPH FinGraph
+MATCH p = ACYCLIC (start:Account {id: 20})-[:Transfers]->
+  {1,3}(mid:Account {id: 7})-[:Transfers]->{1,3}(dst:Account)
+LET all_transfers = EDGES(p)
+LET transfer_amounts = SUM(all_transfers.amount)
+RETURN dst.id AS dst,
+  ARRAY_AGG(DISTINCT NODES(p)[OFFSET(1)].id) AS participating_neighbor_nodes, transfer_amounts
+--
+QueryStatement [0-329]
+  Query [0-329]
+    GqlQuery [0-329]
+      GraphTableQuery [0-329]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-329]
+          GqlOperatorList [15-329]
+            GqlMatch [15-134]
+              GraphPattern [21-134]
+                GraphPathPattern [21-134]
+                  Identifier(p) [21-22]
+                  GraphPathMode [25-32]
+                  GraphNodePattern [33-57]
+                    GraphElementPatternFiller [34-56]
+                      Identifier(start) [34-39]
+                      GraphLabelFilter [39-47]
+                        GraphElementLabel [40-47]
+                          Identifier(Account) [40-47]
+                      GraphPropertySpecification [48-56]
+                        GraphPropertyNameAndValue [49-55]
+                          Identifier(id) [49-51]
+                          IntLiteral(20) [53-55]
+                  ParenthesizedGraphPathPattern [57-80]
+                    BoundedQuantifier [75-80]
+                      QuantifierBound [76-77]
+                        IntLiteral(1) [76-77]
+                      QuantifierBound [78-79]
+                        IntLiteral(3) [78-79]
+                    GraphEdgePattern [57-72]
+                      GraphElementPatternFiller [59-69]
+                        GraphLabelFilter [59-69]
+                          GraphElementLabel [60-69]
+                            Identifier(Transfers) [60-69]
+                  GraphNodePattern [80-101]
+                    GraphElementPatternFiller [81-100]
+                      Identifier(mid) [81-84]
+                      GraphLabelFilter [84-92]
+                        GraphElementLabel [85-92]
+                          Identifier(Account) [85-92]
+                      GraphPropertySpecification [93-100]
+                        GraphPropertyNameAndValue [94-99]
+                          Identifier(id) [94-96]
+                          IntLiteral(7) [98-99]
+                  ParenthesizedGraphPathPattern [101-121]
+                    BoundedQuantifier [116-121]
+                      QuantifierBound [117-118]
+                        IntLiteral(1) [117-118]
+                      QuantifierBound [119-120]
+                        IntLiteral(3) [119-120]
+                    GraphEdgePattern [101-116]
+                      GraphElementPatternFiller [103-113]
+                        GraphLabelFilter [103-113]
+                          GraphElementLabel [104-113]
+                            Identifier(Transfers) [104-113]
+                  GraphNodePattern [121-134]
+                    GraphElementPatternFiller [122-133]
+                      Identifier(dst) [122-125]
+                      GraphLabelFilter [125-133]
+                        GraphElementLabel [126-133]
+                          Identifier(Account) [126-133]
+            GqlLet [135-163]
+              GqlLetVariableDefinitionList [139-163]
+                GqlLetVariableDefinition [139-163]
+                  Identifier(all_transfers) [139-152]
+                  FunctionCall [155-163]
+                    PathExpression [155-160]
+                      Identifier(EDGES) [155-160]
+                    PathExpression [161-162]
+                      Identifier(p) [161-162]
+            GqlLet [164-212]
+              GqlLetVariableDefinitionList [168-212]
+                GqlLetVariableDefinition [168-212]
+                  Identifier(transfer_amounts) [168-184]
+                  FunctionCall [187-212]
+                    PathExpression [187-190]
+                      Identifier(SUM) [187-190]
+                    PathExpression [191-211]
+                      Identifier(all_transfers) [191-204]
+                      Identifier(amount) [205-211]
+            GqlReturn [213-329]
+              Select [220-329]
+                SelectList [220-329]
+                  SelectColumn [220-233]
+                    PathExpression [220-226]
+                      Identifier(dst) [220-223]
+                      Identifier(id) [224-226]
+                    Alias [227-233]
+                      Identifier(dst) [230-233]
+                  SelectColumn [237-311]
+                    FunctionCall(distinct=true) [237-279]
+                      PathExpression [237-246]
+                        Identifier(ARRAY_AGG) [237-246]
+                      DotIdentifier [256-278]
+                        ArrayElement [256-275]
+                          FunctionCall [256-264]
+                            PathExpression [256-261]
+                              Identifier(NODES) [256-261]
+                            PathExpression [262-263]
+                              Identifier(p) [262-263]
+                          Location [264-265]
+                          FunctionCall [265-274]
+                            PathExpression [265-271]
+                              Identifier(OFFSET) [265-271]
+                            IntLiteral(1) [272-273]
+                        Identifier(id) [276-278]
+                    Alias [280-311]
+                      Identifier(participating_neighbor_nodes) [283-311]
+                  SelectColumn [313-329]
+                    PathExpression [313-329]
+                      Identifier(transfer_amounts) [313-329]
diff --git a/parser/testdata/spanner/guides/graph.work-with-visualizations.test b/parser/testdata/spanner/guides/graph.work-with-visualizations.test
new file mode 100644
index 0000000..8cbce59
--- /dev/null
+++ b/parser/testdata/spanner/guides/graph.work-with-visualizations.test
@@ -0,0 +1,161 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/graph/work-with-visualizations
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-visualizations#visualization-results
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+GRAPH FinGraph
+MATCH result_paths = (account:Account {is_blocked: True})-[:Transfers]->(dest_account:Account)
+RETURN SAFE_TO_JSON(result_paths) AS result_paths
+--
+QueryStatement [0-159]
+  Query [0-159]
+    GqlQuery [0-159]
+      GraphTableQuery [0-159]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-159]
+          GqlOperatorList [15-159]
+            GqlMatch [15-109]
+              GraphPattern [21-109]
+                GraphPathPattern [21-109]
+                  Identifier(result_paths) [21-33]
+                  GraphNodePattern [36-72]
+                    GraphElementPatternFiller [37-71]
+                      Identifier(account) [37-44]
+                      GraphLabelFilter [44-52]
+                        GraphElementLabel [45-52]
+                          Identifier(Account) [45-52]
+                      GraphPropertySpecification [53-71]
+                        GraphPropertyNameAndValue [54-70]
+                          Identifier(is_blocked) [54-64]
+                          BooleanLiteral(True) [66-70]
+                  GraphEdgePattern [72-87]
+                    GraphElementPatternFiller [74-84]
+                      GraphLabelFilter [74-84]
+                        GraphElementLabel [75-84]
+                          Identifier(Transfers) [75-84]
+                  GraphNodePattern [87-109]
+                    GraphElementPatternFiller [88-108]
+                      Identifier(dest_account) [88-100]
+                      GraphLabelFilter [100-108]
+                        GraphElementLabel [101-108]
+                          Identifier(Account) [101-108]
+            GqlReturn [110-159]
+              Select [117-159]
+                SelectList [117-159]
+                  SelectColumn [117-159]
+                    FunctionCall [117-143]
+                      PathExpression [117-129]
+                        Identifier(SAFE_TO_JSON) [117-129]
+                      PathExpression [130-142]
+                        Identifier(result_paths) [130-142]
+                    Alias [144-159]
+                      Identifier(result_paths) [147-159]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-visualizations#visualization-not-showing
+GRAPH FinGraph
+MATCH (person:Person {name: "Dana"})-[owns:Owns]->(account:Account)
+RETURN person.id as person_id, account.id as account_id
+--
+QueryStatement [0-138]
+  Query [0-138]
+    GqlQuery [0-138]
+      GraphTableQuery [0-138]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-138]
+          GqlOperatorList [15-138]
+            GqlMatch [15-82]
+              GraphPattern [21-82]
+                GraphPathPattern [21-82]
+                  GraphNodePattern [21-51]
+                    GraphElementPatternFiller [22-50]
+                      Identifier(person) [22-28]
+                      GraphLabelFilter [28-35]
+                        GraphElementLabel [29-35]
+                          Identifier(Person) [29-35]
+                      GraphPropertySpecification [36-50]
+                        GraphPropertyNameAndValue [37-49]
+                          Identifier(name) [37-41]
+                          StringLiteral [43-49]
+                            StringLiteralComponent("Dana") [43-49]
+                  GraphEdgePattern [51-65]
+                    GraphElementPatternFiller [53-62]
+                      Identifier(owns) [53-57]
+                      GraphLabelFilter [57-62]
+                        GraphElementLabel [58-62]
+                          Identifier(Owns) [58-62]
+                  GraphNodePattern [65-82]
+                    GraphElementPatternFiller [66-81]
+                      Identifier(account) [66-73]
+                      GraphLabelFilter [73-81]
+                        GraphElementLabel [74-81]
+                          Identifier(Account) [74-81]
+            GqlReturn [83-138]
+              Select [90-138]
+                SelectList [90-138]
+                  SelectColumn [90-112]
+                    PathExpression [90-99]
+                      Identifier(person) [90-96]
+                      Identifier(id) [97-99]
+                    Alias [100-112]
+                      Identifier(person_id) [103-112]
+                  SelectColumn [114-138]
+                    PathExpression [114-124]
+                      Identifier(account) [114-121]
+                      Identifier(id) [122-124]
+                    Alias [125-138]
+                      Identifier(account_id) [128-138]
+==
+# https://docs.cloud.google.com/spanner/docs/graph/work-with-visualizations#visualization-not-showing
+GRAPH FinGraph
+MATCH (person:Person {name: "Dana"})-[owns:Owns]->(account:Account)
+RETURN owns.create_time, account.nick_name
+--
+QueryStatement [0-125]
+  Query [0-125]
+    GqlQuery [0-125]
+      GraphTableQuery [0-125]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-125]
+          GqlOperatorList [15-125]
+            GqlMatch [15-82]
+              GraphPattern [21-82]
+                GraphPathPattern [21-82]
+                  GraphNodePattern [21-51]
+                    GraphElementPatternFiller [22-50]
+                      Identifier(person) [22-28]
+                      GraphLabelFilter [28-35]
+                        GraphElementLabel [29-35]
+                          Identifier(Person) [29-35]
+                      GraphPropertySpecification [36-50]
+                        GraphPropertyNameAndValue [37-49]
+                          Identifier(name) [37-41]
+                          StringLiteral [43-49]
+                            StringLiteralComponent("Dana") [43-49]
+                  GraphEdgePattern [51-65]
+                    GraphElementPatternFiller [53-62]
+                      Identifier(owns) [53-57]
+                      GraphLabelFilter [57-62]
+                        GraphElementLabel [58-62]
+                          Identifier(Owns) [58-62]
+                  GraphNodePattern [65-82]
+                    GraphElementPatternFiller [66-81]
+                      Identifier(account) [66-73]
+                      GraphLabelFilter [73-81]
+                        GraphElementLabel [74-81]
+                          Identifier(Account) [74-81]
+            GqlReturn [83-125]
+              Select [90-125]
+                SelectList [90-125]
+                  SelectColumn [90-106]
+                    PathExpression [90-106]
+                      Identifier(owns) [90-94]
+                      Identifier(create_time) [95-106]
+                  SelectColumn [108-125]
+                    PathExpression [108-125]
+                      Identifier(account) [108-115]
+                      Identifier(nick_name) [116-125]
diff --git a/parser/testdata/spanner/guides/hybrid-full-text-vector-search.test b/parser/testdata/spanner/guides/hybrid-full-text-vector-search.test
new file mode 100644
index 0000000..994cb9b
--- /dev/null
+++ b/parser/testdata/spanner/guides/hybrid-full-text-vector-search.test
@@ -0,0 +1,663 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/hybrid-full-text-vector-search
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/hybrid-full-text-vector-search#implement-rrf-spanner
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT SUM(1 / (60 + rank)) AS rrf_score, key
+FROM (
+  (
+    SELECT rank, x AS key
+    FROM UNNEST(ARRAY(
+      SELECT key
+      FROM hybrid_search
+      WHERE embedding IS NOT NULL
+      ORDER BY APPROX_DOT_PRODUCT(@vector, embedding,
+        OPTIONS => JSON '{"num_leaves_to_search": 50}') DESC
+      LIMIT 100)) AS x WITH OFFSET AS rank
+  )
+  UNION ALL
+  (
+    SELECT rank, x AS key
+    FROM UNNEST(ARRAY(
+      SELECT key
+      FROM hybrid_search
+      WHERE SEARCH_NGRAMS(text_tokens_ngrams, 'Green')
+      ORDER BY SCORE_NGRAMS(text_tokens_ngrams, 'Green') DESC
+      LIMIT 100)) AS x WITH OFFSET AS rank
+  )
+)
+GROUP BY key
+ORDER BY rrf_score DESC
+LIMIT 5
+--
+QueryStatement [0-661]
+  Query [0-661]
+    Select [0-629]
+      SelectList [7-45]
+        SelectColumn [7-40]
+          FunctionCall [7-27]
+            PathExpression [7-10]
+              Identifier(SUM) [7-10]
+            BinaryExpression(/) [11-26]
+              IntLiteral(1) [11-12]
+              BinaryExpression(+) [16-25]
+                IntLiteral(60) [16-18]
+                PathExpression [21-25]
+                  Identifier(rank) [21-25]
+          Alias [28-40]
+            Identifier(rrf_score) [31-40]
+        SelectColumn [42-45]
+          PathExpression [42-45]
+            Identifier(key) [42-45]
+      FromClause [46-616]
+        TableSubquery [51-616]
+          Query [55-614]
+            SetOperation(UNION ALL) [55-614]
+              SetOperationMetadataList [346-355]
+                SetOperationMetadata [346-355]
+                  SetOperationType [346-351]
+                  SetOperationAllOrDistinct [352-355]
+              Query [61-339]
+                Select [61-339]
+                  SelectList [68-82]
+                    SelectColumn [68-72]
+                      PathExpression [68-72]
+                        Identifier(rank) [68-72]
+                    SelectColumn [74-82]
+                      PathExpression [74-75]
+                        Identifier(x) [74-75]
+                      Alias [76-82]
+                        Identifier(key) [79-82]
+                  FromClause [87-339]
+                    TablePathExpression [92-339]
+                      UnnestExpression [92-314]
+                        ExpressionWithOptAlias [99-313]
+                          ExpressionSubquery(modifier=ARRAY) [99-313]
+                            Query [112-312]
+                              Select [112-181]
+                                SelectList [119-122]
+                                  SelectColumn [119-122]
+                                    PathExpression [119-122]
+                                      Identifier(key) [119-122]
+                                FromClause [129-147]
+                                  TablePathExpression [134-147]
+                                    PathExpression [134-147]
+                                      Identifier(hybrid_search) [134-147]
+                                WhereClause [154-181]
+                                  BinaryExpression(IS NOT) [160-181]
+                                    PathExpression [160-169]
+                                      Identifier(embedding) [160-169]
+                                    NullLiteral(NULL) [177-181]
+                              OrderBy [188-296]
+                                OrderingExpression(DESC) [197-296]
+                                  FunctionCall [197-291]
+                                    PathExpression [197-215]
+                                      Identifier(APPROX_DOT_PRODUCT) [197-215]
+                                    ParameterExpr [216-223]
+                                      Identifier(vector) [217-223]
+                                    PathExpression [225-234]
+                                      Identifier(embedding) [225-234]
+                                    NamedArgument [244-290]
+                                      Identifier(OPTIONS) [244-251]
+                                      JSONLiteral [255-290]
+                                        StringLiteral [260-290]
+                                          StringLiteralComponent('{"num_leaves_to_search": 50}') [260-290]
+                              LimitOffset [303-312]
+                                Limit [303-312]
+                                  IntLiteral(100) [309-312]
+                      Alias [315-319]
+                        Identifier(x) [318-319]
+                      WithOffset [320-339]
+                        Alias [332-339]
+                          Identifier(rank) [335-339]
+              Query [364-610]
+                Select [364-610]
+                  SelectList [371-385]
+                    SelectColumn [371-375]
+                      PathExpression [371-375]
+                        Identifier(rank) [371-375]
+                    SelectColumn [377-385]
+                      PathExpression [377-378]
+                        Identifier(x) [377-378]
+                      Alias [379-385]
+                        Identifier(key) [382-385]
+                  FromClause [390-610]
+                    TablePathExpression [395-610]
+                      UnnestExpression [395-585]
+                        ExpressionWithOptAlias [402-584]
+                          ExpressionSubquery(modifier=ARRAY) [402-584]
+                            Query [415-583]
+                              Select [415-505]
+                                SelectList [422-425]
+                                  SelectColumn [422-425]
+                                    PathExpression [422-425]
+                                      Identifier(key) [422-425]
+                                FromClause [432-450]
+                                  TablePathExpression [437-450]
+                                    PathExpression [437-450]
+                                      Identifier(hybrid_search) [437-450]
+                                WhereClause [457-505]
+                                  FunctionCall [463-505]
+                                    PathExpression [463-476]
+                                      Identifier(SEARCH_NGRAMS) [463-476]
+                                    PathExpression [477-495]
+                                      Identifier(text_tokens_ngrams) [477-495]
+                                    StringLiteral [497-504]
+                                      StringLiteralComponent('Green') [497-504]
+                              OrderBy [512-567]
+                                OrderingExpression(DESC) [521-567]
+                                  FunctionCall [521-562]
+                                    PathExpression [521-533]
+                                      Identifier(SCORE_NGRAMS) [521-533]
+                                    PathExpression [534-552]
+                                      Identifier(text_tokens_ngrams) [534-552]
+                                    StringLiteral [554-561]
+                                      StringLiteralComponent('Green') [554-561]
+                              LimitOffset [574-583]
+                                Limit [574-583]
+                                  IntLiteral(100) [580-583]
+                      Alias [586-590]
+                        Identifier(x) [589-590]
+                      WithOffset [591-610]
+                        Alias [603-610]
+                          Identifier(rank) [606-610]
+      GroupBy [617-629]
+        GroupingItem [626-629]
+          PathExpression [626-629]
+            Identifier(key) [626-629]
+    OrderBy [630-653]
+      OrderingExpression(DESC) [639-653]
+        PathExpression [639-648]
+          Identifier(rrf_score) [639-648]
+    LimitOffset [654-661]
+      Limit [654-661]
+        IntLiteral(5) [660-661]
+==
+# https://docs.cloud.google.com/spanner/docs/hybrid-full-text-vector-search#implement-rsf-spanner
+WITH ann AS (
+  SELECT key, APPROX_COSINE_DISTANCE(@vector, embedding,
+    OPTIONS => JSON '{"num_leaves_to_search": 50}') AS cosine_distance,
+  FROM hybrid_search
+  WHERE embedding IS NOT NULL
+  ORDER BY cosine_distance
+  LIMIT 100
+),
+fts AS (
+  SELECT key, SCORE_NGRAMS(text_tokens_ngrams, 'Green') AS score,
+  FROM hybrid_search
+    WHERE SEARCH_NGRAMS(text_tokens_ngrams, 'Green')
+    ORDER BY score DESC
+    LIMIT 100
+),
+ann_min AS (
+  SELECT MIN(1 - cosine_distance) AS min
+  FROM ann
+),
+ann_max AS (
+  SELECT MAX(1 - cosine_distance) AS max
+  FROM ann
+),
+fts_min AS (
+  SELECT MIN(score) AS min
+  FROM fts
+),
+fts_max AS (
+  SELECT MAX(score) AS max
+  FROM fts
+)
+SELECT IFNULL(ann.key, fts.key),
+       IFNULL(((1 - ann.cosine_distance) - ann_min.min) /
+               (ann_max.max - ann_min.min), 0) +
+       IFNULL((fts.score - fts_min.min) /
+               (fts_max.max - fts_min.min), 0) AS score
+FROM ann
+FULL OUTER JOIN fts
+ON ann.key = fts.key
+CROSS JOIN ann_min
+CROSS JOIN ann_max
+CROSS JOIN fts_min
+CROSS JOIN fts_max
+ORDER BY score DESC
+LIMIT 5
+--
+QueryStatement [0-1060]
+  Query [0-1060]
+    WithClause [0-668]
+      WithClauseEntry [5-234]
+        AliasedQuery [5-234]
+          Identifier(ann) [5-8]
+          Query [12-234]
+            Select [16-193]
+              SelectList [23-142]
+                SelectColumn [23-26]
+                  PathExpression [23-26]
+                    Identifier(key) [23-26]
+                SelectColumn [28-141]
+                  FunctionCall [28-122]
+                    PathExpression [28-50]
+                      Identifier(APPROX_COSINE_DISTANCE) [28-50]
+                    ParameterExpr [51-58]
+                      Identifier(vector) [52-58]
+                    PathExpression [60-69]
+                      Identifier(embedding) [60-69]
+                    NamedArgument [75-121]
+                      Identifier(OPTIONS) [75-82]
+                      JSONLiteral [86-121]
+                        StringLiteral [91-121]
+                          StringLiteralComponent('{"num_leaves_to_search": 50}') [91-121]
+                  Alias [123-141]
+                    Identifier(cosine_distance) [126-141]
+              FromClause [145-163]
+                TablePathExpression [150-163]
+                  PathExpression [150-163]
+                    Identifier(hybrid_search) [150-163]
+              WhereClause [166-193]
+                BinaryExpression(IS NOT) [172-193]
+                  PathExpression [172-181]
+                    Identifier(embedding) [172-181]
+                  NullLiteral(NULL) [189-193]
+            OrderBy [196-220]
+              OrderingExpression(ASC) [205-220]
+                PathExpression [205-220]
+                  Identifier(cosine_distance) [205-220]
+            LimitOffset [223-232]
+              Limit [223-232]
+                IntLiteral(100) [229-232]
+      WithClauseEntry [236-424]
+        AliasedQuery [236-424]
+          Identifier(fts) [236-239]
+          Query [243-424]
+            Select [247-384]
+              SelectList [254-310]
+                SelectColumn [254-257]
+                  PathExpression [254-257]
+                    Identifier(key) [254-257]
+                SelectColumn [259-309]
+                  FunctionCall [259-300]
+                    PathExpression [259-271]
+                      Identifier(SCORE_NGRAMS) [259-271]
+                    PathExpression [272-290]
+                      Identifier(text_tokens_ngrams) [272-290]
+                    StringLiteral [292-299]
+                      StringLiteralComponent('Green') [292-299]
+                  Alias [301-309]
+                    Identifier(score) [304-309]
+              FromClause [313-331]
+                TablePathExpression [318-331]
+                  PathExpression [318-331]
+                    Identifier(hybrid_search) [318-331]
+              WhereClause [336-384]
+                FunctionCall [342-384]
+                  PathExpression [342-355]
+                    Identifier(SEARCH_NGRAMS) [342-355]
+                  PathExpression [356-374]
+                    Identifier(text_tokens_ngrams) [356-374]
+                  StringLiteral [376-383]
+                    StringLiteralComponent('Green') [376-383]
+            OrderBy [389-408]
+              OrderingExpression(DESC) [398-408]
+                PathExpression [398-403]
+                  Identifier(score) [398-403]
+            LimitOffset [413-422]
+              Limit [413-422]
+                IntLiteral(100) [419-422]
+      WithClauseEntry [426-492]
+        AliasedQuery [426-492]
+          Identifier(ann_min) [426-433]
+          Query [437-492]
+            Select [441-490]
+              SelectList [448-479]
+                SelectColumn [448-479]
+                  FunctionCall [448-472]
+                    PathExpression [448-451]
+                      Identifier(MIN) [448-451]
+                    BinaryExpression(-) [452-471]
+                      IntLiteral(1) [452-453]
+                      PathExpression [456-471]
+                        Identifier(cosine_distance) [456-471]
+                  Alias [473-479]
+                    Identifier(min) [476-479]
+              FromClause [482-490]
+                TablePathExpression [487-490]
+                  PathExpression [487-490]
+                    Identifier(ann) [487-490]
+      WithClauseEntry [494-560]
+        AliasedQuery [494-560]
+          Identifier(ann_max) [494-501]
+          Query [505-560]
+            Select [509-558]
+              SelectList [516-547]
+                SelectColumn [516-547]
+                  FunctionCall [516-540]
+                    PathExpression [516-519]
+                      Identifier(MAX) [516-519]
+                    BinaryExpression(-) [520-539]
+                      IntLiteral(1) [520-521]
+                      PathExpression [524-539]
+                        Identifier(cosine_distance) [524-539]
+                  Alias [541-547]
+                    Identifier(max) [544-547]
+              FromClause [550-558]
+                TablePathExpression [555-558]
+                  PathExpression [555-558]
+                    Identifier(ann) [555-558]
+      WithClauseEntry [562-614]
+        AliasedQuery [562-614]
+          Identifier(fts_min) [562-569]
+          Query [573-614]
+            Select [577-612]
+              SelectList [584-601]
+                SelectColumn [584-601]
+                  FunctionCall [584-594]
+                    PathExpression [584-587]
+                      Identifier(MIN) [584-587]
+                    PathExpression [588-593]
+                      Identifier(score) [588-593]
+                  Alias [595-601]
+                    Identifier(min) [598-601]
+              FromClause [604-612]
+                TablePathExpression [609-612]
+                  PathExpression [609-612]
+                    Identifier(fts) [609-612]
+      WithClauseEntry [616-668]
+        AliasedQuery [616-668]
+          Identifier(fts_max) [616-623]
+          Query [627-668]
+            Select [631-666]
+              SelectList [638-655]
+                SelectColumn [638-655]
+                  FunctionCall [638-648]
+                    PathExpression [638-641]
+                      Identifier(MAX) [638-641]
+                    PathExpression [642-647]
+                      Identifier(score) [642-647]
+                  Alias [649-655]
+                    Identifier(max) [652-655]
+              FromClause [658-666]
+                TablePathExpression [663-666]
+                  PathExpression [663-666]
+                    Identifier(fts) [663-666]
+    Select [669-1032]
+      SelectList [676-906]
+        SelectColumn [676-700]
+          FunctionCall [676-700]
+            PathExpression [676-682]
+              Identifier(IFNULL) [676-682]
+            PathExpression [683-690]
+              Identifier(ann) [683-686]
+              Identifier(key) [687-690]
+            PathExpression [692-699]
+              Identifier(fts) [692-695]
+              Identifier(key) [696-699]
+        SelectColumn [709-906]
+          BinaryExpression(+) [709-897]
+            FunctionCall [709-806]
+              PathExpression [709-715]
+                Identifier(IFNULL) [709-715]
+              BinaryExpression(/) [716-802]
+                BinaryExpression(-) [717-756]
+                  BinaryExpression(-) [718-741]
+                    IntLiteral(1) [718-719]
+                    PathExpression [722-741]
+                      Identifier(ann) [722-725]
+                      Identifier(cosine_distance) [726-741]
+                  PathExpression [745-756]
+                    Identifier(ann_min) [745-752]
+                    Identifier(min) [753-756]
+                BinaryExpression(-) [776-801]
+                  PathExpression [776-787]
+                    Identifier(ann_max) [776-783]
+                    Identifier(max) [784-787]
+                  PathExpression [790-801]
+                    Identifier(ann_min) [790-797]
+                    Identifier(min) [798-801]
+              IntLiteral(0) [804-805]
+            FunctionCall [816-897]
+              PathExpression [816-822]
+                Identifier(IFNULL) [816-822]
+              BinaryExpression(/) [823-893]
+                BinaryExpression(-) [824-847]
+                  PathExpression [824-833]
+                    Identifier(fts) [824-827]
+                    Identifier(score) [828-833]
+                  PathExpression [836-847]
+                    Identifier(fts_min) [836-843]
+                    Identifier(min) [844-847]
+                BinaryExpression(-) [867-892]
+                  PathExpression [867-878]
+                    Identifier(fts_max) [867-874]
+                    Identifier(max) [875-878]
+                  PathExpression [881-892]
+                    Identifier(fts_min) [881-888]
+                    Identifier(min) [889-892]
+              IntLiteral(0) [895-896]
+          Alias [898-906]
+            Identifier(score) [901-906]
+      FromClause [907-1032]
+        Join(CROSS) [912-1032]
+          Join(CROSS) [912-1013]
+            Join(CROSS) [912-994]
+              Join(CROSS) [912-975]
+                Join(FULL) [912-956]
+                  TablePathExpression [912-915]
+                    PathExpression [912-915]
+                      Identifier(ann) [912-915]
+                  Location [916-931]
+                  TablePathExpression [932-935]
+                    PathExpression [932-935]
+                      Identifier(fts) [932-935]
+                  OnClause [936-956]
+                    BinaryExpression(=) [939-956]
+                      PathExpression [939-946]
+                        Identifier(ann) [939-942]
+                        Identifier(key) [943-946]
+                      PathExpression [949-956]
+                        Identifier(fts) [949-952]
+                        Identifier(key) [953-956]
+                Location [957-967]
+                TablePathExpression [968-975]
+                  PathExpression [968-975]
+                    Identifier(ann_min) [968-975]
+              Location [976-986]
+              TablePathExpression [987-994]
+                PathExpression [987-994]
+                  Identifier(ann_max) [987-994]
+            Location [995-1005]
+            TablePathExpression [1006-1013]
+              PathExpression [1006-1013]
+                Identifier(fts_min) [1006-1013]
+          Location [1014-1024]
+          TablePathExpression [1025-1032]
+            PathExpression [1025-1032]
+              Identifier(fts_max) [1025-1032]
+    OrderBy [1033-1052]
+      OrderingExpression(DESC) [1042-1052]
+        PathExpression [1042-1047]
+          Identifier(score) [1042-1047]
+    LimitOffset [1053-1060]
+      Limit [1053-1060]
+        IntLiteral(5) [1059-1060]
+==
+# https://docs.cloud.google.com/spanner/docs/hybrid-full-text-vector-search#implement-filtered-search
+SELECT key
+FROM (
+  SELECT key, embedding
+  FROM hybrid_search
+  WHERE SEARCH (text_tokens, 'Green')
+  ORDER BY presort
+  LIMIT 1000)
+ORDER BY DOT_PRODUCT(@vector, embedding) DESC
+LIMIT 10
+--
+QueryStatement [0-188]
+  Query [0-188]
+    Select [0-133]
+      SelectList [7-10]
+        SelectColumn [7-10]
+          PathExpression [7-10]
+            Identifier(key) [7-10]
+      FromClause [11-133]
+        TableSubquery [16-133]
+          Query [20-132]
+            Select [20-100]
+              SelectList [27-41]
+                SelectColumn [27-30]
+                  PathExpression [27-30]
+                    Identifier(key) [27-30]
+                SelectColumn [32-41]
+                  PathExpression [32-41]
+                    Identifier(embedding) [32-41]
+              FromClause [44-62]
+                TablePathExpression [49-62]
+                  PathExpression [49-62]
+                    Identifier(hybrid_search) [49-62]
+              WhereClause [65-100]
+                FunctionCall [71-100]
+                  PathExpression [71-77]
+                    Identifier(SEARCH) [71-77]
+                  PathExpression [79-90]
+                    Identifier(text_tokens) [79-90]
+                  StringLiteral [92-99]
+                    StringLiteralComponent('Green') [92-99]
+            OrderBy [103-119]
+              OrderingExpression(ASC) [112-119]
+                PathExpression [112-119]
+                  Identifier(presort) [112-119]
+            LimitOffset [122-132]
+              Limit [122-132]
+                IntLiteral(1000) [128-132]
+    OrderBy [134-179]
+      OrderingExpression(DESC) [143-179]
+        FunctionCall [143-174]
+          PathExpression [143-154]
+            Identifier(DOT_PRODUCT) [143-154]
+          ParameterExpr [155-162]
+            Identifier(vector) [156-162]
+          PathExpression [164-173]
+            Identifier(embedding) [164-173]
+    LimitOffset [180-188]
+      Limit [180-188]
+        IntLiteral(10) [186-188]
+==
+# https://docs.cloud.google.com/spanner/docs/hybrid-full-text-vector-search#implement-ml-reranking
+CREATE MODEL Reranker
+INPUT (text ARRAY, text_pair ARRAY)
+OUTPUT (score FLOAT32)
+REMOTE
+OPTIONS (
+endpoint = '...'
+)
+--
+CreateModelStatement [0-142]
+  PathExpression [13-21]
+    Identifier(Reranker) [13-21]
+  InputOutputClause [22-106]
+    TableElementList [28-83]
+      ColumnDefinition [29-52]
+        Identifier(text) [29-33]
+        ArrayColumnSchema [34-52]
+          SimpleColumnSchema [40-51]
+            PathExpression [40-46]
+              Identifier(string) [40-46]
+            TypeParameterList [46-50]
+              MaxLiteral() [47-50]
+      ColumnDefinition [54-82]
+        Identifier(text_pair) [54-63]
+        ArrayColumnSchema [64-82]
+          SimpleColumnSchema [70-81]
+            PathExpression [70-76]
+              Identifier(string) [70-76]
+            TypeParameterList [76-80]
+              MaxLiteral() [77-80]
+    TableElementList [91-106]
+      ColumnDefinition [92-105]
+        Identifier(score) [92-97]
+        SimpleColumnSchema [98-105]
+          PathExpression [98-105]
+            Identifier(FLOAT32) [98-105]
+  OptionsList [122-142]
+    OptionsEntry [124-140]
+      Identifier(endpoint) [124-132]
+      StringLiteral [135-140]
+        StringLiteralComponent('...') [135-140]
+==
+# https://docs.cloud.google.com/spanner/docs/hybrid-full-text-vector-search#implement-ml-reranking
+SELECT key
+FROM ML.PREDICT(
+    MODEL Reranker, (
+      SELECT key, text, "gift for 8-year-old" AS text_pair
+      FROM hybrid_search
+      WHERE embedding IS NOT NULL
+      ORDER BY APPROX_DOT_PRODUCT(@vector, embedding, options=>JSON '{"num_leaves_to_search": 50}') DESC
+      LIMIT 100)
+)
+ORDER BY score DESC
+LIMIT 3
+--
+QueryStatement [0-319]
+  Query [0-319]
+    Select [0-291]
+      SelectList [7-10]
+        SelectColumn [7-10]
+          PathExpression [7-10]
+            Identifier(key) [7-10]
+      FromClause [11-291]
+        TVF [16-291]
+          PathExpression [16-26]
+            Identifier(ML) [16-18]
+            Identifier(PREDICT) [19-26]
+          TVFArgument [32-46]
+            ModelClause [32-46]
+              PathExpression [38-46]
+                Identifier(Reranker) [38-46]
+          TVFArgument [48-289]
+            ExpressionSubquery [48-289]
+              Query [56-288]
+                Select [56-167]
+                  SelectList [63-108]
+                    SelectColumn [63-66]
+                      PathExpression [63-66]
+                        Identifier(key) [63-66]
+                    SelectColumn [68-72]
+                      PathExpression [68-72]
+                        Identifier(text) [68-72]
+                    SelectColumn [74-108]
+                      StringLiteral [74-95]
+                        StringLiteralComponent("gift for 8-year-old") [74-95]
+                      Alias [96-108]
+                        Identifier(text_pair) [99-108]
+                  FromClause [115-133]
+                    TablePathExpression [120-133]
+                      PathExpression [120-133]
+                        Identifier(hybrid_search) [120-133]
+                  WhereClause [140-167]
+                    BinaryExpression(IS NOT) [146-167]
+                      PathExpression [146-155]
+                        Identifier(embedding) [146-155]
+                      NullLiteral(NULL) [163-167]
+                OrderBy [174-272]
+                  OrderingExpression(DESC) [183-272]
+                    FunctionCall [183-267]
+                      PathExpression [183-201]
+                        Identifier(APPROX_DOT_PRODUCT) [183-201]
+                      ParameterExpr [202-209]
+                        Identifier(vector) [203-209]
+                      PathExpression [211-220]
+                        Identifier(embedding) [211-220]
+                      NamedArgument [222-266]
+                        Identifier(options) [222-229]
+                        JSONLiteral [231-266]
+                          StringLiteral [236-266]
+                            StringLiteralComponent('{"num_leaves_to_search": 50}') [236-266]
+                LimitOffset [279-288]
+                  Limit [279-288]
+                    IntLiteral(100) [285-288]
+    OrderBy [292-311]
+      OrderingExpression(DESC) [301-311]
+        PathExpression [301-306]
+          Identifier(score) [301-306]
+    LimitOffset [312-319]
+      Limit [312-319]
+        IntLiteral(3) [318-319]
diff --git a/parser/testdata/spanner/guides/implement-multi-tenancy.test b/parser/testdata/spanner/guides/implement-multi-tenancy.test
new file mode 100644
index 0000000..30b7110
--- /dev/null
+++ b/parser/testdata/spanner/guides/implement-multi-tenancy.test
@@ -0,0 +1,41 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/implement-multi-tenancy
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/implement-multi-tenancy#query_generation_and_attribution
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT * FROM EMPLOYEE
+  -- TENANT 356
+  WHERE TENANT = 'T356'
+--
+QueryStatement [0-62]
+  Query [0-62]
+    Select [0-62]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-22]
+        TablePathExpression [14-22]
+          PathExpression [14-22]
+            Identifier(EMPLOYEE) [14-22]
+      WhereClause [41-62]
+        BinaryExpression(=) [47-62]
+          PathExpression [47-53]
+            Identifier(TENANT) [47-53]
+          StringLiteral [56-62]
+            StringLiteralComponent('T356') [56-62]
+==
+# https://docs.cloud.google.com/spanner/docs/implement-multi-tenancy#query_generation_and_attribution
+SELECT * FROM T356_EMPLOYEE
+--
+QueryStatement [0-27]
+  Query [0-27]
+    Select [0-27]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-27]
+        TablePathExpression [14-27]
+          PathExpression [14-27]
+            Identifier(T356_EMPLOYEE) [14-27]
diff --git a/parser/testdata/spanner/guides/index-geo-partitioning.spanner.sql b/parser/testdata/spanner/guides/index-geo-partitioning.spanner.sql
new file mode 100644
index 0000000..f05f6ec
--- /dev/null
+++ b/parser/testdata/spanner/guides/index-geo-partitioning.spanner.sql
@@ -0,0 +1,46 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/index-geo-partitioning
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/index-geo-partitioning#local-index
+# id 2f15738e5e85: data placement / geo-partitioning (index-geo-partitioning)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ")" or "," but got identifier "PLACEMENT" [at 3:33]
+-- Create locations placement table
+CREATE TABLE locations (
+  location STRING(MAX) NOT NULL PLACEMENT KEY,
+) PRIMARY KEY(location);
+
+# https://docs.cloud.google.com/spanner/docs/index-geo-partitioning#local-index
+# id 17b21dc96ab1: data placement / geo-partitioning (index-geo-partitioning)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ")" or "," but got identifier "PLACEMENT" [at 6:33]
+-- Create entity based customer placement table
+CREATE TABLE customer (
+  customerId INT64 NOT NULL,
+  email STRING(MAX),
+  webcookie STRING(64),
+  location STRING(MAX) NOT NULL PLACEMENT KEY
+) PRIMARY KEY(customerId);
+
+# https://docs.cloud.google.com/spanner/docs/index-geo-partitioning#remote-index
+# id cc13be61ce70: data placement / geo-partitioning (index-geo-partitioning)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ")" or "," but got identifier "PLACEMENT" [at 6:33]
+-- Create a customer table with a primary key that is not the location
+CREATE TABLE customer (
+  customerId INT64 NOT NULL ,
+  email STRING(MAX),
+  webcookie STRING(64),
+  location STRING(MAX) NOT NULL PLACEMENT KEY,
+) PRIMARY KEY(customerId);
+
+# https://docs.cloud.google.com/spanner/docs/index-geo-partitioning#unique-index-partial-primary-key
+# id cd34e8a576bb: data placement / geo-partitioning (index-geo-partitioning)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ")" or "," but got identifier "PLACEMENT" [at 3:31]
+-- Create locations placement table
+CREATE TABLE locations (
+location STRING(MAX) NOT NULL PLACEMENT KEY,
+) PRIMARY KEY(location);
diff --git a/parser/testdata/spanner/guides/index-geo-partitioning.test b/parser/testdata/spanner/guides/index-geo-partitioning.test
new file mode 100644
index 0000000..46f0902
--- /dev/null
+++ b/parser/testdata/spanner/guides/index-geo-partitioning.test
@@ -0,0 +1,374 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/index-geo-partitioning
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/index-geo-partitioning#global-index
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE UNIQUE INDEX idx_customer_email ON customer(email)
+--
+CreateIndexStatement(UNIQUE) [0-57]
+  PathExpression [20-38]
+    Identifier(idx_customer_email) [20-38]
+  PathExpression [42-50]
+    Identifier(customer) [42-50]
+  IndexItemList [50-57]
+    OrderingExpression(ASC) [51-56]
+      PathExpression [51-56]
+        Identifier(email) [51-56]
+==
+# https://docs.cloud.google.com/spanner/docs/index-geo-partitioning#local-index
+-- Create customer table interleaved in the locations table
+CREATE TABLE customer (
+  location STRING(MAX) NOT NULL,
+  customerId  INT64 NOT NULL,
+  email STRING(MAX),
+  webcookie STRING(64),
+) PRIMARY KEY(location, customerId), INTERLEAVE IN PARENT locations
+--
+CreateTableStatement [60-259]
+  PathExpression [73-81]
+    Identifier(customer) [73-81]
+  TableElementList [82-193]
+    ColumnDefinition [86-115]
+      Identifier(location) [86-94]
+      SimpleColumnSchema [95-115]
+        PathExpression [95-101]
+          Identifier(STRING) [95-101]
+        TypeParameterList [101-105]
+          MaxLiteral() [102-105]
+        ColumnAttributeList [107-115]
+          NotNullColumnAttribute [107-115]
+    ColumnDefinition [119-145]
+      Identifier(customerId) [119-129]
+      SimpleColumnSchema [131-145]
+        PathExpression [131-136]
+          Identifier(INT64) [131-136]
+        ColumnAttributeList [137-145]
+          NotNullColumnAttribute [137-145]
+    ColumnDefinition [149-166]
+      Identifier(email) [149-154]
+      SimpleColumnSchema [155-166]
+        PathExpression [155-161]
+          Identifier(STRING) [155-161]
+        TypeParameterList [161-165]
+          MaxLiteral() [162-165]
+    ColumnDefinition [170-190]
+      Identifier(webcookie) [170-179]
+      SimpleColumnSchema [180-190]
+        PathExpression [180-186]
+          Identifier(STRING) [180-186]
+        TypeParameterList [186-189]
+          IntLiteral(64) [187-189]
+  SpannerTableOptions [194-259]
+    PrimaryKey(ENFORCED) [194-227]
+      PrimaryKeyElementList [205-227]
+        PrimaryKeyElement(ASC) [206-214]
+          Identifier(location) [206-214]
+        PrimaryKeyElement(ASC) [216-226]
+          Identifier(customerId) [216-226]
+    SpannerInterleaveClause [227-259]
+      PathExpression [250-259]
+        Identifier(locations) [250-259]
+==
+# https://docs.cloud.google.com/spanner/docs/index-geo-partitioning#local-index
+-- Create a local index on the interleaved customer table
+CREATE INDEX idx_customer_email_local ON customer(location, email),
+INTERLEAVE IN locations
+--
+CreateIndexStatement [58-149]
+  PathExpression [71-95]
+    Identifier(idx_customer_email_local) [71-95]
+  PathExpression [99-107]
+    Identifier(customer) [99-107]
+  IndexItemList [107-124]
+    OrderingExpression(ASC) [108-116]
+      PathExpression [108-116]
+        Identifier(location) [108-116]
+    OrderingExpression(ASC) [118-123]
+      PathExpression [118-123]
+        Identifier(email) [118-123]
+  SpannerInterleaveClause [124-149]
+    PathExpression [140-149]
+      Identifier(locations) [140-149]
+==
+# https://docs.cloud.google.com/spanner/docs/index-geo-partitioning#local-index
+-- The location (the index key prefix) must be specified
+SELECT *
+FROM customer
+WHERE location= @location AND email= @email
+--
+QueryStatement [57-123]
+  Query [57-123]
+    Select [57-123]
+      SelectList [64-65]
+        SelectColumn [64-65]
+          Star(*) [64-65]
+      FromClause [66-79]
+        TablePathExpression [71-79]
+          PathExpression [71-79]
+            Identifier(customer) [71-79]
+      WhereClause [80-123]
+        AndExpr [86-123]
+          BinaryExpression(=) [86-105]
+            PathExpression [86-94]
+              Identifier(location) [86-94]
+            ParameterExpr [96-105]
+              Identifier(location) [97-105]
+          BinaryExpression(=) [110-123]
+            PathExpression [110-115]
+              Identifier(email) [110-115]
+            ParameterExpr [117-123]
+              Identifier(email) [118-123]
+==
+# https://docs.cloud.google.com/spanner/docs/index-geo-partitioning#local-index
+-- Create customerOrders child table
+CREATE TABLE customerOrders (
+  customerId INT64 NOT NULL,
+  orderId INT64 NOT NULL,
+  orderName STRING(MAX) NOT NULL
+) PRIMARY KEY(customerId, orderId), INTERLEAVE IN PARENT customer
+--
+CreateTableStatement [37-220]
+  PathExpression [50-64]
+    Identifier(customerOrders) [50-64]
+  TableElementList [65-156]
+    ColumnDefinition [69-94]
+      Identifier(customerId) [69-79]
+      SimpleColumnSchema [80-94]
+        PathExpression [80-85]
+          Identifier(INT64) [80-85]
+        ColumnAttributeList [86-94]
+          NotNullColumnAttribute [86-94]
+    ColumnDefinition [98-120]
+      Identifier(orderId) [98-105]
+      SimpleColumnSchema [106-120]
+        PathExpression [106-111]
+          Identifier(INT64) [106-111]
+        ColumnAttributeList [112-120]
+          NotNullColumnAttribute [112-120]
+    ColumnDefinition [124-154]
+      Identifier(orderName) [124-133]
+      SimpleColumnSchema [134-154]
+        PathExpression [134-140]
+          Identifier(STRING) [134-140]
+        TypeParameterList [140-144]
+          MaxLiteral() [141-144]
+        ColumnAttributeList [146-154]
+          NotNullColumnAttribute [146-154]
+  SpannerTableOptions [157-220]
+    PrimaryKey(ENFORCED) [157-189]
+      PrimaryKeyElementList [168-189]
+        PrimaryKeyElement(ASC) [169-179]
+          Identifier(customerId) [169-179]
+        PrimaryKeyElement(ASC) [181-188]
+          Identifier(orderId) [181-188]
+    SpannerInterleaveClause [189-220]
+      PathExpression [212-220]
+        Identifier(customer) [212-220]
+==
+# https://docs.cloud.google.com/spanner/docs/index-geo-partitioning#local-index
+-- Create a local index on the interleaved child table
+CREATE INDEX idx_order_local ON customerOrders(customerId, orderName),
+INTERLEAVE IN customer
+--
+CreateIndexStatement [55-148]
+  PathExpression [68-83]
+    Identifier(idx_order_local) [68-83]
+  PathExpression [87-101]
+    Identifier(customerOrders) [87-101]
+  IndexItemList [101-124]
+    OrderingExpression(ASC) [102-112]
+      PathExpression [102-112]
+        Identifier(customerId) [102-112]
+    OrderingExpression(ASC) [114-123]
+      PathExpression [114-123]
+        Identifier(orderName) [114-123]
+  SpannerInterleaveClause [124-148]
+    PathExpression [140-148]
+      Identifier(customer) [140-148]
+==
+# https://docs.cloud.google.com/spanner/docs/index-geo-partitioning#remote-index
+ALTER DATABASE example_db SET OPTIONS
+  (auto_managed_root_placement_table_name='root_placement_table')
+--
+AlterDatabaseStatement [0-103]
+  PathExpression [15-25]
+    Identifier(example_db) [15-25]
+  AlterActionList [26-103]
+    SetOptionsAction [26-103]
+      OptionsList [40-103]
+        OptionsEntry [41-102]
+          Identifier(auto_managed_root_placement_table_name) [41-79]
+          StringLiteral [80-102]
+            StringLiteralComponent('root_placement_table') [80-102]
+==
+# https://docs.cloud.google.com/spanner/docs/index-geo-partitioning#remote-index
+-- Create a remote index on the customer table
+CREATE INDEX idx_customer_email_remote ON customer(location, email),
+INTERLEAVE IN root_placement_table
+--
+CreateIndexStatement [47-150]
+  PathExpression [60-85]
+    Identifier(idx_customer_email_remote) [60-85]
+  PathExpression [89-97]
+    Identifier(customer) [89-97]
+  IndexItemList [97-114]
+    OrderingExpression(ASC) [98-106]
+      PathExpression [98-106]
+        Identifier(location) [98-106]
+    OrderingExpression(ASC) [108-113]
+      PathExpression [108-113]
+        Identifier(email) [108-113]
+  SpannerInterleaveClause [114-150]
+    PathExpression [130-150]
+      Identifier(root_placement_table) [130-150]
+==
+# https://docs.cloud.google.com/spanner/docs/index-geo-partitioning#remote-index
+-- Specify the location (the index key prefix) in query
+SELECT *
+FROM customer
+WHERE location= @location AND email= @email
+--
+QueryStatement [56-122]
+  Query [56-122]
+    Select [56-122]
+      SelectList [63-64]
+        SelectColumn [63-64]
+          Star(*) [63-64]
+      FromClause [65-78]
+        TablePathExpression [70-78]
+          PathExpression [70-78]
+            Identifier(customer) [70-78]
+      WhereClause [79-122]
+        AndExpr [85-122]
+          BinaryExpression(=) [85-104]
+            PathExpression [85-93]
+              Identifier(location) [85-93]
+            ParameterExpr [95-104]
+              Identifier(location) [96-104]
+          BinaryExpression(=) [109-122]
+            PathExpression [109-114]
+              Identifier(email) [109-114]
+            ParameterExpr [116-122]
+              Identifier(email) [117-122]
+==
+# https://docs.cloud.google.com/spanner/docs/index-geo-partitioning#use-global-unique
+CREATE INDEX idx_customer_email_local ON customer(location, email), INTERLEAVE IN locations
+--
+CreateIndexStatement [0-91]
+  PathExpression [13-37]
+    Identifier(idx_customer_email_local) [13-37]
+  PathExpression [41-49]
+    Identifier(customer) [41-49]
+  IndexItemList [49-66]
+    OrderingExpression(ASC) [50-58]
+      PathExpression [50-58]
+        Identifier(location) [50-58]
+    OrderingExpression(ASC) [60-65]
+      PathExpression [60-65]
+        Identifier(email) [60-65]
+  SpannerInterleaveClause [66-91]
+    PathExpression [82-91]
+      Identifier(locations) [82-91]
+==
+# https://docs.cloud.google.com/spanner/docs/index-geo-partitioning#use-global-unique
+CREATE INDEX idx_customer_email_remote ON customer(location, email), INTERLEAVE IN root_placement_table
+--
+CreateIndexStatement [0-103]
+  PathExpression [13-38]
+    Identifier(idx_customer_email_remote) [13-38]
+  PathExpression [42-50]
+    Identifier(customer) [42-50]
+  IndexItemList [50-67]
+    OrderingExpression(ASC) [51-59]
+      PathExpression [51-59]
+        Identifier(location) [51-59]
+    OrderingExpression(ASC) [61-66]
+      PathExpression [61-66]
+        Identifier(email) [61-66]
+  SpannerInterleaveClause [67-103]
+    PathExpression [83-103]
+      Identifier(root_placement_table) [83-103]
+==
+# https://docs.cloud.google.com/spanner/docs/index-geo-partitioning#use-global-unique
+SELECT *
+FROM customer
+WHERE email= @email
+--
+QueryStatement [0-42]
+  Query [0-42]
+    Select [0-42]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-22]
+        TablePathExpression [14-22]
+          PathExpression [14-22]
+            Identifier(customer) [14-22]
+      WhereClause [23-42]
+        BinaryExpression(=) [29-42]
+          PathExpression [29-34]
+            Identifier(email) [29-34]
+          ParameterExpr [36-42]
+            Identifier(email) [37-42]
+==
+# https://docs.cloud.google.com/spanner/docs/index-geo-partitioning#unique-index-partial-primary-key
+-- Create global unique index on customerId column
+CREATE UNIQUE INDEX idx_customer_customerid ON customer(customerId)
+--
+CreateIndexStatement(UNIQUE) [51-118]
+  PathExpression [71-94]
+    Identifier(idx_customer_customerid) [71-94]
+  PathExpression [98-106]
+    Identifier(customer) [98-106]
+  IndexItemList [106-118]
+    OrderingExpression(ASC) [107-117]
+      PathExpression [107-117]
+        Identifier(customerId) [107-117]
+==
+# https://docs.cloud.google.com/spanner/docs/index-geo-partitioning#unique-index-partial-primary-key
+SELECT * FROM customer WHERE customerId= @customerId
+--
+QueryStatement [0-52]
+  Query [0-52]
+    Select [0-52]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-22]
+        TablePathExpression [14-22]
+          PathExpression [14-22]
+            Identifier(customer) [14-22]
+      WhereClause [23-52]
+        BinaryExpression(=) [29-52]
+          PathExpression [29-39]
+            Identifier(customerId) [29-39]
+          ParameterExpr [41-52]
+            Identifier(customerId) [42-52]
+==
+# https://docs.cloud.google.com/spanner/docs/index-geo-partitioning#unique-index-partial-primary-key
+SELECT * FROM customer WHERE location = @location AND customerId= @customerId
+--
+QueryStatement [0-77]
+  Query [0-77]
+    Select [0-77]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-22]
+        TablePathExpression [14-22]
+          PathExpression [14-22]
+            Identifier(customer) [14-22]
+      WhereClause [23-77]
+        AndExpr [29-77]
+          BinaryExpression(=) [29-49]
+            PathExpression [29-37]
+              Identifier(location) [29-37]
+            ParameterExpr [40-49]
+              Identifier(location) [41-49]
+          BinaryExpression(=) [54-77]
+            PathExpression [54-64]
+              Identifier(customerId) [54-64]
+            ParameterExpr [66-77]
+              Identifier(customerId) [67-77]
diff --git a/parser/testdata/spanner/guides/introspection.active-partitioned-dmls.test b/parser/testdata/spanner/guides/introspection.active-partitioned-dmls.test
new file mode 100644
index 0000000..d8a09da
--- /dev/null
+++ b/parser/testdata/spanner/guides/introspection.active-partitioned-dmls.test
@@ -0,0 +1,137 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/introspection/active-partitioned-dmls
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/introspection/active-partitioned-dmls#listing_oldest_running_queries
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT text,
+       session_id,
+       num_partitions_total,
+       num_partitions_complete,
+       num_trivial_partitions_complete,
+       progress,
+       rows_processed,
+       start_timestamp,
+       last_update_timestamp
+FROM spanner_sys.active_partitioned_dmls
+ORDER BY start_timestamp ASC
+--
+QueryStatement [0-295]
+  Query [0-295]
+    Select [0-266]
+      SelectList [7-225]
+        SelectColumn [7-11]
+          PathExpression [7-11]
+            Identifier(text) [7-11]
+        SelectColumn [20-30]
+          PathExpression [20-30]
+            Identifier(session_id) [20-30]
+        SelectColumn [39-59]
+          PathExpression [39-59]
+            Identifier(num_partitions_total) [39-59]
+        SelectColumn [68-91]
+          PathExpression [68-91]
+            Identifier(num_partitions_complete) [68-91]
+        SelectColumn [100-131]
+          PathExpression [100-131]
+            Identifier(num_trivial_partitions_complete) [100-131]
+        SelectColumn [140-148]
+          PathExpression [140-148]
+            Identifier(progress) [140-148]
+        SelectColumn [157-171]
+          PathExpression [157-171]
+            Identifier(rows_processed) [157-171]
+        SelectColumn [180-195]
+          PathExpression [180-195]
+            Identifier(start_timestamp) [180-195]
+        SelectColumn [204-225]
+          PathExpression [204-225]
+            Identifier(last_update_timestamp) [204-225]
+      FromClause [226-266]
+        TablePathExpression [231-266]
+          PathExpression [231-266]
+            Identifier(spanner_sys) [231-242]
+            Identifier(active_partitioned_dmls) [243-266]
+    OrderBy [267-295]
+      OrderingExpression(ASC EXPLICITLY) [276-295]
+        PathExpression [276-291]
+          Identifier(start_timestamp) [276-291]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/active-partitioned-dmls#retrieve_a_summary_of_active_partitioned_dmls
+SELECT count(*) as active_count
+FROM spanner_sys.active_partitioned_dmls
+--
+QueryStatement [0-72]
+  Query [0-72]
+    Select [0-72]
+      SelectList [7-31]
+        SelectColumn [7-31]
+          FunctionCall [7-15]
+            PathExpression [7-12]
+              Identifier(count) [7-12]
+            Star(*) [13-14]
+          Alias [16-31]
+            Identifier(active_count) [19-31]
+      FromClause [32-72]
+        TablePathExpression [37-72]
+          PathExpression [37-72]
+            Identifier(spanner_sys) [37-48]
+            Identifier(active_partitioned_dmls) [49-72]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/active-partitioned-dmls#listing_the_top_2_oldest_running_partitioned_dmls
+SELECT text,
+       session_id,
+       num_partitions_total,
+       num_partitions_complete,
+       num_trivial_partitions_complete,
+       progress,
+       rows_processed,
+       start_timestamp,
+       last_update_timestamp
+FROM spanner_sys.active_partitioned_dmls
+ORDER BY start_timestamp ASC LIMIT 2
+--
+QueryStatement [0-303]
+  Query [0-303]
+    Select [0-266]
+      SelectList [7-225]
+        SelectColumn [7-11]
+          PathExpression [7-11]
+            Identifier(text) [7-11]
+        SelectColumn [20-30]
+          PathExpression [20-30]
+            Identifier(session_id) [20-30]
+        SelectColumn [39-59]
+          PathExpression [39-59]
+            Identifier(num_partitions_total) [39-59]
+        SelectColumn [68-91]
+          PathExpression [68-91]
+            Identifier(num_partitions_complete) [68-91]
+        SelectColumn [100-131]
+          PathExpression [100-131]
+            Identifier(num_trivial_partitions_complete) [100-131]
+        SelectColumn [140-148]
+          PathExpression [140-148]
+            Identifier(progress) [140-148]
+        SelectColumn [157-171]
+          PathExpression [157-171]
+            Identifier(rows_processed) [157-171]
+        SelectColumn [180-195]
+          PathExpression [180-195]
+            Identifier(start_timestamp) [180-195]
+        SelectColumn [204-225]
+          PathExpression [204-225]
+            Identifier(last_update_timestamp) [204-225]
+      FromClause [226-266]
+        TablePathExpression [231-266]
+          PathExpression [231-266]
+            Identifier(spanner_sys) [231-242]
+            Identifier(active_partitioned_dmls) [243-266]
+    OrderBy [267-295]
+      OrderingExpression(ASC EXPLICITLY) [276-295]
+        PathExpression [276-291]
+          Identifier(start_timestamp) [276-291]
+    LimitOffset [296-303]
+      Limit [296-303]
+        IntLiteral(2) [302-303]
diff --git a/parser/testdata/spanner/guides/introspection.column-operations-statistics.test b/parser/testdata/spanner/guides/introspection.column-operations-statistics.test
new file mode 100644
index 0000000..fa0940c
--- /dev/null
+++ b/parser/testdata/spanner/guides/introspection.column-operations-statistics.test
@@ -0,0 +1,117 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/introspection/column-operations-statistics
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/introspection/column-operations-statistics#query_the_table_columns_with_the_most_write_operations_for_the_most_recent_interval
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT interval_end,
+          table_name,
+          column_name,
+          write_count
+    FROM spanner_sys.column_operations_stats_minute
+    WHERE interval_end = (
+          SELECT MAX(interval_end)
+          FROM spanner_sys.column_operations_stats_minute)
+    ORDER BY write_count DESC
+--
+QueryStatement [0-290]
+  Query [0-290]
+    Select [0-260]
+      SelectList [7-87]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(interval_end) [7-19]
+        SelectColumn [31-41]
+          PathExpression [31-41]
+            Identifier(table_name) [31-41]
+        SelectColumn [53-64]
+          PathExpression [53-64]
+            Identifier(column_name) [53-64]
+        SelectColumn [76-87]
+          PathExpression [76-87]
+            Identifier(write_count) [76-87]
+      FromClause [92-139]
+        TablePathExpression [97-139]
+          PathExpression [97-139]
+            Identifier(spanner_sys) [97-108]
+            Identifier(column_operations_stats_minute) [109-139]
+      WhereClause [144-260]
+        BinaryExpression(=) [150-260]
+          PathExpression [150-162]
+            Identifier(interval_end) [150-162]
+          ExpressionSubquery [165-260]
+            Query [177-259]
+              Select [177-259]
+                SelectList [184-201]
+                  SelectColumn [184-201]
+                    FunctionCall [184-201]
+                      PathExpression [184-187]
+                        Identifier(MAX) [184-187]
+                      PathExpression [188-200]
+                        Identifier(interval_end) [188-200]
+                FromClause [212-259]
+                  TablePathExpression [217-259]
+                    PathExpression [217-259]
+                      Identifier(spanner_sys) [217-228]
+                      Identifier(column_operations_stats_minute) [229-259]
+    OrderBy [265-290]
+      OrderingExpression(DESC) [274-290]
+        PathExpression [274-285]
+          Identifier(write_count) [274-285]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/column-operations-statistics#query_the_columns_with_the_most_query_operations_for_the_most_recent_interval
+SELECT interval_end,
+          table_name,
+          column_name,
+          query_count
+    FROM spanner_sys.column_operations_stats_minute
+    WHERE interval_end = (
+          SELECT MAX(interval_end)
+          FROM spanner_sys.column_operations_stats_minute)
+    ORDER BY query_count DESC
+--
+QueryStatement [0-290]
+  Query [0-290]
+    Select [0-260]
+      SelectList [7-87]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(interval_end) [7-19]
+        SelectColumn [31-41]
+          PathExpression [31-41]
+            Identifier(table_name) [31-41]
+        SelectColumn [53-64]
+          PathExpression [53-64]
+            Identifier(column_name) [53-64]
+        SelectColumn [76-87]
+          PathExpression [76-87]
+            Identifier(query_count) [76-87]
+      FromClause [92-139]
+        TablePathExpression [97-139]
+          PathExpression [97-139]
+            Identifier(spanner_sys) [97-108]
+            Identifier(column_operations_stats_minute) [109-139]
+      WhereClause [144-260]
+        BinaryExpression(=) [150-260]
+          PathExpression [150-162]
+            Identifier(interval_end) [150-162]
+          ExpressionSubquery [165-260]
+            Query [177-259]
+              Select [177-259]
+                SelectList [184-201]
+                  SelectColumn [184-201]
+                    FunctionCall [184-201]
+                      PathExpression [184-187]
+                        Identifier(MAX) [184-187]
+                      PathExpression [188-200]
+                        Identifier(interval_end) [188-200]
+                FromClause [212-259]
+                  TablePathExpression [217-259]
+                    PathExpression [217-259]
+                      Identifier(spanner_sys) [217-228]
+                      Identifier(column_operations_stats_minute) [229-259]
+    OrderBy [265-290]
+      OrderingExpression(DESC) [274-290]
+        PathExpression [274-285]
+          Identifier(query_count) [274-285]
diff --git a/parser/testdata/spanner/guides/introspection.hot-split-statistics.test b/parser/testdata/spanner/guides/introspection.hot-split-statistics.test
new file mode 100644
index 0000000..345d976
--- /dev/null
+++ b/parser/testdata/spanner/guides/introspection.hot-split-statistics.test
@@ -0,0 +1,267 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/introspection/hot-split-statistics
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/introspection/hot-split-statistics#example_schema
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Users (
+  UserId INT64 NOT NULL,
+  FirstName STRING(MAX),
+  LastName STRING(MAX),
+) PRIMARY KEY(UserId)
+--
+CreateTableStatement [0-116]
+  PathExpression [13-18]
+    Identifier(Users) [13-18]
+  TableElementList [19-96]
+    ColumnDefinition [23-44]
+      Identifier(UserId) [23-29]
+      SimpleColumnSchema [30-44]
+        PathExpression [30-35]
+          Identifier(INT64) [30-35]
+        ColumnAttributeList [36-44]
+          NotNullColumnAttribute [36-44]
+    ColumnDefinition [48-69]
+      Identifier(FirstName) [48-57]
+      SimpleColumnSchema [58-69]
+        PathExpression [58-64]
+          Identifier(STRING) [58-64]
+        TypeParameterList [64-68]
+          MaxLiteral() [65-68]
+    ColumnDefinition [73-93]
+      Identifier(LastName) [73-81]
+      SimpleColumnSchema [82-93]
+        PathExpression [82-88]
+          Identifier(STRING) [82-88]
+        TypeParameterList [88-92]
+          MaxLiteral() [89-92]
+  SpannerTableOptions [97-116]
+    PrimaryKey(ENFORCED) [97-116]
+      PrimaryKeyElementList [108-116]
+        PrimaryKeyElement(ASC) [109-115]
+          Identifier(UserId) [109-115]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/hot-split-statistics#example_schema
+CREATE INDEX UsersByFirstName ON Users(FirstName DESC)
+--
+CreateIndexStatement [0-54]
+  PathExpression [13-29]
+    Identifier(UsersByFirstName) [13-29]
+  PathExpression [33-38]
+    Identifier(Users) [33-38]
+  IndexItemList [38-54]
+    OrderingExpression(DESC) [39-53]
+      PathExpression [39-48]
+        Identifier(FirstName) [39-48]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/hot-split-statistics#example_schema
+CREATE TABLE Threads (
+  UserId INT64 NOT NULL,
+  ThreadId INT64 NOT NULL,
+  Starred BOOL,
+) PRIMARY KEY(UserId, ThreadId),
+  INTERLEAVE IN PARENT Users ON DELETE CASCADE
+--
+CreateTableStatement [0-170]
+  PathExpression [13-20]
+    Identifier(Threads) [13-20]
+  TableElementList [21-92]
+    ColumnDefinition [25-46]
+      Identifier(UserId) [25-31]
+      SimpleColumnSchema [32-46]
+        PathExpression [32-37]
+          Identifier(INT64) [32-37]
+        ColumnAttributeList [38-46]
+          NotNullColumnAttribute [38-46]
+    ColumnDefinition [50-73]
+      Identifier(ThreadId) [50-58]
+      SimpleColumnSchema [59-73]
+        PathExpression [59-64]
+          Identifier(INT64) [59-64]
+        ColumnAttributeList [65-73]
+          NotNullColumnAttribute [65-73]
+    ColumnDefinition [77-89]
+      Identifier(Starred) [77-84]
+      SimpleColumnSchema [85-89]
+        PathExpression [85-89]
+          Identifier(BOOL) [85-89]
+  SpannerTableOptions [93-170]
+    PrimaryKey(ENFORCED) [93-122]
+      PrimaryKeyElementList [104-122]
+        PrimaryKeyElement(ASC) [105-111]
+          Identifier(UserId) [105-111]
+        PrimaryKeyElement(ASC) [113-121]
+          Identifier(ThreadId) [113-121]
+    SpannerInterleaveClause [122-170]
+      PathExpression [147-152]
+        Identifier(Users) [147-152]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/hot-split-statistics#example_schema
+CREATE TABLE Messages (
+  UserId INT64 NOT NULL,
+  ThreadId INT64 NOT NULL,
+  MessageId INT64 NOT NULL,
+  Subject STRING(MAX),
+  Body STRING(MAX),
+) PRIMARY KEY(UserId, ThreadId, MessageId),
+  INTERLEAVE IN PARENT Threads ON DELETE CASCADE
+--
+CreateTableStatement [0-239]
+  PathExpression [13-21]
+    Identifier(Messages) [13-21]
+  TableElementList [22-148]
+    ColumnDefinition [26-47]
+      Identifier(UserId) [26-32]
+      SimpleColumnSchema [33-47]
+        PathExpression [33-38]
+          Identifier(INT64) [33-38]
+        ColumnAttributeList [39-47]
+          NotNullColumnAttribute [39-47]
+    ColumnDefinition [51-74]
+      Identifier(ThreadId) [51-59]
+      SimpleColumnSchema [60-74]
+        PathExpression [60-65]
+          Identifier(INT64) [60-65]
+        ColumnAttributeList [66-74]
+          NotNullColumnAttribute [66-74]
+    ColumnDefinition [78-102]
+      Identifier(MessageId) [78-87]
+      SimpleColumnSchema [88-102]
+        PathExpression [88-93]
+          Identifier(INT64) [88-93]
+        ColumnAttributeList [94-102]
+          NotNullColumnAttribute [94-102]
+    ColumnDefinition [106-125]
+      Identifier(Subject) [106-113]
+      SimpleColumnSchema [114-125]
+        PathExpression [114-120]
+          Identifier(STRING) [114-120]
+        TypeParameterList [120-124]
+          MaxLiteral() [121-124]
+    ColumnDefinition [129-145]
+      Identifier(Body) [129-133]
+      SimpleColumnSchema [134-145]
+        PathExpression [134-140]
+          Identifier(STRING) [134-140]
+        TypeParameterList [140-144]
+          MaxLiteral() [141-144]
+  SpannerTableOptions [149-239]
+    PrimaryKey(ENFORCED) [149-189]
+      PrimaryKeyElementList [160-189]
+        PrimaryKeyElement(ASC) [161-167]
+          Identifier(UserId) [161-167]
+        PrimaryKeyElement(ASC) [169-177]
+          Identifier(ThreadId) [169-177]
+        PrimaryKeyElement(ASC) [179-188]
+          Identifier(MessageId) [179-188]
+    SpannerInterleaveClause [189-239]
+      PathExpression [214-221]
+        Identifier(Threads) [214-221]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/hot-split-statistics#example_schema
+CREATE INDEX MessagesIdx ON Messages(UserId, ThreadId, Subject),
+INTERLEAVE IN Threads
+--
+CreateIndexStatement [0-86]
+  PathExpression [13-24]
+    Identifier(MessagesIdx) [13-24]
+  PathExpression [28-36]
+    Identifier(Messages) [28-36]
+  IndexItemList [36-63]
+    OrderingExpression(ASC) [37-43]
+      PathExpression [37-43]
+        Identifier(UserId) [37-43]
+    OrderingExpression(ASC) [45-53]
+      PathExpression [45-53]
+        Identifier(ThreadId) [45-53]
+    OrderingExpression(ASC) [55-62]
+      PathExpression [55-62]
+        Identifier(Subject) [55-62]
+  SpannerInterleaveClause [63-86]
+    PathExpression [79-86]
+      Identifier(Threads) [79-86]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/hot-split-statistics#check_for_unsplittable_reasons
+SELECT
+  reason,
+  COUNT(*) AS occurrences
+FROM
+  SPANNER_SYS.SPLIT_STATS_TOP_MINUTE AS t,
+  UNNEST(t.unsplittable_reasons) AS reason
+WHERE
+  t.cpu_usage_score >= 50
+  AND ARRAY_LENGTH(t.unsplittable_reasons) > 0
+  AND t.interval_end >= "2072-05-18T17:40:00Z"  -- Start of window
+  AND t.interval_end <= "2072-05-18T17:50:00Z"  -- End of window
+GROUP BY
+  reason
+ORDER BY
+  occurrences DESC
+--
+QueryStatement [0-390]
+  Query [0-390]
+    Select [0-362]
+      SelectList [9-42]
+        SelectColumn [9-15]
+          PathExpression [9-15]
+            Identifier(reason) [9-15]
+        SelectColumn [19-42]
+          FunctionCall [19-27]
+            PathExpression [19-24]
+              Identifier(COUNT) [19-24]
+            Star(*) [25-26]
+          Alias [28-42]
+            Identifier(occurrences) [31-42]
+      FromClause [43-133]
+        Join(COMMA) [50-133]
+          TablePathExpression [50-89]
+            PathExpression [50-84]
+              Identifier(SPANNER_SYS) [50-61]
+              Identifier(SPLIT_STATS_TOP_MINUTE) [62-84]
+            Alias [85-89]
+              Identifier(t) [88-89]
+          Location [89-90]
+          TablePathExpression [93-133]
+            UnnestExpression [93-123]
+              ExpressionWithOptAlias [100-122]
+                PathExpression [100-122]
+                  Identifier(t) [100-101]
+                  Identifier(unsplittable_reasons) [102-122]
+            Alias [124-133]
+              Identifier(reason) [127-133]
+      WhereClause [134-326]
+        AndExpr [142-326]
+          BinaryExpression(>=) [142-165]
+            PathExpression [142-159]
+              Identifier(t) [142-143]
+              Identifier(cpu_usage_score) [144-159]
+            IntLiteral(50) [163-165]
+          BinaryExpression(>) [172-212]
+            FunctionCall [172-208]
+              PathExpression [172-184]
+                Identifier(ARRAY_LENGTH) [172-184]
+              PathExpression [185-207]
+                Identifier(t) [185-186]
+                Identifier(unsplittable_reasons) [187-207]
+            IntLiteral(0) [211-212]
+          BinaryExpression(>=) [219-259]
+            PathExpression [219-233]
+              Identifier(t) [219-220]
+              Identifier(interval_end) [221-233]
+            StringLiteral [237-259]
+              StringLiteralComponent("2072-05-18T17:40:00Z") [237-259]
+          BinaryExpression(<=) [286-326]
+            PathExpression [286-300]
+              Identifier(t) [286-287]
+              Identifier(interval_end) [288-300]
+            StringLiteral [304-326]
+              StringLiteralComponent("2072-05-18T17:50:00Z") [304-326]
+      GroupBy [345-362]
+        GroupingItem [356-362]
+          PathExpression [356-362]
+            Identifier(reason) [356-362]
+    OrderBy [363-390]
+      OrderingExpression(DESC) [374-390]
+        PathExpression [374-385]
+          Identifier(occurrences) [374-385]
diff --git a/parser/testdata/spanner/guides/introspection.investigate-cpu-utilization.test b/parser/testdata/spanner/guides/introspection.investigate-cpu-utilization.test
new file mode 100644
index 0000000..1568797
--- /dev/null
+++ b/parser/testdata/spanner/guides/introspection.investigate-cpu-utilization.test
@@ -0,0 +1,76 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/introspection/investigate-cpu-utilization
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/introspection/investigate-cpu-utilization#analyze-statistics
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT text,
+       execution_count AS count,
+       avg_latency_seconds AS latency,
+       avg_cpu_seconds AS cpu,
+       execution_count * avg_cpu_seconds AS total_cpu
+FROM spanner_sys.query_stats_top_hour
+WHERE interval_end =
+  (SELECT MAX(interval_end)
+   FROM spanner_sys.query_stats_top_hour)
+ORDER BY total_cpu DESC
+--
+QueryStatement [0-322]
+  Query [0-322]
+    Select [0-298]
+      SelectList [7-169]
+        SelectColumn [7-11]
+          PathExpression [7-11]
+            Identifier(text) [7-11]
+        SelectColumn [20-44]
+          PathExpression [20-35]
+            Identifier(execution_count) [20-35]
+          Alias [36-44]
+            Identifier(count) [39-44]
+        SelectColumn [53-83]
+          PathExpression [53-72]
+            Identifier(avg_latency_seconds) [53-72]
+          Alias [73-83]
+            Identifier(latency) [76-83]
+        SelectColumn [92-114]
+          PathExpression [92-107]
+            Identifier(avg_cpu_seconds) [92-107]
+          Alias [108-114]
+            Identifier(cpu) [111-114]
+        SelectColumn [123-169]
+          BinaryExpression(*) [123-156]
+            PathExpression [123-138]
+              Identifier(execution_count) [123-138]
+            PathExpression [141-156]
+              Identifier(avg_cpu_seconds) [141-156]
+          Alias [157-169]
+            Identifier(total_cpu) [160-169]
+      FromClause [170-207]
+        TablePathExpression [175-207]
+          PathExpression [175-207]
+            Identifier(spanner_sys) [175-186]
+            Identifier(query_stats_top_hour) [187-207]
+      WhereClause [208-298]
+        BinaryExpression(=) [214-298]
+          PathExpression [214-226]
+            Identifier(interval_end) [214-226]
+          ExpressionSubquery [231-298]
+            Query [232-297]
+              Select [232-297]
+                SelectList [239-256]
+                  SelectColumn [239-256]
+                    FunctionCall [239-256]
+                      PathExpression [239-242]
+                        Identifier(MAX) [239-242]
+                      PathExpression [243-255]
+                        Identifier(interval_end) [243-255]
+                FromClause [260-297]
+                  TablePathExpression [265-297]
+                    PathExpression [265-297]
+                      Identifier(spanner_sys) [265-276]
+                      Identifier(query_stats_top_hour) [277-297]
+    OrderBy [299-322]
+      OrderingExpression(DESC) [308-322]
+        PathExpression [308-317]
+          Identifier(total_cpu) [308-317]
diff --git a/parser/testdata/spanner/guides/introspection.lock-statistics.test b/parser/testdata/spanner/guides/introspection.lock-statistics.test
new file mode 100644
index 0000000..bc91a8b
--- /dev/null
+++ b/parser/testdata/spanner/guides/introspection.lock-statistics.test
@@ -0,0 +1,560 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/introspection/lock-statistics
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/introspection/lock-statistics#explain-row-range
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Singers (
+  SingerId   INT64 NOT NULL,
+  FirstName  STRING(1024),
+  LastName   STRING(1024),
+  SingerInfo BYTES(MAX),
+) PRIMARY KEY (SingerId)
+--
+CreateTableStatement [0-155]
+  PathExpression [13-20]
+    Identifier(Singers) [13-20]
+  TableElementList [21-132]
+    ColumnDefinition [25-50]
+      Identifier(SingerId) [25-33]
+      SimpleColumnSchema [36-50]
+        PathExpression [36-41]
+          Identifier(INT64) [36-41]
+        ColumnAttributeList [42-50]
+          NotNullColumnAttribute [42-50]
+    ColumnDefinition [54-77]
+      Identifier(FirstName) [54-63]
+      SimpleColumnSchema [65-77]
+        PathExpression [65-71]
+          Identifier(STRING) [65-71]
+        TypeParameterList [71-76]
+          IntLiteral(1024) [72-76]
+    ColumnDefinition [81-104]
+      Identifier(LastName) [81-89]
+      SimpleColumnSchema [92-104]
+        PathExpression [92-98]
+          Identifier(STRING) [92-98]
+        TypeParameterList [98-103]
+          IntLiteral(1024) [99-103]
+    ColumnDefinition [108-129]
+      Identifier(SingerInfo) [108-118]
+      SimpleColumnSchema [119-129]
+        PathExpression [119-124]
+          Identifier(BYTES) [119-124]
+        TypeParameterList [124-128]
+          MaxLiteral() [125-128]
+  SpannerTableOptions [133-155]
+    PrimaryKey(ENFORCED) [133-155]
+      PrimaryKeyElementList [145-155]
+        PrimaryKeyElement(ASC) [146-154]
+          Identifier(SingerId) [146-154]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/lock-statistics#explain-row-range
+CREATE TABLE Albums (
+  SingerId     INT64 NOT NULL,
+  AlbumId      INT64 NOT NULL,
+  AlbumTitle   STRING(MAX),
+) PRIMARY KEY (SingerId, AlbumId),
+  INTERLEAVE IN PARENT Singers ON DELETE CASCADE
+--
+CreateTableStatement [0-195]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-113]
+    ColumnDefinition [24-51]
+      Identifier(SingerId) [24-32]
+      SimpleColumnSchema [37-51]
+        PathExpression [37-42]
+          Identifier(INT64) [37-42]
+        ColumnAttributeList [43-51]
+          NotNullColumnAttribute [43-51]
+    ColumnDefinition [55-82]
+      Identifier(AlbumId) [55-62]
+      SimpleColumnSchema [68-82]
+        PathExpression [68-73]
+          Identifier(INT64) [68-73]
+        ColumnAttributeList [74-82]
+          NotNullColumnAttribute [74-82]
+    ColumnDefinition [86-110]
+      Identifier(AlbumTitle) [86-96]
+      SimpleColumnSchema [99-110]
+        PathExpression [99-105]
+          Identifier(STRING) [99-105]
+        TypeParameterList [105-109]
+          MaxLiteral() [106-109]
+  SpannerTableOptions [114-195]
+    PrimaryKey(ENFORCED) [114-145]
+      PrimaryKeyElementList [126-145]
+        PrimaryKeyElement(ASC) [127-135]
+          Identifier(SingerId) [127-135]
+        PrimaryKeyElement(ASC) [137-144]
+          Identifier(AlbumId) [137-144]
+    SpannerInterleaveClause [145-195]
+      PathExpression [170-177]
+        Identifier(Singers) [170-177]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/lock-statistics#explain-row-range
+CREATE TABLE Songs (
+  SingerId     INT64 NOT NULL,
+  AlbumId      INT64 NOT NULL,
+  TrackId      INT64 NOT NULL,
+  SongName     STRING(MAX),
+) PRIMARY KEY (SingerId, AlbumId, TrackId),
+  INTERLEAVE IN PARENT Albums ON DELETE CASCADE
+--
+CreateTableStatement [0-233]
+  PathExpression [13-18]
+    Identifier(Songs) [13-18]
+  TableElementList [19-143]
+    ColumnDefinition [23-50]
+      Identifier(SingerId) [23-31]
+      SimpleColumnSchema [36-50]
+        PathExpression [36-41]
+          Identifier(INT64) [36-41]
+        ColumnAttributeList [42-50]
+          NotNullColumnAttribute [42-50]
+    ColumnDefinition [54-81]
+      Identifier(AlbumId) [54-61]
+      SimpleColumnSchema [67-81]
+        PathExpression [67-72]
+          Identifier(INT64) [67-72]
+        ColumnAttributeList [73-81]
+          NotNullColumnAttribute [73-81]
+    ColumnDefinition [85-112]
+      Identifier(TrackId) [85-92]
+      SimpleColumnSchema [98-112]
+        PathExpression [98-103]
+          Identifier(INT64) [98-103]
+        ColumnAttributeList [104-112]
+          NotNullColumnAttribute [104-112]
+    ColumnDefinition [116-140]
+      Identifier(SongName) [116-124]
+      SimpleColumnSchema [129-140]
+        PathExpression [129-135]
+          Identifier(STRING) [129-135]
+        TypeParameterList [135-139]
+          MaxLiteral() [136-139]
+  SpannerTableOptions [144-233]
+    PrimaryKey(ENFORCED) [144-184]
+      PrimaryKeyElementList [156-184]
+        PrimaryKeyElement(ASC) [157-165]
+          Identifier(SingerId) [157-165]
+        PrimaryKeyElement(ASC) [167-174]
+          Identifier(AlbumId) [167-174]
+        PrimaryKeyElement(ASC) [176-183]
+          Identifier(TrackId) [176-183]
+    SpannerInterleaveClause [184-233]
+      PathExpression [209-215]
+        Identifier(Albums) [209-215]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/lock-statistics#list_the_lock_statistics_for_the_previous_1-minute_interval
+SELECT CAST(s.row_range_start_key AS STRING) AS row_range_start_key,
+       t.total_lock_wait_seconds,
+       s.lock_wait_seconds,
+       s.lock_wait_seconds/t.total_lock_wait_seconds frac_of_total,
+       s.sample_lock_requests
+FROM spanner_sys.lock_stats_total_minute t, spanner_sys.lock_stats_top_minute s
+WHERE t.interval_end =
+  (SELECT MAX(interval_end)
+   FROM spanner_sys.lock_stats_total_minute)
+AND s.interval_end = t.interval_end
+ORDER BY s.lock_wait_seconds DESC
+--
+QueryStatement [0-474]
+  Query [0-474]
+    Select [0-440]
+      SelectList [7-228]
+        SelectColumn [7-67]
+          CastExpression [7-44]
+            PathExpression [12-33]
+              Identifier(s) [12-13]
+              Identifier(row_range_start_key) [14-33]
+            SimpleType [37-43]
+              PathExpression [37-43]
+                Identifier(STRING) [37-43]
+          Alias [45-67]
+            Identifier(row_range_start_key) [48-67]
+        SelectColumn [76-101]
+          PathExpression [76-101]
+            Identifier(t) [76-77]
+            Identifier(total_lock_wait_seconds) [78-101]
+        SelectColumn [110-129]
+          PathExpression [110-129]
+            Identifier(s) [110-111]
+            Identifier(lock_wait_seconds) [112-129]
+        SelectColumn [138-197]
+          BinaryExpression(/) [138-183]
+            PathExpression [138-157]
+              Identifier(s) [138-139]
+              Identifier(lock_wait_seconds) [140-157]
+            PathExpression [158-183]
+              Identifier(t) [158-159]
+              Identifier(total_lock_wait_seconds) [160-183]
+          Alias [184-197]
+            Identifier(frac_of_total) [184-197]
+        SelectColumn [206-228]
+          PathExpression [206-228]
+            Identifier(s) [206-207]
+            Identifier(sample_lock_requests) [208-228]
+      FromClause [229-308]
+        Join(COMMA) [234-308]
+          TablePathExpression [234-271]
+            PathExpression [234-269]
+              Identifier(spanner_sys) [234-245]
+              Identifier(lock_stats_total_minute) [246-269]
+            Alias [270-271]
+              Identifier(t) [270-271]
+          Location [271-272]
+          TablePathExpression [273-308]
+            PathExpression [273-306]
+              Identifier(spanner_sys) [273-284]
+              Identifier(lock_stats_top_minute) [285-306]
+            Alias [307-308]
+              Identifier(s) [307-308]
+      WhereClause [309-440]
+        AndExpr [315-440]
+          BinaryExpression(=) [315-404]
+            PathExpression [315-329]
+              Identifier(t) [315-316]
+              Identifier(interval_end) [317-329]
+            ExpressionSubquery [334-404]
+              Query [335-403]
+                Select [335-403]
+                  SelectList [342-359]
+                    SelectColumn [342-359]
+                      FunctionCall [342-359]
+                        PathExpression [342-345]
+                          Identifier(MAX) [342-345]
+                        PathExpression [346-358]
+                          Identifier(interval_end) [346-358]
+                  FromClause [363-403]
+                    TablePathExpression [368-403]
+                      PathExpression [368-403]
+                        Identifier(spanner_sys) [368-379]
+                        Identifier(lock_stats_total_minute) [380-403]
+          BinaryExpression(=) [409-440]
+            PathExpression [409-423]
+              Identifier(s) [409-410]
+              Identifier(interval_end) [411-423]
+            PathExpression [426-440]
+              Identifier(t) [426-427]
+              Identifier(interval_end) [428-440]
+    OrderBy [441-474]
+      OrderingExpression(DESC) [450-474]
+        PathExpression [450-469]
+          Identifier(s) [450-451]
+          Identifier(lock_wait_seconds) [452-469]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/lock-statistics#determine_whether_transaction_commit_latency_increased_along_with_the_lock_wait_time_during_the_selected_period
+SELECT t.interval_end, t.avg_commit_latency_seconds, l.total_lock_wait_seconds
+FROM spanner_sys.txn_stats_total_10minute t
+LEFT JOIN spanner_sys.lock_stats_total_10minute l
+ON t.interval_end = l.interval_end
+WHERE
+  t.interval_end >= "2020-11-12T21:50:00Z"
+  AND t.interval_end <= "2020-11-12T23:50:00Z"
+ORDER BY interval_end
+--
+QueryStatement [0-325]
+  Query [0-325]
+    Select [0-303]
+      SelectList [7-78]
+        SelectColumn [7-21]
+          PathExpression [7-21]
+            Identifier(t) [7-8]
+            Identifier(interval_end) [9-21]
+        SelectColumn [23-51]
+          PathExpression [23-51]
+            Identifier(t) [23-24]
+            Identifier(avg_commit_latency_seconds) [25-51]
+        SelectColumn [53-78]
+          PathExpression [53-78]
+            Identifier(l) [53-54]
+            Identifier(total_lock_wait_seconds) [55-78]
+      FromClause [79-207]
+        Join(LEFT) [84-207]
+          TablePathExpression [84-122]
+            PathExpression [84-120]
+              Identifier(spanner_sys) [84-95]
+              Identifier(txn_stats_total_10minute) [96-120]
+            Alias [121-122]
+              Identifier(t) [121-122]
+          Location [123-132]
+          TablePathExpression [133-172]
+            PathExpression [133-170]
+              Identifier(spanner_sys) [133-144]
+              Identifier(lock_stats_total_10minute) [145-170]
+            Alias [171-172]
+              Identifier(l) [171-172]
+          OnClause [173-207]
+            BinaryExpression(=) [176-207]
+              PathExpression [176-190]
+                Identifier(t) [176-177]
+                Identifier(interval_end) [178-190]
+              PathExpression [193-207]
+                Identifier(l) [193-194]
+                Identifier(interval_end) [195-207]
+      WhereClause [208-303]
+        AndExpr [216-303]
+          BinaryExpression(>=) [216-256]
+            PathExpression [216-230]
+              Identifier(t) [216-217]
+              Identifier(interval_end) [218-230]
+            StringLiteral [234-256]
+              StringLiteralComponent("2020-11-12T21:50:00Z") [234-256]
+          BinaryExpression(<=) [263-303]
+            PathExpression [263-277]
+              Identifier(t) [263-264]
+              Identifier(interval_end) [265-277]
+            StringLiteral [281-303]
+              StringLiteralComponent("2020-11-12T23:50:00Z") [281-303]
+    OrderBy [304-325]
+      OrderingExpression(ASC) [313-325]
+        PathExpression [313-325]
+          Identifier(interval_end) [313-325]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/lock-statistics#discover_which_row_keys_and_columns_had_long_lock_wait_times_during_the_selected_period
+SELECT CAST(s.row_range_start_key AS STRING) AS row_range_start_key,
+       t.total_lock_wait_seconds,
+       s.lock_wait_seconds,
+       s.lock_wait_seconds/t.total_lock_wait_seconds frac_of_total,
+       s.sample_lock_requests
+FROM spanner_sys.lock_stats_total_10minute t, spanner_sys.lock_stats_top_10minute s
+WHERE
+  t.interval_end = "2020-11-12T22:50:00Z" and s.interval_end = t.interval_end
+--
+QueryStatement [0-396]
+  Query [0-396]
+    Select [0-396]
+      SelectList [7-228]
+        SelectColumn [7-67]
+          CastExpression [7-44]
+            PathExpression [12-33]
+              Identifier(s) [12-13]
+              Identifier(row_range_start_key) [14-33]
+            SimpleType [37-43]
+              PathExpression [37-43]
+                Identifier(STRING) [37-43]
+          Alias [45-67]
+            Identifier(row_range_start_key) [48-67]
+        SelectColumn [76-101]
+          PathExpression [76-101]
+            Identifier(t) [76-77]
+            Identifier(total_lock_wait_seconds) [78-101]
+        SelectColumn [110-129]
+          PathExpression [110-129]
+            Identifier(s) [110-111]
+            Identifier(lock_wait_seconds) [112-129]
+        SelectColumn [138-197]
+          BinaryExpression(/) [138-183]
+            PathExpression [138-157]
+              Identifier(s) [138-139]
+              Identifier(lock_wait_seconds) [140-157]
+            PathExpression [158-183]
+              Identifier(t) [158-159]
+              Identifier(total_lock_wait_seconds) [160-183]
+          Alias [184-197]
+            Identifier(frac_of_total) [184-197]
+        SelectColumn [206-228]
+          PathExpression [206-228]
+            Identifier(s) [206-207]
+            Identifier(sample_lock_requests) [208-228]
+      FromClause [229-312]
+        Join(COMMA) [234-312]
+          TablePathExpression [234-273]
+            PathExpression [234-271]
+              Identifier(spanner_sys) [234-245]
+              Identifier(lock_stats_total_10minute) [246-271]
+            Alias [272-273]
+              Identifier(t) [272-273]
+          Location [273-274]
+          TablePathExpression [275-312]
+            PathExpression [275-310]
+              Identifier(spanner_sys) [275-286]
+              Identifier(lock_stats_top_10minute) [287-310]
+            Alias [311-312]
+              Identifier(s) [311-312]
+      WhereClause [313-396]
+        AndExpr [321-396]
+          BinaryExpression(=) [321-360]
+            PathExpression [321-335]
+              Identifier(t) [321-322]
+              Identifier(interval_end) [323-335]
+            StringLiteral [338-360]
+              StringLiteralComponent("2020-11-12T22:50:00Z") [338-360]
+          BinaryExpression(=) [365-396]
+            PathExpression [365-379]
+              Identifier(s) [365-366]
+              Identifier(interval_end) [367-379]
+            PathExpression [382-396]
+              Identifier(t) [382-383]
+              Identifier(interval_end) [384-396]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/lock-statistics#find_which_transactions_are_accessing_the_columns_involved_in_the_lock_conflict
+SELECT
+  fprint,
+  read_columns,
+  write_constructive_columns,
+  avg_commit_latency_seconds
+FROM spanner_sys.txn_stats_top_10minute t2
+WHERE (
+  EXISTS (
+    SELECT * FROM t2.read_columns columns WHERE columns IN (
+      SELECT DISTINCT(req.COLUMN)
+      FROM spanner_sys.lock_stats_top_10minute t, t.SAMPLE_LOCK_REQUESTS req
+      WHERE req.LOCK_MODE = "ReaderShared" AND t.interval_end ="2020-11-12T23:50:00Z"))
+OR
+  EXISTS (
+    SELECT * FROM t2.write_constructive_columns columns WHERE columns IN (
+      SELECT DISTINCT(req.COLUMN)
+      FROM spanner_sys.lock_stats_top_10minute t, t.SAMPLE_LOCK_REQUESTS req
+      WHERE req.LOCK_MODE = "WriterShared" AND t.interval_end ="2020-11-12T23:50:00Z"))
+)
+AND t2.interval_end ="2020-11-12T23:50:00Z"
+ORDER BY avg_commit_latency_seconds DESC
+--
+QueryStatement [0-788]
+  Query [0-788]
+    Select [0-747]
+      SelectList [9-91]
+        SelectColumn [9-15]
+          PathExpression [9-15]
+            Identifier(fprint) [9-15]
+        SelectColumn [19-31]
+          PathExpression [19-31]
+            Identifier(read_columns) [19-31]
+        SelectColumn [35-61]
+          PathExpression [35-61]
+            Identifier(write_constructive_columns) [35-61]
+        SelectColumn [65-91]
+          PathExpression [65-91]
+            Identifier(avg_commit_latency_seconds) [65-91]
+      FromClause [92-134]
+        TablePathExpression [97-134]
+          PathExpression [97-131]
+            Identifier(spanner_sys) [97-108]
+            Identifier(txn_stats_top_10minute) [109-131]
+          Alias [132-134]
+            Identifier(t2) [132-134]
+      WhereClause [135-747]
+        AndExpr [141-747]
+          OrExpr [145-701]
+            ExpressionSubquery(modifier=EXISTS) [145-413]
+              Query [158-412]
+                Select [158-412]
+                  SelectList [165-166]
+                    SelectColumn [165-166]
+                      Star(*) [165-166]
+                  FromClause [167-195]
+                    TablePathExpression [172-195]
+                      PathExpression [172-187]
+                        Identifier(t2) [172-174]
+                        Identifier(read_columns) [175-187]
+                      Alias [188-195]
+                        Identifier(columns) [188-195]
+                  WhereClause [196-412]
+                    InExpression(IN) [202-412]
+                      PathExpression [202-209]
+                        Identifier(columns) [202-209]
+                      Location [210-212]
+                      Query [221-411]
+                        Select(distinct=true) [221-411]
+                          SelectList [236-248]
+                            SelectColumn [236-248]
+                              PathExpression [237-247]
+                                Identifier(req) [237-240]
+                                Identifier(COLUMN) [241-247]
+                          FromClause [255-325]
+                            Join(COMMA) [260-325]
+                              TablePathExpression [260-297]
+                                PathExpression [260-295]
+                                  Identifier(spanner_sys) [260-271]
+                                  Identifier(lock_stats_top_10minute) [272-295]
+                                Alias [296-297]
+                                  Identifier(t) [296-297]
+                              Location [297-298]
+                              TablePathExpression [299-325]
+                                PathExpression [299-321]
+                                  Identifier(t) [299-300]
+                                  Identifier(SAMPLE_LOCK_REQUESTS) [301-321]
+                                Alias [322-325]
+                                  Identifier(req) [322-325]
+                          WhereClause [332-411]
+                            AndExpr [338-411]
+                              BinaryExpression(=) [338-368]
+                                PathExpression [338-351]
+                                  Identifier(req) [338-341]
+                                  Identifier(LOCK_MODE) [342-351]
+                                StringLiteral [354-368]
+                                  StringLiteralComponent("ReaderShared") [354-368]
+                              BinaryExpression(=) [373-411]
+                                PathExpression [373-387]
+                                  Identifier(t) [373-374]
+                                  Identifier(interval_end) [375-387]
+                                StringLiteral [389-411]
+                                  StringLiteralComponent("2020-11-12T23:50:00Z") [389-411]
+            ExpressionSubquery(modifier=EXISTS) [419-701]
+              Query [432-700]
+                Select [432-700]
+                  SelectList [439-440]
+                    SelectColumn [439-440]
+                      Star(*) [439-440]
+                  FromClause [441-483]
+                    TablePathExpression [446-483]
+                      PathExpression [446-475]
+                        Identifier(t2) [446-448]
+                        Identifier(write_constructive_columns) [449-475]
+                      Alias [476-483]
+                        Identifier(columns) [476-483]
+                  WhereClause [484-700]
+                    InExpression(IN) [490-700]
+                      PathExpression [490-497]
+                        Identifier(columns) [490-497]
+                      Location [498-500]
+                      Query [509-699]
+                        Select(distinct=true) [509-699]
+                          SelectList [524-536]
+                            SelectColumn [524-536]
+                              PathExpression [525-535]
+                                Identifier(req) [525-528]
+                                Identifier(COLUMN) [529-535]
+                          FromClause [543-613]
+                            Join(COMMA) [548-613]
+                              TablePathExpression [548-585]
+                                PathExpression [548-583]
+                                  Identifier(spanner_sys) [548-559]
+                                  Identifier(lock_stats_top_10minute) [560-583]
+                                Alias [584-585]
+                                  Identifier(t) [584-585]
+                              Location [585-586]
+                              TablePathExpression [587-613]
+                                PathExpression [587-609]
+                                  Identifier(t) [587-588]
+                                  Identifier(SAMPLE_LOCK_REQUESTS) [589-609]
+                                Alias [610-613]
+                                  Identifier(req) [610-613]
+                          WhereClause [620-699]
+                            AndExpr [626-699]
+                              BinaryExpression(=) [626-656]
+                                PathExpression [626-639]
+                                  Identifier(req) [626-629]
+                                  Identifier(LOCK_MODE) [630-639]
+                                StringLiteral [642-656]
+                                  StringLiteralComponent("WriterShared") [642-656]
+                              BinaryExpression(=) [661-699]
+                                PathExpression [661-675]
+                                  Identifier(t) [661-662]
+                                  Identifier(interval_end) [663-675]
+                                StringLiteral [677-699]
+                                  StringLiteralComponent("2020-11-12T23:50:00Z") [677-699]
+          BinaryExpression(=) [708-747]
+            PathExpression [708-723]
+              Identifier(t2) [708-710]
+              Identifier(interval_end) [711-723]
+            StringLiteral [725-747]
+              StringLiteralComponent("2020-11-12T23:50:00Z") [725-747]
+    OrderBy [748-788]
+      OrderingExpression(DESC) [757-788]
+        PathExpression [757-783]
+          Identifier(avg_commit_latency_seconds) [757-783]
diff --git a/parser/testdata/spanner/guides/introspection.oldest-active-queries.test b/parser/testdata/spanner/guides/introspection.oldest-active-queries.test
new file mode 100644
index 0000000..b2f3237
--- /dev/null
+++ b/parser/testdata/spanner/guides/introspection.oldest-active-queries.test
@@ -0,0 +1,192 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/introspection/oldest-active-queries
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/introspection/oldest-active-queries#list_oldest_running_active_queries
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT start_time,
+       text_fingerprint,
+       text,
+       text_truncated,
+       session_id,
+       query_id,
+       api_client_header,
+       server_region,
+       priority,
+       transaction_type
+FROM spanner_sys.oldest_active_queries
+ORDER BY start_time ASC
+--
+QueryStatement [0-267]
+  Query [0-267]
+    Select [0-243]
+      SelectList [7-204]
+        SelectColumn [7-17]
+          PathExpression [7-17]
+            Identifier(start_time) [7-17]
+        SelectColumn [26-42]
+          PathExpression [26-42]
+            Identifier(text_fingerprint) [26-42]
+        SelectColumn [51-55]
+          PathExpression [51-55]
+            Identifier(text) [51-55]
+        SelectColumn [64-78]
+          PathExpression [64-78]
+            Identifier(text_truncated) [64-78]
+        SelectColumn [87-97]
+          PathExpression [87-97]
+            Identifier(session_id) [87-97]
+        SelectColumn [106-114]
+          PathExpression [106-114]
+            Identifier(query_id) [106-114]
+        SelectColumn [123-140]
+          PathExpression [123-140]
+            Identifier(api_client_header) [123-140]
+        SelectColumn [149-162]
+          PathExpression [149-162]
+            Identifier(server_region) [149-162]
+        SelectColumn [171-179]
+          PathExpression [171-179]
+            Identifier(priority) [171-179]
+        SelectColumn [188-204]
+          PathExpression [188-204]
+            Identifier(transaction_type) [188-204]
+      FromClause [205-243]
+        TablePathExpression [210-243]
+          PathExpression [210-243]
+            Identifier(spanner_sys) [210-221]
+            Identifier(oldest_active_queries) [222-243]
+    OrderBy [244-267]
+      OrderingExpression(ASC EXPLICITLY) [253-267]
+        PathExpression [253-263]
+          Identifier(start_time) [253-263]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/oldest-active-queries#listing_the_top_2_oldest_running_queries
+SELECT start_time,
+       text_fingerprint,
+       text,
+       text_truncated,
+       session_id
+FROM spanner_sys.oldest_active_queries
+ORDER BY start_time ASC LIMIT 2
+--
+QueryStatement [0-168]
+  Query [0-168]
+    Select [0-136]
+      SelectList [7-97]
+        SelectColumn [7-17]
+          PathExpression [7-17]
+            Identifier(start_time) [7-17]
+        SelectColumn [26-42]
+          PathExpression [26-42]
+            Identifier(text_fingerprint) [26-42]
+        SelectColumn [51-55]
+          PathExpression [51-55]
+            Identifier(text) [51-55]
+        SelectColumn [64-78]
+          PathExpression [64-78]
+            Identifier(text_truncated) [64-78]
+        SelectColumn [87-97]
+          PathExpression [87-97]
+            Identifier(session_id) [87-97]
+      FromClause [98-136]
+        TablePathExpression [103-136]
+          PathExpression [103-136]
+            Identifier(spanner_sys) [103-114]
+            Identifier(oldest_active_queries) [115-136]
+    OrderBy [137-160]
+      OrderingExpression(ASC EXPLICITLY) [146-160]
+        PathExpression [146-156]
+          Identifier(start_time) [146-156]
+    LimitOffset [161-168]
+      Limit [161-168]
+        IntLiteral(2) [167-168]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/oldest-active-queries#retrieve_a_summary_of_active_queries
+SELECT active_count,
+       oldest_start_time,
+       count_older_than_1s,
+       count_older_than_10s,
+       count_older_than_100s
+FROM spanner_sys.active_queries_summary
+--
+QueryStatement [0-172]
+  Query [0-172]
+    Select [0-172]
+      SelectList [7-132]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(active_count) [7-19]
+        SelectColumn [28-45]
+          PathExpression [28-45]
+            Identifier(oldest_start_time) [28-45]
+        SelectColumn [54-73]
+          PathExpression [54-73]
+            Identifier(count_older_than_1s) [54-73]
+        SelectColumn [82-102]
+          PathExpression [82-102]
+            Identifier(count_older_than_10s) [82-102]
+        SelectColumn [111-132]
+          PathExpression [111-132]
+            Identifier(count_older_than_100s) [111-132]
+      FromClause [133-172]
+        TablePathExpression [138-172]
+          PathExpression [138-172]
+            Identifier(spanner_sys) [138-149]
+            Identifier(active_queries_summary) [150-172]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/oldest-active-queries#retrieve_a_list_of_active_queries
+SELECT start_time,
+       text_fingerprint,
+       text,
+       text_truncated,
+       session_id,
+       query_id
+FROM spanner_sys.oldest_active_queries
+ORDER BY start_time ASC LIMIT 5
+--
+QueryStatement [0-185]
+  Query [0-185]
+    Select [0-153]
+      SelectList [7-114]
+        SelectColumn [7-17]
+          PathExpression [7-17]
+            Identifier(start_time) [7-17]
+        SelectColumn [26-42]
+          PathExpression [26-42]
+            Identifier(text_fingerprint) [26-42]
+        SelectColumn [51-55]
+          PathExpression [51-55]
+            Identifier(text) [51-55]
+        SelectColumn [64-78]
+          PathExpression [64-78]
+            Identifier(text_truncated) [64-78]
+        SelectColumn [87-97]
+          PathExpression [87-97]
+            Identifier(session_id) [87-97]
+        SelectColumn [106-114]
+          PathExpression [106-114]
+            Identifier(query_id) [106-114]
+      FromClause [115-153]
+        TablePathExpression [120-153]
+          PathExpression [120-153]
+            Identifier(spanner_sys) [120-131]
+            Identifier(oldest_active_queries) [132-153]
+    OrderBy [154-177]
+      OrderingExpression(ASC EXPLICITLY) [163-177]
+        PathExpression [163-173]
+          Identifier(start_time) [163-173]
+    LimitOffset [178-185]
+      Limit [178-185]
+        IntLiteral(5) [184-185]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/oldest-active-queries#cancel_an_expensive_query
+CALL cancel_query(query_id)
+--
+CallStatement [0-27]
+  PathExpression [5-17]
+    Identifier(cancel_query) [5-17]
+  TVFArgument [18-26]
+    PathExpression [18-26]
+      Identifier(query_id) [18-26]
diff --git a/parser/testdata/spanner/guides/introspection.query-statistics.test b/parser/testdata/spanner/guides/introspection.query-statistics.test
new file mode 100644
index 0000000..eff117a
--- /dev/null
+++ b/parser/testdata/spanner/guides/introspection.query-statistics.test
@@ -0,0 +1,1243 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/introspection/query-statistics
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/introspection/query-statistics#list_the_basic_statistics_for_each_query_in_a_given_time_period
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT text,
+       request_tag,
+       interval_end,
+       execution_count,
+       avg_latency_seconds,
+       avg_rows,
+       avg_bytes,
+       avg_rows_scanned,
+       avg_cpu_seconds
+FROM spanner_sys.query_stats_top_minute
+ORDER BY interval_end DESC
+--
+QueryStatement [0-255]
+  Query [0-255]
+    Select [0-228]
+      SelectList [7-188]
+        SelectColumn [7-11]
+          PathExpression [7-11]
+            Identifier(text) [7-11]
+        SelectColumn [20-31]
+          PathExpression [20-31]
+            Identifier(request_tag) [20-31]
+        SelectColumn [40-52]
+          PathExpression [40-52]
+            Identifier(interval_end) [40-52]
+        SelectColumn [61-76]
+          PathExpression [61-76]
+            Identifier(execution_count) [61-76]
+        SelectColumn [85-104]
+          PathExpression [85-104]
+            Identifier(avg_latency_seconds) [85-104]
+        SelectColumn [113-121]
+          PathExpression [113-121]
+            Identifier(avg_rows) [113-121]
+        SelectColumn [130-139]
+          PathExpression [130-139]
+            Identifier(avg_bytes) [130-139]
+        SelectColumn [148-164]
+          PathExpression [148-164]
+            Identifier(avg_rows_scanned) [148-164]
+        SelectColumn [173-188]
+          PathExpression [173-188]
+            Identifier(avg_cpu_seconds) [173-188]
+      FromClause [189-228]
+        TablePathExpression [194-228]
+          PathExpression [194-228]
+            Identifier(spanner_sys) [194-205]
+            Identifier(query_stats_top_minute) [206-228]
+    OrderBy [229-255]
+      OrderingExpression(DESC) [238-255]
+        PathExpression [238-250]
+          Identifier(interval_end) [238-250]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/query-statistics#pdml-example
+SELECT text,
+       request_tag,
+       interval_end,
+       sum(execution_count) as execution_count,
+       sum(avg_rows_written*execution_count)/sum(execution_count) as avg_rows_written
+FROM spanner_sys.query_stats_top_hour
+WHERE starts_with(text, "UPDATE") AND query_type = "PARTITIONED_QUERY"
+group by text, request_tag, interval_end
+ORDER BY interval_end DESC
+--
+QueryStatement [0-364]
+  Query [0-364]
+    Select [0-337]
+      SelectList [7-187]
+        SelectColumn [7-11]
+          PathExpression [7-11]
+            Identifier(text) [7-11]
+        SelectColumn [20-31]
+          PathExpression [20-31]
+            Identifier(request_tag) [20-31]
+        SelectColumn [40-52]
+          PathExpression [40-52]
+            Identifier(interval_end) [40-52]
+        SelectColumn [61-100]
+          FunctionCall [61-81]
+            PathExpression [61-64]
+              Identifier(sum) [61-64]
+            PathExpression [65-80]
+              Identifier(execution_count) [65-80]
+          Alias [82-100]
+            Identifier(execution_count) [85-100]
+        SelectColumn [109-187]
+          BinaryExpression(/) [109-167]
+            FunctionCall [109-146]
+              PathExpression [109-112]
+                Identifier(sum) [109-112]
+              BinaryExpression(*) [113-145]
+                PathExpression [113-129]
+                  Identifier(avg_rows_written) [113-129]
+                PathExpression [130-145]
+                  Identifier(execution_count) [130-145]
+            FunctionCall [147-167]
+              PathExpression [147-150]
+                Identifier(sum) [147-150]
+              PathExpression [151-166]
+                Identifier(execution_count) [151-166]
+          Alias [168-187]
+            Identifier(avg_rows_written) [171-187]
+      FromClause [188-225]
+        TablePathExpression [193-225]
+          PathExpression [193-225]
+            Identifier(spanner_sys) [193-204]
+            Identifier(query_stats_top_hour) [205-225]
+      WhereClause [226-296]
+        AndExpr [232-296]
+          FunctionCall [232-259]
+            PathExpression [232-243]
+              Identifier(starts_with) [232-243]
+            PathExpression [244-248]
+              Identifier(text) [244-248]
+            StringLiteral [250-258]
+              StringLiteralComponent("UPDATE") [250-258]
+          BinaryExpression(=) [264-296]
+            PathExpression [264-274]
+              Identifier(query_type) [264-274]
+            StringLiteral [277-296]
+              StringLiteralComponent("PARTITIONED_QUERY") [277-296]
+      GroupBy [297-337]
+        GroupingItem [306-310]
+          PathExpression [306-310]
+            Identifier(text) [306-310]
+        GroupingItem [312-323]
+          PathExpression [312-323]
+            Identifier(request_tag) [312-323]
+        GroupingItem [325-337]
+          PathExpression [325-337]
+            Identifier(interval_end) [325-337]
+    OrderBy [338-364]
+      OrderingExpression(DESC) [347-364]
+        PathExpression [347-359]
+          Identifier(interval_end) [347-359]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/query-statistics#sql-top-queries
+SELECT text,
+       request_tag,
+       execution_count AS count,
+       avg_latency_seconds AS latency,
+       avg_cpu_seconds AS cpu,
+       execution_count * avg_cpu_seconds AS total_cpu
+FROM spanner_sys.query_stats_top_hour
+WHERE interval_end =
+  (SELECT MAX(interval_end)
+   FROM spanner_sys.query_stats_top_hour)
+ORDER BY total_cpu DESC
+--
+QueryStatement [0-342]
+  Query [0-342]
+    Select [0-318]
+      SelectList [7-189]
+        SelectColumn [7-11]
+          PathExpression [7-11]
+            Identifier(text) [7-11]
+        SelectColumn [20-31]
+          PathExpression [20-31]
+            Identifier(request_tag) [20-31]
+        SelectColumn [40-64]
+          PathExpression [40-55]
+            Identifier(execution_count) [40-55]
+          Alias [56-64]
+            Identifier(count) [59-64]
+        SelectColumn [73-103]
+          PathExpression [73-92]
+            Identifier(avg_latency_seconds) [73-92]
+          Alias [93-103]
+            Identifier(latency) [96-103]
+        SelectColumn [112-134]
+          PathExpression [112-127]
+            Identifier(avg_cpu_seconds) [112-127]
+          Alias [128-134]
+            Identifier(cpu) [131-134]
+        SelectColumn [143-189]
+          BinaryExpression(*) [143-176]
+            PathExpression [143-158]
+              Identifier(execution_count) [143-158]
+            PathExpression [161-176]
+              Identifier(avg_cpu_seconds) [161-176]
+          Alias [177-189]
+            Identifier(total_cpu) [180-189]
+      FromClause [190-227]
+        TablePathExpression [195-227]
+          PathExpression [195-227]
+            Identifier(spanner_sys) [195-206]
+            Identifier(query_stats_top_hour) [207-227]
+      WhereClause [228-318]
+        BinaryExpression(=) [234-318]
+          PathExpression [234-246]
+            Identifier(interval_end) [234-246]
+          ExpressionSubquery [251-318]
+            Query [252-317]
+              Select [252-317]
+                SelectList [259-276]
+                  SelectColumn [259-276]
+                    FunctionCall [259-276]
+                      PathExpression [259-262]
+                        Identifier(MAX) [259-262]
+                      PathExpression [263-275]
+                        Identifier(interval_end) [263-275]
+                FromClause [280-317]
+                  TablePathExpression [285-317]
+                    PathExpression [285-317]
+                      Identifier(spanner_sys) [285-296]
+                      Identifier(query_stats_top_hour) [297-317]
+    OrderBy [319-342]
+      OrderingExpression(DESC) [328-342]
+        PathExpression [328-337]
+          Identifier(total_cpu) [328-337]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/query-statistics#sql-execution-count
+SELECT interval_end,
+       execution_count
+FROM spanner_sys.query_stats_total_minute
+WHERE interval_end =
+  (SELECT MAX(interval_end)
+   FROM spanner_sys.query_stats_top_minute)
+--
+QueryStatement [0-178]
+  Query [0-178]
+    Select [0-178]
+      SelectList [7-43]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(interval_end) [7-19]
+        SelectColumn [28-43]
+          PathExpression [28-43]
+            Identifier(execution_count) [28-43]
+      FromClause [44-85]
+        TablePathExpression [49-85]
+          PathExpression [49-85]
+            Identifier(spanner_sys) [49-60]
+            Identifier(query_stats_total_minute) [61-85]
+      WhereClause [86-178]
+        BinaryExpression(=) [92-178]
+          PathExpression [92-104]
+            Identifier(interval_end) [92-104]
+          ExpressionSubquery [109-178]
+            Query [110-177]
+              Select [110-177]
+                SelectList [117-134]
+                  SelectColumn [117-134]
+                    FunctionCall [117-134]
+                      PathExpression [117-120]
+                        Identifier(MAX) [117-120]
+                      PathExpression [121-133]
+                        Identifier(interval_end) [121-133]
+                FromClause [138-177]
+                  TablePathExpression [143-177]
+                    PathExpression [143-177]
+                      Identifier(spanner_sys) [143-154]
+                      Identifier(query_stats_top_minute) [155-177]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/query-statistics#sql-latency
+SELECT avg_latency_seconds
+FROM spanner_sys.query_stats_top_hour
+WHERE text LIKE "SELECT x FROM table WHERE x=@foo;"
+--
+QueryStatement [0-116]
+  Query [0-116]
+    Select [0-116]
+      SelectList [7-26]
+        SelectColumn [7-26]
+          PathExpression [7-26]
+            Identifier(avg_latency_seconds) [7-26]
+      FromClause [27-64]
+        TablePathExpression [32-64]
+          PathExpression [32-64]
+            Identifier(spanner_sys) [32-43]
+            Identifier(query_stats_top_hour) [44-64]
+      WhereClause [65-116]
+        BinaryExpression(LIKE) [71-116]
+          PathExpression [71-75]
+            Identifier(text) [71-75]
+          StringLiteral [81-116]
+            StringLiteralComponent("SELECT x FROM table WHERE x=@foo;") [81-116]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/query-statistics#example-percentile-latency
+SELECT interval_end, avg_latency_seconds, SPANNER_SYS.DISTRIBUTION_PERCENTILE(latency_distribution[OFFSET(0)], 99.0)
+  AS percentile_latency
+FROM spanner_sys.query_stats_total_10minute
+WHERE interval_end =
+  (SELECT MAX(interval_end)
+   FROM spanner_sys.query_stats_total_10minute)
+ORDER BY interval_end
+--
+QueryStatement [0-303]
+  Query [0-303]
+    Select [0-281]
+      SelectList [7-140]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(interval_end) [7-19]
+        SelectColumn [21-40]
+          PathExpression [21-40]
+            Identifier(avg_latency_seconds) [21-40]
+        SelectColumn [42-140]
+          FunctionCall [42-116]
+            PathExpression [42-77]
+              Identifier(SPANNER_SYS) [42-53]
+              Identifier(DISTRIBUTION_PERCENTILE) [54-77]
+            ArrayElement [78-109]
+              PathExpression [78-98]
+                Identifier(latency_distribution) [78-98]
+              Location [98-99]
+              FunctionCall [99-108]
+                PathExpression [99-105]
+                  Identifier(OFFSET) [99-105]
+                IntLiteral(0) [106-107]
+            FloatLiteral(99.0) [111-115]
+          Alias [119-140]
+            Identifier(percentile_latency) [122-140]
+      FromClause [141-184]
+        TablePathExpression [146-184]
+          PathExpression [146-184]
+            Identifier(spanner_sys) [146-157]
+            Identifier(query_stats_total_10minute) [158-184]
+      WhereClause [185-281]
+        BinaryExpression(=) [191-281]
+          PathExpression [191-203]
+            Identifier(interval_end) [191-203]
+          ExpressionSubquery [208-281]
+            Query [209-280]
+              Select [209-280]
+                SelectList [216-233]
+                  SelectColumn [216-233]
+                    FunctionCall [216-233]
+                      PathExpression [216-219]
+                        Identifier(MAX) [216-219]
+                      PathExpression [220-232]
+                        Identifier(interval_end) [220-232]
+                FromClause [237-280]
+                  TablePathExpression [242-280]
+                    PathExpression [242-280]
+                      Identifier(spanner_sys) [242-253]
+                      Identifier(query_stats_total_10minute) [254-280]
+    OrderBy [282-303]
+      OrderingExpression(ASC) [291-303]
+        PathExpression [291-303]
+          Identifier(interval_end) [291-303]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/query-statistics#example-percentile-latency
+SELECT interval_end, avg_latency_seconds, SPANNER_SYS.DISTRIBUTION_PERCENTILE(latency_distribution_json_string, 99.0)
+  AS percentile_latency
+FROM spanner_sys.query_stats_total_10minute
+WHERE interval_end =
+  (SELECT MAX(interval_end)
+   FROM spanner_sys.query_stats_total_10minute)
+ORDER BY interval_end
+--
+QueryStatement [0-304]
+  Query [0-304]
+    Select [0-282]
+      SelectList [7-141]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(interval_end) [7-19]
+        SelectColumn [21-40]
+          PathExpression [21-40]
+            Identifier(avg_latency_seconds) [21-40]
+        SelectColumn [42-141]
+          FunctionCall [42-117]
+            PathExpression [42-77]
+              Identifier(SPANNER_SYS) [42-53]
+              Identifier(DISTRIBUTION_PERCENTILE) [54-77]
+            PathExpression [78-110]
+              Identifier(latency_distribution_json_string) [78-110]
+            FloatLiteral(99.0) [112-116]
+          Alias [120-141]
+            Identifier(percentile_latency) [123-141]
+      FromClause [142-185]
+        TablePathExpression [147-185]
+          PathExpression [147-185]
+            Identifier(spanner_sys) [147-158]
+            Identifier(query_stats_total_10minute) [159-185]
+      WhereClause [186-282]
+        BinaryExpression(=) [192-282]
+          PathExpression [192-204]
+            Identifier(interval_end) [192-204]
+          ExpressionSubquery [209-282]
+            Query [210-281]
+              Select [210-281]
+                SelectList [217-234]
+                  SelectColumn [217-234]
+                    FunctionCall [217-234]
+                      PathExpression [217-220]
+                        Identifier(MAX) [217-220]
+                      PathExpression [221-233]
+                        Identifier(interval_end) [221-233]
+                FromClause [238-281]
+                  TablePathExpression [243-281]
+                    PathExpression [243-281]
+                      Identifier(spanner_sys) [243-254]
+                      Identifier(query_stats_total_10minute) [255-281]
+    OrderBy [283-304]
+      OrderingExpression(ASC) [292-304]
+        PathExpression [292-304]
+          Identifier(interval_end) [292-304]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/query-statistics#sql-scan-data
+SELECT text,
+       execution_count,
+       avg_rows_scanned
+FROM spanner_sys.query_stats_top_hour
+WHERE interval_end =
+  (SELECT MAX(interval_end)
+   FROM spanner_sys.query_stats_top_hour)
+ORDER BY avg_rows_scanned DESC
+--
+QueryStatement [0-220]
+  Query [0-220]
+    Select [0-189]
+      SelectList [7-60]
+        SelectColumn [7-11]
+          PathExpression [7-11]
+            Identifier(text) [7-11]
+        SelectColumn [20-35]
+          PathExpression [20-35]
+            Identifier(execution_count) [20-35]
+        SelectColumn [44-60]
+          PathExpression [44-60]
+            Identifier(avg_rows_scanned) [44-60]
+      FromClause [61-98]
+        TablePathExpression [66-98]
+          PathExpression [66-98]
+            Identifier(spanner_sys) [66-77]
+            Identifier(query_stats_top_hour) [78-98]
+      WhereClause [99-189]
+        BinaryExpression(=) [105-189]
+          PathExpression [105-117]
+            Identifier(interval_end) [105-117]
+          ExpressionSubquery [122-189]
+            Query [123-188]
+              Select [123-188]
+                SelectList [130-147]
+                  SelectColumn [130-147]
+                    FunctionCall [130-147]
+                      PathExpression [130-133]
+                        Identifier(MAX) [130-133]
+                      PathExpression [134-146]
+                        Identifier(interval_end) [134-146]
+                FromClause [151-188]
+                  TablePathExpression [156-188]
+                    PathExpression [156-188]
+                      Identifier(spanner_sys) [156-167]
+                      Identifier(query_stats_top_hour) [168-188]
+    OrderBy [190-220]
+      OrderingExpression(DESC) [199-220]
+        PathExpression [199-215]
+          Identifier(avg_rows_scanned) [199-215]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/query-statistics#sql-data-written
+SELECT text,
+       execution_count,
+       avg_rows_written
+FROM spanner_sys.query_stats_top_hour
+WHERE interval_end =
+  (SELECT MAX(interval_end)
+   FROM spanner_sys.query_stats_top_hour)
+ORDER BY avg_rows_written DESC
+--
+QueryStatement [0-220]
+  Query [0-220]
+    Select [0-189]
+      SelectList [7-60]
+        SelectColumn [7-11]
+          PathExpression [7-11]
+            Identifier(text) [7-11]
+        SelectColumn [20-35]
+          PathExpression [20-35]
+            Identifier(execution_count) [20-35]
+        SelectColumn [44-60]
+          PathExpression [44-60]
+            Identifier(avg_rows_written) [44-60]
+      FromClause [61-98]
+        TablePathExpression [66-98]
+          PathExpression [66-98]
+            Identifier(spanner_sys) [66-77]
+            Identifier(query_stats_top_hour) [78-98]
+      WhereClause [99-189]
+        BinaryExpression(=) [105-189]
+          PathExpression [105-117]
+            Identifier(interval_end) [105-117]
+          ExpressionSubquery [122-189]
+            Query [123-188]
+              Select [123-188]
+                SelectList [130-147]
+                  SelectColumn [130-147]
+                    FunctionCall [130-147]
+                      PathExpression [130-133]
+                        Identifier(MAX) [130-133]
+                      PathExpression [134-146]
+                        Identifier(interval_end) [134-146]
+                FromClause [151-188]
+                  TablePathExpression [156-188]
+                    PathExpression [156-188]
+                      Identifier(spanner_sys) [156-167]
+                      Identifier(query_stats_top_hour) [168-188]
+    OrderBy [190-220]
+      OrderingExpression(DESC) [199-220]
+        PathExpression [199-215]
+          Identifier(avg_rows_written) [199-215]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/query-statistics#sql-total-cpu
+SELECT (avg_cpu_seconds * execution_count / 60 / 60)
+  AS total_cpu_hours
+FROM spanner_sys.query_stats_total_hour
+WHERE interval_end =
+  (SELECT MAX(interval_end)
+   FROM spanner_sys.query_stats_total_hour)
+--
+QueryStatement [0-206]
+  Query [0-206]
+    Select [0-206]
+      SelectList [7-73]
+        SelectColumn [7-73]
+          BinaryExpression(/) [8-51]
+            BinaryExpression(/) [8-46]
+              BinaryExpression(*) [8-41]
+                PathExpression [8-23]
+                  Identifier(avg_cpu_seconds) [8-23]
+                PathExpression [26-41]
+                  Identifier(execution_count) [26-41]
+              IntLiteral(60) [44-46]
+            IntLiteral(60) [49-51]
+          Alias [55-73]
+            Identifier(total_cpu_hours) [58-73]
+      FromClause [74-113]
+        TablePathExpression [79-113]
+          PathExpression [79-113]
+            Identifier(spanner_sys) [79-90]
+            Identifier(query_stats_total_hour) [91-113]
+      WhereClause [114-206]
+        BinaryExpression(=) [120-206]
+          PathExpression [120-132]
+            Identifier(interval_end) [120-132]
+          ExpressionSubquery [137-206]
+            Query [138-205]
+              Select [138-205]
+                SelectList [145-162]
+                  SelectColumn [145-162]
+                    FunctionCall [145-162]
+                      PathExpression [145-148]
+                        Identifier(MAX) [145-148]
+                      PathExpression [149-161]
+                        Identifier(interval_end) [149-161]
+                FromClause [166-205]
+                  TablePathExpression [171-205]
+                    PathExpression [171-205]
+                      Identifier(spanner_sys) [171-182]
+                      Identifier(query_stats_total_hour) [183-205]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/query-statistics#list_the_queries_that_have_failed_in_a_given_time_period
+SELECT text,
+       request_tag,
+       interval_end,
+       execution_count,
+       all_failed_execution_count,
+       all_failed_avg_latency_seconds,
+       avg_latency_seconds,
+       avg_rows,
+       avg_bytes,
+       avg_rows_scanned,
+       avg_cpu_seconds
+FROM spanner_sys.query_stats_top_minute
+WHERE all_failed_execution_count > 0
+ORDER BY interval_end
+--
+QueryStatement [0-361]
+  Query [0-361]
+    Select [0-339]
+      SelectList [7-262]
+        SelectColumn [7-11]
+          PathExpression [7-11]
+            Identifier(text) [7-11]
+        SelectColumn [20-31]
+          PathExpression [20-31]
+            Identifier(request_tag) [20-31]
+        SelectColumn [40-52]
+          PathExpression [40-52]
+            Identifier(interval_end) [40-52]
+        SelectColumn [61-76]
+          PathExpression [61-76]
+            Identifier(execution_count) [61-76]
+        SelectColumn [85-111]
+          PathExpression [85-111]
+            Identifier(all_failed_execution_count) [85-111]
+        SelectColumn [120-150]
+          PathExpression [120-150]
+            Identifier(all_failed_avg_latency_seconds) [120-150]
+        SelectColumn [159-178]
+          PathExpression [159-178]
+            Identifier(avg_latency_seconds) [159-178]
+        SelectColumn [187-195]
+          PathExpression [187-195]
+            Identifier(avg_rows) [187-195]
+        SelectColumn [204-213]
+          PathExpression [204-213]
+            Identifier(avg_bytes) [204-213]
+        SelectColumn [222-238]
+          PathExpression [222-238]
+            Identifier(avg_rows_scanned) [222-238]
+        SelectColumn [247-262]
+          PathExpression [247-262]
+            Identifier(avg_cpu_seconds) [247-262]
+      FromClause [263-302]
+        TablePathExpression [268-302]
+          PathExpression [268-302]
+            Identifier(spanner_sys) [268-279]
+            Identifier(query_stats_top_minute) [280-302]
+      WhereClause [303-339]
+        BinaryExpression(>) [309-339]
+          PathExpression [309-335]
+            Identifier(all_failed_execution_count) [309-335]
+          IntLiteral(0) [338-339]
+    OrderBy [340-361]
+      OrderingExpression(ASC) [349-361]
+        PathExpression [349-361]
+          Identifier(interval_end) [349-361]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/query-statistics#find_the_total_error_count_in_a_given_time_period
+SELECT interval_end,
+       all_failed_execution_count
+FROM spanner_sys.query_stats_total_minute
+WHERE interval_end =
+  (SELECT MAX(interval_end)
+   FROM spanner_sys.query_stats_top_minute)
+ORDER BY interval_end
+--
+QueryStatement [0-211]
+  Query [0-211]
+    Select [0-189]
+      SelectList [7-54]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(interval_end) [7-19]
+        SelectColumn [28-54]
+          PathExpression [28-54]
+            Identifier(all_failed_execution_count) [28-54]
+      FromClause [55-96]
+        TablePathExpression [60-96]
+          PathExpression [60-96]
+            Identifier(spanner_sys) [60-71]
+            Identifier(query_stats_total_minute) [72-96]
+      WhereClause [97-189]
+        BinaryExpression(=) [103-189]
+          PathExpression [103-115]
+            Identifier(interval_end) [103-115]
+          ExpressionSubquery [120-189]
+            Query [121-188]
+              Select [121-188]
+                SelectList [128-145]
+                  SelectColumn [128-145]
+                    FunctionCall [128-145]
+                      PathExpression [128-131]
+                        Identifier(MAX) [128-131]
+                      PathExpression [132-144]
+                        Identifier(interval_end) [132-144]
+                FromClause [149-188]
+                  TablePathExpression [154-188]
+                    PathExpression [154-188]
+                      Identifier(spanner_sys) [154-165]
+                      Identifier(query_stats_top_minute) [166-188]
+    OrderBy [190-211]
+      OrderingExpression(ASC) [199-211]
+        PathExpression [199-211]
+          Identifier(interval_end) [199-211]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/query-statistics#list_the_queries_that_timeout_the_most
+SELECT text,
+       execution_count AS count,
+       timed_out_execution_count AS timeout_count,
+       avg_latency_seconds AS latency,
+       avg_cpu_seconds AS cpu,
+       execution_count * avg_cpu_seconds AS total_cpu
+FROM spanner_sys.query_stats_top_hour
+WHERE interval_end =
+  (SELECT MAX(interval_end)
+   FROM spanner_sys.query_stats_top_hour)
+ORDER BY timed_out_execution_count DESC
+--
+QueryStatement [0-389]
+  Query [0-389]
+    Select [0-349]
+      SelectList [7-220]
+        SelectColumn [7-11]
+          PathExpression [7-11]
+            Identifier(text) [7-11]
+        SelectColumn [20-44]
+          PathExpression [20-35]
+            Identifier(execution_count) [20-35]
+          Alias [36-44]
+            Identifier(count) [39-44]
+        SelectColumn [53-95]
+          PathExpression [53-78]
+            Identifier(timed_out_execution_count) [53-78]
+          Alias [79-95]
+            Identifier(timeout_count) [82-95]
+        SelectColumn [104-134]
+          PathExpression [104-123]
+            Identifier(avg_latency_seconds) [104-123]
+          Alias [124-134]
+            Identifier(latency) [127-134]
+        SelectColumn [143-165]
+          PathExpression [143-158]
+            Identifier(avg_cpu_seconds) [143-158]
+          Alias [159-165]
+            Identifier(cpu) [162-165]
+        SelectColumn [174-220]
+          BinaryExpression(*) [174-207]
+            PathExpression [174-189]
+              Identifier(execution_count) [174-189]
+            PathExpression [192-207]
+              Identifier(avg_cpu_seconds) [192-207]
+          Alias [208-220]
+            Identifier(total_cpu) [211-220]
+      FromClause [221-258]
+        TablePathExpression [226-258]
+          PathExpression [226-258]
+            Identifier(spanner_sys) [226-237]
+            Identifier(query_stats_top_hour) [238-258]
+      WhereClause [259-349]
+        BinaryExpression(=) [265-349]
+          PathExpression [265-277]
+            Identifier(interval_end) [265-277]
+          ExpressionSubquery [282-349]
+            Query [283-348]
+              Select [283-348]
+                SelectList [290-307]
+                  SelectColumn [290-307]
+                    FunctionCall [290-307]
+                      PathExpression [290-293]
+                        Identifier(MAX) [290-293]
+                      PathExpression [294-306]
+                        Identifier(interval_end) [294-306]
+                FromClause [311-348]
+                  TablePathExpression [316-348]
+                    PathExpression [316-348]
+                      Identifier(spanner_sys) [316-327]
+                      Identifier(query_stats_top_hour) [328-348]
+    OrderBy [350-389]
+      OrderingExpression(DESC) [359-389]
+        PathExpression [359-384]
+          Identifier(timed_out_execution_count) [359-384]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/query-statistics#find_the_average_latency_of_successful_and_failed_executions_for_a_query
+SELECT avg_latency_seconds AS combined_avg_latency,
+       all_failed_avg_latency_seconds AS failed_execution_latency,
+       ( avg_latency_seconds * execution_count -
+         all_failed_avg_latency_seconds * all_failed_execution_count
+       ) / (
+       execution_count - all_failed_execution_count ) AS success_execution_latency
+FROM   spanner_sys.query_stats_top_hour
+WHERE  text LIKE "select x from table where x=@foo;"
+--
+QueryStatement [0-425]
+  Query [0-425]
+    Select [0-425]
+      SelectList [7-332]
+        SelectColumn [7-50]
+          PathExpression [7-26]
+            Identifier(avg_latency_seconds) [7-26]
+          Alias [27-50]
+            Identifier(combined_avg_latency) [30-50]
+        SelectColumn [59-117]
+          PathExpression [59-89]
+            Identifier(all_failed_avg_latency_seconds) [59-89]
+          Alias [90-117]
+            Identifier(failed_execution_latency) [93-117]
+        SelectColumn [126-332]
+          BinaryExpression(/) [126-303]
+            BinaryExpression(-) [128-236]
+              BinaryExpression(*) [128-165]
+                PathExpression [128-147]
+                  Identifier(avg_latency_seconds) [128-147]
+                PathExpression [150-165]
+                  Identifier(execution_count) [150-165]
+              BinaryExpression(*) [177-236]
+                PathExpression [177-207]
+                  Identifier(all_failed_avg_latency_seconds) [177-207]
+                PathExpression [210-236]
+                  Identifier(all_failed_execution_count) [210-236]
+            BinaryExpression(-) [257-301]
+              PathExpression [257-272]
+                Identifier(execution_count) [257-272]
+              PathExpression [275-301]
+                Identifier(all_failed_execution_count) [275-301]
+          Alias [304-332]
+            Identifier(success_execution_latency) [307-332]
+      FromClause [333-372]
+        TablePathExpression [340-372]
+          PathExpression [340-372]
+            Identifier(spanner_sys) [340-351]
+            Identifier(query_stats_top_hour) [352-372]
+      WhereClause [373-425]
+        BinaryExpression(LIKE) [380-425]
+          PathExpression [380-384]
+            Identifier(text) [380-384]
+          StringLiteral [390-425]
+            StringLiteralComponent("select x from table where x=@foo;") [390-425]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/query-statistics#gather_query_statistics_for_the_selected_time_period
+SELECT interval_end,
+       execution_count AS count,
+       ROUND(avg_latency_seconds,2) AS latency,
+       ROUND(avg_rows,2) AS rows_returned,
+       ROUND(avg_bytes,2) AS bytes,
+       ROUND(avg_rows_scanned,2) AS rows_scanned,
+       ROUND(avg_cpu_seconds,3) AS avg_cpu
+FROM spanner_sys.query_stats_total_10minute
+WHERE
+  interval_end >= "2020-07-24T16:30:00Z"
+  AND interval_end <= "2020-07-24T17:30:00Z"
+ORDER BY interval_end
+--
+QueryStatement [0-431]
+  Query [0-431]
+    Select [0-409]
+      SelectList [7-273]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(interval_end) [7-19]
+        SelectColumn [28-52]
+          PathExpression [28-43]
+            Identifier(execution_count) [28-43]
+          Alias [44-52]
+            Identifier(count) [47-52]
+        SelectColumn [61-100]
+          FunctionCall [61-89]
+            PathExpression [61-66]
+              Identifier(ROUND) [61-66]
+            PathExpression [67-86]
+              Identifier(avg_latency_seconds) [67-86]
+            IntLiteral(2) [87-88]
+          Alias [90-100]
+            Identifier(latency) [93-100]
+        SelectColumn [109-143]
+          FunctionCall [109-126]
+            PathExpression [109-114]
+              Identifier(ROUND) [109-114]
+            PathExpression [115-123]
+              Identifier(avg_rows) [115-123]
+            IntLiteral(2) [124-125]
+          Alias [127-143]
+            Identifier(rows_returned) [130-143]
+        SelectColumn [152-179]
+          FunctionCall [152-170]
+            PathExpression [152-157]
+              Identifier(ROUND) [152-157]
+            PathExpression [158-167]
+              Identifier(avg_bytes) [158-167]
+            IntLiteral(2) [168-169]
+          Alias [171-179]
+            Identifier(bytes) [174-179]
+        SelectColumn [188-229]
+          FunctionCall [188-213]
+            PathExpression [188-193]
+              Identifier(ROUND) [188-193]
+            PathExpression [194-210]
+              Identifier(avg_rows_scanned) [194-210]
+            IntLiteral(2) [211-212]
+          Alias [214-229]
+            Identifier(rows_scanned) [217-229]
+        SelectColumn [238-273]
+          FunctionCall [238-262]
+            PathExpression [238-243]
+              Identifier(ROUND) [238-243]
+            PathExpression [244-259]
+              Identifier(avg_cpu_seconds) [244-259]
+            IntLiteral(3) [260-261]
+          Alias [263-273]
+            Identifier(avg_cpu) [266-273]
+      FromClause [274-317]
+        TablePathExpression [279-317]
+          PathExpression [279-317]
+            Identifier(spanner_sys) [279-290]
+            Identifier(query_stats_total_10minute) [291-317]
+      WhereClause [318-409]
+        AndExpr [326-409]
+          BinaryExpression(>=) [326-364]
+            PathExpression [326-338]
+              Identifier(interval_end) [326-338]
+            StringLiteral [342-364]
+              StringLiteralComponent("2020-07-24T16:30:00Z") [342-364]
+          BinaryExpression(<=) [371-409]
+            PathExpression [371-383]
+              Identifier(interval_end) [371-383]
+            StringLiteral [387-409]
+              StringLiteralComponent("2020-07-24T17:30:00Z") [387-409]
+    OrderBy [410-431]
+      OrderingExpression(ASC) [419-431]
+        PathExpression [419-431]
+          Identifier(interval_end) [419-431]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/query-statistics#find_the_queries_that_are_causing_high_cpu_usage
+SELECT text_fingerprint AS fingerprint,
+       execution_count AS count,
+       ROUND(avg_latency_seconds,2) AS latency,
+       ROUND(avg_cpu_seconds,3) AS cpu,
+       ROUND(execution_count * avg_cpu_seconds,3) AS total_cpu
+FROM spanner_sys.query_stats_top_10minute
+WHERE
+  interval_end = "2020-07-24T17:00:00Z"
+ORDER BY total_cpu DESC
+--
+QueryStatement [0-335]
+  Query [0-335]
+    Select [0-311]
+      SelectList [7-223]
+        SelectColumn [7-38]
+          PathExpression [7-23]
+            Identifier(text_fingerprint) [7-23]
+          Alias [24-38]
+            Identifier(fingerprint) [27-38]
+        SelectColumn [47-71]
+          PathExpression [47-62]
+            Identifier(execution_count) [47-62]
+          Alias [63-71]
+            Identifier(count) [66-71]
+        SelectColumn [80-119]
+          FunctionCall [80-108]
+            PathExpression [80-85]
+              Identifier(ROUND) [80-85]
+            PathExpression [86-105]
+              Identifier(avg_latency_seconds) [86-105]
+            IntLiteral(2) [106-107]
+          Alias [109-119]
+            Identifier(latency) [112-119]
+        SelectColumn [128-159]
+          FunctionCall [128-152]
+            PathExpression [128-133]
+              Identifier(ROUND) [128-133]
+            PathExpression [134-149]
+              Identifier(avg_cpu_seconds) [134-149]
+            IntLiteral(3) [150-151]
+          Alias [153-159]
+            Identifier(cpu) [156-159]
+        SelectColumn [168-223]
+          FunctionCall [168-210]
+            PathExpression [168-173]
+              Identifier(ROUND) [168-173]
+            BinaryExpression(*) [174-207]
+              PathExpression [174-189]
+                Identifier(execution_count) [174-189]
+              PathExpression [192-207]
+                Identifier(avg_cpu_seconds) [192-207]
+            IntLiteral(3) [208-209]
+          Alias [211-223]
+            Identifier(total_cpu) [214-223]
+      FromClause [224-265]
+        TablePathExpression [229-265]
+          PathExpression [229-265]
+            Identifier(spanner_sys) [229-240]
+            Identifier(query_stats_top_10minute) [241-265]
+      WhereClause [266-311]
+        BinaryExpression(=) [274-311]
+          PathExpression [274-286]
+            Identifier(interval_end) [274-286]
+          StringLiteral [289-311]
+            StringLiteralComponent("2020-07-24T17:00:00Z") [289-311]
+    OrderBy [312-335]
+      OrderingExpression(DESC) [321-335]
+        PathExpression [321-330]
+          Identifier(total_cpu) [321-330]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/query-statistics#compare_query_runs_over_time
+SELECT interval_end,
+       ROUND(avg_latency_seconds,2) AS latency,
+       avg_rows AS rows_returned,
+       avg_bytes AS bytes_returned,
+       avg_rows_scanned AS rows_scanned,
+       ROUND(avg_cpu_seconds,3) AS cpu,
+FROM spanner_sys.query_stats_top_minute
+WHERE text_fingerprint = 5505124206529314852
+ORDER BY interval_end DESC
+--
+QueryStatement [0-331]
+  Query [0-331]
+    Select [0-304]
+      SelectList [7-219]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(interval_end) [7-19]
+        SelectColumn [28-67]
+          FunctionCall [28-56]
+            PathExpression [28-33]
+              Identifier(ROUND) [28-33]
+            PathExpression [34-53]
+              Identifier(avg_latency_seconds) [34-53]
+            IntLiteral(2) [54-55]
+          Alias [57-67]
+            Identifier(latency) [60-67]
+        SelectColumn [76-101]
+          PathExpression [76-84]
+            Identifier(avg_rows) [76-84]
+          Alias [85-101]
+            Identifier(rows_returned) [88-101]
+        SelectColumn [110-137]
+          PathExpression [110-119]
+            Identifier(avg_bytes) [110-119]
+          Alias [120-137]
+            Identifier(bytes_returned) [123-137]
+        SelectColumn [146-178]
+          PathExpression [146-162]
+            Identifier(avg_rows_scanned) [146-162]
+          Alias [163-178]
+            Identifier(rows_scanned) [166-178]
+        SelectColumn [187-218]
+          FunctionCall [187-211]
+            PathExpression [187-192]
+              Identifier(ROUND) [187-192]
+            PathExpression [193-208]
+              Identifier(avg_cpu_seconds) [193-208]
+            IntLiteral(3) [209-210]
+          Alias [212-218]
+            Identifier(cpu) [215-218]
+      FromClause [220-259]
+        TablePathExpression [225-259]
+          PathExpression [225-259]
+            Identifier(spanner_sys) [225-236]
+            Identifier(query_stats_top_minute) [237-259]
+      WhereClause [260-304]
+        BinaryExpression(=) [266-304]
+          PathExpression [266-282]
+            Identifier(text_fingerprint) [266-282]
+          IntLiteral(5505124206529314852) [285-304]
+    OrderBy [305-331]
+      OrderingExpression(DESC) [314-331]
+        PathExpression [314-326]
+          Identifier(interval_end) [314-326]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/query-statistics#compare_query_runs_over_time
+SELECT text,
+       text_truncated
+FROM spanner_sys.query_stats_top_hour
+WHERE text_fingerprint = 5505124206529314852
+LIMIT 1
+--
+QueryStatement [0-125]
+  Query [0-125]
+    Select [0-117]
+      SelectList [7-34]
+        SelectColumn [7-11]
+          PathExpression [7-11]
+            Identifier(text) [7-11]
+        SelectColumn [20-34]
+          PathExpression [20-34]
+            Identifier(text_truncated) [20-34]
+      FromClause [35-72]
+        TablePathExpression [40-72]
+          PathExpression [40-72]
+            Identifier(spanner_sys) [40-51]
+            Identifier(query_stats_top_hour) [52-72]
+      WhereClause [73-117]
+        BinaryExpression(=) [79-117]
+          PathExpression [79-95]
+            Identifier(text_fingerprint) [79-95]
+          IntLiteral(5505124206529314852) [98-117]
+    LimitOffset [118-125]
+      Limit [118-125]
+        IntLiteral(1) [124-125]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/query-statistics#investigate_failed_queries
+SELECT interval_end,
+       all_failed_execution_count AS failed_count,
+       all_failed_avg_latency_seconds AS latency
+FROM spanner_sys.query_stats_total_minute
+WHERE
+  interval_end >= "2020-07-24T16:50:00Z"
+  AND interval_end <= "2020-07-24T17:00:00Z"
+ORDER BY interval_end
+--
+QueryStatement [0-276]
+  Query [0-276]
+    Select [0-254]
+      SelectList [7-120]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(interval_end) [7-19]
+        SelectColumn [28-70]
+          PathExpression [28-54]
+            Identifier(all_failed_execution_count) [28-54]
+          Alias [55-70]
+            Identifier(failed_count) [58-70]
+        SelectColumn [79-120]
+          PathExpression [79-109]
+            Identifier(all_failed_avg_latency_seconds) [79-109]
+          Alias [110-120]
+            Identifier(latency) [113-120]
+      FromClause [121-162]
+        TablePathExpression [126-162]
+          PathExpression [126-162]
+            Identifier(spanner_sys) [126-137]
+            Identifier(query_stats_total_minute) [138-162]
+      WhereClause [163-254]
+        AndExpr [171-254]
+          BinaryExpression(>=) [171-209]
+            PathExpression [171-183]
+              Identifier(interval_end) [171-183]
+            StringLiteral [187-209]
+              StringLiteralComponent("2020-07-24T16:50:00Z") [187-209]
+          BinaryExpression(<=) [216-254]
+            PathExpression [216-228]
+              Identifier(interval_end) [216-228]
+            StringLiteral [232-254]
+              StringLiteralComponent("2020-07-24T17:00:00Z") [232-254]
+    OrderBy [255-276]
+      OrderingExpression(ASC) [264-276]
+        PathExpression [264-276]
+          Identifier(interval_end) [264-276]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/query-statistics#investigate_failed_queries
+SELECT interval_end,
+       text_fingerprint,
+       execution_count,
+       avg_latency_seconds AS avg_latency,
+       all_failed_execution_count AS failed_count,
+       all_failed_avg_latency_seconds AS failed_latency,
+       cancelled_or_disconnected_execution_count AS cancel_count,
+       timed_out_execution_count AS to_count
+FROM spanner_sys.query_stats_top_minute
+WHERE all_failed_execution_count > 0
+ORDER BY interval_end
+--
+QueryStatement [0-430]
+  Query [0-430]
+    Select [0-408]
+      SelectList [7-331]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(interval_end) [7-19]
+        SelectColumn [28-44]
+          PathExpression [28-44]
+            Identifier(text_fingerprint) [28-44]
+        SelectColumn [53-68]
+          PathExpression [53-68]
+            Identifier(execution_count) [53-68]
+        SelectColumn [77-111]
+          PathExpression [77-96]
+            Identifier(avg_latency_seconds) [77-96]
+          Alias [97-111]
+            Identifier(avg_latency) [100-111]
+        SelectColumn [120-162]
+          PathExpression [120-146]
+            Identifier(all_failed_execution_count) [120-146]
+          Alias [147-162]
+            Identifier(failed_count) [150-162]
+        SelectColumn [171-219]
+          PathExpression [171-201]
+            Identifier(all_failed_avg_latency_seconds) [171-201]
+          Alias [202-219]
+            Identifier(failed_latency) [205-219]
+        SelectColumn [228-285]
+          PathExpression [228-269]
+            Identifier(cancelled_or_disconnected_execution_count) [228-269]
+          Alias [270-285]
+            Identifier(cancel_count) [273-285]
+        SelectColumn [294-331]
+          PathExpression [294-319]
+            Identifier(timed_out_execution_count) [294-319]
+          Alias [320-331]
+            Identifier(to_count) [323-331]
+      FromClause [332-371]
+        TablePathExpression [337-371]
+          PathExpression [337-371]
+            Identifier(spanner_sys) [337-348]
+            Identifier(query_stats_top_minute) [349-371]
+      WhereClause [372-408]
+        BinaryExpression(>) [378-408]
+          PathExpression [378-404]
+            Identifier(all_failed_execution_count) [378-404]
+          IntLiteral(0) [407-408]
+    OrderBy [409-430]
+      OrderingExpression(ASC) [418-430]
+        PathExpression [418-430]
+          Identifier(interval_end) [418-430]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/query-statistics#investigate_failed_queries
+SELECT interval_end,
+       avg_latency_seconds AS combined_avg_latency,
+       all_failed_avg_latency_seconds AS failed_execution_latency,
+       ( avg_latency_seconds * execution_count -
+         all_failed_avg_latency_seconds * all_failed_execution_count
+       ) / (
+       execution_count - all_failed_execution_count ) AS success_execution_latency
+FROM   spanner_sys.query_stats_top_hour
+WHERE  text_fingerprint = 5505124206529314852
+--
+QueryStatement [0-439]
+  Query [0-439]
+    Select [0-439]
+      SelectList [7-353]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(interval_end) [7-19]
+        SelectColumn [28-71]
+          PathExpression [28-47]
+            Identifier(avg_latency_seconds) [28-47]
+          Alias [48-71]
+            Identifier(combined_avg_latency) [51-71]
+        SelectColumn [80-138]
+          PathExpression [80-110]
+            Identifier(all_failed_avg_latency_seconds) [80-110]
+          Alias [111-138]
+            Identifier(failed_execution_latency) [114-138]
+        SelectColumn [147-353]
+          BinaryExpression(/) [147-324]
+            BinaryExpression(-) [149-257]
+              BinaryExpression(*) [149-186]
+                PathExpression [149-168]
+                  Identifier(avg_latency_seconds) [149-168]
+                PathExpression [171-186]
+                  Identifier(execution_count) [171-186]
+              BinaryExpression(*) [198-257]
+                PathExpression [198-228]
+                  Identifier(all_failed_avg_latency_seconds) [198-228]
+                PathExpression [231-257]
+                  Identifier(all_failed_execution_count) [231-257]
+            BinaryExpression(-) [278-322]
+              PathExpression [278-293]
+                Identifier(execution_count) [278-293]
+              PathExpression [296-322]
+                Identifier(all_failed_execution_count) [296-322]
+          Alias [325-353]
+            Identifier(success_execution_latency) [328-353]
+      FromClause [354-393]
+        TablePathExpression [361-393]
+          PathExpression [361-393]
+            Identifier(spanner_sys) [361-372]
+            Identifier(query_stats_top_hour) [373-393]
+      WhereClause [394-439]
+        BinaryExpression(=) [401-439]
+          PathExpression [401-417]
+            Identifier(text_fingerprint) [401-417]
+          IntLiteral(5505124206529314852) [420-439]
diff --git a/parser/testdata/spanner/guides/introspection.read-statistics.test b/parser/testdata/spanner/guides/introspection.read-statistics.test
new file mode 100644
index 0000000..690e7c9
--- /dev/null
+++ b/parser/testdata/spanner/guides/introspection.read-statistics.test
@@ -0,0 +1,354 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/introspection/read-statistics
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/introspection/read-statistics#list_the_basic_statistics_for_each_read_shape_in_a_given_time_period
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT fprint,
+       read_columns,
+       execution_count,
+       avg_cpu_seconds,
+       avg_rows,
+       avg_bytes,
+       avg_locking_delay_seconds,
+       avg_client_wait_seconds
+FROM spanner_sys.read_stats_top_minute
+ORDER BY interval_end DESC LIMIT 3
+--
+QueryStatement [0-257]
+  Query [0-257]
+    Select [0-222]
+      SelectList [7-183]
+        SelectColumn [7-13]
+          PathExpression [7-13]
+            Identifier(fprint) [7-13]
+        SelectColumn [22-34]
+          PathExpression [22-34]
+            Identifier(read_columns) [22-34]
+        SelectColumn [43-58]
+          PathExpression [43-58]
+            Identifier(execution_count) [43-58]
+        SelectColumn [67-82]
+          PathExpression [67-82]
+            Identifier(avg_cpu_seconds) [67-82]
+        SelectColumn [91-99]
+          PathExpression [91-99]
+            Identifier(avg_rows) [91-99]
+        SelectColumn [108-117]
+          PathExpression [108-117]
+            Identifier(avg_bytes) [108-117]
+        SelectColumn [126-151]
+          PathExpression [126-151]
+            Identifier(avg_locking_delay_seconds) [126-151]
+        SelectColumn [160-183]
+          PathExpression [160-183]
+            Identifier(avg_client_wait_seconds) [160-183]
+      FromClause [184-222]
+        TablePathExpression [189-222]
+          PathExpression [189-222]
+            Identifier(spanner_sys) [189-200]
+            Identifier(read_stats_top_minute) [201-222]
+    OrderBy [223-249]
+      OrderingExpression(DESC) [232-249]
+        PathExpression [232-244]
+          Identifier(interval_end) [232-244]
+    LimitOffset [250-257]
+      Limit [250-257]
+        IntLiteral(3) [256-257]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/read-statistics#list_the_read_shapes_ordered_by_highest_total_cpu_usage
+SELECT read_columns,
+       execution_count,
+       avg_cpu_seconds,
+       execution_count * avg_cpu_seconds AS total_cpu
+FROM spanner_sys.read_stats_top_hour
+WHERE interval_end =
+  (SELECT MAX(interval_end)
+   FROM spanner_sys.read_stats_top_hour)
+ORDER BY total_cpu DESC LIMIT 3
+--
+QueryStatement [0-281]
+  Query [0-281]
+    Select [0-249]
+      SelectList [7-122]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(read_columns) [7-19]
+        SelectColumn [28-43]
+          PathExpression [28-43]
+            Identifier(execution_count) [28-43]
+        SelectColumn [52-67]
+          PathExpression [52-67]
+            Identifier(avg_cpu_seconds) [52-67]
+        SelectColumn [76-122]
+          BinaryExpression(*) [76-109]
+            PathExpression [76-91]
+              Identifier(execution_count) [76-91]
+            PathExpression [94-109]
+              Identifier(avg_cpu_seconds) [94-109]
+          Alias [110-122]
+            Identifier(total_cpu) [113-122]
+      FromClause [123-159]
+        TablePathExpression [128-159]
+          PathExpression [128-159]
+            Identifier(spanner_sys) [128-139]
+            Identifier(read_stats_top_hour) [140-159]
+      WhereClause [160-249]
+        BinaryExpression(=) [166-249]
+          PathExpression [166-178]
+            Identifier(interval_end) [166-178]
+          ExpressionSubquery [183-249]
+            Query [184-248]
+              Select [184-248]
+                SelectList [191-208]
+                  SelectColumn [191-208]
+                    FunctionCall [191-208]
+                      PathExpression [191-194]
+                        Identifier(MAX) [191-194]
+                      PathExpression [195-207]
+                        Identifier(interval_end) [195-207]
+                FromClause [212-248]
+                  TablePathExpression [217-248]
+                    PathExpression [217-248]
+                      Identifier(spanner_sys) [217-228]
+                      Identifier(read_stats_top_hour) [229-248]
+    OrderBy [250-273]
+      OrderingExpression(DESC) [259-273]
+        PathExpression [259-268]
+          Identifier(total_cpu) [259-268]
+    LimitOffset [274-281]
+      Limit [274-281]
+        IntLiteral(3) [280-281]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/read-statistics#find_the_total_cpu_usage_across_all_read_shapes
+SELECT (avg_cpu_seconds * execution_count / 60 / 60)
+  AS total_cpu_hours
+FROM spanner_sys.read_stats_total_hour
+WHERE interval_end =
+  (SELECT MAX(interval_end)
+   FROM spanner_sys.read_stats_total_hour)
+--
+QueryStatement [0-204]
+  Query [0-204]
+    Select [0-204]
+      SelectList [7-73]
+        SelectColumn [7-73]
+          BinaryExpression(/) [8-51]
+            BinaryExpression(/) [8-46]
+              BinaryExpression(*) [8-41]
+                PathExpression [8-23]
+                  Identifier(avg_cpu_seconds) [8-23]
+                PathExpression [26-41]
+                  Identifier(execution_count) [26-41]
+              IntLiteral(60) [44-46]
+            IntLiteral(60) [49-51]
+          Alias [55-73]
+            Identifier(total_cpu_hours) [58-73]
+      FromClause [74-112]
+        TablePathExpression [79-112]
+          PathExpression [79-112]
+            Identifier(spanner_sys) [79-90]
+            Identifier(read_stats_total_hour) [91-112]
+      WhereClause [113-204]
+        BinaryExpression(=) [119-204]
+          PathExpression [119-131]
+            Identifier(interval_end) [119-131]
+          ExpressionSubquery [136-204]
+            Query [137-203]
+              Select [137-203]
+                SelectList [144-161]
+                  SelectColumn [144-161]
+                    FunctionCall [144-161]
+                      PathExpression [144-147]
+                        Identifier(MAX) [144-147]
+                      PathExpression [148-160]
+                        Identifier(interval_end) [148-160]
+                FromClause [165-203]
+                  TablePathExpression [170-203]
+                    PathExpression [170-203]
+                      Identifier(spanner_sys) [170-181]
+                      Identifier(read_stats_total_hour) [182-203]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/read-statistics#find_the_total_execution_count_in_a_given_time_period
+SELECT interval_end,
+       execution_count
+FROM spanner_sys.read_stats_total_minute
+WHERE interval_end =
+  (SELECT MAX(interval_end)
+   FROM spanner_sys.read_stats_total_minute)
+--
+QueryStatement [0-178]
+  Query [0-178]
+    Select [0-178]
+      SelectList [7-43]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(interval_end) [7-19]
+        SelectColumn [28-43]
+          PathExpression [28-43]
+            Identifier(execution_count) [28-43]
+      FromClause [44-84]
+        TablePathExpression [49-84]
+          PathExpression [49-84]
+            Identifier(spanner_sys) [49-60]
+            Identifier(read_stats_total_minute) [61-84]
+      WhereClause [85-178]
+        BinaryExpression(=) [91-178]
+          PathExpression [91-103]
+            Identifier(interval_end) [91-103]
+          ExpressionSubquery [108-178]
+            Query [109-177]
+              Select [109-177]
+                SelectList [116-133]
+                  SelectColumn [116-133]
+                    FunctionCall [116-133]
+                      PathExpression [116-119]
+                        Identifier(MAX) [116-119]
+                      PathExpression [120-132]
+                        Identifier(interval_end) [120-132]
+                FromClause [137-177]
+                  TablePathExpression [142-177]
+                    PathExpression [142-177]
+                      Identifier(spanner_sys) [142-153]
+                      Identifier(read_stats_total_minute) [154-177]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/read-statistics#gather_read_statistics_for_the_selected_time_period
+SELECT
+  interval_end,
+  ROUND(avg_cpu_seconds,4) as avg_cpu_seconds,
+  execution_count,
+  avg_locking_delay_seconds
+FROM SPANNER_SYS.READ_STATS_TOTAL_10MINUTE
+WHERE
+  interval_end >= "2020-05-28T16:30:00"
+  AND interval_end <= "2020-05-28T19:30:00"
+ORDER BY interval_end
+--
+QueryStatement [0-271]
+  Query [0-271]
+    Select [0-249]
+      SelectList [9-116]
+        SelectColumn [9-21]
+          PathExpression [9-21]
+            Identifier(interval_end) [9-21]
+        SelectColumn [25-68]
+          FunctionCall [25-49]
+            PathExpression [25-30]
+              Identifier(ROUND) [25-30]
+            PathExpression [31-46]
+              Identifier(avg_cpu_seconds) [31-46]
+            IntLiteral(4) [47-48]
+          Alias [50-68]
+            Identifier(avg_cpu_seconds) [53-68]
+        SelectColumn [72-87]
+          PathExpression [72-87]
+            Identifier(execution_count) [72-87]
+        SelectColumn [91-116]
+          PathExpression [91-116]
+            Identifier(avg_locking_delay_seconds) [91-116]
+      FromClause [117-159]
+        TablePathExpression [122-159]
+          PathExpression [122-159]
+            Identifier(SPANNER_SYS) [122-133]
+            Identifier(READ_STATS_TOTAL_10MINUTE) [134-159]
+      WhereClause [160-249]
+        AndExpr [168-249]
+          BinaryExpression(>=) [168-205]
+            PathExpression [168-180]
+              Identifier(interval_end) [168-180]
+            StringLiteral [184-205]
+              StringLiteralComponent("2020-05-28T16:30:00") [184-205]
+          BinaryExpression(<=) [212-249]
+            PathExpression [212-224]
+              Identifier(interval_end) [212-224]
+            StringLiteral [228-249]
+              StringLiteralComponent("2020-05-28T19:30:00") [228-249]
+    OrderBy [250-271]
+      OrderingExpression(ASC) [259-271]
+        PathExpression [259-271]
+          Identifier(interval_end) [259-271]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/read-statistics#find_which_read_shapes_are_causing_high_cpu_usage
+SELECT
+  read_columns,
+  ROUND(avg_cpu_seconds,4) as avg_cpu_seconds,
+  execution_count,
+  avg_rows
+FROM SPANNER_SYS.READ_STATS_TOP_10MINUTE
+WHERE
+  interval_end = "2020-05-28T19:20:00"
+ORDER BY avg_cpu_seconds DESC LIMIT 3
+--
+QueryStatement [0-223]
+  Query [0-223]
+    Select [0-185]
+      SelectList [9-99]
+        SelectColumn [9-21]
+          PathExpression [9-21]
+            Identifier(read_columns) [9-21]
+        SelectColumn [25-68]
+          FunctionCall [25-49]
+            PathExpression [25-30]
+              Identifier(ROUND) [25-30]
+            PathExpression [31-46]
+              Identifier(avg_cpu_seconds) [31-46]
+            IntLiteral(4) [47-48]
+          Alias [50-68]
+            Identifier(avg_cpu_seconds) [53-68]
+        SelectColumn [72-87]
+          PathExpression [72-87]
+            Identifier(execution_count) [72-87]
+        SelectColumn [91-99]
+          PathExpression [91-99]
+            Identifier(avg_rows) [91-99]
+      FromClause [100-140]
+        TablePathExpression [105-140]
+          PathExpression [105-140]
+            Identifier(SPANNER_SYS) [105-116]
+            Identifier(READ_STATS_TOP_10MINUTE) [117-140]
+      WhereClause [141-185]
+        BinaryExpression(=) [149-185]
+          PathExpression [149-161]
+            Identifier(interval_end) [149-161]
+          StringLiteral [164-185]
+            StringLiteralComponent("2020-05-28T19:20:00") [164-185]
+    OrderBy [186-215]
+      OrderingExpression(DESC) [195-215]
+        PathExpression [195-210]
+          Identifier(avg_cpu_seconds) [195-210]
+    LimitOffset [216-223]
+      Limit [216-223]
+        IntLiteral(3) [222-223]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/read-statistics#apply_best_practices_to_reduce_high_cpu_usage
+SELECT interval_end, read_columns, execution_count
+FROM SPANNER_SYS.READ_STATS_TOP_MINUTE
+ORDER BY execution_count DESC
+LIMIT 10
+--
+QueryStatement [0-128]
+  Query [0-128]
+    Select [0-89]
+      SelectList [7-50]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(interval_end) [7-19]
+        SelectColumn [21-33]
+          PathExpression [21-33]
+            Identifier(read_columns) [21-33]
+        SelectColumn [35-50]
+          PathExpression [35-50]
+            Identifier(execution_count) [35-50]
+      FromClause [51-89]
+        TablePathExpression [56-89]
+          PathExpression [56-89]
+            Identifier(SPANNER_SYS) [56-67]
+            Identifier(READ_STATS_TOP_MINUTE) [68-89]
+    OrderBy [90-119]
+      OrderingExpression(DESC) [99-119]
+        PathExpression [99-114]
+          Identifier(execution_count) [99-114]
+    LimitOffset [120-128]
+      Limit [120-128]
+        IntLiteral(10) [126-128]
diff --git a/parser/testdata/spanner/guides/introspection.table-operations-statistics.test b/parser/testdata/spanner/guides/introspection.table-operations-statistics.test
new file mode 100644
index 0000000..ab56489
--- /dev/null
+++ b/parser/testdata/spanner/guides/introspection.table-operations-statistics.test
@@ -0,0 +1,162 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/introspection/table-operations-statistics
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/introspection/table-operations-statistics#query_the_tables_and_indexes_with_no_usage_in_the_last_24_hours
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+(SELECT t.table_name
+ FROM  information_schema.tables AS t
+ WHERE t.table_catalog = ""
+   AND t.table_schema = ""
+   AND t.table_type = "BASE TABLE"
+ UNION ALL
+ SELECT cs.change_stream_name
+ FROM information_schema.change_streams cs
+ WHERE cs.change_stream_catalog = ""
+   AND cs.change_stream_schema = ""
+ UNION ALL
+ SELECT idx.index_name
+ FROM information_schema.indexes idx
+ WHERE idx.index_type = "INDEX"
+   AND idx.table_catalog = ""
+   AND idx.table_schema = "")
+ EXCEPT ALL
+(SELECT  DISTINCT(table_name)
+ FROM spanner_sys.table_operations_stats_hour
+ WHERE interval_end > TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL -24 HOUR))
+--
+QueryStatement [0-633]
+  Query [0-633]
+    SetOperation(EXCEPT ALL) [0-633]
+      SetOperationMetadataList [470-480]
+        SetOperationMetadata [470-480]
+          SetOperationType [470-476]
+          SetOperationAllOrDistinct [477-480]
+      Query [1-467]
+        SetOperation(UNION ALL) [1-467]
+          SetOperationMetadataList [150-316]
+            SetOperationMetadata [150-159]
+              SetOperationType [150-155]
+              SetOperationAllOrDistinct [156-159]
+            SetOperationMetadata [307-316]
+              SetOperationType [307-312]
+              SetOperationAllOrDistinct [313-316]
+          Select [1-148]
+            SelectList [8-20]
+              SelectColumn [8-20]
+                PathExpression [8-20]
+                  Identifier(t) [8-9]
+                  Identifier(table_name) [10-20]
+            FromClause [22-58]
+              TablePathExpression [28-58]
+                PathExpression [28-53]
+                  Identifier(information_schema) [28-46]
+                  Identifier(tables) [47-53]
+                Alias [54-58]
+                  Identifier(t) [57-58]
+            WhereClause [60-148]
+              AndExpr [66-148]
+                BinaryExpression(=) [66-86]
+                  PathExpression [66-81]
+                    Identifier(t) [66-67]
+                    Identifier(table_catalog) [68-81]
+                  StringLiteral [84-86]
+                    StringLiteralComponent("") [84-86]
+                BinaryExpression(=) [94-113]
+                  PathExpression [94-108]
+                    Identifier(t) [94-95]
+                    Identifier(table_schema) [96-108]
+                  StringLiteral [111-113]
+                    StringLiteralComponent("") [111-113]
+                BinaryExpression(=) [121-148]
+                  PathExpression [121-133]
+                    Identifier(t) [121-122]
+                    Identifier(table_type) [123-133]
+                  StringLiteral [136-148]
+                    StringLiteralComponent("BASE TABLE") [136-148]
+          Select [161-305]
+            SelectList [168-189]
+              SelectColumn [168-189]
+                PathExpression [168-189]
+                  Identifier(cs) [168-170]
+                  Identifier(change_stream_name) [171-189]
+            FromClause [191-232]
+              TablePathExpression [196-232]
+                PathExpression [196-229]
+                  Identifier(information_schema) [196-214]
+                  Identifier(change_streams) [215-229]
+                Alias [230-232]
+                  Identifier(cs) [230-232]
+            WhereClause [234-305]
+              AndExpr [240-305]
+                BinaryExpression(=) [240-269]
+                  PathExpression [240-264]
+                    Identifier(cs) [240-242]
+                    Identifier(change_stream_catalog) [243-264]
+                  StringLiteral [267-269]
+                    StringLiteralComponent("") [267-269]
+                BinaryExpression(=) [277-305]
+                  PathExpression [277-300]
+                    Identifier(cs) [277-279]
+                    Identifier(change_stream_schema) [280-300]
+                  StringLiteral [303-305]
+                    StringLiteralComponent("") [303-305]
+          Select [318-467]
+            SelectList [325-339]
+              SelectColumn [325-339]
+                PathExpression [325-339]
+                  Identifier(idx) [325-328]
+                  Identifier(index_name) [329-339]
+            FromClause [341-376]
+              TablePathExpression [346-376]
+                PathExpression [346-372]
+                  Identifier(information_schema) [346-364]
+                  Identifier(indexes) [365-372]
+                Alias [373-376]
+                  Identifier(idx) [373-376]
+            WhereClause [378-467]
+              AndExpr [384-467]
+                BinaryExpression(=) [384-408]
+                  PathExpression [384-398]
+                    Identifier(idx) [384-387]
+                    Identifier(index_type) [388-398]
+                  StringLiteral [401-408]
+                    StringLiteralComponent("INDEX") [401-408]
+                BinaryExpression(=) [416-438]
+                  PathExpression [416-433]
+                    Identifier(idx) [416-419]
+                    Identifier(table_catalog) [420-433]
+                  StringLiteral [436-438]
+                    StringLiteralComponent("") [436-438]
+                BinaryExpression(=) [446-467]
+                  PathExpression [446-462]
+                    Identifier(idx) [446-449]
+                    Identifier(table_schema) [450-462]
+                  StringLiteral [465-467]
+                    StringLiteralComponent("") [465-467]
+      Query [482-632]
+        Select(distinct=true) [482-632]
+          SelectList [498-510]
+            SelectColumn [498-510]
+              PathExpression [499-509]
+                Identifier(table_name) [499-509]
+          FromClause [512-556]
+            TablePathExpression [517-556]
+              PathExpression [517-556]
+                Identifier(spanner_sys) [517-528]
+                Identifier(table_operations_stats_hour) [529-556]
+          WhereClause [558-632]
+            BinaryExpression(>) [564-632]
+              PathExpression [564-576]
+                Identifier(interval_end) [564-576]
+              FunctionCall [579-632]
+                PathExpression [579-592]
+                  Identifier(TIMESTAMP_ADD) [579-592]
+                FunctionCall [593-612]
+                  PathExpression [593-610]
+                    Identifier(`CURRENT_TIMESTAMP`) [593-610]
+                IntervalExpr [614-631]
+                  UnaryExpression(-) [623-626]
+                    IntLiteral(24) [624-626]
+                  Identifier(HOUR) [627-631]
diff --git a/parser/testdata/spanner/guides/introspection.transaction-statistics.test b/parser/testdata/spanner/guides/introspection.transaction-statistics.test
new file mode 100644
index 0000000..690c45a
--- /dev/null
+++ b/parser/testdata/spanner/guides/introspection.transaction-statistics.test
@@ -0,0 +1,757 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/introspection/transaction-statistics
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/introspection/transaction-statistics#list_the_basic_statistics_for_each_transaction_in_a_given_time_period
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT fprint,
+       read_columns,
+       write_constructive_columns,
+       write_delete_tables,
+       avg_total_latency_seconds,
+       avg_commit_latency_seconds,
+       operations_by_table,
+       avg_bytes
+FROM spanner_sys.txn_stats_top_minute
+WHERE interval_end =
+  (SELECT MAX(interval_end)
+   FROM spanner_sys.txn_stats_top_minute)
+--
+QueryStatement [0-341]
+  Query [0-341]
+    Select [0-341]
+      SelectList [7-212]
+        SelectColumn [7-13]
+          PathExpression [7-13]
+            Identifier(fprint) [7-13]
+        SelectColumn [22-34]
+          PathExpression [22-34]
+            Identifier(read_columns) [22-34]
+        SelectColumn [43-69]
+          PathExpression [43-69]
+            Identifier(write_constructive_columns) [43-69]
+        SelectColumn [78-97]
+          PathExpression [78-97]
+            Identifier(write_delete_tables) [78-97]
+        SelectColumn [106-131]
+          PathExpression [106-131]
+            Identifier(avg_total_latency_seconds) [106-131]
+        SelectColumn [140-166]
+          PathExpression [140-166]
+            Identifier(avg_commit_latency_seconds) [140-166]
+        SelectColumn [175-194]
+          PathExpression [175-194]
+            Identifier(operations_by_table) [175-194]
+        SelectColumn [203-212]
+          PathExpression [203-212]
+            Identifier(avg_bytes) [203-212]
+      FromClause [213-250]
+        TablePathExpression [218-250]
+          PathExpression [218-250]
+            Identifier(spanner_sys) [218-229]
+            Identifier(txn_stats_top_minute) [230-250]
+      WhereClause [251-341]
+        BinaryExpression(=) [257-341]
+          PathExpression [257-269]
+            Identifier(interval_end) [257-269]
+          ExpressionSubquery [274-341]
+            Query [275-340]
+              Select [275-340]
+                SelectList [282-299]
+                  SelectColumn [282-299]
+                    FunctionCall [282-299]
+                      PathExpression [282-285]
+                        Identifier(MAX) [282-285]
+                      PathExpression [286-298]
+                        Identifier(interval_end) [286-298]
+                FromClause [303-340]
+                  TablePathExpression [308-340]
+                    PathExpression [308-340]
+                      Identifier(spanner_sys) [308-319]
+                      Identifier(txn_stats_top_minute) [320-340]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/transaction-statistics#list_the_transactions_with_the_highest_average_commit_latency
+SELECT fprint,
+       read_columns,
+       write_constructive_columns,
+       write_delete_tables,
+       avg_total_latency_seconds,
+       avg_commit_latency_seconds,
+       avg_bytes
+FROM spanner_sys.txn_stats_top_hour
+WHERE interval_end =
+  (SELECT MAX(interval_end)
+   FROM spanner_sys.txn_stats_top_hour)
+ORDER BY avg_commit_latency_seconds DESC
+--
+QueryStatement [0-350]
+  Query [0-350]
+    Select [0-309]
+      SelectList [7-184]
+        SelectColumn [7-13]
+          PathExpression [7-13]
+            Identifier(fprint) [7-13]
+        SelectColumn [22-34]
+          PathExpression [22-34]
+            Identifier(read_columns) [22-34]
+        SelectColumn [43-69]
+          PathExpression [43-69]
+            Identifier(write_constructive_columns) [43-69]
+        SelectColumn [78-97]
+          PathExpression [78-97]
+            Identifier(write_delete_tables) [78-97]
+        SelectColumn [106-131]
+          PathExpression [106-131]
+            Identifier(avg_total_latency_seconds) [106-131]
+        SelectColumn [140-166]
+          PathExpression [140-166]
+            Identifier(avg_commit_latency_seconds) [140-166]
+        SelectColumn [175-184]
+          PathExpression [175-184]
+            Identifier(avg_bytes) [175-184]
+      FromClause [185-220]
+        TablePathExpression [190-220]
+          PathExpression [190-220]
+            Identifier(spanner_sys) [190-201]
+            Identifier(txn_stats_top_hour) [202-220]
+      WhereClause [221-309]
+        BinaryExpression(=) [227-309]
+          PathExpression [227-239]
+            Identifier(interval_end) [227-239]
+          ExpressionSubquery [244-309]
+            Query [245-308]
+              Select [245-308]
+                SelectList [252-269]
+                  SelectColumn [252-269]
+                    FunctionCall [252-269]
+                      PathExpression [252-255]
+                        Identifier(MAX) [252-255]
+                      PathExpression [256-268]
+                        Identifier(interval_end) [256-268]
+                FromClause [273-308]
+                  TablePathExpression [278-308]
+                    PathExpression [278-308]
+                      Identifier(spanner_sys) [278-289]
+                      Identifier(txn_stats_top_hour) [290-308]
+    OrderBy [310-350]
+      OrderingExpression(DESC) [319-350]
+        PathExpression [319-345]
+          Identifier(avg_commit_latency_seconds) [319-345]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/transaction-statistics#find_the_average_latency_of_transactions_which_read_certain_columns
+SELECT fprint,
+       read_columns,
+       write_constructive_columns,
+       write_delete_tables,
+       avg_total_latency_seconds
+FROM spanner_sys.txn_stats_top_hour
+WHERE 'ADDRESS' IN UNNEST(read_columns)
+ORDER BY avg_total_latency_seconds DESC
+--
+QueryStatement [0-247]
+  Query [0-247]
+    Select [0-207]
+      SelectList [7-131]
+        SelectColumn [7-13]
+          PathExpression [7-13]
+            Identifier(fprint) [7-13]
+        SelectColumn [22-34]
+          PathExpression [22-34]
+            Identifier(read_columns) [22-34]
+        SelectColumn [43-69]
+          PathExpression [43-69]
+            Identifier(write_constructive_columns) [43-69]
+        SelectColumn [78-97]
+          PathExpression [78-97]
+            Identifier(write_delete_tables) [78-97]
+        SelectColumn [106-131]
+          PathExpression [106-131]
+            Identifier(avg_total_latency_seconds) [106-131]
+      FromClause [132-167]
+        TablePathExpression [137-167]
+          PathExpression [137-167]
+            Identifier(spanner_sys) [137-148]
+            Identifier(txn_stats_top_hour) [149-167]
+      WhereClause [168-207]
+        InExpression(IN) [174-207]
+          StringLiteral [174-183]
+            StringLiteralComponent('ADDRESS') [174-183]
+          Location [184-186]
+          UnnestExpression [187-207]
+            ExpressionWithOptAlias [194-206]
+              PathExpression [194-206]
+                Identifier(read_columns) [194-206]
+    OrderBy [208-247]
+      OrderingExpression(DESC) [217-247]
+        PathExpression [217-242]
+          Identifier(avg_total_latency_seconds) [217-242]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/transaction-statistics#list_transactions_by_the_average_number_of_bytes_modified
+SELECT fprint,
+       read_columns,
+       write_constructive_columns,
+       write_delete_tables,
+       avg_bytes
+FROM spanner_sys.txn_stats_top_hour
+ORDER BY avg_bytes DESC
+--
+QueryStatement [0-175]
+  Query [0-175]
+    Select [0-151]
+      SelectList [7-115]
+        SelectColumn [7-13]
+          PathExpression [7-13]
+            Identifier(fprint) [7-13]
+        SelectColumn [22-34]
+          PathExpression [22-34]
+            Identifier(read_columns) [22-34]
+        SelectColumn [43-69]
+          PathExpression [43-69]
+            Identifier(write_constructive_columns) [43-69]
+        SelectColumn [78-97]
+          PathExpression [78-97]
+            Identifier(write_delete_tables) [78-97]
+        SelectColumn [106-115]
+          PathExpression [106-115]
+            Identifier(avg_bytes) [106-115]
+      FromClause [116-151]
+        TablePathExpression [121-151]
+          PathExpression [121-151]
+            Identifier(spanner_sys) [121-132]
+            Identifier(txn_stats_top_hour) [133-151]
+    OrderBy [152-175]
+      OrderingExpression(DESC) [161-175]
+        PathExpression [161-170]
+          Identifier(avg_bytes) [161-170]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/transaction-statistics#find_the_total_number_of_commit_attempts_for_all_transactions
+SELECT interval_end,
+       commit_attempt_count
+FROM spanner_sys.txn_stats_total_minute
+WHERE interval_end =
+  (SELECT MAX(interval_end)
+   FROM spanner_sys.txn_stats_total_minute)
+ORDER BY interval_end
+--
+QueryStatement [0-203]
+  Query [0-203]
+    Select [0-181]
+      SelectList [7-48]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(interval_end) [7-19]
+        SelectColumn [28-48]
+          PathExpression [28-48]
+            Identifier(commit_attempt_count) [28-48]
+      FromClause [49-88]
+        TablePathExpression [54-88]
+          PathExpression [54-88]
+            Identifier(spanner_sys) [54-65]
+            Identifier(txn_stats_total_minute) [66-88]
+      WhereClause [89-181]
+        BinaryExpression(=) [95-181]
+          PathExpression [95-107]
+            Identifier(interval_end) [95-107]
+          ExpressionSubquery [112-181]
+            Query [113-180]
+              Select [113-180]
+                SelectList [120-137]
+                  SelectColumn [120-137]
+                    FunctionCall [120-137]
+                      PathExpression [120-123]
+                        Identifier(MAX) [120-123]
+                      PathExpression [124-136]
+                        Identifier(interval_end) [124-136]
+                FromClause [141-180]
+                  TablePathExpression [146-180]
+                    PathExpression [146-180]
+                      Identifier(spanner_sys) [146-157]
+                      Identifier(txn_stats_total_minute) [158-180]
+    OrderBy [182-203]
+      OrderingExpression(ASC) [191-203]
+        PathExpression [191-203]
+          Identifier(interval_end) [191-203]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/transaction-statistics#find_the_total_commit_latency_across_all_transactions
+SELECT (avg_commit_latency_seconds * commit_attempt_count / 60 / 60)
+  AS total_commit_latency_hours
+FROM spanner_sys.txn_stats_total_10minute
+WHERE interval_end =
+  (SELECT MAX(interval_end)
+   FROM spanner_sys.txn_stats_total_10minute)
+--
+QueryStatement [0-237]
+  Query [0-237]
+    Select [0-237]
+      SelectList [7-100]
+        SelectColumn [7-100]
+          BinaryExpression(/) [8-67]
+            BinaryExpression(/) [8-62]
+              BinaryExpression(*) [8-57]
+                PathExpression [8-34]
+                  Identifier(avg_commit_latency_seconds) [8-34]
+                PathExpression [37-57]
+                  Identifier(commit_attempt_count) [37-57]
+              IntLiteral(60) [60-62]
+            IntLiteral(60) [65-67]
+          Alias [71-100]
+            Identifier(total_commit_latency_hours) [74-100]
+      FromClause [101-142]
+        TablePathExpression [106-142]
+          PathExpression [106-142]
+            Identifier(spanner_sys) [106-117]
+            Identifier(txn_stats_total_10minute) [118-142]
+      WhereClause [143-237]
+        BinaryExpression(=) [149-237]
+          PathExpression [149-161]
+            Identifier(interval_end) [149-161]
+          ExpressionSubquery [166-237]
+            Query [167-236]
+              Select [167-236]
+                SelectList [174-191]
+                  SelectColumn [174-191]
+                    FunctionCall [174-191]
+                      PathExpression [174-177]
+                        Identifier(MAX) [174-177]
+                      PathExpression [178-190]
+                        Identifier(interval_end) [178-190]
+                FromClause [195-236]
+                  TablePathExpression [200-236]
+                    PathExpression [200-236]
+                      Identifier(spanner_sys) [200-211]
+                      Identifier(txn_stats_total_10minute) [212-236]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/transaction-statistics#example-percentile-latency
+SELECT interval_end, avg_total_latency_seconds,
+       SPANNER_SYS.DISTRIBUTION_PERCENTILE(total_latency_distribution[OFFSET(0)], 99.0)
+  AS percentile_latency
+FROM spanner_sys.txn_stats_total_10minute
+WHERE interval_end =
+  (SELECT MAX(interval_end)
+   FROM spanner_sys.txn_stats_total_10minute)
+ORDER BY interval_end
+--
+QueryStatement [0-318]
+  Query [0-318]
+    Select [0-296]
+      SelectList [7-159]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(interval_end) [7-19]
+        SelectColumn [21-46]
+          PathExpression [21-46]
+            Identifier(avg_total_latency_seconds) [21-46]
+        SelectColumn [55-159]
+          FunctionCall [55-135]
+            PathExpression [55-90]
+              Identifier(SPANNER_SYS) [55-66]
+              Identifier(DISTRIBUTION_PERCENTILE) [67-90]
+            ArrayElement [91-128]
+              PathExpression [91-117]
+                Identifier(total_latency_distribution) [91-117]
+              Location [117-118]
+              FunctionCall [118-127]
+                PathExpression [118-124]
+                  Identifier(OFFSET) [118-124]
+                IntLiteral(0) [125-126]
+            FloatLiteral(99.0) [130-134]
+          Alias [138-159]
+            Identifier(percentile_latency) [141-159]
+      FromClause [160-201]
+        TablePathExpression [165-201]
+          PathExpression [165-201]
+            Identifier(spanner_sys) [165-176]
+            Identifier(txn_stats_total_10minute) [177-201]
+      WhereClause [202-296]
+        BinaryExpression(=) [208-296]
+          PathExpression [208-220]
+            Identifier(interval_end) [208-220]
+          ExpressionSubquery [225-296]
+            Query [226-295]
+              Select [226-295]
+                SelectList [233-250]
+                  SelectColumn [233-250]
+                    FunctionCall [233-250]
+                      PathExpression [233-236]
+                        Identifier(MAX) [233-236]
+                      PathExpression [237-249]
+                        Identifier(interval_end) [237-249]
+                FromClause [254-295]
+                  TablePathExpression [259-295]
+                    PathExpression [259-295]
+                      Identifier(spanner_sys) [259-270]
+                      Identifier(txn_stats_total_10minute) [271-295]
+    OrderBy [297-318]
+      OrderingExpression(ASC) [306-318]
+        PathExpression [306-318]
+          Identifier(interval_end) [306-318]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/transaction-statistics#example-percentile-latency
+SELECT interval_end, avg_total_latency_seconds,
+       SPANNER_SYS.DISTRIBUTION_PERCENTILE(total_latency_distribution_json_string, 99.0)
+  AS percentile_latency
+FROM spanner_sys.txn_stats_total_10minute
+WHERE interval_end =
+  (SELECT MAX(interval_end)
+   FROM spanner_sys.txn_stats_total_10minute)
+ORDER BY interval_end
+--
+QueryStatement [0-319]
+  Query [0-319]
+    Select [0-297]
+      SelectList [7-160]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(interval_end) [7-19]
+        SelectColumn [21-46]
+          PathExpression [21-46]
+            Identifier(avg_total_latency_seconds) [21-46]
+        SelectColumn [55-160]
+          FunctionCall [55-136]
+            PathExpression [55-90]
+              Identifier(SPANNER_SYS) [55-66]
+              Identifier(DISTRIBUTION_PERCENTILE) [67-90]
+            PathExpression [91-129]
+              Identifier(total_latency_distribution_json_string) [91-129]
+            FloatLiteral(99.0) [131-135]
+          Alias [139-160]
+            Identifier(percentile_latency) [142-160]
+      FromClause [161-202]
+        TablePathExpression [166-202]
+          PathExpression [166-202]
+            Identifier(spanner_sys) [166-177]
+            Identifier(txn_stats_total_10minute) [178-202]
+      WhereClause [203-297]
+        BinaryExpression(=) [209-297]
+          PathExpression [209-221]
+            Identifier(interval_end) [209-221]
+          ExpressionSubquery [226-297]
+            Query [227-296]
+              Select [227-296]
+                SelectList [234-251]
+                  SelectColumn [234-251]
+                    FunctionCall [234-251]
+                      PathExpression [234-237]
+                        Identifier(MAX) [234-237]
+                      PathExpression [238-250]
+                        Identifier(interval_end) [238-250]
+                FromClause [255-296]
+                  TablePathExpression [260-296]
+                    PathExpression [260-296]
+                      Identifier(spanner_sys) [260-271]
+                      Identifier(txn_stats_total_10minute) [272-296]
+    OrderBy [298-319]
+      OrderingExpression(ASC) [307-319]
+        PathExpression [307-319]
+          Identifier(interval_end) [307-319]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/transaction-statistics#gather_transaction_statistics_for_the_selected_time_period
+SELECT
+  interval_end,
+  ROUND(avg_total_latency_seconds,4) as avg_total_latency_seconds,
+  commit_attempt_count,
+  commit_abort_count
+FROM SPANNER_SYS.TXN_STATS_TOTAL_10MINUTE
+WHERE
+  interval_end >= "2020-05-17T16:40:00"
+  AND interval_end <= "2020-05-17T19:40:00"
+ORDER BY interval_end
+--
+QueryStatement [0-288]
+  Query [0-288]
+    Select [0-266]
+      SelectList [9-134]
+        SelectColumn [9-21]
+          PathExpression [9-21]
+            Identifier(interval_end) [9-21]
+        SelectColumn [25-88]
+          FunctionCall [25-59]
+            PathExpression [25-30]
+              Identifier(ROUND) [25-30]
+            PathExpression [31-56]
+              Identifier(avg_total_latency_seconds) [31-56]
+            IntLiteral(4) [57-58]
+          Alias [60-88]
+            Identifier(avg_total_latency_seconds) [63-88]
+        SelectColumn [92-112]
+          PathExpression [92-112]
+            Identifier(commit_attempt_count) [92-112]
+        SelectColumn [116-134]
+          PathExpression [116-134]
+            Identifier(commit_abort_count) [116-134]
+      FromClause [135-176]
+        TablePathExpression [140-176]
+          PathExpression [140-176]
+            Identifier(SPANNER_SYS) [140-151]
+            Identifier(TXN_STATS_TOTAL_10MINUTE) [152-176]
+      WhereClause [177-266]
+        AndExpr [185-266]
+          BinaryExpression(>=) [185-222]
+            PathExpression [185-197]
+              Identifier(interval_end) [185-197]
+            StringLiteral [201-222]
+              StringLiteralComponent("2020-05-17T16:40:00") [201-222]
+          BinaryExpression(<=) [229-266]
+            PathExpression [229-241]
+              Identifier(interval_end) [229-241]
+            StringLiteral [245-266]
+              StringLiteralComponent("2020-05-17T19:40:00") [245-266]
+    OrderBy [267-288]
+      OrderingExpression(ASC) [276-288]
+        PathExpression [276-288]
+          Identifier(interval_end) [276-288]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/transaction-statistics#identify_transactions_that_are_experiencing_high_latency
+SELECT
+  interval_end,
+  fprint,
+  ROUND(avg_total_latency_seconds,4) as avg_total_latency_seconds,
+  ROUND(avg_commit_latency_seconds,4) as avg_commit_latency_seconds,
+  commit_attempt_count,
+  commit_abort_count,
+  commit_retry_count
+FROM SPANNER_SYS.TXN_STATS_TOP_10MINUTE
+WHERE
+  interval_end = "2020-05-17T18:40:00"
+ORDER BY avg_total_latency_seconds DESC
+--
+QueryStatement [0-360]
+  Query [0-360]
+    Select [0-320]
+      SelectList [9-235]
+        SelectColumn [9-21]
+          PathExpression [9-21]
+            Identifier(interval_end) [9-21]
+        SelectColumn [25-31]
+          PathExpression [25-31]
+            Identifier(fprint) [25-31]
+        SelectColumn [35-98]
+          FunctionCall [35-69]
+            PathExpression [35-40]
+              Identifier(ROUND) [35-40]
+            PathExpression [41-66]
+              Identifier(avg_total_latency_seconds) [41-66]
+            IntLiteral(4) [67-68]
+          Alias [70-98]
+            Identifier(avg_total_latency_seconds) [73-98]
+        SelectColumn [102-167]
+          FunctionCall [102-137]
+            PathExpression [102-107]
+              Identifier(ROUND) [102-107]
+            PathExpression [108-134]
+              Identifier(avg_commit_latency_seconds) [108-134]
+            IntLiteral(4) [135-136]
+          Alias [138-167]
+            Identifier(avg_commit_latency_seconds) [141-167]
+        SelectColumn [171-191]
+          PathExpression [171-191]
+            Identifier(commit_attempt_count) [171-191]
+        SelectColumn [195-213]
+          PathExpression [195-213]
+            Identifier(commit_abort_count) [195-213]
+        SelectColumn [217-235]
+          PathExpression [217-235]
+            Identifier(commit_retry_count) [217-235]
+      FromClause [236-275]
+        TablePathExpression [241-275]
+          PathExpression [241-275]
+            Identifier(SPANNER_SYS) [241-252]
+            Identifier(TXN_STATS_TOP_10MINUTE) [253-275]
+      WhereClause [276-320]
+        BinaryExpression(=) [284-320]
+          PathExpression [284-296]
+            Identifier(interval_end) [284-296]
+          StringLiteral [299-320]
+            StringLiteralComponent("2020-05-17T18:40:00") [299-320]
+    OrderBy [321-360]
+      OrderingExpression(DESC) [330-360]
+        PathExpression [330-355]
+          Identifier(avg_total_latency_seconds) [330-355]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/transaction-statistics#identify_the_columns_involved_in_a_transaction_experiencing_high_latency
+SELECT
+  fprint,
+  read_columns,
+  write_constructive_columns,
+  write_delete_tables
+FROM SPANNER_SYS.TXN_STATS_TOP_10MINUTE
+WHERE
+  interval_end = "2020-05-17T18:40:00"
+ORDER BY avg_total_latency_seconds DESC LIMIT 3
+--
+QueryStatement [0-217]
+  Query [0-217]
+    Select [0-169]
+      SelectList [9-84]
+        SelectColumn [9-15]
+          PathExpression [9-15]
+            Identifier(fprint) [9-15]
+        SelectColumn [19-31]
+          PathExpression [19-31]
+            Identifier(read_columns) [19-31]
+        SelectColumn [35-61]
+          PathExpression [35-61]
+            Identifier(write_constructive_columns) [35-61]
+        SelectColumn [65-84]
+          PathExpression [65-84]
+            Identifier(write_delete_tables) [65-84]
+      FromClause [85-124]
+        TablePathExpression [90-124]
+          PathExpression [90-124]
+            Identifier(SPANNER_SYS) [90-101]
+            Identifier(TXN_STATS_TOP_10MINUTE) [102-124]
+      WhereClause [125-169]
+        BinaryExpression(=) [133-169]
+          PathExpression [133-145]
+            Identifier(interval_end) [133-145]
+          StringLiteral [148-169]
+            StringLiteralComponent("2020-05-17T18:40:00") [148-169]
+    OrderBy [170-209]
+      OrderingExpression(DESC) [179-209]
+        PathExpression [179-204]
+          Identifier(avg_total_latency_seconds) [179-204]
+    LimitOffset [210-217]
+      Limit [210-217]
+        IntLiteral(3) [216-217]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/transaction-statistics#incorrect-retries
+SELECT
+  *
+FROM (
+  SELECT
+    fprint,
+    SUM(commit_attempt_count) AS total_commit_attempt_count,
+    SUM(commit_abort_count) AS total_commit_abort_count,
+    SUM(commit_retry_count) AS total_commit_retry_count
+  FROM
+    SPANNER_SYS.TXN_STATS_TOP_10MINUTE
+  GROUP BY
+    fprint )
+WHERE
+  total_commit_retry_count = 0
+  AND total_commit_abort_count > 0
+ORDER BY
+  total_commit_abort_count DESC
+--
+QueryStatement [0-395]
+  Query [0-395]
+    Select [0-354]
+      SelectList [9-10]
+        SelectColumn [9-10]
+          Star(*) [9-10]
+      FromClause [11-282]
+        TableSubquery [16-282]
+          Query [20-280]
+            Select [20-280]
+              SelectList [31-212]
+                SelectColumn [31-37]
+                  PathExpression [31-37]
+                    Identifier(fprint) [31-37]
+                SelectColumn [43-98]
+                  FunctionCall [43-68]
+                    PathExpression [43-46]
+                      Identifier(SUM) [43-46]
+                    PathExpression [47-67]
+                      Identifier(commit_attempt_count) [47-67]
+                  Alias [69-98]
+                    Identifier(total_commit_attempt_count) [72-98]
+                SelectColumn [104-155]
+                  FunctionCall [104-127]
+                    PathExpression [104-107]
+                      Identifier(SUM) [104-107]
+                    PathExpression [108-126]
+                      Identifier(commit_abort_count) [108-126]
+                  Alias [128-155]
+                    Identifier(total_commit_abort_count) [131-155]
+                SelectColumn [161-212]
+                  FunctionCall [161-184]
+                    PathExpression [161-164]
+                      Identifier(SUM) [161-164]
+                    PathExpression [165-183]
+                      Identifier(commit_retry_count) [165-183]
+                  Alias [185-212]
+                    Identifier(total_commit_retry_count) [188-212]
+              FromClause [215-258]
+                TablePathExpression [224-258]
+                  PathExpression [224-258]
+                    Identifier(SPANNER_SYS) [224-235]
+                    Identifier(TXN_STATS_TOP_10MINUTE) [236-258]
+              GroupBy [261-280]
+                GroupingItem [274-280]
+                  PathExpression [274-280]
+                    Identifier(fprint) [274-280]
+      WhereClause [283-354]
+        AndExpr [291-354]
+          BinaryExpression(=) [291-319]
+            PathExpression [291-315]
+              Identifier(total_commit_retry_count) [291-315]
+            IntLiteral(0) [318-319]
+          BinaryExpression(>) [326-354]
+            PathExpression [326-350]
+              Identifier(total_commit_abort_count) [326-350]
+            IntLiteral(0) [353-354]
+    OrderBy [355-395]
+      OrderingExpression(DESC) [366-395]
+        PathExpression [366-390]
+          Identifier(total_commit_abort_count) [366-390]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/transaction-statistics#incorrect-retries
+SELECT
+  interval_end,
+  fprint,
+  read_columns,
+  write_constructive_columns,
+  write_delete_tables,
+  commit_attempt_count,
+  commit_abort_count,
+  commit_retry_count
+FROM
+  SPANNER_SYS.TXN_STATS_TOP_10MINUTE
+WHERE
+  fprint = 1557557373282541312
+ORDER BY
+  interval_end DESC
+--
+QueryStatement [0-276]
+  Query [0-276]
+    Select [0-247]
+      SelectList [9-168]
+        SelectColumn [9-21]
+          PathExpression [9-21]
+            Identifier(interval_end) [9-21]
+        SelectColumn [25-31]
+          PathExpression [25-31]
+            Identifier(fprint) [25-31]
+        SelectColumn [35-47]
+          PathExpression [35-47]
+            Identifier(read_columns) [35-47]
+        SelectColumn [51-77]
+          PathExpression [51-77]
+            Identifier(write_constructive_columns) [51-77]
+        SelectColumn [81-100]
+          PathExpression [81-100]
+            Identifier(write_delete_tables) [81-100]
+        SelectColumn [104-124]
+          PathExpression [104-124]
+            Identifier(commit_attempt_count) [104-124]
+        SelectColumn [128-146]
+          PathExpression [128-146]
+            Identifier(commit_abort_count) [128-146]
+        SelectColumn [150-168]
+          PathExpression [150-168]
+            Identifier(commit_retry_count) [150-168]
+      FromClause [169-210]
+        TablePathExpression [176-210]
+          PathExpression [176-210]
+            Identifier(SPANNER_SYS) [176-187]
+            Identifier(TXN_STATS_TOP_10MINUTE) [188-210]
+      WhereClause [211-247]
+        BinaryExpression(=) [219-247]
+          PathExpression [219-225]
+            Identifier(fprint) [219-225]
+          IntLiteral(1557557373282541312) [228-247]
+    OrderBy [248-276]
+      OrderingExpression(DESC) [259-276]
+        PathExpression [259-271]
+          Identifier(interval_end) [259-271]
diff --git a/parser/testdata/spanner/guides/introspection.troubleshooting-with-tags.test b/parser/testdata/spanner/guides/introspection.troubleshooting-with-tags.test
new file mode 100644
index 0000000..14414d4
--- /dev/null
+++ b/parser/testdata/spanner/guides/introspection.troubleshooting-with-tags.test
@@ -0,0 +1,325 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/introspection/troubleshooting-with-tags
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/introspection/troubleshooting-with-tags#how_to_view_request_tags_in_statistics_table
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT t.text,
+       t.request_tag,
+       t.execution_count,
+       t.avg_latency_seconds,
+       t.avg_rows,
+       t.avg_bytes
+FROM SPANNER_SYS.QUERY_STATS_TOP_10MINUTE AS t
+LIMIT 3
+--
+QueryStatement [0-185]
+  Query [0-185]
+    Select [0-177]
+      SelectList [7-130]
+        SelectColumn [7-13]
+          PathExpression [7-13]
+            Identifier(t) [7-8]
+            Identifier(text) [9-13]
+        SelectColumn [22-35]
+          PathExpression [22-35]
+            Identifier(t) [22-23]
+            Identifier(request_tag) [24-35]
+        SelectColumn [44-61]
+          PathExpression [44-61]
+            Identifier(t) [44-45]
+            Identifier(execution_count) [46-61]
+        SelectColumn [70-91]
+          PathExpression [70-91]
+            Identifier(t) [70-71]
+            Identifier(avg_latency_seconds) [72-91]
+        SelectColumn [100-110]
+          PathExpression [100-110]
+            Identifier(t) [100-101]
+            Identifier(avg_rows) [102-110]
+        SelectColumn [119-130]
+          PathExpression [119-130]
+            Identifier(t) [119-120]
+            Identifier(avg_bytes) [121-130]
+      FromClause [131-177]
+        TablePathExpression [136-177]
+          PathExpression [136-172]
+            Identifier(SPANNER_SYS) [136-147]
+            Identifier(QUERY_STATS_TOP_10MINUTE) [148-172]
+          Alias [173-177]
+            Identifier(t) [176-177]
+    LimitOffset [178-185]
+      Limit [178-185]
+        IntLiteral(3) [184-185]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/troubleshooting-with-tags#how_to_view_transaction_tags_in_transaction_statistics_table
+SELECT t.fprint,
+       t.transaction_tag,
+       t.read_columns,
+       t.commit_attempt_count,
+       t.avg_total_latency_seconds
+FROM SPANNER_SYS.TXN_STATS_TOP_10MINUTE AS t
+LIMIT 3
+--
+QueryStatement [0-184]
+  Query [0-184]
+    Select [0-176]
+      SelectList [7-131]
+        SelectColumn [7-15]
+          PathExpression [7-15]
+            Identifier(t) [7-8]
+            Identifier(fprint) [9-15]
+        SelectColumn [24-41]
+          PathExpression [24-41]
+            Identifier(t) [24-25]
+            Identifier(transaction_tag) [26-41]
+        SelectColumn [50-64]
+          PathExpression [50-64]
+            Identifier(t) [50-51]
+            Identifier(read_columns) [52-64]
+        SelectColumn [73-95]
+          PathExpression [73-95]
+            Identifier(t) [73-74]
+            Identifier(commit_attempt_count) [75-95]
+        SelectColumn [104-131]
+          PathExpression [104-131]
+            Identifier(t) [104-105]
+            Identifier(avg_total_latency_seconds) [106-131]
+      FromClause [132-176]
+        TablePathExpression [137-176]
+          PathExpression [137-171]
+            Identifier(SPANNER_SYS) [137-148]
+            Identifier(TXN_STATS_TOP_10MINUTE) [149-171]
+          Alias [172-176]
+            Identifier(t) [175-176]
+    LimitOffset [177-184]
+      Limit [177-184]
+        IntLiteral(3) [183-184]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/troubleshooting-with-tags#how_to_view_transaction_tags_in_lock_statistics_table
+SELECT
+   CAST(s.row_range_start_key AS STRING) AS row_range_start_key,
+   s.lock_wait_seconds,
+   s.sample_lock_requests
+FROM SPANNER_SYS.LOCK_STATS_TOP_10MINUTE s
+LIMIT 2
+--
+QueryStatement [0-172]
+  Query [0-172]
+    Select [0-164]
+      SelectList [10-121]
+        SelectColumn [10-70]
+          CastExpression [10-47]
+            PathExpression [15-36]
+              Identifier(s) [15-16]
+              Identifier(row_range_start_key) [17-36]
+            SimpleType [40-46]
+              PathExpression [40-46]
+                Identifier(STRING) [40-46]
+          Alias [48-70]
+            Identifier(row_range_start_key) [51-70]
+        SelectColumn [75-94]
+          PathExpression [75-94]
+            Identifier(s) [75-76]
+            Identifier(lock_wait_seconds) [77-94]
+        SelectColumn [99-121]
+          PathExpression [99-121]
+            Identifier(s) [99-100]
+            Identifier(sample_lock_requests) [101-121]
+      FromClause [122-164]
+        TablePathExpression [127-164]
+          PathExpression [127-162]
+            Identifier(SPANNER_SYS) [127-138]
+            Identifier(LOCK_STATS_TOP_10MINUTE) [139-162]
+          Alias [163-164]
+            Identifier(s) [163-164]
+    LimitOffset [165-172]
+      Limit [165-172]
+        IntLiteral(2) [171-172]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/troubleshooting-with-tags#finding_the_source_of_a_problematic_transaction
+SELECT
+ fprint,
+ transaction_tag,
+ ROUND(avg_total_latency_seconds,4) as avg_total_latency_sec,
+ ROUND(avg_commit_latency_seconds,4) as avg_commit_latency_sec,
+ commit_attempt_count,
+ commit_abort_count
+FROM SPANNER_SYS.TXN_STATS_TOP_10MINUTE
+WHERE interval_end = "2020-05-17T18:40:00"
+ORDER BY avg_total_latency_seconds DESC
+--
+QueryStatement [0-325]
+  Query [0-325]
+    Select [0-285]
+      SelectList [8-202]
+        SelectColumn [8-14]
+          PathExpression [8-14]
+            Identifier(fprint) [8-14]
+        SelectColumn [17-32]
+          PathExpression [17-32]
+            Identifier(transaction_tag) [17-32]
+        SelectColumn [35-94]
+          FunctionCall [35-69]
+            PathExpression [35-40]
+              Identifier(ROUND) [35-40]
+            PathExpression [41-66]
+              Identifier(avg_total_latency_seconds) [41-66]
+            IntLiteral(4) [67-68]
+          Alias [70-94]
+            Identifier(avg_total_latency_sec) [73-94]
+        SelectColumn [97-158]
+          FunctionCall [97-132]
+            PathExpression [97-102]
+              Identifier(ROUND) [97-102]
+            PathExpression [103-129]
+              Identifier(avg_commit_latency_seconds) [103-129]
+            IntLiteral(4) [130-131]
+          Alias [133-158]
+            Identifier(avg_commit_latency_sec) [136-158]
+        SelectColumn [161-181]
+          PathExpression [161-181]
+            Identifier(commit_attempt_count) [161-181]
+        SelectColumn [184-202]
+          PathExpression [184-202]
+            Identifier(commit_abort_count) [184-202]
+      FromClause [203-242]
+        TablePathExpression [208-242]
+          PathExpression [208-242]
+            Identifier(SPANNER_SYS) [208-219]
+            Identifier(TXN_STATS_TOP_10MINUTE) [220-242]
+      WhereClause [243-285]
+        BinaryExpression(=) [249-285]
+          PathExpression [249-261]
+            Identifier(interval_end) [249-261]
+          StringLiteral [264-285]
+            StringLiteralComponent("2020-05-17T18:40:00") [264-285]
+    OrderBy [286-325]
+      OrderingExpression(DESC) [295-325]
+        PathExpression [295-320]
+          Identifier(avg_total_latency_seconds) [295-320]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/troubleshooting-with-tags#finding_the_latency_and_other_stats_for_transactions_from_a_particular_application_or_microservice
+SELECT
+  transaction_tag,
+  avg_total_latency_sec,
+  avg_commit_latency_sec,
+  commit_attempt_count,
+  commit_abort_count
+FROM SPANNER_SYS.TXN_STATS_TOP_10MINUTE
+WHERE STARTS_WITH(transaction_tag, "app=payment")
+LIMIT 3
+--
+QueryStatement [0-219]
+  Query [0-219]
+    Select [0-211]
+      SelectList [9-121]
+        SelectColumn [9-24]
+          PathExpression [9-24]
+            Identifier(transaction_tag) [9-24]
+        SelectColumn [28-49]
+          PathExpression [28-49]
+            Identifier(avg_total_latency_sec) [28-49]
+        SelectColumn [53-75]
+          PathExpression [53-75]
+            Identifier(avg_commit_latency_sec) [53-75]
+        SelectColumn [79-99]
+          PathExpression [79-99]
+            Identifier(commit_attempt_count) [79-99]
+        SelectColumn [103-121]
+          PathExpression [103-121]
+            Identifier(commit_abort_count) [103-121]
+      FromClause [122-161]
+        TablePathExpression [127-161]
+          PathExpression [127-161]
+            Identifier(SPANNER_SYS) [127-138]
+            Identifier(TXN_STATS_TOP_10MINUTE) [139-161]
+      WhereClause [162-211]
+        FunctionCall [168-211]
+          PathExpression [168-179]
+            Identifier(STARTS_WITH) [168-179]
+          PathExpression [180-195]
+            Identifier(transaction_tag) [180-195]
+          StringLiteral [197-210]
+            StringLiteralComponent("app=payment") [197-210]
+    LimitOffset [212-219]
+      Limit [212-219]
+        IntLiteral(3) [218-219]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/troubleshooting-with-tags#discovering_the_transactions_involved_in_lock_conflict
+SELECT CAST(s.row_range_start_key AS STRING) AS row_range_start_key,
+       t.total_lock_wait_seconds,
+       s.lock_wait_seconds,
+       s.lock_wait_seconds/t.total_lock_wait_seconds frac_of_total,
+       s.sample_lock_requests
+FROM spanner_sys.lock_stats_total_10minute t, spanner_sys.lock_stats_top_10minute s
+WHERE
+  t.interval_end = "2020-05-17T18:40:00" and s.interval_end = t.interval_end
+--
+QueryStatement [0-395]
+  Query [0-395]
+    Select [0-395]
+      SelectList [7-228]
+        SelectColumn [7-67]
+          CastExpression [7-44]
+            PathExpression [12-33]
+              Identifier(s) [12-13]
+              Identifier(row_range_start_key) [14-33]
+            SimpleType [37-43]
+              PathExpression [37-43]
+                Identifier(STRING) [37-43]
+          Alias [45-67]
+            Identifier(row_range_start_key) [48-67]
+        SelectColumn [76-101]
+          PathExpression [76-101]
+            Identifier(t) [76-77]
+            Identifier(total_lock_wait_seconds) [78-101]
+        SelectColumn [110-129]
+          PathExpression [110-129]
+            Identifier(s) [110-111]
+            Identifier(lock_wait_seconds) [112-129]
+        SelectColumn [138-197]
+          BinaryExpression(/) [138-183]
+            PathExpression [138-157]
+              Identifier(s) [138-139]
+              Identifier(lock_wait_seconds) [140-157]
+            PathExpression [158-183]
+              Identifier(t) [158-159]
+              Identifier(total_lock_wait_seconds) [160-183]
+          Alias [184-197]
+            Identifier(frac_of_total) [184-197]
+        SelectColumn [206-228]
+          PathExpression [206-228]
+            Identifier(s) [206-207]
+            Identifier(sample_lock_requests) [208-228]
+      FromClause [229-312]
+        Join(COMMA) [234-312]
+          TablePathExpression [234-273]
+            PathExpression [234-271]
+              Identifier(spanner_sys) [234-245]
+              Identifier(lock_stats_total_10minute) [246-271]
+            Alias [272-273]
+              Identifier(t) [272-273]
+          Location [273-274]
+          TablePathExpression [275-312]
+            PathExpression [275-310]
+              Identifier(spanner_sys) [275-286]
+              Identifier(lock_stats_top_10minute) [287-310]
+            Alias [311-312]
+              Identifier(s) [311-312]
+      WhereClause [313-395]
+        AndExpr [321-395]
+          BinaryExpression(=) [321-359]
+            PathExpression [321-335]
+              Identifier(t) [321-322]
+              Identifier(interval_end) [323-335]
+            StringLiteral [338-359]
+              StringLiteralComponent("2020-05-17T18:40:00") [338-359]
+          BinaryExpression(=) [364-395]
+            PathExpression [364-378]
+              Identifier(s) [364-365]
+              Identifier(interval_end) [366-378]
+            PathExpression [381-395]
+              Identifier(t) [381-382]
+              Identifier(interval_end) [383-395]
diff --git a/parser/testdata/spanner/guides/introspection.vector-index-statistics.test b/parser/testdata/spanner/guides/introspection.vector-index-statistics.test
new file mode 100644
index 0000000..9e2a68a
--- /dev/null
+++ b/parser/testdata/spanner/guides/introspection.vector-index-statistics.test
@@ -0,0 +1,164 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/introspection/vector-index-statistics
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/introspection/vector-index-statistics#example-query-1
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT VECTOR_INDEX_NAME,
+        START_TIME,
+        SUM(CASE WHEN percentile = 99 THEN value_at_percentile ELSE 0 END)
+        / SUM(CASE WHEN percentile = 50 THEN value_at_percentile ELSE 0 END) as cluster_size_imbalance
+FROM (
+  SELECT v.VECTOR_INDEX_NAME,
+         v.START_TIME, d.percentile,
+         d.value_at_percentile
+  FROM SPANNER_SYS.VECTOR_INDEX_METRICS_HISTORY AS v,
+  UNNEST(v.CLUSTER_AVERAGE_DISTANCE_TO_CENTROID_PERCENTILES)
+AS d)
+WHERE VECTOR_INDEX_NAME = "VecIndex2"
+GROUP BY 1, 2
+--
+QueryStatement [0-501]
+  Query [0-501]
+    Select [0-501]
+      SelectList [7-223]
+        SelectColumn [7-24]
+          PathExpression [7-24]
+            Identifier(VECTOR_INDEX_NAME) [7-24]
+        SelectColumn [34-44]
+          PathExpression [34-44]
+            Identifier(START_TIME) [34-44]
+        SelectColumn [54-223]
+          BinaryExpression(/) [54-197]
+            FunctionCall [54-120]
+              PathExpression [54-57]
+                Identifier(SUM) [54-57]
+              CaseNoValueExpression [58-119]
+                BinaryExpression(=) [68-83]
+                  PathExpression [68-78]
+                    Identifier(percentile) [68-78]
+                  IntLiteral(99) [81-83]
+                PathExpression [89-108]
+                  Identifier(value_at_percentile) [89-108]
+                IntLiteral(0) [114-115]
+            FunctionCall [131-197]
+              PathExpression [131-134]
+                Identifier(SUM) [131-134]
+              CaseNoValueExpression [135-196]
+                BinaryExpression(=) [145-160]
+                  PathExpression [145-155]
+                    Identifier(percentile) [145-155]
+                  IntLiteral(50) [158-160]
+                PathExpression [166-185]
+                  Identifier(value_at_percentile) [166-185]
+                IntLiteral(0) [191-192]
+          Alias [198-223]
+            Identifier(cluster_size_imbalance) [201-223]
+      FromClause [224-449]
+        TableSubquery [229-449]
+          Query [233-448]
+            Select [233-448]
+              SelectList [240-328]
+                SelectColumn [240-259]
+                  PathExpression [240-259]
+                    Identifier(v) [240-241]
+                    Identifier(VECTOR_INDEX_NAME) [242-259]
+                SelectColumn [270-282]
+                  PathExpression [270-282]
+                    Identifier(v) [270-271]
+                    Identifier(START_TIME) [272-282]
+                SelectColumn [284-296]
+                  PathExpression [284-296]
+                    Identifier(d) [284-285]
+                    Identifier(percentile) [286-296]
+                SelectColumn [307-328]
+                  PathExpression [307-328]
+                    Identifier(d) [307-308]
+                    Identifier(value_at_percentile) [309-328]
+              FromClause [331-448]
+                Join(COMMA) [336-448]
+                  TablePathExpression [336-381]
+                    PathExpression [336-376]
+                      Identifier(SPANNER_SYS) [336-347]
+                      Identifier(VECTOR_INDEX_METRICS_HISTORY) [348-376]
+                    Alias [377-381]
+                      Identifier(v) [380-381]
+                  Location [381-382]
+                  TablePathExpression [385-448]
+                    UnnestExpression [385-443]
+                      ExpressionWithOptAlias [392-442]
+                        PathExpression [392-442]
+                          Identifier(v) [392-393]
+                          Identifier(CLUSTER_AVERAGE_DISTANCE_TO_CENTROID_PERCENTILES) [394-442]
+                    Alias [444-448]
+                      Identifier(d) [447-448]
+      WhereClause [450-487]
+        BinaryExpression(=) [456-487]
+          PathExpression [456-473]
+            Identifier(VECTOR_INDEX_NAME) [456-473]
+          StringLiteral [476-487]
+            StringLiteralComponent("VecIndex2") [476-487]
+      GroupBy [488-501]
+        GroupingItem [497-498]
+          IntLiteral(1) [497-498]
+        GroupingItem [500-501]
+          IntLiteral(2) [500-501]
+==
+# https://docs.cloud.google.com/spanner/docs/introspection/vector-index-statistics#example-query-2
+SELECT v.VECTOR_INDEX_NAME,
+       v.START_TIME,
+       d.value_at_percentile AS distance_to_centroid_99p
+FROM SPANNER_SYS.VECTOR_INDEX_METRICS_HISTORY AS v,
+UNNEST(v.CLUSTER_AVERAGE_DISTANCE_TO_CENTROID_PERCENTILES) AS d
+WHERE d.percentile=99
+ORDER BY VECTOR_INDEX_NAME, START_TIME DESC
+--
+QueryStatement [0-287]
+  Query [0-287]
+    Select [0-243]
+      SelectList [7-105]
+        SelectColumn [7-26]
+          PathExpression [7-26]
+            Identifier(v) [7-8]
+            Identifier(VECTOR_INDEX_NAME) [9-26]
+        SelectColumn [35-47]
+          PathExpression [35-47]
+            Identifier(v) [35-36]
+            Identifier(START_TIME) [37-47]
+        SelectColumn [56-105]
+          PathExpression [56-77]
+            Identifier(d) [56-57]
+            Identifier(value_at_percentile) [58-77]
+          Alias [78-105]
+            Identifier(distance_to_centroid_99p) [81-105]
+      FromClause [106-221]
+        Join(COMMA) [111-221]
+          TablePathExpression [111-156]
+            PathExpression [111-151]
+              Identifier(SPANNER_SYS) [111-122]
+              Identifier(VECTOR_INDEX_METRICS_HISTORY) [123-151]
+            Alias [152-156]
+              Identifier(v) [155-156]
+          Location [156-157]
+          TablePathExpression [158-221]
+            UnnestExpression [158-216]
+              ExpressionWithOptAlias [165-215]
+                PathExpression [165-215]
+                  Identifier(v) [165-166]
+                  Identifier(CLUSTER_AVERAGE_DISTANCE_TO_CENTROID_PERCENTILES) [167-215]
+            Alias [217-221]
+              Identifier(d) [220-221]
+      WhereClause [222-243]
+        BinaryExpression(=) [228-243]
+          PathExpression [228-240]
+            Identifier(d) [228-229]
+            Identifier(percentile) [230-240]
+          IntLiteral(99) [241-243]
+    OrderBy [244-287]
+      OrderingExpression(ASC) [253-270]
+        PathExpression [253-270]
+          Identifier(VECTOR_INDEX_NAME) [253-270]
+      OrderingExpression(DESC) [272-287]
+        PathExpression [272-282]
+          Identifier(START_TIME) [272-282]
diff --git a/parser/testdata/spanner/guides/isolation-levels.test b/parser/testdata/spanner/guides/isolation-levels.test
new file mode 100644
index 0000000..8bc92bf
--- /dev/null
+++ b/parser/testdata/spanner/guides/isolation-levels.test
@@ -0,0 +1,225 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/isolation-levels
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/isolation-levels#repeatable-read-example
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+-- Transaction 1
+BEGIN
+--
+BeginStatement [17-22]
+==
+# https://docs.cloud.google.com/spanner/docs/isolation-levels#repeatable-read-example
+-- Snapshot established at T1
+SELECT AlbumId, MarketingBudget
+FROM Albums
+WHERE SingerId = 1
+--
+QueryStatement [30-92]
+  Query [30-92]
+    Select [30-92]
+      SelectList [37-61]
+        SelectColumn [37-44]
+          PathExpression [37-44]
+            Identifier(AlbumId) [37-44]
+        SelectColumn [46-61]
+          PathExpression [46-61]
+            Identifier(MarketingBudget) [46-61]
+      FromClause [62-73]
+        TablePathExpression [67-73]
+          PathExpression [67-73]
+            Identifier(Albums) [67-73]
+      WhereClause [74-92]
+        BinaryExpression(=) [80-92]
+          PathExpression [80-88]
+            Identifier(SingerId) [80-88]
+          IntLiteral(1) [91-92]
+==
+# https://docs.cloud.google.com/spanner/docs/isolation-levels#repeatable-read-example
+-- Transaction 2
+BEGIN
+--
+BeginStatement [17-22]
+==
+# https://docs.cloud.google.com/spanner/docs/isolation-levels#repeatable-read-example
+-- Snapshot established at T2 > T1
+SELECT AlbumId, MarketingBudget
+FROM Albums
+WHERE SingerId = 1
+--
+QueryStatement [35-97]
+  Query [35-97]
+    Select [35-97]
+      SelectList [42-66]
+        SelectColumn [42-49]
+          PathExpression [42-49]
+            Identifier(AlbumId) [42-49]
+        SelectColumn [51-66]
+          PathExpression [51-66]
+            Identifier(MarketingBudget) [51-66]
+      FromClause [67-78]
+        TablePathExpression [72-78]
+          PathExpression [72-78]
+            Identifier(Albums) [72-78]
+      WhereClause [79-97]
+        BinaryExpression(=) [85-97]
+          PathExpression [85-93]
+            Identifier(SingerId) [85-93]
+          IntLiteral(1) [96-97]
+==
+# https://docs.cloud.google.com/spanner/docs/isolation-levels#repeatable-read-example
+INSERT INTO Albums (SingerId, AlbumId, MarketingBudget) VALUES (1, 5, 50000)
+--
+InsertStatement [0-76]
+  PathExpression [12-18]
+    Identifier(Albums) [12-18]
+  ColumnList [19-55]
+    Identifier(SingerId) [20-28]
+    Identifier(AlbumId) [30-37]
+    Identifier(MarketingBudget) [39-54]
+  InsertValuesRowList [56-76]
+    InsertValuesRow [63-76]
+      IntLiteral(1) [64-65]
+      IntLiteral(5) [67-68]
+      IntLiteral(50000) [70-75]
+==
+# https://docs.cloud.google.com/spanner/docs/isolation-levels#repeatable-read-example
+COMMIT
+--
+CommitStatement [0-6]
+==
+# https://docs.cloud.google.com/spanner/docs/isolation-levels#repeatable-read-example
+-- Transaction 1 continues
+SELECT SUM(MarketingBudget) as UsedBudget
+FROM Albums
+WHERE SingerId = 1
+--
+QueryStatement [27-99]
+  Query [27-99]
+    Select [27-99]
+      SelectList [34-68]
+        SelectColumn [34-68]
+          FunctionCall [34-54]
+            PathExpression [34-37]
+              Identifier(SUM) [34-37]
+            PathExpression [38-53]
+              Identifier(MarketingBudget) [38-53]
+          Alias [55-68]
+            Identifier(UsedBudget) [58-68]
+      FromClause [69-80]
+        TablePathExpression [74-80]
+          PathExpression [74-80]
+            Identifier(Albums) [74-80]
+      WhereClause [81-99]
+        BinaryExpression(=) [87-99]
+          PathExpression [87-95]
+            Identifier(SingerId) [87-95]
+          IntLiteral(1) [98-99]
+==
+# https://docs.cloud.google.com/spanner/docs/isolation-levels#read-write-conflicts-correctness
+-- Transaction 1 continues..
+UPDATE Albums
+SET MarketingBudget = MarketingBudget + 100000
+WHERE SingerId = 1 AND AlbumId = 4
+--
+UpdateStatement [29-124]
+  PathExpression [36-42]
+    Identifier(Albums) [36-42]
+  UpdateItemList [47-89]
+    UpdateItem [47-89]
+      UpdateSetValue [47-89]
+        PathExpression [47-62]
+          Identifier(MarketingBudget) [47-62]
+        BinaryExpression(+) [65-89]
+          PathExpression [65-80]
+            Identifier(MarketingBudget) [65-80]
+          IntLiteral(100000) [83-89]
+  AndExpr [96-124]
+    BinaryExpression(=) [96-108]
+      PathExpression [96-104]
+        Identifier(SingerId) [96-104]
+      IntLiteral(1) [107-108]
+    BinaryExpression(=) [113-124]
+      PathExpression [113-120]
+        Identifier(AlbumId) [113-120]
+      IntLiteral(4) [123-124]
+==
+# https://docs.cloud.google.com/spanner/docs/isolation-levels#read-write-conflicts-correctness
+-- Transaction 1 continues..
+SELECT SUM(MarketingBudget) AS TotalBudget
+FROM Albums
+WHERE SingerId = 1
+FOR UPDATE
+--
+QueryStatement [29-113]
+  Query [29-113]
+    Select [29-102]
+      SelectList [36-71]
+        SelectColumn [36-71]
+          FunctionCall [36-56]
+            PathExpression [36-39]
+              Identifier(SUM) [36-39]
+            PathExpression [40-55]
+              Identifier(MarketingBudget) [40-55]
+          Alias [57-71]
+            Identifier(TotalBudget) [60-71]
+      FromClause [72-83]
+        TablePathExpression [77-83]
+          PathExpression [77-83]
+            Identifier(Albums) [77-83]
+      WhereClause [84-102]
+        BinaryExpression(=) [90-102]
+          PathExpression [90-98]
+            Identifier(SingerId) [90-98]
+          IntLiteral(1) [101-102]
+    LockMode [103-113]
+==
+# https://docs.cloud.google.com/spanner/docs/isolation-levels#write-write-conflicts-correctness
+-- Snapshot established at T2 (> T1)
+SELECT AlbumId, MarketingBudget
+FROM Albums
+WHERE SingerId = 1
+--
+QueryStatement [37-99]
+  Query [37-99]
+    Select [37-99]
+      SelectList [44-68]
+        SelectColumn [44-51]
+          PathExpression [44-51]
+            Identifier(AlbumId) [44-51]
+        SelectColumn [53-68]
+          PathExpression [53-68]
+            Identifier(MarketingBudget) [53-68]
+      FromClause [69-80]
+        TablePathExpression [74-80]
+          PathExpression [74-80]
+            Identifier(Albums) [74-80]
+      WhereClause [81-99]
+        BinaryExpression(=) [87-99]
+          PathExpression [87-95]
+            Identifier(SingerId) [87-95]
+          IntLiteral(1) [98-99]
+==
+# https://docs.cloud.google.com/spanner/docs/isolation-levels#write-write-conflicts-correctness
+-- Transaction 1 continues
+INSERT INTO Albums (SingerId, AlbumId, MarketingBudget) VALUES (1, 5, 30000)
+--
+InsertStatement [27-103]
+  PathExpression [39-45]
+    Identifier(Albums) [39-45]
+  ColumnList [46-82]
+    Identifier(SingerId) [47-55]
+    Identifier(AlbumId) [57-64]
+    Identifier(MarketingBudget) [66-81]
+  InsertValuesRowList [83-103]
+    InsertValuesRow [90-103]
+      IntLiteral(1) [91-92]
+      IntLiteral(5) [94-95]
+      IntLiteral(30000) [97-102]
+==
+# https://docs.cloud.google.com/spanner/docs/isolation-levels#write-write-conflicts-correctness
+-- Transaction aborts
+COMMIT
+--
+CommitStatement [22-28]
diff --git a/parser/testdata/spanner/guides/migrating-primary-keys.spanner.sql b/parser/testdata/spanner/guides/migrating-primary-keys.spanner.sql
new file mode 100644
index 0000000..662c20b
--- /dev/null
+++ b/parser/testdata/spanner/guides/migrating-primary-keys.spanner.sql
@@ -0,0 +1,41 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/migrating-primary-keys
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/migrating-primary-keys#migrate-sequential-key-columns
+# id 2797564c39f8: trailing comma in OPTIONS (migrating-primary-keys)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected ")" [at 3:1]
+ALTER DATABASE db SET OPTIONS (
+  default_sequence_kind = 'bit_reversed_positive',
+);
+
+# https://docs.cloud.google.com/spanner/docs/migrating-primary-keys#migrate-sequential-key-columns
+# id a2561c1baafc: identity columns (migrating-primary-keys)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ")" but got keyword SKIP [at 2:46]
+CREATE TABLE MyFirstTable (
+  Id INT64 GENERATED BY DEFAULT AS IDENTITY (SKIP RANGE 1, 4294967296),
+  Name STRING(MAX),
+) PRIMARY KEY (Id);
+
+# https://docs.cloud.google.com/spanner/docs/migrating-primary-keys#migrate-sequential-key-columns
+# id 370cc1df2396: identity columns (migrating-primary-keys)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ";" or end of input but got keyword IDENTITY [at 1:48]
+ALTER TABLE MyFirstTable ALTER COLUMN Id ALTER IDENTITY SET SKIP RANGE 1, 4294967296;
+
+# https://docs.cloud.google.com/spanner/docs/migrating-primary-keys#migrate-bit-reversed-key-columns
+# id 6e4905147d32: identity columns (migrating-primary-keys)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected keyword WITH but got identifier "COUNTER" [at 2:52]
+CREATE TABLE MyFirstTable (
+  Id INT64 GENERATED BY DEFAULT AS IDENTITY (START COUNTER WITH 11000),
+  Name STRING(MAX),
+) PRIMARY KEY (Id);
+
+# https://docs.cloud.google.com/spanner/docs/migrating-primary-keys#migrate-bit-reversed-key-columns
+# id 1d6473a902cf: identity columns (migrating-primary-keys)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ";" or end of input but got keyword IDENTITY [at 1:48]
+ALTER TABLE MyFirstTable ALTER COLUMN Id ALTER IDENTITY RESTART COUNTER WITH 11000;
diff --git a/parser/testdata/spanner/guides/migrating-primary-keys.test b/parser/testdata/spanner/guides/migrating-primary-keys.test
new file mode 100644
index 0000000..218a5f1
--- /dev/null
+++ b/parser/testdata/spanner/guides/migrating-primary-keys.test
@@ -0,0 +1,358 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/migrating-primary-keys
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/migrating-primary-keys#sample_migration_workflow
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE SEQUENCE singer_id_sequence OPTIONS (
+     SequenceKind = 'bit_reversed_positive'
+  )
+--
+CreateSequenceStatement [0-92]
+  PathExpression [16-34]
+    Identifier(singer_id_sequence) [16-34]
+  OptionsList [43-92]
+    OptionsEntry [50-88]
+      Identifier(SequenceKind) [50-62]
+      StringLiteral [65-88]
+        StringLiteralComponent('bit_reversed_positive') [65-88]
+==
+# https://docs.cloud.google.com/spanner/docs/migrating-primary-keys#sample_migration_workflow
+CREATE TABLE Singers (
+     SingerId INT64 DEFAULT
+     (GET_NEXT_SEQUENCE_VALUE(SEQUENCE SingerIdSequence)),
+     Name STRING(1024),
+     Biography STRING(MAX),
+  ) PRIMARY KEY (SingerId)
+--
+CreateTableStatement [0-188]
+  PathExpression [13-20]
+    Identifier(Singers) [13-20]
+  TableElementList [21-165]
+    ColumnDefinition [28-108]
+      Identifier(SingerId) [28-36]
+      SimpleColumnSchema [37-108]
+        PathExpression [37-42]
+          Identifier(INT64) [37-42]
+        FunctionCall [57-107]
+          PathExpression [57-80]
+            Identifier(GET_NEXT_SEQUENCE_VALUE) [57-80]
+          SequenceArg [81-106]
+            PathExpression [90-106]
+              Identifier(SingerIdSequence) [90-106]
+    ColumnDefinition [115-132]
+      Identifier(Name) [115-119]
+      SimpleColumnSchema [120-132]
+        PathExpression [120-126]
+          Identifier(STRING) [120-126]
+        TypeParameterList [126-131]
+          IntLiteral(1024) [127-131]
+    ColumnDefinition [139-160]
+      Identifier(Biography) [139-148]
+      SimpleColumnSchema [149-160]
+        PathExpression [149-155]
+          Identifier(STRING) [149-155]
+        TypeParameterList [155-159]
+          MaxLiteral() [156-159]
+  SpannerTableOptions [166-188]
+    PrimaryKey(ENFORCED) [166-188]
+      PrimaryKeyElementList [178-188]
+        PrimaryKeyElement(ASC) [179-187]
+          Identifier(SingerId) [179-187]
+==
+# https://docs.cloud.google.com/spanner/docs/migrating-primary-keys#sample_migration_workflow
+CREATE TABLE Albums (
+     AlbumId INT64,
+     SingerId INT64,
+     AlbumName STRING(1024),
+     SongList STRING(MAX),
+     CONSTRAINT FK_singer_album
+     FOREIGN KEY (SingerId)
+       REFERENCES Singers (SingerId)
+  ) PRIMARY KEY (AlbumId)
+--
+CreateTableStatement [0-241]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-219]
+    ColumnDefinition [27-40]
+      Identifier(AlbumId) [27-34]
+      SimpleColumnSchema [35-40]
+        PathExpression [35-40]
+          Identifier(INT64) [35-40]
+    ColumnDefinition [47-61]
+      Identifier(SingerId) [47-55]
+      SimpleColumnSchema [56-61]
+        PathExpression [56-61]
+          Identifier(INT64) [56-61]
+    ColumnDefinition [68-90]
+      Identifier(AlbumName) [68-77]
+      SimpleColumnSchema [78-90]
+        PathExpression [78-84]
+          Identifier(STRING) [78-84]
+        TypeParameterList [84-89]
+          IntLiteral(1024) [85-89]
+    ColumnDefinition [97-117]
+      Identifier(SongList) [97-105]
+      SimpleColumnSchema [106-117]
+        PathExpression [106-112]
+          Identifier(STRING) [106-112]
+        TypeParameterList [112-116]
+          MaxLiteral() [113-116]
+    ForeignKey [124-215]
+      ColumnList [168-178]
+        Identifier(SingerId) [169-177]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [186-215]
+        PathExpression [197-204]
+          Identifier(Singers) [197-204]
+        ColumnList [205-215]
+          Identifier(SingerId) [206-214]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [215-215]
+      Identifier(FK_singer_album) [135-150]
+  SpannerTableOptions [220-241]
+    PrimaryKey(ENFORCED) [220-241]
+      PrimaryKeyElementList [232-241]
+        PrimaryKeyElement(ASC) [233-240]
+          Identifier(AlbumId) [233-240]
+==
+# https://docs.cloud.google.com/spanner/docs/migrating-primary-keys#sample_migration_workflow
+SELECT MAX(SingerId) FROM Singers
+--
+QueryStatement [0-33]
+  Query [0-33]
+    Select [0-33]
+      SelectList [7-20]
+        SelectColumn [7-20]
+          FunctionCall [7-20]
+            PathExpression [7-10]
+              Identifier(MAX) [7-10]
+            PathExpression [11-19]
+              Identifier(SingerId) [11-19]
+      FromClause [21-33]
+        TablePathExpression [26-33]
+          PathExpression [26-33]
+            Identifier(Singers) [26-33]
+==
+# https://docs.cloud.google.com/spanner/docs/migrating-primary-keys#sample_migration_workflow
+ALTER SEQUENCE SingerIdSequence SET OPTIONS (
+skip_range_min = 1,
+skip_range_max = 21000
+)
+--
+AlterSequenceStatement [0-90]
+  PathExpression [15-31]
+    Identifier(SingerIdSequence) [15-31]
+  AlterActionList [32-90]
+    SetOptionsAction [32-90]
+      OptionsList [44-90]
+        OptionsEntry [46-64]
+          Identifier(skip_range_min) [46-60]
+          IntLiteral(1) [63-64]
+        OptionsEntry [66-88]
+          Identifier(skip_range_max) [66-80]
+          IntLiteral(21000) [83-88]
+==
+# https://docs.cloud.google.com/spanner/docs/migrating-primary-keys#use-spanner-source-db
+ALTER SEQUENCE SingerIdSequence SET OPTIONS (
+skip_range_min = 1,
+skip_range_max = 2147483647 -- 231-1
+)
+--
+AlterSequenceStatement [0-104]
+  PathExpression [15-31]
+    Identifier(SingerIdSequence) [15-31]
+  AlterActionList [32-104]
+    SetOptionsAction [32-104]
+      OptionsList [44-104]
+        OptionsEntry [46-64]
+          Identifier(skip_range_min) [46-60]
+          IntLiteral(1) [63-64]
+        OptionsEntry [66-93]
+          Identifier(skip_range_max) [66-80]
+          IntLiteral(2147483647) [83-93]
+==
+# https://docs.cloud.google.com/spanner/docs/migrating-primary-keys#migrate-uuid-key-columns
+CREATE TABLE Fanclubs (
+FanClubId STRING(36) DEFAULT (GENERATE_UUID()),
+ClubName STRING(1024),
+) PRIMARY KEY (FanClubId)
+--
+CreateTableStatement [0-120]
+  PathExpression [13-21]
+    Identifier(Fanclubs) [13-21]
+  TableElementList [22-96]
+    ColumnDefinition [24-70]
+      Identifier(FanClubId) [24-33]
+      SimpleColumnSchema [34-70]
+        PathExpression [34-40]
+          Identifier(STRING) [34-40]
+        TypeParameterList [40-43]
+          IntLiteral(36) [41-43]
+        FunctionCall [54-69]
+          PathExpression [54-67]
+            Identifier(GENERATE_UUID) [54-67]
+    ColumnDefinition [72-93]
+      Identifier(ClubName) [72-80]
+      SimpleColumnSchema [81-93]
+        PathExpression [81-87]
+          Identifier(STRING) [81-87]
+        TypeParameterList [87-92]
+          IntLiteral(1024) [88-92]
+  SpannerTableOptions [97-120]
+    PrimaryKey(ENFORCED) [97-120]
+      PrimaryKeyElementList [109-120]
+        PrimaryKeyElement(ASC) [110-119]
+          Identifier(FanClubId) [110-119]
+==
+# https://docs.cloud.google.com/spanner/docs/migrating-primary-keys#migrate-uuid-key-columns
+INSERT INTO FanClubs (ClubName) VALUES ("SwiftFanClub")
+--
+InsertStatement [0-55]
+  PathExpression [12-20]
+    Identifier(FanClubs) [12-20]
+  ColumnList [21-31]
+    Identifier(ClubName) [22-30]
+  InsertValuesRowList [32-55]
+    InsertValuesRow [39-55]
+      StringLiteral [40-54]
+        StringLiteralComponent("SwiftFanClub") [40-54]
+==
+# https://docs.cloud.google.com/spanner/docs/migrating-primary-keys#migrate-own-primary-keys
+[mode=expression]
+StudentIdHash = FARM_FINGERPRINT(CAST(StudentId AS STRING))
+--
+BinaryExpression(=) [0-59]
+  PathExpression [0-13]
+    Identifier(StudentIdHash) [0-13]
+  FunctionCall [16-59]
+    PathExpression [16-32]
+      Identifier(FARM_FINGERPRINT) [16-32]
+    CastExpression [33-58]
+      PathExpression [38-47]
+        Identifier(StudentId) [38-47]
+      SimpleType [51-57]
+        PathExpression [51-57]
+          Identifier(STRING) [51-57]
+==
+# https://docs.cloud.google.com/spanner/docs/migrating-primary-keys#migrate-own-primary-keys
+CREATE TABLE student (
+  StudentIdHash INT64 AS (FARM_FINGERPRINT(cast(StudentId as string))) STORED,
+  StudentId INT64 NOT NULL,
+  Info STRING(2048),
+) PRIMARY KEY(StudentIdHash, StudentId)
+--
+CreateTableStatement [0-190]
+  PathExpression [13-20]
+    Identifier(student) [13-20]
+  TableElementList [21-152]
+    ColumnDefinition [25-100]
+      Identifier(StudentIdHash) [25-38]
+      SimpleColumnSchema [39-100]
+        PathExpression [39-44]
+          Identifier(INT64) [39-44]
+        GeneratedColumnInfo(generated_mode=ALWAYS, stored_mode=STORED) [45-100]
+          FunctionCall [49-92]
+            PathExpression [49-65]
+              Identifier(FARM_FINGERPRINT) [49-65]
+            CastExpression [66-91]
+              PathExpression [71-80]
+                Identifier(StudentId) [71-80]
+              SimpleType [84-90]
+                PathExpression [84-90]
+                  Identifier(string) [84-90]
+    ColumnDefinition [104-128]
+      Identifier(StudentId) [104-113]
+      SimpleColumnSchema [114-128]
+        PathExpression [114-119]
+          Identifier(INT64) [114-119]
+        ColumnAttributeList [120-128]
+          NotNullColumnAttribute [120-128]
+    ColumnDefinition [132-149]
+      Identifier(Info) [132-136]
+      SimpleColumnSchema [137-149]
+        PathExpression [137-143]
+          Identifier(STRING) [137-143]
+        TypeParameterList [143-148]
+          IntLiteral(2048) [144-148]
+  SpannerTableOptions [153-190]
+    PrimaryKey(ENFORCED) [153-190]
+      PrimaryKeyElementList [164-190]
+        PrimaryKeyElement(ASC) [165-178]
+          Identifier(StudentIdHash) [165-178]
+        PrimaryKeyElement(ASC) [180-189]
+          Identifier(StudentId) [180-189]
+==
+# https://docs.cloud.google.com/spanner/docs/migrating-primary-keys#migrate-sequential-key-columns
+CREATE SEQUENCE MyFirstSequence OPTIONS (
+  sequence_kind = "bit_reversed_positive",
+  skip_range_min = 1,
+  skip_range_max = 4294967296
+)
+--
+CreateSequenceStatement [0-138]
+  PathExpression [16-31]
+    Identifier(MyFirstSequence) [16-31]
+  OptionsList [40-138]
+    OptionsEntry [44-83]
+      Identifier(sequence_kind) [44-57]
+      StringLiteral [60-83]
+        StringLiteralComponent("bit_reversed_positive") [60-83]
+    OptionsEntry [87-105]
+      Identifier(skip_range_min) [87-101]
+      IntLiteral(1) [104-105]
+    OptionsEntry [109-136]
+      Identifier(skip_range_max) [109-123]
+      IntLiteral(4294967296) [126-136]
+==
+# https://docs.cloud.google.com/spanner/docs/migrating-primary-keys#migrate-sequential-key-columns
+ALTER SEQUENCE MySecondSequence SET OPTIONS (
+  skip_range_min = 1,
+  skip_range_max = 4294967296
+)
+--
+AlterSequenceStatement [0-99]
+  PathExpression [15-31]
+    Identifier(MySecondSequence) [15-31]
+  AlterActionList [32-99]
+    SetOptionsAction [32-99]
+      OptionsList [44-99]
+        OptionsEntry [48-66]
+          Identifier(skip_range_min) [48-62]
+          IntLiteral(1) [65-66]
+        OptionsEntry [70-97]
+          Identifier(skip_range_max) [70-84]
+          IntLiteral(4294967296) [87-97]
+==
+# https://docs.cloud.google.com/spanner/docs/migrating-primary-keys#migrate-bit-reversed-key-columns
+CREATE SEQUENCE MyFirstSequence OPTIONS (
+  sequence_kind = "bit_reversed_positive",
+  start_with_counter = 11000
+)
+--
+CreateSequenceStatement [0-115]
+  PathExpression [16-31]
+    Identifier(MyFirstSequence) [16-31]
+  OptionsList [40-115]
+    OptionsEntry [44-83]
+      Identifier(sequence_kind) [44-57]
+      StringLiteral [60-83]
+        StringLiteralComponent("bit_reversed_positive") [60-83]
+    OptionsEntry [87-113]
+      Identifier(start_with_counter) [87-105]
+      IntLiteral(11000) [108-113]
+==
+# https://docs.cloud.google.com/spanner/docs/migrating-primary-keys#migrate-bit-reversed-key-columns
+ALTER SEQUENCE MySecondSequence SET OPTIONS (
+  start_with_counter = 11000
+)
+--
+AlterSequenceStatement [0-76]
+  PathExpression [15-31]
+    Identifier(MySecondSequence) [15-31]
+  AlterActionList [32-76]
+    SetOptionsAction [32-76]
+      OptionsList [44-76]
+        OptionsEntry [48-74]
+          Identifier(start_with_counter) [48-66]
+          IntLiteral(11000) [69-74]
diff --git a/parser/testdata/spanner/guides/ml-emulator.test b/parser/testdata/spanner/guides/ml-emulator.test
new file mode 100644
index 0000000..6d81b88
--- /dev/null
+++ b/parser/testdata/spanner/guides/ml-emulator.test
@@ -0,0 +1,73 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/ml-emulator
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/ml-emulator#default_behavior
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT Outcome
+FROM ML.PREDICT(
+    MODEL FraudDetection,
+    (SELECT 1000 AS Amount, "John Smith" AS Name))
+--
+QueryStatement [0-108]
+  Query [0-108]
+    Select [0-108]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(Outcome) [7-14]
+      FromClause [15-108]
+        TVF [20-108]
+          PathExpression [20-30]
+            Identifier(ML) [20-22]
+            Identifier(PREDICT) [23-30]
+          TVFArgument [36-56]
+            ModelClause [36-56]
+              PathExpression [42-56]
+                Identifier(FraudDetection) [42-56]
+          TVFArgument [62-107]
+            ExpressionSubquery [62-107]
+              Query [63-106]
+                Select [63-106]
+                  SelectList [70-106]
+                    SelectColumn [70-84]
+                      IntLiteral(1000) [70-74]
+                      Alias [75-84]
+                        Identifier(Amount) [78-84]
+                    SelectColumn [86-106]
+                      StringLiteral [86-98]
+                        StringLiteralComponent("John Smith") [86-98]
+                      Alias [99-106]
+                        Identifier(Name) [102-106]
+==
+# https://docs.cloud.google.com/spanner/docs/ml-emulator#custom_callback
+SELECT content
+    FROM ML.PREDICT(MODEL GeminiPro, (SELECT "Is 7 a prime number?" AS prompt))
+--
+QueryStatement [0-94]
+  Query [0-94]
+    Select [0-94]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(content) [7-14]
+      FromClause [19-94]
+        TVF [24-94]
+          PathExpression [24-34]
+            Identifier(ML) [24-26]
+            Identifier(PREDICT) [27-34]
+          TVFArgument [35-50]
+            ModelClause [35-50]
+              PathExpression [41-50]
+                Identifier(GeminiPro) [41-50]
+          TVFArgument [52-93]
+            ExpressionSubquery [52-93]
+              Query [53-92]
+                Select [53-92]
+                  SelectList [60-92]
+                    SelectColumn [60-92]
+                      StringLiteral [60-82]
+                        StringLiteralComponent("Is 7 a prime number?") [60-82]
+                      Alias [83-92]
+                        Identifier(prompt) [86-92]
diff --git a/parser/testdata/spanner/guides/ml-tutorial-embeddings.test b/parser/testdata/spanner/guides/ml-tutorial-embeddings.test
new file mode 100644
index 0000000..ecf0eb2
--- /dev/null
+++ b/parser/testdata/spanner/guides/ml-tutorial-embeddings.test
@@ -0,0 +1,90 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/ml-tutorial-embeddings
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/ml-tutorial-embeddings#generate_and_store_text_embeddings
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Products(
+  id INT64 NOT NULL,
+  description STRING(MAX),
+  embeddings ARRAY,
+) PRIMARY KEY(id)
+--
+CreateTableStatement [0-117]
+  PathExpression [13-21]
+    Identifier(Products) [13-21]
+  TableElementList [21-101]
+    ColumnDefinition [25-42]
+      Identifier(id) [25-27]
+      SimpleColumnSchema [28-42]
+        PathExpression [28-33]
+          Identifier(INT64) [28-33]
+        ColumnAttributeList [34-42]
+          NotNullColumnAttribute [34-42]
+    ColumnDefinition [46-69]
+      Identifier(description) [46-57]
+      SimpleColumnSchema [58-69]
+        PathExpression [58-64]
+          Identifier(STRING) [58-64]
+        TypeParameterList [64-68]
+          MaxLiteral() [65-68]
+    ColumnDefinition [73-98]
+      Identifier(embeddings) [73-83]
+      ArrayColumnSchema [84-98]
+        SimpleColumnSchema [90-97]
+          PathExpression [90-97]
+            Identifier(FLOAT32) [90-97]
+  SpannerTableOptions [102-117]
+    PrimaryKey(ENFORCED) [102-117]
+      PrimaryKeyElementList [113-117]
+        PrimaryKeyElement(ASC) [114-116]
+          Identifier(id) [114-116]
+==
+# https://docs.cloud.google.com/spanner/docs/ml-tutorial-embeddings#generate_and_store_text_embeddings
+INSERT INTO Products (id, description, embeddings)
+SELECT @Id, @Description, embeddings.values
+FROM ML.PREDICT(
+  MODEL MODEL_NAME,
+  (SELECT @Description as content)
+)
+--
+InsertStatement [0-168]
+  PathExpression [12-20]
+    Identifier(Products) [12-20]
+  ColumnList [21-50]
+    Identifier(id) [22-24]
+    Identifier(description) [26-37]
+    Identifier(embeddings) [39-49]
+  Query [51-168]
+    Select [51-168]
+      SelectList [58-94]
+        SelectColumn [58-61]
+          ParameterExpr [58-61]
+            Identifier(Id) [59-61]
+        SelectColumn [63-75]
+          ParameterExpr [63-75]
+            Identifier(Description) [64-75]
+        SelectColumn [77-94]
+          PathExpression [77-94]
+            Identifier(embeddings) [77-87]
+            Identifier(values) [88-94]
+      FromClause [95-168]
+        TVF [100-168]
+          PathExpression [100-110]
+            Identifier(ML) [100-102]
+            Identifier(PREDICT) [103-110]
+          TVFArgument [114-130]
+            ModelClause [114-130]
+              PathExpression [120-130]
+                Identifier(MODEL_NAME) [120-130]
+          TVFArgument [134-166]
+            ExpressionSubquery [134-166]
+              Query [135-165]
+                Select [135-165]
+                  SelectList [142-165]
+                    SelectColumn [142-165]
+                      ParameterExpr [142-154]
+                        Identifier(Description) [143-154]
+                      Alias [155-165]
+                        Identifier(content) [158-165]
diff --git a/parser/testdata/spanner/guides/ml-tutorial-generative-ai.test b/parser/testdata/spanner/guides/ml-tutorial-generative-ai.test
new file mode 100644
index 0000000..cdbaab8
--- /dev/null
+++ b/parser/testdata/spanner/guides/ml-tutorial-generative-ai.test
@@ -0,0 +1,657 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/ml-tutorial-generative-ai
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/ml-tutorial-generative-ai#create_the_ecommerce_website_schema
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Products (
+  id INT64,
+  name STRING(MAX),
+  description STRING(MAX),
+  category_id INT64,
+) PRIMARY KEY(id)
+--
+CreateTableStatement [0-121]
+  PathExpression [13-21]
+    Identifier(Products) [13-21]
+  TableElementList [22-105]
+    ColumnDefinition [26-34]
+      Identifier(id) [26-28]
+      SimpleColumnSchema [29-34]
+        PathExpression [29-34]
+          Identifier(INT64) [29-34]
+    ColumnDefinition [38-54]
+      Identifier(name) [38-42]
+      SimpleColumnSchema [43-54]
+        PathExpression [43-49]
+          Identifier(STRING) [43-49]
+        TypeParameterList [49-53]
+          MaxLiteral() [50-53]
+    ColumnDefinition [58-81]
+      Identifier(description) [58-69]
+      SimpleColumnSchema [70-81]
+        PathExpression [70-76]
+          Identifier(STRING) [70-76]
+        TypeParameterList [76-80]
+          MaxLiteral() [77-80]
+    ColumnDefinition [85-102]
+      Identifier(category_id) [85-96]
+      SimpleColumnSchema [97-102]
+        PathExpression [97-102]
+          Identifier(INT64) [97-102]
+  SpannerTableOptions [106-121]
+    PrimaryKey(ENFORCED) [106-121]
+      PrimaryKeyElementList [117-121]
+        PrimaryKeyElement(ASC) [118-120]
+          Identifier(id) [118-120]
+==
+# https://docs.cloud.google.com/spanner/docs/ml-tutorial-generative-ai#create_the_ecommerce_website_schema
+CREATE TABLE Categories (
+  id INT64,
+  name STRING(MAX)
+) PRIMARY KEY(id)
+--
+CreateTableStatement [0-74]
+  PathExpression [13-23]
+    Identifier(Categories) [13-23]
+  TableElementList [24-58]
+    ColumnDefinition [28-36]
+      Identifier(id) [28-30]
+      SimpleColumnSchema [31-36]
+        PathExpression [31-36]
+          Identifier(INT64) [31-36]
+    ColumnDefinition [40-56]
+      Identifier(name) [40-44]
+      SimpleColumnSchema [45-56]
+        PathExpression [45-51]
+          Identifier(STRING) [45-51]
+        TypeParameterList [51-55]
+          MaxLiteral() [52-55]
+  SpannerTableOptions [59-74]
+    PrimaryKey(ENFORCED) [59-74]
+      PrimaryKeyElementList [70-74]
+        PrimaryKeyElement(ASC) [71-73]
+          Identifier(id) [71-73]
+==
+# https://docs.cloud.google.com/spanner/docs/ml-tutorial-generative-ai#create_the_ecommerce_website_schema
+CREATE TABLE Users (
+  id INT64,
+  age INT64,
+  likes STRING(MAX)
+) PRIMARY KEY(id)
+--
+CreateTableStatement [0-83]
+  PathExpression [13-18]
+    Identifier(Users) [13-18]
+  TableElementList [19-67]
+    ColumnDefinition [23-31]
+      Identifier(id) [23-25]
+      SimpleColumnSchema [26-31]
+        PathExpression [26-31]
+          Identifier(INT64) [26-31]
+    ColumnDefinition [35-44]
+      Identifier(age) [35-38]
+      SimpleColumnSchema [39-44]
+        PathExpression [39-44]
+          Identifier(INT64) [39-44]
+    ColumnDefinition [48-65]
+      Identifier(likes) [48-53]
+      SimpleColumnSchema [54-65]
+        PathExpression [54-60]
+          Identifier(STRING) [54-60]
+        TypeParameterList [60-64]
+          MaxLiteral() [61-64]
+  SpannerTableOptions [68-83]
+    PrimaryKey(ENFORCED) [68-83]
+      PrimaryKeyElementList [79-83]
+        PrimaryKeyElement(ASC) [80-82]
+          Identifier(id) [80-82]
+==
+# https://docs.cloud.google.com/spanner/docs/ml-tutorial-generative-ai#create_the_ecommerce_website_schema
+INSERT INTO Categories (id, name) VALUES
+    (1, "Toys"),
+    (2, "Tools")
+--
+InsertStatement [0-74]
+  PathExpression [12-22]
+    Identifier(Categories) [12-22]
+  ColumnList [23-33]
+    Identifier(id) [24-26]
+    Identifier(name) [28-32]
+  InsertValuesRowList [34-74]
+    InsertValuesRow [45-56]
+      IntLiteral(1) [46-47]
+      StringLiteral [49-55]
+        StringLiteralComponent("Toys") [49-55]
+    InsertValuesRow [62-74]
+      IntLiteral(2) [63-64]
+      StringLiteral [66-73]
+        StringLiteralComponent("Tools") [66-73]
+==
+# https://docs.cloud.google.com/spanner/docs/ml-tutorial-generative-ai#create_the_ecommerce_website_schema
+INSERT INTO Products (id, name, description, category_id) VALUES
+    (1, "Plush Bear", "Really fluffy. Safe for infants.", 1),
+    (2, "Bike", "Bike for teenagers.", 1),
+    (3, "Drill", "Cordless.", 2)
+--
+InsertStatement [0-202]
+  PathExpression [12-20]
+    Identifier(Products) [12-20]
+  ColumnList [21-57]
+    Identifier(id) [22-24]
+    Identifier(name) [26-30]
+    Identifier(description) [32-43]
+    Identifier(category_id) [45-56]
+  InsertValuesRowList [58-202]
+    InsertValuesRow [69-125]
+      IntLiteral(1) [70-71]
+      StringLiteral [73-85]
+        StringLiteralComponent("Plush Bear") [73-85]
+      StringLiteral [87-121]
+        StringLiteralComponent("Really fluffy. Safe for infants.") [87-121]
+      IntLiteral(1) [123-124]
+    InsertValuesRow [131-168]
+      IntLiteral(2) [132-133]
+      StringLiteral [135-141]
+        StringLiteralComponent("Bike") [135-141]
+      StringLiteral [143-164]
+        StringLiteralComponent("Bike for teenagers.") [143-164]
+      IntLiteral(1) [166-167]
+    InsertValuesRow [174-202]
+      IntLiteral(3) [175-176]
+      StringLiteral [178-185]
+        StringLiteralComponent("Drill") [178-185]
+      StringLiteral [187-198]
+        StringLiteralComponent("Cordless.") [187-198]
+      IntLiteral(2) [200-201]
+==
+# https://docs.cloud.google.com/spanner/docs/ml-tutorial-generative-ai#create_the_ecommerce_website_schema
+INSERT INTO Users (id, age, likes) VALUES
+    (1, 30, "DIY"),
+    (2, 14, "Toys")
+--
+InsertStatement [0-81]
+  PathExpression [12-17]
+    Identifier(Users) [12-17]
+  ColumnList [18-34]
+    Identifier(id) [19-21]
+    Identifier(age) [23-26]
+    Identifier(likes) [28-33]
+  InsertValuesRowList [35-81]
+    InsertValuesRow [46-60]
+      IntLiteral(1) [47-48]
+      IntLiteral(30) [50-52]
+      StringLiteral [54-59]
+        StringLiteralComponent("DIY") [54-59]
+    InsertValuesRow [66-81]
+      IntLiteral(2) [67-68]
+      IntLiteral(14) [70-72]
+      StringLiteral [74-80]
+        StringLiteralComponent("Toys") [74-80]
+==
+# https://docs.cloud.google.com/spanner/docs/ml-tutorial-generative-ai#register_a_generative_ai_model_in_a_schema
+SELECT content
+FROM ML.PREDICT(
+  MODEL TextBison,
+  (SELECT "Is 13 prime?" AS prompt),
+  STRUCT(256 AS maxOutputTokens, 0.2 AS temperature, 40 as topK, 0.95 AS topP)
+) @{remote_udf_max_rows_per_rpc=1}
+--
+QueryStatement [0-201]
+  Query [0-201]
+    Select [0-201]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(content) [7-14]
+      FromClause [15-201]
+        TVF [20-201]
+          PathExpression [20-30]
+            Identifier(ML) [20-22]
+            Identifier(PREDICT) [23-30]
+          TVFArgument [34-49]
+            ModelClause [34-49]
+              PathExpression [40-49]
+                Identifier(TextBison) [40-49]
+          TVFArgument [53-86]
+            ExpressionSubquery [53-86]
+              Query [54-85]
+                Select [54-85]
+                  SelectList [61-85]
+                    SelectColumn [61-85]
+                      StringLiteral [61-75]
+                        StringLiteralComponent("Is 13 prime?") [61-75]
+                      Alias [76-85]
+                        Identifier(prompt) [79-85]
+          TVFArgument [90-166]
+            StructConstructorWithKeyword [90-166]
+              StructConstructorArg [97-119]
+                IntLiteral(256) [97-100]
+                Alias [101-119]
+                  Identifier(maxOutputTokens) [104-119]
+              StructConstructorArg [121-139]
+                FloatLiteral(0.2) [121-124]
+                Alias [125-139]
+                  Identifier(temperature) [128-139]
+              StructConstructorArg [141-151]
+                IntLiteral(40) [141-143]
+                Alias [144-151]
+                  Identifier(topK) [147-151]
+              StructConstructorArg [153-165]
+                FloatLiteral(0.95) [153-157]
+                Alias [158-165]
+                  Identifier(topP) [161-165]
+          Hint [169-201]
+            HintEntry [171-200]
+              Identifier(remote_udf_max_rows_per_rpc) [171-198]
+              IntLiteral(1) [199-200]
+==
+# https://docs.cloud.google.com/spanner/docs/ml-tutorial-generative-ai#use_the_textbison_model_to_answer_customer_questions
+SELECT product_id, product_name, content
+FROM ML.PREDICT(
+  MODEL TextBison,
+  (SELECT
+    product.id as product_id,
+    product.name as product_name,
+    CONCAT("Is this product safe for infants?", "\n",
+        "Product Name: ", product.name, "\n",
+        "Category Name: ", category.name, "\n",
+        "Product Description:", product.description) AS prompt
+   FROM
+     Products AS product JOIN Categories AS category
+       ON product.category_id = category.id),
+  STRUCT(100 AS maxOutputTokens)
+) @{remote_udf_max_rows_per_rpc=1}
+--
+QueryStatement [0-536]
+  Query [0-536]
+    Select [0-536]
+      SelectList [7-40]
+        SelectColumn [7-17]
+          PathExpression [7-17]
+            Identifier(product_id) [7-17]
+        SelectColumn [19-31]
+          PathExpression [19-31]
+            Identifier(product_name) [19-31]
+        SelectColumn [33-40]
+          PathExpression [33-40]
+            Identifier(content) [33-40]
+      FromClause [41-536]
+        TVF [46-536]
+          PathExpression [46-56]
+            Identifier(ML) [46-48]
+            Identifier(PREDICT) [49-56]
+          TVFArgument [60-75]
+            ModelClause [60-75]
+              PathExpression [66-75]
+                Identifier(TextBison) [66-75]
+          TVFArgument [79-467]
+            ExpressionSubquery [79-467]
+              Query [80-466]
+                Select [80-466]
+                  SelectList [91-361]
+                    SelectColumn [91-115]
+                      PathExpression [91-101]
+                        Identifier(product) [91-98]
+                        Identifier(id) [99-101]
+                      Alias [102-115]
+                        Identifier(product_id) [105-115]
+                    SelectColumn [121-149]
+                      PathExpression [121-133]
+                        Identifier(product) [121-128]
+                        Identifier(name) [129-133]
+                      Alias [134-149]
+                        Identifier(product_name) [137-149]
+                    SelectColumn [155-361]
+                      FunctionCall [155-351]
+                        PathExpression [155-161]
+                          Identifier(CONCAT) [155-161]
+                        StringLiteral [162-197]
+                          StringLiteralComponent("Is this product safe for infants?") [162-197]
+                        StringLiteral [199-203]
+                          StringLiteralComponent("\n") [199-203]
+                        StringLiteral [213-229]
+                          StringLiteralComponent("Product Name: ") [213-229]
+                        PathExpression [231-243]
+                          Identifier(product) [231-238]
+                          Identifier(name) [239-243]
+                        StringLiteral [245-249]
+                          StringLiteralComponent("\n") [245-249]
+                        StringLiteral [259-276]
+                          StringLiteralComponent("Category Name: ") [259-276]
+                        PathExpression [278-291]
+                          Identifier(category) [278-286]
+                          Identifier(name) [287-291]
+                        StringLiteral [293-297]
+                          StringLiteralComponent("\n") [293-297]
+                        StringLiteral [307-329]
+                          StringLiteralComponent("Product Description:") [307-329]
+                        PathExpression [331-350]
+                          Identifier(product) [331-338]
+                          Identifier(description) [339-350]
+                      Alias [352-361]
+                        Identifier(prompt) [355-361]
+                  FromClause [365-466]
+                    Join [375-466]
+                      TablePathExpression [375-394]
+                        PathExpression [375-383]
+                          Identifier(Products) [375-383]
+                        Alias [384-394]
+                          Identifier(product) [387-394]
+                      Location [395-399]
+                      TablePathExpression [400-422]
+                        PathExpression [400-410]
+                          Identifier(Categories) [400-410]
+                        Alias [411-422]
+                          Identifier(category) [414-422]
+                      OnClause [430-466]
+                        BinaryExpression(=) [433-466]
+                          PathExpression [433-452]
+                            Identifier(product) [433-440]
+                            Identifier(category_id) [441-452]
+                          PathExpression [455-466]
+                            Identifier(category) [455-463]
+                            Identifier(id) [464-466]
+          TVFArgument [471-501]
+            StructConstructorWithKeyword [471-501]
+              StructConstructorArg [478-500]
+                IntLiteral(100) [478-481]
+                Alias [482-500]
+                  Identifier(maxOutputTokens) [485-500]
+          Hint [504-536]
+            HintEntry [506-535]
+              Identifier(remote_udf_max_rows_per_rpc) [506-533]
+              IntLiteral(1) [534-535]
+==
+# https://docs.cloud.google.com/spanner/docs/ml-tutorial-generative-ai#provide_personalized_product_recommendations_to_customers
+SELECT product_id, product_name, content
+FROM ML.PREDICT(
+  MODEL TextBison,
+  (SELECT
+    product.id as product_id,
+    product.name as product_name,
+    CONCAT(
+        "Answer with YES or NO only: Is this a good fit for me?",
+        "My age:", CAST(user.age AS STRING), "\n",
+        "I like:", user.likes,  "\n",
+        "Product name: ", product.name, "\n",
+        "Category mame: ", category.name, "\n",
+        "Product description:", product.description) AS prompt,
+   FROM
+     Products AS product
+       JOIN Categories AS category ON product.category_id = category.id
+       JOIN Users AS user ON user.id = 1),
+  STRUCT(256 AS maxOutputTokens)
+) @{remote_udf_max_rows_per_rpc=1}
+--
+QueryStatement [0-691]
+  Query [0-691]
+    Select [0-691]
+      SelectList [7-40]
+        SelectColumn [7-17]
+          PathExpression [7-17]
+            Identifier(product_id) [7-17]
+        SelectColumn [19-31]
+          PathExpression [19-31]
+            Identifier(product_name) [19-31]
+        SelectColumn [33-40]
+          PathExpression [33-40]
+            Identifier(content) [33-40]
+      FromClause [41-691]
+        TVF [46-691]
+          PathExpression [46-56]
+            Identifier(ML) [46-48]
+            Identifier(PREDICT) [49-56]
+          TVFArgument [60-75]
+            ModelClause [60-75]
+              PathExpression [66-75]
+                Identifier(TextBison) [66-75]
+          TVFArgument [79-622]
+            ExpressionSubquery [79-622]
+              Query [80-621]
+                Select [80-621]
+                  SelectList [91-475]
+                    SelectColumn [91-115]
+                      PathExpression [91-101]
+                        Identifier(product) [91-98]
+                        Identifier(id) [99-101]
+                      Alias [102-115]
+                        Identifier(product_id) [105-115]
+                    SelectColumn [121-149]
+                      PathExpression [121-133]
+                        Identifier(product) [121-128]
+                        Identifier(name) [129-133]
+                      Alias [134-149]
+                        Identifier(product_name) [137-149]
+                    SelectColumn [155-474]
+                      FunctionCall [155-464]
+                        PathExpression [155-161]
+                          Identifier(CONCAT) [155-161]
+                        StringLiteral [171-227]
+                          StringLiteralComponent("Answer with YES or NO only: Is this a good fit for me?") [171-227]
+                        StringLiteral [237-246]
+                          StringLiteralComponent("My age:") [237-246]
+                        CastExpression [248-272]
+                          PathExpression [253-261]
+                            Identifier(user) [253-257]
+                            Identifier(age) [258-261]
+                          SimpleType [265-271]
+                            PathExpression [265-271]
+                              Identifier(STRING) [265-271]
+                        StringLiteral [274-278]
+                          StringLiteralComponent("\n") [274-278]
+                        StringLiteral [288-297]
+                          StringLiteralComponent("I like:") [288-297]
+                        PathExpression [299-309]
+                          Identifier(user) [299-303]
+                          Identifier(likes) [304-309]
+                        StringLiteral [312-316]
+                          StringLiteralComponent("\n") [312-316]
+                        StringLiteral [326-342]
+                          StringLiteralComponent("Product name: ") [326-342]
+                        PathExpression [344-356]
+                          Identifier(product) [344-351]
+                          Identifier(name) [352-356]
+                        StringLiteral [358-362]
+                          StringLiteralComponent("\n") [358-362]
+                        StringLiteral [372-389]
+                          StringLiteralComponent("Category mame: ") [372-389]
+                        PathExpression [391-404]
+                          Identifier(category) [391-399]
+                          Identifier(name) [400-404]
+                        StringLiteral [406-410]
+                          StringLiteralComponent("\n") [406-410]
+                        StringLiteral [420-442]
+                          StringLiteralComponent("Product description:") [420-442]
+                        PathExpression [444-463]
+                          Identifier(product) [444-451]
+                          Identifier(description) [452-463]
+                      Alias [465-474]
+                        Identifier(prompt) [468-474]
+                  FromClause [479-621]
+                    Join [489-621]
+                      Join [489-580]
+                        TablePathExpression [489-508]
+                          PathExpression [489-497]
+                            Identifier(Products) [489-497]
+                          Alias [498-508]
+                            Identifier(product) [501-508]
+                        Location [516-520]
+                        TablePathExpression [521-543]
+                          PathExpression [521-531]
+                            Identifier(Categories) [521-531]
+                          Alias [532-543]
+                            Identifier(category) [535-543]
+                        OnClause [544-580]
+                          BinaryExpression(=) [547-580]
+                            PathExpression [547-566]
+                              Identifier(product) [547-554]
+                              Identifier(category_id) [555-566]
+                            PathExpression [569-580]
+                              Identifier(category) [569-577]
+                              Identifier(id) [578-580]
+                      Location [588-592]
+                      TablePathExpression [593-606]
+                        PathExpression [593-598]
+                          Identifier(Users) [593-598]
+                        Alias [599-606]
+                          Identifier(user) [602-606]
+                      OnClause [607-621]
+                        BinaryExpression(=) [610-621]
+                          PathExpression [610-617]
+                            Identifier(user) [610-614]
+                            Identifier(id) [615-617]
+                          IntLiteral(1) [620-621]
+          TVFArgument [626-656]
+            StructConstructorWithKeyword [626-656]
+              StructConstructorArg [633-655]
+                IntLiteral(256) [633-636]
+                Alias [637-655]
+                  Identifier(maxOutputTokens) [640-655]
+          Hint [659-691]
+            HintEntry [661-690]
+              Identifier(remote_udf_max_rows_per_rpc) [661-688]
+              IntLiteral(1) [689-690]
+==
+# https://docs.cloud.google.com/spanner/docs/ml-tutorial-generative-ai#provide_personalized_product_recommendations_to_customers
+SELECT product_id, product_name, content
+FROM ML.PREDICT(
+  MODEL TextBison,
+  (SELECT
+    product.id as product_id,
+    product.name as product_name,
+    CONCAT(
+        "Answer with YES or NO only: Is this a good fit for me?",
+        "\nMy's age:", CAST(user.age AS STRING),
+        "\nI like:", user.likes,
+        "\nProduct Name: ", product.name,
+        "\nCategory Name: ", category.name,
+        "\nProduct Description:", product.description) AS prompt,
+   FROM
+     Products AS product
+       JOIN Categories AS category ON product.category_id = category.id
+       JOIN Users AS user ON user.id = 2),
+  STRUCT(40 AS maxOutputTokens)
+) @{remote_udf_max_rows_per_rpc=1}
+--
+QueryStatement [0-677]
+  Query [0-677]
+    Select [0-677]
+      SelectList [7-40]
+        SelectColumn [7-17]
+          PathExpression [7-17]
+            Identifier(product_id) [7-17]
+        SelectColumn [19-31]
+          PathExpression [19-31]
+            Identifier(product_name) [19-31]
+        SelectColumn [33-40]
+          PathExpression [33-40]
+            Identifier(content) [33-40]
+      FromClause [41-677]
+        TVF [46-677]
+          PathExpression [46-56]
+            Identifier(ML) [46-48]
+            Identifier(PREDICT) [49-56]
+          TVFArgument [60-75]
+            ModelClause [60-75]
+              PathExpression [66-75]
+                Identifier(TextBison) [66-75]
+          TVFArgument [79-609]
+            ExpressionSubquery [79-609]
+              Query [80-608]
+                Select [80-608]
+                  SelectList [91-462]
+                    SelectColumn [91-115]
+                      PathExpression [91-101]
+                        Identifier(product) [91-98]
+                        Identifier(id) [99-101]
+                      Alias [102-115]
+                        Identifier(product_id) [105-115]
+                    SelectColumn [121-149]
+                      PathExpression [121-133]
+                        Identifier(product) [121-128]
+                        Identifier(name) [129-133]
+                      Alias [134-149]
+                        Identifier(product_name) [137-149]
+                    SelectColumn [155-461]
+                      FunctionCall [155-451]
+                        PathExpression [155-161]
+                          Identifier(CONCAT) [155-161]
+                        StringLiteral [171-227]
+                          StringLiteralComponent("Answer with YES or NO only: Is this a good fit for me?") [171-227]
+                        StringLiteral [237-250]
+                          StringLiteralComponent("\nMy's age:") [237-250]
+                        CastExpression [252-276]
+                          PathExpression [257-265]
+                            Identifier(user) [257-261]
+                            Identifier(age) [262-265]
+                          SimpleType [269-275]
+                            PathExpression [269-275]
+                              Identifier(STRING) [269-275]
+                        StringLiteral [286-297]
+                          StringLiteralComponent("\nI like:") [286-297]
+                        PathExpression [299-309]
+                          Identifier(user) [299-303]
+                          Identifier(likes) [304-309]
+                        StringLiteral [319-337]
+                          StringLiteralComponent("\nProduct Name: ") [319-337]
+                        PathExpression [339-351]
+                          Identifier(product) [339-346]
+                          Identifier(name) [347-351]
+                        StringLiteral [361-380]
+                          StringLiteralComponent("\nCategory Name: ") [361-380]
+                        PathExpression [382-395]
+                          Identifier(category) [382-390]
+                          Identifier(name) [391-395]
+                        StringLiteral [405-429]
+                          StringLiteralComponent("\nProduct Description:") [405-429]
+                        PathExpression [431-450]
+                          Identifier(product) [431-438]
+                          Identifier(description) [439-450]
+                      Alias [452-461]
+                        Identifier(prompt) [455-461]
+                  FromClause [466-608]
+                    Join [476-608]
+                      Join [476-567]
+                        TablePathExpression [476-495]
+                          PathExpression [476-484]
+                            Identifier(Products) [476-484]
+                          Alias [485-495]
+                            Identifier(product) [488-495]
+                        Location [503-507]
+                        TablePathExpression [508-530]
+                          PathExpression [508-518]
+                            Identifier(Categories) [508-518]
+                          Alias [519-530]
+                            Identifier(category) [522-530]
+                        OnClause [531-567]
+                          BinaryExpression(=) [534-567]
+                            PathExpression [534-553]
+                              Identifier(product) [534-541]
+                              Identifier(category_id) [542-553]
+                            PathExpression [556-567]
+                              Identifier(category) [556-564]
+                              Identifier(id) [565-567]
+                      Location [575-579]
+                      TablePathExpression [580-593]
+                        PathExpression [580-585]
+                          Identifier(Users) [580-585]
+                        Alias [586-593]
+                          Identifier(user) [589-593]
+                      OnClause [594-608]
+                        BinaryExpression(=) [597-608]
+                          PathExpression [597-604]
+                            Identifier(user) [597-601]
+                            Identifier(id) [602-604]
+                          IntLiteral(2) [607-608]
+          TVFArgument [613-642]
+            StructConstructorWithKeyword [613-642]
+              StructConstructorArg [620-641]
+                IntLiteral(40) [620-622]
+                Alias [623-641]
+                  Identifier(maxOutputTokens) [626-641]
+          Hint [645-677]
+            HintEntry [647-676]
+              Identifier(remote_udf_max_rows_per_rpc) [647-674]
+              IntLiteral(1) [675-676]
diff --git a/parser/testdata/spanner/guides/ml-tutorial.test b/parser/testdata/spanner/guides/ml-tutorial.test
new file mode 100644
index 0000000..e27d697
--- /dev/null
+++ b/parser/testdata/spanner/guides/ml-tutorial.test
@@ -0,0 +1,58 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/ml-tutorial
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/ml-tutorial#examples
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT content
+FROM ML.PREDICT(
+  MODEL GeminiPro,
+  (SELECT "Is 7 a prime number?" AS prompt),
+  STRUCT(256 AS maxOutputTokens, 0.2 AS temperature, 40 as topK, 0.95 AS topP)
+)
+--
+QueryStatement [0-176]
+  Query [0-176]
+    Select [0-176]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(content) [7-14]
+      FromClause [15-176]
+        TVF [20-176]
+          PathExpression [20-30]
+            Identifier(ML) [20-22]
+            Identifier(PREDICT) [23-30]
+          TVFArgument [34-49]
+            ModelClause [34-49]
+              PathExpression [40-49]
+                Identifier(GeminiPro) [40-49]
+          TVFArgument [53-94]
+            ExpressionSubquery [53-94]
+              Query [54-93]
+                Select [54-93]
+                  SelectList [61-93]
+                    SelectColumn [61-93]
+                      StringLiteral [61-83]
+                        StringLiteralComponent("Is 7 a prime number?") [61-83]
+                      Alias [84-93]
+                        Identifier(prompt) [87-93]
+          TVFArgument [98-174]
+            StructConstructorWithKeyword [98-174]
+              StructConstructorArg [105-127]
+                IntLiteral(256) [105-108]
+                Alias [109-127]
+                  Identifier(maxOutputTokens) [112-127]
+              StructConstructorArg [129-147]
+                FloatLiteral(0.2) [129-132]
+                Alias [133-147]
+                  Identifier(temperature) [136-147]
+              StructConstructorArg [149-159]
+                IntLiteral(40) [149-151]
+                Alias [152-159]
+                  Identifier(topK) [155-159]
+              StructConstructorArg [161-173]
+                FloatLiteral(0.95) [161-165]
+                Alias [166-173]
+                  Identifier(topP) [169-173]
diff --git a/parser/testdata/spanner/guides/named-schemas.spanner.sql b/parser/testdata/spanner/guides/named-schemas.spanner.sql
new file mode 100644
index 0000000..c64026f
--- /dev/null
+++ b/parser/testdata/spanner/guides/named-schemas.spanner.sql
@@ -0,0 +1,39 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/named-schemas
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/named-schemas#add-fgac-to-named-schema
+# id 5f1bb1f70bc6: CREATE ROLE (named-schemas)
+# execute_query: INVALID_ARGUMENT: ROLE is not a supported object type [at 1:8]
+CREATE ROLE role1
+CREATE ROLE role2;
+
+# https://docs.cloud.google.com/spanner/docs/named-schemas#add-fgac-to-named-schema
+# id 2907f4ab8957: GRANT/REVOKE for fine-grained access control (named-schemas)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected keyword ALL [at 1:17]
+GRANT SELECT ON ALL TABLES IN SCHEMA sch1 TO ROLE role1;
+
+# https://docs.cloud.google.com/spanner/docs/named-schemas#add-fgac-to-named-schema
+# id 16552ab9a0e5: GRANT/REVOKE for fine-grained access control (named-schemas)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "@" or "@@" or string literal but got identifier "ROLE" [at 1:31]
+GRANT USAGE ON SCHEMA sch1 TO ROLE role1, role2;
+
+# https://docs.cloud.google.com/spanner/docs/named-schemas#add_fgac_to_default_schema
+# id 5101cbaad9f1: GRANT/REVOKE for fine-grained access control (named-schemas)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected keyword ALL [at 1:17]
+GRANT SELECT ON ALL TABLES IN SCHEMA default TO ROLE role1;
+
+# https://docs.cloud.google.com/spanner/docs/named-schemas#revoke_fgac_to_default_schema
+# id aaefbbaa683f: GRANT/REVOKE for fine-grained access control (named-schemas)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "." but got keyword DEFAULT [at 1:24]
+REVOKE USAGE ON SCHEMA DEFAULT FROM ROLE public;
+
+# https://docs.cloud.google.com/spanner/docs/named-schemas#revoke_fgac_to_default_schema
+# id d509cbcdf0c6: GRANT/REVOKE for fine-grained access control (named-schemas)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "." or keyword TO but got keyword DEFAULT [at 1:23]
+GRANT USAGE ON SCHEMA default to ROLE role1;
diff --git a/parser/testdata/spanner/guides/named-schemas.test b/parser/testdata/spanner/guides/named-schemas.test
new file mode 100644
index 0000000..ac60aea
--- /dev/null
+++ b/parser/testdata/spanner/guides/named-schemas.test
@@ -0,0 +1,447 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/named-schemas
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/named-schemas#create_a_named_schema
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE SEQUENCE sch1.sequence OPTIONS (
+  sequence_kind = 'bit_reversed_positive'
+)
+--
+CreateSequenceStatement [0-83]
+  PathExpression [16-29]
+    Identifier(sch1) [16-20]
+    Identifier(sequence) [21-29]
+  OptionsList [38-83]
+    OptionsEntry [42-81]
+      Identifier(sequence_kind) [42-55]
+      StringLiteral [58-81]
+        StringLiteralComponent('bit_reversed_positive') [58-81]
+==
+# https://docs.cloud.google.com/spanner/docs/named-schemas#create_a_named_schema
+CREATE TABLE Singers (
+  SingerId INT64 NOT NULL,
+  FirstName STRING(1024),
+  LastName STRING(1024),
+  SingerInfo BYTES(MAX),
+) PRIMARY KEY(SingerId)
+--
+CreateTableStatement [0-149]
+  PathExpression [13-20]
+    Identifier(Singers) [13-20]
+  TableElementList [21-127]
+    ColumnDefinition [25-48]
+      Identifier(SingerId) [25-33]
+      SimpleColumnSchema [34-48]
+        PathExpression [34-39]
+          Identifier(INT64) [34-39]
+        ColumnAttributeList [40-48]
+          NotNullColumnAttribute [40-48]
+    ColumnDefinition [52-74]
+      Identifier(FirstName) [52-61]
+      SimpleColumnSchema [62-74]
+        PathExpression [62-68]
+          Identifier(STRING) [62-68]
+        TypeParameterList [68-73]
+          IntLiteral(1024) [69-73]
+    ColumnDefinition [78-99]
+      Identifier(LastName) [78-86]
+      SimpleColumnSchema [87-99]
+        PathExpression [87-93]
+          Identifier(STRING) [87-93]
+        TypeParameterList [93-98]
+          IntLiteral(1024) [94-98]
+    ColumnDefinition [103-124]
+      Identifier(SingerInfo) [103-113]
+      SimpleColumnSchema [114-124]
+        PathExpression [114-119]
+          Identifier(BYTES) [114-119]
+        TypeParameterList [119-123]
+          MaxLiteral() [120-123]
+  SpannerTableOptions [128-149]
+    PrimaryKey(ENFORCED) [128-149]
+      PrimaryKeyElementList [139-149]
+        PrimaryKeyElement(ASC) [140-148]
+          Identifier(SingerId) [140-148]
+==
+# https://docs.cloud.google.com/spanner/docs/named-schemas#create_a_named_schema
+CREATE INDEX indexOnSingers ON Singers(FirstName)
+--
+CreateIndexStatement [0-49]
+  PathExpression [13-27]
+    Identifier(indexOnSingers) [13-27]
+  PathExpression [31-38]
+    Identifier(Singers) [31-38]
+  IndexItemList [38-49]
+    OrderingExpression(ASC) [39-48]
+      PathExpression [39-48]
+        Identifier(FirstName) [39-48]
+==
+# https://docs.cloud.google.com/spanner/docs/named-schemas#create_a_named_schema
+CREATE TABLE Albums (
+  SingerId INT64 NOT NULL,
+  AlbumId INT64 NOT NULL,
+  AlbumTitle STRING(MAX),
+) PRIMARY KEY(SingerId, AlbumId),
+  INTERLEAVE IN PARENT Singers ON DELETE CASCADE
+--
+CreateTableStatement [0-183]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-102]
+    ColumnDefinition [24-47]
+      Identifier(SingerId) [24-32]
+      SimpleColumnSchema [33-47]
+        PathExpression [33-38]
+          Identifier(INT64) [33-38]
+        ColumnAttributeList [39-47]
+          NotNullColumnAttribute [39-47]
+    ColumnDefinition [51-73]
+      Identifier(AlbumId) [51-58]
+      SimpleColumnSchema [59-73]
+        PathExpression [59-64]
+          Identifier(INT64) [59-64]
+        ColumnAttributeList [65-73]
+          NotNullColumnAttribute [65-73]
+    ColumnDefinition [77-99]
+      Identifier(AlbumTitle) [77-87]
+      SimpleColumnSchema [88-99]
+        PathExpression [88-94]
+          Identifier(STRING) [88-94]
+        TypeParameterList [94-98]
+          MaxLiteral() [95-98]
+  SpannerTableOptions [103-183]
+    PrimaryKey(ENFORCED) [103-133]
+      PrimaryKeyElementList [114-133]
+        PrimaryKeyElement(ASC) [115-123]
+          Identifier(SingerId) [115-123]
+        PrimaryKeyElement(ASC) [125-132]
+          Identifier(AlbumId) [125-132]
+    SpannerInterleaveClause [133-183]
+      PathExpression [158-165]
+        Identifier(Singers) [158-165]
+==
+# https://docs.cloud.google.com/spanner/docs/named-schemas#create_a_named_schema
+CREATE TABLE Songs (
+  SingerId INT64 NOT NULL,
+  AlbumId INT64 NOT NULL,
+  TrackId INT64 NOT NULL,
+  SongName STRING(MAX),
+) PRIMARY KEY(SingerId, AlbumId, TrackId),
+  INTERLEAVE IN PARENT Albums ON DELETE CASCADE
+--
+CreateTableStatement [0-214]
+  PathExpression [13-18]
+    Identifier(Songs) [13-18]
+  TableElementList [19-125]
+    ColumnDefinition [23-46]
+      Identifier(SingerId) [23-31]
+      SimpleColumnSchema [32-46]
+        PathExpression [32-37]
+          Identifier(INT64) [32-37]
+        ColumnAttributeList [38-46]
+          NotNullColumnAttribute [38-46]
+    ColumnDefinition [50-72]
+      Identifier(AlbumId) [50-57]
+      SimpleColumnSchema [58-72]
+        PathExpression [58-63]
+          Identifier(INT64) [58-63]
+        ColumnAttributeList [64-72]
+          NotNullColumnAttribute [64-72]
+    ColumnDefinition [76-98]
+      Identifier(TrackId) [76-83]
+      SimpleColumnSchema [84-98]
+        PathExpression [84-89]
+          Identifier(INT64) [84-89]
+        ColumnAttributeList [90-98]
+          NotNullColumnAttribute [90-98]
+    ColumnDefinition [102-122]
+      Identifier(SongName) [102-110]
+      SimpleColumnSchema [111-122]
+        PathExpression [111-117]
+          Identifier(STRING) [111-117]
+        TypeParameterList [117-121]
+          MaxLiteral() [118-121]
+  SpannerTableOptions [126-214]
+    PrimaryKey(ENFORCED) [126-165]
+      PrimaryKeyElementList [137-165]
+        PrimaryKeyElement(ASC) [138-146]
+          Identifier(SingerId) [138-146]
+        PrimaryKeyElement(ASC) [148-155]
+          Identifier(AlbumId) [148-155]
+        PrimaryKeyElement(ASC) [157-164]
+          Identifier(TrackId) [157-164]
+    SpannerInterleaveClause [165-214]
+      PathExpression [190-196]
+        Identifier(Albums) [190-196]
+==
+# https://docs.cloud.google.com/spanner/docs/named-schemas#create_a_named_schema
+CREATE TABLE sch1.Singers (
+  SingerId INT64 NOT NULL,
+  FirstName STRING(1024),
+  LastName STRING(1024),
+  SingerInfo BYTES(MAX),
+) PRIMARY KEY(SingerId)
+--
+CreateTableStatement [0-154]
+  PathExpression [13-25]
+    Identifier(sch1) [13-17]
+    Identifier(Singers) [18-25]
+  TableElementList [26-132]
+    ColumnDefinition [30-53]
+      Identifier(SingerId) [30-38]
+      SimpleColumnSchema [39-53]
+        PathExpression [39-44]
+          Identifier(INT64) [39-44]
+        ColumnAttributeList [45-53]
+          NotNullColumnAttribute [45-53]
+    ColumnDefinition [57-79]
+      Identifier(FirstName) [57-66]
+      SimpleColumnSchema [67-79]
+        PathExpression [67-73]
+          Identifier(STRING) [67-73]
+        TypeParameterList [73-78]
+          IntLiteral(1024) [74-78]
+    ColumnDefinition [83-104]
+      Identifier(LastName) [83-91]
+      SimpleColumnSchema [92-104]
+        PathExpression [92-98]
+          Identifier(STRING) [92-98]
+        TypeParameterList [98-103]
+          IntLiteral(1024) [99-103]
+    ColumnDefinition [108-129]
+      Identifier(SingerInfo) [108-118]
+      SimpleColumnSchema [119-129]
+        PathExpression [119-124]
+          Identifier(BYTES) [119-124]
+        TypeParameterList [124-128]
+          MaxLiteral() [125-128]
+  SpannerTableOptions [133-154]
+    PrimaryKey(ENFORCED) [133-154]
+      PrimaryKeyElementList [144-154]
+        PrimaryKeyElement(ASC) [145-153]
+          Identifier(SingerId) [145-153]
+==
+# https://docs.cloud.google.com/spanner/docs/named-schemas#create_a_named_schema
+CREATE INDEX sch1.indexOnSingers ON sch1.Singers(FirstName)
+--
+CreateIndexStatement [0-59]
+  PathExpression [13-32]
+    Identifier(sch1) [13-17]
+    Identifier(indexOnSingers) [18-32]
+  PathExpression [36-48]
+    Identifier(sch1) [36-40]
+    Identifier(Singers) [41-48]
+  IndexItemList [48-59]
+    OrderingExpression(ASC) [49-58]
+      PathExpression [49-58]
+        Identifier(FirstName) [49-58]
+==
+# https://docs.cloud.google.com/spanner/docs/named-schemas#create_a_named_schema
+CREATE TABLE sch1.Albums (
+  SingerId INT64 NOT NULL,
+  AlbumId INT64 NOT NULL,
+  AlbumTitle STRING(MAX),
+) PRIMARY KEY(SingerId, AlbumId),
+  INTERLEAVE IN PARENT sch1.Singers ON DELETE CASCADE
+--
+CreateTableStatement [0-193]
+  PathExpression [13-24]
+    Identifier(sch1) [13-17]
+    Identifier(Albums) [18-24]
+  TableElementList [25-107]
+    ColumnDefinition [29-52]
+      Identifier(SingerId) [29-37]
+      SimpleColumnSchema [38-52]
+        PathExpression [38-43]
+          Identifier(INT64) [38-43]
+        ColumnAttributeList [44-52]
+          NotNullColumnAttribute [44-52]
+    ColumnDefinition [56-78]
+      Identifier(AlbumId) [56-63]
+      SimpleColumnSchema [64-78]
+        PathExpression [64-69]
+          Identifier(INT64) [64-69]
+        ColumnAttributeList [70-78]
+          NotNullColumnAttribute [70-78]
+    ColumnDefinition [82-104]
+      Identifier(AlbumTitle) [82-92]
+      SimpleColumnSchema [93-104]
+        PathExpression [93-99]
+          Identifier(STRING) [93-99]
+        TypeParameterList [99-103]
+          MaxLiteral() [100-103]
+  SpannerTableOptions [108-193]
+    PrimaryKey(ENFORCED) [108-138]
+      PrimaryKeyElementList [119-138]
+        PrimaryKeyElement(ASC) [120-128]
+          Identifier(SingerId) [120-128]
+        PrimaryKeyElement(ASC) [130-137]
+          Identifier(AlbumId) [130-137]
+    SpannerInterleaveClause [138-193]
+      PathExpression [163-175]
+        Identifier(sch1) [163-167]
+        Identifier(Singers) [168-175]
+==
+# https://docs.cloud.google.com/spanner/docs/named-schemas#create_a_named_schema
+CREATE TABLE sch1.Songs (
+  SingerId INT64 NOT NULL,
+  AlbumId INT64 NOT NULL,
+  TrackId INT64 NOT NULL,
+  SongName STRING(MAX),
+) PRIMARY KEY(SingerId, AlbumId, TrackId),
+  INTERLEAVE IN PARENT sch1.Albums ON DELETE CASCADE
+--
+CreateTableStatement [0-224]
+  PathExpression [13-23]
+    Identifier(sch1) [13-17]
+    Identifier(Songs) [18-23]
+  TableElementList [24-130]
+    ColumnDefinition [28-51]
+      Identifier(SingerId) [28-36]
+      SimpleColumnSchema [37-51]
+        PathExpression [37-42]
+          Identifier(INT64) [37-42]
+        ColumnAttributeList [43-51]
+          NotNullColumnAttribute [43-51]
+    ColumnDefinition [55-77]
+      Identifier(AlbumId) [55-62]
+      SimpleColumnSchema [63-77]
+        PathExpression [63-68]
+          Identifier(INT64) [63-68]
+        ColumnAttributeList [69-77]
+          NotNullColumnAttribute [69-77]
+    ColumnDefinition [81-103]
+      Identifier(TrackId) [81-88]
+      SimpleColumnSchema [89-103]
+        PathExpression [89-94]
+          Identifier(INT64) [89-94]
+        ColumnAttributeList [95-103]
+          NotNullColumnAttribute [95-103]
+    ColumnDefinition [107-127]
+      Identifier(SongName) [107-115]
+      SimpleColumnSchema [116-127]
+        PathExpression [116-122]
+          Identifier(STRING) [116-122]
+        TypeParameterList [122-126]
+          MaxLiteral() [123-126]
+  SpannerTableOptions [131-224]
+    PrimaryKey(ENFORCED) [131-170]
+      PrimaryKeyElementList [142-170]
+        PrimaryKeyElement(ASC) [143-151]
+          Identifier(SingerId) [143-151]
+        PrimaryKeyElement(ASC) [153-160]
+          Identifier(AlbumId) [153-160]
+        PrimaryKeyElement(ASC) [162-169]
+          Identifier(TrackId) [162-169]
+    SpannerInterleaveClause [170-224]
+      PathExpression [195-206]
+        Identifier(sch1) [195-199]
+        Identifier(Albums) [200-206]
+==
+# https://docs.cloud.google.com/spanner/docs/named-schemas#create_a_named_schema
+CREATE VIEW sch1.SingerView SQL SECURITY INVOKER
+  AS Select s.FirstName, s.LastName, s.SingerInfo
+  FROM sch1.Singers AS s WHERE s.SingerId = 123456
+--
+CreateViewStatement(SQL SECURITY INVOKER) [0-149]
+  PathExpression [12-27]
+    Identifier(sch1) [12-16]
+    Identifier(SingerView) [17-27]
+  Query [54-149]
+    Select [54-149]
+      SelectList [61-98]
+        SelectColumn [61-72]
+          PathExpression [61-72]
+            Identifier(s) [61-62]
+            Identifier(FirstName) [63-72]
+        SelectColumn [74-84]
+          PathExpression [74-84]
+            Identifier(s) [74-75]
+            Identifier(LastName) [76-84]
+        SelectColumn [86-98]
+          PathExpression [86-98]
+            Identifier(s) [86-87]
+            Identifier(SingerInfo) [88-98]
+      FromClause [101-123]
+        TablePathExpression [106-123]
+          PathExpression [106-118]
+            Identifier(sch1) [106-110]
+            Identifier(Singers) [111-118]
+          Alias [119-123]
+            Identifier(s) [122-123]
+      WhereClause [124-149]
+        BinaryExpression(=) [130-149]
+          PathExpression [130-140]
+            Identifier(s) [130-131]
+            Identifier(SingerId) [132-140]
+          IntLiteral(123456) [143-149]
+==
+# https://docs.cloud.google.com/spanner/docs/named-schemas#create_a_named_schema
+CREATE VIEW SingerView SQL SECURITY INVOKER
+  AS Select s.FirstName, s.LastName, s.SingerInfo
+  FROM Singers AS s WHERE s.SingerId = 123456
+--
+CreateViewStatement(SQL SECURITY INVOKER) [0-139]
+  PathExpression [12-22]
+    Identifier(SingerView) [12-22]
+  Query [49-139]
+    Select [49-139]
+      SelectList [56-93]
+        SelectColumn [56-67]
+          PathExpression [56-67]
+            Identifier(s) [56-57]
+            Identifier(FirstName) [58-67]
+        SelectColumn [69-79]
+          PathExpression [69-79]
+            Identifier(s) [69-70]
+            Identifier(LastName) [71-79]
+        SelectColumn [81-93]
+          PathExpression [81-93]
+            Identifier(s) [81-82]
+            Identifier(SingerInfo) [83-93]
+      FromClause [96-113]
+        TablePathExpression [101-113]
+          PathExpression [101-108]
+            Identifier(Singers) [101-108]
+          Alias [109-113]
+            Identifier(s) [112-113]
+      WhereClause [114-139]
+        BinaryExpression(=) [120-139]
+          PathExpression [120-130]
+            Identifier(s) [120-121]
+            Identifier(SingerId) [122-130]
+          IntLiteral(123456) [133-139]
+==
+# https://docs.cloud.google.com/spanner/docs/named-schemas#view_named_schemas
+SELECT schema_name, schema_owner
+  FROM information_schema.schemata
+  ORDER BY schema_owner
+--
+QueryStatement [0-91]
+  Query [0-91]
+    Select [0-67]
+      SelectList [7-32]
+        SelectColumn [7-18]
+          PathExpression [7-18]
+            Identifier(schema_name) [7-18]
+        SelectColumn [20-32]
+          PathExpression [20-32]
+            Identifier(schema_owner) [20-32]
+      FromClause [35-67]
+        TablePathExpression [40-67]
+          PathExpression [40-67]
+            Identifier(information_schema) [40-58]
+            Identifier(schemata) [59-67]
+    OrderBy [70-91]
+      OrderingExpression(ASC) [79-91]
+        PathExpression [79-91]
+          Identifier(schema_owner) [79-91]
+==
+# https://docs.cloud.google.com/spanner/docs/named-schemas#drop_a_named_schema
+DROP SCHEMA IF EXISTS sch1
+--
+DropStatement SCHEMA(is_if_exists) [0-26]
+  PathExpression [22-26]
+    Identifier(sch1) [22-26]
diff --git a/parser/testdata/spanner/guides/pre-splitting-overview.test b/parser/testdata/spanner/guides/pre-splitting-overview.test
new file mode 100644
index 0000000..7631075
--- /dev/null
+++ b/parser/testdata/spanner/guides/pre-splitting-overview.test
@@ -0,0 +1,93 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/pre-splitting-overview
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/pre-splitting-overview#sample_workflow_for_determining_split_points
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE UserInfo (
+ UserId INT64 NOT NULL,
+ Info BYTES(MAX),
+) PRIMARY KEY (UserId)
+--
+CreateTableStatement [0-88]
+  PathExpression [13-21]
+    Identifier(UserInfo) [13-21]
+  TableElementList [22-67]
+    ColumnDefinition [25-46]
+      Identifier(UserId) [25-31]
+      SimpleColumnSchema [32-46]
+        PathExpression [32-37]
+          Identifier(INT64) [32-37]
+        ColumnAttributeList [38-46]
+          NotNullColumnAttribute [38-46]
+    ColumnDefinition [49-64]
+      Identifier(Info) [49-53]
+      SimpleColumnSchema [54-64]
+        PathExpression [54-59]
+          Identifier(BYTES) [54-59]
+        TypeParameterList [59-63]
+          MaxLiteral() [60-63]
+  SpannerTableOptions [68-88]
+    PrimaryKey(ENFORCED) [68-88]
+      PrimaryKeyElementList [80-88]
+        PrimaryKeyElement(ASC) [81-87]
+          Identifier(UserId) [81-87]
+==
+# https://docs.cloud.google.com/spanner/docs/pre-splitting-overview#sample_workflow_for_determining_split_points
+CREATE TABLE UserLocationInfo (
+ UserId INT64 NOT NULL,
+ LocationId STRING(MAX) NOT NULL,
+ ActivityData BYTES(MAX),
+) PRIMARY KEY (UserId, LocationId), INTERLEAVE IN PARENT UserInfo ON DELETE CASCADE
+--
+CreateTableStatement [0-199]
+  PathExpression [13-29]
+    Identifier(UserLocationInfo) [13-29]
+  TableElementList [30-117]
+    ColumnDefinition [33-54]
+      Identifier(UserId) [33-39]
+      SimpleColumnSchema [40-54]
+        PathExpression [40-45]
+          Identifier(INT64) [40-45]
+        ColumnAttributeList [46-54]
+          NotNullColumnAttribute [46-54]
+    ColumnDefinition [57-88]
+      Identifier(LocationId) [57-67]
+      SimpleColumnSchema [68-88]
+        PathExpression [68-74]
+          Identifier(STRING) [68-74]
+        TypeParameterList [74-78]
+          MaxLiteral() [75-78]
+        ColumnAttributeList [80-88]
+          NotNullColumnAttribute [80-88]
+    ColumnDefinition [91-114]
+      Identifier(ActivityData) [91-103]
+      SimpleColumnSchema [104-114]
+        PathExpression [104-109]
+          Identifier(BYTES) [104-109]
+        TypeParameterList [109-113]
+          MaxLiteral() [110-113]
+  SpannerTableOptions [118-199]
+    PrimaryKey(ENFORCED) [118-150]
+      PrimaryKeyElementList [130-150]
+        PrimaryKeyElement(ASC) [131-137]
+          Identifier(UserId) [131-137]
+        PrimaryKeyElement(ASC) [139-149]
+          Identifier(LocationId) [139-149]
+    SpannerInterleaveClause [150-199]
+      PathExpression [173-181]
+        Identifier(UserInfo) [173-181]
+==
+# https://docs.cloud.google.com/spanner/docs/pre-splitting-overview#sample_workflow_for_determining_split_points
+CREATE INDEX UsersByLocation ON UserLocationInfo(LocationId)
+--
+CreateIndexStatement [0-60]
+  PathExpression [13-28]
+    Identifier(UsersByLocation) [13-28]
+  PathExpression [32-48]
+    Identifier(UserLocationInfo) [32-48]
+  IndexItemList [48-60]
+    OrderingExpression(ASC) [49-59]
+      PathExpression [49-59]
+        Identifier(LocationId) [49-59]
diff --git a/parser/testdata/spanner/guides/primary-key-default-value.spanner.sql b/parser/testdata/spanner/guides/primary-key-default-value.spanner.sql
new file mode 100644
index 0000000..943a978
--- /dev/null
+++ b/parser/testdata/spanner/guides/primary-key-default-value.spanner.sql
@@ -0,0 +1,52 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/primary-key-default-value
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/primary-key-default-value#identity-columns
+# id 57c70c72e944: identity columns (primary-key-default-value)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ")" but got identifier "BIT_REVERSED_POSITIVE" [at 2:52]
+CREATE TABLE Singers (
+  SingerId INT64 GENERATED BY DEFAULT AS IDENTITY (BIT_REVERSED_POSITIVE),
+  Name STRING(MAX),
+  Rank INT64
+) PRIMARY KEY (SingerId);
+
+# https://docs.cloud.google.com/spanner/docs/primary-key-default-value#identity-columns
+# id f19adcb13ef5: identity columns (primary-key-default-value)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ")" but got identifier "BIT_REVERSED_POSITIVE" [at 2:52]
+CREATE TABLE Singers (
+  SingerId INT64 GENERATED BY DEFAULT AS IDENTITY (BIT_REVERSED_POSITIVE START COUNTER WITH 1000),
+  Name STRING(MAX),
+  Rank INT64
+) PRIMARY KEY (SingerId);
+
+# https://docs.cloud.google.com/spanner/docs/primary-key-default-value#serial-auto-increment
+# id cbdc2fb90b11: AUTO_INCREMENT columns (primary-key-default-value)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ")" or "," but got identifier "AUTO_INCREMENT" [at 2:12]
+CREATE TABLE Singers (
+  id INT64 AUTO_INCREMENT PRIMARY KEY,
+  name STRING(MAX),
+);
+
+# https://docs.cloud.google.com/spanner/docs/primary-key-default-value#serial-auto-increment
+# id e19b178d0d2e: identity columns (primary-key-default-value)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected "(" but got "," [at 2:44]
+CREATE TABLE Singers (
+  id INT64 GENERATED BY DEFAULT AS IDENTITY,
+  name STRING(MAX)
+) PRIMARY KEY (id);
+
+# https://docs.cloud.google.com/spanner/docs/primary-key-default-value#tables-without-primary-keys
+# id 2fad4777471c: identity columns (primary-key-default-value)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ")" or "," but got keyword GENERATED [at 4:24]
+CREATE TABLE Singers (
+  Name STRING(MAX),
+  Rank INT64,
+  rowid INT64 NOT NULL GENERATED BY DEFAULT AS IDENTITY
+    (BIT_REVERSED_POSITIVE) HIDDEN
+) PRIMARY KEY (rowid);
diff --git a/parser/testdata/spanner/guides/primary-key-default-value.test b/parser/testdata/spanner/guides/primary-key-default-value.test
new file mode 100644
index 0000000..01a1db6
--- /dev/null
+++ b/parser/testdata/spanner/guides/primary-key-default-value.test
@@ -0,0 +1,233 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/primary-key-default-value
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/primary-key-default-value#universally_unique_identifier_uuid
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Fans (
+  FanId UUID DEFAULT (NEW_UUID()),
+  Name STRING(MAX),
+) PRIMARY KEY (FanId)
+--
+CreateTableStatement [0-96]
+  PathExpression [13-17]
+    Identifier(Fans) [13-17]
+  TableElementList [18-76]
+    ColumnDefinition [22-53]
+      Identifier(FanId) [22-27]
+      SimpleColumnSchema [28-53]
+        PathExpression [28-32]
+          Identifier(UUID) [28-32]
+        FunctionCall [42-52]
+          PathExpression [42-50]
+            Identifier(NEW_UUID) [42-50]
+    ColumnDefinition [57-73]
+      Identifier(Name) [57-61]
+      SimpleColumnSchema [62-73]
+        PathExpression [62-68]
+          Identifier(STRING) [62-68]
+        TypeParameterList [68-72]
+          MaxLiteral() [69-72]
+  SpannerTableOptions [77-96]
+    PrimaryKey(ENFORCED) [77-96]
+      PrimaryKeyElementList [89-96]
+        PrimaryKeyElement(ASC) [90-95]
+          Identifier(FanId) [90-95]
+==
+# https://docs.cloud.google.com/spanner/docs/primary-key-default-value#universally_unique_identifier_uuid
+INSERT INTO Fans (Name) VALUES ('Melissa Garcia')
+THEN RETURN FanId
+--
+InsertStatement [0-67]
+  PathExpression [12-16]
+    Identifier(Fans) [12-16]
+  ColumnList [17-23]
+    Identifier(Name) [18-22]
+  InsertValuesRowList [24-49]
+    InsertValuesRow [31-49]
+      StringLiteral [32-48]
+        StringLiteralComponent('Melissa Garcia') [32-48]
+  ReturningClause [50-67]
+    SelectList [62-67]
+      SelectColumn [62-67]
+        PathExpression [62-67]
+          Identifier(FanId) [62-67]
+==
+# https://docs.cloud.google.com/spanner/docs/primary-key-default-value#serial-auto-increment
+ALTER DATABASE db SET OPTIONS (default_sequence_kind = 'bit_reversed_positive')
+--
+AlterDatabaseStatement [0-79]
+  PathExpression [15-17]
+    Identifier(db) [15-17]
+  AlterActionList [18-79]
+    SetOptionsAction [18-79]
+      OptionsList [30-79]
+        OptionsEntry [31-78]
+          Identifier(default_sequence_kind) [31-52]
+          StringLiteral [55-78]
+            StringLiteralComponent('bit_reversed_positive') [55-78]
+==
+# https://docs.cloud.google.com/spanner/docs/primary-key-default-value#bit-reversed-sequence
+CREATE SEQUENCE SingerIdSequence OPTIONS (
+  sequence_kind="bit_reversed_positive"
+)
+--
+CreateSequenceStatement [0-84]
+  PathExpression [16-32]
+    Identifier(SingerIdSequence) [16-32]
+  OptionsList [41-84]
+    OptionsEntry [45-82]
+      Identifier(sequence_kind) [45-58]
+      StringLiteral [59-82]
+        StringLiteralComponent("bit_reversed_positive") [59-82]
+==
+# https://docs.cloud.google.com/spanner/docs/primary-key-default-value#bit-reversed-sequence
+CREATE TABLE Singers (
+  SingerId INT64 DEFAULT (GET_NEXT_SEQUENCE_VALUE(SEQUENCE SingerIdSequence)),
+  Name STRING(MAX),
+  Rank INT64
+) PRIMARY KEY (SingerId)
+--
+CreateTableStatement [0-159]
+  PathExpression [13-20]
+    Identifier(Singers) [13-20]
+  TableElementList [21-136]
+    ColumnDefinition [25-100]
+      Identifier(SingerId) [25-33]
+      SimpleColumnSchema [34-100]
+        PathExpression [34-39]
+          Identifier(INT64) [34-39]
+        FunctionCall [49-99]
+          PathExpression [49-72]
+            Identifier(GET_NEXT_SEQUENCE_VALUE) [49-72]
+          SequenceArg [73-98]
+            PathExpression [82-98]
+              Identifier(SingerIdSequence) [82-98]
+    ColumnDefinition [104-120]
+      Identifier(Name) [104-108]
+      SimpleColumnSchema [109-120]
+        PathExpression [109-115]
+          Identifier(STRING) [109-115]
+        TypeParameterList [115-119]
+          MaxLiteral() [116-119]
+    ColumnDefinition [124-134]
+      Identifier(Rank) [124-128]
+      SimpleColumnSchema [129-134]
+        PathExpression [129-134]
+          Identifier(INT64) [129-134]
+  SpannerTableOptions [137-159]
+    PrimaryKey(ENFORCED) [137-159]
+      PrimaryKeyElementList [149-159]
+        PrimaryKeyElement(ASC) [150-158]
+          Identifier(SingerId) [150-158]
+==
+# https://docs.cloud.google.com/spanner/docs/primary-key-default-value#bit-reversed-sequence
+INSERT INTO Singers (Name) VALUES ('Example Singer')
+THEN RETURN SingerId
+--
+InsertStatement [0-73]
+  PathExpression [12-19]
+    Identifier(Singers) [12-19]
+  ColumnList [20-26]
+    Identifier(Name) [21-25]
+  InsertValuesRowList [27-52]
+    InsertValuesRow [34-52]
+      StringLiteral [35-51]
+        StringLiteralComponent('Example Singer') [35-51]
+  ReturningClause [53-73]
+    SelectList [65-73]
+      SelectColumn [65-73]
+        PathExpression [65-73]
+          Identifier(SingerId) [65-73]
+==
+# https://docs.cloud.google.com/spanner/docs/primary-key-default-value#tables-without-primary-keys
+CREATE TABLE Singers (
+  Name STRING(MAX),
+  Rank INT64
+)
+--
+CreateTableStatement [0-57]
+  PathExpression [13-20]
+    Identifier(Singers) [13-20]
+  TableElementList [21-57]
+    ColumnDefinition [25-41]
+      Identifier(Name) [25-29]
+      SimpleColumnSchema [30-41]
+        PathExpression [30-36]
+          Identifier(STRING) [30-36]
+        TypeParameterList [36-40]
+          MaxLiteral() [37-40]
+    ColumnDefinition [45-55]
+      Identifier(Rank) [45-49]
+      SimpleColumnSchema [50-55]
+        PathExpression [50-55]
+          Identifier(INT64) [50-55]
+==
+# https://docs.cloud.google.com/spanner/docs/primary-key-default-value#tables-without-primary-keys
+CREATE TABLE Singers (
+  rowid INT64,
+  Name STRING(MAX),
+)
+--
+CreateTableStatement [0-59]
+  PathExpression [13-20]
+    Identifier(Singers) [13-20]
+  TableElementList [21-59]
+    ColumnDefinition [25-36]
+      Identifier(rowid) [25-30]
+      SimpleColumnSchema [31-36]
+        PathExpression [31-36]
+          Identifier(INT64) [31-36]
+    ColumnDefinition [40-56]
+      Identifier(Name) [40-44]
+      SimpleColumnSchema [45-56]
+        PathExpression [45-51]
+          Identifier(STRING) [45-51]
+        TypeParameterList [51-55]
+          MaxLiteral() [52-55]
+==
+# https://docs.cloud.google.com/spanner/docs/primary-key-default-value#tables-without-primary-keys
+SELECT rowid FROM Singers
+--
+QueryStatement [0-25]
+  Query [0-25]
+    Select [0-25]
+      SelectList [7-12]
+        SelectColumn [7-12]
+          PathExpression [7-12]
+            Identifier(rowid) [7-12]
+      FromClause [13-25]
+        TablePathExpression [18-25]
+          PathExpression [18-25]
+            Identifier(Singers) [18-25]
+==
+# https://docs.cloud.google.com/spanner/docs/primary-key-default-value#tables-without-primary-keys
+SELECT column_name
+FROM information_schema.key_column_usage
+WHERE constraint_name LIKE 'PK_%' AND table_name = 'Singers'
+--
+QueryStatement [0-120]
+  Query [0-120]
+    Select [0-120]
+      SelectList [7-18]
+        SelectColumn [7-18]
+          PathExpression [7-18]
+            Identifier(column_name) [7-18]
+      FromClause [19-59]
+        TablePathExpression [24-59]
+          PathExpression [24-59]
+            Identifier(information_schema) [24-42]
+            Identifier(key_column_usage) [43-59]
+      WhereClause [60-120]
+        AndExpr [66-120]
+          BinaryExpression(LIKE) [66-93]
+            PathExpression [66-81]
+              Identifier(constraint_name) [66-81]
+            StringLiteral [87-93]
+              StringLiteralComponent('PK_%') [87-93]
+          BinaryExpression(=) [98-120]
+            PathExpression [98-108]
+              Identifier(table_name) [98-108]
+            StringLiteral [111-120]
+              StringLiteralComponent('Singers') [111-120]
diff --git a/parser/testdata/spanner/guides/query-columnar-data.test b/parser/testdata/spanner/guides/query-columnar-data.test
new file mode 100644
index 0000000..fdc6419
--- /dev/null
+++ b/parser/testdata/spanner/guides/query-columnar-data.test
@@ -0,0 +1,35 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/query-columnar-data
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/query-columnar-data#query-columnar-data-using-bigquery
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT SUM(MarketingBudget) AS total_marketing_spend
+FROM
+  EXTERNAL_QUERY(
+    'my-project.us.albums',
+    '@{scan_method=columnar} SELECT AlbumInfo.MarketingBudget FROM AlbumInfo WHERE AlbumInfo.MarketingBudget < 500000;')
+--
+QueryStatement [0-224]
+  Query [0-224]
+    Select [0-224]
+      SelectList [7-52]
+        SelectColumn [7-52]
+          FunctionCall [7-27]
+            PathExpression [7-10]
+              Identifier(SUM) [7-10]
+            PathExpression [11-26]
+              Identifier(MarketingBudget) [11-26]
+          Alias [28-52]
+            Identifier(total_marketing_spend) [31-52]
+      FromClause [53-224]
+        TVF [60-224]
+          PathExpression [60-74]
+            Identifier(EXTERNAL_QUERY) [60-74]
+          TVFArgument [80-102]
+            StringLiteral [80-102]
+              StringLiteralComponent('my-project.us.albums') [80-102]
+          TVFArgument [108-223]
+            StringLiteral [108-223]
+              StringLiteralComponent('@{scan_method=columnar} SELECT AlbumInfo.MarketingBudget FROM AlbumInfo WHERE AlbumInfo.MarketingBudget < 500000;') [108-223]
diff --git a/parser/testdata/spanner/guides/query-execution-plans.test b/parser/testdata/spanner/guides/query-execution-plans.test
new file mode 100644
index 0000000..307156d
--- /dev/null
+++ b/parser/testdata/spanner/guides/query-execution-plans.test
@@ -0,0 +1,481 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/query-execution-plans
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/query-execution-plans#overview
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName)
+--
+CreateIndexStatement [0-67]
+  PathExpression [13-35]
+    Identifier(SingersByFirstLastName) [13-35]
+  PathExpression [39-46]
+    Identifier(Singers) [39-46]
+  IndexItemList [46-67]
+    OrderingExpression(ASC) [47-56]
+      PathExpression [47-56]
+        Identifier(FirstName) [47-56]
+    OrderingExpression(ASC) [58-66]
+      PathExpression [58-66]
+        Identifier(LastName) [58-66]
+==
+# https://docs.cloud.google.com/spanner/docs/query-execution-plans#overview
+CREATE TABLE Albums (
+  SingerId        INT64 NOT NULL,
+  AlbumId         INT64 NOT NULL,
+  AlbumTitle      STRING(MAX),
+  MarketingBudget INT64
+) PRIMARY KEY(SingerId, AlbumId),
+  INTERLEAVE IN PARENT Singers ON DELETE CASCADE
+--
+CreateTableStatement [0-227]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-146]
+    ColumnDefinition [24-54]
+      Identifier(SingerId) [24-32]
+      SimpleColumnSchema [40-54]
+        PathExpression [40-45]
+          Identifier(INT64) [40-45]
+        ColumnAttributeList [46-54]
+          NotNullColumnAttribute [46-54]
+    ColumnDefinition [58-88]
+      Identifier(AlbumId) [58-65]
+      SimpleColumnSchema [74-88]
+        PathExpression [74-79]
+          Identifier(INT64) [74-79]
+        ColumnAttributeList [80-88]
+          NotNullColumnAttribute [80-88]
+    ColumnDefinition [92-119]
+      Identifier(AlbumTitle) [92-102]
+      SimpleColumnSchema [108-119]
+        PathExpression [108-114]
+          Identifier(STRING) [108-114]
+        TypeParameterList [114-118]
+          MaxLiteral() [115-118]
+    ColumnDefinition [123-144]
+      Identifier(MarketingBudget) [123-138]
+      SimpleColumnSchema [139-144]
+        PathExpression [139-144]
+          Identifier(INT64) [139-144]
+  SpannerTableOptions [147-227]
+    PrimaryKey(ENFORCED) [147-177]
+      PrimaryKeyElementList [158-177]
+        PrimaryKeyElement(ASC) [159-167]
+          Identifier(SingerId) [159-167]
+        PrimaryKeyElement(ASC) [169-176]
+          Identifier(AlbumId) [169-176]
+    SpannerInterleaveClause [177-227]
+      PathExpression [202-209]
+        Identifier(Singers) [202-209]
+==
+# https://docs.cloud.google.com/spanner/docs/query-execution-plans#overview
+CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)
+--
+CreateIndexStatement [0-53]
+  PathExpression [13-31]
+    Identifier(AlbumsByAlbumTitle) [13-31]
+  PathExpression [35-41]
+    Identifier(Albums) [35-41]
+  IndexItemList [41-53]
+    OrderingExpression(ASC) [42-52]
+      PathExpression [42-52]
+        Identifier(AlbumTitle) [42-52]
+==
+# https://docs.cloud.google.com/spanner/docs/query-execution-plans#overview
+CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) STORING (MarketingBudget)
+--
+CreateIndexStatement [0-80]
+  PathExpression [13-32]
+    Identifier(AlbumsByAlbumTitle2) [13-32]
+  PathExpression [36-42]
+    Identifier(Albums) [36-42]
+  IndexItemList [42-54]
+    OrderingExpression(ASC) [43-53]
+      PathExpression [43-53]
+        Identifier(AlbumTitle) [43-53]
+  IndexStoringExpressionList [63-80]
+    PathExpression [64-79]
+      Identifier(MarketingBudget) [64-79]
+==
+# https://docs.cloud.google.com/spanner/docs/query-execution-plans#overview
+CREATE TABLE Songs (
+  SingerId  INT64 NOT NULL,
+  AlbumId   INT64 NOT NULL,
+  TrackId   INT64 NOT NULL,
+  SongName  STRING(MAX),
+  Duration  INT64,
+  SongGenre STRING(25)
+) PRIMARY KEY(SingerId, AlbumId, TrackId),
+  INTERLEAVE IN PARENT Albums ON DELETE CASCADE
+--
+CreateTableStatement [0-262]
+  PathExpression [13-18]
+    Identifier(Songs) [13-18]
+  TableElementList [19-173]
+    ColumnDefinition [23-47]
+      Identifier(SingerId) [23-31]
+      SimpleColumnSchema [33-47]
+        PathExpression [33-38]
+          Identifier(INT64) [33-38]
+        ColumnAttributeList [39-47]
+          NotNullColumnAttribute [39-47]
+    ColumnDefinition [51-75]
+      Identifier(AlbumId) [51-58]
+      SimpleColumnSchema [61-75]
+        PathExpression [61-66]
+          Identifier(INT64) [61-66]
+        ColumnAttributeList [67-75]
+          NotNullColumnAttribute [67-75]
+    ColumnDefinition [79-103]
+      Identifier(TrackId) [79-86]
+      SimpleColumnSchema [89-103]
+        PathExpression [89-94]
+          Identifier(INT64) [89-94]
+        ColumnAttributeList [95-103]
+          NotNullColumnAttribute [95-103]
+    ColumnDefinition [107-128]
+      Identifier(SongName) [107-115]
+      SimpleColumnSchema [117-128]
+        PathExpression [117-123]
+          Identifier(STRING) [117-123]
+        TypeParameterList [123-127]
+          MaxLiteral() [124-127]
+    ColumnDefinition [132-147]
+      Identifier(Duration) [132-140]
+      SimpleColumnSchema [142-147]
+        PathExpression [142-147]
+          Identifier(INT64) [142-147]
+    ColumnDefinition [151-171]
+      Identifier(SongGenre) [151-160]
+      SimpleColumnSchema [161-171]
+        PathExpression [161-167]
+          Identifier(STRING) [161-167]
+        TypeParameterList [167-170]
+          IntLiteral(25) [168-170]
+  SpannerTableOptions [174-262]
+    PrimaryKey(ENFORCED) [174-213]
+      PrimaryKeyElementList [185-213]
+        PrimaryKeyElement(ASC) [186-194]
+          Identifier(SingerId) [186-194]
+        PrimaryKeyElement(ASC) [196-203]
+          Identifier(AlbumId) [196-203]
+        PrimaryKeyElement(ASC) [205-212]
+          Identifier(TrackId) [205-212]
+    SpannerInterleaveClause [213-262]
+      PathExpression [238-244]
+        Identifier(Albums) [238-244]
+==
+# https://docs.cloud.google.com/spanner/docs/query-execution-plans#overview
+CREATE INDEX SongsBySingerAlbumSongNameDesc ON Songs(SingerId, AlbumId, SongName DESC), INTERLEAVE IN Albums
+--
+CreateIndexStatement [0-108]
+  PathExpression [13-43]
+    Identifier(SongsBySingerAlbumSongNameDesc) [13-43]
+  PathExpression [47-52]
+    Identifier(Songs) [47-52]
+  IndexItemList [52-86]
+    OrderingExpression(ASC) [53-61]
+      PathExpression [53-61]
+        Identifier(SingerId) [53-61]
+    OrderingExpression(ASC) [63-70]
+      PathExpression [63-70]
+        Identifier(AlbumId) [63-70]
+    OrderingExpression(DESC) [72-85]
+      PathExpression [72-80]
+        Identifier(SongName) [72-80]
+  SpannerInterleaveClause [86-108]
+    PathExpression [102-108]
+      Identifier(Albums) [102-108]
+==
+# https://docs.cloud.google.com/spanner/docs/query-execution-plans#overview
+CREATE INDEX SongsBySongName ON Songs(SongName)
+--
+CreateIndexStatement [0-47]
+  PathExpression [13-28]
+    Identifier(SongsBySongName) [13-28]
+  PathExpression [32-37]
+    Identifier(Songs) [32-37]
+  IndexItemList [37-47]
+    OrderingExpression(ASC) [38-46]
+      PathExpression [38-46]
+        Identifier(SongName) [38-46]
+==
+# https://docs.cloud.google.com/spanner/docs/query-execution-plans#overview
+CREATE TABLE Concerts (
+  VenueId      INT64 NOT NULL,
+  SingerId     INT64 NOT NULL,
+  ConcertDate  DATE NOT NULL,
+  BeginTime    TIMESTAMP,
+  EndTime      TIMESTAMP,
+  TicketPrices ARRAY
+) PRIMARY KEY(VenueId, SingerId, ConcertDate)
+--
+CreateTableStatement [0-241]
+  PathExpression [13-21]
+    Identifier(Concerts) [13-21]
+  TableElementList [22-197]
+    ColumnDefinition [26-53]
+      Identifier(VenueId) [26-33]
+      SimpleColumnSchema [39-53]
+        PathExpression [39-44]
+          Identifier(INT64) [39-44]
+        ColumnAttributeList [45-53]
+          NotNullColumnAttribute [45-53]
+    ColumnDefinition [57-84]
+      Identifier(SingerId) [57-65]
+      SimpleColumnSchema [70-84]
+        PathExpression [70-75]
+          Identifier(INT64) [70-75]
+        ColumnAttributeList [76-84]
+          NotNullColumnAttribute [76-84]
+    ColumnDefinition [88-114]
+      Identifier(ConcertDate) [88-99]
+      SimpleColumnSchema [101-114]
+        PathExpression [101-105]
+          Identifier(DATE) [101-105]
+        ColumnAttributeList [106-114]
+          NotNullColumnAttribute [106-114]
+    ColumnDefinition [118-140]
+      Identifier(BeginTime) [118-127]
+      SimpleColumnSchema [131-140]
+        PathExpression [131-140]
+          Identifier(TIMESTAMP) [131-140]
+    ColumnDefinition [144-166]
+      Identifier(EndTime) [144-151]
+      SimpleColumnSchema [157-166]
+        PathExpression [157-166]
+          Identifier(TIMESTAMP) [157-166]
+    ColumnDefinition [170-195]
+      Identifier(TicketPrices) [170-182]
+      ArrayColumnSchema [183-195]
+        SimpleColumnSchema [189-194]
+          PathExpression [189-194]
+            Identifier(INT64) [189-194]
+  SpannerTableOptions [198-241]
+    PrimaryKey(ENFORCED) [198-241]
+      PrimaryKeyElementList [209-241]
+        PrimaryKeyElement(ASC) [210-217]
+          Identifier(VenueId) [210-217]
+        PrimaryKeyElement(ASC) [219-227]
+          Identifier(SingerId) [219-227]
+        PrimaryKeyElement(ASC) [229-240]
+          Identifier(ConcertDate) [229-240]
+==
+# https://docs.cloud.google.com/spanner/docs/query-execution-plans#overview
+INSERT INTO Singers (SingerId, FirstName, LastName, BirthDate)
+VALUES (1, "Marc", "Richards", "1970-09-03"),
+       (2, "Catalina", "Smith", "1990-08-17"),
+       (3, "Alice", "Trentor", "1991-10-02"),
+       (4, "Lea", "Martin", "1991-11-09"),
+       (5, "David", "Lomond", "1977-01-29")
+--
+InsertStatement [0-288]
+  PathExpression [12-19]
+    Identifier(Singers) [12-19]
+  ColumnList [20-62]
+    Identifier(SingerId) [21-29]
+    Identifier(FirstName) [31-40]
+    Identifier(LastName) [42-50]
+    Identifier(BirthDate) [52-61]
+  InsertValuesRowList [63-288]
+    InsertValuesRow [70-107]
+      IntLiteral(1) [71-72]
+      StringLiteral [74-80]
+        StringLiteralComponent("Marc") [74-80]
+      StringLiteral [82-92]
+        StringLiteralComponent("Richards") [82-92]
+      StringLiteral [94-106]
+        StringLiteralComponent("1970-09-03") [94-106]
+    InsertValuesRow [116-154]
+      IntLiteral(2) [117-118]
+      StringLiteral [120-130]
+        StringLiteralComponent("Catalina") [120-130]
+      StringLiteral [132-139]
+        StringLiteralComponent("Smith") [132-139]
+      StringLiteral [141-153]
+        StringLiteralComponent("1990-08-17") [141-153]
+    InsertValuesRow [163-200]
+      IntLiteral(3) [164-165]
+      StringLiteral [167-174]
+        StringLiteralComponent("Alice") [167-174]
+      StringLiteral [176-185]
+        StringLiteralComponent("Trentor") [176-185]
+      StringLiteral [187-199]
+        StringLiteralComponent("1991-10-02") [187-199]
+    InsertValuesRow [209-243]
+      IntLiteral(4) [210-211]
+      StringLiteral [213-218]
+        StringLiteralComponent("Lea") [213-218]
+      StringLiteral [220-228]
+        StringLiteralComponent("Martin") [220-228]
+      StringLiteral [230-242]
+        StringLiteralComponent("1991-11-09") [230-242]
+    InsertValuesRow [252-288]
+      IntLiteral(5) [253-254]
+      StringLiteral [256-263]
+        StringLiteralComponent("David") [256-263]
+      StringLiteral [265-273]
+        StringLiteralComponent("Lomond") [265-273]
+      StringLiteral [275-287]
+        StringLiteralComponent("1977-01-29") [275-287]
+==
+# https://docs.cloud.google.com/spanner/docs/query-execution-plans#overview
+INSERT INTO Albums (SingerId, AlbumId, AlbumTitle)
+VALUES (1, 1, "Total Junk"),
+       (1, 2, "Go, Go, Go"),
+       (2, 1, "Green"),
+       (2, 2, "Forever Hold Your Peace"),
+       (2, 3, "Terrified"),
+       (3, 1, "Nothing To Do With Me"),
+       (4, 1, "Play")
+--
+InsertStatement [0-264]
+  PathExpression [12-18]
+    Identifier(Albums) [12-18]
+  ColumnList [19-50]
+    Identifier(SingerId) [20-28]
+    Identifier(AlbumId) [30-37]
+    Identifier(AlbumTitle) [39-49]
+  InsertValuesRowList [51-264]
+    InsertValuesRow [58-78]
+      IntLiteral(1) [59-60]
+      IntLiteral(1) [62-63]
+      StringLiteral [65-77]
+        StringLiteralComponent("Total Junk") [65-77]
+    InsertValuesRow [87-107]
+      IntLiteral(1) [88-89]
+      IntLiteral(2) [91-92]
+      StringLiteral [94-106]
+        StringLiteralComponent("Go, Go, Go") [94-106]
+    InsertValuesRow [116-131]
+      IntLiteral(2) [117-118]
+      IntLiteral(1) [120-121]
+      StringLiteral [123-130]
+        StringLiteralComponent("Green") [123-130]
+    InsertValuesRow [140-173]
+      IntLiteral(2) [141-142]
+      IntLiteral(2) [144-145]
+      StringLiteral [147-172]
+        StringLiteralComponent("Forever Hold Your Peace") [147-172]
+    InsertValuesRow [182-201]
+      IntLiteral(2) [183-184]
+      IntLiteral(3) [186-187]
+      StringLiteral [189-200]
+        StringLiteralComponent("Terrified") [189-200]
+    InsertValuesRow [210-241]
+      IntLiteral(3) [211-212]
+      IntLiteral(1) [214-215]
+      StringLiteral [217-240]
+        StringLiteralComponent("Nothing To Do With Me") [217-240]
+    InsertValuesRow [250-264]
+      IntLiteral(4) [251-252]
+      IntLiteral(1) [254-255]
+      StringLiteral [257-263]
+        StringLiteralComponent("Play") [257-263]
+==
+# https://docs.cloud.google.com/spanner/docs/query-execution-plans#overview
+INSERT INTO Songs (SingerId, AlbumId, TrackId, SongName, Duration, SongGenre)
+VALUES (2, 1, 1, "Let's Get Back Together", 182, "COUNTRY"),
+       (2, 1, 2, "Starting Again", 156, "ROCK"),
+       (2, 1, 3, "I Knew You Were Magic", 294, "BLUES"),
+       (2, 1, 4, "42", 185, "CLASSICAL"),
+       (2, 1, 5, "Blue", 238, "BLUES"),
+       (2, 1, 6, "Nothing Is The Same", 303, "BLUES"),
+       (2, 1, 7, "The Second Time", 255, "ROCK"),
+       (2, 3, 1, "Fight Story", 194, "ROCK"),
+       (3, 1, 1, "Not About The Guitar", 278, "BLUES")
+--
+InsertStatement [0-532]
+  PathExpression [12-17]
+    Identifier(Songs) [12-17]
+  ColumnList [18-77]
+    Identifier(SingerId) [19-27]
+    Identifier(AlbumId) [29-36]
+    Identifier(TrackId) [38-45]
+    Identifier(SongName) [47-55]
+    Identifier(Duration) [57-65]
+    Identifier(SongGenre) [67-76]
+  InsertValuesRowList [78-532]
+    InsertValuesRow [85-137]
+      IntLiteral(2) [86-87]
+      IntLiteral(1) [89-90]
+      IntLiteral(1) [92-93]
+      StringLiteral [95-120]
+        StringLiteralComponent("Let's Get Back Together") [95-120]
+      IntLiteral(182) [122-125]
+      StringLiteral [127-136]
+        StringLiteralComponent("COUNTRY") [127-136]
+    InsertValuesRow [146-186]
+      IntLiteral(2) [147-148]
+      IntLiteral(1) [150-151]
+      IntLiteral(2) [153-154]
+      StringLiteral [156-172]
+        StringLiteralComponent("Starting Again") [156-172]
+      IntLiteral(156) [174-177]
+      StringLiteral [179-185]
+        StringLiteralComponent("ROCK") [179-185]
+    InsertValuesRow [195-243]
+      IntLiteral(2) [196-197]
+      IntLiteral(1) [199-200]
+      IntLiteral(3) [202-203]
+      StringLiteral [205-228]
+        StringLiteralComponent("I Knew You Were Magic") [205-228]
+      IntLiteral(294) [230-233]
+      StringLiteral [235-242]
+        StringLiteralComponent("BLUES") [235-242]
+    InsertValuesRow [252-285]
+      IntLiteral(2) [253-254]
+      IntLiteral(1) [256-257]
+      IntLiteral(4) [259-260]
+      StringLiteral [262-266]
+        StringLiteralComponent("42") [262-266]
+      IntLiteral(185) [268-271]
+      StringLiteral [273-284]
+        StringLiteralComponent("CLASSICAL") [273-284]
+    InsertValuesRow [294-325]
+      IntLiteral(2) [295-296]
+      IntLiteral(1) [298-299]
+      IntLiteral(5) [301-302]
+      StringLiteral [304-310]
+        StringLiteralComponent("Blue") [304-310]
+      IntLiteral(238) [312-315]
+      StringLiteral [317-324]
+        StringLiteralComponent("BLUES") [317-324]
+    InsertValuesRow [334-380]
+      IntLiteral(2) [335-336]
+      IntLiteral(1) [338-339]
+      IntLiteral(6) [341-342]
+      StringLiteral [344-365]
+        StringLiteralComponent("Nothing Is The Same") [344-365]
+      IntLiteral(303) [367-370]
+      StringLiteral [372-379]
+        StringLiteralComponent("BLUES") [372-379]
+    InsertValuesRow [389-430]
+      IntLiteral(2) [390-391]
+      IntLiteral(1) [393-394]
+      IntLiteral(7) [396-397]
+      StringLiteral [399-416]
+        StringLiteralComponent("The Second Time") [399-416]
+      IntLiteral(255) [418-421]
+      StringLiteral [423-429]
+        StringLiteralComponent("ROCK") [423-429]
+    InsertValuesRow [439-476]
+      IntLiteral(2) [440-441]
+      IntLiteral(3) [443-444]
+      IntLiteral(1) [446-447]
+      StringLiteral [449-462]
+        StringLiteralComponent("Fight Story") [449-462]
+      IntLiteral(194) [464-467]
+      StringLiteral [469-475]
+        StringLiteralComponent("ROCK") [469-475]
+    InsertValuesRow [485-532]
+      IntLiteral(3) [486-487]
+      IntLiteral(1) [489-490]
+      IntLiteral(1) [492-493]
+      StringLiteral [495-517]
+        StringLiteralComponent("Not About The Guitar") [495-517]
+      IntLiteral(278) [519-522]
+      StringLiteral [524-531]
+        StringLiteralComponent("BLUES") [524-531]
diff --git a/parser/testdata/spanner/guides/query-optimizer.manage-query-optimizer.test b/parser/testdata/spanner/guides/query-optimizer.manage-query-optimizer.test
new file mode 100644
index 0000000..5686eda
--- /dev/null
+++ b/parser/testdata/spanner/guides/query-optimizer.manage-query-optimizer.test
@@ -0,0 +1,234 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer#list-optimizer-versions
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT * FROM SPANNER_SYS.SUPPORTED_OPTIMIZER_VERSIONS
+--
+QueryStatement [0-54]
+  Query [0-54]
+    Select [0-54]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-54]
+        TablePathExpression [14-54]
+          PathExpression [14-54]
+            Identifier(SPANNER_SYS) [14-25]
+            Identifier(SUPPORTED_OPTIMIZER_VERSIONS) [26-54]
+==
+# https://docs.cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer#list-statistics-packages
+SELECT * FROM INFORMATION_SCHEMA.SPANNER_STATISTICS
+--
+QueryStatement [0-51]
+  Query [0-51]
+    Select [0-51]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-51]
+        TablePathExpression [14-51]
+          PathExpression [14-51]
+            Identifier(INFORMATION_SCHEMA) [14-32]
+            Identifier(SPANNER_STATISTICS) [33-51]
+==
+# https://docs.cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer#db-option
+ALTER DATABASE MyDatabase
+SET OPTIONS (optimizer_version =  9)
+--
+AlterDatabaseStatement [0-62]
+  PathExpression [15-25]
+    Identifier(MyDatabase) [15-25]
+  AlterActionList [26-62]
+    SetOptionsAction [26-62]
+      OptionsList [38-62]
+        OptionsEntry [39-61]
+          Identifier(optimizer_version) [39-56]
+          IntLiteral(9) [60-61]
+==
+# https://docs.cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer#db-option
+ALTER DATABASE MyDatabase
+SET OPTIONS (optimizer_statistics_package = "auto_20191128_14_47_22UTC")
+--
+AlterDatabaseStatement [0-98]
+  PathExpression [15-25]
+    Identifier(MyDatabase) [15-25]
+  AlterActionList [26-98]
+    SetOptionsAction [26-98]
+      OptionsList [38-98]
+        OptionsEntry [39-97]
+          Identifier(optimizer_statistics_package) [39-67]
+          StringLiteral [70-97]
+            StringLiteralComponent("auto_20191128_14_47_22UTC") [70-97]
+==
+# https://docs.cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer#db-option
+ALTER DATABASE MyDatabase
+SET OPTIONS (optimizer_version = 9,
+            optimizer_statistics_package = "auto_20191128_14_47_22UTC")
+--
+AlterDatabaseStatement [0-133]
+  PathExpression [15-25]
+    Identifier(MyDatabase) [15-25]
+  AlterActionList [26-133]
+    SetOptionsAction [26-133]
+      OptionsList [38-133]
+        OptionsEntry [39-60]
+          Identifier(optimizer_version) [39-56]
+          IntLiteral(9) [59-60]
+        OptionsEntry [74-132]
+          Identifier(optimizer_statistics_package) [74-102]
+          StringLiteral [105-132]
+            StringLiteralComponent("auto_20191128_14_47_22UTC") [105-132]
+==
+# https://docs.cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer#db-option
+SELECT
+  s.OPTION_NAME,
+  s.OPTION_VALUE
+FROM
+  INFORMATION_SCHEMA.DATABASE_OPTIONS s
+WHERE
+  s.SCHEMA_NAME=""
+  AND s.OPTION_NAME IN ('optimizer_version', 'optimizer_statistics_package')
+--
+QueryStatement [0-187]
+  Query [0-187]
+    Select [0-187]
+      SelectList [9-40]
+        SelectColumn [9-22]
+          PathExpression [9-22]
+            Identifier(s) [9-10]
+            Identifier(OPTION_NAME) [11-22]
+        SelectColumn [26-40]
+          PathExpression [26-40]
+            Identifier(s) [26-27]
+            Identifier(OPTION_VALUE) [28-40]
+      FromClause [41-85]
+        TablePathExpression [48-85]
+          PathExpression [48-83]
+            Identifier(INFORMATION_SCHEMA) [48-66]
+            Identifier(DATABASE_OPTIONS) [67-83]
+          Alias [84-85]
+            Identifier(s) [84-85]
+      WhereClause [86-187]
+        AndExpr [94-187]
+          BinaryExpression(=) [94-110]
+            PathExpression [94-107]
+              Identifier(s) [94-95]
+              Identifier(SCHEMA_NAME) [96-107]
+            StringLiteral [108-110]
+              StringLiteralComponent("") [108-110]
+          InExpression(IN) [117-187]
+            PathExpression [117-130]
+              Identifier(s) [117-118]
+              Identifier(OPTION_NAME) [119-130]
+            Location [131-133]
+            InList [135-186]
+              StringLiteral [135-154]
+                StringLiteralComponent('optimizer_version') [135-154]
+              StringLiteral [156-186]
+                StringLiteralComponent('optimizer_statistics_package') [156-186]
+==
+# https://docs.cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer#statement-hint
+@{OPTIMIZER_VERSION=9} SELECT * FROM MyTable
+--
+HintedStatement [0-44]
+  Hint [0-22]
+    HintEntry [2-21]
+      Identifier(OPTIMIZER_VERSION) [2-19]
+      IntLiteral(9) [20-21]
+  QueryStatement [23-44]
+    Query [23-44]
+      Select [23-44]
+        SelectList [30-31]
+          SelectColumn [30-31]
+            Star(*) [30-31]
+        FromClause [32-44]
+          TablePathExpression [37-44]
+            PathExpression [37-44]
+              Identifier(MyTable) [37-44]
+==
+# https://docs.cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer#statement-hint
+@{OPTIMIZER_VERSION=latest_version} SELECT * FROM MyTable
+--
+HintedStatement [0-57]
+  Hint [0-35]
+    HintEntry [2-34]
+      Identifier(OPTIMIZER_VERSION) [2-19]
+      PathExpression [20-34]
+        Identifier(latest_version) [20-34]
+  QueryStatement [36-57]
+    Query [36-57]
+      Select [36-57]
+        SelectList [43-44]
+          SelectColumn [43-44]
+            Star(*) [43-44]
+        FromClause [45-57]
+          TablePathExpression [50-57]
+            PathExpression [50-57]
+              Identifier(MyTable) [50-57]
+==
+# https://docs.cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer#statement-hint
+@{OPTIMIZER_STATISTICS_PACKAGE=auto_20191128_14_47_22UTC} SELECT * FROM MyTable
+--
+HintedStatement [0-79]
+  Hint [0-57]
+    HintEntry [2-56]
+      Identifier(OPTIMIZER_STATISTICS_PACKAGE) [2-30]
+      PathExpression [31-56]
+        Identifier(auto_20191128_14_47_22UTC) [31-56]
+  QueryStatement [58-79]
+    Query [58-79]
+      Select [58-79]
+        SelectList [65-66]
+          SelectColumn [65-66]
+            Star(*) [65-66]
+        FromClause [67-79]
+          TablePathExpression [72-79]
+            PathExpression [72-79]
+              Identifier(MyTable) [72-79]
+==
+# https://docs.cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer#statement-hint
+@{OPTIMIZER_STATISTICS_PACKAGE=latest} SELECT * FROM MyTable
+--
+HintedStatement [0-60]
+  Hint [0-38]
+    HintEntry [2-37]
+      Identifier(OPTIMIZER_STATISTICS_PACKAGE) [2-30]
+      PathExpression [31-37]
+        Identifier(latest) [31-37]
+  QueryStatement [39-60]
+    Query [39-60]
+      Select [39-60]
+        SelectList [46-47]
+          SelectColumn [46-47]
+            Star(*) [46-47]
+        FromClause [48-60]
+          TablePathExpression [53-60]
+            PathExpression [53-60]
+              Identifier(MyTable) [53-60]
+==
+# https://docs.cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer#statement-hint
+@{OPTIMIZER_VERSION=default_version, OPTIMIZER_STATISTICS_PACKAGE=auto_20191128_14_47_22UTC} SELECT * FROM MyTable
+--
+HintedStatement [0-114]
+  Hint [0-92]
+    HintEntry [2-35]
+      Identifier(OPTIMIZER_VERSION) [2-19]
+      PathExpression [20-35]
+        Identifier(default_version) [20-35]
+    HintEntry [37-91]
+      Identifier(OPTIMIZER_STATISTICS_PACKAGE) [37-65]
+      PathExpression [66-91]
+        Identifier(auto_20191128_14_47_22UTC) [66-91]
+  QueryStatement [93-114]
+    Query [93-114]
+      Select [93-114]
+        SelectList [100-101]
+          SelectColumn [100-101]
+            Star(*) [100-101]
+        FromClause [102-114]
+          TablePathExpression [107-114]
+            PathExpression [107-114]
+              Identifier(MyTable) [107-114]
diff --git a/parser/testdata/spanner/guides/query-optimizer.versions.test b/parser/testdata/spanner/guides/query-optimizer.versions.test
new file mode 100644
index 0000000..a5281ea
--- /dev/null
+++ b/parser/testdata/spanner/guides/query-optimizer.versions.test
@@ -0,0 +1,244 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/query-optimizer/versions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/query-optimizer/versions#version-9
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT s.SingerId, s.FirstName, s.LastName
+FROM Singers s
+WHERE LOWER(s.FirstName) > 'k'
+LIMIT 10
+--
+QueryStatement [0-97]
+  Query [0-97]
+    Select [0-88]
+      SelectList [7-42]
+        SelectColumn [7-17]
+          PathExpression [7-17]
+            Identifier(s) [7-8]
+            Identifier(SingerId) [9-17]
+        SelectColumn [19-30]
+          PathExpression [19-30]
+            Identifier(s) [19-20]
+            Identifier(FirstName) [21-30]
+        SelectColumn [32-42]
+          PathExpression [32-42]
+            Identifier(s) [32-33]
+            Identifier(LastName) [34-42]
+      FromClause [43-57]
+        TablePathExpression [48-57]
+          PathExpression [48-55]
+            Identifier(Singers) [48-55]
+          Alias [56-57]
+            Identifier(s) [56-57]
+      WhereClause [58-88]
+        BinaryExpression(>) [64-88]
+          FunctionCall [64-82]
+            PathExpression [64-69]
+              Identifier(LOWER) [64-69]
+            PathExpression [70-81]
+              Identifier(s) [70-71]
+              Identifier(FirstName) [72-81]
+          StringLiteral [85-88]
+            StringLiteralComponent('k') [85-88]
+    LimitOffset [89-97]
+      Limit [89-97]
+        IntLiteral(10) [95-97]
+==
+# https://docs.cloud.google.com/spanner/docs/query-optimizer/versions#version-3
+CREATE TABLE myTable(
+  a INT64,
+  b INT64,
+  c INT64,
+  d INT64)
+PRIMARY KEY (a, b, c)
+--
+CreateTableStatement [0-87]
+  PathExpression [13-20]
+    Identifier(myTable) [13-20]
+  TableElementList [20-65]
+    ColumnDefinition [24-31]
+      Identifier(a) [24-25]
+      SimpleColumnSchema [26-31]
+        PathExpression [26-31]
+          Identifier(INT64) [26-31]
+    ColumnDefinition [35-42]
+      Identifier(b) [35-36]
+      SimpleColumnSchema [37-42]
+        PathExpression [37-42]
+          Identifier(INT64) [37-42]
+    ColumnDefinition [46-53]
+      Identifier(c) [46-47]
+      SimpleColumnSchema [48-53]
+        PathExpression [48-53]
+          Identifier(INT64) [48-53]
+    ColumnDefinition [57-64]
+      Identifier(d) [57-58]
+      SimpleColumnSchema [59-64]
+        PathExpression [59-64]
+          Identifier(INT64) [59-64]
+  SpannerTableOptions [66-87]
+    PrimaryKey(ENFORCED) [66-87]
+      PrimaryKeyElementList [78-87]
+        PrimaryKeyElement(ASC) [79-80]
+          Identifier(a) [79-80]
+        PrimaryKeyElement(ASC) [82-83]
+          Identifier(b) [82-83]
+        PrimaryKeyElement(ASC) [85-86]
+          Identifier(c) [85-86]
+==
+# https://docs.cloud.google.com/spanner/docs/query-optimizer/versions#version-3
+SELECT a, b
+FROM myTable
+GROUP BY a, b
+--
+QueryStatement [0-38]
+  Query [0-38]
+    Select [0-38]
+      SelectList [7-11]
+        SelectColumn [7-8]
+          PathExpression [7-8]
+            Identifier(a) [7-8]
+        SelectColumn [10-11]
+          PathExpression [10-11]
+            Identifier(b) [10-11]
+      FromClause [12-24]
+        TablePathExpression [17-24]
+          PathExpression [17-24]
+            Identifier(myTable) [17-24]
+      GroupBy [25-38]
+        GroupingItem [34-35]
+          PathExpression [34-35]
+            Identifier(a) [34-35]
+        GroupingItem [37-38]
+          PathExpression [37-38]
+            Identifier(b) [37-38]
+==
+# https://docs.cloud.google.com/spanner/docs/query-optimizer/versions#version-3
+SELECT a2.*
+FROM Albums@{FORCE_INDEX=_BASE_TABLE} a1
+JOIN Albums@{FORCE_INDEX=_BASE_TABLE} a2 USING(SingerId)
+ORDER BY a1.AlbumId
+LIMIT 2
+--
+QueryStatement [0-137]
+  Query [0-137]
+    Select [0-109]
+      SelectList [7-11]
+        SelectColumn [7-11]
+          DotStar [7-11]
+            PathExpression [7-9]
+              Identifier(a2) [7-9]
+      FromClause [12-109]
+        Join [17-109]
+          TablePathExpression [17-52]
+            PathExpression [17-23]
+              Identifier(Albums) [17-23]
+            Hint [23-49]
+              HintEntry [25-48]
+                Identifier(FORCE_INDEX) [25-36]
+                PathExpression [37-48]
+                  Identifier(_BASE_TABLE) [37-48]
+            Alias [50-52]
+              Identifier(a1) [50-52]
+          Location [53-57]
+          TablePathExpression [58-93]
+            PathExpression [58-64]
+              Identifier(Albums) [58-64]
+            Hint [64-90]
+              HintEntry [66-89]
+                Identifier(FORCE_INDEX) [66-77]
+                PathExpression [78-89]
+                  Identifier(_BASE_TABLE) [78-89]
+            Alias [91-93]
+              Identifier(a2) [91-93]
+          UsingClause [94-109]
+            Identifier(SingerId) [100-108]
+    OrderBy [110-129]
+      OrderingExpression(ASC) [119-129]
+        PathExpression [119-129]
+          Identifier(a1) [119-121]
+          Identifier(AlbumId) [122-129]
+    LimitOffset [130-137]
+      Limit [130-137]
+        IntLiteral(2) [136-137]
+==
+# https://docs.cloud.google.com/spanner/docs/query-optimizer/versions#version-3
+SELECT
+  t.ConcertDate,
+  (
+    SELECT COUNT(*) FROM UNNEST(t.TicketPrices) p WHERE p > 10
+  ) AS expensive_tickets,
+  u.VenueName
+FROM Concerts t
+JOIN Venues u ON t.VenueId = u.VenueId
+ORDER BY expensive_tickets
+LIMIT 2
+--
+QueryStatement [0-220]
+  Query [0-220]
+    Select [0-185]
+      SelectList [9-130]
+        SelectColumn [9-22]
+          PathExpression [9-22]
+            Identifier(t) [9-10]
+            Identifier(ConcertDate) [11-22]
+        SelectColumn [26-115]
+          ExpressionSubquery [26-94]
+            Query [32-90]
+              Select [32-90]
+                SelectList [39-47]
+                  SelectColumn [39-47]
+                    FunctionCall [39-47]
+                      PathExpression [39-44]
+                        Identifier(COUNT) [39-44]
+                      Star(*) [45-46]
+                FromClause [48-77]
+                  TablePathExpression [53-77]
+                    UnnestExpression [53-75]
+                      ExpressionWithOptAlias [60-74]
+                        PathExpression [60-74]
+                          Identifier(t) [60-61]
+                          Identifier(TicketPrices) [62-74]
+                    Alias [76-77]
+                      Identifier(p) [76-77]
+                WhereClause [78-90]
+                  BinaryExpression(>) [84-90]
+                    PathExpression [84-85]
+                      Identifier(p) [84-85]
+                    IntLiteral(10) [88-90]
+          Alias [95-115]
+            Identifier(expensive_tickets) [98-115]
+        SelectColumn [119-130]
+          PathExpression [119-130]
+            Identifier(u) [119-120]
+            Identifier(VenueName) [121-130]
+      FromClause [131-185]
+        Join [136-185]
+          TablePathExpression [136-146]
+            PathExpression [136-144]
+              Identifier(Concerts) [136-144]
+            Alias [145-146]
+              Identifier(t) [145-146]
+          Location [147-151]
+          TablePathExpression [152-160]
+            PathExpression [152-158]
+              Identifier(Venues) [152-158]
+            Alias [159-160]
+              Identifier(u) [159-160]
+          OnClause [161-185]
+            BinaryExpression(=) [164-185]
+              PathExpression [164-173]
+                Identifier(t) [164-165]
+                Identifier(VenueId) [166-173]
+              PathExpression [176-185]
+                Identifier(u) [176-177]
+                Identifier(VenueId) [178-185]
+    OrderBy [186-212]
+      OrderingExpression(ASC) [195-212]
+        PathExpression [195-212]
+          Identifier(expensive_tickets) [195-212]
+    LimitOffset [213-220]
+      Limit [213-220]
+        IntLiteral(2) [219-220]
diff --git a/parser/testdata/spanner/guides/queues.queues-examples.spanner.sql b/parser/testdata/spanner/guides/queues.queues-examples.spanner.sql
new file mode 100644
index 0000000..ed77de2
--- /dev/null
+++ b/parser/testdata/spanner/guides/queues.queues-examples.spanner.sql
@@ -0,0 +1,27 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/queues/queues-examples
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#handle_large_message_payloads
+# id 8f0f50d8a999: queues (queues.queues-examples)
+# execute_query: INVALID_ARGUMENT: QUEUE is not a supported object type [at 1:8]
+CREATE QUEUE ImageProcessingQueue (
+  UserId    INT64 NOT NULL,
+  ImageId   STRING(36) NOT NULL,
+  Payload   BYTES(1) NOT NULL -- Payload can be minimal
+) PRIMARY KEY (UserId, ImageId),
+  INTERLEAVE IN PARENT ImageUploads ON DELETE CASCADE;
+
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#strategy_1_include_a_type_column_in_the_primary_key
+# id 23d917fa8b61: queues (queues.queues-examples)
+# execute_query: INVALID_ARGUMENT: QUEUE is not a supported object type [at 1:8]
+CREATE QUEUE ApplicationTasks (
+  TaskType   STRING(50) NOT NULL,
+  TaskId     STRING(36) NOT NULL,
+  Payload    BYTES(MAX) NOT NULL,
+) PRIMARY KEY (TaskType, TaskId);
diff --git a/parser/testdata/spanner/guides/queues.queues-examples.test b/parser/testdata/spanner/guides/queues.queues-examples.test
new file mode 100644
index 0000000..930c0f3
--- /dev/null
+++ b/parser/testdata/spanner/guides/queues.queues-examples.test
@@ -0,0 +1,569 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/queues/queues-examples
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#perform_work_after_a_transaction_commits
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+-- Inside your application transaction:
+-- 1. Insert into Users table
+INSERT INTO Users (UserId, UserName) VALUES (124, 'New User')
+--
+InsertStatement [70-131]
+  PathExpression [82-87]
+    Identifier(Users) [82-87]
+  ColumnList [88-106]
+    Identifier(UserId) [89-95]
+    Identifier(UserName) [97-105]
+  InsertValuesRowList [107-131]
+    InsertValuesRow [114-131]
+      IntLiteral(124) [115-118]
+      StringLiteral [120-130]
+        StringLiteralComponent('New User') [120-130]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#perform_work_after_a_transaction_commits
+-- 2. Send message to queue to trigger email
+INSERT INTO UserTasks (UserId, MessageId, Payload)
+VALUES (
+  124,
+  'welcome-email-id',
+  b'{"type": "welcome", "email": "user@example.com"}'
+)
+--
+InsertStatement [45-189]
+  PathExpression [57-66]
+    Identifier(UserTasks) [57-66]
+  ColumnList [67-95]
+    Identifier(UserId) [68-74]
+    Identifier(MessageId) [76-85]
+    Identifier(Payload) [87-94]
+  InsertValuesRowList [96-189]
+    InsertValuesRow [103-189]
+      IntLiteral(124) [107-110]
+      StringLiteral [114-132]
+        StringLiteralComponent('welcome-email-id') [114-132]
+      BytesLiteral [136-187]
+        BytesLiteralComponent(b'{"type": "welcome", "email": "user@example.com"}') [136-187]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#perform_work_after_a_transaction_commits
+-- 1. In the receiver process, stream messages from the queue
+SELECT
+  UserId,
+  MessageId,
+  Payload,
+  DeliverTime,
+  SpannerLeaseExpirationTimestamp,
+  SpannerLeaseToken
+FROM RECEIVE_UserTasks(max_duration=>'20m')
+--
+QueryStatement [62-216]
+  Query [62-216]
+    Select [62-216]
+      SelectList [71-172]
+        SelectColumn [71-77]
+          PathExpression [71-77]
+            Identifier(UserId) [71-77]
+        SelectColumn [81-90]
+          PathExpression [81-90]
+            Identifier(MessageId) [81-90]
+        SelectColumn [94-101]
+          PathExpression [94-101]
+            Identifier(Payload) [94-101]
+        SelectColumn [105-116]
+          PathExpression [105-116]
+            Identifier(DeliverTime) [105-116]
+        SelectColumn [120-151]
+          PathExpression [120-151]
+            Identifier(SpannerLeaseExpirationTimestamp) [120-151]
+        SelectColumn [155-172]
+          PathExpression [155-172]
+            Identifier(SpannerLeaseToken) [155-172]
+      FromClause [173-216]
+        TVF [178-216]
+          PathExpression [178-195]
+            Identifier(RECEIVE_UserTasks) [178-195]
+          TVFArgument [196-215]
+            NamedArgument [196-215]
+              Identifier(max_duration) [196-208]
+              StringLiteral [210-215]
+                StringLiteralComponent('20m') [210-215]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#perform_work_after_a_transaction_commits
+-- 2. After sending the welcome email, acknowledge the message
+DELETE FROM UserTasks
+WHERE UserId = 124 AND MessageId = 'welcome-email-id'
+--
+DeleteStatement [63-138]
+  PathExpression [75-84]
+    Identifier(UserTasks) [75-84]
+  AndExpr [91-138]
+    BinaryExpression(=) [91-103]
+      PathExpression [91-97]
+        Identifier(UserId) [91-97]
+      IntLiteral(124) [100-103]
+    BinaryExpression(=) [108-138]
+      PathExpression [108-117]
+        Identifier(MessageId) [108-117]
+      StringLiteral [120-138]
+        StringLiteralComponent('welcome-email-id') [120-138]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#schedule_work_for_a_specific_time_in_the_future
+-- 1. Insert into Users table
+INSERT INTO Users (UserId, UserName) VALUES (125, 'Trial User')
+--
+InsertStatement [30-93]
+  PathExpression [42-47]
+    Identifier(Users) [42-47]
+  ColumnList [48-66]
+    Identifier(UserId) [49-55]
+    Identifier(UserName) [57-65]
+  InsertValuesRowList [67-93]
+    InsertValuesRow [74-93]
+      IntLiteral(125) [75-78]
+      StringLiteral [80-92]
+        StringLiteralComponent('Trial User') [80-92]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#schedule_work_for_a_specific_time_in_the_future
+-- 2. Send message to queue with a future delivery time
+INSERT INTO UserTasks (UserId, MessageId, Payload, DeliverTime)
+VALUES (125, 'trial-expire-reminder', b'{"type": "reminder"}', TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 29 DAY))
+--
+InsertStatement [56-235]
+  PathExpression [68-77]
+    Identifier(UserTasks) [68-77]
+  ColumnList [78-119]
+    Identifier(UserId) [79-85]
+    Identifier(MessageId) [87-96]
+    Identifier(Payload) [98-105]
+    Identifier(DeliverTime) [107-118]
+  InsertValuesRowList [120-235]
+    InsertValuesRow [127-235]
+      IntLiteral(125) [128-131]
+      StringLiteral [133-156]
+        StringLiteralComponent('trial-expire-reminder') [133-156]
+      BytesLiteral [158-181]
+        BytesLiteralComponent(b'{"type": "reminder"}') [158-181]
+      FunctionCall [183-234]
+        PathExpression [183-196]
+          Identifier(TIMESTAMP_ADD) [183-196]
+        FunctionCall [197-216]
+          PathExpression [197-214]
+            Identifier(`CURRENT_TIMESTAMP`) [197-214]
+        IntervalExpr [218-233]
+          IntLiteral(29) [227-229]
+          Identifier(DAY) [230-233]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#handle_large_message_payloads
+-- Schema
+CREATE TABLE ImageUploads (
+  UserId    INT64 NOT NULL,
+  ImageId   STRING(36) NOT NULL,
+  ImageData BYTES(MAX),
+  Status    STRING(MAX) -- PENDING, PROCESSING, DONE
+) PRIMARY KEY (UserId, ImageId),
+  INTERLEAVE IN PARENT Users
+--
+CreateTableStatement [10-237]
+  PathExpression [23-35]
+    Identifier(ImageUploads) [23-35]
+  TableElementList [36-177]
+    ColumnDefinition [40-64]
+      Identifier(UserId) [40-46]
+      SimpleColumnSchema [50-64]
+        PathExpression [50-55]
+          Identifier(INT64) [50-55]
+        ColumnAttributeList [56-64]
+          NotNullColumnAttribute [56-64]
+    ColumnDefinition [68-97]
+      Identifier(ImageId) [68-75]
+      SimpleColumnSchema [78-97]
+        PathExpression [78-84]
+          Identifier(STRING) [78-84]
+        TypeParameterList [84-87]
+          IntLiteral(36) [85-87]
+        ColumnAttributeList [89-97]
+          NotNullColumnAttribute [89-97]
+    ColumnDefinition [101-121]
+      Identifier(ImageData) [101-110]
+      SimpleColumnSchema [111-121]
+        PathExpression [111-116]
+          Identifier(BYTES) [111-116]
+        TypeParameterList [116-120]
+          MaxLiteral() [117-120]
+    ColumnDefinition [125-146]
+      Identifier(Status) [125-131]
+      SimpleColumnSchema [135-146]
+        PathExpression [135-141]
+          Identifier(STRING) [135-141]
+        TypeParameterList [141-145]
+          MaxLiteral() [142-145]
+  SpannerTableOptions [178-237]
+    PrimaryKey(ENFORCED) [178-207]
+      PrimaryKeyElementList [190-207]
+        PrimaryKeyElement(ASC) [191-197]
+          Identifier(UserId) [191-197]
+        PrimaryKeyElement(ASC) [199-206]
+          Identifier(ImageId) [199-206]
+    SpannerInterleaveClause [207-237]
+      PathExpression [232-237]
+        Identifier(Users) [232-237]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#handle_large_message_payloads
+-- Application Logic
+-- 1. Upload image, insert into ImageUploads with Status 'PENDING'
+-- 2. Send message to ImageProcessingQueue
+INSERT INTO ImageProcessingQueue (UserId, ImageId, Payload) VALUES (123, 'image-uuid-1', b'')
+--
+InsertStatement [131-224]
+  PathExpression [143-163]
+    Identifier(ImageProcessingQueue) [143-163]
+  ColumnList [164-190]
+    Identifier(UserId) [165-171]
+    Identifier(ImageId) [173-180]
+    Identifier(Payload) [182-189]
+  InsertValuesRowList [191-224]
+    InsertValuesRow [198-224]
+      IntLiteral(123) [199-202]
+      StringLiteral [204-218]
+        StringLiteralComponent('image-uuid-1') [204-218]
+      BytesLiteral [220-223]
+        BytesLiteralComponent(b'') [220-223]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#perform_an_action_periodically
+-- Inside your application transaction:
+-- 1. Acknowledge current message
+DELETE FROM AggregationQueue
+WHERE TaskType = 'hourly-aggregator' AND MessageId = 'current-uuid'
+ASSERT_ROWS_MODIFIED 1
+--
+DeleteStatement [74-193]
+  PathExpression [86-102]
+    Identifier(AggregationQueue) [86-102]
+  AndExpr [109-170]
+    BinaryExpression(=) [109-139]
+      PathExpression [109-117]
+        Identifier(TaskType) [109-117]
+      StringLiteral [120-139]
+        StringLiteralComponent('hourly-aggregator') [120-139]
+    BinaryExpression(=) [144-170]
+      PathExpression [144-153]
+        Identifier(MessageId) [144-153]
+      StringLiteral [156-170]
+        StringLiteralComponent('current-uuid') [156-170]
+  AssertRowsModified [171-193]
+    IntLiteral(1) [192-193]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#perform_an_action_periodically
+-- 2. Schedule next run 1 hour in the future
+INSERT INTO AggregationQueue (TaskType, MessageId, Payload, DeliverTime)
+VALUES ('hourly-aggregator', 'next-uuid', b'', TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR))
+--
+InsertStatement [45-217]
+  PathExpression [57-73]
+    Identifier(AggregationQueue) [57-73]
+  ColumnList [74-117]
+    Identifier(TaskType) [75-83]
+    Identifier(MessageId) [85-94]
+    Identifier(Payload) [96-103]
+    Identifier(DeliverTime) [105-116]
+  InsertValuesRowList [118-217]
+    InsertValuesRow [125-217]
+      StringLiteral [126-145]
+        StringLiteralComponent('hourly-aggregator') [126-145]
+      StringLiteral [147-158]
+        StringLiteralComponent('next-uuid') [147-158]
+      BytesLiteral [160-163]
+        BytesLiteralComponent(b'') [160-163]
+      FunctionCall [165-216]
+        PathExpression [165-178]
+          Identifier(TIMESTAMP_ADD) [165-178]
+        FunctionCall [179-198]
+          PathExpression [179-196]
+            Identifier(`CURRENT_TIMESTAMP`) [179-196]
+        IntervalExpr [200-215]
+          IntLiteral(1) [209-210]
+          Identifier(HOUR) [211-215]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#batch_messages_using_temporal_batching
+-- Calculate delivery time rounded to the next 10-second interval.
+-- Use DIV(..., 10) * 10 to perform the RoundDown in SQL:
+INSERT INTO OrderProcessingQueue (OrderId, DeliverTime, Payload)
+VALUES (
+  'order-101',
+  TIMESTAMP_SECONDS(DIV(UNIX_SECONDS(CURRENT_TIMESTAMP()), 10) * 10 + 10),
+  b'{"item": "book", "qty": 1}'
+)
+--
+InsertStatement [125-322]
+  PathExpression [137-157]
+    Identifier(OrderProcessingQueue) [137-157]
+  ColumnList [158-189]
+    Identifier(OrderId) [159-166]
+    Identifier(DeliverTime) [168-179]
+    Identifier(Payload) [181-188]
+  InsertValuesRowList [190-322]
+    InsertValuesRow [197-322]
+      StringLiteral [201-212]
+        StringLiteralComponent('order-101') [201-212]
+      FunctionCall [216-287]
+        PathExpression [216-233]
+          Identifier(TIMESTAMP_SECONDS) [216-233]
+        BinaryExpression(+) [234-286]
+          BinaryExpression(*) [234-281]
+            FunctionCall [234-276]
+              PathExpression [234-237]
+                Identifier(DIV) [234-237]
+              FunctionCall [238-271]
+                PathExpression [238-250]
+                  Identifier(UNIX_SECONDS) [238-250]
+                FunctionCall [251-270]
+                  PathExpression [251-268]
+                    Identifier(`CURRENT_TIMESTAMP`) [251-268]
+              IntLiteral(10) [273-275]
+            IntLiteral(10) [279-281]
+          IntLiteral(10) [284-286]
+      BytesLiteral [291-320]
+        BytesLiteralComponent(b'{"item": "book", "qty": 1}') [291-320]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#batch_messages_using_temporal_batching
+SELECT OrderId, Payload, DeliverTime, SpannerLeaseToken
+FROM RECEIVE_OrderProcessingQueue(max_duration=>'20m', max_batch_size=>50)
+--
+QueryStatement [0-130]
+  Query [0-130]
+    Select [0-130]
+      SelectList [7-55]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(OrderId) [7-14]
+        SelectColumn [16-23]
+          PathExpression [16-23]
+            Identifier(Payload) [16-23]
+        SelectColumn [25-36]
+          PathExpression [25-36]
+            Identifier(DeliverTime) [25-36]
+        SelectColumn [38-55]
+          PathExpression [38-55]
+            Identifier(SpannerLeaseToken) [38-55]
+      FromClause [56-130]
+        TVF [61-130]
+          PathExpression [61-89]
+            Identifier(RECEIVE_OrderProcessingQueue) [61-89]
+          TVFArgument [90-109]
+            NamedArgument [90-109]
+              Identifier(max_duration) [90-102]
+              StringLiteral [104-109]
+                StringLiteralComponent('20m') [104-109]
+          TVFArgument [111-129]
+            NamedArgument [111-129]
+              Identifier(max_batch_size) [111-125]
+              IntLiteral(50) [127-129]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#decouple_data_modification_from_processing_dirty_flag_pattern
+-- Inside user profile update transaction:
+-- 1. Update the primary entity table
+UPDATE UserProfiles
+SET FullName = 'Jane Doe', UpdatedAt = CURRENT_TIMESTAMP()
+WHERE UserId = 456
+--
+UpdateStatement [81-178]
+  PathExpression [88-100]
+    Identifier(UserProfiles) [88-100]
+  UpdateItemList [105-159]
+    UpdateItem [105-126]
+      UpdateSetValue [105-126]
+        PathExpression [105-113]
+          Identifier(FullName) [105-113]
+        StringLiteral [116-126]
+          StringLiteralComponent('Jane Doe') [116-126]
+    UpdateItem [128-159]
+      UpdateSetValue [128-159]
+        PathExpression [128-137]
+          Identifier(UpdatedAt) [128-137]
+        FunctionCall [140-159]
+          PathExpression [140-157]
+            Identifier(`CURRENT_TIMESTAMP`) [140-157]
+  BinaryExpression(=) [166-178]
+    PathExpression [166-172]
+      Identifier(UserId) [166-172]
+    IntLiteral(456) [175-178]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#decouple_data_modification_from_processing_dirty_flag_pattern
+-- 2. Send lightweight dirty flag message to the queue.
+-- It is recommended to interleave the queue in the primary UserProfiles
+-- table for better transaction performance.
+INSERT INTO UserDirtyQueue (UserId, TaskType, CommitTimestamp, Payload)
+VALUES (456, 'reindex-user-profile', CURRENT_TIMESTAMP(), b'')
+--
+InsertStatement [174-308]
+  PathExpression [186-200]
+    Identifier(UserDirtyQueue) [186-200]
+  ColumnList [201-245]
+    Identifier(UserId) [202-208]
+    Identifier(TaskType) [210-218]
+    Identifier(CommitTimestamp) [220-235]
+    Identifier(Payload) [237-244]
+  InsertValuesRowList [246-308]
+    InsertValuesRow [253-308]
+      IntLiteral(456) [254-257]
+      StringLiteral [259-281]
+        StringLiteralComponent('reindex-user-profile') [259-281]
+      FunctionCall [283-302]
+        PathExpression [283-300]
+          Identifier(`CURRENT_TIMESTAMP`) [283-300]
+      BytesLiteral [304-307]
+        BytesLiteralComponent(b'') [304-307]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#monitor_worker_health_and_detect_timeouts
+-- Inside the worker heartbeat transaction (executed every 10 seconds):
+-- 1. Acknowledge the existing heartbeat message
+DELETE FROM WorkerHealthQueue
+WHERE WorkerId = 'worker-node-42' ASSERT_ROWS_MODIFIED 1
+--
+DeleteStatement [121-207]
+  PathExpression [133-150]
+    Identifier(WorkerHealthQueue) [133-150]
+  BinaryExpression(=) [157-184]
+    PathExpression [157-165]
+      Identifier(WorkerId) [157-165]
+    StringLiteral [168-184]
+      StringLiteralComponent('worker-node-42') [168-184]
+  AssertRowsModified [185-207]
+    IntLiteral(1) [206-207]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#monitor_worker_health_and_detect_timeouts
+-- 2. Send replacement heartbeat with refreshed 60-second deadline
+INSERT INTO WorkerHealthQueue (WorkerId, DeliverTime, Payload)
+VALUES (
+  'worker-node-42',
+  TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 60 SECOND),
+  b'{"status": "healthy", "active_jobs": 3}'
+)
+--
+InsertStatement [67-263]
+  PathExpression [79-96]
+    Identifier(WorkerHealthQueue) [79-96]
+  ColumnList [97-129]
+    Identifier(WorkerId) [98-106]
+    Identifier(DeliverTime) [108-119]
+    Identifier(Payload) [121-128]
+  InsertValuesRowList [130-263]
+    InsertValuesRow [137-263]
+      StringLiteral [141-157]
+        StringLiteralComponent('worker-node-42') [141-157]
+      FunctionCall [161-215]
+        PathExpression [161-174]
+          Identifier(TIMESTAMP_ADD) [161-174]
+        FunctionCall [175-194]
+          PathExpression [175-192]
+            Identifier(`CURRENT_TIMESTAMP`) [175-192]
+        IntervalExpr [196-214]
+          IntLiteral(60) [205-207]
+          Identifier(SECOND) [208-214]
+      BytesLiteral [219-261]
+        BytesLiteralComponent(b'{"status": "healthy", "active_jobs": 3}') [219-261]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#monitor_worker_health_and_detect_timeouts
+DELETE FROM WorkerHealthQueue WHERE WorkerId = 'worker-node-42' ASSERT_ROWS_MODIFIED 1
+--
+DeleteStatement [0-86]
+  PathExpression [12-29]
+    Identifier(WorkerHealthQueue) [12-29]
+  BinaryExpression(=) [36-63]
+    PathExpression [36-44]
+      Identifier(WorkerId) [36-44]
+    StringLiteral [47-63]
+      StringLiteralComponent('worker-node-42') [47-63]
+  AssertRowsModified [64-86]
+    IntLiteral(1) [85-86]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#strategy_1_include_a_type_column_in_the_primary_key
+-- Enqueue an email task
+INSERT INTO ApplicationTasks (TaskType, TaskId, Payload)
+VALUES ('SEND_EMAIL', 'task-uuid-1', b'{"to": "user@example.com", "template": "welcome"}')
+--
+InsertStatement [25-172]
+  PathExpression [37-53]
+    Identifier(ApplicationTasks) [37-53]
+  ColumnList [54-81]
+    Identifier(TaskType) [55-63]
+    Identifier(TaskId) [65-71]
+    Identifier(Payload) [73-80]
+  InsertValuesRowList [82-172]
+    InsertValuesRow [89-172]
+      StringLiteral [90-102]
+        StringLiteralComponent('SEND_EMAIL') [90-102]
+      StringLiteral [104-117]
+        StringLiteralComponent('task-uuid-1') [104-117]
+      BytesLiteral [119-171]
+        BytesLiteralComponent(b'{"to": "user@example.com", "template": "welcome"}') [119-171]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#strategy_1_include_a_type_column_in_the_primary_key
+-- Enqueue an image thumbnail task
+INSERT INTO ApplicationTasks (TaskType, TaskId, Payload)
+VALUES ('GENERATE_THUMBNAIL', 'task-uuid-2', b'{"image_id": "img-789", "size": "small"}')
+--
+InsertStatement [35-181]
+  PathExpression [47-63]
+    Identifier(ApplicationTasks) [47-63]
+  ColumnList [64-91]
+    Identifier(TaskType) [65-73]
+    Identifier(TaskId) [75-81]
+    Identifier(Payload) [83-90]
+  InsertValuesRowList [92-181]
+    InsertValuesRow [99-181]
+      StringLiteral [100-120]
+        StringLiteralComponent('GENERATE_THUMBNAIL') [100-120]
+      StringLiteral [122-135]
+        StringLiteralComponent('task-uuid-2') [122-135]
+      BytesLiteral [137-180]
+        BytesLiteralComponent(b'{"image_id": "img-789", "size": "small"}') [137-180]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#implement_custom_retry_delays
+-- Inside failure-handling transaction:
+-- 1. Acknowledge the failed message
+DELETE FROM OutboundNotificationQueue
+WHERE NotificationId = 'notif-555' ASSERT_ROWS_MODIFIED 1
+--
+DeleteStatement [77-172]
+  PathExpression [89-114]
+    Identifier(OutboundNotificationQueue) [89-114]
+  BinaryExpression(=) [121-149]
+    PathExpression [121-135]
+      Identifier(NotificationId) [121-135]
+    StringLiteral [138-149]
+      StringLiteralComponent('notif-555') [138-149]
+  AssertRowsModified [150-172]
+    IntLiteral(1) [171-172]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-examples#implement_custom_retry_delays
+-- 2. Reschedule delivery 5 minutes in the future
+INSERT INTO OutboundNotificationQueue (NotificationId, DeliverTime, Payload)
+VALUES (
+  'notif-555',
+  TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 5 MINUTE),
+  b'{"recipient": "user@example.com", "retry_count": 2}'
+)
+--
+InsertStatement [50-266]
+  PathExpression [62-87]
+    Identifier(OutboundNotificationQueue) [62-87]
+  ColumnList [88-126]
+    Identifier(NotificationId) [89-103]
+    Identifier(DeliverTime) [105-116]
+    Identifier(Payload) [118-125]
+  InsertValuesRowList [127-266]
+    InsertValuesRow [134-266]
+      StringLiteral [138-149]
+        StringLiteralComponent('notif-555') [138-149]
+      FunctionCall [153-206]
+        PathExpression [153-166]
+          Identifier(TIMESTAMP_ADD) [153-166]
+        FunctionCall [167-186]
+          PathExpression [167-184]
+            Identifier(`CURRENT_TIMESTAMP`) [167-184]
+        IntervalExpr [188-205]
+          IntLiteral(5) [197-198]
+          Identifier(MINUTE) [199-205]
+      BytesLiteral [210-264]
+        BytesLiteralComponent(b'{"recipient": "user@example.com", "retry_count": 2}') [210-264]
diff --git a/parser/testdata/spanner/guides/queues.queues-using.spanner.sql b/parser/testdata/spanner/guides/queues.queues-using.spanner.sql
new file mode 100644
index 0000000..6992199
--- /dev/null
+++ b/parser/testdata/spanner/guides/queues.queues-using.spanner.sql
@@ -0,0 +1,22 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/queues/queues-using
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/queues/queues-using#create_a_queue
+# id aef3cbd9ca49: queues (queues.queues-using)
+# execute_query: INVALID_ARGUMENT: QUEUE is not a supported object type [at 5:8]
+-- Queue for processing user-related tasks
+-- NOTE: Queues do not have to be interleaved, but for locality and
+-- pre-warming purposes, it's recommended if you are inserting into a table
+-- and a queue simultaneously.
+CREATE QUEUE UserTasks (
+  UserId     INT64 NOT NULL,
+  MessageId  STRING(36) NOT NULL, -- UUID recommended
+  Payload    BYTES(MAX) NOT NULL  -- Also: Proto, JSON, String are possible.
+) PRIMARY KEY (UserId, MessageId),
+INTERLEAVE IN PARENT Users ON DELETE CASCADE;
diff --git a/parser/testdata/spanner/guides/queues.queues-using.test b/parser/testdata/spanner/guides/queues.queues-using.test
new file mode 100644
index 0000000..e5c1809
--- /dev/null
+++ b/parser/testdata/spanner/guides/queues.queues-using.test
@@ -0,0 +1,242 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/queues/queues-using
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/queues/queues-using#create_a_queue
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+-- Example table for interleaving
+CREATE TABLE Users (
+  UserId   INT64 NOT NULL,
+  UserName STRING(MAX)
+) PRIMARY KEY (UserId)
+--
+CreateTableStatement [34-127]
+  PathExpression [47-52]
+    Identifier(Users) [47-52]
+  TableElementList [53-106]
+    ColumnDefinition [57-80]
+      Identifier(UserId) [57-63]
+      SimpleColumnSchema [66-80]
+        PathExpression [66-71]
+          Identifier(INT64) [66-71]
+        ColumnAttributeList [72-80]
+          NotNullColumnAttribute [72-80]
+    ColumnDefinition [84-104]
+      Identifier(UserName) [84-92]
+      SimpleColumnSchema [93-104]
+        PathExpression [93-99]
+          Identifier(STRING) [93-99]
+        TypeParameterList [99-103]
+          MaxLiteral() [100-103]
+  SpannerTableOptions [107-127]
+    PrimaryKey(ENFORCED) [107-127]
+      PrimaryKeyElementList [119-127]
+        PrimaryKeyElement(ASC) [120-126]
+          Identifier(UserId) [120-126]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-using#send_a_message
+-- Send message immediately
+INSERT INTO Users (UserId) VALUES (123)
+--
+InsertStatement [28-67]
+  PathExpression [40-45]
+    Identifier(Users) [40-45]
+  ColumnList [46-54]
+    Identifier(UserId) [47-53]
+  InsertValuesRowList [55-67]
+    InsertValuesRow [62-67]
+      IntLiteral(123) [63-66]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-using#send_a_message
+INSERT INTO UserTasks (UserId, MessageId, Payload, DeliverTime)
+VALUES (123, 'some-unique-id-1', b'Your task payload here', CURRENT_TIMESTAMP())
+--
+InsertStatement [0-144]
+  PathExpression [12-21]
+    Identifier(UserTasks) [12-21]
+  ColumnList [22-63]
+    Identifier(UserId) [23-29]
+    Identifier(MessageId) [31-40]
+    Identifier(Payload) [42-49]
+    Identifier(DeliverTime) [51-62]
+  InsertValuesRowList [64-144]
+    InsertValuesRow [71-144]
+      IntLiteral(123) [72-75]
+      StringLiteral [77-95]
+        StringLiteralComponent('some-unique-id-1') [77-95]
+      BytesLiteral [97-122]
+        BytesLiteralComponent(b'Your task payload here') [97-122]
+      FunctionCall [124-143]
+        PathExpression [124-141]
+          Identifier(`CURRENT_TIMESTAMP`) [124-141]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-using#send_a_message
+-- Schedule a message delivery
+INSERT INTO UserTasks (UserId, MessageId, Payload, DeliverTime)
+VALUES (123, 'some-unique-id-2', b'Scheduled task', TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR))
+--
+InsertStatement [31-199]
+  PathExpression [43-52]
+    Identifier(UserTasks) [43-52]
+  ColumnList [53-94]
+    Identifier(UserId) [54-60]
+    Identifier(MessageId) [62-71]
+    Identifier(Payload) [73-80]
+    Identifier(DeliverTime) [82-93]
+  InsertValuesRowList [95-199]
+    InsertValuesRow [102-199]
+      IntLiteral(123) [103-106]
+      StringLiteral [108-126]
+        StringLiteralComponent('some-unique-id-2') [108-126]
+      BytesLiteral [128-145]
+        BytesLiteralComponent(b'Scheduled task') [128-145]
+      FunctionCall [147-198]
+        PathExpression [147-160]
+          Identifier(TIMESTAMP_ADD) [147-160]
+        FunctionCall [161-180]
+          PathExpression [161-178]
+            Identifier(`CURRENT_TIMESTAMP`) [161-178]
+        IntervalExpr [182-197]
+          IntLiteral(1) [191-192]
+          Identifier(HOUR) [193-197]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-using#receive_messages
+-- SQL query to stream messages
+SELECT
+  UserId,
+  MessageId,
+  Payload,
+  DeliverTime,
+  SpannerLeaseExpirationTimestamp,
+  SpannerLeaseToken
+FROM RECEIVE_UserTasks(max_duration=>'20m')
+--
+QueryStatement [32-186]
+  Query [32-186]
+    Select [32-186]
+      SelectList [41-142]
+        SelectColumn [41-47]
+          PathExpression [41-47]
+            Identifier(UserId) [41-47]
+        SelectColumn [51-60]
+          PathExpression [51-60]
+            Identifier(MessageId) [51-60]
+        SelectColumn [64-71]
+          PathExpression [64-71]
+            Identifier(Payload) [64-71]
+        SelectColumn [75-86]
+          PathExpression [75-86]
+            Identifier(DeliverTime) [75-86]
+        SelectColumn [90-121]
+          PathExpression [90-121]
+            Identifier(SpannerLeaseExpirationTimestamp) [90-121]
+        SelectColumn [125-142]
+          PathExpression [125-142]
+            Identifier(SpannerLeaseToken) [125-142]
+      FromClause [143-186]
+        TVF [148-186]
+          PathExpression [148-165]
+            Identifier(RECEIVE_UserTasks) [148-165]
+          TVFArgument [166-185]
+            NamedArgument [166-185]
+              Identifier(max_duration) [166-178]
+              StringLiteral [180-185]
+                StringLiteralComponent('20m') [180-185]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-using#receive_messages_in_a_batch
+-- SQL query to stream messages
+SELECT
+  UserId,
+  MessageId,
+  Payload,
+  DeliverTime,
+  SpannerLeaseExpirationTimestamp,
+  SpannerLeaseToken,
+  SpannerLastBatchMessage -- Special boolean column returns TRUE if the
+                          -- last message is in a batch.
+FROM RECEIVE_UserTasks(max_duration=>'20m', max_batch_size=>20)
+--
+QueryStatement [32-336]
+  Query [32-336]
+    Select [32-336]
+      SelectList [41-169]
+        SelectColumn [41-47]
+          PathExpression [41-47]
+            Identifier(UserId) [41-47]
+        SelectColumn [51-60]
+          PathExpression [51-60]
+            Identifier(MessageId) [51-60]
+        SelectColumn [64-71]
+          PathExpression [64-71]
+            Identifier(Payload) [64-71]
+        SelectColumn [75-86]
+          PathExpression [75-86]
+            Identifier(DeliverTime) [75-86]
+        SelectColumn [90-121]
+          PathExpression [90-121]
+            Identifier(SpannerLeaseExpirationTimestamp) [90-121]
+        SelectColumn [125-142]
+          PathExpression [125-142]
+            Identifier(SpannerLeaseToken) [125-142]
+        SelectColumn [146-169]
+          PathExpression [146-169]
+            Identifier(SpannerLastBatchMessage) [146-169]
+      FromClause [273-336]
+        TVF [278-336]
+          PathExpression [278-295]
+            Identifier(RECEIVE_UserTasks) [278-295]
+          TVFArgument [296-315]
+            NamedArgument [296-315]
+              Identifier(max_duration) [296-308]
+              StringLiteral [310-315]
+                StringLiteralComponent('20m') [310-315]
+          TVFArgument [317-335]
+            NamedArgument [317-335]
+              Identifier(max_batch_size) [317-331]
+              IntLiteral(20) [333-335]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-using#acknowledge_a_message
+-- DML for acknowledging
+DELETE FROM UserTasks WHERE UserId = @userId AND MessageId = @messageId ASSERT_ROWS_MODIFIED 1
+--
+DeleteStatement [25-119]
+  PathExpression [37-46]
+    Identifier(UserTasks) [37-46]
+  AndExpr [53-96]
+    BinaryExpression(=) [53-69]
+      PathExpression [53-59]
+        Identifier(UserId) [53-59]
+      ParameterExpr [62-69]
+        Identifier(userId) [63-69]
+    BinaryExpression(=) [74-96]
+      PathExpression [74-83]
+        Identifier(MessageId) [74-83]
+      ParameterExpr [86-96]
+        Identifier(messageId) [87-96]
+  AssertRowsModified [97-119]
+    IntLiteral(1) [118-119]
+==
+# https://docs.cloud.google.com/spanner/docs/queues/queues-using#resolution_2
+SELECT *
+FROM UserTasks
+ORDER BY DeliverTime ASC
+LIMIT 10
+--
+QueryStatement [0-57]
+  Query [0-57]
+    Select [0-23]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-23]
+        TablePathExpression [14-23]
+          PathExpression [14-23]
+            Identifier(UserTasks) [14-23]
+    OrderBy [24-48]
+      OrderingExpression(ASC EXPLICITLY) [33-48]
+        PathExpression [33-44]
+          Identifier(DeliverTime) [33-44]
+    LimitOffset [49-57]
+      Limit [49-57]
+        IntLiteral(10) [55-57]
diff --git a/parser/testdata/spanner/guides/schema-and-data-model.spanner.sql b/parser/testdata/spanner/guides/schema-and-data-model.spanner.sql
new file mode 100644
index 0000000..1334a73
--- /dev/null
+++ b/parser/testdata/spanner/guides/schema-and-data-model.spanner.sql
@@ -0,0 +1,18 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/schema-and-data-model
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/schema-and-data-model#create-interleaved-tables
+# id bda3df9837d9: INTERLEAVE IN without PARENT (schema-and-data-model)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected keyword PARENT but got identifier "Projects" [at 6:17]
+CREATE TABLE Resources (
+  ProjectId    INT64 NOT NULL,
+  ResourceId   INT64 NOT NULL,
+  ResourceName STRING(1024),
+) PRIMARY KEY (ProjectId, ResourceId),
+  INTERLEAVE IN Projects;
diff --git a/parser/testdata/spanner/guides/schema-and-data-model.test b/parser/testdata/spanner/guides/schema-and-data-model.test
new file mode 100644
index 0000000..a7f7e7a
--- /dev/null
+++ b/parser/testdata/spanner/guides/schema-and-data-model.test
@@ -0,0 +1,414 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/schema-and-data-model
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/schema-and-data-model#create_a_parent_table
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Singers (
+SingerId   INT64 NOT NULL PRIMARY KEY,
+FirstName  STRING(1024),
+LastName   STRING(1024),
+SingerInfo BYTES(MAX),
+)
+--
+CreateTableStatement [0-136]
+  PathExpression [13-20]
+    Identifier(Singers) [13-20]
+  TableElementList [21-136]
+    ColumnDefinition [23-60]
+      Identifier(SingerId) [23-31]
+      SimpleColumnSchema [34-60]
+        PathExpression [34-39]
+          Identifier(INT64) [34-39]
+        ColumnAttributeList [40-60]
+          NotNullColumnAttribute [40-48]
+          PrimaryKeyColumnAttribute(ENFORCED) [49-60]
+    ColumnDefinition [62-85]
+      Identifier(FirstName) [62-71]
+      SimpleColumnSchema [73-85]
+        PathExpression [73-79]
+          Identifier(STRING) [73-79]
+        TypeParameterList [79-84]
+          IntLiteral(1024) [80-84]
+    ColumnDefinition [87-110]
+      Identifier(LastName) [87-95]
+      SimpleColumnSchema [98-110]
+        PathExpression [98-104]
+          Identifier(STRING) [98-104]
+        TypeParameterList [104-109]
+          IntLiteral(1024) [105-109]
+    ColumnDefinition [112-133]
+      Identifier(SingerInfo) [112-122]
+      SimpleColumnSchema [123-133]
+        PathExpression [123-128]
+          Identifier(BYTES) [123-128]
+        TypeParameterList [128-132]
+          MaxLiteral() [129-132]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-and-data-model#create_parent_and_child_tables
+CREATE TABLE Singers (
+ SingerId   INT64 NOT NULL PRIMARY KEY,
+ FirstName  STRING(1024),
+ LastName   STRING(1024),
+ SingerInfo BYTES(MAX),
+)
+--
+CreateTableStatement [0-140]
+  PathExpression [13-20]
+    Identifier(Singers) [13-20]
+  TableElementList [21-140]
+    ColumnDefinition [24-61]
+      Identifier(SingerId) [24-32]
+      SimpleColumnSchema [35-61]
+        PathExpression [35-40]
+          Identifier(INT64) [35-40]
+        ColumnAttributeList [41-61]
+          NotNullColumnAttribute [41-49]
+          PrimaryKeyColumnAttribute(ENFORCED) [50-61]
+    ColumnDefinition [64-87]
+      Identifier(FirstName) [64-73]
+      SimpleColumnSchema [75-87]
+        PathExpression [75-81]
+          Identifier(STRING) [75-81]
+        TypeParameterList [81-86]
+          IntLiteral(1024) [82-86]
+    ColumnDefinition [90-113]
+      Identifier(LastName) [90-98]
+      SimpleColumnSchema [101-113]
+        PathExpression [101-107]
+          Identifier(STRING) [101-107]
+        TypeParameterList [107-112]
+          IntLiteral(1024) [108-112]
+    ColumnDefinition [116-137]
+      Identifier(SingerInfo) [116-126]
+      SimpleColumnSchema [127-137]
+        PathExpression [127-132]
+          Identifier(BYTES) [127-132]
+        TypeParameterList [132-136]
+          MaxLiteral() [133-136]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-and-data-model#create_parent_and_child_tables
+CREATE TABLE Albums (
+SingerId     INT64 NOT NULL,
+AlbumId      INT64 NOT NULL,
+AlbumTitle   STRING(MAX),
+) PRIMARY KEY (SingerId, AlbumId)
+--
+CreateTableStatement [0-139]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-107]
+    ColumnDefinition [22-49]
+      Identifier(SingerId) [22-30]
+      SimpleColumnSchema [35-49]
+        PathExpression [35-40]
+          Identifier(INT64) [35-40]
+        ColumnAttributeList [41-49]
+          NotNullColumnAttribute [41-49]
+    ColumnDefinition [51-78]
+      Identifier(AlbumId) [51-58]
+      SimpleColumnSchema [64-78]
+        PathExpression [64-69]
+          Identifier(INT64) [64-69]
+        ColumnAttributeList [70-78]
+          NotNullColumnAttribute [70-78]
+    ColumnDefinition [80-104]
+      Identifier(AlbumTitle) [80-90]
+      SimpleColumnSchema [93-104]
+        PathExpression [93-99]
+          Identifier(STRING) [93-99]
+        TypeParameterList [99-103]
+          MaxLiteral() [100-103]
+  SpannerTableOptions [108-139]
+    PrimaryKey(ENFORCED) [108-139]
+      PrimaryKeyElementList [120-139]
+        PrimaryKeyElement(ASC) [121-129]
+          Identifier(SingerId) [121-129]
+        PrimaryKeyElement(ASC) [131-138]
+          Identifier(AlbumId) [131-138]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-and-data-model#create-interleaved-tables
+CREATE TABLE Singers (
+ SingerId   INT64 NOT NULL PRIMARY KEY,
+ FirstName  STRING(1024),
+ LastName   STRING(1024),
+ SingerInfo BYTES(MAX),
+ )
+--
+CreateTableStatement [0-141]
+  PathExpression [13-20]
+    Identifier(Singers) [13-20]
+  TableElementList [21-141]
+    ColumnDefinition [24-61]
+      Identifier(SingerId) [24-32]
+      SimpleColumnSchema [35-61]
+        PathExpression [35-40]
+          Identifier(INT64) [35-40]
+        ColumnAttributeList [41-61]
+          NotNullColumnAttribute [41-49]
+          PrimaryKeyColumnAttribute(ENFORCED) [50-61]
+    ColumnDefinition [64-87]
+      Identifier(FirstName) [64-73]
+      SimpleColumnSchema [75-87]
+        PathExpression [75-81]
+          Identifier(STRING) [75-81]
+        TypeParameterList [81-86]
+          IntLiteral(1024) [82-86]
+    ColumnDefinition [90-113]
+      Identifier(LastName) [90-98]
+      SimpleColumnSchema [101-113]
+        PathExpression [101-107]
+          Identifier(STRING) [101-107]
+        TypeParameterList [107-112]
+          IntLiteral(1024) [108-112]
+    ColumnDefinition [116-137]
+      Identifier(SingerInfo) [116-126]
+      SimpleColumnSchema [127-137]
+        PathExpression [127-132]
+          Identifier(BYTES) [127-132]
+        TypeParameterList [132-136]
+          MaxLiteral() [133-136]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-and-data-model#create-interleaved-tables
+CREATE TABLE Albums (
+ SingerId     INT64 NOT NULL,
+ AlbumId      INT64 NOT NULL,
+ AlbumTitle   STRING(MAX),
+ ) PRIMARY KEY (SingerId, AlbumId),
+INTERLEAVE IN PARENT Singers ON DELETE CASCADE
+--
+CreateTableStatement [0-191]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-111]
+    ColumnDefinition [23-50]
+      Identifier(SingerId) [23-31]
+      SimpleColumnSchema [36-50]
+        PathExpression [36-41]
+          Identifier(INT64) [36-41]
+        ColumnAttributeList [42-50]
+          NotNullColumnAttribute [42-50]
+    ColumnDefinition [53-80]
+      Identifier(AlbumId) [53-60]
+      SimpleColumnSchema [66-80]
+        PathExpression [66-71]
+          Identifier(INT64) [66-71]
+        ColumnAttributeList [72-80]
+          NotNullColumnAttribute [72-80]
+    ColumnDefinition [83-107]
+      Identifier(AlbumTitle) [83-93]
+      SimpleColumnSchema [96-107]
+        PathExpression [96-102]
+          Identifier(STRING) [96-102]
+        TypeParameterList [102-106]
+          MaxLiteral() [103-106]
+  SpannerTableOptions [112-191]
+    PrimaryKey(ENFORCED) [112-143]
+      PrimaryKeyElementList [124-143]
+        PrimaryKeyElement(ASC) [125-133]
+          Identifier(SingerId) [125-133]
+        PrimaryKeyElement(ASC) [135-142]
+          Identifier(AlbumId) [135-142]
+    SpannerInterleaveClause [143-191]
+      PathExpression [166-173]
+        Identifier(Singers) [166-173]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-and-data-model#create-interleaved-tables
+CREATE TABLE Projects (
+  ProjectId   INT64 NOT NULL,
+  ProjectName STRING(1024),
+) PRIMARY KEY (ProjectId)
+--
+CreateTableStatement [0-107]
+  PathExpression [13-21]
+    Identifier(Projects) [13-21]
+  TableElementList [22-83]
+    ColumnDefinition [26-52]
+      Identifier(ProjectId) [26-35]
+      SimpleColumnSchema [38-52]
+        PathExpression [38-43]
+          Identifier(INT64) [38-43]
+        ColumnAttributeList [44-52]
+          NotNullColumnAttribute [44-52]
+    ColumnDefinition [56-80]
+      Identifier(ProjectName) [56-67]
+      SimpleColumnSchema [68-80]
+        PathExpression [68-74]
+          Identifier(STRING) [68-74]
+        TypeParameterList [74-79]
+          IntLiteral(1024) [75-79]
+  SpannerTableOptions [84-107]
+    PrimaryKey(ENFORCED) [84-107]
+      PrimaryKeyElementList [96-107]
+        PrimaryKeyElement(ASC) [97-106]
+          Identifier(ProjectId) [97-106]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-and-data-model#create_a_hierarchy_of_interleaved_tables
+CREATE TABLE Albums (
+ SingerId     INT64 NOT NULL,
+ AlbumId      INT64 NOT NULL,
+ AlbumTitle   STRING(MAX),
+) PRIMARY KEY (SingerId, AlbumId),
+ INTERLEAVE IN PARENT Singers ON DELETE CASCADE
+--
+CreateTableStatement [0-191]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-110]
+    ColumnDefinition [23-50]
+      Identifier(SingerId) [23-31]
+      SimpleColumnSchema [36-50]
+        PathExpression [36-41]
+          Identifier(INT64) [36-41]
+        ColumnAttributeList [42-50]
+          NotNullColumnAttribute [42-50]
+    ColumnDefinition [53-80]
+      Identifier(AlbumId) [53-60]
+      SimpleColumnSchema [66-80]
+        PathExpression [66-71]
+          Identifier(INT64) [66-71]
+        ColumnAttributeList [72-80]
+          NotNullColumnAttribute [72-80]
+    ColumnDefinition [83-107]
+      Identifier(AlbumTitle) [83-93]
+      SimpleColumnSchema [96-107]
+        PathExpression [96-102]
+          Identifier(STRING) [96-102]
+        TypeParameterList [102-106]
+          MaxLiteral() [103-106]
+  SpannerTableOptions [111-191]
+    PrimaryKey(ENFORCED) [111-142]
+      PrimaryKeyElementList [123-142]
+        PrimaryKeyElement(ASC) [124-132]
+          Identifier(SingerId) [124-132]
+        PrimaryKeyElement(ASC) [134-141]
+          Identifier(AlbumId) [134-141]
+    SpannerInterleaveClause [142-191]
+      PathExpression [166-173]
+        Identifier(Singers) [166-173]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-and-data-model#create_a_hierarchy_of_interleaved_tables
+CREATE TABLE Songs (
+ SingerId     INT64 NOT NULL,
+ AlbumId      INT64 NOT NULL,
+ TrackId      INT64 NOT NULL,
+ SongName     STRING(MAX),
+) PRIMARY KEY (SingerId, AlbumId, TrackId),
+ INTERLEAVE IN PARENT Albums ON DELETE CASCADE
+--
+CreateTableStatement [0-228]
+  PathExpression [13-18]
+    Identifier(Songs) [13-18]
+  TableElementList [19-139]
+    ColumnDefinition [22-49]
+      Identifier(SingerId) [22-30]
+      SimpleColumnSchema [35-49]
+        PathExpression [35-40]
+          Identifier(INT64) [35-40]
+        ColumnAttributeList [41-49]
+          NotNullColumnAttribute [41-49]
+    ColumnDefinition [52-79]
+      Identifier(AlbumId) [52-59]
+      SimpleColumnSchema [65-79]
+        PathExpression [65-70]
+          Identifier(INT64) [65-70]
+        ColumnAttributeList [71-79]
+          NotNullColumnAttribute [71-79]
+    ColumnDefinition [82-109]
+      Identifier(TrackId) [82-89]
+      SimpleColumnSchema [95-109]
+        PathExpression [95-100]
+          Identifier(INT64) [95-100]
+        ColumnAttributeList [101-109]
+          NotNullColumnAttribute [101-109]
+    ColumnDefinition [112-136]
+      Identifier(SongName) [112-120]
+      SimpleColumnSchema [125-136]
+        PathExpression [125-131]
+          Identifier(STRING) [125-131]
+        TypeParameterList [131-135]
+          MaxLiteral() [132-135]
+  SpannerTableOptions [140-228]
+    PrimaryKey(ENFORCED) [140-180]
+      PrimaryKeyElementList [152-180]
+        PrimaryKeyElement(ASC) [153-161]
+          Identifier(SingerId) [153-161]
+        PrimaryKeyElement(ASC) [163-170]
+          Identifier(AlbumId) [163-170]
+        PrimaryKeyElement(ASC) [172-179]
+          Identifier(TrackId) [172-179]
+    SpannerInterleaveClause [180-228]
+      PathExpression [204-210]
+        Identifier(Albums) [204-210]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-and-data-model#joins_with_interleaved_tables
+SELECT s.FirstName, a.AlbumTitle
+FROM Singers AS s JOIN Albums AS a ON s.SingerId = a.SingerId
+--
+QueryStatement [0-94]
+  Query [0-94]
+    Select [0-94]
+      SelectList [7-32]
+        SelectColumn [7-18]
+          PathExpression [7-18]
+            Identifier(s) [7-8]
+            Identifier(FirstName) [9-18]
+        SelectColumn [20-32]
+          PathExpression [20-32]
+            Identifier(a) [20-21]
+            Identifier(AlbumTitle) [22-32]
+      FromClause [33-94]
+        Join [38-94]
+          TablePathExpression [38-50]
+            PathExpression [38-45]
+              Identifier(Singers) [38-45]
+            Alias [46-50]
+              Identifier(s) [49-50]
+          Location [51-55]
+          TablePathExpression [56-67]
+            PathExpression [56-62]
+              Identifier(Albums) [56-62]
+            Alias [63-67]
+              Identifier(a) [66-67]
+          OnClause [68-94]
+            BinaryExpression(=) [71-94]
+              PathExpression [71-81]
+                Identifier(s) [71-72]
+                Identifier(SingerId) [73-81]
+              PathExpression [84-94]
+                Identifier(a) [84-85]
+                Identifier(SingerId) [86-94]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-and-data-model#store-nulls
+CREATE TABLE Singers (
+  SingerId   INT64 PRIMARY KEY,
+  FirstName  STRING(1024),
+  LastName   STRING(1024),
+)
+--
+CreateTableStatement [0-110]
+  PathExpression [13-20]
+    Identifier(Singers) [13-20]
+  TableElementList [21-110]
+    ColumnDefinition [25-53]
+      Identifier(SingerId) [25-33]
+      SimpleColumnSchema [36-53]
+        PathExpression [36-41]
+          Identifier(INT64) [36-41]
+        ColumnAttributeList [42-53]
+          PrimaryKeyColumnAttribute(ENFORCED) [42-53]
+    ColumnDefinition [57-80]
+      Identifier(FirstName) [57-66]
+      SimpleColumnSchema [68-80]
+        PathExpression [68-74]
+          Identifier(STRING) [68-74]
+        TypeParameterList [74-79]
+          IntLiteral(1024) [75-79]
+    ColumnDefinition [84-107]
+      Identifier(LastName) [84-92]
+      SimpleColumnSchema [95-107]
+        PathExpression [95-101]
+          Identifier(STRING) [95-101]
+        TypeParameterList [101-106]
+          IntLiteral(1024) [102-106]
diff --git a/parser/testdata/spanner/guides/schema-design.spanner.sql b/parser/testdata/spanner/guides/schema-design.spanner.sql
new file mode 100644
index 0000000..226a0a0
--- /dev/null
+++ b/parser/testdata/spanner/guides/schema-design.spanner.sql
@@ -0,0 +1,18 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/schema-design
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/schema-design#fix_hash_the_key
+# id e993c9dd5625: generated column after NOT NULL (schema-design)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ")" or "," but got keyword AS [at 3:1]
+CREATE TABLE UserAccessLogs (
+ShardId INT64 NOT NULL
+AS (MOD(FARM_FINGERPRINT(CAST(LastAccess AS STRING)), 2048)) STORED,
+LastAccess TIMESTAMP NOT NULL,
+UserId    INT64 NOT NULL,
+) PRIMARY KEY (ShardId, LastAccess, UserId);
diff --git a/parser/testdata/spanner/guides/schema-design.test b/parser/testdata/spanner/guides/schema-design.test
new file mode 100644
index 0000000..f37d098
--- /dev/null
+++ b/parser/testdata/spanner/guides/schema-design.test
@@ -0,0 +1,140 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/schema-design
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/schema-design#uuid_primary_key
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE UserAccessLogs (
+    LogEntryId UUID DEFAULT (NEW_UUID()),
+    LastAccess TIMESTAMP NOT NULL,
+    UserId STRING(1024)
+  ) PRIMARY KEY (LogEntryId)
+--
+CreateTableStatement [0-159]
+  PathExpression [13-27]
+    Identifier(UserAccessLogs) [13-27]
+  TableElementList [28-134]
+    ColumnDefinition [34-70]
+      Identifier(LogEntryId) [34-44]
+      SimpleColumnSchema [45-70]
+        PathExpression [45-49]
+          Identifier(UUID) [45-49]
+        FunctionCall [59-69]
+          PathExpression [59-67]
+            Identifier(NEW_UUID) [59-67]
+    ColumnDefinition [76-105]
+      Identifier(LastAccess) [76-86]
+      SimpleColumnSchema [87-105]
+        PathExpression [87-96]
+          Identifier(TIMESTAMP) [87-96]
+        ColumnAttributeList [97-105]
+          NotNullColumnAttribute [97-105]
+    ColumnDefinition [111-130]
+      Identifier(UserId) [111-117]
+      SimpleColumnSchema [118-130]
+        PathExpression [118-124]
+          Identifier(STRING) [118-124]
+        TypeParameterList [124-129]
+          IntLiteral(1024) [125-129]
+  SpannerTableOptions [135-159]
+    PrimaryKey(ENFORCED) [135-159]
+      PrimaryKeyElementList [147-159]
+        PrimaryKeyElement(ASC) [148-158]
+          Identifier(LogEntryId) [148-158]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-design#uuid_primary_key
+INSERT INTO UserAccessLogs (LastAccess, UserId)
+VALUES ('2016-01-25 10:10:10.555555-05:00', 'TomSmith')
+--
+InsertStatement [0-103]
+  PathExpression [12-26]
+    Identifier(UserAccessLogs) [12-26]
+  ColumnList [27-47]
+    Identifier(LastAccess) [28-38]
+    Identifier(UserId) [40-46]
+  InsertValuesRowList [48-103]
+    InsertValuesRow [55-103]
+      StringLiteral [56-90]
+        StringLiteralComponent('2016-01-25 10:10:10.555555-05:00') [56-90]
+      StringLiteral [92-102]
+        StringLiteralComponent('TomSmith') [92-102]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-design#uuid_primary_key
+INSERT INTO
+  UserAccessLogs (LogEntryId, LastAccess, UserId)
+VALUES
+  (NEW_UUID(), '2016-01-25 10:10:10.555555-05:00', 'TomSmith')
+--
+InsertStatement [0-131]
+  PathExpression [14-28]
+    Identifier(UserAccessLogs) [14-28]
+  ColumnList [29-61]
+    Identifier(LogEntryId) [30-40]
+    Identifier(LastAccess) [42-52]
+    Identifier(UserId) [54-60]
+  InsertValuesRowList [62-131]
+    InsertValuesRow [71-131]
+      FunctionCall [72-82]
+        PathExpression [72-80]
+          Identifier(NEW_UUID) [72-80]
+      StringLiteral [84-118]
+        StringLiteralComponent('2016-01-25 10:10:10.555555-05:00') [84-118]
+      StringLiteral [120-130]
+        StringLiteralComponent('TomSmith') [120-130]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-design#uuid_primary_key
+INSERT INTO
+  UserAccessLogs (LogEntryId, LastAccess, UserId)
+VALUES
+  ('4192bff0-e1e0-43ce-a4db-912808c32493', '2016-01-25 10:10:10.555555-05:00', 'TomSmith')
+--
+InsertStatement [0-159]
+  PathExpression [14-28]
+    Identifier(UserAccessLogs) [14-28]
+  ColumnList [29-61]
+    Identifier(LogEntryId) [30-40]
+    Identifier(LastAccess) [42-52]
+    Identifier(UserId) [54-60]
+  InsertValuesRowList [62-159]
+    InsertValuesRow [71-159]
+      StringLiteral [72-110]
+        StringLiteralComponent('4192bff0-e1e0-43ce-a4db-912808c32493') [72-110]
+      StringLiteral [112-146]
+        StringLiteralComponent('2016-01-25 10:10:10.555555-05:00') [112-146]
+      StringLiteral [148-158]
+        StringLiteralComponent('TomSmith') [148-158]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-design#creating-indexes
+CREATE NULL_FILTERED INDEX UsersByLastAccess ON Users(LastAccess)
+--
+CreateIndexStatement [0-65]
+  PathExpression [27-44]
+    Identifier(UsersByLastAccess) [27-44]
+  PathExpression [48-53]
+    Identifier(Users) [48-53]
+  IndexItemList [53-65]
+    OrderingExpression(ASC) [54-64]
+      PathExpression [54-64]
+        Identifier(LastAccess) [54-64]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-design#creating-indexes
+CREATE NULL_FILTERED INDEX UsersByLastAccess
+ON Users(UserId, LastAccess),
+INTERLEAVE IN Users
+--
+CreateIndexStatement [0-94]
+  PathExpression [27-44]
+    Identifier(UsersByLastAccess) [27-44]
+  PathExpression [48-53]
+    Identifier(Users) [48-53]
+  IndexItemList [53-73]
+    OrderingExpression(ASC) [54-60]
+      PathExpression [54-60]
+        Identifier(UserId) [54-60]
+    OrderingExpression(ASC) [62-72]
+      PathExpression [62-72]
+        Identifier(LastAccess) [62-72]
+  SpannerInterleaveClause [73-94]
+    PathExpression [89-94]
+      Identifier(Users) [89-94]
diff --git a/parser/testdata/spanner/guides/schema-updates-best-practices.test b/parser/testdata/spanner/guides/schema-updates-best-practices.test
new file mode 100644
index 0000000..88be6f8
--- /dev/null
+++ b/parser/testdata/spanner/guides/schema-updates-best-practices.test
@@ -0,0 +1,135 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/schema-updates-best-practices
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/schema-updates-best-practices#large-updates
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Singers (
+SingerId   INT64 NOT NULL,
+FirstName  STRING(1024),
+LastName   STRING(1024),
+) PRIMARY KEY (SingerId)
+--
+CreateTableStatement [0-124]
+  PathExpression [13-20]
+    Identifier(Singers) [13-20]
+  TableElementList [21-101]
+    ColumnDefinition [23-48]
+      Identifier(SingerId) [23-31]
+      SimpleColumnSchema [34-48]
+        PathExpression [34-39]
+          Identifier(INT64) [34-39]
+        ColumnAttributeList [40-48]
+          NotNullColumnAttribute [40-48]
+    ColumnDefinition [50-73]
+      Identifier(FirstName) [50-59]
+      SimpleColumnSchema [61-73]
+        PathExpression [61-67]
+          Identifier(STRING) [61-67]
+        TypeParameterList [67-72]
+          IntLiteral(1024) [68-72]
+    ColumnDefinition [75-98]
+      Identifier(LastName) [75-83]
+      SimpleColumnSchema [86-98]
+        PathExpression [86-92]
+          Identifier(STRING) [86-92]
+        TypeParameterList [92-97]
+          IntLiteral(1024) [93-97]
+  SpannerTableOptions [102-124]
+    PrimaryKey(ENFORCED) [102-124]
+      PrimaryKeyElementList [114-124]
+        PrimaryKeyElement(ASC) [115-123]
+          Identifier(SingerId) [115-123]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-updates-best-practices#large-updates
+CREATE INDEX SingersByFirstName ON Singers(FirstName)
+--
+CreateIndexStatement [0-53]
+  PathExpression [13-31]
+    Identifier(SingersByFirstName) [13-31]
+  PathExpression [35-42]
+    Identifier(Singers) [35-42]
+  IndexItemList [42-53]
+    OrderingExpression(ASC) [43-52]
+      PathExpression [43-52]
+        Identifier(FirstName) [43-52]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-updates-best-practices#large-updates
+CREATE INDEX SingersByLastName ON Singers(LastName)
+--
+CreateIndexStatement [0-51]
+  PathExpression [13-30]
+    Identifier(SingersByLastName) [13-30]
+  PathExpression [34-41]
+    Identifier(Singers) [34-41]
+  IndexItemList [41-51]
+    OrderingExpression(ASC) [42-50]
+      PathExpression [42-50]
+        Identifier(LastName) [42-50]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-updates-best-practices#large-updates
+CREATE TABLE Albums (
+SingerId   INT64 NOT NULL,
+AlbumId    INT64 NOT NULL,
+AlbumTitle STRING(MAX),
+) PRIMARY KEY (SingerId, AlbumId)
+--
+CreateTableStatement [0-133]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-101]
+    ColumnDefinition [22-47]
+      Identifier(SingerId) [22-30]
+      SimpleColumnSchema [33-47]
+        PathExpression [33-38]
+          Identifier(INT64) [33-38]
+        ColumnAttributeList [39-47]
+          NotNullColumnAttribute [39-47]
+    ColumnDefinition [49-74]
+      Identifier(AlbumId) [49-56]
+      SimpleColumnSchema [60-74]
+        PathExpression [60-65]
+          Identifier(INT64) [60-65]
+        ColumnAttributeList [66-74]
+          NotNullColumnAttribute [66-74]
+    ColumnDefinition [76-98]
+      Identifier(AlbumTitle) [76-86]
+      SimpleColumnSchema [87-98]
+        PathExpression [87-93]
+          Identifier(STRING) [87-93]
+        TypeParameterList [93-97]
+          MaxLiteral() [94-97]
+  SpannerTableOptions [102-133]
+    PrimaryKey(ENFORCED) [102-133]
+      PrimaryKeyElementList [114-133]
+        PrimaryKeyElement(ASC) [115-123]
+          Identifier(SingerId) [115-123]
+        PrimaryKeyElement(ASC) [125-132]
+          Identifier(AlbumId) [125-132]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-updates-best-practices#large-updates
+CREATE INDEX AlbumsByTitle ON Albums(AlbumTitle)
+--
+CreateIndexStatement [0-48]
+  PathExpression [13-26]
+    Identifier(AlbumsByTitle) [13-26]
+  PathExpression [30-36]
+    Identifier(Albums) [30-36]
+  IndexItemList [36-48]
+    OrderingExpression(ASC) [37-47]
+      PathExpression [37-47]
+        Identifier(AlbumTitle) [37-47]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-updates-best-practices#large-updates
+CREATE INDEX UnrelatedIndex ON UnrelatedTable(UnrelatedIndexKey)
+--
+CreateIndexStatement [0-64]
+  PathExpression [13-27]
+    Identifier(UnrelatedIndex) [13-27]
+  PathExpression [31-45]
+    Identifier(UnrelatedTable) [31-45]
+  IndexItemList [45-64]
+    OrderingExpression(ASC) [46-63]
+      PathExpression [46-63]
+        Identifier(UnrelatedIndexKey) [46-63]
diff --git a/parser/testdata/spanner/guides/schema-updates.spanner.sql b/parser/testdata/spanner/guides/schema-updates.spanner.sql
new file mode 100644
index 0000000..b4f782e
--- /dev/null
+++ b/parser/testdata/spanner/guides/schema-updates.spanner.sql
@@ -0,0 +1,16 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/schema-updates
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/schema-updates#updates-that-require-validation
+# id 41eab2a72585: proto bundles (schema-updates)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected keyword PROTO [at 1:8]
+CREATE PROTO BUNDLE (
+  googlesql.example.music.Songwriter,
+  googlesql.example.music.RecordLabel,
+);
diff --git a/parser/testdata/spanner/guides/schema-updates.test b/parser/testdata/spanner/guides/schema-updates.test
new file mode 100644
index 0000000..db8bd81
--- /dev/null
+++ b/parser/testdata/spanner/guides/schema-updates.test
@@ -0,0 +1,213 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/schema-updates
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/schema-updates#updates-that-require-validation
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+PACIFIC_ENTERTAINMENT = 1
+--
+BinaryExpression(=) [0-25]
+  PathExpression [0-21]
+    Identifier(PACIFIC_ENTERTAINMENT) [0-21]
+  IntLiteral(1) [24-25]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-updates#updates-that-require-validation
+[mode=expression]
+XYZ_RECORDS = 2
+--
+BinaryExpression(=) [0-15]
+  PathExpression [0-11]
+    Identifier(XYZ_RECORDS) [0-11]
+  IntLiteral(2) [14-15]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-updates#updates-that-require-validation
+CREATE TABLE Songwriters (
+  Id         INT64 NOT NULL,
+  FirstName  STRING(1024),
+  LastName   STRING(1024),
+  Nickname   STRING(MAX),
+  OpaqueData BYTES(MAX),
+  SongWriter googlesql.example.music.Songwriter
+) PRIMARY KEY (Id)
+--
+CreateTableStatement [0-227]
+  PathExpression [13-24]
+    Identifier(Songwriters) [13-24]
+  TableElementList [25-210]
+    ColumnDefinition [29-54]
+      Identifier(Id) [29-31]
+      SimpleColumnSchema [40-54]
+        PathExpression [40-45]
+          Identifier(INT64) [40-45]
+        ColumnAttributeList [46-54]
+          NotNullColumnAttribute [46-54]
+    ColumnDefinition [58-81]
+      Identifier(FirstName) [58-67]
+      SimpleColumnSchema [69-81]
+        PathExpression [69-75]
+          Identifier(STRING) [69-75]
+        TypeParameterList [75-80]
+          IntLiteral(1024) [76-80]
+    ColumnDefinition [85-108]
+      Identifier(LastName) [85-93]
+      SimpleColumnSchema [96-108]
+        PathExpression [96-102]
+          Identifier(STRING) [96-102]
+        TypeParameterList [102-107]
+          IntLiteral(1024) [103-107]
+    ColumnDefinition [112-134]
+      Identifier(Nickname) [112-120]
+      SimpleColumnSchema [123-134]
+        PathExpression [123-129]
+          Identifier(STRING) [123-129]
+        TypeParameterList [129-133]
+          MaxLiteral() [130-133]
+    ColumnDefinition [138-159]
+      Identifier(OpaqueData) [138-148]
+      SimpleColumnSchema [149-159]
+        PathExpression [149-154]
+          Identifier(BYTES) [149-154]
+        TypeParameterList [154-158]
+          MaxLiteral() [155-158]
+    ColumnDefinition [163-208]
+      Identifier(SongWriter) [163-173]
+      SimpleColumnSchema [174-208]
+        PathExpression [174-208]
+          Identifier(googlesql) [174-183]
+          Identifier(example) [184-191]
+          Identifier(music) [192-197]
+          Identifier(Songwriter) [198-208]
+  SpannerTableOptions [211-227]
+    PrimaryKey(ENFORCED) [211-227]
+      PrimaryKeyElementList [223-227]
+        PrimaryKeyElement(ASC) [224-226]
+          Identifier(Id) [224-226]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-updates#updates-that-require-validation
+CREATE TABLE Albums (
+  SongwriterId     INT64 NOT NULL,
+  AlbumId          INT64 NOT NULL,
+  AlbumTitle       STRING(MAX),
+  Label            INT32
+) PRIMARY KEY (SongwriterId, AlbumId)
+--
+CreateTableStatement [0-186]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-150]
+    ColumnDefinition [24-55]
+      Identifier(SongwriterId) [24-36]
+      SimpleColumnSchema [41-55]
+        PathExpression [41-46]
+          Identifier(INT64) [41-46]
+        ColumnAttributeList [47-55]
+          NotNullColumnAttribute [47-55]
+    ColumnDefinition [59-90]
+      Identifier(AlbumId) [59-66]
+      SimpleColumnSchema [76-90]
+        PathExpression [76-81]
+          Identifier(INT64) [76-81]
+        ColumnAttributeList [82-90]
+          NotNullColumnAttribute [82-90]
+    ColumnDefinition [94-122]
+      Identifier(AlbumTitle) [94-104]
+      SimpleColumnSchema [111-122]
+        PathExpression [111-117]
+          Identifier(STRING) [111-117]
+        TypeParameterList [117-121]
+          MaxLiteral() [118-121]
+    ColumnDefinition [126-148]
+      Identifier(Label) [126-131]
+      SimpleColumnSchema [143-148]
+        PathExpression [143-148]
+          Identifier(INT32) [143-148]
+  SpannerTableOptions [151-186]
+    PrimaryKey(ENFORCED) [151-186]
+      PrimaryKeyElementList [163-186]
+        PrimaryKeyElement(ASC) [164-176]
+          Identifier(SongwriterId) [164-176]
+        PrimaryKeyElement(ASC) [178-185]
+          Identifier(AlbumId) [178-185]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-updates#updates-that-require-validation
+ALTER TABLE Songwriters ALTER COLUMN Nickname STRING(MAX) NOT NULL
+--
+AlterTableStatement [0-66]
+  PathExpression [12-23]
+    Identifier(Songwriters) [12-23]
+  AlterActionList [24-66]
+    SpannerAlterColumnAction [24-66]
+      ColumnDefinition [37-66]
+        Identifier(Nickname) [37-45]
+        SimpleColumnSchema [46-66]
+          PathExpression [46-52]
+            Identifier(STRING) [46-52]
+          TypeParameterList [52-56]
+            MaxLiteral() [53-56]
+          ColumnAttributeList [58-66]
+            NotNullColumnAttribute [58-66]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-updates#updates-that-require-validation
+ALTER TABLE Songwriters ALTER COLUMN FirstName STRING(10)
+--
+AlterTableStatement [0-57]
+  PathExpression [12-23]
+    Identifier(Songwriters) [12-23]
+  AlterActionList [24-57]
+    SpannerAlterColumnAction [24-57]
+      ColumnDefinition [37-57]
+        Identifier(FirstName) [37-46]
+        SimpleColumnSchema [47-57]
+          PathExpression [47-53]
+            Identifier(STRING) [47-53]
+          TypeParameterList [53-56]
+            IntLiteral(10) [54-56]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-updates#updates-that-require-validation
+ALTER TABLE Songwriters ALTER COLUMN OpaqueData STRING(MAX)
+--
+AlterTableStatement [0-59]
+  PathExpression [12-23]
+    Identifier(Songwriters) [12-23]
+  AlterActionList [24-59]
+    SpannerAlterColumnAction [24-59]
+      ColumnDefinition [37-59]
+        Identifier(OpaqueData) [37-47]
+        SimpleColumnSchema [48-59]
+          PathExpression [48-54]
+            Identifier(STRING) [48-54]
+          TypeParameterList [54-58]
+            MaxLiteral() [55-58]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-updates#updates-that-require-validation
+ALTER TABLE Albums ALTER COLUMN Label googlesql.example.music.RecordLabel
+--
+AlterTableStatement [0-73]
+  PathExpression [12-18]
+    Identifier(Albums) [12-18]
+  AlterActionList [19-73]
+    SpannerAlterColumnAction [19-73]
+      ColumnDefinition [32-73]
+        Identifier(Label) [32-37]
+        SimpleColumnSchema [38-73]
+          PathExpression [38-73]
+            Identifier(googlesql) [38-47]
+            Identifier(example) [48-55]
+            Identifier(music) [56-61]
+            Identifier(RecordLabel) [62-73]
+==
+# https://docs.cloud.google.com/spanner/docs/schema-updates#updates-that-require-validation
+ALTER TABLE Albums ALTER COLUMN LastUpdateTime SET OPTIONS (allow_commit_timestamp = true)
+--
+AlterTableStatement [0-90]
+  PathExpression [12-18]
+    Identifier(Albums) [12-18]
+  AlterActionList [19-90]
+    AlterColumnOptionsAction [19-90]
+      Identifier(LastUpdateTime) [32-46]
+      OptionsList [59-90]
+        OptionsEntry [60-89]
+          Identifier(allow_commit_timestamp) [60-82]
+          BooleanLiteral(true) [85-89]
diff --git a/parser/testdata/spanner/guides/secondary-indexes.spanner.sql b/parser/testdata/spanner/guides/secondary-indexes.spanner.sql
new file mode 100644
index 0000000..bf7b5ba
--- /dev/null
+++ b/parser/testdata/spanner/guides/secondary-indexes.spanner.sql
@@ -0,0 +1,23 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/secondary-indexes
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#alter_index
+# id 11fea51e4d41: ALTER INDEX ADD/DROP STORED COLUMN (secondary-indexes)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected keyword STORED [at 1:36]
+ALTER INDEX AlbumsByAlbumTitle ADD STORED COLUMN MarketingBudget;
+
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#alter_index
+# id 23425be4c4f6: ALTER INDEX ADD/DROP STORED COLUMN (secondary-indexes)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected keyword STORED [at 1:37]
+ALTER INDEX AlbumsByAlbumTitle DROP STORED COLUMN MarketingBudget;
+
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#proto-indexing
+# id fe13330c8cf7: proto bundles (secondary-indexes)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected keyword PROTO [at 1:8]
+CREATE PROTO BUNDLE (googlesql.example.SingerInfo, googlesql.example.SingerInfo.Residence);
diff --git a/parser/testdata/spanner/guides/secondary-indexes.test b/parser/testdata/spanner/guides/secondary-indexes.test
new file mode 100644
index 0000000..9e5ce98
--- /dev/null
+++ b/parser/testdata/spanner/guides/secondary-indexes.test
@@ -0,0 +1,835 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/secondary-indexes
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#add-index
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE INDEX SongsBySingerSongName ON Songs(SingerId, SongName),
+    INTERLEAVE IN Singers
+--
+CreateIndexStatement [0-90]
+  PathExpression [13-34]
+    Identifier(SongsBySingerSongName) [13-34]
+  PathExpression [38-43]
+    Identifier(Songs) [38-43]
+  IndexItemList [43-63]
+    OrderingExpression(ASC) [44-52]
+      PathExpression [44-52]
+        Identifier(SingerId) [44-52]
+    OrderingExpression(ASC) [54-62]
+      PathExpression [54-62]
+        Identifier(SongName) [54-62]
+  SpannerInterleaveClause [63-90]
+    PathExpression [83-90]
+      Identifier(Singers) [83-90]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#add-index
+CREATE INDEX SongsBySingerAlbumSongName ON Songs(SingerId, AlbumId, SongName),
+    INTERLEAVE IN Albums
+--
+CreateIndexStatement [0-103]
+  PathExpression [13-39]
+    Identifier(SongsBySingerAlbumSongName) [13-39]
+  PathExpression [43-48]
+    Identifier(Songs) [43-48]
+  IndexItemList [48-77]
+    OrderingExpression(ASC) [49-57]
+      PathExpression [49-57]
+        Identifier(SingerId) [49-57]
+    OrderingExpression(ASC) [59-66]
+      PathExpression [59-66]
+        Identifier(AlbumId) [59-66]
+    OrderingExpression(ASC) [68-76]
+      PathExpression [68-76]
+        Identifier(SongName) [68-76]
+  SpannerInterleaveClause [77-103]
+    PathExpression [97-103]
+      Identifier(Albums) [97-103]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#add-index
+CREATE INDEX SongsBySingerAlbumSongNameDesc ON Songs(SingerId, AlbumId, SongName DESC),
+    INTERLEAVE IN Albums
+--
+CreateIndexStatement [0-112]
+  PathExpression [13-43]
+    Identifier(SongsBySingerAlbumSongNameDesc) [13-43]
+  PathExpression [47-52]
+    Identifier(Songs) [47-52]
+  IndexItemList [52-86]
+    OrderingExpression(ASC) [53-61]
+      PathExpression [53-61]
+        Identifier(SingerId) [53-61]
+    OrderingExpression(ASC) [63-70]
+      PathExpression [63-70]
+        Identifier(AlbumId) [63-70]
+    OrderingExpression(DESC) [72-85]
+      PathExpression [72-80]
+        Identifier(SongName) [72-80]
+  SpannerInterleaveClause [86-112]
+    PathExpression [106-112]
+      Identifier(Albums) [106-112]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#indexes_and_interleaving
+CREATE TABLE Albums (
+  SingerId     INT64 NOT NULL,
+  AlbumId      INT64 NOT NULL,
+  AlbumTitle   STRING(MAX),
+  PublisherId  INT64 NOT NULL
+) PRIMARY KEY (SingerId, AlbumId),
+  INTERLEAVE IN PARENT Singers ON DELETE CASCADE
+--
+CreateTableStatement [0-225]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-143]
+    ColumnDefinition [24-51]
+      Identifier(SingerId) [24-32]
+      SimpleColumnSchema [37-51]
+        PathExpression [37-42]
+          Identifier(INT64) [37-42]
+        ColumnAttributeList [43-51]
+          NotNullColumnAttribute [43-51]
+    ColumnDefinition [55-82]
+      Identifier(AlbumId) [55-62]
+      SimpleColumnSchema [68-82]
+        PathExpression [68-73]
+          Identifier(INT64) [68-73]
+        ColumnAttributeList [74-82]
+          NotNullColumnAttribute [74-82]
+    ColumnDefinition [86-110]
+      Identifier(AlbumTitle) [86-96]
+      SimpleColumnSchema [99-110]
+        PathExpression [99-105]
+          Identifier(STRING) [99-105]
+        TypeParameterList [105-109]
+          MaxLiteral() [106-109]
+    ColumnDefinition [114-141]
+      Identifier(PublisherId) [114-125]
+      SimpleColumnSchema [127-141]
+        PathExpression [127-132]
+          Identifier(INT64) [127-132]
+        ColumnAttributeList [133-141]
+          NotNullColumnAttribute [133-141]
+  SpannerTableOptions [144-225]
+    PrimaryKey(ENFORCED) [144-175]
+      PrimaryKeyElementList [156-175]
+        PrimaryKeyElement(ASC) [157-165]
+          Identifier(SingerId) [157-165]
+        PrimaryKeyElement(ASC) [167-174]
+          Identifier(AlbumId) [167-174]
+    SpannerInterleaveClause [175-225]
+      PathExpression [200-207]
+        Identifier(Singers) [200-207]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#indexes_and_interleaving
+CREATE TABLE Songs (
+  SingerId     INT64 NOT NULL,
+  AlbumId      INT64 NOT NULL,
+  TrackId      INT64 NOT NULL,
+  PublisherId  INT64 NOT NULL,
+  SongName     STRING(MAX)
+) PRIMARY KEY (SingerId, AlbumId, TrackId),
+  INTERLEAVE IN PARENT Albums ON DELETE CASCADE
+--
+CreateTableStatement [0-263]
+  PathExpression [13-18]
+    Identifier(Songs) [13-18]
+  TableElementList [19-173]
+    ColumnDefinition [23-50]
+      Identifier(SingerId) [23-31]
+      SimpleColumnSchema [36-50]
+        PathExpression [36-41]
+          Identifier(INT64) [36-41]
+        ColumnAttributeList [42-50]
+          NotNullColumnAttribute [42-50]
+    ColumnDefinition [54-81]
+      Identifier(AlbumId) [54-61]
+      SimpleColumnSchema [67-81]
+        PathExpression [67-72]
+          Identifier(INT64) [67-72]
+        ColumnAttributeList [73-81]
+          NotNullColumnAttribute [73-81]
+    ColumnDefinition [85-112]
+      Identifier(TrackId) [85-92]
+      SimpleColumnSchema [98-112]
+        PathExpression [98-103]
+          Identifier(INT64) [98-103]
+        ColumnAttributeList [104-112]
+          NotNullColumnAttribute [104-112]
+    ColumnDefinition [116-143]
+      Identifier(PublisherId) [116-127]
+      SimpleColumnSchema [129-143]
+        PathExpression [129-134]
+          Identifier(INT64) [129-134]
+        ColumnAttributeList [135-143]
+          NotNullColumnAttribute [135-143]
+    ColumnDefinition [147-171]
+      Identifier(SongName) [147-155]
+      SimpleColumnSchema [160-171]
+        PathExpression [160-166]
+          Identifier(STRING) [160-166]
+        TypeParameterList [166-170]
+          MaxLiteral() [167-170]
+  SpannerTableOptions [174-263]
+    PrimaryKey(ENFORCED) [174-214]
+      PrimaryKeyElementList [186-214]
+        PrimaryKeyElement(ASC) [187-195]
+          Identifier(SingerId) [187-195]
+        PrimaryKeyElement(ASC) [197-204]
+          Identifier(AlbumId) [197-204]
+        PrimaryKeyElement(ASC) [206-213]
+          Identifier(TrackId) [206-213]
+    SpannerInterleaveClause [214-263]
+      PathExpression [239-245]
+        Identifier(Albums) [239-245]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#indexes_and_interleaving
+CREATE TABLE Publishers (
+  Id            INT64 NOT NULL,
+  PublisherName STRING(MAX)
+) PRIMARY KEY (Id)
+--
+CreateTableStatement [0-104]
+  PathExpression [13-23]
+    Identifier(Publishers) [13-23]
+  TableElementList [24-87]
+    ColumnDefinition [28-56]
+      Identifier(Id) [28-30]
+      SimpleColumnSchema [42-56]
+        PathExpression [42-47]
+          Identifier(INT64) [42-47]
+        ColumnAttributeList [48-56]
+          NotNullColumnAttribute [48-56]
+    ColumnDefinition [60-85]
+      Identifier(PublisherName) [60-73]
+      SimpleColumnSchema [74-85]
+        PathExpression [74-80]
+          Identifier(STRING) [74-80]
+        TypeParameterList [80-84]
+          MaxLiteral() [81-84]
+  SpannerTableOptions [88-104]
+    PrimaryKey(ENFORCED) [88-104]
+      PrimaryKeyElementList [100-104]
+        PrimaryKeyElement(ASC) [101-103]
+          Identifier(Id) [101-103]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#indexes_and_interleaving
+CREATE INDEX SongsBySingerAlbumSongName
+    ON Songs(SingerId, AlbumId, SongName)
+--
+CreateIndexStatement [0-81]
+  PathExpression [13-39]
+    Identifier(SongsBySingerAlbumSongName) [13-39]
+  PathExpression [47-52]
+    Identifier(Songs) [47-52]
+  IndexItemList [52-81]
+    OrderingExpression(ASC) [53-61]
+      PathExpression [53-61]
+        Identifier(SingerId) [53-61]
+    OrderingExpression(ASC) [63-70]
+      PathExpression [63-70]
+        Identifier(AlbumId) [63-70]
+    OrderingExpression(ASC) [72-80]
+      PathExpression [72-80]
+        Identifier(SongName) [72-80]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#indexes_and_interleaving
+CREATE INDEX SongsBySingerAlbumSongName
+    ON Songs(SingerId, AlbumId, SongName),
+    INTERLEAVE IN Albums
+--
+CreateIndexStatement [0-107]
+  PathExpression [13-39]
+    Identifier(SongsBySingerAlbumSongName) [13-39]
+  PathExpression [47-52]
+    Identifier(Songs) [47-52]
+  IndexItemList [52-81]
+    OrderingExpression(ASC) [53-61]
+      PathExpression [53-61]
+        Identifier(SingerId) [53-61]
+    OrderingExpression(ASC) [63-70]
+      PathExpression [63-70]
+        Identifier(AlbumId) [63-70]
+    OrderingExpression(ASC) [72-80]
+      PathExpression [72-80]
+        Identifier(SongName) [72-80]
+  SpannerInterleaveClause [81-107]
+    PathExpression [101-107]
+      Identifier(Albums) [101-107]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#indexes_and_interleaving
+CREATE INDEX SongsByPublisherSingerAlbumSongName
+    ON Songs(PublisherId, SingerId, AlbumId, SongName),
+    INTERLEAVE IN Publishers
+--
+CreateIndexStatement [0-133]
+  PathExpression [13-48]
+    Identifier(SongsByPublisherSingerAlbumSongName) [13-48]
+  PathExpression [56-61]
+    Identifier(Songs) [56-61]
+  IndexItemList [61-103]
+    OrderingExpression(ASC) [62-73]
+      PathExpression [62-73]
+        Identifier(PublisherId) [62-73]
+    OrderingExpression(ASC) [75-83]
+      PathExpression [75-83]
+        Identifier(SingerId) [75-83]
+    OrderingExpression(ASC) [85-92]
+      PathExpression [85-92]
+        Identifier(AlbumId) [85-92]
+    OrderingExpression(ASC) [94-102]
+      PathExpression [94-102]
+        Identifier(SongName) [94-102]
+  SpannerInterleaveClause [103-133]
+    PathExpression [123-133]
+      Identifier(Publishers) [123-133]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#when-interleaved
+-- Indexing albums by title for a specific singer
+CREATE INDEX AlbumsBySingerTitle ON Albums(SingerId, AlbumTitle),
+  INTERLEAVE IN Singers
+--
+CreateIndexStatement [50-139]
+  PathExpression [63-82]
+    Identifier(AlbumsBySingerTitle) [63-82]
+  PathExpression [86-92]
+    Identifier(Albums) [86-92]
+  IndexItemList [92-114]
+    OrderingExpression(ASC) [93-101]
+      PathExpression [93-101]
+        Identifier(SingerId) [93-101]
+    OrderingExpression(ASC) [103-113]
+      PathExpression [103-113]
+        Identifier(AlbumTitle) [103-113]
+  SpannerInterleaveClause [114-139]
+    PathExpression [132-139]
+      Identifier(Singers) [132-139]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#when-global
+-- Indexing all albums globally by title. For example, for a global search catalog.
+CREATE INDEX AlbumsByTitle ON Albums(AlbumTitle)
+--
+CreateIndexStatement [84-132]
+  PathExpression [97-110]
+    Identifier(AlbumsByTitle) [97-110]
+  PathExpression [114-120]
+    Identifier(Albums) [114-120]
+  IndexItemList [120-132]
+    OrderingExpression(ASC) [121-131]
+      PathExpression [121-131]
+        Identifier(AlbumTitle) [121-131]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#query-with-index
+CREATE TABLE Albums (
+  SingerId         INT64 NOT NULL,
+  AlbumId          INT64 NOT NULL,
+  AlbumTitle       STRING(MAX),
+  MarketingBudget  INT64,
+) PRIMARY KEY (SingerId, AlbumId),
+  INTERLEAVE IN PARENT Singers ON DELETE CASCADE
+--
+CreateTableStatement [0-233]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-151]
+    ColumnDefinition [24-55]
+      Identifier(SingerId) [24-32]
+      SimpleColumnSchema [41-55]
+        PathExpression [41-46]
+          Identifier(INT64) [41-46]
+        ColumnAttributeList [47-55]
+          NotNullColumnAttribute [47-55]
+    ColumnDefinition [59-90]
+      Identifier(AlbumId) [59-66]
+      SimpleColumnSchema [76-90]
+        PathExpression [76-81]
+          Identifier(INT64) [76-81]
+        ColumnAttributeList [82-90]
+          NotNullColumnAttribute [82-90]
+    ColumnDefinition [94-122]
+      Identifier(AlbumTitle) [94-104]
+      SimpleColumnSchema [111-122]
+        PathExpression [111-117]
+          Identifier(STRING) [111-117]
+        TypeParameterList [117-121]
+          MaxLiteral() [118-121]
+    ColumnDefinition [126-148]
+      Identifier(MarketingBudget) [126-141]
+      SimpleColumnSchema [143-148]
+        PathExpression [143-148]
+          Identifier(INT64) [143-148]
+  SpannerTableOptions [152-233]
+    PrimaryKey(ENFORCED) [152-183]
+      PrimaryKeyElementList [164-183]
+        PrimaryKeyElement(ASC) [165-173]
+          Identifier(SingerId) [165-173]
+        PrimaryKeyElement(ASC) [175-182]
+          Identifier(AlbumId) [175-182]
+    SpannerInterleaveClause [183-233]
+      PathExpression [208-215]
+        Identifier(Singers) [208-215]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#index-directive
+SELECT AlbumId, AlbumTitle, MarketingBudget
+    FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle}
+    WHERE AlbumTitle >= "Aardvark" AND AlbumTitle < "Goo"
+--
+QueryStatement [0-150]
+  Query [0-150]
+    Select [0-150]
+      SelectList [7-43]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+        SelectColumn [16-26]
+          PathExpression [16-26]
+            Identifier(AlbumTitle) [16-26]
+        SelectColumn [28-43]
+          PathExpression [28-43]
+            Identifier(MarketingBudget) [28-43]
+      FromClause [48-92]
+        TablePathExpression [53-92]
+          PathExpression [53-59]
+            Identifier(Albums) [53-59]
+          Hint [59-92]
+            HintEntry [61-91]
+              Identifier(FORCE_INDEX) [61-72]
+              PathExpression [73-91]
+                Identifier(AlbumsByAlbumTitle) [73-91]
+      WhereClause [97-150]
+        AndExpr [103-150]
+          BinaryExpression(>=) [103-127]
+            PathExpression [103-113]
+              Identifier(AlbumTitle) [103-113]
+            StringLiteral [117-127]
+              StringLiteralComponent("Aardvark") [117-127]
+          BinaryExpression(<) [132-150]
+            PathExpression [132-142]
+              Identifier(AlbumTitle) [132-142]
+            StringLiteral [145-150]
+              StringLiteralComponent("Goo") [145-150]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#read-with-index
+CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle DESC)
+--
+CreateIndexStatement [0-58]
+  PathExpression [13-31]
+    Identifier(AlbumsByAlbumTitle) [13-31]
+  PathExpression [35-41]
+    Identifier(Albums) [35-41]
+  IndexItemList [41-58]
+    OrderingExpression(DESC) [42-57]
+      PathExpression [42-52]
+        Identifier(AlbumTitle) [42-52]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#null-indexing
+SELECT s.SingerId, s.FirstName, s.LastName
+    FROM Singers@{FORCE_INDEX=SingersByFirstLastName} AS s
+    WHERE s.FirstName IS NULL
+--
+QueryStatement [0-131]
+  Query [0-131]
+    Select [0-131]
+      SelectList [7-42]
+        SelectColumn [7-17]
+          PathExpression [7-17]
+            Identifier(s) [7-8]
+            Identifier(SingerId) [9-17]
+        SelectColumn [19-30]
+          PathExpression [19-30]
+            Identifier(s) [19-20]
+            Identifier(FirstName) [21-30]
+        SelectColumn [32-42]
+          PathExpression [32-42]
+            Identifier(s) [32-33]
+            Identifier(LastName) [34-42]
+      FromClause [47-101]
+        TablePathExpression [52-101]
+          PathExpression [52-59]
+            Identifier(Singers) [52-59]
+          Hint [59-96]
+            HintEntry [61-95]
+              Identifier(FORCE_INDEX) [61-72]
+              PathExpression [73-95]
+                Identifier(SingersByFirstLastName) [73-95]
+          Alias [97-101]
+            Identifier(s) [100-101]
+      WhereClause [106-131]
+        BinaryExpression(IS) [112-131]
+          PathExpression [112-123]
+            Identifier(s) [112-113]
+            Identifier(FirstName) [114-123]
+          NullLiteral(NULL) [127-131]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#null-indexing-disable
+CREATE NULL_FILTERED INDEX SingersByFirstLastNameNoNulls
+    ON Singers(FirstName, LastName)
+--
+CreateIndexStatement [0-92]
+  PathExpression [27-56]
+    Identifier(SingersByFirstLastNameNoNulls) [27-56]
+  PathExpression [64-71]
+    Identifier(Singers) [64-71]
+  IndexItemList [71-92]
+    OrderingExpression(ASC) [72-81]
+      PathExpression [72-81]
+        Identifier(FirstName) [72-81]
+    OrderingExpression(ASC) [83-91]
+      PathExpression [83-91]
+        Identifier(LastName) [83-91]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#null-indexing-disable
+SELECT FirstName, LastName
+    FROM Singers@{FORCE_INDEX=SingersByFirstLastNameNoNulls}
+    WHERE FirstName = 'John' AND LastName IS NOT NULL
+--
+QueryStatement [0-141]
+  Query [0-141]
+    Select [0-141]
+      SelectList [7-26]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(FirstName) [7-16]
+        SelectColumn [18-26]
+          PathExpression [18-26]
+            Identifier(LastName) [18-26]
+      FromClause [31-87]
+        TablePathExpression [36-87]
+          PathExpression [36-43]
+            Identifier(Singers) [36-43]
+          Hint [43-87]
+            HintEntry [45-86]
+              Identifier(FORCE_INDEX) [45-56]
+              PathExpression [57-86]
+                Identifier(SingersByFirstLastNameNoNulls) [57-86]
+      WhereClause [92-141]
+        AndExpr [98-141]
+          BinaryExpression(=) [98-116]
+            PathExpression [98-107]
+              Identifier(FirstName) [98-107]
+            StringLiteral [110-116]
+              StringLiteralComponent('John') [110-116]
+          BinaryExpression(IS NOT) [121-141]
+            PathExpression [121-129]
+              Identifier(LastName) [121-129]
+            NullLiteral(NULL) [137-141]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#proto-indexing
+CREATE INDEX SingersByNationality ON Singers(SingerNationality)
+--
+CreateIndexStatement [0-63]
+  PathExpression [13-33]
+    Identifier(SingersByNationality) [13-33]
+  PathExpression [37-44]
+    Identifier(Singers) [37-44]
+  IndexItemList [44-63]
+    OrderingExpression(ASC) [45-62]
+      PathExpression [45-62]
+        Identifier(SingerNationality) [45-62]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#proto-indexing
+SELECT s.SingerId, s.FirstName
+FROM Singers AS s
+WHERE s.SingerNationality = "English"
+--
+QueryStatement [0-86]
+  Query [0-86]
+    Select [0-86]
+      SelectList [7-30]
+        SelectColumn [7-17]
+          PathExpression [7-17]
+            Identifier(s) [7-8]
+            Identifier(SingerId) [9-17]
+        SelectColumn [19-30]
+          PathExpression [19-30]
+            Identifier(s) [19-20]
+            Identifier(FirstName) [21-30]
+      FromClause [31-48]
+        TablePathExpression [36-48]
+          PathExpression [36-43]
+            Identifier(Singers) [36-43]
+          Alias [44-48]
+            Identifier(s) [47-48]
+      WhereClause [49-86]
+        BinaryExpression(=) [55-86]
+          PathExpression [55-74]
+            Identifier(s) [55-56]
+            Identifier(SingerNationality) [57-74]
+          StringLiteral [77-86]
+            StringLiteralComponent("English") [77-86]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#unique-indexes-null-filtered
+CREATE TABLE ExampleTable (
+  Key1 INT64 NOT NULL,
+  Key2 INT64,
+  Key3 INT64,
+  Col1 INT64,
+) PRIMARY KEY (Key1, Key2, Key3)
+--
+CreateTableStatement [0-125]
+  PathExpression [13-25]
+    Identifier(ExampleTable) [13-25]
+  TableElementList [26-94]
+    ColumnDefinition [30-49]
+      Identifier(Key1) [30-34]
+      SimpleColumnSchema [35-49]
+        PathExpression [35-40]
+          Identifier(INT64) [35-40]
+        ColumnAttributeList [41-49]
+          NotNullColumnAttribute [41-49]
+    ColumnDefinition [53-63]
+      Identifier(Key2) [53-57]
+      SimpleColumnSchema [58-63]
+        PathExpression [58-63]
+          Identifier(INT64) [58-63]
+    ColumnDefinition [67-77]
+      Identifier(Key3) [67-71]
+      SimpleColumnSchema [72-77]
+        PathExpression [72-77]
+          Identifier(INT64) [72-77]
+    ColumnDefinition [81-91]
+      Identifier(Col1) [81-85]
+      SimpleColumnSchema [86-91]
+        PathExpression [86-91]
+          Identifier(INT64) [86-91]
+  SpannerTableOptions [95-125]
+    PrimaryKey(ENFORCED) [95-125]
+      PrimaryKeyElementList [107-125]
+        PrimaryKeyElement(ASC) [108-112]
+          Identifier(Key1) [108-112]
+        PrimaryKeyElement(ASC) [114-118]
+          Identifier(Key2) [114-118]
+        PrimaryKeyElement(ASC) [120-124]
+          Identifier(Key3) [120-124]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#unique-indexes-null-filtered
+CREATE UNIQUE NULL_FILTERED INDEX ExampleIndex ON ExampleTable (Key1, Key2, Col1)
+--
+CreateIndexStatement(UNIQUE) [0-81]
+  PathExpression [34-46]
+    Identifier(ExampleIndex) [34-46]
+  PathExpression [50-62]
+    Identifier(ExampleTable) [50-62]
+  IndexItemList [63-81]
+    OrderingExpression(ASC) [64-68]
+      PathExpression [64-68]
+        Identifier(Key1) [64-68]
+    OrderingExpression(ASC) [70-74]
+      PathExpression [70-74]
+        Identifier(Key2) [70-74]
+    OrderingExpression(ASC) [76-80]
+      PathExpression [76-80]
+        Identifier(Col1) [76-80]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#expression-index
+CREATE TABLE Venues (
+  Id INT64 NOT NULL,
+  VenueData JSON,
+  VenueCity STRING AS (JSON_VALUE(VenueData.address.city))
+) PRIMARY KEY (Id)
+--
+CreateTableStatement [0-138]
+  PathExpression [13-19]
+    Identifier(Venues) [13-19]
+  TableElementList [20-121]
+    ColumnDefinition [24-41]
+      Identifier(Id) [24-26]
+      SimpleColumnSchema [27-41]
+        PathExpression [27-32]
+          Identifier(INT64) [27-32]
+        ColumnAttributeList [33-41]
+          NotNullColumnAttribute [33-41]
+    ColumnDefinition [45-59]
+      Identifier(VenueData) [45-54]
+      SimpleColumnSchema [55-59]
+        PathExpression [55-59]
+          Identifier(JSON) [55-59]
+    ColumnDefinition [63-119]
+      Identifier(VenueCity) [63-72]
+      SimpleColumnSchema [73-119]
+        PathExpression [73-79]
+          Identifier(STRING) [73-79]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [80-119]
+          FunctionCall [84-118]
+            PathExpression [84-94]
+              Identifier(JSON_VALUE) [84-94]
+            PathExpression [95-117]
+              Identifier(VenueData) [95-104]
+              Identifier(address) [105-112]
+              Identifier(city) [113-117]
+  SpannerTableOptions [122-138]
+    PrimaryKey(ENFORCED) [122-138]
+      PrimaryKeyElementList [134-138]
+        PrimaryKeyElement(ASC) [135-137]
+          Identifier(Id) [135-137]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#expression-index
+CREATE INDEX VenuesByCity ON Venues(VenueCity)
+--
+CreateIndexStatement [0-46]
+  PathExpression [13-25]
+    Identifier(VenuesByCity) [13-25]
+  PathExpression [29-35]
+    Identifier(Venues) [29-35]
+  IndexItemList [35-46]
+    OrderingExpression(ASC) [36-45]
+      PathExpression [36-45]
+        Identifier(VenueCity) [36-45]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#expression-index
+SELECT Id
+FROM Venues
+WHERE JSON_VALUE(VenueData.address.city) = 'Seattle'
+--
+QueryStatement [0-74]
+  Query [0-74]
+    Select [0-74]
+      SelectList [7-9]
+        SelectColumn [7-9]
+          PathExpression [7-9]
+            Identifier(Id) [7-9]
+      FromClause [10-21]
+        TablePathExpression [15-21]
+          PathExpression [15-21]
+            Identifier(Venues) [15-21]
+      WhereClause [22-74]
+        BinaryExpression(=) [28-74]
+          FunctionCall [28-62]
+            PathExpression [28-38]
+              Identifier(JSON_VALUE) [28-38]
+            PathExpression [39-61]
+              Identifier(VenueData) [39-48]
+              Identifier(address) [49-56]
+              Identifier(city) [57-61]
+          StringLiteral [65-74]
+            StringLiteralComponent('Seattle') [65-74]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#expression-index
+CREATE TABLE Venues (
+  Id INT64 NOT NULL,
+  VenueName STRING(MAX),
+  VenueData JSON
+) PRIMARY KEY (Id)
+--
+CreateTableStatement [0-103]
+  PathExpression [13-19]
+    Identifier(Venues) [13-19]
+  TableElementList [20-86]
+    ColumnDefinition [24-41]
+      Identifier(Id) [24-26]
+      SimpleColumnSchema [27-41]
+        PathExpression [27-32]
+          Identifier(INT64) [27-32]
+        ColumnAttributeList [33-41]
+          NotNullColumnAttribute [33-41]
+    ColumnDefinition [45-66]
+      Identifier(VenueName) [45-54]
+      SimpleColumnSchema [55-66]
+        PathExpression [55-61]
+          Identifier(STRING) [55-61]
+        TypeParameterList [61-65]
+          MaxLiteral() [62-65]
+    ColumnDefinition [70-84]
+      Identifier(VenueData) [70-79]
+      SimpleColumnSchema [80-84]
+        PathExpression [80-84]
+          Identifier(JSON) [80-84]
+  SpannerTableOptions [87-103]
+    PrimaryKey(ENFORCED) [87-103]
+      PrimaryKeyElementList [99-103]
+        PrimaryKeyElement(ASC) [100-102]
+          Identifier(Id) [100-102]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#expression-index
+CREATE INDEX VenuesByCity ON Venues(
+  VenueName,
+  (JSON_VALUE(VenueData.address.city)),
+  (JSON_VALUE(VenueData.address.state))
+)
+--
+CreateIndexStatement [0-131]
+  PathExpression [13-25]
+    Identifier(VenuesByCity) [13-25]
+  PathExpression [29-35]
+    Identifier(Venues) [29-35]
+  IndexItemList [35-131]
+    OrderingExpression(ASC) [39-48]
+      PathExpression [39-48]
+        Identifier(VenueName) [39-48]
+    OrderingExpression(ASC) [52-88]
+      FunctionCall [53-87]
+        PathExpression [53-63]
+          Identifier(JSON_VALUE) [53-63]
+        PathExpression [64-86]
+          Identifier(VenueData) [64-73]
+          Identifier(address) [74-81]
+          Identifier(city) [82-86]
+    OrderingExpression(ASC) [92-129]
+      FunctionCall [93-128]
+        PathExpression [93-103]
+          Identifier(JSON_VALUE) [93-103]
+        PathExpression [104-127]
+          Identifier(VenueData) [104-113]
+          Identifier(address) [114-121]
+          Identifier(state) [122-127]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#drop-index
+DROP INDEX SingersByFirstLastName
+--
+DropStatement INDEX [0-33]
+  PathExpression [11-33]
+    Identifier(SingersByFirstLastName) [11-33]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#index-scanning
+SELECT SongId FROM Songs LIMIT 1
+--
+QueryStatement [0-32]
+  Query [0-32]
+    Select [0-24]
+      SelectList [7-13]
+        SelectColumn [7-13]
+          PathExpression [7-13]
+            Identifier(SongId) [7-13]
+      FromClause [14-24]
+        TablePathExpression [19-24]
+          PathExpression [19-24]
+            Identifier(Songs) [19-24]
+    LimitOffset [25-32]
+      Limit [25-32]
+        IntLiteral(1) [31-32]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#index-scanning
+SELECT SongId FROM Songs ORDER BY SongId DESC LIMIT 1
+--
+QueryStatement [0-53]
+  Query [0-53]
+    Select [0-24]
+      SelectList [7-13]
+        SelectColumn [7-13]
+          PathExpression [7-13]
+            Identifier(SongId) [7-13]
+      FromClause [14-24]
+        TablePathExpression [19-24]
+          PathExpression [19-24]
+            Identifier(Songs) [19-24]
+    OrderBy [25-45]
+      OrderingExpression(DESC) [34-45]
+        PathExpression [34-40]
+          Identifier(SongId) [34-40]
+    LimitOffset [46-53]
+      Limit [46-53]
+        IntLiteral(1) [52-53]
+==
+# https://docs.cloud.google.com/spanner/docs/secondary-indexes#index-scanning
+CREATE INDEX SongIdDesc On Songs(SongId DESC)
+--
+CreateIndexStatement [0-45]
+  PathExpression [13-23]
+    Identifier(SongIdDesc) [13-23]
+  PathExpression [27-32]
+    Identifier(Songs) [27-32]
+  IndexItemList [32-45]
+    OrderingExpression(DESC) [33-44]
+      PathExpression [33-39]
+        Identifier(SongId) [33-39]
diff --git a/parser/testdata/spanner/guides/sequence-tasks.test b/parser/testdata/spanner/guides/sequence-tasks.test
new file mode 100644
index 0000000..4989dd1
--- /dev/null
+++ b/parser/testdata/spanner/guides/sequence-tasks.test
@@ -0,0 +1,30 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/sequence-tasks
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/sequence-tasks#sequence-options
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE SEQUENCE IF NOT EXISTS customer_garage_seq_seed OPTIONS (
+    sequence_kind = 'bit_reversed_positive',
+    start_with_counter = 1000,
+    skip_range_min = 100,
+    skip_range_max = 200)
+--
+CreateSequenceStatement(is_if_not_exists) [0-192]
+  PathExpression [30-54]
+    Identifier(customer_garage_seq_seed) [30-54]
+  OptionsList [63-192]
+    OptionsEntry [69-108]
+      Identifier(sequence_kind) [69-82]
+      StringLiteral [85-108]
+        StringLiteralComponent('bit_reversed_positive') [85-108]
+    OptionsEntry [114-139]
+      Identifier(start_with_counter) [114-132]
+      IntLiteral(1000) [135-139]
+    OptionsEntry [145-165]
+      Identifier(skip_range_min) [145-159]
+      IntLiteral(100) [162-165]
+    OptionsEntry [171-191]
+      Identifier(skip_range_max) [171-185]
+      IntLiteral(200) [188-191]
diff --git a/parser/testdata/spanner/guides/set-default-time-zone.test b/parser/testdata/spanner/guides/set-default-time-zone.test
new file mode 100644
index 0000000..efbdf73
--- /dev/null
+++ b/parser/testdata/spanner/guides/set-default-time-zone.test
@@ -0,0 +1,97 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/set-default-time-zone
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/set-default-time-zone#default_time_zone_not_customized
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT TIMESTAMP("2072-12-25 15:30:00") AS timestamp_str
+--
+QueryStatement [0-56]
+  Query [0-56]
+    Select [0-56]
+      SelectList [7-56]
+        SelectColumn [7-56]
+          FunctionCall [7-39]
+            PathExpression [7-16]
+              Identifier(TIMESTAMP) [7-16]
+            StringLiteral [17-38]
+              StringLiteralComponent("2072-12-25 15:30:00") [17-38]
+          Alias [40-56]
+            Identifier(timestamp_str) [43-56]
+==
+# https://docs.cloud.google.com/spanner/docs/set-default-time-zone#default_time_zone_not_customized
+SELECT EXTRACT(HOUR FROM TIMESTAMP("2072-12-25 15:30:00Z")) AS hour
+--
+QueryStatement [0-67]
+  Query [0-67]
+    Select [0-67]
+      SelectList [7-67]
+        SelectColumn [7-67]
+          ExtractExpression [7-59]
+            PathExpression [15-19]
+              Identifier(HOUR) [15-19]
+            FunctionCall [25-58]
+              PathExpression [25-34]
+                Identifier(TIMESTAMP) [25-34]
+              StringLiteral [35-57]
+                StringLiteralComponent("2072-12-25 15:30:00Z") [35-57]
+          Alias [60-67]
+            Identifier(hour) [63-67]
+==
+# https://docs.cloud.google.com/spanner/docs/set-default-time-zone#default_time_zone_not_customized
+SELECT TIMESTAMP_TRUNC(TIMESTAMP "2072-12-25 15:30:00Z", DAY) AS date_str
+--
+QueryStatement [0-73]
+  Query [0-73]
+    Select [0-73]
+      SelectList [7-73]
+        SelectColumn [7-73]
+          FunctionCall [7-61]
+            PathExpression [7-22]
+              Identifier(TIMESTAMP_TRUNC) [7-22]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [23-55]
+              StringLiteral [33-55]
+                StringLiteralComponent("2072-12-25 15:30:00Z") [33-55]
+            PathExpression [57-60]
+              Identifier(DAY) [57-60]
+          Alias [62-73]
+            Identifier(date_str) [65-73]
+==
+# https://docs.cloud.google.com/spanner/docs/set-default-time-zone#default_time_zone_overridden_by_function_parameter
+SELECT FORMAT_TIMESTAMP("%c", TIMESTAMP "2050-12-25 15:30:55+00", "Australia/Sydney")
+AS formatted
+--
+QueryStatement [0-98]
+  Query [0-98]
+    Select [0-98]
+      SelectList [7-98]
+        SelectColumn [7-98]
+          FunctionCall [7-85]
+            PathExpression [7-23]
+              Identifier(FORMAT_TIMESTAMP) [7-23]
+            StringLiteral [24-28]
+              StringLiteralComponent("%c") [24-28]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [30-64]
+              StringLiteral [40-64]
+                StringLiteralComponent("2050-12-25 15:30:55+00") [40-64]
+            StringLiteral [66-84]
+              StringLiteralComponent("Australia/Sydney") [66-84]
+          Alias [86-98]
+            Identifier(formatted) [89-98]
+==
+# https://docs.cloud.google.com/spanner/docs/set-default-time-zone#default_time_zone_overridden_by_function_parameter
+SELECT TIMESTAMP("2072-12-25 15:30:00+11:00") AS timestamp_str
+--
+QueryStatement [0-62]
+  Query [0-62]
+    Select [0-62]
+      SelectList [7-62]
+        SelectColumn [7-62]
+          FunctionCall [7-45]
+            PathExpression [7-16]
+              Identifier(TIMESTAMP) [7-16]
+            StringLiteral [17-44]
+              StringLiteralComponent("2072-12-25 15:30:00+11:00") [17-44]
+          Alias [46-62]
+            Identifier(timestamp_str) [49-62]
diff --git a/parser/testdata/spanner/guides/sql-best-practices.test b/parser/testdata/spanner/guides/sql-best-practices.test
new file mode 100644
index 0000000..433cdcc
--- /dev/null
+++ b/parser/testdata/spanner/guides/sql-best-practices.test
@@ -0,0 +1,615 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/sql-best-practices
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/sql-best-practices#
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Singers (
+ SingerId   INT64 NOT NULL,
+ FirstName  STRING(1024),
+ LastName   STRING(1024),
+ SingerInfo BYTES(MAX),
+ BirthDate  DATE
+) PRIMARY KEY (SingerId)
+--
+CreateTableStatement [0-168]
+  PathExpression [13-20]
+    Identifier(Singers) [13-20]
+  TableElementList [21-145]
+    ColumnDefinition [24-49]
+      Identifier(SingerId) [24-32]
+      SimpleColumnSchema [35-49]
+        PathExpression [35-40]
+          Identifier(INT64) [35-40]
+        ColumnAttributeList [41-49]
+          NotNullColumnAttribute [41-49]
+    ColumnDefinition [52-75]
+      Identifier(FirstName) [52-61]
+      SimpleColumnSchema [63-75]
+        PathExpression [63-69]
+          Identifier(STRING) [63-69]
+        TypeParameterList [69-74]
+          IntLiteral(1024) [70-74]
+    ColumnDefinition [78-101]
+      Identifier(LastName) [78-86]
+      SimpleColumnSchema [89-101]
+        PathExpression [89-95]
+          Identifier(STRING) [89-95]
+        TypeParameterList [95-100]
+          IntLiteral(1024) [96-100]
+    ColumnDefinition [104-125]
+      Identifier(SingerInfo) [104-114]
+      SimpleColumnSchema [115-125]
+        PathExpression [115-120]
+          Identifier(BYTES) [115-120]
+        TypeParameterList [120-124]
+          MaxLiteral() [121-124]
+    ColumnDefinition [128-143]
+      Identifier(BirthDate) [128-137]
+      SimpleColumnSchema [139-143]
+        PathExpression [139-143]
+          Identifier(DATE) [139-143]
+  SpannerTableOptions [146-168]
+    PrimaryKey(ENFORCED) [146-168]
+      PrimaryKeyElementList [158-168]
+        PrimaryKeyElement(ASC) [159-167]
+          Identifier(SingerId) [159-167]
+==
+# https://docs.cloud.google.com/spanner/docs/sql-best-practices#
+CREATE TABLE Albums (
+ SingerId     INT64 NOT NULL,
+ AlbumId      INT64 NOT NULL,
+ AlbumTitle   STRING(MAX),
+ ReleaseDate  DATE
+) PRIMARY KEY (SingerId, AlbumId),
+INTERLEAVE IN PARENT Singers ON DELETE CASCADE
+--
+CreateTableStatement [0-209]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-129]
+    ColumnDefinition [23-50]
+      Identifier(SingerId) [23-31]
+      SimpleColumnSchema [36-50]
+        PathExpression [36-41]
+          Identifier(INT64) [36-41]
+        ColumnAttributeList [42-50]
+          NotNullColumnAttribute [42-50]
+    ColumnDefinition [53-80]
+      Identifier(AlbumId) [53-60]
+      SimpleColumnSchema [66-80]
+        PathExpression [66-71]
+          Identifier(INT64) [66-71]
+        ColumnAttributeList [72-80]
+          NotNullColumnAttribute [72-80]
+    ColumnDefinition [83-107]
+      Identifier(AlbumTitle) [83-93]
+      SimpleColumnSchema [96-107]
+        PathExpression [96-102]
+          Identifier(STRING) [96-102]
+        TypeParameterList [102-106]
+          MaxLiteral() [103-106]
+    ColumnDefinition [110-127]
+      Identifier(ReleaseDate) [110-121]
+      SimpleColumnSchema [123-127]
+        PathExpression [123-127]
+          Identifier(DATE) [123-127]
+  SpannerTableOptions [130-209]
+    PrimaryKey(ENFORCED) [130-161]
+      PrimaryKeyElementList [142-161]
+        PrimaryKeyElement(ASC) [143-151]
+          Identifier(SingerId) [143-151]
+        PrimaryKeyElement(ASC) [153-160]
+          Identifier(AlbumId) [153-160]
+    SpannerInterleaveClause [161-209]
+      PathExpression [184-191]
+        Identifier(Singers) [184-191]
+==
+# https://docs.cloud.google.com/spanner/docs/sql-best-practices#secondary-indexes
+SELECT s.SingerId
+FROM Singers AS s
+WHERE s.LastName = 'Smith'
+--
+QueryStatement [0-62]
+  Query [0-62]
+    Select [0-62]
+      SelectList [7-17]
+        SelectColumn [7-17]
+          PathExpression [7-17]
+            Identifier(s) [7-8]
+            Identifier(SingerId) [9-17]
+      FromClause [18-35]
+        TablePathExpression [23-35]
+          PathExpression [23-30]
+            Identifier(Singers) [23-30]
+          Alias [31-35]
+            Identifier(s) [34-35]
+      WhereClause [36-62]
+        BinaryExpression(=) [42-62]
+          PathExpression [42-52]
+            Identifier(s) [42-43]
+            Identifier(LastName) [44-52]
+          StringLiteral [55-62]
+            StringLiteralComponent('Smith') [55-62]
+==
+# https://docs.cloud.google.com/spanner/docs/sql-best-practices#secondary-indexes
+CREATE INDEX SingersByLastName ON Singers (LastName)
+--
+CreateIndexStatement [0-52]
+  PathExpression [13-30]
+    Identifier(SingersByLastName) [13-30]
+  PathExpression [34-41]
+    Identifier(Singers) [34-41]
+  IndexItemList [42-52]
+    OrderingExpression(ASC) [43-51]
+      PathExpression [43-51]
+        Identifier(LastName) [43-51]
+==
+# https://docs.cloud.google.com/spanner/docs/sql-best-practices#secondary-indexes
+SELECT s.SingerId
+FROM Singers@{FORCE_INDEX=SingersByLastName} AS s
+WHERE s.LastName = 'Smith'
+--
+QueryStatement [0-94]
+  Query [0-94]
+    Select [0-94]
+      SelectList [7-17]
+        SelectColumn [7-17]
+          PathExpression [7-17]
+            Identifier(s) [7-8]
+            Identifier(SingerId) [9-17]
+      FromClause [18-67]
+        TablePathExpression [23-67]
+          PathExpression [23-30]
+            Identifier(Singers) [23-30]
+          Hint [30-62]
+            HintEntry [32-61]
+              Identifier(FORCE_INDEX) [32-43]
+              PathExpression [44-61]
+                Identifier(SingersByLastName) [44-61]
+          Alias [63-67]
+            Identifier(s) [66-67]
+      WhereClause [68-94]
+        BinaryExpression(=) [74-94]
+          PathExpression [74-84]
+            Identifier(s) [74-75]
+            Identifier(LastName) [76-84]
+          StringLiteral [87-94]
+            StringLiteralComponent('Smith') [87-94]
+==
+# https://docs.cloud.google.com/spanner/docs/sql-best-practices#secondary-indexes
+SELECT s.SingerId, s.FirstName
+FROM Singers@{FORCE_INDEX=SingersByLastName} AS s
+WHERE s.LastName = 'Smith'
+--
+QueryStatement [0-107]
+  Query [0-107]
+    Select [0-107]
+      SelectList [7-30]
+        SelectColumn [7-17]
+          PathExpression [7-17]
+            Identifier(s) [7-8]
+            Identifier(SingerId) [9-17]
+        SelectColumn [19-30]
+          PathExpression [19-30]
+            Identifier(s) [19-20]
+            Identifier(FirstName) [21-30]
+      FromClause [31-80]
+        TablePathExpression [36-80]
+          PathExpression [36-43]
+            Identifier(Singers) [36-43]
+          Hint [43-75]
+            HintEntry [45-74]
+              Identifier(FORCE_INDEX) [45-56]
+              PathExpression [57-74]
+                Identifier(SingersByLastName) [57-74]
+          Alias [76-80]
+            Identifier(s) [79-80]
+      WhereClause [81-107]
+        BinaryExpression(=) [87-107]
+          PathExpression [87-97]
+            Identifier(s) [87-88]
+            Identifier(LastName) [89-97]
+          StringLiteral [100-107]
+            StringLiteralComponent('Smith') [100-107]
+==
+# https://docs.cloud.google.com/spanner/docs/sql-best-practices#secondary-indexes
+CREATE INDEX SingersByLastName ON Singers (LastName) STORING (FirstName)
+--
+CreateIndexStatement [0-72]
+  PathExpression [13-30]
+    Identifier(SingersByLastName) [13-30]
+  PathExpression [34-41]
+    Identifier(Singers) [34-41]
+  IndexItemList [42-52]
+    OrderingExpression(ASC) [43-51]
+      PathExpression [43-51]
+        Identifier(LastName) [43-51]
+  IndexStoringExpressionList [61-72]
+    PathExpression [62-71]
+      Identifier(FirstName) [62-71]
+==
+# https://docs.cloud.google.com/spanner/docs/sql-best-practices#secondary-indexes
+SELECT a.AlbumTitle, a.ReleaseDate
+FROM Albums AS a
+ORDER BY a.ReleaseDate, a.AlbumTitle DESC
+--
+QueryStatement [0-93]
+  Query [0-93]
+    Select [0-51]
+      SelectList [7-34]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(a) [7-8]
+            Identifier(AlbumTitle) [9-19]
+        SelectColumn [21-34]
+          PathExpression [21-34]
+            Identifier(a) [21-22]
+            Identifier(ReleaseDate) [23-34]
+      FromClause [35-51]
+        TablePathExpression [40-51]
+          PathExpression [40-46]
+            Identifier(Albums) [40-46]
+          Alias [47-51]
+            Identifier(a) [50-51]
+    OrderBy [52-93]
+      OrderingExpression(ASC) [61-74]
+        PathExpression [61-74]
+          Identifier(a) [61-62]
+          Identifier(ReleaseDate) [63-74]
+      OrderingExpression(DESC) [76-93]
+        PathExpression [76-88]
+          Identifier(a) [76-77]
+          Identifier(AlbumTitle) [78-88]
+==
+# https://docs.cloud.google.com/spanner/docs/sql-best-practices#secondary-indexes
+CREATE INDEX AlbumsByReleaseDateTitleDesc on Albums (ReleaseDate, AlbumTitle DESC)
+--
+CreateIndexStatement [0-82]
+  PathExpression [13-41]
+    Identifier(AlbumsByReleaseDateTitleDesc) [13-41]
+  PathExpression [45-51]
+    Identifier(Albums) [45-51]
+  IndexItemList [52-82]
+    OrderingExpression(ASC) [53-64]
+      PathExpression [53-64]
+        Identifier(ReleaseDate) [53-64]
+    OrderingExpression(DESC) [66-81]
+      PathExpression [66-76]
+        Identifier(AlbumTitle) [66-76]
+==
+# https://docs.cloud.google.com/spanner/docs/sql-best-practices#secondary-indexes
+SELECT a.AlbumTitle, a.ReleaseDate
+FROM Albums@{FORCE_INDEX=AlbumsByReleaseDateTitleDesc} AS a
+ORDER BY a.ReleaseDate, a.AlbumTitle DESC
+--
+QueryStatement [0-136]
+  Query [0-136]
+    Select [0-94]
+      SelectList [7-34]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(a) [7-8]
+            Identifier(AlbumTitle) [9-19]
+        SelectColumn [21-34]
+          PathExpression [21-34]
+            Identifier(a) [21-22]
+            Identifier(ReleaseDate) [23-34]
+      FromClause [35-94]
+        TablePathExpression [40-94]
+          PathExpression [40-46]
+            Identifier(Albums) [40-46]
+          Hint [46-89]
+            HintEntry [48-88]
+              Identifier(FORCE_INDEX) [48-59]
+              PathExpression [60-88]
+                Identifier(AlbumsByReleaseDateTitleDesc) [60-88]
+          Alias [90-94]
+            Identifier(a) [93-94]
+    OrderBy [95-136]
+      OrderingExpression(ASC) [104-117]
+        PathExpression [104-117]
+          Identifier(a) [104-105]
+          Identifier(ReleaseDate) [106-117]
+      OrderingExpression(DESC) [119-136]
+        PathExpression [119-131]
+          Identifier(a) [119-120]
+          Identifier(AlbumTitle) [121-131]
+==
+# https://docs.cloud.google.com/spanner/docs/sql-best-practices#optimize-range-lookups
+SELECT *
+FROM Table AS t
+WHERE t.Key IN UNNEST (@KeyList)
+--
+QueryStatement [0-57]
+  Query [0-57]
+    Select [0-57]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-24]
+        TablePathExpression [14-24]
+          PathExpression [14-19]
+            Identifier(Table) [14-19]
+          Alias [20-24]
+            Identifier(t) [23-24]
+      WhereClause [25-57]
+        InExpression(IN) [31-57]
+          PathExpression [31-36]
+            Identifier(t) [31-32]
+            Identifier(Key) [33-36]
+          Location [37-39]
+          UnnestExpression [40-57]
+            ExpressionWithOptAlias [48-56]
+              ParameterExpr [48-56]
+                Identifier(KeyList) [49-56]
+==
+# https://docs.cloud.google.com/spanner/docs/sql-best-practices#optimize-range-lookups
+SELECT *
+FROM Table AS t
+WHERE t.Key BETWEEN @min AND @max
+--
+QueryStatement [0-58]
+  Query [0-58]
+    Select [0-58]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-24]
+        TablePathExpression [14-24]
+          PathExpression [14-19]
+            Identifier(Table) [14-19]
+          Alias [20-24]
+            Identifier(t) [23-24]
+      WhereClause [25-58]
+        BetweenExpression(BETWEEN) [31-58]
+          PathExpression [31-36]
+            Identifier(t) [31-32]
+            Identifier(Key) [33-36]
+          Location [37-44]
+          ParameterExpr [45-49]
+            Identifier(min) [46-49]
+          ParameterExpr [54-58]
+            Identifier(max) [55-58]
+==
+# https://docs.cloud.google.com/spanner/docs/sql-best-practices#optimize_joins
+SELECT s.FirstName, a.ReleaseDate
+FROM Singers AS s JOIN Albums AS a ON s.SingerId = a.SingerId
+--
+QueryStatement [0-95]
+  Query [0-95]
+    Select [0-95]
+      SelectList [7-33]
+        SelectColumn [7-18]
+          PathExpression [7-18]
+            Identifier(s) [7-8]
+            Identifier(FirstName) [9-18]
+        SelectColumn [20-33]
+          PathExpression [20-33]
+            Identifier(a) [20-21]
+            Identifier(ReleaseDate) [22-33]
+      FromClause [34-95]
+        Join [39-95]
+          TablePathExpression [39-51]
+            PathExpression [39-46]
+              Identifier(Singers) [39-46]
+            Alias [47-51]
+              Identifier(s) [50-51]
+          Location [52-56]
+          TablePathExpression [57-68]
+            PathExpression [57-63]
+              Identifier(Albums) [57-63]
+            Alias [64-68]
+              Identifier(a) [67-68]
+          OnClause [69-95]
+            BinaryExpression(=) [72-95]
+              PathExpression [72-82]
+                Identifier(s) [72-73]
+                Identifier(SingerId) [74-82]
+              PathExpression [85-95]
+                Identifier(a) [85-86]
+                Identifier(SingerId) [87-95]
+==
+# https://docs.cloud.google.com/spanner/docs/sql-best-practices#optimize_joins
+SELECT *
+FROM Singers AS s JOIN@{FORCE_JOIN_ORDER=TRUE} Albums AS a
+ON s.SingerId = a.Singerid
+WHERE s.LastName LIKE '%x%' AND a.AlbumTitle LIKE '%love%'
+--
+QueryStatement [0-153]
+  Query [0-153]
+    Select [0-153]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-94]
+        Join [14-94]
+          TablePathExpression [14-26]
+            PathExpression [14-21]
+              Identifier(Singers) [14-21]
+            Alias [22-26]
+              Identifier(s) [25-26]
+          Hint [31-55]
+            HintEntry [33-54]
+              Identifier(FORCE_JOIN_ORDER) [33-49]
+              BooleanLiteral(TRUE) [50-54]
+          Location [27-31]
+          TablePathExpression [56-67]
+            PathExpression [56-62]
+              Identifier(Albums) [56-62]
+            Alias [63-67]
+              Identifier(a) [66-67]
+          OnClause [68-94]
+            BinaryExpression(=) [71-94]
+              PathExpression [71-81]
+                Identifier(s) [71-72]
+                Identifier(SingerId) [73-81]
+              PathExpression [84-94]
+                Identifier(a) [84-85]
+                Identifier(Singerid) [86-94]
+      WhereClause [95-153]
+        AndExpr [101-153]
+          BinaryExpression(LIKE) [101-122]
+            PathExpression [101-111]
+              Identifier(s) [101-102]
+              Identifier(LastName) [103-111]
+            StringLiteral [117-122]
+              StringLiteralComponent('%x%') [117-122]
+          BinaryExpression(LIKE) [127-153]
+            PathExpression [127-139]
+              Identifier(a) [127-128]
+              Identifier(AlbumTitle) [129-139]
+            StringLiteral [145-153]
+              StringLiteralComponent('%love%') [145-153]
+==
+# https://docs.cloud.google.com/spanner/docs/sql-best-practices#optimize_joins
+SELECT *
+FROM Singers s JOIN@{JOIN_METHOD=HASH_JOIN} Albums AS a
+ON a.SingerId = a.SingerId
+--
+QueryStatement [0-91]
+  Query [0-91]
+    Select [0-91]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-91]
+        Join [14-91]
+          TablePathExpression [14-23]
+            PathExpression [14-21]
+              Identifier(Singers) [14-21]
+            Alias [22-23]
+              Identifier(s) [22-23]
+          Hint [28-52]
+            HintEntry [30-51]
+              Identifier(JOIN_METHOD) [30-41]
+              PathExpression [42-51]
+                Identifier(HASH_JOIN) [42-51]
+          Location [24-28]
+          TablePathExpression [53-64]
+            PathExpression [53-59]
+              Identifier(Albums) [53-59]
+            Alias [60-64]
+              Identifier(a) [63-64]
+          OnClause [65-91]
+            BinaryExpression(=) [68-91]
+              PathExpression [68-78]
+                Identifier(a) [68-69]
+                Identifier(SingerId) [70-78]
+              PathExpression [81-91]
+                Identifier(a) [81-82]
+                Identifier(SingerId) [83-91]
+==
+# https://docs.cloud.google.com/spanner/docs/sql-best-practices#optimize-timestamp-predicate-pushdown
+@{allow_timestamp_predicate_pushdown=TRUE}
+SELECT s.SingerInfo
+FROM Singers s
+WHERE s.ModificationTime > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 12 HOUR)
+--
+HintedStatement [0-157]
+  Hint [0-42]
+    HintEntry [2-41]
+      Identifier(allow_timestamp_predicate_pushdown) [2-36]
+      BooleanLiteral(TRUE) [37-41]
+  QueryStatement [43-157]
+    Query [43-157]
+      Select [43-157]
+        SelectList [50-62]
+          SelectColumn [50-62]
+            PathExpression [50-62]
+              Identifier(s) [50-51]
+              Identifier(SingerInfo) [52-62]
+        FromClause [63-77]
+          TablePathExpression [68-77]
+            PathExpression [68-75]
+              Identifier(Singers) [68-75]
+            Alias [76-77]
+              Identifier(s) [76-77]
+        WhereClause [78-157]
+          BinaryExpression(>) [84-157]
+            PathExpression [84-102]
+              Identifier(s) [84-85]
+              Identifier(ModificationTime) [86-102]
+            FunctionCall [105-157]
+              PathExpression [105-118]
+                Identifier(TIMESTAMP_SUB) [105-118]
+              FunctionCall [119-138]
+                PathExpression [119-136]
+                  Identifier(`CURRENT_TIMESTAMP`) [119-136]
+              IntervalExpr [140-156]
+                IntLiteral(12) [149-151]
+                Identifier(HOUR) [152-156]
+==
+# https://docs.cloud.google.com/spanner/docs/sql-best-practices#use_order_by_to_ensure_the_ordering_of_your_sql_results
+SELECT * FROM Singers
+ORDER BY SingerId
+--
+QueryStatement [0-39]
+  Query [0-39]
+    Select [0-21]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-21]
+        TablePathExpression [14-21]
+          PathExpression [14-21]
+            Identifier(Singers) [14-21]
+    OrderBy [22-39]
+      OrderingExpression(ASC) [31-39]
+        PathExpression [31-39]
+          Identifier(SingerId) [31-39]
+==
+# https://docs.cloud.google.com/spanner/docs/sql-best-practices#param-like
+SELECT a.AlbumTitle FROM Albums a
+WHERE a.AlbumTitle LIKE @like_clause
+--
+QueryStatement [0-70]
+  Query [0-70]
+    Select [0-70]
+      SelectList [7-19]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(a) [7-8]
+            Identifier(AlbumTitle) [9-19]
+      FromClause [20-33]
+        TablePathExpression [25-33]
+          PathExpression [25-31]
+            Identifier(Albums) [25-31]
+          Alias [32-33]
+            Identifier(a) [32-33]
+      WhereClause [34-70]
+        BinaryExpression(LIKE) [40-70]
+          PathExpression [40-52]
+            Identifier(a) [40-41]
+            Identifier(AlbumTitle) [42-52]
+          ParameterExpr [58-70]
+            Identifier(like_clause) [59-70]
+==
+# https://docs.cloud.google.com/spanner/docs/sql-best-practices#param-like
+SELECT a.AlbumTitle FROM Albums a
+WHERE STARTS_WITH(a.AlbumTitle, @prefix)
+--
+QueryStatement [0-74]
+  Query [0-74]
+    Select [0-74]
+      SelectList [7-19]
+        SelectColumn [7-19]
+          PathExpression [7-19]
+            Identifier(a) [7-8]
+            Identifier(AlbumTitle) [9-19]
+      FromClause [20-33]
+        TablePathExpression [25-33]
+          PathExpression [25-31]
+            Identifier(Albums) [25-31]
+          Alias [32-33]
+            Identifier(a) [32-33]
+      WhereClause [34-74]
+        FunctionCall [40-74]
+          PathExpression [40-51]
+            Identifier(STARTS_WITH) [40-51]
+          PathExpression [52-64]
+            Identifier(a) [52-53]
+            Identifier(AlbumTitle) [54-64]
+          ParameterExpr [66-73]
+            Identifier(prefix) [67-73]
diff --git a/parser/testdata/spanner/guides/storing-numeric-data.test b/parser/testdata/spanner/guides/storing-numeric-data.test
new file mode 100644
index 0000000..4d48cba
--- /dev/null
+++ b/parser/testdata/spanner/guides/storing-numeric-data.test
@@ -0,0 +1,74 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/storing-numeric-data
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/storing-numeric-data#perform_approximate_aggregations_sorting_and_calculations
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT SUM(CAST(value AS FLOAT64)) FROM my_table
+--
+QueryStatement [0-48]
+  Query [0-48]
+    Select [0-48]
+      SelectList [7-34]
+        SelectColumn [7-34]
+          FunctionCall [7-34]
+            PathExpression [7-10]
+              Identifier(SUM) [7-10]
+            CastExpression [11-33]
+              PathExpression [16-21]
+                Identifier(value) [16-21]
+              SimpleType [25-32]
+                PathExpression [25-32]
+                  Identifier(FLOAT64) [25-32]
+      FromClause [35-48]
+        TablePathExpression [40-48]
+          PathExpression [40-48]
+            Identifier(my_table) [40-48]
+==
+# https://docs.cloud.google.com/spanner/docs/storing-numeric-data#perform_approximate_aggregations_sorting_and_calculations
+SELECT value FROM my_table ORDER BY CAST(value AS FLOAT64)
+--
+QueryStatement [0-58]
+  Query [0-58]
+    Select [0-26]
+      SelectList [7-12]
+        SelectColumn [7-12]
+          PathExpression [7-12]
+            Identifier(value) [7-12]
+      FromClause [13-26]
+        TablePathExpression [18-26]
+          PathExpression [18-26]
+            Identifier(my_table) [18-26]
+    OrderBy [27-58]
+      OrderingExpression(ASC) [36-58]
+        CastExpression [36-58]
+          PathExpression [41-46]
+            Identifier(value) [41-46]
+          SimpleType [50-57]
+            PathExpression [50-57]
+              Identifier(FLOAT64) [50-57]
+==
+# https://docs.cloud.google.com/spanner/docs/storing-numeric-data#perform_approximate_aggregations_sorting_and_calculations
+SELECT value FROM my_table WHERE CAST(value AS FLOAT64) > 100.0
+--
+QueryStatement [0-63]
+  Query [0-63]
+    Select [0-63]
+      SelectList [7-12]
+        SelectColumn [7-12]
+          PathExpression [7-12]
+            Identifier(value) [7-12]
+      FromClause [13-26]
+        TablePathExpression [18-26]
+          PathExpression [18-26]
+            Identifier(my_table) [18-26]
+      WhereClause [27-63]
+        BinaryExpression(>) [33-63]
+          CastExpression [33-55]
+            PathExpression [38-43]
+              Identifier(value) [38-43]
+            SimpleType [47-54]
+              PathExpression [47-54]
+                Identifier(FLOAT64) [47-54]
+          FloatLiteral(100.0) [58-63]
diff --git a/parser/testdata/spanner/guides/table-name-synonym.spanner.sql b/parser/testdata/spanner/guides/table-name-synonym.spanner.sql
new file mode 100644
index 0000000..f2bd0d3
--- /dev/null
+++ b/parser/testdata/spanner/guides/table-name-synonym.spanner.sql
@@ -0,0 +1,53 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/table-name-synonym
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/table-name-synonym#rename-add-synonym
+# id 7a6c31530ba7: table synonyms (table-name-synonym)
+# execute_query: INVALID_ARGUMENT: SYNONYM is not a supported nested object type [at 1:54]
+ALTER TABLE table_name RENAME TO new_table_name, ADD SYNONYM table_name;
+
+# https://docs.cloud.google.com/spanner/docs/table-name-synonym#rename-add-synonym
+# id b44f41011be9: table synonyms (table-name-synonym)
+# execute_query: INVALID_ARGUMENT: SYNONYM is not a supported nested object type [at 1:47]
+ALTER TABLE Singers RENAME TO SingersNew, ADD SYNONYM Singers;
+
+# https://docs.cloud.google.com/spanner/docs/table-name-synonym#rename-tables
+# id 560790b2e65e: RENAME TABLE with several renames (table-name-synonym)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ";" or end of input but got "," [at 1:29]
+RENAME TABLE Singers TO Temp, SingersNew TO Singers, Temp TO SingersNew;
+
+# https://docs.cloud.google.com/spanner/docs/table-name-synonym#add-synonym
+# id 92ddbe4350a8: table synonyms (table-name-synonym)
+# execute_query: INVALID_ARGUMENT: SYNONYM is not a supported nested object type [at 1:28]
+ALTER TABLE table_name ADD SYNONYM synonym;
+
+# https://docs.cloud.google.com/spanner/docs/table-name-synonym#add-synonym
+# id 815ade90f45a: table synonyms (table-name-synonym)
+# execute_query: INVALID_ARGUMENT: SYNONYM is not a supported nested object type [at 1:25]
+ALTER TABLE Singers ADD SYNONYM SingersTest;
+
+# https://docs.cloud.google.com/spanner/docs/table-name-synonym#create-synonym
+# id c388096005d9: table synonyms (table-name-synonym)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected "(" [at 5:15]
+# The table's name is Singers and the synonym is Artists.
+  CREATE TABLE Singers (
+      SingerId INT64 NOT NULL,
+      SingerName STRING(1024),
+      SYNONYM (Artists)
+  ) PRIMARY KEY (SingerId);
+
+# https://docs.cloud.google.com/spanner/docs/table-name-synonym#remove-synonym
+# id 47f072ed10b4: table synonyms (table-name-synonym)
+# execute_query: INVALID_ARGUMENT: SYNONYM is not a supported nested object type [at 1:29]
+ALTER TABLE table_name DROP SYNONYM synonym;
+
+# https://docs.cloud.google.com/spanner/docs/table-name-synonym#remove-synonym
+# id 116da4526916: table synonyms (table-name-synonym)
+# execute_query: INVALID_ARGUMENT: SYNONYM is not a supported nested object type [at 1:26]
+ALTER TABLE Singers DROP SYNONYM SingersTest;
diff --git a/parser/testdata/spanner/guides/table-name-synonym.test b/parser/testdata/spanner/guides/table-name-synonym.test
new file mode 100644
index 0000000..401853d
--- /dev/null
+++ b/parser/testdata/spanner/guides/table-name-synonym.test
@@ -0,0 +1,36 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/table-name-synonym
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/table-name-synonym#rename-table
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+ALTER TABLE table_name RENAME TO new_table_name
+--
+AlterTableStatement [0-47]
+  PathExpression [12-22]
+    Identifier(table_name) [12-22]
+  AlterActionList [23-47]
+    RenameToClause [23-47]
+      PathExpression [33-47]
+        Identifier(new_table_name) [33-47]
+==
+# https://docs.cloud.google.com/spanner/docs/table-name-synonym#rename-table
+RENAME TABLE table_name TO new_table_name
+--
+RenameStatement [0-41]
+  Identifier(TABLE) [7-12]
+  PathExpression [13-23]
+    Identifier(table_name) [13-23]
+  PathExpression [27-41]
+    Identifier(new_table_name) [27-41]
+==
+# https://docs.cloud.google.com/spanner/docs/table-name-synonym#rename-table
+RENAME TABLE Singers TO SingersNew
+--
+RenameStatement [0-34]
+  Identifier(TABLE) [7-12]
+  PathExpression [13-20]
+    Identifier(Singers) [13-20]
+  PathExpression [24-34]
+    Identifier(SingersNew) [24-34]
diff --git a/parser/testdata/spanner/guides/troubleshooting-performance-regressions.test b/parser/testdata/spanner/guides/troubleshooting-performance-regressions.test
new file mode 100644
index 0000000..ba2ae5e
--- /dev/null
+++ b/parser/testdata/spanner/guides/troubleshooting-performance-regressions.test
@@ -0,0 +1,50 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/troubleshooting-performance-regressions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/troubleshooting-performance-regressions#operators-table-scan
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT AlbumTitle FROM Albums WHERE STARTS_WITH(AlbumTitle, "Now")
+--
+QueryStatement [0-66]
+  Query [0-66]
+    Select [0-66]
+      SelectList [7-17]
+        SelectColumn [7-17]
+          PathExpression [7-17]
+            Identifier(AlbumTitle) [7-17]
+      FromClause [18-29]
+        TablePathExpression [23-29]
+          PathExpression [23-29]
+            Identifier(Albums) [23-29]
+      WhereClause [30-66]
+        FunctionCall [36-66]
+          PathExpression [36-47]
+            Identifier(STARTS_WITH) [36-47]
+          PathExpression [48-58]
+            Identifier(AlbumTitle) [48-58]
+          StringLiteral [60-65]
+            StringLiteralComponent("Now") [60-65]
+==
+# https://docs.cloud.google.com/spanner/docs/troubleshooting-performance-regressions#operators-cross-apply
+SELECT * FROM Albums WHERE STARTS_WITH(AlbumTitle, "Now")
+--
+QueryStatement [0-57]
+  Query [0-57]
+    Select [0-57]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-20]
+        TablePathExpression [14-20]
+          PathExpression [14-20]
+            Identifier(Albums) [14-20]
+      WhereClause [21-57]
+        FunctionCall [27-57]
+          PathExpression [27-38]
+            Identifier(STARTS_WITH) [27-38]
+          PathExpression [39-49]
+            Identifier(AlbumTitle) [39-49]
+          StringLiteral [51-56]
+            StringLiteralComponent("Now") [51-56]
diff --git a/parser/testdata/spanner/guides/ttl.monitoring-and-metrics.test b/parser/testdata/spanner/guides/ttl.monitoring-and-metrics.test
new file mode 100644
index 0000000..5e31718
--- /dev/null
+++ b/parser/testdata/spanner/guides/ttl.monitoring-and-metrics.test
@@ -0,0 +1,60 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/ttl/monitoring-and-metrics
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/ttl/monitoring-and-metrics#use-query
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT TABLE_NAME, UNDELETABLE_ROWS
+FROM SPANNER_SYS.ROW_DELETION_POLICIES
+WHERE UNDELETABLE_ROWS > 0
+--
+QueryStatement [0-101]
+  Query [0-101]
+    Select [0-101]
+      SelectList [7-35]
+        SelectColumn [7-17]
+          PathExpression [7-17]
+            Identifier(TABLE_NAME) [7-17]
+        SelectColumn [19-35]
+          PathExpression [19-35]
+            Identifier(UNDELETABLE_ROWS) [19-35]
+      FromClause [36-74]
+        TablePathExpression [41-74]
+          PathExpression [41-74]
+            Identifier(SPANNER_SYS) [41-52]
+            Identifier(ROW_DELETION_POLICIES) [53-74]
+      WhereClause [75-101]
+        BinaryExpression(>) [81-101]
+          PathExpression [81-97]
+            Identifier(UNDELETABLE_ROWS) [81-97]
+          IntLiteral(0) [100-101]
+==
+# https://docs.cloud.google.com/spanner/docs/ttl/monitoring-and-metrics#monitor_undeletable_rows
+SELECT TABLE_NAME, UNDELETABLE_ROWS, MIN_UNDELETABLE_TIMESTAMP
+FROM SPANNER_SYS.ROW_DELETION_POLICIES
+WHERE UNDELETABLE_ROWS > 0
+--
+QueryStatement [0-128]
+  Query [0-128]
+    Select [0-128]
+      SelectList [7-62]
+        SelectColumn [7-17]
+          PathExpression [7-17]
+            Identifier(TABLE_NAME) [7-17]
+        SelectColumn [19-35]
+          PathExpression [19-35]
+            Identifier(UNDELETABLE_ROWS) [19-35]
+        SelectColumn [37-62]
+          PathExpression [37-62]
+            Identifier(MIN_UNDELETABLE_TIMESTAMP) [37-62]
+      FromClause [63-101]
+        TablePathExpression [68-101]
+          PathExpression [68-101]
+            Identifier(SPANNER_SYS) [68-79]
+            Identifier(ROW_DELETION_POLICIES) [80-101]
+      WhereClause [102-128]
+        BinaryExpression(>) [108-128]
+          PathExpression [108-124]
+            Identifier(UNDELETABLE_ROWS) [108-124]
+          IntLiteral(0) [127-128]
diff --git a/parser/testdata/spanner/guides/ttl.working-with-ttl.spanner.sql b/parser/testdata/spanner/guides/ttl.working-with-ttl.spanner.sql
new file mode 100644
index 0000000..a577475
--- /dev/null
+++ b/parser/testdata/spanner/guides/ttl.working-with-ttl.spanner.sql
@@ -0,0 +1,53 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/ttl/working-with-ttl
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/ttl/working-with-ttl#ttl_on_tables_referenced_by_a_foreign_key
+# id 14c9930fca7c: row deletion policy (TTL) (ttl.working-with-ttl)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected keyword INTERLEAVE but got keyword ROW [at 5:1]
+CREATE TABLE Districts (
+  DistrictID INT64,
+  CreatedAt TIMESTAMP
+) PRIMARY KEY (DistrictID),
+ROW DELETION POLICY (OLDER_THAN(CreatedAt, INTERVAL 1 DAY));
+
+# https://docs.cloud.google.com/spanner/docs/ttl/working-with-ttl#ttl_on_tables_referenced_by_a_foreign_key
+# id 278715e1de63: row deletion policy (TTL) (ttl.working-with-ttl)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ";" or end of input but got "," [at 6:49]
+CREATE TABLE Customers (
+  DistrictID INT64,
+  CustomerID INT64,
+  CreatedAt TIMESTAMP
+) PRIMARY KEY (DistrictID, CustomerID),
+INTERLEAVE IN PARENT Districts ON DELETE CASCADE,
+ROW DELETION POLICY (OLDER_THAN(CreatedAt, INTERVAL 1 DAY));
+
+# https://docs.cloud.google.com/spanner/docs/ttl/working-with-ttl#ttl_on_generated_columns
+# id 9fabc8cd139c: row deletion policy (TTL) (ttl.working-with-ttl)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected keyword INTERLEAVE but got keyword ROW [at 9:1]
+CREATE TABLE Orders (
+  OrderId INT64 NOT NULL,
+  OrderStatus STRING(30) NOT NULL,
+  LastModifiedDate TIMESTAMP NOT NULL,
+  ExpiredDate TIMESTAMP AS (IF(OrderStatus = 'Cancelled',
+    TIMESTAMP_ADD(LastModifiedDate, INTERVAL 30 DAY),
+    TIMESTAMP_ADD(LastModifiedDate, INTERVAL 180 DAY))) STORED,
+) PRIMARY KEY(OrderId),
+ROW DELETION POLICY (OLDER_THAN(ExpiredDate, INTERVAL 0 DAY));
+
+# https://docs.cloud.google.com/spanner/docs/ttl/working-with-ttl#delete
+# id ebe181a95224: row deletion policy (TTL) (ttl.working-with-ttl)
+# execute_query: INVALID_ARGUMENT: DROP ROW DELETION POLICY clause is not supported. [at 2:6]
+ALTER TABLE MyTable
+DROP ROW DELETION POLICY;
+
+# https://docs.cloud.google.com/spanner/docs/ttl/working-with-ttl#modify
+# id 1de829b56fcf: row deletion policy (TTL) (ttl.working-with-ttl)
+# execute_query: INVALID_ARGUMENT: REPLACE ROW DELETION POLICY clause is not supported. [at 2:9]
+ALTER TABLE MyTable
+REPLACE ROW DELETION POLICY (OLDER_THAN(ModifiedAt, INTERVAL 7 DAY));
diff --git a/parser/testdata/spanner/guides/ttl.working-with-ttl.test b/parser/testdata/spanner/guides/ttl.working-with-ttl.test
new file mode 100644
index 0000000..890f346
--- /dev/null
+++ b/parser/testdata/spanner/guides/ttl.working-with-ttl.test
@@ -0,0 +1,240 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/ttl/working-with-ttl
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/ttl/working-with-ttl#verify_your_conditions
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT COUNT(*)
+  FROM CalculatedRoutes
+  WHERE TIMESTAMP_ADD(CreatedAt, INTERVAL 30 DAY) < CURRENT_TIMESTAMP()
+--
+QueryStatement [0-111]
+  Query [0-111]
+    Select [0-111]
+      SelectList [7-15]
+        SelectColumn [7-15]
+          FunctionCall [7-15]
+            PathExpression [7-12]
+              Identifier(COUNT) [7-12]
+            Star(*) [13-14]
+      FromClause [18-39]
+        TablePathExpression [23-39]
+          PathExpression [23-39]
+            Identifier(CalculatedRoutes) [23-39]
+      WhereClause [42-111]
+        BinaryExpression(<) [48-111]
+          FunctionCall [48-89]
+            PathExpression [48-61]
+              Identifier(TIMESTAMP_ADD) [48-61]
+            PathExpression [62-71]
+              Identifier(CreatedAt) [62-71]
+            IntervalExpr [73-88]
+              IntLiteral(30) [82-84]
+              Identifier(DAY) [85-88]
+          FunctionCall [92-111]
+            PathExpression [92-109]
+              Identifier(`CURRENT_TIMESTAMP`) [92-109]
+==
+# https://docs.cloud.google.com/spanner/docs/ttl/working-with-ttl#ttl_on_tables_referenced_by_a_foreign_key
+CREATE TABLE Districts (
+  DistrictID INT64
+) PRIMARY KEY (DistrictID)
+--
+CreateTableStatement [0-70]
+  PathExpression [13-22]
+    Identifier(Districts) [13-22]
+  TableElementList [23-45]
+    ColumnDefinition [27-43]
+      Identifier(DistrictID) [27-37]
+      SimpleColumnSchema [38-43]
+        PathExpression [38-43]
+          Identifier(INT64) [38-43]
+  SpannerTableOptions [46-70]
+    PrimaryKey(ENFORCED) [46-70]
+      PrimaryKeyElementList [58-70]
+        PrimaryKeyElement(ASC) [59-69]
+          Identifier(DistrictID) [59-69]
+==
+# https://docs.cloud.google.com/spanner/docs/ttl/working-with-ttl#ttl_on_tables_referenced_by_a_foreign_key
+CREATE TABLE Customers (
+  DistrictID INT64,
+  CustomerID INT64,
+  CreatedAt TIMESTAMP
+) PRIMARY KEY (DistrictID, CustomerID),
+INTERLEAVE IN PARENT Districts ON DELETE CASCADE
+--
+CreateTableStatement [0-175]
+  PathExpression [13-22]
+    Identifier(Customers) [13-22]
+  TableElementList [23-88]
+    ColumnDefinition [27-43]
+      Identifier(DistrictID) [27-37]
+      SimpleColumnSchema [38-43]
+        PathExpression [38-43]
+          Identifier(INT64) [38-43]
+    ColumnDefinition [47-63]
+      Identifier(CustomerID) [47-57]
+      SimpleColumnSchema [58-63]
+        PathExpression [58-63]
+          Identifier(INT64) [58-63]
+    ColumnDefinition [67-86]
+      Identifier(CreatedAt) [67-76]
+      SimpleColumnSchema [77-86]
+        PathExpression [77-86]
+          Identifier(TIMESTAMP) [77-86]
+  SpannerTableOptions [89-175]
+    PrimaryKey(ENFORCED) [89-125]
+      PrimaryKeyElementList [101-125]
+        PrimaryKeyElement(ASC) [102-112]
+          Identifier(DistrictID) [102-112]
+        PrimaryKeyElement(ASC) [114-124]
+          Identifier(CustomerID) [114-124]
+    SpannerInterleaveClause [125-175]
+      PathExpression [148-157]
+        Identifier(Districts) [148-157]
+==
+# https://docs.cloud.google.com/spanner/docs/ttl/working-with-ttl#ttl_on_tables_referenced_by_a_foreign_key
+CREATE TABLE Orders (
+  OrderID INT64,
+  DistrictID INT64,
+  CustomerID INT64,
+  CONSTRAINT FK_CustomerOrder FOREIGN KEY (DistrictID, CustomerID) REFERENCES Customers (DistrictID, CustomerID)
+) PRIMARY KEY (OrderID)
+--
+CreateTableStatement [0-215]
+  PathExpression [13-19]
+    Identifier(Orders) [13-19]
+  TableElementList [20-193]
+    ColumnDefinition [24-37]
+      Identifier(OrderID) [24-31]
+      SimpleColumnSchema [32-37]
+        PathExpression [32-37]
+          Identifier(INT64) [32-37]
+    ColumnDefinition [41-57]
+      Identifier(DistrictID) [41-51]
+      SimpleColumnSchema [52-57]
+        PathExpression [52-57]
+          Identifier(INT64) [52-57]
+    ColumnDefinition [61-77]
+      Identifier(CustomerID) [61-71]
+      SimpleColumnSchema [72-77]
+        PathExpression [72-77]
+          Identifier(INT64) [72-77]
+    ForeignKey [81-191]
+      ColumnList [121-145]
+        Identifier(DistrictID) [122-132]
+        Identifier(CustomerID) [134-144]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [146-191]
+        PathExpression [157-166]
+          Identifier(Customers) [157-166]
+        ColumnList [167-191]
+          Identifier(DistrictID) [168-178]
+          Identifier(CustomerID) [180-190]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE NO ACTION) [191-191]
+      Identifier(FK_CustomerOrder) [92-108]
+  SpannerTableOptions [194-215]
+    PrimaryKey(ENFORCED) [194-215]
+      PrimaryKeyElementList [206-215]
+        PrimaryKeyElement(ASC) [207-214]
+          Identifier(OrderID) [207-214]
+==
+# https://docs.cloud.google.com/spanner/docs/ttl/working-with-ttl#ttl_on_tables_referenced_by_a_foreign_key
+CREATE TABLE Orders (
+  OrderID INT64,
+  DistrictID INT64,
+  CustomerID INT64,
+  CONSTRAINT FK_CustomerOrder FOREIGN KEY (DistrictID, CustomerID) REFERENCES Customers (DistrictID, CustomerID) ON DELETE CASCADE
+) PRIMARY KEY (OrderID)
+--
+CreateTableStatement [0-233]
+  PathExpression [13-19]
+    Identifier(Orders) [13-19]
+  TableElementList [20-211]
+    ColumnDefinition [24-37]
+      Identifier(OrderID) [24-31]
+      SimpleColumnSchema [32-37]
+        PathExpression [32-37]
+          Identifier(INT64) [32-37]
+    ColumnDefinition [41-57]
+      Identifier(DistrictID) [41-51]
+      SimpleColumnSchema [52-57]
+        PathExpression [52-57]
+          Identifier(INT64) [52-57]
+    ColumnDefinition [61-77]
+      Identifier(CustomerID) [61-71]
+      SimpleColumnSchema [72-77]
+        PathExpression [72-77]
+          Identifier(INT64) [72-77]
+    ForeignKey [81-209]
+      ColumnList [121-145]
+        Identifier(DistrictID) [122-132]
+        Identifier(CustomerID) [134-144]
+      ForeignKeyReference(MATCH SIMPLE ENFORCED) [146-209]
+        PathExpression [157-166]
+          Identifier(Customers) [157-166]
+        ColumnList [167-191]
+          Identifier(DistrictID) [168-178]
+          Identifier(CustomerID) [180-190]
+        ForeignKeyActions(ON UPDATE NO ACTION ON DELETE CASCADE) [192-209]
+      Identifier(FK_CustomerOrder) [92-108]
+  SpannerTableOptions [212-233]
+    PrimaryKey(ENFORCED) [212-233]
+      PrimaryKeyElementList [224-233]
+        PrimaryKeyElement(ASC) [225-232]
+          Identifier(OrderID) [225-232]
+==
+# https://docs.cloud.google.com/spanner/docs/ttl/working-with-ttl#ttl_on_columns_with_default_values
+CREATE TABLE Customers (
+  CustomerID INT64,
+  CreatedAt TIMESTAMP DEFAULT (CURRENT_TIMESTAMP())
+) PRIMARY KEY (CustomerID)
+--
+CreateTableStatement [0-123]
+  PathExpression [13-22]
+    Identifier(Customers) [13-22]
+  TableElementList [23-98]
+    ColumnDefinition [27-43]
+      Identifier(CustomerID) [27-37]
+      SimpleColumnSchema [38-43]
+        PathExpression [38-43]
+          Identifier(INT64) [38-43]
+    ColumnDefinition [47-96]
+      Identifier(CreatedAt) [47-56]
+      SimpleColumnSchema [57-96]
+        PathExpression [57-66]
+          Identifier(TIMESTAMP) [57-66]
+        FunctionCall [76-95]
+          PathExpression [76-93]
+            Identifier(`CURRENT_TIMESTAMP`) [76-93]
+  SpannerTableOptions [99-123]
+    PrimaryKey(ENFORCED) [99-123]
+      PrimaryKeyElementList [111-123]
+        PrimaryKeyElement(ASC) [112-122]
+          Identifier(CustomerID) [112-122]
+==
+# https://docs.cloud.google.com/spanner/docs/ttl/working-with-ttl#view
+SELECT TABLE_NAME, ROW_DELETION_POLICY_EXPRESSION
+FROM INFORMATION_SCHEMA.TABLES
+WHERE ROW_DELETION_POLICY_EXPRESSION IS NOT NULL
+--
+QueryStatement [0-129]
+  Query [0-129]
+    Select [0-129]
+      SelectList [7-49]
+        SelectColumn [7-17]
+          PathExpression [7-17]
+            Identifier(TABLE_NAME) [7-17]
+        SelectColumn [19-49]
+          PathExpression [19-49]
+            Identifier(ROW_DELETION_POLICY_EXPRESSION) [19-49]
+      FromClause [50-80]
+        TablePathExpression [55-80]
+          PathExpression [55-80]
+            Identifier(INFORMATION_SCHEMA) [55-73]
+            Identifier(TABLES) [74-80]
+      WhereClause [81-129]
+        BinaryExpression(IS NOT) [87-129]
+          PathExpression [87-117]
+            Identifier(ROW_DELETION_POLICY_EXPRESSION) [87-117]
+          NullLiteral(NULL) [125-129]
diff --git a/parser/testdata/spanner/guides/use-pitr.spanner.sql b/parser/testdata/spanner/guides/use-pitr.spanner.sql
new file mode 100644
index 0000000..73f71d3
--- /dev/null
+++ b/parser/testdata/spanner/guides/use-pitr.spanner.sql
@@ -0,0 +1,15 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/use-pitr
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/use-pitr#set-period
+# id 4cb01623f288: database ids with dashes (use-pitr)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected "-" [at 1:23]
+ALTER DATABASE example-db SET OPTIONS (
+  version_retention_period = '7d'
+);
diff --git a/parser/testdata/spanner/guides/use-pitr.test b/parser/testdata/spanner/guides/use-pitr.test
new file mode 100644
index 0000000..1c6e97c
--- /dev/null
+++ b/parser/testdata/spanner/guides/use-pitr.test
@@ -0,0 +1,72 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/use-pitr
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/use-pitr#estimate-storage
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT
+  SUM(bytes_per_hour) / (1024 * 1024 * 1024 ) as GiB
+FROM (
+  SELECT
+    ((commit_attempt_count - commit_failed_precondition_count - commit_abort_count) * avg_bytes)
+    AS bytes_per_hour, interval_end
+  FROM
+    spanner_sys.txn_stats_total_hour
+  ORDER BY
+    interval_end DESC
+  LIMIT
+    168)
+--
+QueryStatement [0-302]
+  Query [0-302]
+    Select [0-302]
+      SelectList [9-59]
+        SelectColumn [9-59]
+          BinaryExpression(/) [9-52]
+            FunctionCall [9-28]
+              PathExpression [9-12]
+                Identifier(SUM) [9-12]
+              PathExpression [13-27]
+                Identifier(bytes_per_hour) [13-27]
+            BinaryExpression(*) [32-50]
+              BinaryExpression(*) [32-43]
+                IntLiteral(1024) [32-36]
+                IntLiteral(1024) [39-43]
+              IntLiteral(1024) [46-50]
+          Alias [53-59]
+            Identifier(GiB) [56-59]
+      FromClause [60-302]
+        TableSubquery [65-302]
+          Query [69-301]
+            Select [69-252]
+              SelectList [80-208]
+                SelectColumn [80-194]
+                  BinaryExpression(*) [81-171]
+                    BinaryExpression(-) [82-158]
+                      BinaryExpression(-) [82-137]
+                        PathExpression [82-102]
+                          Identifier(commit_attempt_count) [82-102]
+                        PathExpression [105-137]
+                          Identifier(commit_failed_precondition_count) [105-137]
+                      PathExpression [140-158]
+                        Identifier(commit_abort_count) [140-158]
+                    PathExpression [162-171]
+                      Identifier(avg_bytes) [162-171]
+                  Alias [177-194]
+                    Identifier(bytes_per_hour) [180-194]
+                SelectColumn [196-208]
+                  PathExpression [196-208]
+                    Identifier(interval_end) [196-208]
+              FromClause [211-252]
+                TablePathExpression [220-252]
+                  PathExpression [220-252]
+                    Identifier(spanner_sys) [220-231]
+                    Identifier(txn_stats_total_hour) [232-252]
+            OrderBy [255-285]
+              OrderingExpression(DESC) [268-285]
+                PathExpression [268-280]
+                  Identifier(interval_end) [268-280]
+            LimitOffset [288-301]
+              Limit [288-301]
+                IntLiteral(168) [298-301]
diff --git a/parser/testdata/spanner/guides/use-select-for-update-repeatable-read.test b/parser/testdata/spanner/guides/use-select-for-update-repeatable-read.test
new file mode 100644
index 0000000..ad6799b
--- /dev/null
+++ b/parser/testdata/spanner/guides/use-select-for-update-repeatable-read.test
@@ -0,0 +1,223 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/use-select-for-update-repeatable-read
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/use-select-for-update-repeatable-read#query-syntax
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT SingerId, SingerInfo
+FROM Singers WHERE SingerID = 5
+FOR UPDATE
+--
+QueryStatement [0-70]
+  Query [0-70]
+    Select [0-59]
+      SelectList [7-27]
+        SelectColumn [7-15]
+          PathExpression [7-15]
+            Identifier(SingerId) [7-15]
+        SelectColumn [17-27]
+          PathExpression [17-27]
+            Identifier(SingerInfo) [17-27]
+      FromClause [28-40]
+        TablePathExpression [33-40]
+          PathExpression [33-40]
+            Identifier(Singers) [33-40]
+      WhereClause [41-59]
+        BinaryExpression(=) [47-59]
+          PathExpression [47-55]
+            Identifier(SingerID) [47-55]
+          IntLiteral(5) [58-59]
+    LockMode [60-70]
+==
+# https://docs.cloud.google.com/spanner/docs/use-select-for-update-repeatable-read#with-statements
+WITH s AS (SELECT SingerId, SingerInfo FROM Singers WHERE SingerID > 5)
+SELECT * FROM s
+FOR UPDATE
+--
+QueryStatement [0-98]
+  Query [0-98]
+    WithClause [0-71]
+      WithClauseEntry [5-71]
+        AliasedQuery [5-71]
+          Identifier(s) [5-6]
+          Query [10-71]
+            Select [11-70]
+              SelectList [18-38]
+                SelectColumn [18-26]
+                  PathExpression [18-26]
+                    Identifier(SingerId) [18-26]
+                SelectColumn [28-38]
+                  PathExpression [28-38]
+                    Identifier(SingerInfo) [28-38]
+              FromClause [39-51]
+                TablePathExpression [44-51]
+                  PathExpression [44-51]
+                    Identifier(Singers) [44-51]
+              WhereClause [52-70]
+                BinaryExpression(>) [58-70]
+                  PathExpression [58-66]
+                    Identifier(SingerID) [58-66]
+                  IntLiteral(5) [69-70]
+    Select [72-87]
+      SelectList [79-80]
+        SelectColumn [79-80]
+          Star(*) [79-80]
+      FromClause [81-87]
+        TablePathExpression [86-87]
+          PathExpression [86-87]
+            Identifier(s) [86-87]
+    LockMode [88-98]
+==
+# https://docs.cloud.google.com/spanner/docs/use-select-for-update-repeatable-read#with-statements
+WITH s AS
+  (SELECT SingerId, SingerInfo FROM Singers WHERE SingerId > 5 FOR UPDATE)
+SELECT * FROM s
+--
+QueryStatement [0-100]
+  Query [0-100]
+    WithClause [0-84]
+      WithClauseEntry [5-84]
+        AliasedQuery [5-84]
+          Identifier(s) [5-6]
+          Query [12-84]
+            Select [13-72]
+              SelectList [20-40]
+                SelectColumn [20-28]
+                  PathExpression [20-28]
+                    Identifier(SingerId) [20-28]
+                SelectColumn [30-40]
+                  PathExpression [30-40]
+                    Identifier(SingerInfo) [30-40]
+              FromClause [41-53]
+                TablePathExpression [46-53]
+                  PathExpression [46-53]
+                    Identifier(Singers) [46-53]
+              WhereClause [54-72]
+                BinaryExpression(>) [60-72]
+                  PathExpression [60-68]
+                    Identifier(SingerId) [60-68]
+                  IntLiteral(5) [71-72]
+            LockMode [73-83]
+    Select [85-100]
+      SelectList [92-93]
+        SelectColumn [92-93]
+          Star(*) [92-93]
+      FromClause [94-100]
+        TablePathExpression [99-100]
+          PathExpression [99-100]
+            Identifier(s) [99-100]
+==
+# https://docs.cloud.google.com/spanner/docs/use-select-for-update-repeatable-read#subqueries
+(SELECT SingerId, SingerInfo FROM Singers WHERE SingerId > 5) AS t
+FOR UPDATE
+--
+QueryStatement [0-77]
+  Query [0-77]
+    AliasedQueryExpression [0-66]
+      Query [1-60]
+        Select [1-60]
+          SelectList [8-28]
+            SelectColumn [8-16]
+              PathExpression [8-16]
+                Identifier(SingerId) [8-16]
+            SelectColumn [18-28]
+              PathExpression [18-28]
+                Identifier(SingerInfo) [18-28]
+          FromClause [29-41]
+            TablePathExpression [34-41]
+              PathExpression [34-41]
+                Identifier(Singers) [34-41]
+          WhereClause [42-60]
+            BinaryExpression(>) [48-60]
+              PathExpression [48-56]
+                Identifier(SingerId) [48-56]
+              IntLiteral(5) [59-60]
+      Alias [62-66]
+        Identifier(t) [65-66]
+    LockMode [67-77]
+==
+# https://docs.cloud.google.com/spanner/docs/use-select-for-update-repeatable-read#subqueries
+SELECT SingerId, SingerInfo
+FROM Singers
+WHERE SingerId = (SELECT SingerId FROM Albums WHERE MarketingBudget > 100000)
+FOR UPDATE
+--
+QueryStatement [0-129]
+  Query [0-129]
+    Select [0-118]
+      SelectList [7-27]
+        SelectColumn [7-15]
+          PathExpression [7-15]
+            Identifier(SingerId) [7-15]
+        SelectColumn [17-27]
+          PathExpression [17-27]
+            Identifier(SingerInfo) [17-27]
+      FromClause [28-40]
+        TablePathExpression [33-40]
+          PathExpression [33-40]
+            Identifier(Singers) [33-40]
+      WhereClause [41-118]
+        BinaryExpression(=) [47-118]
+          PathExpression [47-55]
+            Identifier(SingerId) [47-55]
+          ExpressionSubquery [58-118]
+            Query [59-117]
+              Select [59-117]
+                SelectList [66-74]
+                  SelectColumn [66-74]
+                    PathExpression [66-74]
+                      Identifier(SingerId) [66-74]
+                FromClause [75-86]
+                  TablePathExpression [80-86]
+                    PathExpression [80-86]
+                      Identifier(Albums) [80-86]
+                WhereClause [87-117]
+                  BinaryExpression(>) [93-117]
+                    PathExpression [93-108]
+                      Identifier(MarketingBudget) [93-108]
+                    IntLiteral(100000) [111-117]
+    LockMode [119-129]
+==
+# https://docs.cloud.google.com/spanner/docs/use-select-for-update-repeatable-read#query-views
+CREATE VIEW SingerBio AS SELECT SingerId, FullName, SingerInfo FROM Singers
+--
+CreateViewStatement [0-75]
+  PathExpression [12-21]
+    Identifier(SingerBio) [12-21]
+  Query [25-75]
+    Select [25-75]
+      SelectList [32-62]
+        SelectColumn [32-40]
+          PathExpression [32-40]
+            Identifier(SingerId) [32-40]
+        SelectColumn [42-50]
+          PathExpression [42-50]
+            Identifier(FullName) [42-50]
+        SelectColumn [52-62]
+          PathExpression [52-62]
+            Identifier(SingerInfo) [52-62]
+      FromClause [63-75]
+        TablePathExpression [68-75]
+          PathExpression [68-75]
+            Identifier(Singers) [68-75]
+==
+# https://docs.cloud.google.com/spanner/docs/use-select-for-update-repeatable-read#query-views
+SELECT * FROM SingerBio WHERE SingerId = 5 FOR UPDATE
+--
+QueryStatement [0-53]
+  Query [0-53]
+    Select [0-42]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-23]
+        TablePathExpression [14-23]
+          PathExpression [14-23]
+            Identifier(SingerBio) [14-23]
+      WhereClause [24-42]
+        BinaryExpression(=) [30-42]
+          PathExpression [30-38]
+            Identifier(SingerId) [30-38]
+          IntLiteral(5) [41-42]
+    LockMode [43-53]
diff --git a/parser/testdata/spanner/guides/use-select-for-update-serializable.test b/parser/testdata/spanner/guides/use-select-for-update-serializable.test
new file mode 100644
index 0000000..e8415e1
--- /dev/null
+++ b/parser/testdata/spanner/guides/use-select-for-update-serializable.test
@@ -0,0 +1,284 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/use-select-for-update-serializable
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/use-select-for-update-serializable#comparison
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+@{lock_scanned_ranges=exclusive}
+SELECT s.SingerId, s.FullName FROM Singers AS s
+JOIN (SELECT SingerId FROM Albums WHERE MarketingBudget > 100000)
+AS a ON a.SingerId = s.SingerId
+--
+HintedStatement [0-178]
+  Hint [0-32]
+    HintEntry [2-31]
+      Identifier(lock_scanned_ranges) [2-21]
+      PathExpression [22-31]
+        Identifier(exclusive) [22-31]
+  QueryStatement [33-178]
+    Query [33-178]
+      Select [33-178]
+        SelectList [40-62]
+          SelectColumn [40-50]
+            PathExpression [40-50]
+              Identifier(s) [40-41]
+              Identifier(SingerId) [42-50]
+          SelectColumn [52-62]
+            PathExpression [52-62]
+              Identifier(s) [52-53]
+              Identifier(FullName) [54-62]
+        FromClause [63-178]
+          Join [68-178]
+            TablePathExpression [68-80]
+              PathExpression [68-75]
+                Identifier(Singers) [68-75]
+              Alias [76-80]
+                Identifier(s) [79-80]
+            Location [81-85]
+            TableSubquery [86-151]
+              Query [87-145]
+                Select [87-145]
+                  SelectList [94-102]
+                    SelectColumn [94-102]
+                      PathExpression [94-102]
+                        Identifier(SingerId) [94-102]
+                  FromClause [103-114]
+                    TablePathExpression [108-114]
+                      PathExpression [108-114]
+                        Identifier(Albums) [108-114]
+                  WhereClause [115-145]
+                    BinaryExpression(>) [121-145]
+                      PathExpression [121-136]
+                        Identifier(MarketingBudget) [121-136]
+                      IntLiteral(100000) [139-145]
+              Alias [147-151]
+                Identifier(a) [150-151]
+            OnClause [152-178]
+              BinaryExpression(=) [155-178]
+                PathExpression [155-165]
+                  Identifier(a) [155-156]
+                  Identifier(SingerId) [157-165]
+                PathExpression [168-178]
+                  Identifier(s) [168-169]
+                  Identifier(SingerId) [170-178]
+==
+# https://docs.cloud.google.com/spanner/docs/use-select-for-update-serializable#reading-locked-data
+-- Transaction 1
+SELECT MarketingBudget
+FROM Albums
+WHERE SingerId = 1 and AlbumId >= 1 and AlbumId < 5
+FOR UPDATE
+--
+QueryStatement [17-114]
+  Query [17-114]
+    Select [17-103]
+      SelectList [24-39]
+        SelectColumn [24-39]
+          PathExpression [24-39]
+            Identifier(MarketingBudget) [24-39]
+      FromClause [40-51]
+        TablePathExpression [45-51]
+          PathExpression [45-51]
+            Identifier(Albums) [45-51]
+      WhereClause [52-103]
+        AndExpr [58-103]
+          BinaryExpression(=) [58-70]
+            PathExpression [58-66]
+              Identifier(SingerId) [58-66]
+            IntLiteral(1) [69-70]
+          BinaryExpression(>=) [75-87]
+            PathExpression [75-82]
+              Identifier(AlbumId) [75-82]
+            IntLiteral(1) [86-87]
+          BinaryExpression(<) [92-103]
+            PathExpression [92-99]
+              Identifier(AlbumId) [92-99]
+            IntLiteral(5) [102-103]
+    LockMode [104-114]
+==
+# https://docs.cloud.google.com/spanner/docs/use-select-for-update-serializable#reading-locked-data
+-- Transaction 2
+SELECT MarketingBudget
+FROM Albums
+WHERE SingerId = 1 and AlbumId = 1
+--
+QueryStatement [17-86]
+  Query [17-86]
+    Select [17-86]
+      SelectList [24-39]
+        SelectColumn [24-39]
+          PathExpression [24-39]
+            Identifier(MarketingBudget) [24-39]
+      FromClause [40-51]
+        TablePathExpression [45-51]
+          PathExpression [45-51]
+            Identifier(Albums) [45-51]
+      WhereClause [52-86]
+        AndExpr [58-86]
+          BinaryExpression(=) [58-70]
+            PathExpression [58-66]
+              Identifier(SingerId) [58-66]
+            IntLiteral(1) [69-70]
+          BinaryExpression(=) [75-86]
+            PathExpression [75-82]
+              Identifier(AlbumId) [75-82]
+            IntLiteral(1) [85-86]
+==
+# https://docs.cloud.google.com/spanner/docs/use-select-for-update-serializable#reading-locked-data
+-- Transaction 3
+SELECT MarketingBudget
+FROM Albums
+WHERE SingerId = 1 and AlbumId >= 3 and AlbumId < 10
+FOR UPDATE
+--
+QueryStatement [17-115]
+  Query [17-115]
+    Select [17-104]
+      SelectList [24-39]
+        SelectColumn [24-39]
+          PathExpression [24-39]
+            Identifier(MarketingBudget) [24-39]
+      FromClause [40-51]
+        TablePathExpression [45-51]
+          PathExpression [45-51]
+            Identifier(Albums) [45-51]
+      WhereClause [52-104]
+        AndExpr [58-104]
+          BinaryExpression(=) [58-70]
+            PathExpression [58-66]
+              Identifier(SingerId) [58-66]
+            IntLiteral(1) [69-70]
+          BinaryExpression(>=) [75-87]
+            PathExpression [75-82]
+              Identifier(AlbumId) [75-82]
+            IntLiteral(3) [86-87]
+          BinaryExpression(<) [92-104]
+            PathExpression [92-99]
+              Identifier(AlbumId) [92-99]
+            IntLiteral(10) [102-104]
+    LockMode [105-115]
+==
+# https://docs.cloud.google.com/spanner/docs/use-select-for-update-serializable#read-index
+-- Transaction 1
+SELECT SingerId, SingerInfo
+FROM Singers
+WHERE SingerId = 1
+FOR UPDATE
+--
+QueryStatement [17-87]
+  Query [17-87]
+    Select [17-76]
+      SelectList [24-44]
+        SelectColumn [24-32]
+          PathExpression [24-32]
+            Identifier(SingerId) [24-32]
+        SelectColumn [34-44]
+          PathExpression [34-44]
+            Identifier(SingerInfo) [34-44]
+      FromClause [45-57]
+        TablePathExpression [50-57]
+          PathExpression [50-57]
+            Identifier(Singers) [50-57]
+      WhereClause [58-76]
+        BinaryExpression(=) [64-76]
+          PathExpression [64-72]
+            Identifier(SingerId) [64-72]
+          IntLiteral(1) [75-76]
+    LockMode [77-87]
+==
+# https://docs.cloud.google.com/spanner/docs/use-select-for-update-serializable#read-index
+-- Transaction 2
+SELECT SingerId FROM Singers
+--
+QueryStatement [17-45]
+  Query [17-45]
+    Select [17-45]
+      SelectList [24-32]
+        SelectColumn [24-32]
+          PathExpression [24-32]
+            Identifier(SingerId) [24-32]
+      FromClause [33-45]
+        TablePathExpression [38-45]
+          PathExpression [38-45]
+            Identifier(Singers) [38-45]
+==
+# https://docs.cloud.google.com/spanner/docs/use-select-for-update-serializable#already-locked-data
+-- Transaction 2
+UPDATE Albums
+SET MarketingBudget = 200000
+WHERE SingerId = 1 and AlbumId = 1
+--
+UpdateStatement [17-94]
+  PathExpression [24-30]
+    Identifier(Albums) [24-30]
+  UpdateItemList [35-59]
+    UpdateItem [35-59]
+      UpdateSetValue [35-59]
+        PathExpression [35-50]
+          Identifier(MarketingBudget) [35-50]
+        IntLiteral(200000) [53-59]
+  AndExpr [66-94]
+    BinaryExpression(=) [66-78]
+      PathExpression [66-74]
+        Identifier(SingerId) [66-74]
+      IntLiteral(1) [77-78]
+    BinaryExpression(=) [83-94]
+      PathExpression [83-90]
+        Identifier(AlbumId) [83-90]
+      IntLiteral(1) [93-94]
+==
+# https://docs.cloud.google.com/spanner/docs/use-select-for-update-serializable#existing-rows
+-- Transaction 1
+SELECT MarketingBudget
+FROM Albums
+WHERE SingerId = 1 and AlbumId >= 1 and AlbumId < 10
+FOR UPDATE
+--
+QueryStatement [17-115]
+  Query [17-115]
+    Select [17-104]
+      SelectList [24-39]
+        SelectColumn [24-39]
+          PathExpression [24-39]
+            Identifier(MarketingBudget) [24-39]
+      FromClause [40-51]
+        TablePathExpression [45-51]
+          PathExpression [45-51]
+            Identifier(Albums) [45-51]
+      WhereClause [52-104]
+        AndExpr [58-104]
+          BinaryExpression(=) [58-70]
+            PathExpression [58-66]
+              Identifier(SingerId) [58-66]
+            IntLiteral(1) [69-70]
+          BinaryExpression(>=) [75-87]
+            PathExpression [75-82]
+              Identifier(AlbumId) [75-82]
+            IntLiteral(1) [86-87]
+          BinaryExpression(<) [92-104]
+            PathExpression [92-99]
+              Identifier(AlbumId) [92-99]
+            IntLiteral(10) [102-104]
+    LockMode [105-115]
+==
+# https://docs.cloud.google.com/spanner/docs/use-select-for-update-serializable#existing-rows
+-- Transaction 2
+INSERT INTO Albums (SingerId, AlbumId, AlbumTitle, MarketingBudget)
+VALUES (1, 9, "Hello hello!", 10000)
+--
+InsertStatement [17-121]
+  PathExpression [29-35]
+    Identifier(Albums) [29-35]
+  ColumnList [36-84]
+    Identifier(SingerId) [37-45]
+    Identifier(AlbumId) [47-54]
+    Identifier(AlbumTitle) [56-66]
+    Identifier(MarketingBudget) [68-83]
+  InsertValuesRowList [85-121]
+    InsertValuesRow [92-121]
+      IntLiteral(1) [93-94]
+      IntLiteral(9) [96-97]
+      StringLiteral [99-113]
+        StringLiteralComponent("Hello hello!") [99-113]
+      IntLiteral(10000) [115-120]
diff --git a/parser/testdata/spanner/guides/vector-index-best-practices.metadata.json b/parser/testdata/spanner/guides/vector-index-best-practices.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/vector-index-best-practices.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/vector-index-best-practices.test b/parser/testdata/spanner/guides/vector-index-best-practices.test
new file mode 100644
index 0000000..5d67dc6
--- /dev/null
+++ b/parser/testdata/spanner/guides/vector-index-best-practices.test
@@ -0,0 +1,68 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/vector-index-best-practices
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/vector-index-best-practices#determine-vector-search-option-values
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT
+  opt.option_name,
+  opt.option_type,
+  opt.option_value
+FROM
+  INFORMATION_SCHEMA.INDEX_OPTIONS AS opt
+WHERE
+  opt.index_name = @vector_index_name
+--
+QueryStatement [0-154]
+  Query [0-154]
+    Select [0-154]
+      SelectList [9-63]
+        SelectColumn [9-24]
+          PathExpression [9-24]
+            Identifier(opt) [9-12]
+            Identifier(option_name) [13-24]
+        SelectColumn [28-43]
+          PathExpression [28-43]
+            Identifier(opt) [28-31]
+            Identifier(option_type) [32-43]
+        SelectColumn [47-63]
+          PathExpression [47-63]
+            Identifier(opt) [47-50]
+            Identifier(option_value) [51-63]
+      FromClause [64-110]
+        TablePathExpression [71-110]
+          PathExpression [71-103]
+            Identifier(INFORMATION_SCHEMA) [71-89]
+            Identifier(INDEX_OPTIONS) [90-103]
+          Alias [104-110]
+            Identifier(opt) [107-110]
+      WhereClause [111-154]
+        BinaryExpression(=) [119-154]
+          PathExpression [119-133]
+            Identifier(opt) [119-122]
+            Identifier(index_name) [123-133]
+          ParameterExpr [136-154]
+            Identifier(vector_index_name) [137-154]
+==
+# https://docs.cloud.google.com/spanner/docs/vector-index-best-practices#rebuild
+ALTER VECTOR INDEX IncidentVectorIndex REBUILD
+--
+AlterIndexStatement [0-46]
+  PathExpression [19-38]
+    Identifier(IncidentVectorIndex) [19-38]
+  AlterActionList [39-46]
+    RebuildAction [39-46]
+==
+# https://docs.cloud.google.com/spanner/docs/vector-index-best-practices#rebuild
+ALTER VECTOR INDEX IncidentVectorIndex SET OPTIONS (disable_search=true)
+--
+AlterIndexStatement [0-72]
+  PathExpression [19-38]
+    Identifier(IncidentVectorIndex) [19-38]
+  AlterActionList [39-72]
+    SetOptionsAction [39-72]
+      OptionsList [51-72]
+        OptionsEntry [52-71]
+          Identifier(disable_search) [52-66]
+          BooleanLiteral(true) [67-71]
diff --git a/parser/testdata/spanner/guides/vector-indexes.spanner.sql b/parser/testdata/spanner/guides/vector-indexes.spanner.sql
new file mode 100644
index 0000000..6a5e09c
--- /dev/null
+++ b/parser/testdata/spanner/guides/vector-indexes.spanner.sql
@@ -0,0 +1,44 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/vector-indexes
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/vector-indexes#create-vector-index
+# id 132bb7f629e6: ARRAY(vector_length=>N) (vector-indexes)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected identifier "vector_length" [at 6:31]
+CREATE TABLE Documents (
+  UserId INT64 NOT NULL,
+  DocId INT64 NOT NULL,
+  Author STRING (1024),
+  DocContents Bytes(MAX),
+  DocEmbedding ARRAY(vector_length=>128) NOT NULL,
+  NullableDocEmbedding ARRAY(vector_length=>128),
+  WordCount INT64
+) PRIMARY KEY (DocId);
+
+# https://docs.cloud.google.com/spanner/docs/vector-indexes#create-vector-index
+# id c2a8b55f1e16: vector index clauses (vector-indexes)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ";" or end of input but got keyword WHERE [at 4:3]
+CREATE VECTOR INDEX DocEmbeddingThreeLevelIndex
+  ON Documents(NullableDocEmbedding)
+  STORING (WordCount)
+  WHERE NullableDocEmbedding IS NOT NULL
+  OPTIONS (distance_type = 'COSINE');
+
+# https://docs.cloud.google.com/spanner/docs/vector-indexes#filter-vector-index
+# id e2f603537017: ARRAY(vector_length=>N) (vector-indexes)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected identifier "vector_length" [at 9:31]
+CREATE TABLE Documents2 (
+  UserId INT64 NOT NULL,
+  DocId INT64 NOT NULL,
+  DocName STRING (1024),
+  Author STRING (1024),
+  DocContents Bytes(MAX),
+  Category STRING(MAX),
+  NullIfFiltered BOOL AS (IF(Category = 'Tech', TRUE, NULL)) HIDDEN,
+  DocEmbedding ARRAY(vector_length=>128)
+) PRIMARY KEY (DocId);
diff --git a/parser/testdata/spanner/guides/vector-indexes.test b/parser/testdata/spanner/guides/vector-indexes.test
new file mode 100644
index 0000000..9f192ee
--- /dev/null
+++ b/parser/testdata/spanner/guides/vector-indexes.test
@@ -0,0 +1,29 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/vector-indexes
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/vector-indexes#create-vector-index
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE VECTOR INDEX DocEmbeddingIndex
+  ON Documents(DocEmbedding)
+  STORING (WordCount)
+  OPTIONS (distance_type = 'COSINE')
+--
+CreateIndexStatement(VECTOR) [0-125]
+  PathExpression [20-37]
+    Identifier(DocEmbeddingIndex) [20-37]
+  PathExpression [43-52]
+    Identifier(Documents) [43-52]
+  IndexItemList [52-66]
+    OrderingExpression(ASC) [53-65]
+      PathExpression [53-65]
+        Identifier(DocEmbedding) [53-65]
+  IndexStoringExpressionList [77-88]
+    PathExpression [78-87]
+      Identifier(WordCount) [78-87]
+  OptionsList [99-125]
+    OptionsEntry [100-124]
+      Identifier(distance_type) [100-113]
+      StringLiteral [116-124]
+        StringLiteralComponent('COSINE') [116-124]
diff --git a/parser/testdata/spanner/guides/vector-search-best-practices.test b/parser/testdata/spanner/guides/vector-search-best-practices.test
new file mode 100644
index 0000000..00ec5c5
--- /dev/null
+++ b/parser/testdata/spanner/guides/vector-search-best-practices.test
@@ -0,0 +1,111 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/vector-search-best-practices
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/vector-search-best-practices#use-top-k-query
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT d.DocId
+FROM Documents AS d
+WHERE COSINE_DISTANCE(d.DocEmbedding, @vector) < 1
+--
+QueryStatement [0-85]
+  Query [0-85]
+    Select [0-85]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(d) [7-8]
+            Identifier(DocId) [9-14]
+      FromClause [15-34]
+        TablePathExpression [20-34]
+          PathExpression [20-29]
+            Identifier(Documents) [20-29]
+          Alias [30-34]
+            Identifier(d) [33-34]
+      WhereClause [35-85]
+        BinaryExpression(<) [41-85]
+          FunctionCall [41-81]
+            PathExpression [41-56]
+              Identifier(COSINE_DISTANCE) [41-56]
+            PathExpression [57-71]
+              Identifier(d) [57-58]
+              Identifier(DocEmbedding) [59-71]
+            ParameterExpr [73-80]
+              Identifier(vector) [74-80]
+          IntLiteral(1) [84-85]
+==
+# https://docs.cloud.google.com/spanner/docs/vector-search-best-practices#use-top-k-query
+SELECT d.DocId
+FROM Documents AS d
+ORDER BY COSINE_DISTANCE(d.DocEmbedding, @vector)
+LIMIT 10
+--
+QueryStatement [0-93]
+  Query [0-93]
+    Select [0-34]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(d) [7-8]
+            Identifier(DocId) [9-14]
+      FromClause [15-34]
+        TablePathExpression [20-34]
+          PathExpression [20-29]
+            Identifier(Documents) [20-29]
+          Alias [30-34]
+            Identifier(d) [33-34]
+    OrderBy [35-84]
+      OrderingExpression(ASC) [44-84]
+        FunctionCall [44-84]
+          PathExpression [44-59]
+            Identifier(COSINE_DISTANCE) [44-59]
+          PathExpression [60-74]
+            Identifier(d) [60-61]
+            Identifier(DocEmbedding) [62-74]
+          ParameterExpr [76-83]
+            Identifier(vector) [77-83]
+    LimitOffset [85-93]
+      Limit [85-93]
+        IntLiteral(10) [91-93]
+==
+# https://docs.cloud.google.com/spanner/docs/vector-search-best-practices#use-secondary-index
+SELECT d.DocId
+FROM Documents AS d
+WHERE Category = 'toy'
+ORDER BY COSINE_DISTANCE(d.DocEmbedding, @vector)
+LIMIT 10
+--
+QueryStatement [0-116]
+  Query [0-116]
+    Select [0-57]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(d) [7-8]
+            Identifier(DocId) [9-14]
+      FromClause [15-34]
+        TablePathExpression [20-34]
+          PathExpression [20-29]
+            Identifier(Documents) [20-29]
+          Alias [30-34]
+            Identifier(d) [33-34]
+      WhereClause [35-57]
+        BinaryExpression(=) [41-57]
+          PathExpression [41-49]
+            Identifier(Category) [41-49]
+          StringLiteral [52-57]
+            StringLiteralComponent('toy') [52-57]
+    OrderBy [58-107]
+      OrderingExpression(ASC) [67-107]
+        FunctionCall [67-107]
+          PathExpression [67-82]
+            Identifier(COSINE_DISTANCE) [67-82]
+          PathExpression [83-97]
+            Identifier(d) [83-84]
+            Identifier(DocEmbedding) [85-97]
+          ParameterExpr [99-106]
+            Identifier(vector) [100-106]
+    LimitOffset [108-116]
+      Limit [108-116]
+        IntLiteral(10) [114-116]
diff --git a/parser/testdata/spanner/guides/vector-search-tutorial.spanner.sql b/parser/testdata/spanner/guides/vector-search-tutorial.spanner.sql
new file mode 100644
index 0000000..ffa202e
--- /dev/null
+++ b/parser/testdata/spanner/guides/vector-search-tutorial.spanner.sql
@@ -0,0 +1,18 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/vector-search-tutorial
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/vector-search-tutorial#create-vector-index
+# id d2267b14470f: vector index clauses (vector-search-tutorial)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ";" or end of input but got keyword WHERE [at 3:5]
+CREATE VECTOR INDEX ProductDescriptionEmbeddingIndex
+    ON products(productDescriptionEmbedding)
+    WHERE productDescriptionEmbedding IS NOT NULL
+OPTIONS (
+distance_type = 'COSINE'
+);
diff --git a/parser/testdata/spanner/guides/vector-search-tutorial.test b/parser/testdata/spanner/guides/vector-search-tutorial.test
new file mode 100644
index 0000000..5416de4
--- /dev/null
+++ b/parser/testdata/spanner/guides/vector-search-tutorial.test
@@ -0,0 +1,531 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/vector-search-tutorial
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/vector-search-tutorial#create-database
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE products (
+    categoryId INT64 NOT NULL,
+    productId INT64 NOT NULL,
+    productName STRING(MAX) NOT NULL,
+    productDescription STRING(MAX) NOT NULL,
+    productDescriptionEmbedding ARRAY,
+    createTime TIMESTAMP NOT NULL OPTIONS (
+    allow_commit_timestamp = true
+    ), inventoryCount INT64 NOT NULL,
+    priceInCents INT64,
+  ) PRIMARY KEY(categoryId, productId)
+--
+CreateTableStatement [0-394]
+  PathExpression [13-21]
+    Identifier(products) [13-21]
+  TableElementList [22-359]
+    ColumnDefinition [28-53]
+      Identifier(categoryId) [28-38]
+      SimpleColumnSchema [39-53]
+        PathExpression [39-44]
+          Identifier(INT64) [39-44]
+        ColumnAttributeList [45-53]
+          NotNullColumnAttribute [45-53]
+    ColumnDefinition [59-83]
+      Identifier(productId) [59-68]
+      SimpleColumnSchema [69-83]
+        PathExpression [69-74]
+          Identifier(INT64) [69-74]
+        ColumnAttributeList [75-83]
+          NotNullColumnAttribute [75-83]
+    ColumnDefinition [89-121]
+      Identifier(productName) [89-100]
+      SimpleColumnSchema [101-121]
+        PathExpression [101-107]
+          Identifier(STRING) [101-107]
+        TypeParameterList [107-111]
+          MaxLiteral() [108-111]
+        ColumnAttributeList [113-121]
+          NotNullColumnAttribute [113-121]
+    ColumnDefinition [127-166]
+      Identifier(productDescription) [127-145]
+      SimpleColumnSchema [146-166]
+        PathExpression [146-152]
+          Identifier(STRING) [146-152]
+        TypeParameterList [152-156]
+          MaxLiteral() [153-156]
+        ColumnAttributeList [158-166]
+          NotNullColumnAttribute [158-166]
+    ColumnDefinition [172-214]
+      Identifier(productDescriptionEmbedding) [172-199]
+      ArrayColumnSchema [200-214]
+        SimpleColumnSchema [206-213]
+          PathExpression [206-213]
+            Identifier(FLOAT32) [206-213]
+    ColumnDefinition [220-299]
+      Identifier(createTime) [220-230]
+      SimpleColumnSchema [231-299]
+        PathExpression [231-240]
+          Identifier(TIMESTAMP) [231-240]
+        ColumnAttributeList [241-249]
+          NotNullColumnAttribute [241-249]
+        OptionsList [258-299]
+          OptionsEntry [264-293]
+            Identifier(allow_commit_timestamp) [264-286]
+            BooleanLiteral(true) [289-293]
+    ColumnDefinition [301-330]
+      Identifier(inventoryCount) [301-315]
+      SimpleColumnSchema [316-330]
+        PathExpression [316-321]
+          Identifier(INT64) [316-321]
+        ColumnAttributeList [322-330]
+          NotNullColumnAttribute [322-330]
+    ColumnDefinition [336-354]
+      Identifier(priceInCents) [336-348]
+      SimpleColumnSchema [349-354]
+        PathExpression [349-354]
+          Identifier(INT64) [349-354]
+  SpannerTableOptions [360-394]
+    PrimaryKey(ENFORCED) [360-394]
+      PrimaryKeyElementList [371-394]
+        PrimaryKeyElement(ASC) [372-382]
+          Identifier(categoryId) [372-382]
+        PrimaryKeyElement(ASC) [384-393]
+          Identifier(productId) [384-393]
+==
+# https://docs.cloud.google.com/spanner/docs/vector-search-tutorial#load-data
+INSERT INTO products (categoryId, productId, productName, productDescription, createTime, inventoryCount, priceInCents)
+VALUES (1, 1, "Cymbal Helios Helmet", "Safety meets style with the Cymbal children's bike helmet. Its lightweight design, superior ventilation, and adjustable fit ensure comfort and protection on every ride. Stay bright and keep your child safe under the sun with Cymbal Helios!", PENDING_COMMIT_TIMESTAMP(), 100, 10999),
+      (1, 2, "Cymbal Sprout", "Let their cycling journey begin with the Cymbal Sprout, the ideal balance bike for beginning riders ages 2-4 years. Its lightweight frame, low seat height, and puncture-proof tires promote stability and confidence as little ones learn to balance and steer. Watch them sprout into cycling enthusiasts with Cymbal Sprout!", PENDING_COMMIT_TIMESTAMP(), 10, 13999),
+      (1, 3, "Cymbal Spark Jr.", "Light, vibrant, and ready for adventure, the Spark Jr. is the perfect first bike for young riders (ages 5-8). Its sturdy frame, easy-to-use brakes, and puncture-resistant tires inspire confidence and endless playtime. Let the spark of cycling ignite with Cymbal!", PENDING_COMMIT_TIMESTAMP(), 34, 13900),
+      (1, 4, "Cymbal Summit", "Conquering trails is a breeze with the Summit mountain bike. Its lightweight aluminum frame, responsive suspension, and powerful disc brakes provide exceptional control and comfort for experienced bikers navigating rocky climbs or shredding downhill. Reach new heights with Cymbal Summit!", PENDING_COMMIT_TIMESTAMP(), 0, 79999),
+      (1, 5, "Cymbal Breeze", "Cruise in style and embrace effortless pedaling with the Breeze electric bike. Its whisper-quiet motor and long-lasting battery let you conquer hills and distances with ease. Enjoy scenic rides, commutes, or errands with a boost of confidence from Cymbal Breeze!", PENDING_COMMIT_TIMESTAMP(), 72, 129999),
+      (1, 6, "Cymbal Trailblazer Backpack", "Carry all your essentials in style with the Trailblazer backpack. Its water-resistant material, multiple compartments, and comfortable straps keep your gear organized and accessible, allowing you to focus on the adventure. Blaze new trails with Cymbal Trailblazer!", PENDING_COMMIT_TIMESTAMP(), 24, 7999),
+      (1, 7, "Cymbal Phoenix Lights", "See and be seen with the Phoenix bike lights. Powerful LEDs and multiple light modes ensure superior visibility, enhancing your safety and enjoyment during day or night rides. Light up your journey with Cymbal Phoenix!", PENDING_COMMIT_TIMESTAMP(), 87, 3999),
+      (1, 8, "Cymbal Windstar Pump", "Flat tires are no match for the Windstar pump. Its compact design, lightweight construction, and high-pressure capacity make inflating tires quick and effortless. Get back on the road in no time with Cymbal Windstar!", PENDING_COMMIT_TIMESTAMP(), 36, 24999),
+      (1, 9,"Cymbal Odyssey Multi-Tool","Be prepared for anything with the Odyssey multi-tool. This handy gadget features essential tools like screwdrivers, hex wrenches, and tire levers, keeping you ready for minor repairs and adjustments on the go. Conquer your journey with Cymbal Odyssey!", PENDING_COMMIT_TIMESTAMP(), 52, 999),
+      (1, 10,"Cymbal Nomad Water Bottle","Stay hydrated on every ride with the Nomad water bottle. Its sleek design, BPA-free construction, and secure lock lid make it the perfect companion for staying refreshed and motivated throughout your adventures. Hydrate and explore with Cymbal Nomad!", PENDING_COMMIT_TIMESTAMP(), 42, 1299)
+--
+InsertStatement [0-3484]
+  PathExpression [12-20]
+    Identifier(products) [12-20]
+  ColumnList [21-119]
+    Identifier(categoryId) [22-32]
+    Identifier(productId) [34-43]
+    Identifier(productName) [45-56]
+    Identifier(productDescription) [58-76]
+    Identifier(createTime) [78-88]
+    Identifier(inventoryCount) [90-104]
+    Identifier(priceInCents) [106-118]
+  InsertValuesRowList [120-3484]
+    InsertValuesRow [127-440]
+      IntLiteral(1) [128-129]
+      IntLiteral(1) [131-132]
+      StringLiteral [134-156]
+        StringLiteralComponent("Cymbal Helios Helmet") [134-156]
+      StringLiteral [158-399]
+        StringLiteralComponent("Safety meets style with the Cymbal children's bike helmet. Its lightweight design, superior ventilation, and adjustable fit ensure comfort and protection on every ride. Stay bright and keep your child safe under the sun with Cymbal Helios!") [158-399]
+      FunctionCall [401-427]
+        PathExpression [401-425]
+          Identifier(PENDING_COMMIT_TIMESTAMP) [401-425]
+      IntLiteral(100) [429-432]
+      IntLiteral(10999) [434-439]
+    InsertValuesRow [448-833]
+      IntLiteral(1) [449-450]
+      IntLiteral(2) [452-453]
+      StringLiteral [455-470]
+        StringLiteralComponent("Cymbal Sprout") [455-470]
+      StringLiteral [472-793]
+        StringLiteralComponent("Let their cycling journey begin with the Cymbal Sprout, the ideal balance bike for beginning riders ages 2-4 years. Its lightweight frame, low seat height, and puncture-proof tires promote stability and confidence as little ones learn to balance and steer. Watch them sprout into cycling enthusiasts with Cymbal Sprout!") [472-793]
+      FunctionCall [795-821]
+        PathExpression [795-819]
+          Identifier(PENDING_COMMIT_TIMESTAMP) [795-819]
+      IntLiteral(10) [823-825]
+      IntLiteral(13999) [827-832]
+    InsertValuesRow [841-1172]
+      IntLiteral(1) [842-843]
+      IntLiteral(3) [845-846]
+      StringLiteral [848-866]
+        StringLiteralComponent("Cymbal Spark Jr.") [848-866]
+      StringLiteral [868-1132]
+        StringLiteralComponent("Light, vibrant, and ready for adventure, the Spark Jr. is the perfect first bike for young riders (ages 5-8). Its sturdy frame, easy-to-use brakes, and puncture-resistant tires inspire confidence and endless playtime. Let the spark of cycling ignite with Cymbal!") [868-1132]
+      FunctionCall [1134-1160]
+        PathExpression [1134-1158]
+          Identifier(PENDING_COMMIT_TIMESTAMP) [1134-1158]
+      IntLiteral(34) [1162-1164]
+      IntLiteral(13900) [1166-1171]
+    InsertValuesRow [1180-1533]
+      IntLiteral(1) [1181-1182]
+      IntLiteral(4) [1184-1185]
+      StringLiteral [1187-1202]
+        StringLiteralComponent("Cymbal Summit") [1187-1202]
+      StringLiteral [1204-1494]
+        StringLiteralComponent("Conquering trails is a breeze with the Summit mountain bike. Its lightweight aluminum frame, responsive suspension, and powerful disc brakes provide exceptional control and comfort for experienced bikers navigating rocky climbs or shredding downhill. Reach new heights with Cymbal Summit!") [1204-1494]
+      FunctionCall [1496-1522]
+        PathExpression [1496-1520]
+          Identifier(PENDING_COMMIT_TIMESTAMP) [1496-1520]
+      IntLiteral(0) [1524-1525]
+      IntLiteral(79999) [1527-1532]
+    InsertValuesRow [1541-1870]
+      IntLiteral(1) [1542-1543]
+      IntLiteral(5) [1545-1546]
+      StringLiteral [1548-1563]
+        StringLiteralComponent("Cymbal Breeze") [1548-1563]
+      StringLiteral [1565-1829]
+        StringLiteralComponent("Cruise in style and embrace effortless pedaling with the Breeze electric bike. Its whisper-quiet motor and long-lasting battery let you conquer hills and distances with ease. Enjoy scenic rides, commutes, or errands with a boost of confidence from Cymbal Breeze!") [1565-1829]
+      FunctionCall [1831-1857]
+        PathExpression [1831-1855]
+          Identifier(PENDING_COMMIT_TIMESTAMP) [1831-1855]
+      IntLiteral(72) [1859-1861]
+      IntLiteral(129999) [1863-1869]
+    InsertValuesRow [1878-2221]
+      IntLiteral(1) [1879-1880]
+      IntLiteral(6) [1882-1883]
+      StringLiteral [1885-1914]
+        StringLiteralComponent("Cymbal Trailblazer Backpack") [1885-1914]
+      StringLiteral [1916-2182]
+        StringLiteralComponent("Carry all your essentials in style with the Trailblazer backpack. Its water-resistant material, multiple compartments, and comfortable straps keep your gear organized and accessible, allowing you to focus on the adventure. Blaze new trails with Cymbal Trailblazer!") [1916-2182]
+      FunctionCall [2184-2210]
+        PathExpression [2184-2208]
+          Identifier(PENDING_COMMIT_TIMESTAMP) [2184-2208]
+      IntLiteral(24) [2212-2214]
+      IntLiteral(7999) [2216-2220]
+    InsertValuesRow [2229-2520]
+      IntLiteral(1) [2230-2231]
+      IntLiteral(7) [2233-2234]
+      StringLiteral [2236-2259]
+        StringLiteralComponent("Cymbal Phoenix Lights") [2236-2259]
+      StringLiteral [2261-2481]
+        StringLiteralComponent("See and be seen with the Phoenix bike lights. Powerful LEDs and multiple light modes ensure superior visibility, enhancing your safety and enjoyment during day or night rides. Light up your journey with Cymbal Phoenix!") [2261-2481]
+      FunctionCall [2483-2509]
+        PathExpression [2483-2507]
+          Identifier(PENDING_COMMIT_TIMESTAMP) [2483-2507]
+      IntLiteral(87) [2511-2513]
+      IntLiteral(3999) [2515-2519]
+    InsertValuesRow [2528-2817]
+      IntLiteral(1) [2529-2530]
+      IntLiteral(8) [2532-2533]
+      StringLiteral [2535-2557]
+        StringLiteralComponent("Cymbal Windstar Pump") [2535-2557]
+      StringLiteral [2559-2777]
+        StringLiteralComponent("Flat tires are no match for the Windstar pump. Its compact design, lightweight construction, and high-pressure capacity make inflating tires quick and effortless. Get back on the road in no time with Cymbal Windstar!") [2559-2777]
+      FunctionCall [2779-2805]
+        PathExpression [2779-2803]
+          Identifier(PENDING_COMMIT_TIMESTAMP) [2779-2803]
+      IntLiteral(36) [2807-2809]
+      IntLiteral(24999) [2811-2816]
+    InsertValuesRow [2825-3150]
+      IntLiteral(1) [2826-2827]
+      IntLiteral(9) [2829-2830]
+      StringLiteral [2831-2858]
+        StringLiteralComponent("Cymbal Odyssey Multi-Tool") [2831-2858]
+      StringLiteral [2859-3112]
+        StringLiteralComponent("Be prepared for anything with the Odyssey multi-tool. This handy gadget features essential tools like screwdrivers, hex wrenches, and tire levers, keeping you ready for minor repairs and adjustments on the go. Conquer your journey with Cymbal Odyssey!") [2859-3112]
+      FunctionCall [3114-3140]
+        PathExpression [3114-3138]
+          Identifier(PENDING_COMMIT_TIMESTAMP) [3114-3138]
+      IntLiteral(52) [3142-3144]
+      IntLiteral(999) [3146-3149]
+    InsertValuesRow [3158-3484]
+      IntLiteral(1) [3159-3160]
+      IntLiteral(10) [3162-3164]
+      StringLiteral [3165-3192]
+        StringLiteralComponent("Cymbal Nomad Water Bottle") [3165-3192]
+      StringLiteral [3193-3445]
+        StringLiteralComponent("Stay hydrated on every ride with the Nomad water bottle. Its sleek design, BPA-free construction, and secure lock lid make it the perfect companion for staying refreshed and motivated throughout your adventures. Hydrate and explore with Cymbal Nomad!") [3193-3445]
+      FunctionCall [3447-3473]
+        PathExpression [3447-3471]
+          Identifier(PENDING_COMMIT_TIMESTAMP) [3447-3471]
+      IntLiteral(42) [3475-3477]
+      IntLiteral(1299) [3479-3483]
+==
+# https://docs.cloud.google.com/spanner/docs/vector-search-tutorial#generate-embeddings
+UPDATE products p1
+SET productDescriptionEmbedding =
+  (SELECT embeddings.values
+    FROM ML.PREDICT(MODEL EmbeddingsModel,
+      (SELECT p1.productDescription as content)
+    )
+  )
+WHERE categoryId=1
+--
+UpdateStatement [0-200]
+  PathExpression [7-15]
+    Identifier(products) [7-15]
+  Alias [16-18]
+    Identifier(p1) [16-18]
+  UpdateItemList [23-181]
+    UpdateItem [23-181]
+      UpdateSetValue [23-181]
+        PathExpression [23-50]
+          Identifier(productDescriptionEmbedding) [23-50]
+        ExpressionSubquery [55-181]
+          Query [56-177]
+            Select [56-177]
+              SelectList [63-80]
+                SelectColumn [63-80]
+                  PathExpression [63-80]
+                    Identifier(embeddings) [63-73]
+                    Identifier(values) [74-80]
+              FromClause [85-177]
+                TVF [90-177]
+                  PathExpression [90-100]
+                    Identifier(ML) [90-92]
+                    Identifier(PREDICT) [93-100]
+                  TVFArgument [101-122]
+                    ModelClause [101-122]
+                      PathExpression [107-122]
+                        Identifier(EmbeddingsModel) [107-122]
+                  TVFArgument [130-171]
+                    ExpressionSubquery [130-171]
+                      Query [131-170]
+                        Select [131-170]
+                          SelectList [138-170]
+                            SelectColumn [138-170]
+                              PathExpression [138-159]
+                                Identifier(p1) [138-140]
+                                Identifier(productDescription) [141-159]
+                              Alias [160-170]
+                                Identifier(content) [163-170]
+  BinaryExpression(=) [188-200]
+    PathExpression [188-198]
+      Identifier(categoryId) [188-198]
+    IntLiteral(1) [199-200]
+==
+# https://docs.cloud.google.com/spanner/docs/vector-search-tutorial#perform-vector-search
+SELECT productName, productDescription, inventoryCount,
+  COSINE_DISTANCE(
+    productDescriptionEmbedding,
+    (
+      SELECT embeddings.values
+      FROM
+        ML.PREDICT(
+          MODEL EmbeddingsModel,
+          (SELECT "I'd like to buy a starter bike for my 3 year old child" AS content))
+    )) AS distance
+FROM products
+WHERE inventoryCount > 0
+ORDER BY distance
+LIMIT 5
+--
+QueryStatement [0-380]
+  Query [0-380]
+    Select [0-354]
+      SelectList [7-315]
+        SelectColumn [7-18]
+          PathExpression [7-18]
+            Identifier(productName) [7-18]
+        SelectColumn [20-38]
+          PathExpression [20-38]
+            Identifier(productDescription) [20-38]
+        SelectColumn [40-54]
+          PathExpression [40-54]
+            Identifier(inventoryCount) [40-54]
+        SelectColumn [58-315]
+          FunctionCall [58-303]
+            PathExpression [58-73]
+              Identifier(COSINE_DISTANCE) [58-73]
+            PathExpression [79-106]
+              Identifier(productDescriptionEmbedding) [79-106]
+            ExpressionSubquery [112-302]
+              Query [120-296]
+                Select [120-296]
+                  SelectList [127-144]
+                    SelectColumn [127-144]
+                      PathExpression [127-144]
+                        Identifier(embeddings) [127-137]
+                        Identifier(values) [138-144]
+                  FromClause [151-296]
+                    TVF [164-296]
+                      PathExpression [164-174]
+                        Identifier(ML) [164-166]
+                        Identifier(PREDICT) [167-174]
+                      TVFArgument [186-207]
+                        ModelClause [186-207]
+                          PathExpression [192-207]
+                            Identifier(EmbeddingsModel) [192-207]
+                      TVFArgument [219-295]
+                        ExpressionSubquery [219-295]
+                          Query [220-294]
+                            Select [220-294]
+                              SelectList [227-294]
+                                SelectColumn [227-294]
+                                  StringLiteral [227-283]
+                                    StringLiteralComponent("I'd like to buy a starter bike for my 3 year old child") [227-283]
+                                  Alias [284-294]
+                                    Identifier(content) [287-294]
+          Alias [304-315]
+            Identifier(distance) [307-315]
+      FromClause [316-329]
+        TablePathExpression [321-329]
+          PathExpression [321-329]
+            Identifier(products) [321-329]
+      WhereClause [330-354]
+        BinaryExpression(>) [336-354]
+          PathExpression [336-350]
+            Identifier(inventoryCount) [336-350]
+          IntLiteral(0) [353-354]
+    OrderBy [355-372]
+      OrderingExpression(ASC) [364-372]
+        PathExpression [364-372]
+          Identifier(distance) [364-372]
+    LimitOffset [373-380]
+      Limit [373-380]
+        IntLiteral(5) [379-380]
+==
+# https://docs.cloud.google.com/spanner/docs/vector-search-tutorial#create-vector-index
+UPDATE products p1
+SET productDescriptionEmbedding =
+(SELECT embeddings.values from ML.PREDICT(MODEL EmbeddingsModel,
+(SELECT p1.productDescription as content)))
+WHERE categoryId=1
+--
+UpdateStatement [0-180]
+  PathExpression [7-15]
+    Identifier(products) [7-15]
+  Alias [16-18]
+    Identifier(p1) [16-18]
+  UpdateItemList [23-161]
+    UpdateItem [23-161]
+      UpdateSetValue [23-161]
+        PathExpression [23-50]
+          Identifier(productDescriptionEmbedding) [23-50]
+        ExpressionSubquery [53-161]
+          Query [54-160]
+            Select [54-160]
+              SelectList [61-78]
+                SelectColumn [61-78]
+                  PathExpression [61-78]
+                    Identifier(embeddings) [61-71]
+                    Identifier(values) [72-78]
+              FromClause [79-160]
+                TVF [84-160]
+                  PathExpression [84-94]
+                    Identifier(ML) [84-86]
+                    Identifier(PREDICT) [87-94]
+                  TVFArgument [95-116]
+                    ModelClause [95-116]
+                      PathExpression [101-116]
+                        Identifier(EmbeddingsModel) [101-116]
+                  TVFArgument [118-159]
+                    ExpressionSubquery [118-159]
+                      Query [119-158]
+                        Select [119-158]
+                          SelectList [126-158]
+                            SelectColumn [126-158]
+                              PathExpression [126-147]
+                                Identifier(p1) [126-128]
+                                Identifier(productDescription) [129-147]
+                              Alias [148-158]
+                                Identifier(content) [151-158]
+  BinaryExpression(=) [168-180]
+    PathExpression [168-178]
+      Identifier(categoryId) [168-178]
+    IntLiteral(1) [179-180]
+==
+# https://docs.cloud.google.com/spanner/docs/vector-search-tutorial#use-ANN-function
+-- Generate the prompt embedding
+WITH embedding AS (
+  SELECT embeddings.values
+  FROM ML.PREDICT(
+    MODEL EmbeddingsModel,
+      (SELECT "I'd like to buy a starter bike for my 3 year old child" as content)
+  )
+)
+-- Use embedding to find the most similar entries in the database
+SELECT productName, productDescription, inventoryCount,
+  (APPROX_COSINE_DISTANCE(productDescriptionEmbedding,
+   embedding.values,
+  options => JSON '{"num_leaves_to_search": 10}')) as distance
+FROM products @{force_index=ProductDescriptionEmbeddingIndex}, embedding
+WHERE productDescriptionEmbedding IS NOT NULL AND inventoryCount > 0
+ORDER BY distance
+LIMIT 5
+--
+QueryStatement [33-643]
+  Query [33-643]
+    WithClause [33-214]
+      WithClauseEntry [38-214]
+        AliasedQuery [38-214]
+          Identifier(embedding) [38-47]
+          Query [51-214]
+            Select [55-212]
+              SelectList [62-79]
+                SelectColumn [62-79]
+                  PathExpression [62-79]
+                    Identifier(embeddings) [62-72]
+                    Identifier(values) [73-79]
+              FromClause [82-212]
+                TVF [87-212]
+                  PathExpression [87-97]
+                    Identifier(ML) [87-89]
+                    Identifier(PREDICT) [90-97]
+                  TVFArgument [103-124]
+                    ModelClause [103-124]
+                      PathExpression [109-124]
+                        Identifier(EmbeddingsModel) [109-124]
+                  TVFArgument [132-208]
+                    ExpressionSubquery [132-208]
+                      Query [133-207]
+                        Select [133-207]
+                          SelectList [140-207]
+                            SelectColumn [140-207]
+                              StringLiteral [140-196]
+                                StringLiteralComponent("I'd like to buy a starter bike for my 3 year old child") [140-196]
+                              Alias [197-207]
+                                Identifier(content) [200-207]
+    Select [281-617]
+      SelectList [288-475]
+        SelectColumn [288-299]
+          PathExpression [288-299]
+            Identifier(productName) [288-299]
+        SelectColumn [301-319]
+          PathExpression [301-319]
+            Identifier(productDescription) [301-319]
+        SelectColumn [321-335]
+          PathExpression [321-335]
+            Identifier(inventoryCount) [321-335]
+        SelectColumn [339-475]
+          FunctionCall [340-462]
+            PathExpression [340-362]
+              Identifier(APPROX_COSINE_DISTANCE) [340-362]
+            PathExpression [363-390]
+              Identifier(productDescriptionEmbedding) [363-390]
+            PathExpression [395-411]
+              Identifier(embedding) [395-404]
+              Identifier(values) [405-411]
+            NamedArgument [415-461]
+              Identifier(options) [415-422]
+              JSONLiteral [426-461]
+                StringLiteral [431-461]
+                  StringLiteralComponent('{"num_leaves_to_search": 10}') [431-461]
+          Alias [464-475]
+            Identifier(distance) [467-475]
+      FromClause [476-548]
+        Join(COMMA) [481-548]
+          TablePathExpression [481-537]
+            PathExpression [481-489]
+              Identifier(products) [481-489]
+            Hint [490-537]
+              HintEntry [492-536]
+                Identifier(force_index) [492-503]
+                PathExpression [504-536]
+                  Identifier(ProductDescriptionEmbeddingIndex) [504-536]
+          Location [537-538]
+          TablePathExpression [539-548]
+            PathExpression [539-548]
+              Identifier(embedding) [539-548]
+      WhereClause [549-617]
+        AndExpr [555-617]
+          BinaryExpression(IS NOT) [555-594]
+            PathExpression [555-582]
+              Identifier(productDescriptionEmbedding) [555-582]
+            NullLiteral(NULL) [590-594]
+          BinaryExpression(>) [599-617]
+            PathExpression [599-613]
+              Identifier(inventoryCount) [599-613]
+            IntLiteral(0) [616-617]
+    OrderBy [618-635]
+      OrderingExpression(ASC) [627-635]
+        PathExpression [627-635]
+          Identifier(distance) [627-635]
+    LimitOffset [636-643]
+      Limit [636-643]
+        IntLiteral(5) [642-643]
diff --git a/parser/testdata/spanner/guides/working-with-json.metadata.json b/parser/testdata/spanner/guides/working-with-json.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/guides/working-with-json.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/guides/working-with-json.test b/parser/testdata/spanner/guides/working-with-json.test
new file mode 100644
index 0000000..896cb93
--- /dev/null
+++ b/parser/testdata/spanner/guides/working-with-json.test
@@ -0,0 +1,305 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/working-with-json
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/working-with-json#nullability
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT (JSON '{"a":null}').a IS NULL
+--
+QueryStatement [0-36]
+  Query [0-36]
+    Select [0-36]
+      SelectList [7-36]
+        SelectColumn [7-36]
+          BinaryExpression(IS) [7-36]
+            DotIdentifier [7-28]
+              JSONLiteral [8-25]
+                StringLiteral [13-25]
+                  StringLiteralComponent('{"a":null}') [13-25]
+              Identifier(a) [27-28]
+            NullLiteral(NULL) [32-36]
+==
+# https://docs.cloud.google.com/spanner/docs/working-with-json#nullability
+-- Returns FALSE
+SELECT (JSON '{"a":null}').b IS NULL
+--
+QueryStatement [17-53]
+  Query [17-53]
+    Select [17-53]
+      SelectList [24-53]
+        SelectColumn [24-53]
+          BinaryExpression(IS) [24-53]
+            DotIdentifier [24-45]
+              JSONLiteral [25-42]
+                StringLiteral [30-42]
+                  StringLiteralComponent('{"a":null}') [30-42]
+              Identifier(b) [44-45]
+            NullLiteral(NULL) [49-53]
+==
+# https://docs.cloud.google.com/spanner/docs/working-with-json#nullability
+-- Returns TRUE
+
+SELECT JSON_QUERY(JSON '{"a":null}', "$.a")
+--
+QueryStatement [17-60]
+  Query [17-60]
+    Select [17-60]
+      SelectList [24-60]
+        SelectColumn [24-60]
+          FunctionCall [24-60]
+            PathExpression [24-34]
+              Identifier(JSON_QUERY) [24-34]
+            JSONLiteral [35-52]
+              StringLiteral [40-52]
+                StringLiteralComponent('{"a":null}') [40-52]
+            StringLiteral [54-59]
+              StringLiteralComponent("$.a") [54-59]
+==
+# https://docs.cloud.google.com/spanner/docs/working-with-json#nullability
+-- Returns a JSON 'null'
+SELECT JSON_QUERY(JSON '{"a":null}', "$.b")
+--
+QueryStatement [25-68]
+  Query [25-68]
+    Select [25-68]
+      SelectList [32-68]
+        SelectColumn [32-68]
+          FunctionCall [32-68]
+            PathExpression [32-42]
+              Identifier(JSON_QUERY) [32-42]
+            JSONLiteral [43-60]
+              StringLiteral [48-60]
+                StringLiteralComponent('{"a":null}') [48-60]
+            StringLiteral [62-67]
+              StringLiteralComponent("$.b") [62-67]
+==
+# https://docs.cloud.google.com/spanner/docs/working-with-json#create
+CREATE TABLE Venues (
+  VenueId   INT64 NOT NULL,
+  VenueName  STRING(1024),
+  VenueAddress STRING(1024),
+  VenueFeatures JSON,
+  DateOpened  DATE,
+) PRIMARY KEY(VenueId)
+--
+CreateTableStatement [0-170]
+  PathExpression [13-19]
+    Identifier(Venues) [13-19]
+  TableElementList [20-149]
+    ColumnDefinition [24-48]
+      Identifier(VenueId) [24-31]
+      SimpleColumnSchema [34-48]
+        PathExpression [34-39]
+          Identifier(INT64) [34-39]
+        ColumnAttributeList [40-48]
+          NotNullColumnAttribute [40-48]
+    ColumnDefinition [52-75]
+      Identifier(VenueName) [52-61]
+      SimpleColumnSchema [63-75]
+        PathExpression [63-69]
+          Identifier(STRING) [63-69]
+        TypeParameterList [69-74]
+          IntLiteral(1024) [70-74]
+    ColumnDefinition [79-104]
+      Identifier(VenueAddress) [79-91]
+      SimpleColumnSchema [92-104]
+        PathExpression [92-98]
+          Identifier(STRING) [92-98]
+        TypeParameterList [98-103]
+          IntLiteral(1024) [99-103]
+    ColumnDefinition [108-126]
+      Identifier(VenueFeatures) [108-121]
+      SimpleColumnSchema [122-126]
+        PathExpression [122-126]
+          Identifier(JSON) [122-126]
+    ColumnDefinition [130-146]
+      Identifier(DateOpened) [130-140]
+      SimpleColumnSchema [142-146]
+        PathExpression [142-146]
+          Identifier(DATE) [142-146]
+  SpannerTableOptions [150-170]
+    PrimaryKey(ENFORCED) [150-170]
+      PrimaryKeyElementList [161-170]
+        PrimaryKeyElement(ASC) [162-169]
+          Identifier(VenueId) [162-169]
+==
+# https://docs.cloud.google.com/spanner/docs/working-with-json#add-remove
+ALTER TABLE Venues ADD COLUMN VenueDetails JSON
+--
+AlterTableStatement [0-47]
+  PathExpression [12-18]
+    Identifier(Venues) [12-18]
+  AlterActionList [19-47]
+    AddColumnAction [19-47]
+      ColumnDefinition [30-47]
+        Identifier(VenueDetails) [30-42]
+        SimpleColumnSchema [43-47]
+          PathExpression [43-47]
+            Identifier(JSON) [43-47]
+==
+# https://docs.cloud.google.com/spanner/docs/working-with-json#add-remove
+ALTER TABLE Venues DROP COLUMN VenueDetails
+--
+AlterTableStatement [0-43]
+  PathExpression [12-18]
+    Identifier(Venues) [12-18]
+  AlterActionList [19-43]
+    DropColumnAction [19-43]
+      Identifier(VenueDetails) [31-43]
+==
+# https://docs.cloud.google.com/spanner/docs/working-with-json#use-secondary-index
+ALTER TABLE Venues
+ADD COLUMN VenueCapacity INT64 AS (INT64(VenueDetails.capacity))
+--
+AlterTableStatement [0-83]
+  PathExpression [12-18]
+    Identifier(Venues) [12-18]
+  AlterActionList [19-83]
+    AddColumnAction [19-83]
+      ColumnDefinition [30-83]
+        Identifier(VenueCapacity) [30-43]
+        SimpleColumnSchema [44-83]
+          PathExpression [44-49]
+            Identifier(INT64) [44-49]
+          GeneratedColumnInfo(generated_mode=ALWAYS) [50-83]
+            FunctionCall [54-82]
+              PathExpression [54-59]
+                Identifier(INT64) [54-59]
+              PathExpression [60-81]
+                Identifier(VenueDetails) [60-72]
+                Identifier(capacity) [73-81]
+==
+# https://docs.cloud.google.com/spanner/docs/working-with-json#use-secondary-index
+CREATE INDEX VenuesByCapacity ON Venue (VenueCapacity)
+--
+CreateIndexStatement [0-54]
+  PathExpression [13-29]
+    Identifier(VenuesByCapacity) [13-29]
+  PathExpression [33-38]
+    Identifier(Venue) [33-38]
+  IndexItemList [39-54]
+    OrderingExpression(ASC) [40-53]
+      PathExpression [40-53]
+        Identifier(VenueCapacity) [40-53]
+==
+# https://docs.cloud.google.com/spanner/docs/working-with-json#use-secondary-index
+SELECT VenueName
+FROM Venues
+WHERE VenueCapacity > 1000
+--
+QueryStatement [0-55]
+  Query [0-55]
+    Select [0-55]
+      SelectList [7-16]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(VenueName) [7-16]
+      FromClause [17-28]
+        TablePathExpression [22-28]
+          PathExpression [22-28]
+            Identifier(Venues) [22-28]
+      WhereClause [29-55]
+        BinaryExpression(>) [35-55]
+          PathExpression [35-48]
+            Identifier(VenueCapacity) [35-48]
+          IntLiteral(1000) [51-55]
+==
+# https://docs.cloud.google.com/spanner/docs/working-with-json#use-search-indexes
+ALTER TABLE Venues
+ADD COLUMN VenueDetails_Tokens TOKENLIST AS (TOKENIZE_JSON(VenueDetails)) HIDDEN
+--
+AlterTableStatement [0-99]
+  PathExpression [12-18]
+    Identifier(Venues) [12-18]
+  AlterActionList [19-99]
+    AddColumnAction [19-99]
+      ColumnDefinition [30-99]
+        Identifier(VenueDetails_Tokens) [30-49]
+        SimpleColumnSchema [50-99]
+          PathExpression [50-59]
+            Identifier(TOKENLIST) [50-59]
+          GeneratedColumnInfo(generated_mode=ALWAYS) [60-92]
+            FunctionCall [64-91]
+              PathExpression [64-77]
+                Identifier(TOKENIZE_JSON) [64-77]
+              PathExpression [78-90]
+                Identifier(VenueDetails) [78-90]
+          ColumnAttributeList [93-99]
+            HiddenColumnAttribute [93-99]
+==
+# https://docs.cloud.google.com/spanner/docs/working-with-json#use-search-indexes
+CREATE SEARCH INDEX VenuesByVenueDetails
+ON Venue (VenueDetails_Tokens)
+--
+CreateIndexStatement(SEARCH) [0-71]
+  PathExpression [20-40]
+    Identifier(VenuesByVenueDetails) [20-40]
+  PathExpression [44-49]
+    Identifier(Venue) [44-49]
+  IndexItemList [50-71]
+    OrderingExpression(ASC) [51-70]
+      PathExpression [51-70]
+        Identifier(VenueDetails_Tokens) [51-70]
+==
+# https://docs.cloud.google.com/spanner/docs/working-with-json#use-search-indexes
+SELECT VenueName
+FROM Venues
+WHERE JSON_CONTAINS(VenueDetails, JSON '{"labels": ["large"], "open": {"Friday": true}}')
+--
+QueryStatement [0-118]
+  Query [0-118]
+    Select [0-118]
+      SelectList [7-16]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(VenueName) [7-16]
+      FromClause [17-28]
+        TablePathExpression [22-28]
+          PathExpression [22-28]
+            Identifier(Venues) [22-28]
+      WhereClause [29-118]
+        FunctionCall [35-118]
+          PathExpression [35-48]
+            Identifier(JSON_CONTAINS) [35-48]
+          PathExpression [49-61]
+            Identifier(VenueDetails) [49-61]
+          JSONLiteral [63-117]
+            StringLiteral [68-117]
+              StringLiteralComponent('{"labels": ["large"], "open": {"Friday": true}}') [68-117]
+==
+# https://docs.cloud.google.com/spanner/docs/working-with-json#use-search-indexes
+SELECT VenueName
+FROM Venues
+WHERE VenueDetails.open.Friday = true
+  AND VenueDetails.type IN ("stadium", "arena")
+--
+QueryStatement [0-114]
+  Query [0-114]
+    Select [0-114]
+      SelectList [7-16]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(VenueName) [7-16]
+      FromClause [17-28]
+        TablePathExpression [22-28]
+          PathExpression [22-28]
+            Identifier(Venues) [22-28]
+      WhereClause [29-114]
+        AndExpr [35-114]
+          BinaryExpression(=) [35-66]
+            PathExpression [35-59]
+              Identifier(VenueDetails) [35-47]
+              Identifier(open) [48-52]
+              Identifier(Friday) [53-59]
+            BooleanLiteral(true) [62-66]
+          InExpression(IN) [73-114]
+            PathExpression [73-90]
+              Identifier(VenueDetails) [73-85]
+              Identifier(type) [86-90]
+            Location [91-93]
+            InList [95-113]
+              StringLiteral [95-104]
+                StringLiteralComponent("stadium") [95-104]
+              StringLiteral [106-113]
+                StringLiteralComponent("arena") [106-113]
diff --git a/parser/testdata/spanner/guides/write-sql-gemini.test b/parser/testdata/spanner/guides/write-sql-gemini.test
new file mode 100644
index 0000000..1d436ce
--- /dev/null
+++ b/parser/testdata/spanner/guides/write-sql-gemini.test
@@ -0,0 +1,54 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/write-sql-gemini
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/write-sql-gemini#generate-sql-queries-using-natural-language-prompts
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+INSERT INTO Singers (SingerId, FirstName, LastName, BirthDate)
+VALUES (1, Alex, 'M.', '1977-10-16')
+--
+InsertStatement [0-99]
+  PathExpression [12-19]
+    Identifier(Singers) [12-19]
+  ColumnList [20-62]
+    Identifier(SingerId) [21-29]
+    Identifier(FirstName) [31-40]
+    Identifier(LastName) [42-50]
+    Identifier(BirthDate) [52-61]
+  InsertValuesRowList [63-99]
+    InsertValuesRow [70-99]
+      IntLiteral(1) [71-72]
+      PathExpression [74-78]
+        Identifier(Alex) [74-78]
+      StringLiteral [80-84]
+        StringLiteralComponent('M.') [80-84]
+      StringLiteral [86-98]
+        StringLiteralComponent('1977-10-16') [86-98]
+==
+# https://docs.cloud.google.com/spanner/docs/write-sql-gemini#generate-sql-queries-using-natural-language-prompts
+SELECT *
+FROM Singers
+WHERE Singers.BirthDate
+BETWEEN '1970-01-01' AND '1979-12-31'
+--
+QueryStatement [0-83]
+  Query [0-83]
+    Select [0-83]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-21]
+        TablePathExpression [14-21]
+          PathExpression [14-21]
+            Identifier(Singers) [14-21]
+      WhereClause [22-83]
+        BetweenExpression(BETWEEN) [28-83]
+          PathExpression [28-45]
+            Identifier(Singers) [28-35]
+            Identifier(BirthDate) [36-45]
+          Location [46-53]
+          StringLiteral [54-66]
+            StringLiteralComponent('1970-01-01') [54-66]
+          StringLiteral [71-83]
+            StringLiteralComponent('1979-12-31') [71-83]
diff --git a/parser/testdata/spanner/hash_functions.test b/parser/testdata/spanner/hash_functions.test
new file mode 100644
index 0000000..a84c672
--- /dev/null
+++ b/parser/testdata/spanner/hash_functions.test
@@ -0,0 +1,197 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/hash_functions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/hash_functions#farm_fingerprint
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+FARM_FINGERPRINT(value)
+--
+FunctionCall [0-23]
+  PathExpression [0-16]
+    Identifier(FARM_FINGERPRINT) [0-16]
+  PathExpression [17-22]
+    Identifier(value) [17-22]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/hash_functions#farm_fingerprint
+WITH example AS (
+  SELECT 1 AS x, "foo" AS y, true AS z UNION ALL
+  SELECT 2 AS x, "apple" AS y, false AS z UNION ALL
+  SELECT 3 AS x, "" AS y, true AS z
+)
+SELECT
+  *,
+  FARM_FINGERPRINT(CONCAT(CAST(x AS STRING), y, CAST(z AS STRING)))
+    AS row_fingerprint
+FROM example
+--
+QueryStatement [0-272]
+  Query [0-272]
+    WithClause [0-156]
+      WithClauseEntry [5-156]
+        AliasedQuery [5-156]
+          Identifier(example) [5-12]
+          Query [16-156]
+            SetOperation(UNION ALL) [20-154]
+              SetOperationMetadataList [57-118]
+                SetOperationMetadata [57-66]
+                  SetOperationType [57-62]
+                  SetOperationAllOrDistinct [63-66]
+                SetOperationMetadata [109-118]
+                  SetOperationType [109-114]
+                  SetOperationAllOrDistinct [115-118]
+              Select [20-56]
+                SelectList [27-56]
+                  SelectColumn [27-33]
+                    IntLiteral(1) [27-28]
+                    Alias [29-33]
+                      Identifier(x) [32-33]
+                  SelectColumn [35-45]
+                    StringLiteral [35-40]
+                      StringLiteralComponent("foo") [35-40]
+                    Alias [41-45]
+                      Identifier(y) [44-45]
+                  SelectColumn [47-56]
+                    BooleanLiteral(true) [47-51]
+                    Alias [52-56]
+                      Identifier(z) [55-56]
+              Select [69-108]
+                SelectList [76-108]
+                  SelectColumn [76-82]
+                    IntLiteral(2) [76-77]
+                    Alias [78-82]
+                      Identifier(x) [81-82]
+                  SelectColumn [84-96]
+                    StringLiteral [84-91]
+                      StringLiteralComponent("apple") [84-91]
+                    Alias [92-96]
+                      Identifier(y) [95-96]
+                  SelectColumn [98-108]
+                    BooleanLiteral(false) [98-103]
+                    Alias [104-108]
+                      Identifier(z) [107-108]
+              Select [121-154]
+                SelectList [128-154]
+                  SelectColumn [128-134]
+                    IntLiteral(3) [128-129]
+                    Alias [130-134]
+                      Identifier(x) [133-134]
+                  SelectColumn [136-143]
+                    StringLiteral [136-138]
+                      StringLiteralComponent("") [136-138]
+                    Alias [139-143]
+                      Identifier(y) [142-143]
+                  SelectColumn [145-154]
+                    BooleanLiteral(true) [145-149]
+                    Alias [150-154]
+                      Identifier(z) [153-154]
+    Select [157-272]
+      SelectList [166-259]
+        SelectColumn [166-167]
+          Star(*) [166-167]
+        SelectColumn [171-259]
+          FunctionCall [171-236]
+            PathExpression [171-187]
+              Identifier(FARM_FINGERPRINT) [171-187]
+            FunctionCall [188-235]
+              PathExpression [188-194]
+                Identifier(CONCAT) [188-194]
+              CastExpression [195-212]
+                PathExpression [200-201]
+                  Identifier(x) [200-201]
+                SimpleType [205-211]
+                  PathExpression [205-211]
+                    Identifier(STRING) [205-211]
+              PathExpression [214-215]
+                Identifier(y) [214-215]
+              CastExpression [217-234]
+                PathExpression [222-223]
+                  Identifier(z) [222-223]
+                SimpleType [227-233]
+                  PathExpression [227-233]
+                    Identifier(STRING) [227-233]
+          Alias [241-259]
+            Identifier(row_fingerprint) [244-259]
+      FromClause [260-272]
+        TablePathExpression [265-272]
+          PathExpression [265-272]
+            Identifier(example) [265-272]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/hash_functions#sha1
+[mode=expression]
+SHA1(input)
+--
+FunctionCall [0-11]
+  PathExpression [0-4]
+    Identifier(SHA1) [0-4]
+  PathExpression [5-10]
+    Identifier(input) [5-10]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/hash_functions#sha1
+SELECT SHA1("Hello World") as sha1
+--
+QueryStatement [0-34]
+  Query [0-34]
+    Select [0-34]
+      SelectList [7-34]
+        SelectColumn [7-34]
+          FunctionCall [7-26]
+            PathExpression [7-11]
+              Identifier(SHA1) [7-11]
+            StringLiteral [12-25]
+              StringLiteralComponent("Hello World") [12-25]
+          Alias [27-34]
+            Identifier(sha1) [30-34]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/hash_functions#sha256
+[mode=expression]
+SHA256(input)
+--
+FunctionCall [0-13]
+  PathExpression [0-6]
+    Identifier(SHA256) [0-6]
+  PathExpression [7-12]
+    Identifier(input) [7-12]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/hash_functions#sha256
+SELECT SHA256("Hello World") as sha256
+--
+QueryStatement [0-38]
+  Query [0-38]
+    Select [0-38]
+      SelectList [7-38]
+        SelectColumn [7-38]
+          FunctionCall [7-28]
+            PathExpression [7-13]
+              Identifier(SHA256) [7-13]
+            StringLiteral [14-27]
+              StringLiteralComponent("Hello World") [14-27]
+          Alias [29-38]
+            Identifier(sha256) [32-38]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/hash_functions#sha512
+[mode=expression]
+SHA512(input)
+--
+FunctionCall [0-13]
+  PathExpression [0-6]
+    Identifier(SHA512) [0-6]
+  PathExpression [7-12]
+    Identifier(input) [7-12]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/hash_functions#sha512
+SELECT SHA512("Hello World") as sha512
+--
+QueryStatement [0-38]
+  Query [0-38]
+    Select [0-38]
+      SelectList [7-38]
+        SelectColumn [7-38]
+          FunctionCall [7-28]
+            PathExpression [7-13]
+              Identifier(SHA512) [7-13]
+            StringLiteral [14-27]
+              StringLiteralComponent("Hello World") [14-27]
+          Alias [29-38]
+            Identifier(sha512) [32-38]
diff --git a/parser/testdata/spanner/interval_functions.test b/parser/testdata/spanner/interval_functions.test
new file mode 100644
index 0000000..21f7d27
--- /dev/null
+++ b/parser/testdata/spanner/interval_functions.test
@@ -0,0 +1,399 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/interval_functions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/interval_functions#extract
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+EXTRACT(part FROM interval_expression)
+--
+ExtractExpression [0-38]
+  PathExpression [8-12]
+    Identifier(part) [8-12]
+  PathExpression [18-37]
+    Identifier(interval_expression) [18-37]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/interval_functions#extract
+SELECT
+  EXTRACT(YEAR FROM i) AS year,
+  EXTRACT(MONTH FROM i) AS month,
+  EXTRACT(DAY FROM i) AS day,
+  EXTRACT(HOUR FROM i) AS hour,
+  EXTRACT(MINUTE FROM i) AS minute,
+  EXTRACT(SECOND FROM i) AS second,
+  EXTRACT(MILLISECOND FROM i) AS milli,
+  EXTRACT(MICROSECOND FROM i) AS micro
+FROM
+  UNNEST([INTERVAL '1-2 3 4:5:6.789999' YEAR TO SECOND,
+          INTERVAL '0-13 370 48:61:61' YEAR TO SECOND]) AS i
+--
+QueryStatement [0-407]
+  Query [0-407]
+    Select [0-407]
+      SelectList [9-285]
+        SelectColumn [9-37]
+          ExtractExpression [9-29]
+            PathExpression [17-21]
+              Identifier(YEAR) [17-21]
+            PathExpression [27-28]
+              Identifier(i) [27-28]
+          Alias [30-37]
+            Identifier(year) [33-37]
+        SelectColumn [41-71]
+          ExtractExpression [41-62]
+            PathExpression [49-54]
+              Identifier(MONTH) [49-54]
+            PathExpression [60-61]
+              Identifier(i) [60-61]
+          Alias [63-71]
+            Identifier(month) [66-71]
+        SelectColumn [75-101]
+          ExtractExpression [75-94]
+            PathExpression [83-86]
+              Identifier(DAY) [83-86]
+            PathExpression [92-93]
+              Identifier(i) [92-93]
+          Alias [95-101]
+            Identifier(day) [98-101]
+        SelectColumn [105-133]
+          ExtractExpression [105-125]
+            PathExpression [113-117]
+              Identifier(HOUR) [113-117]
+            PathExpression [123-124]
+              Identifier(i) [123-124]
+          Alias [126-133]
+            Identifier(hour) [129-133]
+        SelectColumn [137-169]
+          ExtractExpression [137-159]
+            PathExpression [145-151]
+              Identifier(MINUTE) [145-151]
+            PathExpression [157-158]
+              Identifier(i) [157-158]
+          Alias [160-169]
+            Identifier(minute) [163-169]
+        SelectColumn [173-205]
+          ExtractExpression [173-195]
+            PathExpression [181-187]
+              Identifier(SECOND) [181-187]
+            PathExpression [193-194]
+              Identifier(i) [193-194]
+          Alias [196-205]
+            Identifier(second) [199-205]
+        SelectColumn [209-245]
+          ExtractExpression [209-236]
+            PathExpression [217-228]
+              Identifier(MILLISECOND) [217-228]
+            PathExpression [234-235]
+              Identifier(i) [234-235]
+          Alias [237-245]
+            Identifier(milli) [240-245]
+        SelectColumn [249-285]
+          ExtractExpression [249-276]
+            PathExpression [257-268]
+              Identifier(MICROSECOND) [257-268]
+            PathExpression [274-275]
+              Identifier(i) [274-275]
+          Alias [277-285]
+            Identifier(micro) [280-285]
+      FromClause [286-407]
+        TablePathExpression [293-407]
+          UnnestExpression [293-402]
+            ExpressionWithOptAlias [300-401]
+              ArrayConstructor [300-401]
+                IntervalExpr [301-345]
+                  StringLiteral [310-330]
+                    StringLiteralComponent('1-2 3 4:5:6.789999') [310-330]
+                  Identifier(YEAR) [331-335]
+                  Identifier(SECOND) [339-345]
+                IntervalExpr [357-400]
+                  StringLiteral [366-385]
+                    StringLiteralComponent('0-13 370 48:61:61') [366-385]
+                  Identifier(YEAR) [386-390]
+                  Identifier(SECOND) [394-400]
+          Alias [403-407]
+            Identifier(i) [406-407]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/interval_functions#extract
+SELECT
+  EXTRACT(HOUR FROM i) AS hour,
+  EXTRACT(MINUTE FROM i) AS minute
+FROM
+  UNNEST([INTERVAL '10 -12:30' DAY TO MINUTE]) AS i
+--
+QueryStatement [0-130]
+  Query [0-130]
+    Select [0-130]
+      SelectList [9-73]
+        SelectColumn [9-37]
+          ExtractExpression [9-29]
+            PathExpression [17-21]
+              Identifier(HOUR) [17-21]
+            PathExpression [27-28]
+              Identifier(i) [27-28]
+          Alias [30-37]
+            Identifier(hour) [33-37]
+        SelectColumn [41-73]
+          ExtractExpression [41-63]
+            PathExpression [49-55]
+              Identifier(MINUTE) [49-55]
+            PathExpression [61-62]
+              Identifier(i) [61-62]
+          Alias [64-73]
+            Identifier(minute) [67-73]
+      FromClause [74-130]
+        TablePathExpression [81-130]
+          UnnestExpression [81-125]
+            ExpressionWithOptAlias [88-124]
+              ArrayConstructor [88-124]
+                IntervalExpr [89-123]
+                  StringLiteral [98-109]
+                    StringLiteralComponent('10 -12:30') [98-109]
+                  Identifier(DAY) [110-113]
+                  Identifier(MINUTE) [117-123]
+          Alias [126-130]
+            Identifier(i) [129-130]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/interval_functions#extract
+SELECT
+  EXTRACT(YEAR FROM i) AS year,
+  EXTRACT(MONTH FROM i) AS month
+FROM
+  UNNEST([INTERVAL '-22-6 10 -12:30' YEAR TO MINUTE]) AS i
+--
+QueryStatement [0-135]
+  Query [0-135]
+    Select [0-135]
+      SelectList [9-71]
+        SelectColumn [9-37]
+          ExtractExpression [9-29]
+            PathExpression [17-21]
+              Identifier(YEAR) [17-21]
+            PathExpression [27-28]
+              Identifier(i) [27-28]
+          Alias [30-37]
+            Identifier(year) [33-37]
+        SelectColumn [41-71]
+          ExtractExpression [41-62]
+            PathExpression [49-54]
+              Identifier(MONTH) [49-54]
+            PathExpression [60-61]
+              Identifier(i) [60-61]
+          Alias [63-71]
+            Identifier(month) [66-71]
+      FromClause [72-135]
+        TablePathExpression [79-135]
+          UnnestExpression [79-130]
+            ExpressionWithOptAlias [86-129]
+              ArrayConstructor [86-129]
+                IntervalExpr [87-128]
+                  StringLiteral [96-113]
+                    StringLiteralComponent('-22-6 10 -12:30') [96-113]
+                  Identifier(YEAR) [114-118]
+                  Identifier(MINUTE) [122-128]
+          Alias [131-135]
+            Identifier(i) [134-135]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/interval_functions#justify_days
+[mode=expression]
+JUSTIFY_DAYS(interval_expression)
+--
+FunctionCall [0-33]
+  PathExpression [0-12]
+    Identifier(JUSTIFY_DAYS) [0-12]
+  PathExpression [13-32]
+    Identifier(interval_expression) [13-32]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/interval_functions#justify_days
+SELECT
+  JUSTIFY_DAYS(INTERVAL 29 DAY) AS i1,
+  JUSTIFY_DAYS(INTERVAL -30 DAY) AS i2,
+  JUSTIFY_DAYS(INTERVAL 31 DAY) AS i3,
+  JUSTIFY_DAYS(INTERVAL -65 DAY) AS i4,
+  JUSTIFY_DAYS(INTERVAL 370 DAY) AS i5
+--
+QueryStatement [0-203]
+  Query [0-203]
+    Select [0-203]
+      SelectList [9-203]
+        SelectColumn [9-44]
+          FunctionCall [9-38]
+            PathExpression [9-21]
+              Identifier(JUSTIFY_DAYS) [9-21]
+            IntervalExpr [22-37]
+              IntLiteral(29) [31-33]
+              Identifier(DAY) [34-37]
+          Alias [39-44]
+            Identifier(i1) [42-44]
+        SelectColumn [48-84]
+          FunctionCall [48-78]
+            PathExpression [48-60]
+              Identifier(JUSTIFY_DAYS) [48-60]
+            IntervalExpr [61-77]
+              UnaryExpression(-) [70-73]
+                IntLiteral(30) [71-73]
+              Identifier(DAY) [74-77]
+          Alias [79-84]
+            Identifier(i2) [82-84]
+        SelectColumn [88-123]
+          FunctionCall [88-117]
+            PathExpression [88-100]
+              Identifier(JUSTIFY_DAYS) [88-100]
+            IntervalExpr [101-116]
+              IntLiteral(31) [110-112]
+              Identifier(DAY) [113-116]
+          Alias [118-123]
+            Identifier(i3) [121-123]
+        SelectColumn [127-163]
+          FunctionCall [127-157]
+            PathExpression [127-139]
+              Identifier(JUSTIFY_DAYS) [127-139]
+            IntervalExpr [140-156]
+              UnaryExpression(-) [149-152]
+                IntLiteral(65) [150-152]
+              Identifier(DAY) [153-156]
+          Alias [158-163]
+            Identifier(i4) [161-163]
+        SelectColumn [167-203]
+          FunctionCall [167-197]
+            PathExpression [167-179]
+              Identifier(JUSTIFY_DAYS) [167-179]
+            IntervalExpr [180-196]
+              IntLiteral(370) [189-192]
+              Identifier(DAY) [193-196]
+          Alias [198-203]
+            Identifier(i5) [201-203]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/interval_functions#justify_hours
+[mode=expression]
+JUSTIFY_HOURS(interval_expression)
+--
+FunctionCall [0-34]
+  PathExpression [0-13]
+    Identifier(JUSTIFY_HOURS) [0-13]
+  PathExpression [14-33]
+    Identifier(interval_expression) [14-33]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/interval_functions#justify_hours
+SELECT
+  JUSTIFY_HOURS(INTERVAL 23 HOUR) AS i1,
+  JUSTIFY_HOURS(INTERVAL -24 HOUR) AS i2,
+  JUSTIFY_HOURS(INTERVAL 47 HOUR) AS i3,
+  JUSTIFY_HOURS(INTERVAL -12345 MINUTE) AS i4
+--
+QueryStatement [0-176]
+  Query [0-176]
+    Select [0-176]
+      SelectList [9-176]
+        SelectColumn [9-46]
+          FunctionCall [9-40]
+            PathExpression [9-22]
+              Identifier(JUSTIFY_HOURS) [9-22]
+            IntervalExpr [23-39]
+              IntLiteral(23) [32-34]
+              Identifier(HOUR) [35-39]
+          Alias [41-46]
+            Identifier(i1) [44-46]
+        SelectColumn [50-88]
+          FunctionCall [50-82]
+            PathExpression [50-63]
+              Identifier(JUSTIFY_HOURS) [50-63]
+            IntervalExpr [64-81]
+              UnaryExpression(-) [73-76]
+                IntLiteral(24) [74-76]
+              Identifier(HOUR) [77-81]
+          Alias [83-88]
+            Identifier(i2) [86-88]
+        SelectColumn [92-129]
+          FunctionCall [92-123]
+            PathExpression [92-105]
+              Identifier(JUSTIFY_HOURS) [92-105]
+            IntervalExpr [106-122]
+              IntLiteral(47) [115-117]
+              Identifier(HOUR) [118-122]
+          Alias [124-129]
+            Identifier(i3) [127-129]
+        SelectColumn [133-176]
+          FunctionCall [133-170]
+            PathExpression [133-146]
+              Identifier(JUSTIFY_HOURS) [133-146]
+            IntervalExpr [147-169]
+              UnaryExpression(-) [156-162]
+                IntLiteral(12345) [157-162]
+              Identifier(MINUTE) [163-169]
+          Alias [171-176]
+            Identifier(i4) [174-176]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/interval_functions#justify_interval
+[mode=expression]
+JUSTIFY_INTERVAL(interval_expression)
+--
+FunctionCall [0-37]
+  PathExpression [0-16]
+    Identifier(JUSTIFY_INTERVAL) [0-16]
+  PathExpression [17-36]
+    Identifier(interval_expression) [17-36]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/interval_functions#justify_interval
+SELECT JUSTIFY_INTERVAL(INTERVAL '29 49:00:00' DAY TO SECOND) AS i
+--
+QueryStatement [0-66]
+  Query [0-66]
+    Select [0-66]
+      SelectList [7-66]
+        SelectColumn [7-66]
+          FunctionCall [7-61]
+            PathExpression [7-23]
+              Identifier(JUSTIFY_INTERVAL) [7-23]
+            IntervalExpr [24-60]
+              StringLiteral [33-46]
+                StringLiteralComponent('29 49:00:00') [33-46]
+              Identifier(DAY) [47-50]
+              Identifier(SECOND) [54-60]
+          Alias [62-66]
+            Identifier(i) [65-66]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/interval_functions#make_interval
+SELECT
+  MAKE_INTERVAL(1, 6, 15) AS i1,
+  MAKE_INTERVAL(hour => 10, second => 20) AS i2,
+  MAKE_INTERVAL(1, minute => 5, day => 2) AS i3
+--
+QueryStatement [0-136]
+  Query [0-136]
+    Select [0-136]
+      SelectList [9-136]
+        SelectColumn [9-38]
+          FunctionCall [9-32]
+            PathExpression [9-22]
+              Identifier(MAKE_INTERVAL) [9-22]
+            IntLiteral(1) [23-24]
+            IntLiteral(6) [26-27]
+            IntLiteral(15) [29-31]
+          Alias [33-38]
+            Identifier(i1) [36-38]
+        SelectColumn [42-87]
+          FunctionCall [42-81]
+            PathExpression [42-55]
+              Identifier(MAKE_INTERVAL) [42-55]
+            NamedArgument [56-66]
+              Identifier(hour) [56-60]
+              IntLiteral(10) [64-66]
+            NamedArgument [68-80]
+              Identifier(second) [68-74]
+              IntLiteral(20) [78-80]
+          Alias [82-87]
+            Identifier(i2) [85-87]
+        SelectColumn [91-136]
+          FunctionCall [91-130]
+            PathExpression [91-104]
+              Identifier(MAKE_INTERVAL) [91-104]
+            IntLiteral(1) [105-106]
+            NamedArgument [108-119]
+              Identifier(minute) [108-114]
+              IntLiteral(5) [118-119]
+            NamedArgument [121-129]
+              Identifier(day) [121-124]
+              IntLiteral(2) [128-129]
+          Alias [131-136]
+            Identifier(i3) [134-136]
diff --git a/parser/testdata/spanner/json_functions.test b/parser/testdata/spanner/json_functions.test
new file mode 100644
index 0000000..7682888
--- /dev/null
+++ b/parser/testdata/spanner/json_functions.test
@@ -0,0 +1,6403 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#bool_for_json
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+BOOL(json_expr)
+--
+FunctionCall [0-15]
+  PathExpression [0-4]
+    Identifier(BOOL) [0-4]
+  PathExpression [5-14]
+    Identifier(json_expr) [5-14]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#bool_for_json
+[mode=expression]
+JSON 'true'
+--
+JSONLiteral [0-11]
+  StringLiteral [5-11]
+    StringLiteralComponent('true') [5-11]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#bool_for_json
+SELECT BOOL(JSON 'true') AS vacancy
+--
+QueryStatement [0-35]
+  Query [0-35]
+    Select [0-35]
+      SelectList [7-35]
+        SelectColumn [7-35]
+          FunctionCall [7-24]
+            PathExpression [7-11]
+              Identifier(BOOL) [7-11]
+            JSONLiteral [12-23]
+              StringLiteral [17-23]
+                StringLiteralComponent('true') [17-23]
+          Alias [25-35]
+            Identifier(vacancy) [28-35]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#bool_for_json
+SELECT BOOL(JSON_QUERY(JSON '{"hotel class": "5-star", "vacancy": true}', "$.vacancy")) AS vacancy
+--
+QueryStatement [0-98]
+  Query [0-98]
+    Select [0-98]
+      SelectList [7-98]
+        SelectColumn [7-98]
+          FunctionCall [7-87]
+            PathExpression [7-11]
+              Identifier(BOOL) [7-11]
+            FunctionCall [12-86]
+              PathExpression [12-22]
+                Identifier(JSON_QUERY) [12-22]
+              JSONLiteral [23-72]
+                StringLiteral [28-72]
+                  StringLiteralComponent('{"hotel class": "5-star", "vacancy": true}') [28-72]
+              StringLiteral [74-85]
+                StringLiteralComponent("$.vacancy") [74-85]
+          Alias [88-98]
+            Identifier(vacancy) [91-98]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#bool_for_json
+-- An error is thrown if JSON isn't of type bool.
+SELECT BOOL(JSON '123') AS result
+--
+QueryStatement [50-83]
+  Query [50-83]
+    Select [50-83]
+      SelectList [57-83]
+        SelectColumn [57-83]
+          FunctionCall [57-73]
+            PathExpression [57-61]
+              Identifier(BOOL) [57-61]
+            JSONLiteral [62-72]
+              StringLiteral [67-72]
+                StringLiteralComponent('123') [67-72]
+          Alias [74-83]
+            Identifier(result) [77-83]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#bool_for_json
+-- Throws an error
+SELECT BOOL(JSON 'null') AS result
+--
+QueryStatement [19-53]
+  Query [19-53]
+    Select [19-53]
+      SelectList [26-53]
+        SelectColumn [26-53]
+          FunctionCall [26-43]
+            PathExpression [26-30]
+              Identifier(BOOL) [26-30]
+            JSONLiteral [31-42]
+              StringLiteral [36-42]
+                StringLiteralComponent('null') [36-42]
+          Alias [44-53]
+            Identifier(result) [47-53]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#bool_for_json
+-- Throws an error
+SELECT SAFE.BOOL(JSON '123') AS result
+--
+QueryStatement [19-57]
+  Query [19-57]
+    Select [19-57]
+      SelectList [26-57]
+        SelectColumn [26-57]
+          FunctionCall [26-47]
+            PathExpression [26-35]
+              Identifier(SAFE) [26-30]
+              Identifier(BOOL) [31-35]
+            JSONLiteral [36-46]
+              StringLiteral [41-46]
+                StringLiteralComponent('123') [41-46]
+          Alias [48-57]
+            Identifier(result) [51-57]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#bool_array_for_json
+[mode=expression]
+BOOL_ARRAY(json_expr)
+--
+FunctionCall [0-21]
+  PathExpression [0-10]
+    Identifier(BOOL_ARRAY) [0-10]
+  PathExpression [11-20]
+    Identifier(json_expr) [11-20]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#bool_array_for_json
+[mode=expression]
+JSON '[true]'
+--
+JSONLiteral [0-13]
+  StringLiteral [5-13]
+    StringLiteralComponent('[true]') [5-13]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#bool_array_for_json
+SELECT BOOL_ARRAY(JSON '[true, false]') AS vacancies
+--
+QueryStatement [0-52]
+  Query [0-52]
+    Select [0-52]
+      SelectList [7-52]
+        SelectColumn [7-52]
+          FunctionCall [7-39]
+            PathExpression [7-17]
+              Identifier(BOOL_ARRAY) [7-17]
+            JSONLiteral [18-38]
+              StringLiteral [23-38]
+                StringLiteralComponent('[true, false]') [23-38]
+          Alias [40-52]
+            Identifier(vacancies) [43-52]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#bool_array_for_json
+-- An error is thrown if the JSON isn't an array of booleans.
+SELECT BOOL_ARRAY(JSON '[123]') AS result
+--
+QueryStatement [62-103]
+  Query [62-103]
+    Select [62-103]
+      SelectList [69-103]
+        SelectColumn [69-103]
+          FunctionCall [69-93]
+            PathExpression [69-79]
+              Identifier(BOOL_ARRAY) [69-79]
+            JSONLiteral [80-92]
+              StringLiteral [85-92]
+                StringLiteralComponent('[123]') [85-92]
+          Alias [94-103]
+            Identifier(result) [97-103]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#bool_array_for_json
+-- Throws an error
+SELECT BOOL_ARRAY(JSON '[null]') AS result
+--
+QueryStatement [19-61]
+  Query [19-61]
+    Select [19-61]
+      SelectList [26-61]
+        SelectColumn [26-61]
+          FunctionCall [26-51]
+            PathExpression [26-36]
+              Identifier(BOOL_ARRAY) [26-36]
+            JSONLiteral [37-50]
+              StringLiteral [42-50]
+                StringLiteralComponent('[null]') [42-50]
+          Alias [52-61]
+            Identifier(result) [55-61]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#bool_array_for_json
+-- Throws an error
+SELECT BOOL_ARRAY(JSON 'null') AS result
+--
+QueryStatement [19-59]
+  Query [19-59]
+    Select [19-59]
+      SelectList [26-59]
+        SelectColumn [26-59]
+          FunctionCall [26-49]
+            PathExpression [26-36]
+              Identifier(BOOL_ARRAY) [26-36]
+            JSONLiteral [37-48]
+              StringLiteral [42-48]
+                StringLiteralComponent('null') [42-48]
+          Alias [50-59]
+            Identifier(result) [53-59]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#double_for_json
+[mode=expression]
+JSON '9.8'
+--
+JSONLiteral [0-10]
+  StringLiteral [5-10]
+    StringLiteralComponent('9.8') [5-10]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#double_for_json
+SELECT FLOAT64(JSON '9.8') AS velocity
+--
+QueryStatement [0-38]
+  Query [0-38]
+    Select [0-38]
+      SelectList [7-38]
+        SelectColumn [7-38]
+          FunctionCall [7-26]
+            PathExpression [7-14]
+              Identifier(FLOAT64) [7-14]
+            JSONLiteral [15-25]
+              StringLiteral [20-25]
+                StringLiteralComponent('9.8') [20-25]
+          Alias [27-38]
+            Identifier(velocity) [30-38]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#double_for_json
+SELECT FLOAT64(JSON_QUERY(JSON '{"vo2_max": 39.1, "age": 18}', "$.vo2_max")) AS vo2_max
+--
+QueryStatement [0-87]
+  Query [0-87]
+    Select [0-87]
+      SelectList [7-87]
+        SelectColumn [7-87]
+          FunctionCall [7-76]
+            PathExpression [7-14]
+              Identifier(FLOAT64) [7-14]
+            FunctionCall [15-75]
+              PathExpression [15-25]
+                Identifier(JSON_QUERY) [15-25]
+              JSONLiteral [26-61]
+                StringLiteral [31-61]
+                  StringLiteralComponent('{"vo2_max": 39.1, "age": 18}') [31-61]
+              StringLiteral [63-74]
+                StringLiteralComponent("$.vo2_max") [63-74]
+          Alias [77-87]
+            Identifier(vo2_max) [80-87]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#double_for_json
+SELECT FLOAT64(JSON '18446744073709551615', wide_number_mode=>'round') as result
+--
+QueryStatement [0-80]
+  Query [0-80]
+    Select [0-80]
+      SelectList [7-80]
+        SelectColumn [7-80]
+          FunctionCall [7-70]
+            PathExpression [7-14]
+              Identifier(FLOAT64) [7-14]
+            JSONLiteral [15-42]
+              StringLiteral [20-42]
+                StringLiteralComponent('18446744073709551615') [20-42]
+            NamedArgument [44-69]
+              Identifier(wide_number_mode) [44-60]
+              StringLiteral [62-69]
+                StringLiteralComponent('round') [62-69]
+          Alias [71-80]
+            Identifier(result) [74-80]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#double_for_json
+SELECT FLOAT64(JSON '18446744073709551615') as result
+--
+QueryStatement [0-53]
+  Query [0-53]
+    Select [0-53]
+      SelectList [7-53]
+        SelectColumn [7-53]
+          FunctionCall [7-43]
+            PathExpression [7-14]
+              Identifier(FLOAT64) [7-14]
+            JSONLiteral [15-42]
+              StringLiteral [20-42]
+                StringLiteralComponent('18446744073709551615') [20-42]
+          Alias [44-53]
+            Identifier(result) [47-53]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#double_for_json
+-- An error is thrown if JSON isn't of type FLOAT64.
+SELECT FLOAT64(JSON '"strawberry"') AS result
+--
+QueryStatement [53-98]
+  Query [53-98]
+    Select [53-98]
+      SelectList [60-98]
+        SelectColumn [60-98]
+          FunctionCall [60-88]
+            PathExpression [60-67]
+              Identifier(FLOAT64) [60-67]
+            JSONLiteral [68-87]
+              StringLiteral [73-87]
+                StringLiteralComponent('"strawberry"') [73-87]
+          Alias [89-98]
+            Identifier(result) [92-98]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#double_for_json
+SELECT FLOAT64(JSON 'null') AS result
+--
+QueryStatement [0-37]
+  Query [0-37]
+    Select [0-37]
+      SelectList [7-37]
+        SelectColumn [7-37]
+          FunctionCall [7-27]
+            PathExpression [7-14]
+              Identifier(FLOAT64) [7-14]
+            JSONLiteral [15-26]
+              StringLiteral [20-26]
+                StringLiteralComponent('null') [20-26]
+          Alias [28-37]
+            Identifier(result) [31-37]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#double_for_json
+-- An error is thrown because `wide_number_mode` is case-sensitive and not "exact" or "round".
+SELECT FLOAT64(JSON '123.4', wide_number_mode=>'EXACT') as result
+--
+QueryStatement [95-160]
+  Query [95-160]
+    Select [95-160]
+      SelectList [102-160]
+        SelectColumn [102-160]
+          FunctionCall [102-150]
+            PathExpression [102-109]
+              Identifier(FLOAT64) [102-109]
+            JSONLiteral [110-122]
+              StringLiteral [115-122]
+                StringLiteralComponent('123.4') [115-122]
+            NamedArgument [124-149]
+              Identifier(wide_number_mode) [124-140]
+              StringLiteral [142-149]
+                StringLiteralComponent('EXACT') [142-149]
+          Alias [151-160]
+            Identifier(result) [154-160]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#double_for_json
+SELECT FLOAT64(JSON '123.4', wide_number_mode=>'exac') as result
+--
+QueryStatement [0-64]
+  Query [0-64]
+    Select [0-64]
+      SelectList [7-64]
+        SelectColumn [7-64]
+          FunctionCall [7-54]
+            PathExpression [7-14]
+              Identifier(FLOAT64) [7-14]
+            JSONLiteral [15-27]
+              StringLiteral [20-27]
+                StringLiteralComponent('123.4') [20-27]
+            NamedArgument [29-53]
+              Identifier(wide_number_mode) [29-45]
+              StringLiteral [47-53]
+                StringLiteralComponent('exac') [47-53]
+          Alias [55-64]
+            Identifier(result) [58-64]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#double_for_json
+-- An error is thrown because the number can't be converted to DOUBLE without loss of precision
+SELECT FLOAT64(JSON '18446744073709551615', wide_number_mode=>'exact') as result
+--
+QueryStatement [96-176]
+  Query [96-176]
+    Select [96-176]
+      SelectList [103-176]
+        SelectColumn [103-176]
+          FunctionCall [103-166]
+            PathExpression [103-110]
+              Identifier(FLOAT64) [103-110]
+            JSONLiteral [111-138]
+              StringLiteral [116-138]
+                StringLiteralComponent('18446744073709551615') [116-138]
+            NamedArgument [140-165]
+              Identifier(wide_number_mode) [140-156]
+              StringLiteral [158-165]
+                StringLiteralComponent('exact') [158-165]
+          Alias [167-176]
+            Identifier(result) [170-176]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#double_for_json
+-- Returns a SQL NULL
+SELECT SAFE.FLOAT64(JSON '"strawberry"') AS result
+--
+QueryStatement [22-72]
+  Query [22-72]
+    Select [22-72]
+      SelectList [29-72]
+        SelectColumn [29-72]
+          FunctionCall [29-62]
+            PathExpression [29-41]
+              Identifier(SAFE) [29-33]
+              Identifier(FLOAT64) [34-41]
+            JSONLiteral [42-61]
+              StringLiteral [47-61]
+                StringLiteralComponent('"strawberry"') [47-61]
+          Alias [63-72]
+            Identifier(result) [66-72]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#double_array_for_json
+[mode=expression]
+JSON '[9.8]'
+--
+JSONLiteral [0-12]
+  StringLiteral [5-12]
+    StringLiteralComponent('[9.8]') [5-12]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#double_array_for_json
+SELECT FLOAT64_ARRAY(JSON '[9, 9.8]') AS velocities
+--
+QueryStatement [0-51]
+  Query [0-51]
+    Select [0-51]
+      SelectList [7-51]
+        SelectColumn [7-51]
+          FunctionCall [7-37]
+            PathExpression [7-20]
+              Identifier(FLOAT64_ARRAY) [7-20]
+            JSONLiteral [21-36]
+              StringLiteral [26-36]
+                StringLiteralComponent('[9, 9.8]') [26-36]
+          Alias [38-51]
+            Identifier(velocities) [41-51]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#double_array_for_json
+SELECT FLOAT64_ARRAY(JSON '[18446744073709551615]', wide_number_mode=>'round') as result
+--
+QueryStatement [0-88]
+  Query [0-88]
+    Select [0-88]
+      SelectList [7-88]
+        SelectColumn [7-88]
+          FunctionCall [7-78]
+            PathExpression [7-20]
+              Identifier(FLOAT64_ARRAY) [7-20]
+            JSONLiteral [21-50]
+              StringLiteral [26-50]
+                StringLiteralComponent('[18446744073709551615]') [26-50]
+            NamedArgument [52-77]
+              Identifier(wide_number_mode) [52-68]
+              StringLiteral [70-77]
+                StringLiteralComponent('round') [70-77]
+          Alias [79-88]
+            Identifier(result) [82-88]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#double_array_for_json
+SELECT FLOAT64_ARRAY(JSON '[18446744073709551615]') as result
+--
+QueryStatement [0-61]
+  Query [0-61]
+    Select [0-61]
+      SelectList [7-61]
+        SelectColumn [7-61]
+          FunctionCall [7-51]
+            PathExpression [7-20]
+              Identifier(FLOAT64_ARRAY) [7-20]
+            JSONLiteral [21-50]
+              StringLiteral [26-50]
+                StringLiteralComponent('[18446744073709551615]') [26-50]
+          Alias [52-61]
+            Identifier(result) [55-61]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#double_array_for_json
+-- An error is thrown if the JSON isn't an array of numbers.
+SELECT FLOAT64_ARRAY(JSON '["strawberry"]') AS result
+--
+QueryStatement [61-114]
+  Query [61-114]
+    Select [61-114]
+      SelectList [68-114]
+        SelectColumn [68-114]
+          FunctionCall [68-104]
+            PathExpression [68-81]
+              Identifier(FLOAT64_ARRAY) [68-81]
+            JSONLiteral [82-103]
+              StringLiteral [87-103]
+                StringLiteralComponent('["strawberry"]') [87-103]
+          Alias [105-114]
+            Identifier(result) [108-114]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#double_array_for_json
+SELECT FLOAT64_ARRAY(JSON '[null]') AS result
+--
+QueryStatement [0-45]
+  Query [0-45]
+    Select [0-45]
+      SelectList [7-45]
+        SelectColumn [7-45]
+          FunctionCall [7-35]
+            PathExpression [7-20]
+              Identifier(FLOAT64_ARRAY) [7-20]
+            JSONLiteral [21-34]
+              StringLiteral [26-34]
+                StringLiteralComponent('[null]') [26-34]
+          Alias [36-45]
+            Identifier(result) [39-45]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#double_array_for_json
+SELECT FLOAT64_ARRAY(JSON 'null') AS result
+--
+QueryStatement [0-43]
+  Query [0-43]
+    Select [0-43]
+      SelectList [7-43]
+        SelectColumn [7-43]
+          FunctionCall [7-33]
+            PathExpression [7-20]
+              Identifier(FLOAT64_ARRAY) [7-20]
+            JSONLiteral [21-32]
+              StringLiteral [26-32]
+                StringLiteralComponent('null') [26-32]
+          Alias [34-43]
+            Identifier(result) [37-43]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#double_array_for_json
+-- An error is thrown because `wide_number_mode` is case-sensitive and not "exact" or "round".
+SELECT FLOAT64_ARRAY(JSON '[123.4]', wide_number_mode=>'EXACT') as result
+--
+QueryStatement [95-168]
+  Query [95-168]
+    Select [95-168]
+      SelectList [102-168]
+        SelectColumn [102-168]
+          FunctionCall [102-158]
+            PathExpression [102-115]
+              Identifier(FLOAT64_ARRAY) [102-115]
+            JSONLiteral [116-130]
+              StringLiteral [121-130]
+                StringLiteralComponent('[123.4]') [121-130]
+            NamedArgument [132-157]
+              Identifier(wide_number_mode) [132-148]
+              StringLiteral [150-157]
+                StringLiteralComponent('EXACT') [150-157]
+          Alias [159-168]
+            Identifier(result) [162-168]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#double_array_for_json
+SELECT FLOAT64_ARRAY(JSON '[123.4]', wide_number_mode=>'exac') as result
+--
+QueryStatement [0-72]
+  Query [0-72]
+    Select [0-72]
+      SelectList [7-72]
+        SelectColumn [7-72]
+          FunctionCall [7-62]
+            PathExpression [7-20]
+              Identifier(FLOAT64_ARRAY) [7-20]
+            JSONLiteral [21-35]
+              StringLiteral [26-35]
+                StringLiteralComponent('[123.4]') [26-35]
+            NamedArgument [37-61]
+              Identifier(wide_number_mode) [37-53]
+              StringLiteral [55-61]
+                StringLiteralComponent('exac') [55-61]
+          Alias [63-72]
+            Identifier(result) [66-72]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#double_array_for_json
+-- An error is thrown because the number can't be converted to DOUBLE without loss of precision
+SELECT FLOAT64_ARRAY(JSON '[18446744073709551615]', wide_number_mode=>'exact') as result
+--
+QueryStatement [96-184]
+  Query [96-184]
+    Select [96-184]
+      SelectList [103-184]
+        SelectColumn [103-184]
+          FunctionCall [103-174]
+            PathExpression [103-116]
+              Identifier(FLOAT64_ARRAY) [103-116]
+            JSONLiteral [117-146]
+              StringLiteral [122-146]
+                StringLiteralComponent('[18446744073709551615]') [122-146]
+            NamedArgument [148-173]
+              Identifier(wide_number_mode) [148-164]
+              StringLiteral [166-173]
+                StringLiteralComponent('exact') [166-173]
+          Alias [175-184]
+            Identifier(result) [178-184]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#float_for_json
+SELECT FLOAT32(JSON '9.8') AS velocity
+--
+QueryStatement [0-38]
+  Query [0-38]
+    Select [0-38]
+      SelectList [7-38]
+        SelectColumn [7-38]
+          FunctionCall [7-26]
+            PathExpression [7-14]
+              Identifier(FLOAT32) [7-14]
+            JSONLiteral [15-25]
+              StringLiteral [20-25]
+                StringLiteralComponent('9.8') [20-25]
+          Alias [27-38]
+            Identifier(velocity) [30-38]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#float_for_json
+SELECT FLOAT32(JSON_QUERY(JSON '{"vo2_max": 39.1, "age": 18}', "$.vo2_max")) AS vo2_max
+--
+QueryStatement [0-87]
+  Query [0-87]
+    Select [0-87]
+      SelectList [7-87]
+        SelectColumn [7-87]
+          FunctionCall [7-76]
+            PathExpression [7-14]
+              Identifier(FLOAT32) [7-14]
+            FunctionCall [15-75]
+              PathExpression [15-25]
+                Identifier(JSON_QUERY) [15-25]
+              JSONLiteral [26-61]
+                StringLiteral [31-61]
+                  StringLiteralComponent('{"vo2_max": 39.1, "age": 18}') [31-61]
+              StringLiteral [63-74]
+                StringLiteralComponent("$.vo2_max") [63-74]
+          Alias [77-87]
+            Identifier(vo2_max) [80-87]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#float_for_json
+SELECT FLOAT32(JSON '16777217', wide_number_mode=>'round') as result
+--
+QueryStatement [0-68]
+  Query [0-68]
+    Select [0-68]
+      SelectList [7-68]
+        SelectColumn [7-68]
+          FunctionCall [7-58]
+            PathExpression [7-14]
+              Identifier(FLOAT32) [7-14]
+            JSONLiteral [15-30]
+              StringLiteral [20-30]
+                StringLiteralComponent('16777217') [20-30]
+            NamedArgument [32-57]
+              Identifier(wide_number_mode) [32-48]
+              StringLiteral [50-57]
+                StringLiteralComponent('round') [50-57]
+          Alias [59-68]
+            Identifier(result) [62-68]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#float_for_json
+SELECT FLOAT32(JSON '16777216') as result
+--
+QueryStatement [0-41]
+  Query [0-41]
+    Select [0-41]
+      SelectList [7-41]
+        SelectColumn [7-41]
+          FunctionCall [7-31]
+            PathExpression [7-14]
+              Identifier(FLOAT32) [7-14]
+            JSONLiteral [15-30]
+              StringLiteral [20-30]
+                StringLiteralComponent('16777216') [20-30]
+          Alias [32-41]
+            Identifier(result) [35-41]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#float_for_json
+-- An error is thrown if JSON isn't of type FLOAT32.
+SELECT FLOAT32(JSON '"strawberry"') AS result
+--
+QueryStatement [53-98]
+  Query [53-98]
+    Select [53-98]
+      SelectList [60-98]
+        SelectColumn [60-98]
+          FunctionCall [60-88]
+            PathExpression [60-67]
+              Identifier(FLOAT32) [60-67]
+            JSONLiteral [68-87]
+              StringLiteral [73-87]
+                StringLiteralComponent('"strawberry"') [73-87]
+          Alias [89-98]
+            Identifier(result) [92-98]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#float_for_json
+SELECT FLOAT32(JSON 'null') AS result
+--
+QueryStatement [0-37]
+  Query [0-37]
+    Select [0-37]
+      SelectList [7-37]
+        SelectColumn [7-37]
+          FunctionCall [7-27]
+            PathExpression [7-14]
+              Identifier(FLOAT32) [7-14]
+            JSONLiteral [15-26]
+              StringLiteral [20-26]
+                StringLiteralComponent('null') [20-26]
+          Alias [28-37]
+            Identifier(result) [31-37]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#float_for_json
+-- An error is thrown because `wide_number_mode` is case-sensitive and not "exact" or "round".
+SELECT FLOAT32(JSON '123.4', wide_number_mode=>'EXACT') as result
+--
+QueryStatement [95-160]
+  Query [95-160]
+    Select [95-160]
+      SelectList [102-160]
+        SelectColumn [102-160]
+          FunctionCall [102-150]
+            PathExpression [102-109]
+              Identifier(FLOAT32) [102-109]
+            JSONLiteral [110-122]
+              StringLiteral [115-122]
+                StringLiteralComponent('123.4') [115-122]
+            NamedArgument [124-149]
+              Identifier(wide_number_mode) [124-140]
+              StringLiteral [142-149]
+                StringLiteralComponent('EXACT') [142-149]
+          Alias [151-160]
+            Identifier(result) [154-160]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#float_for_json
+SELECT FLOAT32(JSON '123.4', wide_number_mode=>'exac') as result
+--
+QueryStatement [0-64]
+  Query [0-64]
+    Select [0-64]
+      SelectList [7-64]
+        SelectColumn [7-64]
+          FunctionCall [7-54]
+            PathExpression [7-14]
+              Identifier(FLOAT32) [7-14]
+            JSONLiteral [15-27]
+              StringLiteral [20-27]
+                StringLiteralComponent('123.4') [20-27]
+            NamedArgument [29-53]
+              Identifier(wide_number_mode) [29-45]
+              StringLiteral [47-53]
+                StringLiteralComponent('exac') [47-53]
+          Alias [55-64]
+            Identifier(result) [58-64]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#float_for_json
+-- An error is thrown because the number can't be converted to FLOAT without loss of precision
+SELECT FLOAT32(JSON '16777217', wide_number_mode=>'exact') as result
+--
+QueryStatement [95-163]
+  Query [95-163]
+    Select [95-163]
+      SelectList [102-163]
+        SelectColumn [102-163]
+          FunctionCall [102-153]
+            PathExpression [102-109]
+              Identifier(FLOAT32) [102-109]
+            JSONLiteral [110-125]
+              StringLiteral [115-125]
+                StringLiteralComponent('16777217') [115-125]
+            NamedArgument [127-152]
+              Identifier(wide_number_mode) [127-143]
+              StringLiteral [145-152]
+                StringLiteralComponent('exact') [145-152]
+          Alias [154-163]
+            Identifier(result) [157-163]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#float_for_json
+-- Returns a SQL NULL
+SELECT SAFE.FLOAT32(JSON '"strawberry"') AS result
+--
+QueryStatement [22-72]
+  Query [22-72]
+    Select [22-72]
+      SelectList [29-72]
+        SelectColumn [29-72]
+          FunctionCall [29-62]
+            PathExpression [29-41]
+              Identifier(SAFE) [29-33]
+              Identifier(FLOAT32) [34-41]
+            JSONLiteral [42-61]
+              StringLiteral [47-61]
+                StringLiteralComponent('"strawberry"') [47-61]
+          Alias [63-72]
+            Identifier(result) [66-72]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#float_array_for_json
+SELECT FLOAT32_ARRAY(JSON '[9, 9.8]') AS velocities
+--
+QueryStatement [0-51]
+  Query [0-51]
+    Select [0-51]
+      SelectList [7-51]
+        SelectColumn [7-51]
+          FunctionCall [7-37]
+            PathExpression [7-20]
+              Identifier(FLOAT32_ARRAY) [7-20]
+            JSONLiteral [21-36]
+              StringLiteral [26-36]
+                StringLiteralComponent('[9, 9.8]') [26-36]
+          Alias [38-51]
+            Identifier(velocities) [41-51]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#float_array_for_json
+SELECT FLOAT32_ARRAY(JSON '[16777217]', wide_number_mode=>'round') as result
+--
+QueryStatement [0-76]
+  Query [0-76]
+    Select [0-76]
+      SelectList [7-76]
+        SelectColumn [7-76]
+          FunctionCall [7-66]
+            PathExpression [7-20]
+              Identifier(FLOAT32_ARRAY) [7-20]
+            JSONLiteral [21-38]
+              StringLiteral [26-38]
+                StringLiteralComponent('[16777217]') [26-38]
+            NamedArgument [40-65]
+              Identifier(wide_number_mode) [40-56]
+              StringLiteral [58-65]
+                StringLiteralComponent('round') [58-65]
+          Alias [67-76]
+            Identifier(result) [70-76]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#float_array_for_json
+SELECT FLOAT32_ARRAY(JSON '[16777216]') as result
+--
+QueryStatement [0-49]
+  Query [0-49]
+    Select [0-49]
+      SelectList [7-49]
+        SelectColumn [7-49]
+          FunctionCall [7-39]
+            PathExpression [7-20]
+              Identifier(FLOAT32_ARRAY) [7-20]
+            JSONLiteral [21-38]
+              StringLiteral [26-38]
+                StringLiteralComponent('[16777216]') [26-38]
+          Alias [40-49]
+            Identifier(result) [43-49]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#float_array_for_json
+-- An error is thrown if the JSON isn't an array of numbers in FLOAT32 domain.
+SELECT FLOAT32_ARRAY(JSON '["strawberry"]') AS result
+--
+QueryStatement [79-132]
+  Query [79-132]
+    Select [79-132]
+      SelectList [86-132]
+        SelectColumn [86-132]
+          FunctionCall [86-122]
+            PathExpression [86-99]
+              Identifier(FLOAT32_ARRAY) [86-99]
+            JSONLiteral [100-121]
+              StringLiteral [105-121]
+                StringLiteralComponent('["strawberry"]') [105-121]
+          Alias [123-132]
+            Identifier(result) [126-132]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#float_array_for_json
+SELECT FLOAT32_ARRAY(JSON '[null]') AS result
+--
+QueryStatement [0-45]
+  Query [0-45]
+    Select [0-45]
+      SelectList [7-45]
+        SelectColumn [7-45]
+          FunctionCall [7-35]
+            PathExpression [7-20]
+              Identifier(FLOAT32_ARRAY) [7-20]
+            JSONLiteral [21-34]
+              StringLiteral [26-34]
+                StringLiteralComponent('[null]') [26-34]
+          Alias [36-45]
+            Identifier(result) [39-45]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#float_array_for_json
+SELECT FLOAT32_ARRAY(JSON 'null') AS result
+--
+QueryStatement [0-43]
+  Query [0-43]
+    Select [0-43]
+      SelectList [7-43]
+        SelectColumn [7-43]
+          FunctionCall [7-33]
+            PathExpression [7-20]
+              Identifier(FLOAT32_ARRAY) [7-20]
+            JSONLiteral [21-32]
+              StringLiteral [26-32]
+                StringLiteralComponent('null') [26-32]
+          Alias [34-43]
+            Identifier(result) [37-43]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#float_array_for_json
+-- An error is thrown because `wide_number_mode` is case-sensitive and not "exact" or "round".
+SELECT FLOAT32_ARRAY(JSON '[123.4]', wide_number_mode=>'EXACT') as result
+--
+QueryStatement [95-168]
+  Query [95-168]
+    Select [95-168]
+      SelectList [102-168]
+        SelectColumn [102-168]
+          FunctionCall [102-158]
+            PathExpression [102-115]
+              Identifier(FLOAT32_ARRAY) [102-115]
+            JSONLiteral [116-130]
+              StringLiteral [121-130]
+                StringLiteralComponent('[123.4]') [121-130]
+            NamedArgument [132-157]
+              Identifier(wide_number_mode) [132-148]
+              StringLiteral [150-157]
+                StringLiteralComponent('EXACT') [150-157]
+          Alias [159-168]
+            Identifier(result) [162-168]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#float_array_for_json
+SELECT FLOAT32_ARRAY(JSON '[123.4]', wide_number_mode=>'exac') as result
+--
+QueryStatement [0-72]
+  Query [0-72]
+    Select [0-72]
+      SelectList [7-72]
+        SelectColumn [7-72]
+          FunctionCall [7-62]
+            PathExpression [7-20]
+              Identifier(FLOAT32_ARRAY) [7-20]
+            JSONLiteral [21-35]
+              StringLiteral [26-35]
+                StringLiteralComponent('[123.4]') [26-35]
+            NamedArgument [37-61]
+              Identifier(wide_number_mode) [37-53]
+              StringLiteral [55-61]
+                StringLiteralComponent('exac') [55-61]
+          Alias [63-72]
+            Identifier(result) [66-72]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#float_array_for_json
+-- An error is thrown because the number can't be converted to FLOAT without loss of precision
+SELECT FLOAT32_ARRAY(JSON '[16777217]', wide_number_mode=>'exact') as result
+--
+QueryStatement [95-171]
+  Query [95-171]
+    Select [95-171]
+      SelectList [102-171]
+        SelectColumn [102-171]
+          FunctionCall [102-161]
+            PathExpression [102-115]
+              Identifier(FLOAT32_ARRAY) [102-115]
+            JSONLiteral [116-133]
+              StringLiteral [121-133]
+                StringLiteralComponent('[16777217]') [121-133]
+            NamedArgument [135-160]
+              Identifier(wide_number_mode) [135-151]
+              StringLiteral [153-160]
+                StringLiteralComponent('exact') [153-160]
+          Alias [162-171]
+            Identifier(result) [165-171]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#int64_for_json
+[mode=expression]
+INT64(json_expr)
+--
+FunctionCall [0-16]
+  PathExpression [0-5]
+    Identifier(INT64) [0-5]
+  PathExpression [6-15]
+    Identifier(json_expr) [6-15]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#int64_for_json
+[mode=expression]
+JSON '999'
+--
+JSONLiteral [0-10]
+  StringLiteral [5-10]
+    StringLiteralComponent('999') [5-10]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#int64_for_json
+SELECT INT64(JSON '2005') AS flight_number
+--
+QueryStatement [0-42]
+  Query [0-42]
+    Select [0-42]
+      SelectList [7-42]
+        SelectColumn [7-42]
+          FunctionCall [7-25]
+            PathExpression [7-12]
+              Identifier(INT64) [7-12]
+            JSONLiteral [13-24]
+              StringLiteral [18-24]
+                StringLiteralComponent('2005') [18-24]
+          Alias [26-42]
+            Identifier(flight_number) [29-42]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#int64_for_json
+SELECT INT64(JSON_QUERY(JSON '{"gate": "A4", "flight_number": 2005}', "$.flight_number")) AS flight_number
+--
+QueryStatement [0-106]
+  Query [0-106]
+    Select [0-106]
+      SelectList [7-106]
+        SelectColumn [7-106]
+          FunctionCall [7-89]
+            PathExpression [7-12]
+              Identifier(INT64) [7-12]
+            FunctionCall [13-88]
+              PathExpression [13-23]
+                Identifier(JSON_QUERY) [13-23]
+              JSONLiteral [24-68]
+                StringLiteral [29-68]
+                  StringLiteralComponent('{"gate": "A4", "flight_number": 2005}') [29-68]
+              StringLiteral [70-87]
+                StringLiteralComponent("$.flight_number") [70-87]
+          Alias [90-106]
+            Identifier(flight_number) [93-106]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#int64_for_json
+SELECT INT64(JSON '10.0') AS score
+--
+QueryStatement [0-34]
+  Query [0-34]
+    Select [0-34]
+      SelectList [7-34]
+        SelectColumn [7-34]
+          FunctionCall [7-25]
+            PathExpression [7-12]
+              Identifier(INT64) [7-12]
+            JSONLiteral [13-24]
+              StringLiteral [18-24]
+                StringLiteralComponent('10.0') [18-24]
+          Alias [26-34]
+            Identifier(score) [29-34]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#int64_for_json
+-- An error is thrown if JSON isn't a number or can't be converted to a 64-bit integer.
+SELECT INT64(JSON '10.1') AS result
+--
+QueryStatement [88-123]
+  Query [88-123]
+    Select [88-123]
+      SelectList [95-123]
+        SelectColumn [95-123]
+          FunctionCall [95-113]
+            PathExpression [95-100]
+              Identifier(INT64) [95-100]
+            JSONLiteral [101-112]
+              StringLiteral [106-112]
+                StringLiteralComponent('10.1') [106-112]
+          Alias [114-123]
+            Identifier(result) [117-123]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#int64_for_json
+-- Throws an error
+SELECT INT64(JSON '"strawberry"') AS result
+--
+QueryStatement [19-62]
+  Query [19-62]
+    Select [19-62]
+      SelectList [26-62]
+        SelectColumn [26-62]
+          FunctionCall [26-52]
+            PathExpression [26-31]
+              Identifier(INT64) [26-31]
+            JSONLiteral [32-51]
+              StringLiteral [37-51]
+                StringLiteralComponent('"strawberry"') [37-51]
+          Alias [53-62]
+            Identifier(result) [56-62]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#int64_for_json
+-- Throws an error
+SELECT INT64(JSON 'null') AS result
+--
+QueryStatement [19-54]
+  Query [19-54]
+    Select [19-54]
+      SelectList [26-54]
+        SelectColumn [26-54]
+          FunctionCall [26-44]
+            PathExpression [26-31]
+              Identifier(INT64) [26-31]
+            JSONLiteral [32-43]
+              StringLiteral [37-43]
+                StringLiteralComponent('null') [37-43]
+          Alias [45-54]
+            Identifier(result) [48-54]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#int64_for_json
+-- Throws an error
+SELECT SAFE.INT64(JSON '"strawberry"') AS result
+--
+QueryStatement [19-67]
+  Query [19-67]
+    Select [19-67]
+      SelectList [26-67]
+        SelectColumn [26-67]
+          FunctionCall [26-57]
+            PathExpression [26-36]
+              Identifier(SAFE) [26-30]
+              Identifier(INT64) [31-36]
+            JSONLiteral [37-56]
+              StringLiteral [42-56]
+                StringLiteralComponent('"strawberry"') [42-56]
+          Alias [58-67]
+            Identifier(result) [61-67]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#int64_array_for_json
+[mode=expression]
+INT64_ARRAY(json_expr)
+--
+FunctionCall [0-22]
+  PathExpression [0-11]
+    Identifier(INT64_ARRAY) [0-11]
+  PathExpression [12-21]
+    Identifier(json_expr) [12-21]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#int64_array_for_json
+[mode=expression]
+JSON '[999]'
+--
+JSONLiteral [0-12]
+  StringLiteral [5-12]
+    StringLiteralComponent('[999]') [5-12]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#int64_array_for_json
+SELECT INT64_ARRAY(JSON '[2005, 2003]') AS flight_numbers
+--
+QueryStatement [0-57]
+  Query [0-57]
+    Select [0-57]
+      SelectList [7-57]
+        SelectColumn [7-57]
+          FunctionCall [7-39]
+            PathExpression [7-18]
+              Identifier(INT64_ARRAY) [7-18]
+            JSONLiteral [19-38]
+              StringLiteral [24-38]
+                StringLiteralComponent('[2005, 2003]') [24-38]
+          Alias [40-57]
+            Identifier(flight_numbers) [43-57]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#int64_array_for_json
+SELECT INT64_ARRAY(JSON '[10.0]') AS scores
+--
+QueryStatement [0-43]
+  Query [0-43]
+    Select [0-43]
+      SelectList [7-43]
+        SelectColumn [7-43]
+          FunctionCall [7-33]
+            PathExpression [7-18]
+              Identifier(INT64_ARRAY) [7-18]
+            JSONLiteral [19-32]
+              StringLiteral [24-32]
+                StringLiteralComponent('[10.0]') [24-32]
+          Alias [34-43]
+            Identifier(scores) [37-43]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#int64_array_for_json
+-- An error is thrown if the JSON isn't an array of numbers in INT64 domain.
+SELECT INT64_ARRAY(JSON '[10.1]') AS result
+--
+QueryStatement [77-120]
+  Query [77-120]
+    Select [77-120]
+      SelectList [84-120]
+        SelectColumn [84-120]
+          FunctionCall [84-110]
+            PathExpression [84-95]
+              Identifier(INT64_ARRAY) [84-95]
+            JSONLiteral [96-109]
+              StringLiteral [101-109]
+                StringLiteralComponent('[10.1]') [101-109]
+          Alias [111-120]
+            Identifier(result) [114-120]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#int64_array_for_json
+-- Throws an error
+SELECT INT64_ARRAY(JSON '["strawberry"]') AS result
+--
+QueryStatement [19-70]
+  Query [19-70]
+    Select [19-70]
+      SelectList [26-70]
+        SelectColumn [26-70]
+          FunctionCall [26-60]
+            PathExpression [26-37]
+              Identifier(INT64_ARRAY) [26-37]
+            JSONLiteral [38-59]
+              StringLiteral [43-59]
+                StringLiteralComponent('["strawberry"]') [43-59]
+          Alias [61-70]
+            Identifier(result) [64-70]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#int64_array_for_json
+-- Throws an error
+SELECT INT64_ARRAY(JSON '[null]') AS result
+--
+QueryStatement [19-62]
+  Query [19-62]
+    Select [19-62]
+      SelectList [26-62]
+        SelectColumn [26-62]
+          FunctionCall [26-52]
+            PathExpression [26-37]
+              Identifier(INT64_ARRAY) [26-37]
+            JSONLiteral [38-51]
+              StringLiteral [43-51]
+                StringLiteralComponent('[null]') [43-51]
+          Alias [53-62]
+            Identifier(result) [56-62]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#int64_array_for_json
+-- Throws an error
+SELECT INT64_ARRAY(JSON 'null') AS result
+--
+QueryStatement [19-60]
+  Query [19-60]
+    Select [19-60]
+      SelectList [26-60]
+        SelectColumn [26-60]
+          FunctionCall [26-50]
+            PathExpression [26-37]
+              Identifier(INT64_ARRAY) [26-37]
+            JSONLiteral [38-49]
+              StringLiteral [43-49]
+                StringLiteralComponent('null') [43-49]
+          Alias [51-60]
+            Identifier(result) [54-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array
+SELECT JSON_ARRAY(10) AS json_data
+--
+QueryStatement [0-34]
+  Query [0-34]
+    Select [0-34]
+      SelectList [7-34]
+        SelectColumn [7-34]
+          FunctionCall [7-21]
+            PathExpression [7-17]
+              Identifier(JSON_ARRAY) [7-17]
+            IntLiteral(10) [18-20]
+          Alias [22-34]
+            Identifier(json_data) [25-34]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array
+SELECT JSON_ARRAY([]) AS json_data
+--
+QueryStatement [0-34]
+  Query [0-34]
+    Select [0-34]
+      SelectList [7-34]
+        SelectColumn [7-34]
+          FunctionCall [7-21]
+            PathExpression [7-17]
+              Identifier(JSON_ARRAY) [7-17]
+            ArrayConstructor [18-20]
+          Alias [22-34]
+            Identifier(json_data) [25-34]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array
+SELECT JSON_ARRAY(10, 'foo', NULL) AS json_data
+--
+QueryStatement [0-47]
+  Query [0-47]
+    Select [0-47]
+      SelectList [7-47]
+        SelectColumn [7-47]
+          FunctionCall [7-34]
+            PathExpression [7-17]
+              Identifier(JSON_ARRAY) [7-17]
+            IntLiteral(10) [18-20]
+            StringLiteral [22-27]
+              StringLiteralComponent('foo') [22-27]
+            NullLiteral(NULL) [29-33]
+          Alias [35-47]
+            Identifier(json_data) [38-47]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array
+SELECT JSON_ARRAY(STRUCT(10 AS a, 'foo' AS b)) AS json_data
+--
+QueryStatement [0-59]
+  Query [0-59]
+    Select [0-59]
+      SelectList [7-59]
+        SelectColumn [7-59]
+          FunctionCall [7-46]
+            PathExpression [7-17]
+              Identifier(JSON_ARRAY) [7-17]
+            StructConstructorWithKeyword [18-45]
+              StructConstructorArg [25-32]
+                IntLiteral(10) [25-27]
+                Alias [28-32]
+                  Identifier(a) [31-32]
+              StructConstructorArg [34-44]
+                StringLiteral [34-39]
+                  StringLiteralComponent('foo') [34-39]
+                Alias [40-44]
+                  Identifier(b) [43-44]
+          Alias [47-59]
+            Identifier(json_data) [50-59]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array
+SELECT JSON_ARRAY(10, ['foo', 'bar'], [20, 30]) AS json_data
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-60]
+      SelectList [7-60]
+        SelectColumn [7-60]
+          FunctionCall [7-47]
+            PathExpression [7-17]
+              Identifier(JSON_ARRAY) [7-17]
+            IntLiteral(10) [18-20]
+            ArrayConstructor [22-36]
+              StringLiteral [23-28]
+                StringLiteralComponent('foo') [23-28]
+              StringLiteral [30-35]
+                StringLiteralComponent('bar') [30-35]
+            ArrayConstructor [38-46]
+              IntLiteral(20) [39-41]
+              IntLiteral(30) [43-45]
+          Alias [48-60]
+            Identifier(json_data) [51-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array
+SELECT JSON_ARRAY(10, [JSON '20', JSON '"foo"']) AS json_data
+--
+QueryStatement [0-61]
+  Query [0-61]
+    Select [0-61]
+      SelectList [7-61]
+        SelectColumn [7-61]
+          FunctionCall [7-48]
+            PathExpression [7-17]
+              Identifier(JSON_ARRAY) [7-17]
+            IntLiteral(10) [18-20]
+            ArrayConstructor [22-47]
+              JSONLiteral [23-32]
+                StringLiteral [28-32]
+                  StringLiteralComponent('20') [28-32]
+              JSONLiteral [34-46]
+                StringLiteral [39-46]
+                  StringLiteralComponent('"foo"') [39-46]
+          Alias [49-61]
+            Identifier(json_data) [52-61]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array
+SELECT JSON_ARRAY() AS json_data
+--
+QueryStatement [0-32]
+  Query [0-32]
+    Select [0-32]
+      SelectList [7-32]
+        SelectColumn [7-32]
+          FunctionCall [7-19]
+            PathExpression [7-17]
+              Identifier(JSON_ARRAY) [7-17]
+          Alias [20-32]
+            Identifier(json_data) [23-32]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array_append
+[mode=expression]
+JSON '["a", "b", "c"]'
+--
+JSONLiteral [0-22]
+  StringLiteral [5-22]
+    StringLiteralComponent('["a", "b", "c"]') [5-22]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array_append
+SELECT JSON_ARRAY_APPEND(JSON '["a", "b", "c"]', '$', 1) AS json_data
+--
+QueryStatement [0-69]
+  Query [0-69]
+    Select [0-69]
+      SelectList [7-69]
+        SelectColumn [7-69]
+          FunctionCall [7-56]
+            PathExpression [7-24]
+              Identifier(JSON_ARRAY_APPEND) [7-24]
+            JSONLiteral [25-47]
+              StringLiteral [30-47]
+                StringLiteralComponent('["a", "b", "c"]') [30-47]
+            StringLiteral [49-52]
+              StringLiteralComponent('$') [49-52]
+            IntLiteral(1) [54-55]
+          Alias [57-69]
+            Identifier(json_data) [60-69]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array_append
+SELECT JSON_ARRAY_APPEND(JSON '["a", "b", "c"]', '$', [1, 2]) AS json_data
+--
+QueryStatement [0-74]
+  Query [0-74]
+    Select [0-74]
+      SelectList [7-74]
+        SelectColumn [7-74]
+          FunctionCall [7-61]
+            PathExpression [7-24]
+              Identifier(JSON_ARRAY_APPEND) [7-24]
+            JSONLiteral [25-47]
+              StringLiteral [30-47]
+                StringLiteralComponent('["a", "b", "c"]') [30-47]
+            StringLiteral [49-52]
+              StringLiteralComponent('$') [49-52]
+            ArrayConstructor [54-60]
+              IntLiteral(1) [55-56]
+              IntLiteral(2) [58-59]
+          Alias [62-74]
+            Identifier(json_data) [65-74]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array_append
+SELECT JSON_ARRAY_APPEND(
+  JSON '["a", "b", "c"]',
+  '$', [1, 2],
+  append_each_element=>FALSE) AS json_data
+--
+QueryStatement [0-109]
+  Query [0-109]
+    Select [0-109]
+      SelectList [7-109]
+        SelectColumn [7-109]
+          FunctionCall [7-96]
+            PathExpression [7-24]
+              Identifier(JSON_ARRAY_APPEND) [7-24]
+            JSONLiteral [28-50]
+              StringLiteral [33-50]
+                StringLiteralComponent('["a", "b", "c"]') [33-50]
+            StringLiteral [54-57]
+              StringLiteralComponent('$') [54-57]
+            ArrayConstructor [59-65]
+              IntLiteral(1) [60-61]
+              IntLiteral(2) [63-64]
+            NamedArgument [69-95]
+              Identifier(append_each_element) [69-88]
+              BooleanLiteral(FALSE) [90-95]
+          Alias [97-109]
+            Identifier(json_data) [100-109]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array_append
+SELECT JSON_ARRAY_APPEND(
+  JSON '["a", ["b"], "c"]',
+  '$[1]', [1, 2],
+  '$[1][1]', [3, 4],
+  append_each_element=>FALSE) AS json_data
+--
+QueryStatement [0-135]
+  Query [0-135]
+    Select [0-135]
+      SelectList [7-135]
+        SelectColumn [7-135]
+          FunctionCall [7-122]
+            PathExpression [7-24]
+              Identifier(JSON_ARRAY_APPEND) [7-24]
+            JSONLiteral [28-52]
+              StringLiteral [33-52]
+                StringLiteralComponent('["a", ["b"], "c"]') [33-52]
+            StringLiteral [56-62]
+              StringLiteralComponent('$[1]') [56-62]
+            ArrayConstructor [64-70]
+              IntLiteral(1) [65-66]
+              IntLiteral(2) [68-69]
+            StringLiteral [74-83]
+              StringLiteralComponent('$[1][1]') [74-83]
+            ArrayConstructor [85-91]
+              IntLiteral(3) [86-87]
+              IntLiteral(4) [89-90]
+            NamedArgument [95-121]
+              Identifier(append_each_element) [95-114]
+              BooleanLiteral(FALSE) [116-121]
+          Alias [123-135]
+            Identifier(json_data) [126-135]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array_append
+SELECT JSON_ARRAY_APPEND(
+  JSON '["a", ["b"], "c"]',
+  '$[1]', [1, 2],
+  '$[1][1]', [3, 4]) AS json_data
+--
+QueryStatement [0-105]
+  Query [0-105]
+    Select [0-105]
+      SelectList [7-105]
+        SelectColumn [7-105]
+          FunctionCall [7-92]
+            PathExpression [7-24]
+              Identifier(JSON_ARRAY_APPEND) [7-24]
+            JSONLiteral [28-52]
+              StringLiteral [33-52]
+                StringLiteralComponent('["a", ["b"], "c"]') [33-52]
+            StringLiteral [56-62]
+              StringLiteralComponent('$[1]') [56-62]
+            ArrayConstructor [64-70]
+              IntLiteral(1) [65-66]
+              IntLiteral(2) [68-69]
+            StringLiteral [74-83]
+              StringLiteralComponent('$[1][1]') [74-83]
+            ArrayConstructor [85-91]
+              IntLiteral(3) [86-87]
+              IntLiteral(4) [89-90]
+          Alias [93-105]
+            Identifier(json_data) [96-105]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array_append
+SELECT JSON_ARRAY_APPEND(JSON '{"a": [1]}', '$.a', 2) AS json_data
+--
+QueryStatement [0-66]
+  Query [0-66]
+    Select [0-66]
+      SelectList [7-66]
+        SelectColumn [7-66]
+          FunctionCall [7-53]
+            PathExpression [7-24]
+              Identifier(JSON_ARRAY_APPEND) [7-24]
+            JSONLiteral [25-42]
+              StringLiteral [30-42]
+                StringLiteralComponent('{"a": [1]}') [30-42]
+            StringLiteral [44-49]
+              StringLiteralComponent('$.a') [44-49]
+            IntLiteral(2) [51-52]
+          Alias [54-66]
+            Identifier(json_data) [57-66]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array_append
+SELECT JSON_ARRAY_APPEND(JSON '{"a": null}', '$.a', 10)
+--
+QueryStatement [0-55]
+  Query [0-55]
+    Select [0-55]
+      SelectList [7-55]
+        SelectColumn [7-55]
+          FunctionCall [7-55]
+            PathExpression [7-24]
+              Identifier(JSON_ARRAY_APPEND) [7-24]
+            JSONLiteral [25-43]
+              StringLiteral [30-43]
+                StringLiteralComponent('{"a": null}') [30-43]
+            StringLiteral [45-50]
+              StringLiteralComponent('$.a') [45-50]
+            IntLiteral(10) [52-54]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array_append
+SELECT JSON_ARRAY_APPEND(JSON '{"a": 1}', '$.a', 2) AS json_data
+--
+QueryStatement [0-64]
+  Query [0-64]
+    Select [0-64]
+      SelectList [7-64]
+        SelectColumn [7-64]
+          FunctionCall [7-51]
+            PathExpression [7-24]
+              Identifier(JSON_ARRAY_APPEND) [7-24]
+            JSONLiteral [25-40]
+              StringLiteral [30-40]
+                StringLiteralComponent('{"a": 1}') [30-40]
+            StringLiteral [42-47]
+              StringLiteralComponent('$.a') [42-47]
+            IntLiteral(2) [49-50]
+          Alias [52-64]
+            Identifier(json_data) [55-64]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array_append
+SELECT JSON_ARRAY_APPEND(JSON '{"a": 1}', '$.b', 2) AS json_data
+--
+QueryStatement [0-64]
+  Query [0-64]
+    Select [0-64]
+      SelectList [7-64]
+        SelectColumn [7-64]
+          FunctionCall [7-51]
+            PathExpression [7-24]
+              Identifier(JSON_ARRAY_APPEND) [7-24]
+            JSONLiteral [25-40]
+              StringLiteral [30-40]
+                StringLiteralComponent('{"a": 1}') [30-40]
+            StringLiteral [42-47]
+              StringLiteralComponent('$.b') [42-47]
+            IntLiteral(2) [49-50]
+          Alias [52-64]
+            Identifier(json_data) [55-64]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array_insert
+SELECT JSON_ARRAY_INSERT(JSON '["a", ["b", "c"], "d"]', '$[1]', 1) AS json_data
+--
+QueryStatement [0-79]
+  Query [0-79]
+    Select [0-79]
+      SelectList [7-79]
+        SelectColumn [7-79]
+          FunctionCall [7-66]
+            PathExpression [7-24]
+              Identifier(JSON_ARRAY_INSERT) [7-24]
+            JSONLiteral [25-54]
+              StringLiteral [30-54]
+                StringLiteralComponent('["a", ["b", "c"], "d"]') [30-54]
+            StringLiteral [56-62]
+              StringLiteralComponent('$[1]') [56-62]
+            IntLiteral(1) [64-65]
+          Alias [67-79]
+            Identifier(json_data) [70-79]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array_insert
+SELECT JSON_ARRAY_INSERT(JSON '["a", ["b", "c"], "d"]', '$[1][0]', 1) AS json_data
+--
+QueryStatement [0-82]
+  Query [0-82]
+    Select [0-82]
+      SelectList [7-82]
+        SelectColumn [7-82]
+          FunctionCall [7-69]
+            PathExpression [7-24]
+              Identifier(JSON_ARRAY_INSERT) [7-24]
+            JSONLiteral [25-54]
+              StringLiteral [30-54]
+                StringLiteralComponent('["a", ["b", "c"], "d"]') [30-54]
+            StringLiteral [56-65]
+              StringLiteralComponent('$[1][0]') [56-65]
+            IntLiteral(1) [67-68]
+          Alias [70-82]
+            Identifier(json_data) [73-82]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array_insert
+SELECT JSON_ARRAY_INSERT(JSON '["a", "b", "c"]', '$[1]', [1, 2]) AS json_data
+--
+QueryStatement [0-77]
+  Query [0-77]
+    Select [0-77]
+      SelectList [7-77]
+        SelectColumn [7-77]
+          FunctionCall [7-64]
+            PathExpression [7-24]
+              Identifier(JSON_ARRAY_INSERT) [7-24]
+            JSONLiteral [25-47]
+              StringLiteral [30-47]
+                StringLiteralComponent('["a", "b", "c"]') [30-47]
+            StringLiteral [49-55]
+              StringLiteralComponent('$[1]') [49-55]
+            ArrayConstructor [57-63]
+              IntLiteral(1) [58-59]
+              IntLiteral(2) [61-62]
+          Alias [65-77]
+            Identifier(json_data) [68-77]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array_insert
+SELECT JSON_ARRAY_INSERT(
+  JSON '["a", "b", "c"]',
+  '$[1]', [1, 2],
+  insert_each_element=>FALSE) AS json_data
+--
+QueryStatement [0-112]
+  Query [0-112]
+    Select [0-112]
+      SelectList [7-112]
+        SelectColumn [7-112]
+          FunctionCall [7-99]
+            PathExpression [7-24]
+              Identifier(JSON_ARRAY_INSERT) [7-24]
+            JSONLiteral [28-50]
+              StringLiteral [33-50]
+                StringLiteralComponent('["a", "b", "c"]') [33-50]
+            StringLiteral [54-60]
+              StringLiteralComponent('$[1]') [54-60]
+            ArrayConstructor [62-68]
+              IntLiteral(1) [63-64]
+              IntLiteral(2) [66-67]
+            NamedArgument [72-98]
+              Identifier(insert_each_element) [72-91]
+              BooleanLiteral(FALSE) [93-98]
+          Alias [100-112]
+            Identifier(json_data) [103-112]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array_insert
+SELECT JSON_ARRAY_INSERT(JSON '["a", "b", "c", "d"]', '$[7]', "e") AS json_data
+--
+QueryStatement [0-79]
+  Query [0-79]
+    Select [0-79]
+      SelectList [7-79]
+        SelectColumn [7-79]
+          FunctionCall [7-66]
+            PathExpression [7-24]
+              Identifier(JSON_ARRAY_INSERT) [7-24]
+            JSONLiteral [25-52]
+              StringLiteral [30-52]
+                StringLiteralComponent('["a", "b", "c", "d"]') [30-52]
+            StringLiteral [54-60]
+              StringLiteralComponent('$[7]') [54-60]
+            StringLiteral [62-65]
+              StringLiteralComponent("e") [62-65]
+          Alias [67-79]
+            Identifier(json_data) [70-79]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array_insert
+SELECT JSON_ARRAY_INSERT(JSON '{"a": {}}', '$.a[0]', 2) AS json_data
+--
+QueryStatement [0-68]
+  Query [0-68]
+    Select [0-68]
+      SelectList [7-68]
+        SelectColumn [7-68]
+          FunctionCall [7-55]
+            PathExpression [7-24]
+              Identifier(JSON_ARRAY_INSERT) [7-24]
+            JSONLiteral [25-41]
+              StringLiteral [30-41]
+                StringLiteralComponent('{"a": {}}') [30-41]
+            StringLiteral [43-51]
+              StringLiteralComponent('$.a[0]') [43-51]
+            IntLiteral(2) [53-54]
+          Alias [56-68]
+            Identifier(json_data) [59-68]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array_insert
+SELECT JSON_ARRAY_INSERT(JSON '[1, 2]', '$', 3) AS json_data
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-60]
+      SelectList [7-60]
+        SelectColumn [7-60]
+          FunctionCall [7-47]
+            PathExpression [7-24]
+              Identifier(JSON_ARRAY_INSERT) [7-24]
+            JSONLiteral [25-38]
+              StringLiteral [30-38]
+                StringLiteralComponent('[1, 2]') [30-38]
+            StringLiteral [40-43]
+              StringLiteralComponent('$') [40-43]
+            IntLiteral(3) [45-46]
+          Alias [48-60]
+            Identifier(json_data) [51-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array_insert
+SELECT JSON_ARRAY_INSERT(JSON '{"a": null}', '$.a[2]', 10) AS json_data
+--
+QueryStatement [0-71]
+  Query [0-71]
+    Select [0-71]
+      SelectList [7-71]
+        SelectColumn [7-71]
+          FunctionCall [7-58]
+            PathExpression [7-24]
+              Identifier(JSON_ARRAY_INSERT) [7-24]
+            JSONLiteral [25-43]
+              StringLiteral [30-43]
+                StringLiteralComponent('{"a": null}') [30-43]
+            StringLiteral [45-53]
+              StringLiteralComponent('$.a[2]') [45-53]
+            IntLiteral(10) [55-57]
+          Alias [59-71]
+            Identifier(json_data) [62-71]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_array_insert
+SELECT JSON_ARRAY_INSERT(JSON '1', '$[0]', 'r1') AS json_data
+--
+QueryStatement [0-61]
+  Query [0-61]
+    Select [0-61]
+      SelectList [7-61]
+        SelectColumn [7-61]
+          FunctionCall [7-48]
+            PathExpression [7-24]
+              Identifier(JSON_ARRAY_INSERT) [7-24]
+            JSONLiteral [25-33]
+              StringLiteral [30-33]
+                StringLiteralComponent('1') [30-33]
+            StringLiteral [35-41]
+              StringLiteralComponent('$[0]') [35-41]
+            StringLiteral [43-47]
+              StringLiteralComponent('r1') [43-47]
+          Alias [49-61]
+            Identifier(json_data) [52-61]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_contains
+[mode=expression]
+JSON_CONTAINS(json_expr, json_expr)
+--
+FunctionCall [0-35]
+  PathExpression [0-13]
+    Identifier(JSON_CONTAINS) [0-13]
+  PathExpression [14-23]
+    Identifier(json_expr) [14-23]
+  PathExpression [25-34]
+    Identifier(json_expr) [25-34]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_contains
+[mode=expression]
+JSON '{"class": {"students": [{"name": "Jane"}]}}'
+--
+JSONLiteral [0-50]
+  StringLiteral [5-50]
+    StringLiteralComponent('{"class": {"students": [{"name": "Jane"}]}}') [5-50]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_contains
+SELECT JSON_CONTAINS(JSON '"a"', JSON '"a"') AS result
+--
+QueryStatement [0-54]
+  Query [0-54]
+    Select [0-54]
+      SelectList [7-54]
+        SelectColumn [7-54]
+          FunctionCall [7-44]
+            PathExpression [7-20]
+              Identifier(JSON_CONTAINS) [7-20]
+            JSONLiteral [21-31]
+              StringLiteral [26-31]
+                StringLiteralComponent('"a"') [26-31]
+            JSONLiteral [33-43]
+              StringLiteral [38-43]
+                StringLiteralComponent('"a"') [38-43]
+          Alias [45-54]
+            Identifier(result) [48-54]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_contains
+SELECT
+    JSON_CONTAINS(JSON '{"a": {"b": 1}, "c": 2}', JSON '{"b": 1}') AS result1,
+    JSON_CONTAINS(JSON '{"a": {"b": 1}, "c": 2}', JSON '{"a": {"b": 1}}') AS result2,
+    JSON_CONTAINS(JSON '{"a": {"b": 1, "d": 3}, "c": 2}', JSON '{"a": {"b": 1}}') AS result3
+--
+QueryStatement [0-264]
+  Query [0-264]
+    Select [0-264]
+      SelectList [11-264]
+        SelectColumn [11-84]
+          FunctionCall [11-73]
+            PathExpression [11-24]
+              Identifier(JSON_CONTAINS) [11-24]
+            JSONLiteral [25-55]
+              StringLiteral [30-55]
+                StringLiteralComponent('{"a": {"b": 1}, "c": 2}') [30-55]
+            JSONLiteral [57-72]
+              StringLiteral [62-72]
+                StringLiteralComponent('{"b": 1}') [62-72]
+          Alias [74-84]
+            Identifier(result1) [77-84]
+        SelectColumn [90-170]
+          FunctionCall [90-159]
+            PathExpression [90-103]
+              Identifier(JSON_CONTAINS) [90-103]
+            JSONLiteral [104-134]
+              StringLiteral [109-134]
+                StringLiteralComponent('{"a": {"b": 1}, "c": 2}') [109-134]
+            JSONLiteral [136-158]
+              StringLiteral [141-158]
+                StringLiteralComponent('{"a": {"b": 1}}') [141-158]
+          Alias [160-170]
+            Identifier(result2) [163-170]
+        SelectColumn [176-264]
+          FunctionCall [176-253]
+            PathExpression [176-189]
+              Identifier(JSON_CONTAINS) [176-189]
+            JSONLiteral [190-228]
+              StringLiteral [195-228]
+                StringLiteralComponent('{"a": {"b": 1, "d": 3}, "c": 2}') [195-228]
+            JSONLiteral [230-252]
+              StringLiteral [235-252]
+                StringLiteralComponent('{"a": {"b": 1}}') [235-252]
+          Alias [254-264]
+            Identifier(result3) [257-264]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_contains
+SELECT
+    JSON_CONTAINS(JSON '[1, 2, 3]', JSON '[2]') AS result1,
+    JSON_CONTAINS(JSON '[1, 2, 3]', JSON '2') AS result2
+--
+QueryStatement [0-123]
+  Query [0-123]
+    Select [0-123]
+      SelectList [11-123]
+        SelectColumn [11-65]
+          FunctionCall [11-54]
+            PathExpression [11-24]
+              Identifier(JSON_CONTAINS) [11-24]
+            JSONLiteral [25-41]
+              StringLiteral [30-41]
+                StringLiteralComponent('[1, 2, 3]') [30-41]
+            JSONLiteral [43-53]
+              StringLiteral [48-53]
+                StringLiteralComponent('[2]') [48-53]
+          Alias [55-65]
+            Identifier(result1) [58-65]
+        SelectColumn [71-123]
+          FunctionCall [71-112]
+            PathExpression [71-84]
+              Identifier(JSON_CONTAINS) [71-84]
+            JSONLiteral [85-101]
+              StringLiteral [90-101]
+                StringLiteralComponent('[1, 2, 3]') [90-101]
+            JSONLiteral [103-111]
+              StringLiteral [108-111]
+                StringLiteralComponent('2') [108-111]
+          Alias [113-123]
+            Identifier(result2) [116-123]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_contains
+SELECT
+    JSON_CONTAINS(JSON '[[1, 2, 3]]', JSON '2') AS result1,
+    JSON_CONTAINS(JSON '[[1, 2, 3]]', JSON '[2]') AS result2,
+    JSON_CONTAINS(JSON '[[1, 2, 3]]', JSON '[[2]]') AS result3
+--
+QueryStatement [0-191]
+  Query [0-191]
+    Select [0-191]
+      SelectList [11-191]
+        SelectColumn [11-65]
+          FunctionCall [11-54]
+            PathExpression [11-24]
+              Identifier(JSON_CONTAINS) [11-24]
+            JSONLiteral [25-43]
+              StringLiteral [30-43]
+                StringLiteralComponent('[[1, 2, 3]]') [30-43]
+            JSONLiteral [45-53]
+              StringLiteral [50-53]
+                StringLiteralComponent('2') [50-53]
+          Alias [55-65]
+            Identifier(result1) [58-65]
+        SelectColumn [71-127]
+          FunctionCall [71-116]
+            PathExpression [71-84]
+              Identifier(JSON_CONTAINS) [71-84]
+            JSONLiteral [85-103]
+              StringLiteral [90-103]
+                StringLiteralComponent('[[1, 2, 3]]') [90-103]
+            JSONLiteral [105-115]
+              StringLiteral [110-115]
+                StringLiteralComponent('[2]') [110-115]
+          Alias [117-127]
+            Identifier(result2) [120-127]
+        SelectColumn [133-191]
+          FunctionCall [133-180]
+            PathExpression [133-146]
+              Identifier(JSON_CONTAINS) [133-146]
+            JSONLiteral [147-165]
+              StringLiteral [152-165]
+                StringLiteralComponent('[[1, 2, 3]]') [152-165]
+            JSONLiteral [167-179]
+              StringLiteral [172-179]
+                StringLiteralComponent('[[2]]') [172-179]
+          Alias [181-191]
+            Identifier(result3) [184-191]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_contains
+SELECT
+    JSON_CONTAINS(JSON '[{"a":0}, {"b":1, "c":2}]', JSON '[{"b":1}]') AS result1,
+    JSON_CONTAINS(JSON '[{"a":0}, {"b":1, "c":2}]', JSON '{"b":1}') AS results2,
+    JSON_CONTAINS(JSON '[{"a":0}, {"b":1, "c":2}]', JSON '[{"a":0, "b":1}]') AS results3
+--
+QueryStatement [0-258]
+  Query [0-258]
+    Select [0-258]
+      SelectList [11-258]
+        SelectColumn [11-87]
+          FunctionCall [11-76]
+            PathExpression [11-24]
+              Identifier(JSON_CONTAINS) [11-24]
+            JSONLiteral [25-57]
+              StringLiteral [30-57]
+                StringLiteralComponent('[{"a":0}, {"b":1, "c":2}]') [30-57]
+            JSONLiteral [59-75]
+              StringLiteral [64-75]
+                StringLiteralComponent('[{"b":1}]') [64-75]
+          Alias [77-87]
+            Identifier(result1) [80-87]
+        SelectColumn [93-168]
+          FunctionCall [93-156]
+            PathExpression [93-106]
+              Identifier(JSON_CONTAINS) [93-106]
+            JSONLiteral [107-139]
+              StringLiteral [112-139]
+                StringLiteralComponent('[{"a":0}, {"b":1, "c":2}]') [112-139]
+            JSONLiteral [141-155]
+              StringLiteral [146-155]
+                StringLiteralComponent('{"b":1}') [146-155]
+          Alias [157-168]
+            Identifier(results2) [160-168]
+        SelectColumn [174-258]
+          FunctionCall [174-246]
+            PathExpression [174-187]
+              Identifier(JSON_CONTAINS) [174-187]
+            JSONLiteral [188-220]
+              StringLiteral [193-220]
+                StringLiteralComponent('[{"a":0}, {"b":1, "c":2}]') [193-220]
+            JSONLiteral [222-245]
+              StringLiteral [227-245]
+                StringLiteralComponent('[{"a":0, "b":1}]') [227-245]
+          Alias [247-258]
+            Identifier(results3) [250-258]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_keys
+SELECT JSON_KEYS(JSON '{"a": {"b":1}}') AS json_keys
+--
+QueryStatement [0-52]
+  Query [0-52]
+    Select [0-52]
+      SelectList [7-52]
+        SelectColumn [7-52]
+          FunctionCall [7-39]
+            PathExpression [7-16]
+              Identifier(JSON_KEYS) [7-16]
+            JSONLiteral [17-38]
+              StringLiteral [22-38]
+                StringLiteralComponent('{"a": {"b":1}}') [22-38]
+          Alias [40-52]
+            Identifier(json_keys) [43-52]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_keys
+SELECT JSON_KEYS(JSON '{"a": {"b":1}}', 1) AS json_keys
+--
+QueryStatement [0-55]
+  Query [0-55]
+    Select [0-55]
+      SelectList [7-55]
+        SelectColumn [7-55]
+          FunctionCall [7-42]
+            PathExpression [7-16]
+              Identifier(JSON_KEYS) [7-16]
+            JSONLiteral [17-38]
+              StringLiteral [22-38]
+                StringLiteralComponent('{"a": {"b":1}}') [22-38]
+            IntLiteral(1) [40-41]
+          Alias [43-55]
+            Identifier(json_keys) [46-55]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_keys
+SELECT JSON_KEYS(JSON '{"a":[{"b":1}, {"c":2}], "d":3}') AS json_keys
+--
+QueryStatement [0-69]
+  Query [0-69]
+    Select [0-69]
+      SelectList [7-69]
+        SelectColumn [7-69]
+          FunctionCall [7-56]
+            PathExpression [7-16]
+              Identifier(JSON_KEYS) [7-16]
+            JSONLiteral [17-55]
+              StringLiteral [22-55]
+                StringLiteralComponent('{"a":[{"b":1}, {"c":2}], "d":3}') [22-55]
+          Alias [57-69]
+            Identifier(json_keys) [60-69]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_keys
+SELECT JSON_KEYS(
+  JSON '{"a":[{"b":1}, {"c":2}], "d":3}',
+  mode => "lax") as json_keys
+--
+QueryStatement [0-89]
+  Query [0-89]
+    Select [0-89]
+      SelectList [7-89]
+        SelectColumn [7-89]
+          FunctionCall [7-76]
+            PathExpression [7-16]
+              Identifier(JSON_KEYS) [7-16]
+            JSONLiteral [20-58]
+              StringLiteral [25-58]
+                StringLiteralComponent('{"a":[{"b":1}, {"c":2}], "d":3}') [25-58]
+            NamedArgument [62-75]
+              Identifier(mode) [62-66]
+              StringLiteral [70-75]
+                StringLiteralComponent("lax") [70-75]
+          Alias [77-89]
+            Identifier(json_keys) [80-89]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_keys
+SELECT JSON_KEYS(JSON '{"a":[[{"b":1}]]}', mode => "lax") as json_keys
+--
+QueryStatement [0-70]
+  Query [0-70]
+    Select [0-70]
+      SelectList [7-70]
+        SelectColumn [7-70]
+          FunctionCall [7-57]
+            PathExpression [7-16]
+              Identifier(JSON_KEYS) [7-16]
+            JSONLiteral [17-41]
+              StringLiteral [22-41]
+                StringLiteralComponent('{"a":[[{"b":1}]]}') [22-41]
+            NamedArgument [43-56]
+              Identifier(mode) [43-47]
+              StringLiteral [51-56]
+                StringLiteralComponent("lax") [51-56]
+          Alias [58-70]
+            Identifier(json_keys) [61-70]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_keys
+SELECT JSON_KEYS(JSON '{"a":[[{"b":1}]]}', mode => "lax recursive") as json_keys
+--
+QueryStatement [0-80]
+  Query [0-80]
+    Select [0-80]
+      SelectList [7-80]
+        SelectColumn [7-80]
+          FunctionCall [7-67]
+            PathExpression [7-16]
+              Identifier(JSON_KEYS) [7-16]
+            JSONLiteral [17-41]
+              StringLiteral [22-41]
+                StringLiteralComponent('{"a":[[{"b":1}]]}') [22-41]
+            NamedArgument [43-66]
+              Identifier(mode) [43-47]
+              StringLiteral [51-66]
+                StringLiteralComponent("lax recursive") [51-66]
+          Alias [68-80]
+            Identifier(json_keys) [71-80]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_keys
+SELECT JSON_KEYS(JSON '{"a":[{"b":[{"c":1}]}]}', mode => "lax") as json_keys
+--
+QueryStatement [0-76]
+  Query [0-76]
+    Select [0-76]
+      SelectList [7-76]
+        SelectColumn [7-76]
+          FunctionCall [7-63]
+            PathExpression [7-16]
+              Identifier(JSON_KEYS) [7-16]
+            JSONLiteral [17-47]
+              StringLiteral [22-47]
+                StringLiteralComponent('{"a":[{"b":[{"c":1}]}]}') [22-47]
+            NamedArgument [49-62]
+              Identifier(mode) [49-53]
+              StringLiteral [57-62]
+                StringLiteralComponent("lax") [57-62]
+          Alias [64-76]
+            Identifier(json_keys) [67-76]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_keys
+SELECT JSON_KEYS(JSON '{"a":[{"b":[[{"c":1}]]}]}', mode => "lax") as json_keys
+--
+QueryStatement [0-78]
+  Query [0-78]
+    Select [0-78]
+      SelectList [7-78]
+        SelectColumn [7-78]
+          FunctionCall [7-65]
+            PathExpression [7-16]
+              Identifier(JSON_KEYS) [7-16]
+            JSONLiteral [17-49]
+              StringLiteral [22-49]
+                StringLiteralComponent('{"a":[{"b":[[{"c":1}]]}]}') [22-49]
+            NamedArgument [51-64]
+              Identifier(mode) [51-55]
+              StringLiteral [59-64]
+                StringLiteralComponent("lax") [59-64]
+          Alias [66-78]
+            Identifier(json_keys) [69-78]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_keys
+SELECT JSON_KEYS(
+  JSON '{"a":[{"b":[[{"c":1}]]}]}', mode => "lax recursive") as json_keys
+--
+QueryStatement [0-91]
+  Query [0-91]
+    Select [0-91]
+      SelectList [7-91]
+        SelectColumn [7-91]
+          FunctionCall [7-78]
+            PathExpression [7-16]
+              Identifier(JSON_KEYS) [7-16]
+            JSONLiteral [20-52]
+              StringLiteral [25-52]
+                StringLiteralComponent('{"a":[{"b":[[{"c":1}]]}]}') [25-52]
+            NamedArgument [54-77]
+              Identifier(mode) [54-58]
+              StringLiteral [62-77]
+                StringLiteralComponent("lax recursive") [62-77]
+          Alias [79-91]
+            Identifier(json_keys) [82-91]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_object_signature1
+SELECT JSON_OBJECT() AS json_data
+--
+QueryStatement [0-33]
+  Query [0-33]
+    Select [0-33]
+      SelectList [7-33]
+        SelectColumn [7-33]
+          FunctionCall [7-20]
+            PathExpression [7-18]
+              Identifier(JSON_OBJECT) [7-18]
+          Alias [21-33]
+            Identifier(json_data) [24-33]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_object_signature1
+SELECT JSON_OBJECT('foo', 10, 'bar', TRUE) AS json_data
+--
+QueryStatement [0-55]
+  Query [0-55]
+    Select [0-55]
+      SelectList [7-55]
+        SelectColumn [7-55]
+          FunctionCall [7-42]
+            PathExpression [7-18]
+              Identifier(JSON_OBJECT) [7-18]
+            StringLiteral [19-24]
+              StringLiteralComponent('foo') [19-24]
+            IntLiteral(10) [26-28]
+            StringLiteral [30-35]
+              StringLiteralComponent('bar') [30-35]
+            BooleanLiteral(TRUE) [37-41]
+          Alias [43-55]
+            Identifier(json_data) [46-55]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_object_signature1
+SELECT JSON_OBJECT('foo', 10, 'bar', ['a', 'b']) AS json_data
+--
+QueryStatement [0-61]
+  Query [0-61]
+    Select [0-61]
+      SelectList [7-61]
+        SelectColumn [7-61]
+          FunctionCall [7-48]
+            PathExpression [7-18]
+              Identifier(JSON_OBJECT) [7-18]
+            StringLiteral [19-24]
+              StringLiteralComponent('foo') [19-24]
+            IntLiteral(10) [26-28]
+            StringLiteral [30-35]
+              StringLiteralComponent('bar') [30-35]
+            ArrayConstructor [37-47]
+              StringLiteral [38-41]
+                StringLiteralComponent('a') [38-41]
+              StringLiteral [43-46]
+                StringLiteralComponent('b') [43-46]
+          Alias [49-61]
+            Identifier(json_data) [52-61]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_object_signature1
+SELECT JSON_OBJECT('a', NULL, 'b', JSON 'null') AS json_data
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-60]
+      SelectList [7-60]
+        SelectColumn [7-60]
+          FunctionCall [7-47]
+            PathExpression [7-18]
+              Identifier(JSON_OBJECT) [7-18]
+            StringLiteral [19-22]
+              StringLiteralComponent('a') [19-22]
+            NullLiteral(NULL) [24-28]
+            StringLiteral [30-33]
+              StringLiteralComponent('b') [30-33]
+            JSONLiteral [35-46]
+              StringLiteral [40-46]
+                StringLiteralComponent('null') [40-46]
+          Alias [48-60]
+            Identifier(json_data) [51-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_object_signature1
+SELECT JSON_OBJECT('a', 10, 'a', 'foo') AS json_data
+--
+QueryStatement [0-52]
+  Query [0-52]
+    Select [0-52]
+      SelectList [7-52]
+        SelectColumn [7-52]
+          FunctionCall [7-39]
+            PathExpression [7-18]
+              Identifier(JSON_OBJECT) [7-18]
+            StringLiteral [19-22]
+              StringLiteralComponent('a') [19-22]
+            IntLiteral(10) [24-26]
+            StringLiteral [28-31]
+              StringLiteralComponent('a') [28-31]
+            StringLiteral [33-38]
+              StringLiteralComponent('foo') [33-38]
+          Alias [40-52]
+            Identifier(json_data) [43-52]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_object_signature1
+WITH Items AS (SELECT 'hello' AS key, 'world' AS value)
+SELECT JSON_OBJECT(key, value) AS json_data FROM Items
+--
+QueryStatement [0-110]
+  Query [0-110]
+    WithClause [0-55]
+      WithClauseEntry [5-55]
+        AliasedQuery [5-55]
+          Identifier(Items) [5-10]
+          Query [14-55]
+            Select [15-54]
+              SelectList [22-54]
+                SelectColumn [22-36]
+                  StringLiteral [22-29]
+                    StringLiteralComponent('hello') [22-29]
+                  Alias [30-36]
+                    Identifier(key) [33-36]
+                SelectColumn [38-54]
+                  StringLiteral [38-45]
+                    StringLiteralComponent('world') [38-45]
+                  Alias [46-54]
+                    Identifier(value) [49-54]
+    Select [56-110]
+      SelectList [63-99]
+        SelectColumn [63-99]
+          FunctionCall [63-86]
+            PathExpression [63-74]
+              Identifier(JSON_OBJECT) [63-74]
+            PathExpression [75-78]
+              Identifier(key) [75-78]
+            PathExpression [80-85]
+              Identifier(value) [80-85]
+          Alias [87-99]
+            Identifier(json_data) [90-99]
+      FromClause [100-110]
+        TablePathExpression [105-110]
+          PathExpression [105-110]
+            Identifier(Items) [105-110]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_object_signature1
+-- Error: A key can't be NULL.
+SELECT JSON_OBJECT(NULL, 1) AS json_data
+--
+QueryStatement [31-71]
+  Query [31-71]
+    Select [31-71]
+      SelectList [38-71]
+        SelectColumn [38-71]
+          FunctionCall [38-58]
+            PathExpression [38-49]
+              Identifier(JSON_OBJECT) [38-49]
+            NullLiteral(NULL) [50-54]
+            IntLiteral(1) [56-57]
+          Alias [59-71]
+            Identifier(json_data) [62-71]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_object_signature1
+-- Error: No matching signature for function JSON_OBJECT for argument types:
+-- STRING, INT64, STRING
+SELECT JSON_OBJECT('a', 1, 'b') AS json_data
+--
+QueryStatement [102-146]
+  Query [102-146]
+    Select [102-146]
+      SelectList [109-146]
+        SelectColumn [109-146]
+          FunctionCall [109-133]
+            PathExpression [109-120]
+              Identifier(JSON_OBJECT) [109-120]
+            StringLiteral [121-124]
+              StringLiteralComponent('a') [121-124]
+            IntLiteral(1) [126-127]
+            StringLiteral [129-132]
+              StringLiteralComponent('b') [129-132]
+          Alias [134-146]
+            Identifier(json_data) [137-146]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_object_signature2
+[mode=expression]
+JSON_OBJECT(json_key_array, json_value_array)
+--
+FunctionCall [0-45]
+  PathExpression [0-11]
+    Identifier(JSON_OBJECT) [0-11]
+  PathExpression [12-26]
+    Identifier(json_key_array) [12-26]
+  PathExpression [28-44]
+    Identifier(json_value_array) [28-44]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_object_signature2
+SELECT JSON_OBJECT(CAST([] AS ARRAY), []) AS json_data
+--
+QueryStatement [0-62]
+  Query [0-62]
+    Select [0-62]
+      SelectList [7-62]
+        SelectColumn [7-62]
+          FunctionCall [7-49]
+            PathExpression [7-18]
+              Identifier(JSON_OBJECT) [7-18]
+            CastExpression [19-44]
+              ArrayConstructor [24-26]
+              ArrayType [30-43]
+                SimpleType [36-42]
+                  PathExpression [36-42]
+                    Identifier(STRING) [36-42]
+            ArrayConstructor [46-48]
+          Alias [50-62]
+            Identifier(json_data) [53-62]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_object_signature2
+SELECT JSON_OBJECT(['a', 'b'], [10, NULL]) AS json_data
+--
+QueryStatement [0-55]
+  Query [0-55]
+    Select [0-55]
+      SelectList [7-55]
+        SelectColumn [7-55]
+          FunctionCall [7-42]
+            PathExpression [7-18]
+              Identifier(JSON_OBJECT) [7-18]
+            ArrayConstructor [19-29]
+              StringLiteral [20-23]
+                StringLiteralComponent('a') [20-23]
+              StringLiteral [25-28]
+                StringLiteralComponent('b') [25-28]
+            ArrayConstructor [31-41]
+              IntLiteral(10) [32-34]
+              NullLiteral(NULL) [36-40]
+          Alias [43-55]
+            Identifier(json_data) [46-55]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_object_signature2
+SELECT JSON_OBJECT(['a', 'b'], [JSON '10', JSON '"foo"']) AS json_data
+--
+QueryStatement [0-70]
+  Query [0-70]
+    Select [0-70]
+      SelectList [7-70]
+        SelectColumn [7-70]
+          FunctionCall [7-57]
+            PathExpression [7-18]
+              Identifier(JSON_OBJECT) [7-18]
+            ArrayConstructor [19-29]
+              StringLiteral [20-23]
+                StringLiteralComponent('a') [20-23]
+              StringLiteral [25-28]
+                StringLiteralComponent('b') [25-28]
+            ArrayConstructor [31-56]
+              JSONLiteral [32-41]
+                StringLiteral [37-41]
+                  StringLiteralComponent('10') [37-41]
+              JSONLiteral [43-55]
+                StringLiteral [48-55]
+                  StringLiteralComponent('"foo"') [48-55]
+          Alias [58-70]
+            Identifier(json_data) [61-70]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_object_signature2
+SELECT
+  JSON_OBJECT(
+\    ['a', 'b'],
+\    [STRUCT(10 AS id, 'Red' AS color), STRUCT(20 AS id, 'Blue' AS color)])
+    AS json_data
+--
+QueryStatement [0-129]
+  Query [0-129]
+    Select [0-129]
+      SelectList [9-129]
+        SelectColumn [9-129]
+          FunctionCall [9-112]
+            PathExpression [9-20]
+              Identifier(JSON_OBJECT) [9-20]
+            ArrayConstructor [26-36]
+              StringLiteral [27-30]
+                StringLiteralComponent('a') [27-30]
+              StringLiteral [32-35]
+                StringLiteralComponent('b') [32-35]
+            ArrayConstructor [42-111]
+              StructConstructorWithKeyword [43-75]
+                StructConstructorArg [50-58]
+                  IntLiteral(10) [50-52]
+                  Alias [53-58]
+                    Identifier(id) [56-58]
+                StructConstructorArg [60-74]
+                  StringLiteral [60-65]
+                    StringLiteralComponent('Red') [60-65]
+                  Alias [66-74]
+                    Identifier(color) [69-74]
+              StructConstructorWithKeyword [77-110]
+                StructConstructorArg [84-92]
+                  IntLiteral(20) [84-86]
+                  Alias [87-92]
+                    Identifier(id) [90-92]
+                StructConstructorArg [94-109]
+                  StringLiteral [94-100]
+                    StringLiteralComponent('Blue') [94-100]
+                  Alias [101-109]
+                    Identifier(color) [104-109]
+          Alias [117-129]
+            Identifier(json_data) [120-129]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_object_signature2
+SELECT
+  JSON_OBJECT(
+\    ['a', 'b'],
+\    [TO_JSON(10), TO_JSON(['foo', 'bar'])])
+    AS json_data
+--
+QueryStatement [0-98]
+  Query [0-98]
+    Select [0-98]
+      SelectList [9-98]
+        SelectColumn [9-98]
+          FunctionCall [9-81]
+            PathExpression [9-20]
+              Identifier(JSON_OBJECT) [9-20]
+            ArrayConstructor [26-36]
+              StringLiteral [27-30]
+                StringLiteralComponent('a') [27-30]
+              StringLiteral [32-35]
+                StringLiteralComponent('b') [32-35]
+            ArrayConstructor [42-80]
+              FunctionCall [43-54]
+                PathExpression [43-50]
+                  Identifier(TO_JSON) [43-50]
+                IntLiteral(10) [51-53]
+              FunctionCall [56-79]
+                PathExpression [56-63]
+                  Identifier(TO_JSON) [56-63]
+                ArrayConstructor [64-78]
+                  StringLiteral [65-70]
+                    StringLiteralComponent('foo') [65-70]
+                  StringLiteral [72-77]
+                    StringLiteralComponent('bar') [72-77]
+          Alias [86-98]
+            Identifier(json_data) [89-98]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_object_signature2
+WITH
+  Fruits AS (
+    SELECT 0 AS id, 'color' AS json_key, 'red' AS json_value UNION ALL
+    SELECT 0, 'fruit', 'apple' UNION ALL
+    SELECT 1, 'fruit', 'banana' UNION ALL
+    SELECT 1, 'ripe', 'true'
+  )
+SELECT JSON_OBJECT(ARRAY_AGG(json_key), ARRAY_AGG(json_value)) AS json_data
+FROM Fruits
+GROUP BY id
+--
+QueryStatement [0-305]
+  Query [0-305]
+    WithClause [0-205]
+      WithClauseEntry [7-205]
+        AliasedQuery [7-205]
+          Identifier(Fruits) [7-13]
+          Query [17-205]
+            SetOperation(UNION ALL) [23-201]
+              SetOperationMetadataList [80-172]
+                SetOperationMetadata [80-89]
+                  SetOperationType [80-85]
+                  SetOperationAllOrDistinct [86-89]
+                SetOperationMetadata [121-130]
+                  SetOperationType [121-126]
+                  SetOperationAllOrDistinct [127-130]
+                SetOperationMetadata [163-172]
+                  SetOperationType [163-168]
+                  SetOperationAllOrDistinct [169-172]
+              Select [23-79]
+                SelectList [30-79]
+                  SelectColumn [30-37]
+                    IntLiteral(0) [30-31]
+                    Alias [32-37]
+                      Identifier(id) [35-37]
+                  SelectColumn [39-58]
+                    StringLiteral [39-46]
+                      StringLiteralComponent('color') [39-46]
+                    Alias [47-58]
+                      Identifier(json_key) [50-58]
+                  SelectColumn [60-79]
+                    StringLiteral [60-65]
+                      StringLiteralComponent('red') [60-65]
+                    Alias [66-79]
+                      Identifier(json_value) [69-79]
+              Select [94-120]
+                SelectList [101-120]
+                  SelectColumn [101-102]
+                    IntLiteral(0) [101-102]
+                  SelectColumn [104-111]
+                    StringLiteral [104-111]
+                      StringLiteralComponent('fruit') [104-111]
+                  SelectColumn [113-120]
+                    StringLiteral [113-120]
+                      StringLiteralComponent('apple') [113-120]
+              Select [135-162]
+                SelectList [142-162]
+                  SelectColumn [142-143]
+                    IntLiteral(1) [142-143]
+                  SelectColumn [145-152]
+                    StringLiteral [145-152]
+                      StringLiteralComponent('fruit') [145-152]
+                  SelectColumn [154-162]
+                    StringLiteral [154-162]
+                      StringLiteralComponent('banana') [154-162]
+              Select [177-201]
+                SelectList [184-201]
+                  SelectColumn [184-185]
+                    IntLiteral(1) [184-185]
+                  SelectColumn [187-193]
+                    StringLiteral [187-193]
+                      StringLiteralComponent('ripe') [187-193]
+                  SelectColumn [195-201]
+                    StringLiteral [195-201]
+                      StringLiteralComponent('true') [195-201]
+    Select [206-305]
+      SelectList [213-281]
+        SelectColumn [213-281]
+          FunctionCall [213-268]
+            PathExpression [213-224]
+              Identifier(JSON_OBJECT) [213-224]
+            FunctionCall [225-244]
+              PathExpression [225-234]
+                Identifier(ARRAY_AGG) [225-234]
+              PathExpression [235-243]
+                Identifier(json_key) [235-243]
+            FunctionCall [246-267]
+              PathExpression [246-255]
+                Identifier(ARRAY_AGG) [246-255]
+              PathExpression [256-266]
+                Identifier(json_value) [256-266]
+          Alias [269-281]
+            Identifier(json_data) [272-281]
+      FromClause [282-293]
+        TablePathExpression [287-293]
+          PathExpression [287-293]
+            Identifier(Fruits) [287-293]
+      GroupBy [294-305]
+        GroupingItem [303-305]
+          PathExpression [303-305]
+            Identifier(id) [303-305]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_object_signature2
+-- Error: The number of keys and values must match.
+SELECT JSON_OBJECT(['a', 'b'], [10]) AS json_data
+--
+QueryStatement [52-101]
+  Query [52-101]
+    Select [52-101]
+      SelectList [59-101]
+        SelectColumn [59-101]
+          FunctionCall [59-88]
+            PathExpression [59-70]
+              Identifier(JSON_OBJECT) [59-70]
+            ArrayConstructor [71-81]
+              StringLiteral [72-75]
+                StringLiteralComponent('a') [72-75]
+              StringLiteral [77-80]
+                StringLiteralComponent('b') [77-80]
+            ArrayConstructor [83-87]
+              IntLiteral(10) [84-86]
+          Alias [89-101]
+            Identifier(json_data) [92-101]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_object_signature2
+-- Error: The keys array can't be NULL.
+SELECT JSON_OBJECT(CAST(NULL AS ARRAY), [10, 20]) AS json_data
+--
+QueryStatement [40-110]
+  Query [40-110]
+    Select [40-110]
+      SelectList [47-110]
+        SelectColumn [47-110]
+          FunctionCall [47-97]
+            PathExpression [47-58]
+              Identifier(JSON_OBJECT) [47-58]
+            CastExpression [59-86]
+              NullLiteral(NULL) [64-68]
+              ArrayType [72-85]
+                SimpleType [78-84]
+                  PathExpression [78-84]
+                    Identifier(STRING) [78-84]
+            ArrayConstructor [88-96]
+              IntLiteral(10) [89-91]
+              IntLiteral(20) [93-95]
+          Alias [98-110]
+            Identifier(json_data) [101-110]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_object_signature2
+-- Error: The values array can't be NULL.
+SELECT JSON_OBJECT(['a', 'b'], CAST(NULL AS ARRAY)) AS json_data
+--
+QueryStatement [42-113]
+  Query [42-113]
+    Select [42-113]
+      SelectList [49-113]
+        SelectColumn [49-113]
+          FunctionCall [49-100]
+            PathExpression [49-60]
+              Identifier(JSON_OBJECT) [49-60]
+            ArrayConstructor [61-71]
+              StringLiteral [62-65]
+                StringLiteralComponent('a') [62-65]
+              StringLiteral [67-70]
+                StringLiteralComponent('b') [67-70]
+            CastExpression [73-99]
+              NullLiteral(NULL) [78-82]
+              ArrayType [86-98]
+                SimpleType [92-97]
+                  PathExpression [92-97]
+                    Identifier(INT64) [92-97]
+          Alias [101-113]
+            Identifier(json_data) [104-113]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+[mode=expression]
+JSON_QUERY(json_string_expr, json_path)
+--
+FunctionCall [0-39]
+  PathExpression [0-10]
+    Identifier(JSON_QUERY) [0-10]
+  PathExpression [11-27]
+    Identifier(json_string_expr) [11-27]
+  PathExpression [29-38]
+    Identifier(json_path) [29-38]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+[mode=expression]
+JSON_QUERY(json_expr, json_path)
+--
+FunctionCall [0-32]
+  PathExpression [0-10]
+    Identifier(JSON_QUERY) [0-10]
+  PathExpression [11-20]
+    Identifier(json_expr) [11-20]
+  PathExpression [22-31]
+    Identifier(json_path) [22-31]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+[mode=expression]
+'{"class": {"students": [{"name": "Jane"}]}}'
+--
+StringLiteral [0-45]
+  StringLiteralComponent('{"class": {"students": [{"name": "Jane"}]}}') [0-45]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+SELECT JSON_QUERY("null", "$") -- Returns a SQL NULL
+--
+QueryStatement [0-30]
+  Query [0-30]
+    Select [0-30]
+      SelectList [7-30]
+        SelectColumn [7-30]
+          FunctionCall [7-30]
+            PathExpression [7-17]
+              Identifier(JSON_QUERY) [7-17]
+            StringLiteral [18-24]
+              StringLiteralComponent("null") [18-24]
+            StringLiteral [26-29]
+              StringLiteralComponent("$") [26-29]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+SELECT JSON_QUERY(JSON 'null', "$") -- Returns a JSON 'null'
+--
+QueryStatement [0-35]
+  Query [0-35]
+    Select [0-35]
+      SelectList [7-35]
+        SelectColumn [7-35]
+          FunctionCall [7-35]
+            PathExpression [7-17]
+              Identifier(JSON_QUERY) [7-17]
+            JSONLiteral [18-29]
+              StringLiteral [23-29]
+                StringLiteralComponent('null') [23-29]
+            StringLiteral [31-34]
+              StringLiteralComponent("$") [31-34]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+SELECT
+  JSON_QUERY(
+    JSON '{"class": {"students": [{"id": 5}, {"id": 12}]}}',
+    '$.class') AS json_data
+--
+QueryStatement [0-109]
+  Query [0-109]
+    Select [0-109]
+      SelectList [9-109]
+        SelectColumn [9-109]
+          FunctionCall [9-96]
+            PathExpression [9-19]
+              Identifier(JSON_QUERY) [9-19]
+            JSONLiteral [25-80]
+              StringLiteral [30-80]
+                StringLiteralComponent('{"class": {"students": [{"id": 5}, {"id": 12}]}}') [30-80]
+            StringLiteral [86-95]
+              StringLiteralComponent('$.class') [86-95]
+          Alias [97-109]
+            Identifier(json_data) [100-109]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+SELECT
+  JSON_QUERY('{"class": {"students": [{"name": "Jane"}]}}', '$') AS json_text_string
+--
+QueryStatement [0-91]
+  Query [0-91]
+    Select [0-91]
+      SelectList [9-91]
+        SelectColumn [9-91]
+          FunctionCall [9-71]
+            PathExpression [9-19]
+              Identifier(JSON_QUERY) [9-19]
+            StringLiteral [20-65]
+              StringLiteralComponent('{"class": {"students": [{"name": "Jane"}]}}') [20-65]
+            StringLiteral [67-70]
+              StringLiteralComponent('$') [67-70]
+          Alias [72-91]
+            Identifier(json_text_string) [75-91]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+SELECT JSON_QUERY('{"class": {"students": []}}', '$') AS json_text_string
+--
+QueryStatement [0-73]
+  Query [0-73]
+    Select [0-73]
+      SelectList [7-73]
+        SelectColumn [7-73]
+          FunctionCall [7-53]
+            PathExpression [7-17]
+              Identifier(JSON_QUERY) [7-17]
+            StringLiteral [18-47]
+              StringLiteralComponent('{"class": {"students": []}}') [18-47]
+            StringLiteral [49-52]
+              StringLiteralComponent('$') [49-52]
+          Alias [54-73]
+            Identifier(json_text_string) [57-73]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+SELECT
+  JSON_QUERY(
+    '{"class": {"students": [{"name": "John"},{"name": "Jamie"}]}}',
+    '$') AS json_text_string
+--
+QueryStatement [0-118]
+  Query [0-118]
+    Select [0-118]
+      SelectList [9-118]
+        SelectColumn [9-118]
+          FunctionCall [9-98]
+            PathExpression [9-19]
+              Identifier(JSON_QUERY) [9-19]
+            StringLiteral [25-88]
+              StringLiteralComponent('{"class": {"students": [{"name": "John"},{"name": "Jamie"}]}}') [25-88]
+            StringLiteral [94-97]
+              StringLiteralComponent('$') [94-97]
+          Alias [99-118]
+            Identifier(json_text_string) [102-118]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+SELECT
+  JSON_QUERY(
+    '{"class": {"students": [{"name": "Jane"}]}}',
+    '$.class.students[0]') AS first_student
+--
+QueryStatement [0-115]
+  Query [0-115]
+    Select [0-115]
+      SelectList [9-115]
+        SelectColumn [9-115]
+          FunctionCall [9-98]
+            PathExpression [9-19]
+              Identifier(JSON_QUERY) [9-19]
+            StringLiteral [25-70]
+              StringLiteralComponent('{"class": {"students": [{"name": "Jane"}]}}') [25-70]
+            StringLiteral [76-97]
+              StringLiteralComponent('$.class.students[0]') [76-97]
+          Alias [99-115]
+            Identifier(first_student) [102-115]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+SELECT
+  JSON_QUERY('{"class": {"students": []}}', '$.class.students[0]') AS first_student
+--
+QueryStatement [0-90]
+  Query [0-90]
+    Select [0-90]
+      SelectList [9-90]
+        SelectColumn [9-90]
+          FunctionCall [9-73]
+            PathExpression [9-19]
+              Identifier(JSON_QUERY) [9-19]
+            StringLiteral [20-49]
+              StringLiteralComponent('{"class": {"students": []}}') [20-49]
+            StringLiteral [51-72]
+              StringLiteralComponent('$.class.students[0]') [51-72]
+          Alias [74-90]
+            Identifier(first_student) [77-90]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+SELECT
+  JSON_QUERY(
+    '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}',
+    '$.class.students[0]') AS first_student
+--
+QueryStatement [0-134]
+  Query [0-134]
+    Select [0-134]
+      SelectList [9-134]
+        SelectColumn [9-134]
+          FunctionCall [9-117]
+            PathExpression [9-19]
+              Identifier(JSON_QUERY) [9-19]
+            StringLiteral [25-89]
+              StringLiteralComponent('{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}') [25-89]
+            StringLiteral [95-116]
+              StringLiteralComponent('$.class.students[0]') [95-116]
+          Alias [118-134]
+            Identifier(first_student) [121-134]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+SELECT
+  JSON_QUERY(
+    '{"class": {"students": [{"name": "Jane"}]}}',
+    '$.class.students[1].name') AS second_student
+--
+QueryStatement [0-121]
+  Query [0-121]
+    Select [0-121]
+      SelectList [9-121]
+        SelectColumn [9-121]
+          FunctionCall [9-103]
+            PathExpression [9-19]
+              Identifier(JSON_QUERY) [9-19]
+            StringLiteral [25-70]
+              StringLiteralComponent('{"class": {"students": [{"name": "Jane"}]}}') [25-70]
+            StringLiteral [76-102]
+              StringLiteralComponent('$.class.students[1].name') [76-102]
+          Alias [104-121]
+            Identifier(second_student) [107-121]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+SELECT
+  JSON_QUERY(
+    '{"class": {"students": []}}',
+    '$.class.students[1].name') AS second_student
+--
+QueryStatement [0-105]
+  Query [0-105]
+    Select [0-105]
+      SelectList [9-105]
+        SelectColumn [9-105]
+          FunctionCall [9-87]
+            PathExpression [9-19]
+              Identifier(JSON_QUERY) [9-19]
+            StringLiteral [25-54]
+              StringLiteralComponent('{"class": {"students": []}}') [25-54]
+            StringLiteral [60-86]
+              StringLiteralComponent('$.class.students[1].name') [60-86]
+          Alias [88-105]
+            Identifier(second_student) [91-105]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+SELECT
+  JSON_QUERY(
+    '{"class": {"students": [{"name": "John"}, {"name": null}]}}',
+    '$.class.students[1].name') AS second_student
+--
+QueryStatement [0-137]
+  Query [0-137]
+    Select [0-137]
+      SelectList [9-137]
+        SelectColumn [9-137]
+          FunctionCall [9-119]
+            PathExpression [9-19]
+              Identifier(JSON_QUERY) [9-19]
+            StringLiteral [25-86]
+              StringLiteralComponent('{"class": {"students": [{"name": "John"}, {"name": null}]}}') [25-86]
+            StringLiteral [92-118]
+              StringLiteralComponent('$.class.students[1].name') [92-118]
+          Alias [120-137]
+            Identifier(second_student) [123-137]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+SELECT
+  JSON_QUERY(
+    '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}',
+    '$.class.students[1].name') AS second_student
+--
+QueryStatement [0-140]
+  Query [0-140]
+    Select [0-140]
+      SelectList [9-140]
+        SelectColumn [9-140]
+          FunctionCall [9-122]
+            PathExpression [9-19]
+              Identifier(JSON_QUERY) [9-19]
+            StringLiteral [25-89]
+              StringLiteralComponent('{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}') [25-89]
+            StringLiteral [95-121]
+              StringLiteralComponent('$.class.students[1].name') [95-121]
+          Alias [123-140]
+            Identifier(second_student) [126-140]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+SELECT
+  JSON_QUERY(
+    '{"class": {"students": [{"name": "Jane"}]}}',
+    '$.class."students"') AS student_names
+--
+QueryStatement [0-114]
+  Query [0-114]
+    Select [0-114]
+      SelectList [9-114]
+        SelectColumn [9-114]
+          FunctionCall [9-97]
+            PathExpression [9-19]
+              Identifier(JSON_QUERY) [9-19]
+            StringLiteral [25-70]
+              StringLiteralComponent('{"class": {"students": [{"name": "Jane"}]}}') [25-70]
+            StringLiteral [76-96]
+              StringLiteralComponent('$.class."students"') [76-96]
+          Alias [98-114]
+            Identifier(student_names) [101-114]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+SELECT
+  JSON_QUERY(
+    '{"class": {"students": []}}',
+    '$.class."students"') AS student_names
+--
+QueryStatement [0-98]
+  Query [0-98]
+    Select [0-98]
+      SelectList [9-98]
+        SelectColumn [9-98]
+          FunctionCall [9-81]
+            PathExpression [9-19]
+              Identifier(JSON_QUERY) [9-19]
+            StringLiteral [25-54]
+              StringLiteralComponent('{"class": {"students": []}}') [25-54]
+            StringLiteral [60-80]
+              StringLiteralComponent('$.class."students"') [60-80]
+          Alias [82-98]
+            Identifier(student_names) [85-98]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+SELECT
+  JSON_QUERY(
+    '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}',
+    '$.class."students"') AS student_names
+--
+QueryStatement [0-133]
+  Query [0-133]
+    Select [0-133]
+      SelectList [9-133]
+        SelectColumn [9-133]
+          FunctionCall [9-116]
+            PathExpression [9-19]
+              Identifier(JSON_QUERY) [9-19]
+            StringLiteral [25-89]
+              StringLiteralComponent('{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}') [25-89]
+            StringLiteral [95-115]
+              StringLiteralComponent('$.class."students"') [95-115]
+          Alias [117-133]
+            Identifier(student_names) [120-133]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+SELECT JSON_QUERY('{"a": null}', "$.a")
+--
+QueryStatement [0-39]
+  Query [0-39]
+    Select [0-39]
+      SelectList [7-39]
+        SelectColumn [7-39]
+          FunctionCall [7-39]
+            PathExpression [7-17]
+              Identifier(JSON_QUERY) [7-17]
+            StringLiteral [18-31]
+              StringLiteralComponent('{"a": null}') [18-31]
+            StringLiteral [33-38]
+              StringLiteralComponent("$.a") [33-38]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+-- Returns a SQL NULL
+SELECT JSON_QUERY('{"a": null}', "$.b")
+--
+QueryStatement [22-61]
+  Query [22-61]
+    Select [22-61]
+      SelectList [29-61]
+        SelectColumn [29-61]
+          FunctionCall [29-61]
+            PathExpression [29-39]
+              Identifier(JSON_QUERY) [29-39]
+            StringLiteral [40-53]
+              StringLiteralComponent('{"a": null}') [40-53]
+            StringLiteral [55-60]
+              StringLiteralComponent("$.b") [55-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+SELECT JSON_QUERY(JSON '{"a": null}', "$.a")
+--
+QueryStatement [0-44]
+  Query [0-44]
+    Select [0-44]
+      SelectList [7-44]
+        SelectColumn [7-44]
+          FunctionCall [7-44]
+            PathExpression [7-17]
+              Identifier(JSON_QUERY) [7-17]
+            JSONLiteral [18-36]
+              StringLiteral [23-36]
+                StringLiteralComponent('{"a": null}') [23-36]
+            StringLiteral [38-43]
+              StringLiteralComponent("$.a") [38-43]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query
+-- Returns a JSON 'null'
+SELECT JSON_QUERY(JSON '{"a": null}', "$.b")
+--
+QueryStatement [25-69]
+  Query [25-69]
+    Select [25-69]
+      SelectList [32-69]
+        SelectColumn [32-69]
+          FunctionCall [32-69]
+            PathExpression [32-42]
+              Identifier(JSON_QUERY) [32-42]
+            JSONLiteral [43-61]
+              StringLiteral [48-61]
+                StringLiteralComponent('{"a": null}') [48-61]
+            StringLiteral [63-68]
+              StringLiteralComponent("$.b") [63-68]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query_array
+[mode=expression]
+'["a", "b", {"key": "c"}]'
+--
+StringLiteral [0-26]
+  StringLiteralComponent('["a", "b", {"key": "c"}]') [0-26]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query_array
+[mode=expression]
+JSON '["a", "b", {"key": "c"}]'
+--
+JSONLiteral [0-31]
+  StringLiteral [5-31]
+    StringLiteralComponent('["a", "b", {"key": "c"}]') [5-31]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query_array
+SELECT JSON_QUERY_ARRAY(
+  JSON '{"fruits": ["apples", "oranges", "grapes"]}', '$.fruits'
+  ) AS json_array
+--
+QueryStatement [0-107]
+  Query [0-107]
+    Select [0-107]
+      SelectList [7-107]
+        SelectColumn [7-107]
+          FunctionCall [7-93]
+            PathExpression [7-23]
+              Identifier(JSON_QUERY_ARRAY) [7-23]
+            JSONLiteral [27-77]
+              StringLiteral [32-77]
+                StringLiteralComponent('{"fruits": ["apples", "oranges", "grapes"]}') [32-77]
+            StringLiteral [79-89]
+              StringLiteralComponent('$.fruits') [79-89]
+          Alias [94-107]
+            Identifier(json_array) [97-107]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query_array
+SELECT JSON_QUERY_ARRAY('[1, 2, 3]') AS string_array
+--
+QueryStatement [0-52]
+  Query [0-52]
+    Select [0-52]
+      SelectList [7-52]
+        SelectColumn [7-52]
+          FunctionCall [7-36]
+            PathExpression [7-23]
+              Identifier(JSON_QUERY_ARRAY) [7-23]
+            StringLiteral [24-35]
+              StringLiteralComponent('[1, 2, 3]') [24-35]
+          Alias [37-52]
+            Identifier(string_array) [40-52]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query_array
+SELECT ARRAY(
+  SELECT CAST(integer_element AS INT64)
+  FROM UNNEST(
+    JSON_QUERY_ARRAY('[1, 2, 3]','$')
+  ) AS integer_element
+) AS integer_array
+--
+QueryStatement [0-148]
+  Query [0-148]
+    Select [0-148]
+      SelectList [7-148]
+        SelectColumn [7-148]
+          ExpressionSubquery(modifier=ARRAY) [7-131]
+            Query [16-129]
+              Select [16-129]
+                SelectList [23-53]
+                  SelectColumn [23-53]
+                    CastExpression [23-53]
+                      PathExpression [28-43]
+                        Identifier(integer_element) [28-43]
+                      SimpleType [47-52]
+                        PathExpression [47-52]
+                          Identifier(INT64) [47-52]
+                FromClause [56-129]
+                  TablePathExpression [61-129]
+                    UnnestExpression [61-110]
+                      ExpressionWithOptAlias [73-106]
+                        FunctionCall [73-106]
+                          PathExpression [73-89]
+                            Identifier(JSON_QUERY_ARRAY) [73-89]
+                          StringLiteral [90-101]
+                            StringLiteralComponent('[1, 2, 3]') [90-101]
+                          StringLiteral [102-105]
+                            StringLiteralComponent('$') [102-105]
+                    Alias [111-129]
+                      Identifier(integer_element) [114-129]
+          Alias [132-148]
+            Identifier(integer_array) [135-148]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query_array
+-- Doesn't strip the double quotes
+SELECT JSON_QUERY_ARRAY('["apples", "oranges", "grapes"]', '$') AS string_array
+--
+QueryStatement [35-114]
+  Query [35-114]
+    Select [35-114]
+      SelectList [42-114]
+        SelectColumn [42-114]
+          FunctionCall [42-98]
+            PathExpression [42-58]
+              Identifier(JSON_QUERY_ARRAY) [42-58]
+            StringLiteral [59-92]
+              StringLiteralComponent('["apples", "oranges", "grapes"]') [59-92]
+            StringLiteral [94-97]
+              StringLiteralComponent('$') [94-97]
+          Alias [99-114]
+            Identifier(string_array) [102-114]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query_array
+-- Strips the double quotes
+SELECT ARRAY(
+  SELECT JSON_VALUE(string_element, '$')
+  FROM UNNEST(JSON_QUERY_ARRAY('["apples", "oranges", "grapes"]', '$')) AS string_element
+) AS string_array
+--
+QueryStatement [28-190]
+  Query [28-190]
+    Select [28-190]
+      SelectList [35-190]
+        SelectColumn [35-190]
+          ExpressionSubquery(modifier=ARRAY) [35-174]
+            Query [44-172]
+              Select [44-172]
+                SelectList [51-82]
+                  SelectColumn [51-82]
+                    FunctionCall [51-82]
+                      PathExpression [51-61]
+                        Identifier(JSON_VALUE) [51-61]
+                      PathExpression [62-76]
+                        Identifier(string_element) [62-76]
+                      StringLiteral [78-81]
+                        StringLiteralComponent('$') [78-81]
+                FromClause [85-172]
+                  TablePathExpression [90-172]
+                    UnnestExpression [90-154]
+                      ExpressionWithOptAlias [97-153]
+                        FunctionCall [97-153]
+                          PathExpression [97-113]
+                            Identifier(JSON_QUERY_ARRAY) [97-113]
+                          StringLiteral [114-147]
+                            StringLiteralComponent('["apples", "oranges", "grapes"]') [114-147]
+                          StringLiteral [149-152]
+                            StringLiteralComponent('$') [149-152]
+                    Alias [155-172]
+                      Identifier(string_element) [158-172]
+          Alias [175-190]
+            Identifier(string_array) [178-190]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query_array
+SELECT JSON_QUERY_ARRAY(
+  '{"fruit": [{"apples": 5, "oranges": 10}, {"apples": 2, "oranges": 4}], "vegetables": [{"lettuce": 7, "kale": 8}]}',
+  '$.fruit'
+) AS string_array
+--
+QueryStatement [0-173]
+  Query [0-173]
+    Select [0-173]
+      SelectList [7-173]
+        SelectColumn [7-173]
+          FunctionCall [7-157]
+            PathExpression [7-23]
+              Identifier(JSON_QUERY_ARRAY) [7-23]
+            StringLiteral [27-142]
+              StringLiteralComponent('{"fruit": [{"apples": 5, "oranges": 10}, {"apples": 2, "oranges": 4}], "vegetables": [{"lettuce": 7, "kale": 8}]}') [27-142]
+            StringLiteral [146-155]
+              StringLiteralComponent('$.fruit') [146-155]
+          Alias [158-173]
+            Identifier(string_array) [161-173]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query_array
+SELECT JSON_QUERY_ARRAY('{"fruits": ["apples", "oranges", "grapes"]}', '$.fruits') AS string_array
+--
+QueryStatement [0-98]
+  Query [0-98]
+    Select [0-98]
+      SelectList [7-98]
+        SelectColumn [7-98]
+          FunctionCall [7-82]
+            PathExpression [7-23]
+              Identifier(JSON_QUERY_ARRAY) [7-23]
+            StringLiteral [24-69]
+              StringLiteralComponent('{"fruits": ["apples", "oranges", "grapes"]}') [24-69]
+            StringLiteral [71-81]
+              StringLiteralComponent('$.fruits') [71-81]
+          Alias [83-98]
+            Identifier(string_array) [86-98]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query_array
+SELECT JSON_QUERY_ARRAY('{"fruits": ["apples", "oranges", "grapes"]}', '$."fruits"') AS string_array
+--
+QueryStatement [0-100]
+  Query [0-100]
+    Select [0-100]
+      SelectList [7-100]
+        SelectColumn [7-100]
+          FunctionCall [7-84]
+            PathExpression [7-23]
+              Identifier(JSON_QUERY_ARRAY) [7-23]
+            StringLiteral [24-69]
+              StringLiteralComponent('{"fruits": ["apples", "oranges", "grapes"]}') [24-69]
+            StringLiteral [71-83]
+              StringLiteralComponent('$."fruits"') [71-83]
+          Alias [85-100]
+            Identifier(string_array) [88-100]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query_array
+SELECT JSON_QUERY_ARRAY('{"a.b": {"c": ["world"]}}', '$."a.b".c') AS hello
+--
+QueryStatement [0-74]
+  Query [0-74]
+    Select [0-74]
+      SelectList [7-74]
+        SelectColumn [7-74]
+          FunctionCall [7-65]
+            PathExpression [7-23]
+              Identifier(JSON_QUERY_ARRAY) [7-23]
+            StringLiteral [24-51]
+              StringLiteralComponent('{"a.b": {"c": ["world"]}}') [24-51]
+            StringLiteral [53-64]
+              StringLiteralComponent('$."a.b".c') [53-64]
+          Alias [66-74]
+            Identifier(hello) [69-74]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query_array
+-- An error is returned if you provide an invalid JSONPath.
+SELECT JSON_QUERY_ARRAY('["foo", "bar", "baz"]', 'INVALID_JSONPath') AS result
+--
+QueryStatement [60-138]
+  Query [60-138]
+    Select [60-138]
+      SelectList [67-138]
+        SelectColumn [67-138]
+          FunctionCall [67-128]
+            PathExpression [67-83]
+              Identifier(JSON_QUERY_ARRAY) [67-83]
+            StringLiteral [84-107]
+              StringLiteralComponent('["foo", "bar", "baz"]') [84-107]
+            StringLiteral [109-127]
+              StringLiteralComponent('INVALID_JSONPath') [109-127]
+          Alias [129-138]
+            Identifier(result) [132-138]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query_array
+-- If the JSONPath doesn't refer to an array, then NULL is returned.
+SELECT JSON_QUERY_ARRAY('{"a": "foo"}', '$.a') AS result
+--
+QueryStatement [69-125]
+  Query [69-125]
+    Select [69-125]
+      SelectList [76-125]
+        SelectColumn [76-125]
+          FunctionCall [76-115]
+            PathExpression [76-92]
+              Identifier(JSON_QUERY_ARRAY) [76-92]
+            StringLiteral [93-107]
+              StringLiteralComponent('{"a": "foo"}') [93-107]
+            StringLiteral [109-114]
+              StringLiteralComponent('$.a') [109-114]
+          Alias [116-125]
+            Identifier(result) [119-125]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query_array
+-- If a key that doesn't exist is specified, then the result is NULL.
+SELECT JSON_QUERY_ARRAY('{"a": "foo"}', '$.b') AS result
+--
+QueryStatement [70-126]
+  Query [70-126]
+    Select [70-126]
+      SelectList [77-126]
+        SelectColumn [77-126]
+          FunctionCall [77-116]
+            PathExpression [77-93]
+              Identifier(JSON_QUERY_ARRAY) [77-93]
+            StringLiteral [94-108]
+              StringLiteralComponent('{"a": "foo"}') [94-108]
+            StringLiteral [110-115]
+              StringLiteralComponent('$.b') [110-115]
+          Alias [117-126]
+            Identifier(result) [120-126]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_query_array
+-- Empty arrays in JSON-formatted strings are supported.
+SELECT JSON_QUERY_ARRAY('{"a": "foo", "b": []}', '$.b') AS result
+--
+QueryStatement [57-122]
+  Query [57-122]
+    Select [57-122]
+      SelectList [64-122]
+        SelectColumn [64-122]
+          FunctionCall [64-112]
+            PathExpression [64-80]
+              Identifier(JSON_QUERY_ARRAY) [64-80]
+            StringLiteral [81-104]
+              StringLiteralComponent('{"a": "foo", "b": []}') [81-104]
+            StringLiteral [106-111]
+              StringLiteralComponent('$.b') [106-111]
+          Alias [113-122]
+            Identifier(result) [116-122]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_remove
+SELECT JSON_REMOVE(JSON '["a", ["b", "c"], "d"]', '$[1]') AS json_data
+--
+QueryStatement [0-70]
+  Query [0-70]
+    Select [0-70]
+      SelectList [7-70]
+        SelectColumn [7-70]
+          FunctionCall [7-57]
+            PathExpression [7-18]
+              Identifier(JSON_REMOVE) [7-18]
+            JSONLiteral [19-48]
+              StringLiteral [24-48]
+                StringLiteralComponent('["a", ["b", "c"], "d"]') [24-48]
+            StringLiteral [50-56]
+              StringLiteralComponent('$[1]') [50-56]
+          Alias [58-70]
+            Identifier(json_data) [61-70]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_remove
+WITH T AS (SELECT JSON '{"a": {"b": 10, "c": 20}}' AS data)
+SELECT JSON_REMOVE(data.a, '$.b') AS json_data FROM T
+--
+QueryStatement [0-113]
+  Query [0-113]
+    WithClause [0-59]
+      WithClauseEntry [5-59]
+        AliasedQuery [5-59]
+          Identifier(T) [5-6]
+          Query [10-59]
+            Select [11-58]
+              SelectList [18-58]
+                SelectColumn [18-58]
+                  JSONLiteral [18-50]
+                    StringLiteral [23-50]
+                      StringLiteralComponent('{"a": {"b": 10, "c": 20}}') [23-50]
+                  Alias [51-58]
+                    Identifier(data) [54-58]
+    Select [60-113]
+      SelectList [67-106]
+        SelectColumn [67-106]
+          FunctionCall [67-93]
+            PathExpression [67-78]
+              Identifier(JSON_REMOVE) [67-78]
+            PathExpression [79-85]
+              Identifier(data) [79-83]
+              Identifier(a) [84-85]
+            StringLiteral [87-92]
+              StringLiteralComponent('$.b') [87-92]
+          Alias [94-106]
+            Identifier(json_data) [97-106]
+      FromClause [107-113]
+        TablePathExpression [112-113]
+          PathExpression [112-113]
+            Identifier(T) [112-113]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_remove
+SELECT JSON_REMOVE(JSON '["a", ["b", "c"], "d"]', '$[1]', '$[1]') AS json_data
+--
+QueryStatement [0-78]
+  Query [0-78]
+    Select [0-78]
+      SelectList [7-78]
+        SelectColumn [7-78]
+          FunctionCall [7-65]
+            PathExpression [7-18]
+              Identifier(JSON_REMOVE) [7-18]
+            JSONLiteral [19-48]
+              StringLiteral [24-48]
+                StringLiteralComponent('["a", ["b", "c"], "d"]') [24-48]
+            StringLiteral [50-56]
+              StringLiteralComponent('$[1]') [50-56]
+            StringLiteral [58-64]
+              StringLiteralComponent('$[1]') [58-64]
+          Alias [66-78]
+            Identifier(json_data) [69-78]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_remove
+SELECT JSON_REMOVE(JSON '["a", ["b", "c"], "d"]', '$[1]', '$[1]', '$[0]') AS json_data
+--
+QueryStatement [0-86]
+  Query [0-86]
+    Select [0-86]
+      SelectList [7-86]
+        SelectColumn [7-86]
+          FunctionCall [7-73]
+            PathExpression [7-18]
+              Identifier(JSON_REMOVE) [7-18]
+            JSONLiteral [19-48]
+              StringLiteral [24-48]
+                StringLiteralComponent('["a", ["b", "c"], "d"]') [24-48]
+            StringLiteral [50-56]
+              StringLiteralComponent('$[1]') [50-56]
+            StringLiteral [58-64]
+              StringLiteralComponent('$[1]') [58-64]
+            StringLiteral [66-72]
+              StringLiteralComponent('$[0]') [66-72]
+          Alias [74-86]
+            Identifier(json_data) [77-86]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_remove
+SELECT JSON_REMOVE(JSON '{"a": {"b": {"c": "d"}}}', '$.a.b.c') AS json_data
+--
+QueryStatement [0-75]
+  Query [0-75]
+    Select [0-75]
+      SelectList [7-75]
+        SelectColumn [7-75]
+          FunctionCall [7-62]
+            PathExpression [7-18]
+              Identifier(JSON_REMOVE) [7-18]
+            JSONLiteral [19-50]
+              StringLiteral [24-50]
+                StringLiteralComponent('{"a": {"b": {"c": "d"}}}') [24-50]
+            StringLiteral [52-61]
+              StringLiteralComponent('$.a.b.c') [52-61]
+          Alias [63-75]
+            Identifier(json_data) [66-75]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_remove
+SELECT JSON_REMOVE(JSON '{"a": {"b": {"c": "d"}}}', '$.a.b') AS json_data
+--
+QueryStatement [0-73]
+  Query [0-73]
+    Select [0-73]
+      SelectList [7-73]
+        SelectColumn [7-73]
+          FunctionCall [7-60]
+            PathExpression [7-18]
+              Identifier(JSON_REMOVE) [7-18]
+            JSONLiteral [19-50]
+              StringLiteral [24-50]
+                StringLiteralComponent('{"a": {"b": {"c": "d"}}}') [24-50]
+            StringLiteral [52-59]
+              StringLiteralComponent('$.a.b') [52-59]
+          Alias [61-73]
+            Identifier(json_data) [64-73]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_remove
+SELECT JSON_REMOVE(JSON '{"a": 1}', '$.b') AS json_data
+--
+QueryStatement [0-55]
+  Query [0-55]
+    Select [0-55]
+      SelectList [7-55]
+        SelectColumn [7-55]
+          FunctionCall [7-42]
+            PathExpression [7-18]
+              Identifier(JSON_REMOVE) [7-18]
+            JSONLiteral [19-34]
+              StringLiteral [24-34]
+                StringLiteralComponent('{"a": 1}') [24-34]
+            StringLiteral [36-41]
+              StringLiteralComponent('$.b') [36-41]
+          Alias [43-55]
+            Identifier(json_data) [46-55]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_remove
+SELECT JSON_REMOVE(JSON '{"a": [1, 2, 3]}', '$.a[0]', '$.a.b', '$.b', '$.a[0]') AS json_data
+--
+QueryStatement [0-92]
+  Query [0-92]
+    Select [0-92]
+      SelectList [7-92]
+        SelectColumn [7-92]
+          FunctionCall [7-79]
+            PathExpression [7-18]
+              Identifier(JSON_REMOVE) [7-18]
+            JSONLiteral [19-42]
+              StringLiteral [24-42]
+                StringLiteralComponent('{"a": [1, 2, 3]}') [24-42]
+            StringLiteral [44-52]
+              StringLiteralComponent('$.a[0]') [44-52]
+            StringLiteral [54-61]
+              StringLiteralComponent('$.a.b') [54-61]
+            StringLiteral [63-68]
+              StringLiteralComponent('$.b') [63-68]
+            StringLiteral [70-78]
+              StringLiteralComponent('$.a[0]') [70-78]
+          Alias [80-92]
+            Identifier(json_data) [83-92]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_remove
+-- Error: The JSONPath can't be '$'
+SELECT JSON_REMOVE(JSON '{}', '$') AS json_data
+--
+QueryStatement [36-83]
+  Query [36-83]
+    Select [36-83]
+      SelectList [43-83]
+        SelectColumn [43-83]
+          FunctionCall [43-70]
+            PathExpression [43-54]
+              Identifier(JSON_REMOVE) [43-54]
+            JSONLiteral [55-64]
+              StringLiteral [60-64]
+                StringLiteralComponent('{}') [60-64]
+            StringLiteral [66-69]
+              StringLiteralComponent('$') [66-69]
+          Alias [71-83]
+            Identifier(json_data) [74-83]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_remove
+SELECT JSON_REMOVE(JSON 'null', '$.a.b') AS json_data
+--
+QueryStatement [0-53]
+  Query [0-53]
+    Select [0-53]
+      SelectList [7-53]
+        SelectColumn [7-53]
+          FunctionCall [7-40]
+            PathExpression [7-18]
+              Identifier(JSON_REMOVE) [7-18]
+            JSONLiteral [19-30]
+              StringLiteral [24-30]
+                StringLiteralComponent('null') [24-30]
+            StringLiteral [32-39]
+              StringLiteralComponent('$.a.b') [32-39]
+          Alias [41-53]
+            Identifier(json_data) [44-53]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_set
+SELECT JSON_SET(JSON '{"a": 1}', '$', JSON '{"b": 2, "c": 3}') AS json_data
+--
+QueryStatement [0-75]
+  Query [0-75]
+    Select [0-75]
+      SelectList [7-75]
+        SelectColumn [7-75]
+          FunctionCall [7-62]
+            PathExpression [7-15]
+              Identifier(JSON_SET) [7-15]
+            JSONLiteral [16-31]
+              StringLiteral [21-31]
+                StringLiteralComponent('{"a": 1}') [21-31]
+            StringLiteral [33-36]
+              StringLiteralComponent('$') [33-36]
+            JSONLiteral [38-61]
+              StringLiteral [43-61]
+                StringLiteralComponent('{"b": 2, "c": 3}') [43-61]
+          Alias [63-75]
+            Identifier(json_data) [66-75]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_set
+SELECT JSON_SET(
+  JSON '{"a": 1}',
+  "$.b", 999,
+  create_if_missing => false) AS json_data
+--
+QueryStatement [0-92]
+  Query [0-92]
+    Select [0-92]
+      SelectList [7-92]
+        SelectColumn [7-92]
+          FunctionCall [7-79]
+            PathExpression [7-15]
+              Identifier(JSON_SET) [7-15]
+            JSONLiteral [19-34]
+              StringLiteral [24-34]
+                StringLiteralComponent('{"a": 1}') [24-34]
+            StringLiteral [38-43]
+              StringLiteralComponent("$.b") [38-43]
+            IntLiteral(999) [45-48]
+            NamedArgument [52-78]
+              Identifier(create_if_missing) [52-69]
+              BooleanLiteral(false) [73-78]
+          Alias [80-92]
+            Identifier(json_data) [83-92]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_set
+SELECT JSON_SET(
+  JSON '{"a": 1}',
+  "$.a", 999,
+  create_if_missing => false) AS json_data
+--
+QueryStatement [0-92]
+  Query [0-92]
+    Select [0-92]
+      SelectList [7-92]
+        SelectColumn [7-92]
+          FunctionCall [7-79]
+            PathExpression [7-15]
+              Identifier(JSON_SET) [7-15]
+            JSONLiteral [19-34]
+              StringLiteral [24-34]
+                StringLiteralComponent('{"a": 1}') [24-34]
+            StringLiteral [38-43]
+              StringLiteralComponent("$.a") [38-43]
+            IntLiteral(999) [45-48]
+            NamedArgument [52-78]
+              Identifier(create_if_missing) [52-69]
+              BooleanLiteral(false) [73-78]
+          Alias [80-92]
+            Identifier(json_data) [83-92]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_set
+SELECT JSON_SET(JSON '{"a": {}}', '$.a.b', 100) AS json_data
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-60]
+      SelectList [7-60]
+        SelectColumn [7-60]
+          FunctionCall [7-47]
+            PathExpression [7-15]
+              Identifier(JSON_SET) [7-15]
+            JSONLiteral [16-32]
+              StringLiteral [21-32]
+                StringLiteralComponent('{"a": {}}') [21-32]
+            StringLiteral [34-41]
+              StringLiteralComponent('$.a.b') [34-41]
+            IntLiteral(100) [43-46]
+          Alias [48-60]
+            Identifier(json_data) [51-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_set
+SELECT JSON_SET(JSON 'null', '$.a.b', 100) AS json_data
+--
+QueryStatement [0-55]
+  Query [0-55]
+    Select [0-55]
+      SelectList [7-55]
+        SelectColumn [7-55]
+          FunctionCall [7-42]
+            PathExpression [7-15]
+              Identifier(JSON_SET) [7-15]
+            JSONLiteral [16-27]
+              StringLiteral [21-27]
+                StringLiteralComponent('null') [21-27]
+            StringLiteral [29-36]
+              StringLiteralComponent('$.a.b') [29-36]
+            IntLiteral(100) [38-41]
+          Alias [43-55]
+            Identifier(json_data) [46-55]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_set
+SELECT JSON_SET(
+  JSON '{"a": 1}',
+  '$.b', 2,
+  '$.a.c', 100,
+  '$.d', 3) AS json_data
+--
+QueryStatement [0-88]
+  Query [0-88]
+    Select [0-88]
+      SelectList [7-88]
+        SelectColumn [7-88]
+          FunctionCall [7-75]
+            PathExpression [7-15]
+              Identifier(JSON_SET) [7-15]
+            JSONLiteral [19-34]
+              StringLiteral [24-34]
+                StringLiteralComponent('{"a": 1}') [24-34]
+            StringLiteral [38-43]
+              StringLiteralComponent('$.b') [38-43]
+            IntLiteral(2) [45-46]
+            StringLiteral [50-57]
+              StringLiteralComponent('$.a.c') [50-57]
+            IntLiteral(100) [59-62]
+            StringLiteral [66-71]
+              StringLiteralComponent('$.d') [66-71]
+            IntLiteral(3) [73-74]
+          Alias [76-88]
+            Identifier(json_data) [79-88]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_set
+SELECT JSON_SET(
+  JSON '{"a": 1}',
+  '$.a[2]', 100,
+  '$.b', 2) AS json_data
+--
+QueryStatement [0-77]
+  Query [0-77]
+    Select [0-77]
+      SelectList [7-77]
+        SelectColumn [7-77]
+          FunctionCall [7-64]
+            PathExpression [7-15]
+              Identifier(JSON_SET) [7-15]
+            JSONLiteral [19-34]
+              StringLiteral [24-34]
+                StringLiteralComponent('{"a": 1}') [24-34]
+            StringLiteral [38-46]
+              StringLiteralComponent('$.a[2]') [38-46]
+            IntLiteral(100) [48-51]
+            StringLiteral [55-60]
+              StringLiteralComponent('$.b') [55-60]
+            IntLiteral(2) [62-63]
+          Alias [65-77]
+            Identifier(json_data) [68-77]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_set
+SELECT JSON_SET(JSON '["a", ["b", "c"], "d"]', '$[1]', "foo") AS json_data
+--
+QueryStatement [0-74]
+  Query [0-74]
+    Select [0-74]
+      SelectList [7-74]
+        SelectColumn [7-74]
+          FunctionCall [7-61]
+            PathExpression [7-15]
+              Identifier(JSON_SET) [7-15]
+            JSONLiteral [16-45]
+              StringLiteral [21-45]
+                StringLiteralComponent('["a", ["b", "c"], "d"]') [21-45]
+            StringLiteral [47-53]
+              StringLiteralComponent('$[1]') [47-53]
+            StringLiteral [55-60]
+              StringLiteralComponent("foo") [55-60]
+          Alias [62-74]
+            Identifier(json_data) [65-74]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_set
+SELECT JSON_SET(JSON '["a", ["b", "c"], "d"]', '$[1][0]', "foo") AS json_data
+--
+QueryStatement [0-77]
+  Query [0-77]
+    Select [0-77]
+      SelectList [7-77]
+        SelectColumn [7-77]
+          FunctionCall [7-64]
+            PathExpression [7-15]
+              Identifier(JSON_SET) [7-15]
+            JSONLiteral [16-45]
+              StringLiteral [21-45]
+                StringLiteralComponent('["a", ["b", "c"], "d"]') [21-45]
+            StringLiteral [47-56]
+              StringLiteralComponent('$[1][0]') [47-56]
+            StringLiteral [58-63]
+              StringLiteralComponent("foo") [58-63]
+          Alias [65-77]
+            Identifier(json_data) [68-77]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_set
+SELECT JSON_SET(JSON 'null', '$[0][3]', "foo")
+--
+QueryStatement [0-46]
+  Query [0-46]
+    Select [0-46]
+      SelectList [7-46]
+        SelectColumn [7-46]
+          FunctionCall [7-46]
+            PathExpression [7-15]
+              Identifier(JSON_SET) [7-15]
+            JSONLiteral [16-27]
+              StringLiteral [21-27]
+                StringLiteralComponent('null') [21-27]
+            StringLiteral [29-38]
+              StringLiteralComponent('$[0][3]') [29-38]
+            StringLiteral [40-45]
+              StringLiteralComponent("foo") [40-45]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_set
+SELECT JSON_SET(JSON '["a", ["b", "c"], "d"]', '$[1][4]', "foo") AS json_data
+--
+QueryStatement [0-77]
+  Query [0-77]
+    Select [0-77]
+      SelectList [7-77]
+        SelectColumn [7-77]
+          FunctionCall [7-64]
+            PathExpression [7-15]
+              Identifier(JSON_SET) [7-15]
+            JSONLiteral [16-45]
+              StringLiteral [21-45]
+                StringLiteralComponent('["a", ["b", "c"], "d"]') [21-45]
+            StringLiteral [47-56]
+              StringLiteralComponent('$[1][4]') [47-56]
+            StringLiteral [58-63]
+              StringLiteralComponent("foo") [58-63]
+          Alias [65-77]
+            Identifier(json_data) [68-77]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_set
+SELECT JSON_SET(JSON '["a", ["b", "c"], "d"]', '$[1][0][0]', "foo") AS json_data
+--
+QueryStatement [0-80]
+  Query [0-80]
+    Select [0-80]
+      SelectList [7-80]
+        SelectColumn [7-80]
+          FunctionCall [7-67]
+            PathExpression [7-15]
+              Identifier(JSON_SET) [7-15]
+            JSONLiteral [16-45]
+              StringLiteral [21-45]
+                StringLiteralComponent('["a", ["b", "c"], "d"]') [21-45]
+            StringLiteral [47-59]
+              StringLiteralComponent('$[1][0][0]') [47-59]
+            StringLiteral [61-66]
+              StringLiteralComponent("foo") [61-66]
+          Alias [68-80]
+            Identifier(json_data) [71-80]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_set
+SELECT JSON_SET(JSON '["a", ["b", "c"], "d"]', '$[1][2][1]', "foo") AS json_data
+--
+QueryStatement [0-80]
+  Query [0-80]
+    Select [0-80]
+      SelectList [7-80]
+        SelectColumn [7-80]
+          FunctionCall [7-67]
+            PathExpression [7-15]
+              Identifier(JSON_SET) [7-15]
+            JSONLiteral [16-45]
+              StringLiteral [21-45]
+                StringLiteralComponent('["a", ["b", "c"], "d"]') [21-45]
+            StringLiteral [47-59]
+              StringLiteralComponent('$[1][2][1]') [47-59]
+            StringLiteral [61-66]
+              StringLiteralComponent("foo") [61-66]
+          Alias [68-80]
+            Identifier(json_data) [71-80]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_set
+SELECT JSON_SET(JSON '{}', '$.b[2].d', 100) AS json_data
+--
+QueryStatement [0-56]
+  Query [0-56]
+    Select [0-56]
+      SelectList [7-56]
+        SelectColumn [7-56]
+          FunctionCall [7-43]
+            PathExpression [7-15]
+              Identifier(JSON_SET) [7-15]
+            JSONLiteral [16-25]
+              StringLiteral [21-25]
+                StringLiteralComponent('{}') [21-25]
+            StringLiteral [27-37]
+              StringLiteralComponent('$.b[2].d') [27-37]
+            IntLiteral(100) [39-42]
+          Alias [44-56]
+            Identifier(json_data) [47-56]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_set
+SELECT JSON_SET(
+  JSON '{"a": 1, "b": {"c":3}, "d": [4]}',
+  '$.a', 'v1',
+  '$.b.e', 'v2',
+  '$.d[2]', 'v3') AS json_data
+--
+QueryStatement [0-122]
+  Query [0-122]
+    Select [0-122]
+      SelectList [7-122]
+        SelectColumn [7-122]
+          FunctionCall [7-109]
+            PathExpression [7-15]
+              Identifier(JSON_SET) [7-15]
+            JSONLiteral [19-58]
+              StringLiteral [24-58]
+                StringLiteralComponent('{"a": 1, "b": {"c":3}, "d": [4]}') [24-58]
+            StringLiteral [62-67]
+              StringLiteralComponent('$.a') [62-67]
+            StringLiteral [69-73]
+              StringLiteralComponent('v1') [69-73]
+            StringLiteral [77-84]
+              StringLiteralComponent('$.b.e') [77-84]
+            StringLiteral [86-90]
+              StringLiteralComponent('v2') [86-90]
+            StringLiteral [94-102]
+              StringLiteralComponent('$.d[2]') [94-102]
+            StringLiteral [104-108]
+              StringLiteralComponent('v3') [104-108]
+          Alias [110-122]
+            Identifier(json_data) [113-122]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_strip_nulls
+[mode=expression]
+JSON '{"a": null, "b": "c"}'
+--
+JSONLiteral [0-28]
+  StringLiteral [5-28]
+    StringLiteralComponent('{"a": null, "b": "c"}') [5-28]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_strip_nulls
+SELECT JSON_STRIP_NULLS(JSON '{"a": null, "b": "c"}') AS json_data
+--
+QueryStatement [0-66]
+  Query [0-66]
+    Select [0-66]
+      SelectList [7-66]
+        SelectColumn [7-66]
+          FunctionCall [7-53]
+            PathExpression [7-23]
+              Identifier(JSON_STRIP_NULLS) [7-23]
+            JSONLiteral [24-52]
+              StringLiteral [29-52]
+                StringLiteralComponent('{"a": null, "b": "c"}') [29-52]
+          Alias [54-66]
+            Identifier(json_data) [57-66]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_strip_nulls
+SELECT JSON_STRIP_NULLS(JSON '[1, null, 2, null]') AS json_data
+--
+QueryStatement [0-63]
+  Query [0-63]
+    Select [0-63]
+      SelectList [7-63]
+        SelectColumn [7-63]
+          FunctionCall [7-50]
+            PathExpression [7-23]
+              Identifier(JSON_STRIP_NULLS) [7-23]
+            JSONLiteral [24-49]
+              StringLiteral [29-49]
+                StringLiteralComponent('[1, null, 2, null]') [29-49]
+          Alias [51-63]
+            Identifier(json_data) [54-63]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_strip_nulls
+SELECT JSON_STRIP_NULLS(JSON '[1, null, 2, null]', include_arrays=>FALSE) AS json_data
+--
+QueryStatement [0-86]
+  Query [0-86]
+    Select [0-86]
+      SelectList [7-86]
+        SelectColumn [7-86]
+          FunctionCall [7-73]
+            PathExpression [7-23]
+              Identifier(JSON_STRIP_NULLS) [7-23]
+            JSONLiteral [24-49]
+              StringLiteral [29-49]
+                StringLiteralComponent('[1, null, 2, null]') [29-49]
+            NamedArgument [51-72]
+              Identifier(include_arrays) [51-65]
+              BooleanLiteral(FALSE) [67-72]
+          Alias [74-86]
+            Identifier(json_data) [77-86]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_strip_nulls
+SELECT JSON_STRIP_NULLS(JSON '[1, null, 2, null, [null]]') AS json_data
+--
+QueryStatement [0-71]
+  Query [0-71]
+    Select [0-71]
+      SelectList [7-71]
+        SelectColumn [7-71]
+          FunctionCall [7-58]
+            PathExpression [7-23]
+              Identifier(JSON_STRIP_NULLS) [7-23]
+            JSONLiteral [24-57]
+              StringLiteral [29-57]
+                StringLiteralComponent('[1, null, 2, null, [null]]') [29-57]
+          Alias [59-71]
+            Identifier(json_data) [62-71]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_strip_nulls
+SELECT JSON_STRIP_NULLS(
+  JSON '[1, null, 2, null, [null]]',
+  remove_empty=>TRUE) AS json_data
+--
+QueryStatement [0-96]
+  Query [0-96]
+    Select [0-96]
+      SelectList [7-96]
+        SelectColumn [7-96]
+          FunctionCall [7-83]
+            PathExpression [7-23]
+              Identifier(JSON_STRIP_NULLS) [7-23]
+            JSONLiteral [27-60]
+              StringLiteral [32-60]
+                StringLiteralComponent('[1, null, 2, null, [null]]') [32-60]
+            NamedArgument [64-82]
+              Identifier(remove_empty) [64-76]
+              BooleanLiteral(TRUE) [78-82]
+          Alias [84-96]
+            Identifier(json_data) [87-96]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_strip_nulls
+SELECT JSON_STRIP_NULLS(JSON '{"a": null}', remove_empty=>TRUE) AS json_data
+--
+QueryStatement [0-76]
+  Query [0-76]
+    Select [0-76]
+      SelectList [7-76]
+        SelectColumn [7-76]
+          FunctionCall [7-63]
+            PathExpression [7-23]
+              Identifier(JSON_STRIP_NULLS) [7-23]
+            JSONLiteral [24-42]
+              StringLiteral [29-42]
+                StringLiteralComponent('{"a": null}') [29-42]
+            NamedArgument [44-62]
+              Identifier(remove_empty) [44-56]
+              BooleanLiteral(TRUE) [58-62]
+          Alias [64-76]
+            Identifier(json_data) [67-76]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_strip_nulls
+SELECT JSON_STRIP_NULLS(JSON '{"a": [null]}', remove_empty=>TRUE) AS json_data
+--
+QueryStatement [0-78]
+  Query [0-78]
+    Select [0-78]
+      SelectList [7-78]
+        SelectColumn [7-78]
+          FunctionCall [7-65]
+            PathExpression [7-23]
+              Identifier(JSON_STRIP_NULLS) [7-23]
+            JSONLiteral [24-44]
+              StringLiteral [29-44]
+                StringLiteralComponent('{"a": [null]}') [29-44]
+            NamedArgument [46-64]
+              Identifier(remove_empty) [46-58]
+              BooleanLiteral(TRUE) [60-64]
+          Alias [66-78]
+            Identifier(json_data) [69-78]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_strip_nulls
+SELECT JSON_STRIP_NULLS(
+  JSON '{"a": {"b": {"c": null}}, "d": [null], "e": [], "f": 1}',
+  include_arrays=>FALSE,
+  remove_empty=>TRUE) AS json_data
+--
+QueryStatement [0-150]
+  Query [0-150]
+    Select [0-150]
+      SelectList [7-150]
+        SelectColumn [7-150]
+          FunctionCall [7-137]
+            PathExpression [7-23]
+              Identifier(JSON_STRIP_NULLS) [7-23]
+            JSONLiteral [27-89]
+              StringLiteral [32-89]
+                StringLiteralComponent('{"a": {"b": {"c": null}}, "d": [null], "e": [], "f": 1}') [32-89]
+            NamedArgument [93-114]
+              Identifier(include_arrays) [93-107]
+              BooleanLiteral(FALSE) [109-114]
+            NamedArgument [118-136]
+              Identifier(remove_empty) [118-130]
+              BooleanLiteral(TRUE) [132-136]
+          Alias [138-150]
+            Identifier(json_data) [141-150]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_strip_nulls
+SELECT JSON_STRIP_NULLS(
+  JSON '{"a": {"b": {"c": null}}, "d": [null], "e": [], "f": 1}',
+  remove_empty=>TRUE) AS json_data
+--
+QueryStatement [0-125]
+  Query [0-125]
+    Select [0-125]
+      SelectList [7-125]
+        SelectColumn [7-125]
+          FunctionCall [7-112]
+            PathExpression [7-23]
+              Identifier(JSON_STRIP_NULLS) [7-23]
+            JSONLiteral [27-89]
+              StringLiteral [32-89]
+                StringLiteralComponent('{"a": {"b": {"c": null}}, "d": [null], "e": [], "f": 1}') [32-89]
+            NamedArgument [93-111]
+              Identifier(remove_empty) [93-105]
+              BooleanLiteral(TRUE) [107-111]
+          Alias [113-125]
+            Identifier(json_data) [116-125]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_strip_nulls
+SELECT JSON_STRIP_NULLS(JSON 'null') AS json_data
+--
+QueryStatement [0-49]
+  Query [0-49]
+    Select [0-49]
+      SelectList [7-49]
+        SelectColumn [7-49]
+          FunctionCall [7-36]
+            PathExpression [7-23]
+              Identifier(JSON_STRIP_NULLS) [7-23]
+            JSONLiteral [24-35]
+              StringLiteral [29-35]
+                StringLiteralComponent('null') [29-35]
+          Alias [37-49]
+            Identifier(json_data) [40-49]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_type
+[mode=expression]
+JSON_TYPE(json_expr)
+--
+FunctionCall [0-20]
+  PathExpression [0-9]
+    Identifier(JSON_TYPE) [0-9]
+  PathExpression [10-19]
+    Identifier(json_expr) [10-19]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_type
+[mode=expression]
+JSON '{"name": "sky", "color": "blue"}'
+--
+JSONLiteral [0-39]
+  StringLiteral [5-39]
+    StringLiteralComponent('{"name": "sky", "color": "blue"}') [5-39]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_type
+SELECT json_val, JSON_TYPE(json_val) AS type
+FROM
+  UNNEST(
+\    [
+      JSON '"apple"',
+      JSON '10',
+      JSON '3.14',
+      JSON 'null',
+      JSON '{"city": "New York", "State": "NY"}',
+      JSON '["apple", "banana"]',
+      JSON 'false'
+    ]
+  ) AS json_val
+--
+QueryStatement [0-267]
+  Query [0-267]
+    Select [0-267]
+      SelectList [7-44]
+        SelectColumn [7-15]
+          PathExpression [7-15]
+            Identifier(json_val) [7-15]
+        SelectColumn [17-44]
+          FunctionCall [17-36]
+            PathExpression [17-26]
+              Identifier(JSON_TYPE) [17-26]
+            PathExpression [27-35]
+              Identifier(json_val) [27-35]
+          Alias [37-44]
+            Identifier(type) [40-44]
+      FromClause [45-267]
+        TablePathExpression [52-267]
+          UnnestExpression [52-255]
+            ExpressionWithOptAlias [64-251]
+              ArrayConstructor [64-251]
+                JSONLiteral [72-86]
+                  StringLiteral [77-86]
+                    StringLiteralComponent('"apple"') [77-86]
+                JSONLiteral [94-103]
+                  StringLiteral [99-103]
+                    StringLiteralComponent('10') [99-103]
+                JSONLiteral [111-122]
+                  StringLiteral [116-122]
+                    StringLiteralComponent('3.14') [116-122]
+                JSONLiteral [130-141]
+                  StringLiteral [135-141]
+                    StringLiteralComponent('null') [135-141]
+                JSONLiteral [149-191]
+                  StringLiteral [154-191]
+                    StringLiteralComponent('{"city": "New York", "State": "NY"}') [154-191]
+                JSONLiteral [199-225]
+                  StringLiteral [204-225]
+                    StringLiteralComponent('["apple", "banana"]') [204-225]
+                JSONLiteral [233-245]
+                  StringLiteral [238-245]
+                    StringLiteralComponent('false') [238-245]
+          Alias [256-267]
+            Identifier(json_val) [259-267]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value
+[mode=expression]
+'{"name": "Jakob", "age": "6"}'
+--
+StringLiteral [0-31]
+  StringLiteralComponent('{"name": "Jakob", "age": "6"}') [0-31]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value
+[mode=expression]
+JSON '{"name": "Jane", "age": "6"}'
+--
+JSONLiteral [0-35]
+  StringLiteral [5-35]
+    StringLiteralComponent('{"name": "Jane", "age": "6"}') [5-35]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value
+SELECT JSON_VALUE(JSON '{"name": "Jakob", "age": "6" }', '$.age') AS scalar_age
+--
+QueryStatement [0-79]
+  Query [0-79]
+    Select [0-79]
+      SelectList [7-79]
+        SelectColumn [7-79]
+          FunctionCall [7-65]
+            PathExpression [7-17]
+              Identifier(JSON_VALUE) [7-17]
+            JSONLiteral [18-55]
+              StringLiteral [23-55]
+                StringLiteralComponent('{"name": "Jakob", "age": "6" }') [23-55]
+            StringLiteral [57-64]
+              StringLiteralComponent('$.age') [57-64]
+          Alias [66-79]
+            Identifier(scalar_age) [69-79]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value
+SELECT JSON_QUERY('{"name": "Jakob", "age": "6"}', '$.name') AS json_name,
+  JSON_VALUE('{"name": "Jakob", "age": "6"}', '$.name') AS scalar_name,
+  JSON_QUERY('{"name": "Jakob", "age": "6"}', '$.age') AS json_age,
+  JSON_VALUE('{"name": "Jakob", "age": "6"}', '$.age') AS scalar_age
+--
+QueryStatement [0-283]
+  Query [0-283]
+    Select [0-283]
+      SelectList [7-283]
+        SelectColumn [7-73]
+          FunctionCall [7-60]
+            PathExpression [7-17]
+              Identifier(JSON_QUERY) [7-17]
+            StringLiteral [18-49]
+              StringLiteralComponent('{"name": "Jakob", "age": "6"}') [18-49]
+            StringLiteral [51-59]
+              StringLiteralComponent('$.name') [51-59]
+          Alias [61-73]
+            Identifier(json_name) [64-73]
+        SelectColumn [77-145]
+          FunctionCall [77-130]
+            PathExpression [77-87]
+              Identifier(JSON_VALUE) [77-87]
+            StringLiteral [88-119]
+              StringLiteralComponent('{"name": "Jakob", "age": "6"}') [88-119]
+            StringLiteral [121-129]
+              StringLiteralComponent('$.name') [121-129]
+          Alias [131-145]
+            Identifier(scalar_name) [134-145]
+        SelectColumn [149-213]
+          FunctionCall [149-201]
+            PathExpression [149-159]
+              Identifier(JSON_QUERY) [149-159]
+            StringLiteral [160-191]
+              StringLiteralComponent('{"name": "Jakob", "age": "6"}') [160-191]
+            StringLiteral [193-200]
+              StringLiteralComponent('$.age') [193-200]
+          Alias [202-213]
+            Identifier(json_age) [205-213]
+        SelectColumn [217-283]
+          FunctionCall [217-269]
+            PathExpression [217-227]
+              Identifier(JSON_VALUE) [217-227]
+            StringLiteral [228-259]
+              StringLiteralComponent('{"name": "Jakob", "age": "6"}') [228-259]
+            StringLiteral [261-268]
+              StringLiteralComponent('$.age') [261-268]
+          Alias [270-283]
+            Identifier(scalar_age) [273-283]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value
+SELECT JSON_QUERY('{"fruits": ["apple", "banana"]}', '$.fruits') AS json_query,
+  JSON_VALUE('{"fruits": ["apple", "banana"]}', '$.fruits') AS json_value
+--
+QueryStatement [0-153]
+  Query [0-153]
+    Select [0-153]
+      SelectList [7-153]
+        SelectColumn [7-78]
+          FunctionCall [7-64]
+            PathExpression [7-17]
+              Identifier(JSON_QUERY) [7-17]
+            StringLiteral [18-51]
+              StringLiteralComponent('{"fruits": ["apple", "banana"]}') [18-51]
+            StringLiteral [53-63]
+              StringLiteralComponent('$.fruits') [53-63]
+          Alias [65-78]
+            Identifier(json_query) [68-78]
+        SelectColumn [82-153]
+          FunctionCall [82-139]
+            PathExpression [82-92]
+              Identifier(JSON_VALUE) [82-92]
+            StringLiteral [93-126]
+              StringLiteralComponent('{"fruits": ["apple", "banana"]}') [93-126]
+            StringLiteral [128-138]
+              StringLiteralComponent('$.fruits') [128-138]
+          Alias [140-153]
+            Identifier(json_value) [143-153]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value
+SELECT JSON_VALUE('{"a.b": {"c": "world"}}', '$."a.b".c') AS hello
+--
+QueryStatement [0-66]
+  Query [0-66]
+    Select [0-66]
+      SelectList [7-66]
+        SelectColumn [7-66]
+          FunctionCall [7-57]
+            PathExpression [7-17]
+              Identifier(JSON_VALUE) [7-17]
+            StringLiteral [18-43]
+              StringLiteralComponent('{"a.b": {"c": "world"}}') [18-43]
+            StringLiteral [45-56]
+              StringLiteralComponent('$."a.b".c') [45-56]
+          Alias [58-66]
+            Identifier(hello) [61-66]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value_array
+[mode=expression]
+'["apples", "oranges", "grapes"]'
+--
+StringLiteral [0-33]
+  StringLiteralComponent('["apples", "oranges", "grapes"]') [0-33]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value_array
+[mode=expression]
+JSON '["apples", "oranges", "grapes"]'
+--
+JSONLiteral [0-38]
+  StringLiteral [5-38]
+    StringLiteralComponent('["apples", "oranges", "grapes"]') [5-38]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value_array
+SELECT JSON_VALUE_ARRAY(
+  JSON '{"fruits": ["apples", "oranges", "grapes"]}', '$.fruits'
+  ) AS string_array
+--
+QueryStatement [0-109]
+  Query [0-109]
+    Select [0-109]
+      SelectList [7-109]
+        SelectColumn [7-109]
+          FunctionCall [7-93]
+            PathExpression [7-23]
+              Identifier(JSON_VALUE_ARRAY) [7-23]
+            JSONLiteral [27-77]
+              StringLiteral [32-77]
+                StringLiteralComponent('{"fruits": ["apples", "oranges", "grapes"]}') [32-77]
+            StringLiteral [79-89]
+              StringLiteralComponent('$.fruits') [79-89]
+          Alias [94-109]
+            Identifier(string_array) [97-109]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value_array
+SELECT JSON_QUERY_ARRAY('["apples", "oranges"]') AS json_array,
+       JSON_VALUE_ARRAY('["apples", "oranges"]') AS string_array
+--
+QueryStatement [0-128]
+  Query [0-128]
+    Select [0-128]
+      SelectList [7-128]
+        SelectColumn [7-62]
+          FunctionCall [7-48]
+            PathExpression [7-23]
+              Identifier(JSON_QUERY_ARRAY) [7-23]
+            StringLiteral [24-47]
+              StringLiteralComponent('["apples", "oranges"]') [24-47]
+          Alias [49-62]
+            Identifier(json_array) [52-62]
+        SelectColumn [71-128]
+          FunctionCall [71-112]
+            PathExpression [71-87]
+              Identifier(JSON_VALUE_ARRAY) [71-87]
+            StringLiteral [88-111]
+              StringLiteralComponent('["apples", "oranges"]') [88-111]
+          Alias [113-128]
+            Identifier(string_array) [116-128]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value_array
+-- Strips the double quotes
+SELECT JSON_VALUE_ARRAY('["foo", "bar", "baz"]', '$') AS string_array
+--
+QueryStatement [28-97]
+  Query [28-97]
+    Select [28-97]
+      SelectList [35-97]
+        SelectColumn [35-97]
+          FunctionCall [35-81]
+            PathExpression [35-51]
+              Identifier(JSON_VALUE_ARRAY) [35-51]
+            StringLiteral [52-75]
+              StringLiteralComponent('["foo", "bar", "baz"]') [52-75]
+            StringLiteral [77-80]
+              StringLiteralComponent('$') [77-80]
+          Alias [82-97]
+            Identifier(string_array) [85-97]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value_array
+SELECT ARRAY(
+  SELECT CAST(integer_element AS INT64)
+  FROM UNNEST(
+    JSON_VALUE_ARRAY('[1, 2, 3]', '$')
+  ) AS integer_element
+) AS integer_array
+--
+QueryStatement [0-149]
+  Query [0-149]
+    Select [0-149]
+      SelectList [7-149]
+        SelectColumn [7-149]
+          ExpressionSubquery(modifier=ARRAY) [7-132]
+            Query [16-130]
+              Select [16-130]
+                SelectList [23-53]
+                  SelectColumn [23-53]
+                    CastExpression [23-53]
+                      PathExpression [28-43]
+                        Identifier(integer_element) [28-43]
+                      SimpleType [47-52]
+                        PathExpression [47-52]
+                          Identifier(INT64) [47-52]
+                FromClause [56-130]
+                  TablePathExpression [61-130]
+                    UnnestExpression [61-111]
+                      ExpressionWithOptAlias [73-107]
+                        FunctionCall [73-107]
+                          PathExpression [73-89]
+                            Identifier(JSON_VALUE_ARRAY) [73-89]
+                          StringLiteral [90-101]
+                            StringLiteralComponent('[1, 2, 3]') [90-101]
+                          StringLiteral [103-106]
+                            StringLiteralComponent('$') [103-106]
+                    Alias [112-130]
+                      Identifier(integer_element) [115-130]
+          Alias [133-149]
+            Identifier(integer_array) [136-149]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value_array
+SELECT JSON_VALUE_ARRAY('{"fruits": ["apples", "oranges", "grapes"]}', '$.fruits') AS string_array
+--
+QueryStatement [0-98]
+  Query [0-98]
+    Select [0-98]
+      SelectList [7-98]
+        SelectColumn [7-98]
+          FunctionCall [7-82]
+            PathExpression [7-23]
+              Identifier(JSON_VALUE_ARRAY) [7-23]
+            StringLiteral [24-69]
+              StringLiteralComponent('{"fruits": ["apples", "oranges", "grapes"]}') [24-69]
+            StringLiteral [71-81]
+              StringLiteralComponent('$.fruits') [71-81]
+          Alias [83-98]
+            Identifier(string_array) [86-98]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value_array
+SELECT JSON_VALUE_ARRAY('{"fruits": ["apples", "oranges", "grapes"]}', '$."fruits"') AS string_array
+--
+QueryStatement [0-100]
+  Query [0-100]
+    Select [0-100]
+      SelectList [7-100]
+        SelectColumn [7-100]
+          FunctionCall [7-84]
+            PathExpression [7-23]
+              Identifier(JSON_VALUE_ARRAY) [7-23]
+            StringLiteral [24-69]
+              StringLiteralComponent('{"fruits": ["apples", "oranges", "grapes"]}') [24-69]
+            StringLiteral [71-83]
+              StringLiteralComponent('$."fruits"') [71-83]
+          Alias [85-100]
+            Identifier(string_array) [88-100]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value_array
+SELECT JSON_VALUE_ARRAY('{"a.b": {"c": ["world"]}}', '$."a.b".c') AS hello
+--
+QueryStatement [0-74]
+  Query [0-74]
+    Select [0-74]
+      SelectList [7-74]
+        SelectColumn [7-74]
+          FunctionCall [7-65]
+            PathExpression [7-23]
+              Identifier(JSON_VALUE_ARRAY) [7-23]
+            StringLiteral [24-51]
+              StringLiteralComponent('{"a.b": {"c": ["world"]}}') [24-51]
+            StringLiteral [53-64]
+              StringLiteralComponent('$."a.b".c') [53-64]
+          Alias [66-74]
+            Identifier(hello) [69-74]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value_array
+-- An error is thrown if you provide an invalid JSONPath.
+SELECT JSON_VALUE_ARRAY('["foo", "bar", "baz"]', 'INVALID_JSONPath') AS result
+--
+QueryStatement [58-136]
+  Query [58-136]
+    Select [58-136]
+      SelectList [65-136]
+        SelectColumn [65-136]
+          FunctionCall [65-126]
+            PathExpression [65-81]
+              Identifier(JSON_VALUE_ARRAY) [65-81]
+            StringLiteral [82-105]
+              StringLiteralComponent('["foo", "bar", "baz"]') [82-105]
+            StringLiteral [107-125]
+              StringLiteralComponent('INVALID_JSONPath') [107-125]
+          Alias [127-136]
+            Identifier(result) [130-136]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value_array
+-- If the JSON-formatted string is invalid, then NULL is returned.
+SELECT JSON_VALUE_ARRAY('}}', '$') AS result
+--
+QueryStatement [67-111]
+  Query [67-111]
+    Select [67-111]
+      SelectList [74-111]
+        SelectColumn [74-111]
+          FunctionCall [74-101]
+            PathExpression [74-90]
+              Identifier(JSON_VALUE_ARRAY) [74-90]
+            StringLiteral [91-95]
+              StringLiteralComponent('}}') [91-95]
+            StringLiteral [97-100]
+              StringLiteralComponent('$') [97-100]
+          Alias [102-111]
+            Identifier(result) [105-111]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value_array
+-- If the JSON document is NULL, then NULL is returned.
+SELECT JSON_VALUE_ARRAY(NULL, '$') AS result
+--
+QueryStatement [56-100]
+  Query [56-100]
+    Select [56-100]
+      SelectList [63-100]
+        SelectColumn [63-100]
+          FunctionCall [63-90]
+            PathExpression [63-79]
+              Identifier(JSON_VALUE_ARRAY) [63-79]
+            NullLiteral(NULL) [80-84]
+            StringLiteral [86-89]
+              StringLiteralComponent('$') [86-89]
+          Alias [91-100]
+            Identifier(result) [94-100]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value_array
+-- If a JSONPath doesn't match anything, then the output is NULL.
+SELECT JSON_VALUE_ARRAY('{"a": ["foo", "bar", "baz"]}', '$.b') AS result
+--
+QueryStatement [66-138]
+  Query [66-138]
+    Select [66-138]
+      SelectList [73-138]
+        SelectColumn [73-138]
+          FunctionCall [73-128]
+            PathExpression [73-89]
+              Identifier(JSON_VALUE_ARRAY) [73-89]
+            StringLiteral [90-120]
+              StringLiteralComponent('{"a": ["foo", "bar", "baz"]}') [90-120]
+            StringLiteral [122-127]
+              StringLiteralComponent('$.b') [122-127]
+          Alias [129-138]
+            Identifier(result) [132-138]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value_array
+-- If a JSONPath matches an object that isn't an array, then the output is NULL.
+SELECT JSON_VALUE_ARRAY('{"a": "foo"}', '$') AS result
+--
+QueryStatement [81-135]
+  Query [81-135]
+    Select [81-135]
+      SelectList [88-135]
+        SelectColumn [88-135]
+          FunctionCall [88-125]
+            PathExpression [88-104]
+              Identifier(JSON_VALUE_ARRAY) [88-104]
+            StringLiteral [105-119]
+              StringLiteralComponent('{"a": "foo"}') [105-119]
+            StringLiteral [121-124]
+              StringLiteralComponent('$') [121-124]
+          Alias [126-135]
+            Identifier(result) [129-135]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value_array
+-- If a JSONPath matches an array of non-scalar objects, then the output is NULL.
+SELECT JSON_VALUE_ARRAY('{"a": [{"b": "foo", "c": 1}, {"b": "bar", "c": 2}], "d": "baz"}', '$.a') AS result
+--
+QueryStatement [82-189]
+  Query [82-189]
+    Select [82-189]
+      SelectList [89-189]
+        SelectColumn [89-189]
+          FunctionCall [89-179]
+            PathExpression [89-105]
+              Identifier(JSON_VALUE_ARRAY) [89-105]
+            StringLiteral [106-171]
+              StringLiteralComponent('{"a": [{"b": "foo", "c": 1}, {"b": "bar", "c": 2}], "d": "baz"}') [106-171]
+            StringLiteral [173-178]
+              StringLiteralComponent('$.a') [173-178]
+          Alias [180-189]
+            Identifier(result) [183-189]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value_array
+-- If a JSONPath matches an array of mixed scalar and non-scalar objects,
+-- then the output is NULL.
+SELECT JSON_VALUE_ARRAY('{"a": [10, {"b": 20}]', '$.a') AS result
+--
+QueryStatement [102-167]
+  Query [102-167]
+    Select [102-167]
+      SelectList [109-167]
+        SelectColumn [109-167]
+          FunctionCall [109-157]
+            PathExpression [109-125]
+              Identifier(JSON_VALUE_ARRAY) [109-125]
+            StringLiteral [126-149]
+              StringLiteralComponent('{"a": [10, {"b": 20}]') [126-149]
+            StringLiteral [151-156]
+              StringLiteralComponent('$.a') [151-156]
+          Alias [158-167]
+            Identifier(result) [161-167]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value_array
+-- If a JSONPath matches an empty JSON array, then the output is an empty array instead of NULL.
+SELECT JSON_VALUE_ARRAY('{"a": "foo", "b": []}', '$.b') AS result
+--
+QueryStatement [97-162]
+  Query [97-162]
+    Select [97-162]
+      SelectList [104-162]
+        SelectColumn [104-162]
+          FunctionCall [104-152]
+            PathExpression [104-120]
+              Identifier(JSON_VALUE_ARRAY) [104-120]
+            StringLiteral [121-144]
+              StringLiteralComponent('{"a": "foo", "b": []}') [121-144]
+            StringLiteral [146-151]
+              StringLiteralComponent('$.b') [146-151]
+          Alias [153-162]
+            Identifier(result) [156-162]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_value_array
+-- In the following query, the JSON null input is returned as a
+-- SQL NULL in the output.
+SELECT JSON_VALUE_ARRAY('["world", null, 1]') AS result
+--
+QueryStatement [91-146]
+  Query [91-146]
+    Select [91-146]
+      SelectList [98-146]
+        SelectColumn [98-146]
+          FunctionCall [98-136]
+            PathExpression [98-114]
+              Identifier(JSON_VALUE_ARRAY) [98-114]
+            StringLiteral [115-135]
+              StringLiteralComponent('["world", null, 1]') [115-135]
+          Alias [137-146]
+            Identifier(result) [140-146]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_bool
+[mode=expression]
+LAX_BOOL(json_expr)
+--
+FunctionCall [0-19]
+  PathExpression [0-8]
+    Identifier(LAX_BOOL) [0-8]
+  PathExpression [9-18]
+    Identifier(json_expr) [9-18]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_bool
+SELECT LAX_BOOL(JSON 'true') AS result
+--
+QueryStatement [0-38]
+  Query [0-38]
+    Select [0-38]
+      SelectList [7-38]
+        SelectColumn [7-38]
+          FunctionCall [7-28]
+            PathExpression [7-15]
+              Identifier(LAX_BOOL) [7-15]
+            JSONLiteral [16-27]
+              StringLiteral [21-27]
+                StringLiteralComponent('true') [21-27]
+          Alias [29-38]
+            Identifier(result) [32-38]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_bool
+SELECT LAX_BOOL(JSON '"true"') AS result
+--
+QueryStatement [0-40]
+  Query [0-40]
+    Select [0-40]
+      SelectList [7-40]
+        SelectColumn [7-40]
+          FunctionCall [7-30]
+            PathExpression [7-15]
+              Identifier(LAX_BOOL) [7-15]
+            JSONLiteral [16-29]
+              StringLiteral [21-29]
+                StringLiteralComponent('"true"') [21-29]
+          Alias [31-40]
+            Identifier(result) [34-40]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_bool
+SELECT LAX_BOOL(JSON '"true "') AS result
+--
+QueryStatement [0-41]
+  Query [0-41]
+    Select [0-41]
+      SelectList [7-41]
+        SelectColumn [7-41]
+          FunctionCall [7-31]
+            PathExpression [7-15]
+              Identifier(LAX_BOOL) [7-15]
+            JSONLiteral [16-30]
+              StringLiteral [21-30]
+                StringLiteralComponent('"true "') [21-30]
+          Alias [32-41]
+            Identifier(result) [35-41]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_bool
+SELECT LAX_BOOL(JSON '"foo"') AS result
+--
+QueryStatement [0-39]
+  Query [0-39]
+    Select [0-39]
+      SelectList [7-39]
+        SelectColumn [7-39]
+          FunctionCall [7-29]
+            PathExpression [7-15]
+              Identifier(LAX_BOOL) [7-15]
+            JSONLiteral [16-28]
+              StringLiteral [21-28]
+                StringLiteralComponent('"foo"') [21-28]
+          Alias [30-39]
+            Identifier(result) [33-39]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_bool
+SELECT LAX_BOOL(JSON '10') AS result
+--
+QueryStatement [0-36]
+  Query [0-36]
+    Select [0-36]
+      SelectList [7-36]
+        SelectColumn [7-36]
+          FunctionCall [7-26]
+            PathExpression [7-15]
+              Identifier(LAX_BOOL) [7-15]
+            JSONLiteral [16-25]
+              StringLiteral [21-25]
+                StringLiteralComponent('10') [21-25]
+          Alias [27-36]
+            Identifier(result) [30-36]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_bool
+SELECT LAX_BOOL(JSON '0') AS result
+--
+QueryStatement [0-35]
+  Query [0-35]
+    Select [0-35]
+      SelectList [7-35]
+        SelectColumn [7-35]
+          FunctionCall [7-25]
+            PathExpression [7-15]
+              Identifier(LAX_BOOL) [7-15]
+            JSONLiteral [16-24]
+              StringLiteral [21-24]
+                StringLiteralComponent('0') [21-24]
+          Alias [26-35]
+            Identifier(result) [29-35]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_bool
+SELECT LAX_BOOL(JSON '0.0') AS result
+--
+QueryStatement [0-37]
+  Query [0-37]
+    Select [0-37]
+      SelectList [7-37]
+        SelectColumn [7-37]
+          FunctionCall [7-27]
+            PathExpression [7-15]
+              Identifier(LAX_BOOL) [7-15]
+            JSONLiteral [16-26]
+              StringLiteral [21-26]
+                StringLiteralComponent('0.0') [21-26]
+          Alias [28-37]
+            Identifier(result) [31-37]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_bool
+SELECT LAX_BOOL(JSON '-1.1') AS result
+--
+QueryStatement [0-38]
+  Query [0-38]
+    Select [0-38]
+      SelectList [7-38]
+        SelectColumn [7-38]
+          FunctionCall [7-28]
+            PathExpression [7-15]
+              Identifier(LAX_BOOL) [7-15]
+            JSONLiteral [16-27]
+              StringLiteral [21-27]
+                StringLiteralComponent('-1.1') [21-27]
+          Alias [29-38]
+            Identifier(result) [32-38]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_double
+[mode=expression]
+LAX_FLOAT64(json_expr)
+--
+FunctionCall [0-22]
+  PathExpression [0-11]
+    Identifier(LAX_FLOAT64) [0-11]
+  PathExpression [12-21]
+    Identifier(json_expr) [12-21]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_double
+SELECT LAX_FLOAT64(JSON '9.8') AS result
+--
+QueryStatement [0-40]
+  Query [0-40]
+    Select [0-40]
+      SelectList [7-40]
+        SelectColumn [7-40]
+          FunctionCall [7-30]
+            PathExpression [7-18]
+              Identifier(LAX_FLOAT64) [7-18]
+            JSONLiteral [19-29]
+              StringLiteral [24-29]
+                StringLiteralComponent('9.8') [24-29]
+          Alias [31-40]
+            Identifier(result) [34-40]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_double
+SELECT LAX_FLOAT64(JSON '9') AS result
+--
+QueryStatement [0-38]
+  Query [0-38]
+    Select [0-38]
+      SelectList [7-38]
+        SelectColumn [7-38]
+          FunctionCall [7-28]
+            PathExpression [7-18]
+              Identifier(LAX_FLOAT64) [7-18]
+            JSONLiteral [19-27]
+              StringLiteral [24-27]
+                StringLiteralComponent('9') [24-27]
+          Alias [29-38]
+            Identifier(result) [32-38]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_double
+SELECT LAX_FLOAT64(JSON '9007199254740993') AS result
+--
+QueryStatement [0-53]
+  Query [0-53]
+    Select [0-53]
+      SelectList [7-53]
+        SelectColumn [7-53]
+          FunctionCall [7-43]
+            PathExpression [7-18]
+              Identifier(LAX_FLOAT64) [7-18]
+            JSONLiteral [19-42]
+              StringLiteral [24-42]
+                StringLiteralComponent('9007199254740993') [24-42]
+          Alias [44-53]
+            Identifier(result) [47-53]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_double
+SELECT LAX_FLOAT64(JSON '1e100') AS result
+--
+QueryStatement [0-42]
+  Query [0-42]
+    Select [0-42]
+      SelectList [7-42]
+        SelectColumn [7-42]
+          FunctionCall [7-32]
+            PathExpression [7-18]
+              Identifier(LAX_FLOAT64) [7-18]
+            JSONLiteral [19-31]
+              StringLiteral [24-31]
+                StringLiteralComponent('1e100') [24-31]
+          Alias [33-42]
+            Identifier(result) [36-42]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_double
+SELECT LAX_FLOAT64(JSON 'true') AS result
+--
+QueryStatement [0-41]
+  Query [0-41]
+    Select [0-41]
+      SelectList [7-41]
+        SelectColumn [7-41]
+          FunctionCall [7-31]
+            PathExpression [7-18]
+              Identifier(LAX_FLOAT64) [7-18]
+            JSONLiteral [19-30]
+              StringLiteral [24-30]
+                StringLiteralComponent('true') [24-30]
+          Alias [32-41]
+            Identifier(result) [35-41]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_double
+SELECT LAX_FLOAT64(JSON 'false') AS result
+--
+QueryStatement [0-42]
+  Query [0-42]
+    Select [0-42]
+      SelectList [7-42]
+        SelectColumn [7-42]
+          FunctionCall [7-32]
+            PathExpression [7-18]
+              Identifier(LAX_FLOAT64) [7-18]
+            JSONLiteral [19-31]
+              StringLiteral [24-31]
+                StringLiteralComponent('false') [24-31]
+          Alias [33-42]
+            Identifier(result) [36-42]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_double
+SELECT LAX_FLOAT64(JSON '"10"') AS result
+--
+QueryStatement [0-41]
+  Query [0-41]
+    Select [0-41]
+      SelectList [7-41]
+        SelectColumn [7-41]
+          FunctionCall [7-31]
+            PathExpression [7-18]
+              Identifier(LAX_FLOAT64) [7-18]
+            JSONLiteral [19-30]
+              StringLiteral [24-30]
+                StringLiteralComponent('"10"') [24-30]
+          Alias [32-41]
+            Identifier(result) [35-41]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_double
+SELECT LAX_FLOAT64(JSON '"1.1"') AS result
+--
+QueryStatement [0-42]
+  Query [0-42]
+    Select [0-42]
+      SelectList [7-42]
+        SelectColumn [7-42]
+          FunctionCall [7-32]
+            PathExpression [7-18]
+              Identifier(LAX_FLOAT64) [7-18]
+            JSONLiteral [19-31]
+              StringLiteral [24-31]
+                StringLiteralComponent('"1.1"') [24-31]
+          Alias [33-42]
+            Identifier(result) [36-42]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_double
+SELECT LAX_FLOAT64(JSON '"1.1e2"') AS result
+--
+QueryStatement [0-44]
+  Query [0-44]
+    Select [0-44]
+      SelectList [7-44]
+        SelectColumn [7-44]
+          FunctionCall [7-34]
+            PathExpression [7-18]
+              Identifier(LAX_FLOAT64) [7-18]
+            JSONLiteral [19-33]
+              StringLiteral [24-33]
+                StringLiteralComponent('"1.1e2"') [24-33]
+          Alias [35-44]
+            Identifier(result) [38-44]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_double
+SELECT LAX_FLOAT64(JSON '"9007199254740993"') AS result
+--
+QueryStatement [0-55]
+  Query [0-55]
+    Select [0-55]
+      SelectList [7-55]
+        SelectColumn [7-55]
+          FunctionCall [7-45]
+            PathExpression [7-18]
+              Identifier(LAX_FLOAT64) [7-18]
+            JSONLiteral [19-44]
+              StringLiteral [24-44]
+                StringLiteralComponent('"9007199254740993"') [24-44]
+          Alias [46-55]
+            Identifier(result) [49-55]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_double
+SELECT LAX_FLOAT64(JSON '"+1.5"') AS result
+--
+QueryStatement [0-43]
+  Query [0-43]
+    Select [0-43]
+      SelectList [7-43]
+        SelectColumn [7-43]
+          FunctionCall [7-33]
+            PathExpression [7-18]
+              Identifier(LAX_FLOAT64) [7-18]
+            JSONLiteral [19-32]
+              StringLiteral [24-32]
+                StringLiteralComponent('"+1.5"') [24-32]
+          Alias [34-43]
+            Identifier(result) [37-43]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_double
+SELECT LAX_FLOAT64(JSON '"NaN"') AS result
+--
+QueryStatement [0-42]
+  Query [0-42]
+    Select [0-42]
+      SelectList [7-42]
+        SelectColumn [7-42]
+          FunctionCall [7-32]
+            PathExpression [7-18]
+              Identifier(LAX_FLOAT64) [7-18]
+            JSONLiteral [19-31]
+              StringLiteral [24-31]
+                StringLiteralComponent('"NaN"') [24-31]
+          Alias [33-42]
+            Identifier(result) [36-42]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_double
+SELECT LAX_FLOAT64(JSON '"Inf"') AS result
+--
+QueryStatement [0-42]
+  Query [0-42]
+    Select [0-42]
+      SelectList [7-42]
+        SelectColumn [7-42]
+          FunctionCall [7-32]
+            PathExpression [7-18]
+              Identifier(LAX_FLOAT64) [7-18]
+            JSONLiteral [19-31]
+              StringLiteral [24-31]
+                StringLiteralComponent('"Inf"') [24-31]
+          Alias [33-42]
+            Identifier(result) [36-42]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_double
+SELECT LAX_FLOAT64(JSON '"-InfiNiTY"') AS result
+--
+QueryStatement [0-48]
+  Query [0-48]
+    Select [0-48]
+      SelectList [7-48]
+        SelectColumn [7-48]
+          FunctionCall [7-38]
+            PathExpression [7-18]
+              Identifier(LAX_FLOAT64) [7-18]
+            JSONLiteral [19-37]
+              StringLiteral [24-37]
+                StringLiteralComponent('"-InfiNiTY"') [24-37]
+          Alias [39-48]
+            Identifier(result) [42-48]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_double
+SELECT LAX_FLOAT64(JSON '"foo"') AS result
+--
+QueryStatement [0-42]
+  Query [0-42]
+    Select [0-42]
+      SelectList [7-42]
+        SelectColumn [7-42]
+          FunctionCall [7-32]
+            PathExpression [7-18]
+              Identifier(LAX_FLOAT64) [7-18]
+            JSONLiteral [19-31]
+              StringLiteral [24-31]
+                StringLiteralComponent('"foo"') [24-31]
+          Alias [33-42]
+            Identifier(result) [36-42]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_int64
+[mode=expression]
+LAX_INT64(json_expr)
+--
+FunctionCall [0-20]
+  PathExpression [0-9]
+    Identifier(LAX_INT64) [0-9]
+  PathExpression [10-19]
+    Identifier(json_expr) [10-19]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_int64
+SELECT LAX_INT64(JSON '10') AS result
+--
+QueryStatement [0-37]
+  Query [0-37]
+    Select [0-37]
+      SelectList [7-37]
+        SelectColumn [7-37]
+          FunctionCall [7-27]
+            PathExpression [7-16]
+              Identifier(LAX_INT64) [7-16]
+            JSONLiteral [17-26]
+              StringLiteral [22-26]
+                StringLiteralComponent('10') [22-26]
+          Alias [28-37]
+            Identifier(result) [31-37]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_int64
+SELECT LAX_INT64(JSON '10.0') AS result
+--
+QueryStatement [0-39]
+  Query [0-39]
+    Select [0-39]
+      SelectList [7-39]
+        SelectColumn [7-39]
+          FunctionCall [7-29]
+            PathExpression [7-16]
+              Identifier(LAX_INT64) [7-16]
+            JSONLiteral [17-28]
+              StringLiteral [22-28]
+                StringLiteralComponent('10.0') [22-28]
+          Alias [30-39]
+            Identifier(result) [33-39]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_int64
+SELECT LAX_INT64(JSON '1.1') AS result
+--
+QueryStatement [0-38]
+  Query [0-38]
+    Select [0-38]
+      SelectList [7-38]
+        SelectColumn [7-38]
+          FunctionCall [7-28]
+            PathExpression [7-16]
+              Identifier(LAX_INT64) [7-16]
+            JSONLiteral [17-27]
+              StringLiteral [22-27]
+                StringLiteralComponent('1.1') [22-27]
+          Alias [29-38]
+            Identifier(result) [32-38]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_int64
+SELECT LAX_INT64(JSON '3.5') AS result
+--
+QueryStatement [0-38]
+  Query [0-38]
+    Select [0-38]
+      SelectList [7-38]
+        SelectColumn [7-38]
+          FunctionCall [7-28]
+            PathExpression [7-16]
+              Identifier(LAX_INT64) [7-16]
+            JSONLiteral [17-27]
+              StringLiteral [22-27]
+                StringLiteralComponent('3.5') [22-27]
+          Alias [29-38]
+            Identifier(result) [32-38]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_int64
+SELECT LAX_INT64(JSON '1.1e2') AS result
+--
+QueryStatement [0-40]
+  Query [0-40]
+    Select [0-40]
+      SelectList [7-40]
+        SelectColumn [7-40]
+          FunctionCall [7-30]
+            PathExpression [7-16]
+              Identifier(LAX_INT64) [7-16]
+            JSONLiteral [17-29]
+              StringLiteral [22-29]
+                StringLiteralComponent('1.1e2') [22-29]
+          Alias [31-40]
+            Identifier(result) [34-40]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_int64
+SELECT LAX_INT64(JSON '1e100') AS result
+--
+QueryStatement [0-40]
+  Query [0-40]
+    Select [0-40]
+      SelectList [7-40]
+        SelectColumn [7-40]
+          FunctionCall [7-30]
+            PathExpression [7-16]
+              Identifier(LAX_INT64) [7-16]
+            JSONLiteral [17-29]
+              StringLiteral [22-29]
+                StringLiteralComponent('1e100') [22-29]
+          Alias [31-40]
+            Identifier(result) [34-40]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_int64
+SELECT LAX_INT64(JSON 'true') AS result
+--
+QueryStatement [0-39]
+  Query [0-39]
+    Select [0-39]
+      SelectList [7-39]
+        SelectColumn [7-39]
+          FunctionCall [7-29]
+            PathExpression [7-16]
+              Identifier(LAX_INT64) [7-16]
+            JSONLiteral [17-28]
+              StringLiteral [22-28]
+                StringLiteralComponent('true') [22-28]
+          Alias [30-39]
+            Identifier(result) [33-39]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_int64
+SELECT LAX_INT64(JSON 'false') AS result
+--
+QueryStatement [0-40]
+  Query [0-40]
+    Select [0-40]
+      SelectList [7-40]
+        SelectColumn [7-40]
+          FunctionCall [7-30]
+            PathExpression [7-16]
+              Identifier(LAX_INT64) [7-16]
+            JSONLiteral [17-29]
+              StringLiteral [22-29]
+                StringLiteralComponent('false') [22-29]
+          Alias [31-40]
+            Identifier(result) [34-40]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_int64
+SELECT LAX_INT64(JSON '"10"') AS result
+--
+QueryStatement [0-39]
+  Query [0-39]
+    Select [0-39]
+      SelectList [7-39]
+        SelectColumn [7-39]
+          FunctionCall [7-29]
+            PathExpression [7-16]
+              Identifier(LAX_INT64) [7-16]
+            JSONLiteral [17-28]
+              StringLiteral [22-28]
+                StringLiteralComponent('"10"') [22-28]
+          Alias [30-39]
+            Identifier(result) [33-39]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_int64
+SELECT LAX_INT64(JSON '"1.1"') AS result
+--
+QueryStatement [0-40]
+  Query [0-40]
+    Select [0-40]
+      SelectList [7-40]
+        SelectColumn [7-40]
+          FunctionCall [7-30]
+            PathExpression [7-16]
+              Identifier(LAX_INT64) [7-16]
+            JSONLiteral [17-29]
+              StringLiteral [22-29]
+                StringLiteralComponent('"1.1"') [22-29]
+          Alias [31-40]
+            Identifier(result) [34-40]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_int64
+SELECT LAX_INT64(JSON '"1.1e2"') AS result
+--
+QueryStatement [0-42]
+  Query [0-42]
+    Select [0-42]
+      SelectList [7-42]
+        SelectColumn [7-42]
+          FunctionCall [7-32]
+            PathExpression [7-16]
+              Identifier(LAX_INT64) [7-16]
+            JSONLiteral [17-31]
+              StringLiteral [22-31]
+                StringLiteralComponent('"1.1e2"') [22-31]
+          Alias [33-42]
+            Identifier(result) [36-42]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_int64
+SELECT LAX_INT64(JSON '"+1.5"') AS result
+--
+QueryStatement [0-41]
+  Query [0-41]
+    Select [0-41]
+      SelectList [7-41]
+        SelectColumn [7-41]
+          FunctionCall [7-31]
+            PathExpression [7-16]
+              Identifier(LAX_INT64) [7-16]
+            JSONLiteral [17-30]
+              StringLiteral [22-30]
+                StringLiteralComponent('"+1.5"') [22-30]
+          Alias [32-41]
+            Identifier(result) [35-41]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_int64
+SELECT LAX_INT64(JSON '"1e100"') AS result
+--
+QueryStatement [0-42]
+  Query [0-42]
+    Select [0-42]
+      SelectList [7-42]
+        SelectColumn [7-42]
+          FunctionCall [7-32]
+            PathExpression [7-16]
+              Identifier(LAX_INT64) [7-16]
+            JSONLiteral [17-31]
+              StringLiteral [22-31]
+                StringLiteralComponent('"1e100"') [22-31]
+          Alias [33-42]
+            Identifier(result) [36-42]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_int64
+SELECT LAX_INT64(JSON '"foo"') AS result
+--
+QueryStatement [0-40]
+  Query [0-40]
+    Select [0-40]
+      SelectList [7-40]
+        SelectColumn [7-40]
+          FunctionCall [7-30]
+            PathExpression [7-16]
+              Identifier(LAX_INT64) [7-16]
+            JSONLiteral [17-29]
+              StringLiteral [22-29]
+                StringLiteralComponent('"foo"') [22-29]
+          Alias [31-40]
+            Identifier(result) [34-40]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_string
+[mode=expression]
+LAX_STRING(json_expr)
+--
+FunctionCall [0-21]
+  PathExpression [0-10]
+    Identifier(LAX_STRING) [0-10]
+  PathExpression [11-20]
+    Identifier(json_expr) [11-20]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_string
+[mode=expression]
+JSON '"name"'
+--
+JSONLiteral [0-13]
+  StringLiteral [5-13]
+    StringLiteralComponent('"name"') [5-13]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_string
+SELECT LAX_STRING(JSON '"purple"') AS result
+--
+QueryStatement [0-44]
+  Query [0-44]
+    Select [0-44]
+      SelectList [7-44]
+        SelectColumn [7-44]
+          FunctionCall [7-34]
+            PathExpression [7-17]
+              Identifier(LAX_STRING) [7-17]
+            JSONLiteral [18-33]
+              StringLiteral [23-33]
+                StringLiteralComponent('"purple"') [23-33]
+          Alias [35-44]
+            Identifier(result) [38-44]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_string
+SELECT LAX_STRING(JSON '"10"') AS result
+--
+QueryStatement [0-40]
+  Query [0-40]
+    Select [0-40]
+      SelectList [7-40]
+        SelectColumn [7-40]
+          FunctionCall [7-30]
+            PathExpression [7-17]
+              Identifier(LAX_STRING) [7-17]
+            JSONLiteral [18-29]
+              StringLiteral [23-29]
+                StringLiteralComponent('"10"') [23-29]
+          Alias [31-40]
+            Identifier(result) [34-40]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_string
+SELECT LAX_STRING(JSON 'true') AS result
+--
+QueryStatement [0-40]
+  Query [0-40]
+    Select [0-40]
+      SelectList [7-40]
+        SelectColumn [7-40]
+          FunctionCall [7-30]
+            PathExpression [7-17]
+              Identifier(LAX_STRING) [7-17]
+            JSONLiteral [18-29]
+              StringLiteral [23-29]
+                StringLiteralComponent('true') [23-29]
+          Alias [31-40]
+            Identifier(result) [34-40]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_string
+SELECT LAX_STRING(JSON 'false') AS result
+--
+QueryStatement [0-41]
+  Query [0-41]
+    Select [0-41]
+      SelectList [7-41]
+        SelectColumn [7-41]
+          FunctionCall [7-31]
+            PathExpression [7-17]
+              Identifier(LAX_STRING) [7-17]
+            JSONLiteral [18-30]
+              StringLiteral [23-30]
+                StringLiteralComponent('false') [23-30]
+          Alias [32-41]
+            Identifier(result) [35-41]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_string
+SELECT LAX_STRING(JSON '10.0') AS result
+--
+QueryStatement [0-40]
+  Query [0-40]
+    Select [0-40]
+      SelectList [7-40]
+        SelectColumn [7-40]
+          FunctionCall [7-30]
+            PathExpression [7-17]
+              Identifier(LAX_STRING) [7-17]
+            JSONLiteral [18-29]
+              StringLiteral [23-29]
+                StringLiteralComponent('10.0') [23-29]
+          Alias [31-40]
+            Identifier(result) [34-40]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_string
+SELECT LAX_STRING(JSON '10') AS result
+--
+QueryStatement [0-38]
+  Query [0-38]
+    Select [0-38]
+      SelectList [7-38]
+        SelectColumn [7-38]
+          FunctionCall [7-28]
+            PathExpression [7-17]
+              Identifier(LAX_STRING) [7-17]
+            JSONLiteral [18-27]
+              StringLiteral [23-27]
+                StringLiteralComponent('10') [23-27]
+          Alias [29-38]
+            Identifier(result) [32-38]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#lax_string
+SELECT LAX_STRING(JSON '1e100') AS result
+--
+QueryStatement [0-41]
+  Query [0-41]
+    Select [0-41]
+      SelectList [7-41]
+        SelectColumn [7-41]
+          FunctionCall [7-31]
+            PathExpression [7-17]
+              Identifier(LAX_STRING) [7-17]
+            JSONLiteral [18-30]
+              StringLiteral [23-30]
+                StringLiteralComponent('1e100') [23-30]
+          Alias [32-41]
+            Identifier(result) [35-41]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#parse_json
+SELECT PARSE_JSON('{"coordinates": [10, 20], "id": 1}') AS json_data
+--
+QueryStatement [0-68]
+  Query [0-68]
+    Select [0-68]
+      SelectList [7-68]
+        SelectColumn [7-68]
+          FunctionCall [7-55]
+            PathExpression [7-17]
+              Identifier(PARSE_JSON) [7-17]
+            StringLiteral [18-54]
+              StringLiteralComponent('{"coordinates": [10, 20], "id": 1}') [18-54]
+          Alias [56-68]
+            Identifier(json_data) [59-68]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#parse_json
+SELECT PARSE_JSON('{"id": 922337203685477580701}') AS json_data
+--
+QueryStatement [0-63]
+  Query [0-63]
+    Select [0-63]
+      SelectList [7-63]
+        SelectColumn [7-63]
+          FunctionCall [7-50]
+            PathExpression [7-17]
+              Identifier(PARSE_JSON) [7-17]
+            StringLiteral [18-49]
+              StringLiteralComponent('{"id": 922337203685477580701}') [18-49]
+          Alias [51-63]
+            Identifier(json_data) [54-63]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#parse_json
+-- fails
+SELECT PARSE_JSON('{"id": 922337203685477580701}', wide_number_mode=>'exact') AS json_data
+--
+QueryStatement [9-99]
+  Query [9-99]
+    Select [9-99]
+      SelectList [16-99]
+        SelectColumn [16-99]
+          FunctionCall [16-86]
+            PathExpression [16-26]
+              Identifier(PARSE_JSON) [16-26]
+            StringLiteral [27-58]
+              StringLiteralComponent('{"id": 922337203685477580701}') [27-58]
+            NamedArgument [60-85]
+              Identifier(wide_number_mode) [60-76]
+              StringLiteral [78-85]
+                StringLiteralComponent('exact') [78-85]
+          Alias [87-99]
+            Identifier(json_data) [90-99]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#parse_json
+SELECT PARSE_JSON('{"id": 922337203685477580701}', wide_number_mode=>'round') AS json_data
+--
+QueryStatement [0-90]
+  Query [0-90]
+    Select [0-90]
+      SelectList [7-90]
+        SelectColumn [7-90]
+          FunctionCall [7-77]
+            PathExpression [7-17]
+              Identifier(PARSE_JSON) [7-17]
+            StringLiteral [18-49]
+              StringLiteralComponent('{"id": 922337203685477580701}') [18-49]
+            NamedArgument [51-76]
+              Identifier(wide_number_mode) [51-67]
+              StringLiteral [69-76]
+                StringLiteralComponent('round') [69-76]
+          Alias [78-90]
+            Identifier(json_data) [81-90]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#parse_json
+SELECT PARSE_JSON('6') AS json_data
+--
+QueryStatement [0-35]
+  Query [0-35]
+    Select [0-35]
+      SelectList [7-35]
+        SelectColumn [7-35]
+          FunctionCall [7-22]
+            PathExpression [7-17]
+              Identifier(PARSE_JSON) [7-17]
+            StringLiteral [18-21]
+              StringLiteralComponent('6') [18-21]
+          Alias [23-35]
+            Identifier(json_data) [26-35]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#parse_json
+SELECT PARSE_JSON('"red"') AS json_data
+--
+QueryStatement [0-39]
+  Query [0-39]
+    Select [0-39]
+      SelectList [7-39]
+        SelectColumn [7-39]
+          FunctionCall [7-26]
+            PathExpression [7-17]
+              Identifier(PARSE_JSON) [7-17]
+            StringLiteral [18-25]
+              StringLiteralComponent('"red"') [18-25]
+          Alias [27-39]
+            Identifier(json_data) [30-39]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#safe_to_json
+[mode=expression]
+SAFE_TO_JSON(sql_value)
+--
+FunctionCall [0-23]
+  PathExpression [0-12]
+    Identifier(SAFE_TO_JSON) [0-12]
+  PathExpression [13-22]
+    Identifier(sql_value) [13-22]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#safe_to_json
+-- Produces a JSON null.
+SELECT SAFE_TO_JSON(CAST(b'' AS UNSUPPORTED_TYPE)) as result
+--
+QueryStatement [25-85]
+  Query [25-85]
+    Select [25-85]
+      SelectList [32-85]
+        SelectColumn [32-85]
+          FunctionCall [32-75]
+            PathExpression [32-44]
+              Identifier(SAFE_TO_JSON) [32-44]
+            CastExpression [45-74]
+              BytesLiteral [50-53]
+                BytesLiteralComponent(b'') [50-53]
+              SimpleType [57-73]
+                PathExpression [57-73]
+                  Identifier(UNSUPPORTED_TYPE) [57-73]
+          Alias [76-85]
+            Identifier(result) [79-85]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#safe_to_json
+-- Produces an error.
+SELECT TO_JSON(CAST(b'' AS UNSUPPORTED_TYPE), stringify_wide_numbers=>TRUE) as result
+--
+QueryStatement [22-107]
+  Query [22-107]
+    Select [22-107]
+      SelectList [29-107]
+        SelectColumn [29-107]
+          FunctionCall [29-97]
+            PathExpression [29-36]
+              Identifier(TO_JSON) [29-36]
+            CastExpression [37-66]
+              BytesLiteral [42-45]
+                BytesLiteralComponent(b'') [42-45]
+              SimpleType [49-65]
+                PathExpression [49-65]
+                  Identifier(UNSUPPORTED_TYPE) [49-65]
+            NamedArgument [68-96]
+              Identifier(stringify_wide_numbers) [68-90]
+              BooleanLiteral(TRUE) [92-96]
+          Alias [98-107]
+            Identifier(result) [101-107]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#string_for_json
+[mode=expression]
+STRING(json_expr)
+--
+FunctionCall [0-17]
+  PathExpression [0-6]
+    Identifier(STRING) [0-6]
+  PathExpression [7-16]
+    Identifier(json_expr) [7-16]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#string_for_json
+[mode=expression]
+JSON '"purple"'
+--
+JSONLiteral [0-15]
+  StringLiteral [5-15]
+    StringLiteralComponent('"purple"') [5-15]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#string_for_json
+SELECT STRING(JSON '"purple"') AS color
+--
+QueryStatement [0-39]
+  Query [0-39]
+    Select [0-39]
+      SelectList [7-39]
+        SelectColumn [7-39]
+          FunctionCall [7-30]
+            PathExpression [7-13]
+              Identifier(STRING) [7-13]
+            JSONLiteral [14-29]
+              StringLiteral [19-29]
+                StringLiteralComponent('"purple"') [19-29]
+          Alias [31-39]
+            Identifier(color) [34-39]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#string_for_json
+SELECT STRING(JSON_QUERY(JSON '{"name": "sky", "color": "blue"}', "$.color")) AS color
+--
+QueryStatement [0-86]
+  Query [0-86]
+    Select [0-86]
+      SelectList [7-86]
+        SelectColumn [7-86]
+          FunctionCall [7-77]
+            PathExpression [7-13]
+              Identifier(STRING) [7-13]
+            FunctionCall [14-76]
+              PathExpression [14-24]
+                Identifier(JSON_QUERY) [14-24]
+              JSONLiteral [25-64]
+                StringLiteral [30-64]
+                  StringLiteralComponent('{"name": "sky", "color": "blue"}') [30-64]
+              StringLiteral [66-75]
+                StringLiteralComponent("$.color") [66-75]
+          Alias [78-86]
+            Identifier(color) [81-86]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#string_for_json
+-- An error is thrown if the JSON isn't of type string.
+SELECT STRING(JSON '123') AS result
+--
+QueryStatement [56-91]
+  Query [56-91]
+    Select [56-91]
+      SelectList [63-91]
+        SelectColumn [63-91]
+          FunctionCall [63-81]
+            PathExpression [63-69]
+              Identifier(STRING) [63-69]
+            JSONLiteral [70-80]
+              StringLiteral [75-80]
+                StringLiteralComponent('123') [75-80]
+          Alias [82-91]
+            Identifier(result) [85-91]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#string_for_json
+-- Throws an error
+SELECT STRING(JSON 'null') AS result
+--
+QueryStatement [19-55]
+  Query [19-55]
+    Select [19-55]
+      SelectList [26-55]
+        SelectColumn [26-55]
+          FunctionCall [26-45]
+            PathExpression [26-32]
+              Identifier(STRING) [26-32]
+            JSONLiteral [33-44]
+              StringLiteral [38-44]
+                StringLiteralComponent('null') [38-44]
+          Alias [46-55]
+            Identifier(result) [49-55]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#string_for_json
+-- Throws an error
+SELECT SAFE.STRING(JSON '123') AS result
+--
+QueryStatement [19-59]
+  Query [19-59]
+    Select [19-59]
+      SelectList [26-59]
+        SelectColumn [26-59]
+          FunctionCall [26-49]
+            PathExpression [26-37]
+              Identifier(SAFE) [26-30]
+              Identifier(STRING) [31-37]
+            JSONLiteral [38-48]
+              StringLiteral [43-48]
+                StringLiteralComponent('123') [43-48]
+          Alias [50-59]
+            Identifier(result) [53-59]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#string_array_for_json
+[mode=expression]
+STRING_ARRAY(json_expr)
+--
+FunctionCall [0-23]
+  PathExpression [0-12]
+    Identifier(STRING_ARRAY) [0-12]
+  PathExpression [13-22]
+    Identifier(json_expr) [13-22]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#string_array_for_json
+[mode=expression]
+JSON '["purple", "blue"]'
+--
+JSONLiteral [0-25]
+  StringLiteral [5-25]
+    StringLiteralComponent('["purple", "blue"]') [5-25]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#string_array_for_json
+SELECT STRING_ARRAY(JSON '["purple", "blue"]') AS colors
+--
+QueryStatement [0-56]
+  Query [0-56]
+    Select [0-56]
+      SelectList [7-56]
+        SelectColumn [7-56]
+          FunctionCall [7-46]
+            PathExpression [7-19]
+              Identifier(STRING_ARRAY) [7-19]
+            JSONLiteral [20-45]
+              StringLiteral [25-45]
+                StringLiteralComponent('["purple", "blue"]') [25-45]
+          Alias [47-56]
+            Identifier(colors) [50-56]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#string_array_for_json
+-- An error is thrown if the JSON isn't an array of strings.
+SELECT STRING_ARRAY(JSON '[123]') AS result
+--
+QueryStatement [61-104]
+  Query [61-104]
+    Select [61-104]
+      SelectList [68-104]
+        SelectColumn [68-104]
+          FunctionCall [68-94]
+            PathExpression [68-80]
+              Identifier(STRING_ARRAY) [68-80]
+            JSONLiteral [81-93]
+              StringLiteral [86-93]
+                StringLiteralComponent('[123]') [86-93]
+          Alias [95-104]
+            Identifier(result) [98-104]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#string_array_for_json
+-- Throws an error
+SELECT STRING_ARRAY(JSON '[null]') AS result
+--
+QueryStatement [19-63]
+  Query [19-63]
+    Select [19-63]
+      SelectList [26-63]
+        SelectColumn [26-63]
+          FunctionCall [26-53]
+            PathExpression [26-38]
+              Identifier(STRING_ARRAY) [26-38]
+            JSONLiteral [39-52]
+              StringLiteral [44-52]
+                StringLiteralComponent('[null]') [44-52]
+          Alias [54-63]
+            Identifier(result) [57-63]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#string_array_for_json
+-- Throws an error
+SELECT STRING_ARRAY(JSON 'null') AS result
+--
+QueryStatement [19-61]
+  Query [19-61]
+    Select [19-61]
+      SelectList [26-61]
+        SelectColumn [26-61]
+          FunctionCall [26-51]
+            PathExpression [26-38]
+              Identifier(STRING_ARRAY) [26-38]
+            JSONLiteral [39-50]
+              StringLiteral [44-50]
+                StringLiteralComponent('null') [44-50]
+          Alias [52-61]
+            Identifier(result) [55-61]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#to_json
+With CoordinatesTable AS (
+    (SELECT 1 AS id, [10, 20] AS coordinates) UNION ALL
+    (SELECT 2 AS id, [30, 40] AS coordinates) UNION ALL
+    (SELECT 3 AS id, [50, 60] AS coordinates))
+SELECT TO_JSON(t) AS json_objects
+FROM CoordinatesTable AS t
+--
+QueryStatement [0-246]
+  Query [0-246]
+    WithClause [0-185]
+      WithClauseEntry [5-185]
+        AliasedQuery [5-185]
+          Identifier(CoordinatesTable) [5-21]
+          Query [25-185]
+            SetOperation(UNION ALL) [31-184]
+              SetOperationMetadataList [73-138]
+                SetOperationMetadata [73-82]
+                  SetOperationType [73-78]
+                  SetOperationAllOrDistinct [79-82]
+                SetOperationMetadata [129-138]
+                  SetOperationType [129-134]
+                  SetOperationAllOrDistinct [135-138]
+              Query [32-71]
+                Select [32-71]
+                  SelectList [39-71]
+                    SelectColumn [39-46]
+                      IntLiteral(1) [39-40]
+                      Alias [41-46]
+                        Identifier(id) [44-46]
+                    SelectColumn [48-71]
+                      ArrayConstructor [48-56]
+                        IntLiteral(10) [49-51]
+                        IntLiteral(20) [53-55]
+                      Alias [57-71]
+                        Identifier(coordinates) [60-71]
+              Query [88-127]
+                Select [88-127]
+                  SelectList [95-127]
+                    SelectColumn [95-102]
+                      IntLiteral(2) [95-96]
+                      Alias [97-102]
+                        Identifier(id) [100-102]
+                    SelectColumn [104-127]
+                      ArrayConstructor [104-112]
+                        IntLiteral(30) [105-107]
+                        IntLiteral(40) [109-111]
+                      Alias [113-127]
+                        Identifier(coordinates) [116-127]
+              Query [144-183]
+                Select [144-183]
+                  SelectList [151-183]
+                    SelectColumn [151-158]
+                      IntLiteral(3) [151-152]
+                      Alias [153-158]
+                        Identifier(id) [156-158]
+                    SelectColumn [160-183]
+                      ArrayConstructor [160-168]
+                        IntLiteral(50) [161-163]
+                        IntLiteral(60) [165-167]
+                      Alias [169-183]
+                        Identifier(coordinates) [172-183]
+    Select [186-246]
+      SelectList [193-219]
+        SelectColumn [193-219]
+          FunctionCall [193-203]
+            PathExpression [193-200]
+              Identifier(TO_JSON) [193-200]
+            PathExpression [201-202]
+              Identifier(t) [201-202]
+          Alias [204-219]
+            Identifier(json_objects) [207-219]
+      FromClause [220-246]
+        TablePathExpression [225-246]
+          PathExpression [225-241]
+            Identifier(CoordinatesTable) [225-241]
+          Alias [242-246]
+            Identifier(t) [245-246]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#to_json
+SELECT TO_JSON(9007199254740993, stringify_wide_numbers=>TRUE) as stringify_on
+--
+QueryStatement [0-78]
+  Query [0-78]
+    Select [0-78]
+      SelectList [7-78]
+        SelectColumn [7-78]
+          FunctionCall [7-62]
+            PathExpression [7-14]
+              Identifier(TO_JSON) [7-14]
+            IntLiteral(9007199254740993) [15-31]
+            NamedArgument [33-61]
+              Identifier(stringify_wide_numbers) [33-55]
+              BooleanLiteral(TRUE) [57-61]
+          Alias [63-78]
+            Identifier(stringify_on) [66-78]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#to_json
+SELECT TO_JSON(9007199254740993, stringify_wide_numbers=>FALSE) as stringify_off
+--
+QueryStatement [0-80]
+  Query [0-80]
+    Select [0-80]
+      SelectList [7-80]
+        SelectColumn [7-80]
+          FunctionCall [7-63]
+            PathExpression [7-14]
+              Identifier(TO_JSON) [7-14]
+            IntLiteral(9007199254740993) [15-31]
+            NamedArgument [33-62]
+              Identifier(stringify_wide_numbers) [33-55]
+              BooleanLiteral(FALSE) [57-62]
+          Alias [64-80]
+            Identifier(stringify_off) [67-80]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#to_json
+SELECT TO_JSON(9007199254740993) as stringify_off
+--
+QueryStatement [0-49]
+  Query [0-49]
+    Select [0-49]
+      SelectList [7-49]
+        SelectColumn [7-49]
+          FunctionCall [7-32]
+            PathExpression [7-14]
+              Identifier(TO_JSON) [7-14]
+            IntLiteral(9007199254740993) [15-31]
+          Alias [33-49]
+            Identifier(stringify_off) [36-49]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#to_json
+With T1 AS (
+  (SELECT 9007199254740993 AS id) UNION ALL
+  (SELECT 2 AS id))
+SELECT TO_JSON(t, stringify_wide_numbers=>TRUE) AS json_objects
+FROM T1 AS t
+--
+QueryStatement [0-153]
+  Query [0-153]
+    WithClause [0-76]
+      WithClauseEntry [5-76]
+        AliasedQuery [5-76]
+          Identifier(T1) [5-7]
+          Query [11-76]
+            SetOperation(UNION ALL) [15-75]
+              SetOperationMetadataList [47-56]
+                SetOperationMetadata [47-56]
+                  SetOperationType [47-52]
+                  SetOperationAllOrDistinct [53-56]
+              Query [16-45]
+                Select [16-45]
+                  SelectList [23-45]
+                    SelectColumn [23-45]
+                      IntLiteral(9007199254740993) [23-39]
+                      Alias [40-45]
+                        Identifier(id) [43-45]
+              Query [60-74]
+                Select [60-74]
+                  SelectList [67-74]
+                    SelectColumn [67-74]
+                      IntLiteral(2) [67-68]
+                      Alias [69-74]
+                        Identifier(id) [72-74]
+    Select [77-153]
+      SelectList [84-140]
+        SelectColumn [84-140]
+          FunctionCall [84-124]
+            PathExpression [84-91]
+              Identifier(TO_JSON) [84-91]
+            PathExpression [92-93]
+              Identifier(t) [92-93]
+            NamedArgument [95-123]
+              Identifier(stringify_wide_numbers) [95-117]
+              BooleanLiteral(TRUE) [119-123]
+          Alias [125-140]
+            Identifier(json_objects) [128-140]
+      FromClause [141-153]
+        TablePathExpression [146-153]
+          PathExpression [146-148]
+            Identifier(T1) [146-148]
+          Alias [149-153]
+            Identifier(t) [152-153]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#to_json
+With T1 AS (
+  (SELECT 9007199254740993 AS id) UNION ALL
+  (SELECT 2.1 AS id))
+SELECT TO_JSON(t, stringify_wide_numbers=>TRUE) AS json_objects
+FROM T1 AS t
+--
+QueryStatement [0-155]
+  Query [0-155]
+    WithClause [0-78]
+      WithClauseEntry [5-78]
+        AliasedQuery [5-78]
+          Identifier(T1) [5-7]
+          Query [11-78]
+            SetOperation(UNION ALL) [15-77]
+              SetOperationMetadataList [47-56]
+                SetOperationMetadata [47-56]
+                  SetOperationType [47-52]
+                  SetOperationAllOrDistinct [53-56]
+              Query [16-45]
+                Select [16-45]
+                  SelectList [23-45]
+                    SelectColumn [23-45]
+                      IntLiteral(9007199254740993) [23-39]
+                      Alias [40-45]
+                        Identifier(id) [43-45]
+              Query [60-76]
+                Select [60-76]
+                  SelectList [67-76]
+                    SelectColumn [67-76]
+                      FloatLiteral(2.1) [67-70]
+                      Alias [71-76]
+                        Identifier(id) [74-76]
+    Select [79-155]
+      SelectList [86-142]
+        SelectColumn [86-142]
+          FunctionCall [86-126]
+            PathExpression [86-93]
+              Identifier(TO_JSON) [86-93]
+            PathExpression [94-95]
+              Identifier(t) [94-95]
+            NamedArgument [97-125]
+              Identifier(stringify_wide_numbers) [97-119]
+              BooleanLiteral(TRUE) [121-125]
+          Alias [127-142]
+            Identifier(json_objects) [130-142]
+      FromClause [143-155]
+        TablePathExpression [148-155]
+          PathExpression [148-150]
+            Identifier(T1) [148-150]
+          Alias [151-155]
+            Identifier(t) [154-155]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#to_json
+GRAPH FinGraph
+MATCH p=(src:Account)-[t1:Transfers]->(dst:Account)
+RETURN TO_JSON(p) AS json_array
+--
+QueryStatement [0-98]
+  Query [0-98]
+    GqlQuery [0-98]
+      GraphTableQuery [0-98]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-98]
+          GqlOperatorList [15-98]
+            GqlMatch [15-66]
+              GraphPattern [21-66]
+                GraphPathPattern [21-66]
+                  Identifier(p) [21-22]
+                  GraphNodePattern [23-36]
+                    GraphElementPatternFiller [24-35]
+                      Identifier(src) [24-27]
+                      GraphLabelFilter [27-35]
+                        GraphElementLabel [28-35]
+                          Identifier(Account) [28-35]
+                  GraphEdgePattern [36-53]
+                    GraphElementPatternFiller [38-50]
+                      Identifier(t1) [38-40]
+                      GraphLabelFilter [40-50]
+                        GraphElementLabel [41-50]
+                          Identifier(Transfers) [41-50]
+                  GraphNodePattern [53-66]
+                    GraphElementPatternFiller [54-65]
+                      Identifier(dst) [54-57]
+                      GraphLabelFilter [57-65]
+                        GraphElementLabel [58-65]
+                          Identifier(Account) [58-65]
+            GqlReturn [67-98]
+              Select [74-98]
+                SelectList [74-98]
+                  SelectColumn [74-98]
+                    FunctionCall [74-84]
+                      PathExpression [74-81]
+                        Identifier(TO_JSON) [74-81]
+                      PathExpression [82-83]
+                        Identifier(p) [82-83]
+                    Alias [85-98]
+                      Identifier(json_array) [88-98]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#to_json
+GRAPH FinGraph
+MATCH (src:Account {id: 7})-[t1:Transfers]->(dst:Account)
+RETURN TO_JSON(src) AS json_array
+--
+QueryStatement [0-106]
+  Query [0-106]
+    GqlQuery [0-106]
+      GraphTableQuery [0-106]
+        PathExpression [6-14]
+          Identifier(FinGraph) [6-14]
+        GqlOperatorList [15-106]
+          GqlOperatorList [15-106]
+            GqlMatch [15-72]
+              GraphPattern [21-72]
+                GraphPathPattern [21-72]
+                  GraphNodePattern [21-42]
+                    GraphElementPatternFiller [22-41]
+                      Identifier(src) [22-25]
+                      GraphLabelFilter [25-33]
+                        GraphElementLabel [26-33]
+                          Identifier(Account) [26-33]
+                      GraphPropertySpecification [34-41]
+                        GraphPropertyNameAndValue [35-40]
+                          Identifier(id) [35-37]
+                          IntLiteral(7) [39-40]
+                  GraphEdgePattern [42-59]
+                    GraphElementPatternFiller [44-56]
+                      Identifier(t1) [44-46]
+                      GraphLabelFilter [46-56]
+                        GraphElementLabel [47-56]
+                          Identifier(Transfers) [47-56]
+                  GraphNodePattern [59-72]
+                    GraphElementPatternFiller [60-71]
+                      Identifier(dst) [60-63]
+                      GraphLabelFilter [63-71]
+                        GraphElementLabel [64-71]
+                          Identifier(Account) [64-71]
+            GqlReturn [73-106]
+              Select [80-106]
+                SelectList [80-106]
+                  SelectColumn [80-106]
+                    FunctionCall [80-92]
+                      PathExpression [80-87]
+                        Identifier(TO_JSON) [80-87]
+                      PathExpression [88-91]
+                        Identifier(src) [88-91]
+                    Alias [93-106]
+                      Identifier(json_array) [96-106]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#to_json_string
+[mode=expression]
+TO_JSON_STRING(json_expr)
+--
+FunctionCall [0-25]
+  PathExpression [0-14]
+    Identifier(TO_JSON_STRING) [0-14]
+  PathExpression [15-24]
+    Identifier(json_expr) [15-24]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#to_json_string
+SELECT TO_JSON_STRING(JSON '{"id": 1, "coordinates": [10, 20]}') AS json_string
+--
+QueryStatement [0-79]
+  Query [0-79]
+    Select [0-79]
+      SelectList [7-79]
+        SelectColumn [7-79]
+          FunctionCall [7-64]
+            PathExpression [7-21]
+              Identifier(TO_JSON_STRING) [7-21]
+            JSONLiteral [22-63]
+              StringLiteral [27-63]
+                StringLiteralComponent('{"id": 1, "coordinates": [10, 20]}') [27-63]
+          Alias [65-79]
+            Identifier(json_string) [68-79]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#non-validation_of_string_inputs
+SELECT JSON_VALUE('{"hello": "world"', "$.hello") AS hello
+--
+QueryStatement [0-58]
+  Query [0-58]
+    Select [0-58]
+      SelectList [7-58]
+        SelectColumn [7-58]
+          FunctionCall [7-49]
+            PathExpression [7-17]
+              Identifier(JSON_VALUE) [7-17]
+            StringLiteral [18-37]
+              StringLiteralComponent('{"hello": "world"') [18-37]
+            StringLiteral [39-48]
+              StringLiteralComponent("$.hello") [39-48]
+          Alias [50-58]
+            Identifier(hello) [53-58]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#non-validation_of_string_inputs
+SELECT JSON_VALUE(JSON '{"hello": "world"', "$.hello") AS hello
+--
+QueryStatement [0-63]
+  Query [0-63]
+    Select [0-63]
+      SelectList [7-63]
+        SelectColumn [7-63]
+          FunctionCall [7-54]
+            PathExpression [7-17]
+              Identifier(JSON_VALUE) [7-17]
+            JSONLiteral [18-42]
+              StringLiteral [23-42]
+                StringLiteralComponent('{"hello": "world"') [23-42]
+            StringLiteral [44-53]
+              StringLiteralComponent("$.hello") [44-53]
+          Alias [55-63]
+            Identifier(hello) [58-63]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#no_strict_validation_of_extracted_values
+SELECT JSON_QUERY('{"key": 1, "key": 2}', "$") AS string
+--
+QueryStatement [0-56]
+  Query [0-56]
+    Select [0-56]
+      SelectList [7-56]
+        SelectColumn [7-56]
+          FunctionCall [7-46]
+            PathExpression [7-17]
+              Identifier(JSON_QUERY) [7-17]
+            StringLiteral [18-40]
+              StringLiteralComponent('{"key": 1, "key": 2}') [18-40]
+            StringLiteral [42-45]
+              StringLiteralComponent("$") [42-45]
+          Alias [47-56]
+            Identifier(string) [50-56]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#no_strict_validation_of_extracted_values
+SELECT JSON_QUERY(JSON '{"key": 1, "key": 2}', "$") AS json
+--
+QueryStatement [0-59]
+  Query [0-59]
+    Select [0-59]
+      SelectList [7-59]
+        SelectColumn [7-59]
+          FunctionCall [7-51]
+            PathExpression [7-17]
+              Identifier(JSON_QUERY) [7-17]
+            JSONLiteral [18-45]
+              StringLiteral [23-45]
+                StringLiteralComponent('{"key": 1, "key": 2}') [23-45]
+            StringLiteral [47-50]
+              StringLiteralComponent("$") [47-50]
+          Alias [52-59]
+            Identifier(json) [55-59]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/json_functions#json_null
+WITH t AS (
+  SELECT '{"name": null}' AS json_string, JSON '{"name": null}' AS json)
+SELECT JSON_QUERY(json_string, "$.name") AS name_string,
+  JSON_QUERY(json_string, "$.name") IS NULL AS name_string_is_null,
+  JSON_QUERY(json, "$.name") AS name_json,
+  JSON_QUERY(json, "$.name") IS NULL AS name_json_is_null
+FROM t
+--
+QueryStatement [0-317]
+  Query [0-317]
+    WithClause [0-84]
+      WithClauseEntry [5-84]
+        AliasedQuery [5-84]
+          Identifier(t) [5-6]
+          Query [10-84]
+            Select [14-83]
+              SelectList [21-83]
+                SelectColumn [21-52]
+                  StringLiteral [21-37]
+                    StringLiteralComponent('{"name": null}') [21-37]
+                  Alias [38-52]
+                    Identifier(json_string) [41-52]
+                SelectColumn [54-83]
+                  JSONLiteral [54-75]
+                    StringLiteral [59-75]
+                      StringLiteralComponent('{"name": null}') [59-75]
+                  Alias [76-83]
+                    Identifier(json) [79-83]
+    Select [85-317]
+      SelectList [92-310]
+        SelectColumn [92-140]
+          FunctionCall [92-125]
+            PathExpression [92-102]
+              Identifier(JSON_QUERY) [92-102]
+            PathExpression [103-114]
+              Identifier(json_string) [103-114]
+            StringLiteral [116-124]
+              StringLiteralComponent("$.name") [116-124]
+          Alias [126-140]
+            Identifier(name_string) [129-140]
+        SelectColumn [144-208]
+          BinaryExpression(IS) [144-185]
+            FunctionCall [144-177]
+              PathExpression [144-154]
+                Identifier(JSON_QUERY) [144-154]
+              PathExpression [155-166]
+                Identifier(json_string) [155-166]
+              StringLiteral [168-176]
+                StringLiteralComponent("$.name") [168-176]
+            NullLiteral(NULL) [181-185]
+          Alias [186-208]
+            Identifier(name_string_is_null) [189-208]
+        SelectColumn [212-251]
+          FunctionCall [212-238]
+            PathExpression [212-222]
+              Identifier(JSON_QUERY) [212-222]
+            PathExpression [223-227]
+              Identifier(json) [223-227]
+            StringLiteral [229-237]
+              StringLiteralComponent("$.name") [229-237]
+          Alias [239-251]
+            Identifier(name_json) [242-251]
+        SelectColumn [255-310]
+          BinaryExpression(IS) [255-289]
+            FunctionCall [255-281]
+              PathExpression [255-265]
+                Identifier(JSON_QUERY) [255-265]
+              PathExpression [266-270]
+                Identifier(json) [266-270]
+              StringLiteral [272-280]
+                StringLiteralComponent("$.name") [272-280]
+            NullLiteral(NULL) [285-289]
+          Alias [290-310]
+            Identifier(name_json_is_null) [293-310]
+      FromClause [311-317]
+        TablePathExpression [316-317]
+          PathExpression [316-317]
+            Identifier(t) [316-317]
diff --git a/parser/testdata/spanner/lexical.test b/parser/testdata/spanner/lexical.test
new file mode 100644
index 0000000..a01621c
--- /dev/null
+++ b/parser/testdata/spanner/lexical.test
@@ -0,0 +1,476 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#identifier_examples
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+-- Valid. dataField is a valid identifier in a function called foo().
+foo().dataField
+--
+DotIdentifier [70-85]
+  FunctionCall [70-75]
+    PathExpression [70-73]
+      Identifier(foo) [70-73]
+  Identifier(dataField) [76-85]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#identifier_examples
+[mode=expression]
+-- Valid. dataField is a valid identifier in an array called items.
+items[OFFSET(3)].dataField
+--
+DotIdentifier [68-94]
+  ArrayElement [68-84]
+    PathExpression [68-73]
+      Identifier(items) [68-73]
+    Location [73-74]
+    FunctionCall [74-83]
+      PathExpression [74-80]
+        Identifier(OFFSET) [74-80]
+      IntLiteral(3) [81-82]
+  Identifier(dataField) [85-94]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#identifier_examples
+[mode=expression]
+-- Valid. param and dataField are valid identifiers.
+@param.dataField
+--
+DotIdentifier [53-69]
+  ParameterExpr [53-59]
+    Identifier(param) [54-59]
+  Identifier(dataField) [60-69]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#identifier_examples
+[mode=expression]
+-- Valid. dataField is a valid identifier in a protocol buffer called foo.
+(foo).dataField
+--
+DotIdentifier [75-90]
+  PathExpression [76-79]
+    Identifier(foo) [76-79]
+  Identifier(dataField) [81-90]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#numeric_literals
+SELECT NUMERIC '0'
+--
+QueryStatement [0-18]
+  Query [0-18]
+    Select [0-18]
+      SelectList [7-18]
+        SelectColumn [7-18]
+          NumericLiteral [7-18]
+            StringLiteral [15-18]
+              StringLiteralComponent('0') [15-18]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#numeric_literals
+SELECT NUMERIC '123456'
+--
+QueryStatement [0-23]
+  Query [0-23]
+    Select [0-23]
+      SelectList [7-23]
+        SelectColumn [7-23]
+          NumericLiteral [7-23]
+            StringLiteral [15-23]
+              StringLiteralComponent('123456') [15-23]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#numeric_literals
+SELECT NUMERIC '-3.14'
+--
+QueryStatement [0-22]
+  Query [0-22]
+    Select [0-22]
+      SelectList [7-22]
+        SelectColumn [7-22]
+          NumericLiteral [7-22]
+            StringLiteral [15-22]
+              StringLiteralComponent('-3.14') [15-22]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#numeric_literals
+SELECT NUMERIC '-0.54321'
+--
+QueryStatement [0-25]
+  Query [0-25]
+    Select [0-25]
+      SelectList [7-25]
+        SelectColumn [7-25]
+          NumericLiteral [7-25]
+            StringLiteral [15-25]
+              StringLiteralComponent('-0.54321') [15-25]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#numeric_literals
+SELECT NUMERIC '1.23456e05'
+--
+QueryStatement [0-27]
+  Query [0-27]
+    Select [0-27]
+      SelectList [7-27]
+        SelectColumn [7-27]
+          NumericLiteral [7-27]
+            StringLiteral [15-27]
+              StringLiteralComponent('1.23456e05') [15-27]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#numeric_literals
+SELECT NUMERIC '-9.876e-3'
+--
+QueryStatement [0-26]
+  Query [0-26]
+    Select [0-26]
+      SelectList [7-26]
+        SelectColumn [7-26]
+          NumericLiteral [7-26]
+            StringLiteral [15-26]
+              StringLiteralComponent('-9.876e-3') [15-26]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#date_literals
+[mode=expression]
+DATE 'date_canonical_format'
+--
+DateOrTimeLiteral(TYPE_DATE) [0-28]
+  StringLiteral [5-28]
+    StringLiteralComponent('date_canonical_format') [5-28]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#date_literals
+[mode=expression]
+DATE '2014-09-27'
+--
+DateOrTimeLiteral(TYPE_DATE) [0-17]
+  StringLiteral [5-17]
+    StringLiteralComponent('2014-09-27') [5-17]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#date_literals
+SELECT * FROM foo WHERE date_col = "2014-09-27"
+--
+QueryStatement [0-47]
+  Query [0-47]
+    Select [0-47]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-17]
+        TablePathExpression [14-17]
+          PathExpression [14-17]
+            Identifier(foo) [14-17]
+      WhereClause [18-47]
+        BinaryExpression(=) [24-47]
+          PathExpression [24-32]
+            Identifier(date_col) [24-32]
+          StringLiteral [35-47]
+            StringLiteralComponent("2014-09-27") [35-47]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#timestamp_literals
+[mode=expression]
+TIMESTAMP 'timestamp_canonical_format'
+--
+DateOrTimeLiteral(TYPE_TIMESTAMP) [0-38]
+  StringLiteral [10-38]
+    StringLiteralComponent('timestamp_canonical_format') [10-38]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#timestamp_literals
+[mode=expression]
+TIMESTAMP '2014-09-27 12:30:00.45-08'
+--
+DateOrTimeLiteral(TYPE_TIMESTAMP) [0-37]
+  StringLiteral [10-37]
+    StringLiteralComponent('2014-09-27 12:30:00.45-08') [10-37]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#timestamp_literals
+[mode=expression]
+TIMESTAMP '2014-09-27 12:30:00.45'
+--
+DateOrTimeLiteral(TYPE_TIMESTAMP) [0-34]
+  StringLiteral [10-34]
+    StringLiteralComponent('2014-09-27 12:30:00.45') [10-34]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#timestamp_literals
+SELECT * FROM foo
+WHERE timestamp_col = "2014-09-27 12:30:00.45 America/Los_Angeles"
+--
+QueryStatement [0-84]
+  Query [0-84]
+    Select [0-84]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-17]
+        TablePathExpression [14-17]
+          PathExpression [14-17]
+            Identifier(foo) [14-17]
+      WhereClause [18-84]
+        BinaryExpression(=) [24-84]
+          PathExpression [24-37]
+            Identifier(timestamp_col) [24-37]
+          StringLiteral [40-84]
+            StringLiteralComponent("2014-09-27 12:30:00.45 America/Los_Angeles") [40-84]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#timezone
+[mode=expression]
+'-08:00'
+'-8:15'
+'+3:00'
+'+07:30'
+'-7'
+--
+StringLiteral [0-38]
+  StringLiteralComponent('-08:00') [0-8]
+  StringLiteralComponent('-8:15') [9-16]
+  StringLiteralComponent('+3:00') [17-24]
+  StringLiteralComponent('+07:30') [25-33]
+  StringLiteralComponent('-7') [34-38]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#json_literals
+[mode=expression]
+JSON 'json_formatted_data'
+--
+JSONLiteral [0-26]
+  StringLiteral [5-26]
+    StringLiteralComponent('json_formatted_data') [5-26]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#trailing_commas
+SELECT name, release_date, FROM Books
+--
+QueryStatement [0-37]
+  Query [0-37]
+    Select [0-37]
+      SelectList [7-26]
+        SelectColumn [7-11]
+          PathExpression [7-11]
+            Identifier(name) [7-11]
+        SelectColumn [13-25]
+          PathExpression [13-25]
+            Identifier(release_date) [13-25]
+      FromClause [27-37]
+        TablePathExpression [32-37]
+          PathExpression [32-37]
+            Identifier(Books) [32-37]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#query_parameters
+SELECT AlbumId FROM Albums WHERE SEARCH(AlbumTitle_Tokens, 'cat')
+--
+QueryStatement [0-65]
+  Query [0-65]
+    Select [0-65]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-65]
+        FunctionCall [33-65]
+          PathExpression [33-39]
+            Identifier(SEARCH) [33-39]
+          PathExpression [40-57]
+            Identifier(AlbumTitle_Tokens) [40-57]
+          StringLiteral [59-64]
+            StringLiteralComponent('cat') [59-64]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#query_parameters
+SELECT AlbumId FROM Albums WHERE SEARCH(AlbumTitle_Tokens, @p)
+--
+QueryStatement [0-62]
+  Query [0-62]
+    Select [0-62]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(AlbumId) [7-14]
+      FromClause [15-26]
+        TablePathExpression [20-26]
+          PathExpression [20-26]
+            Identifier(Albums) [20-26]
+      WhereClause [27-62]
+        FunctionCall [33-62]
+          PathExpression [33-39]
+            Identifier(SEARCH) [33-39]
+          PathExpression [40-57]
+            Identifier(AlbumTitle_Tokens) [40-57]
+          ParameterExpr [59-61]
+            Identifier(p) [60-61]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#named_query_parameters
+[mode=expression]
+@parameter_name
+--
+ParameterExpr [0-15]
+  Identifier(parameter_name) [1-15]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#named_query_parameters
+SELECT * FROM Roster WHERE LastName = @myparam
+--
+QueryStatement [0-46]
+  Query [0-46]
+    Select [0-46]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-20]
+        TablePathExpression [14-20]
+          PathExpression [14-20]
+            Identifier(Roster) [14-20]
+      WhereClause [21-46]
+        BinaryExpression(=) [27-46]
+          PathExpression [27-35]
+            Identifier(LastName) [27-35]
+          ParameterExpr [38-46]
+            Identifier(myparam) [39-46]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#single_line_comments
+\# this is a single-line comment
+SELECT book FROM library
+--
+QueryStatement [32-56]
+  Query [32-56]
+    Select [32-56]
+      SelectList [39-43]
+        SelectColumn [39-43]
+          PathExpression [39-43]
+            Identifier(book) [39-43]
+      FromClause [44-56]
+        TablePathExpression [49-56]
+          PathExpression [49-56]
+            Identifier(library) [49-56]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#single_line_comments
+-- this is a single-line comment
+SELECT book FROM library
+--
+QueryStatement [33-57]
+  Query [33-57]
+    Select [33-57]
+      SelectList [40-44]
+        SelectColumn [40-44]
+          PathExpression [40-44]
+            Identifier(book) [40-44]
+      FromClause [45-57]
+        TablePathExpression [50-57]
+          PathExpression [50-57]
+            Identifier(library) [50-57]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#single_line_comments
+/* this is a single-line comment */
+SELECT book FROM library
+--
+QueryStatement [36-60]
+  Query [36-60]
+    Select [36-60]
+      SelectList [43-47]
+        SelectColumn [43-47]
+          PathExpression [43-47]
+            Identifier(book) [43-47]
+      FromClause [48-60]
+        TablePathExpression [53-60]
+          PathExpression [53-60]
+            Identifier(library) [53-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#single_line_comments
+SELECT book FROM library
+/* this is a single-line comment */
+WHERE book = "Ulysses"
+--
+QueryStatement [0-83]
+  Query [0-83]
+    Select [0-83]
+      SelectList [7-11]
+        SelectColumn [7-11]
+          PathExpression [7-11]
+            Identifier(book) [7-11]
+      FromClause [12-24]
+        TablePathExpression [17-24]
+          PathExpression [17-24]
+            Identifier(library) [17-24]
+      WhereClause [61-83]
+        BinaryExpression(=) [67-83]
+          PathExpression [67-71]
+            Identifier(book) [67-71]
+          StringLiteral [74-83]
+            StringLiteralComponent("Ulysses") [74-83]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#inline_comments
+SELECT book FROM library
+--
+QueryStatement [0-24]
+  Query [0-24]
+    Select [0-24]
+      SelectList [7-11]
+        SelectColumn [7-11]
+          PathExpression [7-11]
+            Identifier(book) [7-11]
+      FromClause [12-24]
+        TablePathExpression [17-24]
+          PathExpression [17-24]
+            Identifier(library) [17-24]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#inline_comments
+SELECT book FROM library /* this is an inline comment */ WHERE book = "Ulysses"
+--
+QueryStatement [0-79]
+  Query [0-79]
+    Select [0-79]
+      SelectList [7-11]
+        SelectColumn [7-11]
+          PathExpression [7-11]
+            Identifier(book) [7-11]
+      FromClause [12-24]
+        TablePathExpression [17-24]
+          PathExpression [17-24]
+            Identifier(library) [17-24]
+      WhereClause [57-79]
+        BinaryExpression(=) [63-79]
+          PathExpression [63-67]
+            Identifier(book) [63-67]
+          StringLiteral [70-79]
+            StringLiteralComponent("Ulysses") [70-79]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#multiline_comments
+SELECT book FROM library
+/*
+  This is a multiline comment
+  on multiple lines
+*/
+WHERE book = "Ulysses"
+--
+QueryStatement [0-103]
+  Query [0-103]
+    Select [0-103]
+      SelectList [7-11]
+        SelectColumn [7-11]
+          PathExpression [7-11]
+            Identifier(book) [7-11]
+      FromClause [12-24]
+        TablePathExpression [17-24]
+          PathExpression [17-24]
+            Identifier(library) [17-24]
+      WhereClause [81-103]
+        BinaryExpression(=) [87-103]
+          PathExpression [87-91]
+            Identifier(book) [87-91]
+          StringLiteral [94-103]
+            StringLiteralComponent("Ulysses") [94-103]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/lexical#multiline_comments
+SELECT book FROM library
+/* this is a multiline comment
+on two lines */
+WHERE book = "Ulysses"
+--
+QueryStatement [0-94]
+  Query [0-94]
+    Select [0-94]
+      SelectList [7-11]
+        SelectColumn [7-11]
+          PathExpression [7-11]
+            Identifier(book) [7-11]
+      FromClause [12-24]
+        TablePathExpression [17-24]
+          PathExpression [17-24]
+            Identifier(library) [17-24]
+      WhereClause [72-94]
+        BinaryExpression(=) [78-94]
+          PathExpression [78-82]
+            Identifier(book) [78-82]
+          StringLiteral [85-94]
+            StringLiteralComponent("Ulysses") [85-94]
diff --git a/parser/testdata/spanner/mathematical_functions.test b/parser/testdata/spanner/mathematical_functions.test
new file mode 100644
index 0000000..986c158
--- /dev/null
+++ b/parser/testdata/spanner/mathematical_functions.test
@@ -0,0 +1,788 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#abs
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+ABS(X)
+--
+FunctionCall [0-6]
+  PathExpression [0-3]
+    Identifier(ABS) [0-3]
+  PathExpression [4-5]
+    Identifier(X) [4-5]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#acos
+[mode=expression]
+ACOS(X)
+--
+FunctionCall [0-7]
+  PathExpression [0-4]
+    Identifier(ACOS) [0-4]
+  PathExpression [5-6]
+    Identifier(X) [5-6]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#acosh
+[mode=expression]
+ACOSH(X)
+--
+FunctionCall [0-8]
+  PathExpression [0-5]
+    Identifier(ACOSH) [0-5]
+  PathExpression [6-7]
+    Identifier(X) [6-7]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#approx_cosine_distance
+[mode=expression]
+APPROX_COSINE_DISTANCE(vector1, vector2, options=>value)
+--
+FunctionCall [0-56]
+  PathExpression [0-22]
+    Identifier(APPROX_COSINE_DISTANCE) [0-22]
+  PathExpression [23-30]
+    Identifier(vector1) [23-30]
+  PathExpression [32-39]
+    Identifier(vector2) [32-39]
+  NamedArgument [41-55]
+    Identifier(options) [41-48]
+    PathExpression [50-55]
+      Identifier(value) [50-55]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#approx_cosine_distance
+[mode=expression]
+\[10.0, 55.0, 40.0, 34.0]
+--
+ArrayConstructor [0-24]
+  FloatLiteral(10.0) [1-5]
+  FloatLiteral(55.0) [7-11]
+  FloatLiteral(40.0) [13-17]
+  FloatLiteral(34.0) [19-23]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#approx_cosine_distance
+SELECT FirstName, LastName
+FROM Singers@{FORCE_INDEX=Singer_vector_index} AS s
+ORDER BY APPROX_COSINE_DISTANCE(@queryVector, s.embedding, options=>JSON'{"num_leaves_to_search": 1000}')
+LIMIT 2
+--
+QueryStatement [0-192]
+  Query [0-192]
+    Select [0-78]
+      SelectList [7-26]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(FirstName) [7-16]
+        SelectColumn [18-26]
+          PathExpression [18-26]
+            Identifier(LastName) [18-26]
+      FromClause [27-78]
+        TablePathExpression [32-78]
+          PathExpression [32-39]
+            Identifier(Singers) [32-39]
+          Hint [39-73]
+            HintEntry [41-72]
+              Identifier(FORCE_INDEX) [41-52]
+              PathExpression [53-72]
+                Identifier(Singer_vector_index) [53-72]
+          Alias [74-78]
+            Identifier(s) [77-78]
+    OrderBy [79-184]
+      OrderingExpression(ASC) [88-184]
+        FunctionCall [88-184]
+          PathExpression [88-110]
+            Identifier(APPROX_COSINE_DISTANCE) [88-110]
+          ParameterExpr [111-123]
+            Identifier(queryVector) [112-123]
+          PathExpression [125-136]
+            Identifier(s) [125-126]
+            Identifier(embedding) [127-136]
+          NamedArgument [138-183]
+            Identifier(options) [138-145]
+            JSONLiteral [147-183]
+              StringLiteral [151-183]
+                StringLiteralComponent('{"num_leaves_to_search": 1000}') [151-183]
+    LimitOffset [185-192]
+      Limit [185-192]
+        IntLiteral(2) [191-192]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#approx_dot_product
+[mode=expression]
+APPROX_DOT_PRODUCT(vector1, vector2, options=>value)
+--
+FunctionCall [0-52]
+  PathExpression [0-18]
+    Identifier(APPROX_DOT_PRODUCT) [0-18]
+  PathExpression [19-26]
+    Identifier(vector1) [19-26]
+  PathExpression [28-35]
+    Identifier(vector2) [28-35]
+  NamedArgument [37-51]
+    Identifier(options) [37-44]
+    PathExpression [46-51]
+      Identifier(value) [46-51]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#approx_dot_product
+SELECT FirstName, LastName
+FROM Singers@{FORCE_INDEX=Singer_vector_index} AS s
+ORDER BY APPROX_DOT_PRODUCT(@queryVector, s.embedding, options=>JSON'{"num_leaves_to_search": 1000}') DESC
+LIMIT 2
+--
+QueryStatement [0-193]
+  Query [0-193]
+    Select [0-78]
+      SelectList [7-26]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(FirstName) [7-16]
+        SelectColumn [18-26]
+          PathExpression [18-26]
+            Identifier(LastName) [18-26]
+      FromClause [27-78]
+        TablePathExpression [32-78]
+          PathExpression [32-39]
+            Identifier(Singers) [32-39]
+          Hint [39-73]
+            HintEntry [41-72]
+              Identifier(FORCE_INDEX) [41-52]
+              PathExpression [53-72]
+                Identifier(Singer_vector_index) [53-72]
+          Alias [74-78]
+            Identifier(s) [77-78]
+    OrderBy [79-185]
+      OrderingExpression(DESC) [88-185]
+        FunctionCall [88-180]
+          PathExpression [88-106]
+            Identifier(APPROX_DOT_PRODUCT) [88-106]
+          ParameterExpr [107-119]
+            Identifier(queryVector) [108-119]
+          PathExpression [121-132]
+            Identifier(s) [121-122]
+            Identifier(embedding) [123-132]
+          NamedArgument [134-179]
+            Identifier(options) [134-141]
+            JSONLiteral [143-179]
+              StringLiteral [147-179]
+                StringLiteralComponent('{"num_leaves_to_search": 1000}') [147-179]
+    LimitOffset [186-193]
+      Limit [186-193]
+        IntLiteral(2) [192-193]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#approx_euclidean_distance
+[mode=expression]
+APPROX_EUCLIDEAN_DISTANCE(vector1, vector2, options=>value)
+--
+FunctionCall [0-59]
+  PathExpression [0-25]
+    Identifier(APPROX_EUCLIDEAN_DISTANCE) [0-25]
+  PathExpression [26-33]
+    Identifier(vector1) [26-33]
+  PathExpression [35-42]
+    Identifier(vector2) [35-42]
+  NamedArgument [44-58]
+    Identifier(options) [44-51]
+    PathExpression [53-58]
+      Identifier(value) [53-58]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#asin
+[mode=expression]
+ASIN(X)
+--
+FunctionCall [0-7]
+  PathExpression [0-4]
+    Identifier(ASIN) [0-4]
+  PathExpression [5-6]
+    Identifier(X) [5-6]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#asinh
+[mode=expression]
+ASINH(X)
+--
+FunctionCall [0-8]
+  PathExpression [0-5]
+    Identifier(ASINH) [0-5]
+  PathExpression [6-7]
+    Identifier(X) [6-7]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#atan
+[mode=expression]
+ATAN(X)
+--
+FunctionCall [0-7]
+  PathExpression [0-4]
+    Identifier(ATAN) [0-4]
+  PathExpression [5-6]
+    Identifier(X) [5-6]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#atan2
+[mode=expression]
+ATAN2(X, Y)
+--
+FunctionCall [0-11]
+  PathExpression [0-5]
+    Identifier(ATAN2) [0-5]
+  PathExpression [6-7]
+    Identifier(X) [6-7]
+  PathExpression [9-10]
+    Identifier(Y) [9-10]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#atanh
+[mode=expression]
+ATANH(X)
+--
+FunctionCall [0-8]
+  PathExpression [0-5]
+    Identifier(ATANH) [0-5]
+  PathExpression [6-7]
+    Identifier(X) [6-7]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#ceil
+[mode=expression]
+CEIL(X)
+--
+FunctionCall [0-7]
+  PathExpression [0-4]
+    Identifier(CEIL) [0-4]
+  PathExpression [5-6]
+    Identifier(X) [5-6]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#ceiling
+[mode=expression]
+CEILING(X)
+--
+FunctionCall [0-10]
+  PathExpression [0-7]
+    Identifier(CEILING) [0-7]
+  PathExpression [8-9]
+    Identifier(X) [8-9]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#cos
+[mode=expression]
+COS(X)
+--
+FunctionCall [0-6]
+  PathExpression [0-3]
+    Identifier(COS) [0-3]
+  PathExpression [4-5]
+    Identifier(X) [4-5]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#cosh
+[mode=expression]
+COSH(X)
+--
+FunctionCall [0-7]
+  PathExpression [0-4]
+    Identifier(COSH) [0-4]
+  PathExpression [5-6]
+    Identifier(X) [5-6]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#cosine_distance
+[mode=expression]
+COSINE_DISTANCE(vector1, vector2)
+--
+FunctionCall [0-33]
+  PathExpression [0-15]
+    Identifier(COSINE_DISTANCE) [0-15]
+  PathExpression [16-23]
+    Identifier(vector1) [16-23]
+  PathExpression [25-32]
+    Identifier(vector2) [25-32]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#cosine_distance
+SELECT COSINE_DISTANCE([1.0, 2.0], [3.0, 4.0]) AS results
+--
+QueryStatement [0-57]
+  Query [0-57]
+    Select [0-57]
+      SelectList [7-57]
+        SelectColumn [7-57]
+          FunctionCall [7-46]
+            PathExpression [7-22]
+              Identifier(COSINE_DISTANCE) [7-22]
+            ArrayConstructor [23-33]
+              FloatLiteral(1.0) [24-27]
+              FloatLiteral(2.0) [29-32]
+            ArrayConstructor [35-45]
+              FloatLiteral(3.0) [36-39]
+              FloatLiteral(4.0) [41-44]
+          Alias [47-57]
+            Identifier(results) [50-57]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#cosine_distance
+SELECT COSINE_DISTANCE([2.0, 1.0], [4.0, 3.0]) AS results
+--
+QueryStatement [0-57]
+  Query [0-57]
+    Select [0-57]
+      SelectList [7-57]
+        SelectColumn [7-57]
+          FunctionCall [7-46]
+            PathExpression [7-22]
+              Identifier(COSINE_DISTANCE) [7-22]
+            ArrayConstructor [23-33]
+              FloatLiteral(2.0) [24-27]
+              FloatLiteral(1.0) [29-32]
+            ArrayConstructor [35-45]
+              FloatLiteral(4.0) [36-39]
+              FloatLiteral(3.0) [41-44]
+          Alias [47-57]
+            Identifier(results) [50-57]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#cosine_distance
+-- ERROR
+SELECT COSINE_DISTANCE([0.0, 0.0], [3.0, 4.0]) AS results
+--
+QueryStatement [9-66]
+  Query [9-66]
+    Select [9-66]
+      SelectList [16-66]
+        SelectColumn [16-66]
+          FunctionCall [16-55]
+            PathExpression [16-31]
+              Identifier(COSINE_DISTANCE) [16-31]
+            ArrayConstructor [32-42]
+              FloatLiteral(0.0) [33-36]
+              FloatLiteral(0.0) [38-41]
+            ArrayConstructor [44-54]
+              FloatLiteral(3.0) [45-48]
+              FloatLiteral(4.0) [50-53]
+          Alias [56-66]
+            Identifier(results) [59-66]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#cosine_distance
+-- ERROR
+SELECT COSINE_DISTANCE([9.0, 7.0], [8.0, 4.0, 5.0]) AS results
+--
+QueryStatement [9-71]
+  Query [9-71]
+    Select [9-71]
+      SelectList [16-71]
+        SelectColumn [16-71]
+          FunctionCall [16-60]
+            PathExpression [16-31]
+              Identifier(COSINE_DISTANCE) [16-31]
+            ArrayConstructor [32-42]
+              FloatLiteral(9.0) [33-36]
+              FloatLiteral(7.0) [38-41]
+            ArrayConstructor [44-59]
+              FloatLiteral(8.0) [45-48]
+              FloatLiteral(4.0) [50-53]
+              FloatLiteral(5.0) [55-58]
+          Alias [61-71]
+            Identifier(results) [64-71]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#div
+[mode=expression]
+DIV(X, Y)
+--
+FunctionCall [0-9]
+  PathExpression [0-3]
+    Identifier(DIV) [0-3]
+  PathExpression [4-5]
+    Identifier(X) [4-5]
+  PathExpression [7-8]
+    Identifier(Y) [7-8]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#dot_product
+[mode=expression]
+DOT_PRODUCT(vector1, vector2)
+--
+FunctionCall [0-29]
+  PathExpression [0-11]
+    Identifier(DOT_PRODUCT) [0-11]
+  PathExpression [12-19]
+    Identifier(vector1) [12-19]
+  PathExpression [21-28]
+    Identifier(vector2) [21-28]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#dot_product
+SELECT DOT_PRODUCT([100], [200]) AS results
+--
+QueryStatement [0-43]
+  Query [0-43]
+    Select [0-43]
+      SelectList [7-43]
+        SelectColumn [7-43]
+          FunctionCall [7-32]
+            PathExpression [7-18]
+              Identifier(DOT_PRODUCT) [7-18]
+            ArrayConstructor [19-24]
+              IntLiteral(100) [20-23]
+            ArrayConstructor [26-31]
+              IntLiteral(200) [27-30]
+          Alias [33-43]
+            Identifier(results) [36-43]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#dot_product
+SELECT DOT_PRODUCT([100, 10], [200, 6]) AS results
+--
+QueryStatement [0-50]
+  Query [0-50]
+    Select [0-50]
+      SelectList [7-50]
+        SelectColumn [7-50]
+          FunctionCall [7-39]
+            PathExpression [7-18]
+              Identifier(DOT_PRODUCT) [7-18]
+            ArrayConstructor [19-28]
+              IntLiteral(100) [20-23]
+              IntLiteral(10) [25-27]
+            ArrayConstructor [30-38]
+              IntLiteral(200) [31-34]
+              IntLiteral(6) [36-37]
+          Alias [40-50]
+            Identifier(results) [43-50]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#dot_product
+SELECT DOT_PRODUCT([100, 10, 1], [200, 6, 2]) AS results
+--
+QueryStatement [0-56]
+  Query [0-56]
+    Select [0-56]
+      SelectList [7-56]
+        SelectColumn [7-56]
+          FunctionCall [7-45]
+            PathExpression [7-18]
+              Identifier(DOT_PRODUCT) [7-18]
+            ArrayConstructor [19-31]
+              IntLiteral(100) [20-23]
+              IntLiteral(10) [25-27]
+              IntLiteral(1) [29-30]
+            ArrayConstructor [33-44]
+              IntLiteral(200) [34-37]
+              IntLiteral(6) [39-40]
+              IntLiteral(2) [42-43]
+          Alias [46-56]
+            Identifier(results) [49-56]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#dot_product
+SELECT DOT_PRODUCT([], []) AS results
+--
+QueryStatement [0-37]
+  Query [0-37]
+    Select [0-37]
+      SelectList [7-37]
+        SelectColumn [7-37]
+          FunctionCall [7-26]
+            PathExpression [7-18]
+              Identifier(DOT_PRODUCT) [7-18]
+            ArrayConstructor [19-21]
+            ArrayConstructor [23-25]
+          Alias [27-37]
+            Identifier(results) [30-37]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#exp
+[mode=expression]
+EXP(X)
+--
+FunctionCall [0-6]
+  PathExpression [0-3]
+    Identifier(EXP) [0-3]
+  PathExpression [4-5]
+    Identifier(X) [4-5]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#euclidean_distance
+[mode=expression]
+EUCLIDEAN_DISTANCE(vector1, vector2)
+--
+FunctionCall [0-36]
+  PathExpression [0-18]
+    Identifier(EUCLIDEAN_DISTANCE) [0-18]
+  PathExpression [19-26]
+    Identifier(vector1) [19-26]
+  PathExpression [28-35]
+    Identifier(vector2) [28-35]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#euclidean_distance
+SELECT EUCLIDEAN_DISTANCE([1.0, 2.0], [3.0, 4.0]) AS results
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-60]
+      SelectList [7-60]
+        SelectColumn [7-60]
+          FunctionCall [7-49]
+            PathExpression [7-25]
+              Identifier(EUCLIDEAN_DISTANCE) [7-25]
+            ArrayConstructor [26-36]
+              FloatLiteral(1.0) [27-30]
+              FloatLiteral(2.0) [32-35]
+            ArrayConstructor [38-48]
+              FloatLiteral(3.0) [39-42]
+              FloatLiteral(4.0) [44-47]
+          Alias [50-60]
+            Identifier(results) [53-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#euclidean_distance
+SELECT EUCLIDEAN_DISTANCE([1.0, 2.0], [3.0, 4.0])
+--
+QueryStatement [0-49]
+  Query [0-49]
+    Select [0-49]
+      SelectList [7-49]
+        SelectColumn [7-49]
+          FunctionCall [7-49]
+            PathExpression [7-25]
+              Identifier(EUCLIDEAN_DISTANCE) [7-25]
+            ArrayConstructor [26-36]
+              FloatLiteral(1.0) [27-30]
+              FloatLiteral(2.0) [32-35]
+            ArrayConstructor [38-48]
+              FloatLiteral(3.0) [39-42]
+              FloatLiteral(4.0) [44-47]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#euclidean_distance
+SELECT EUCLIDEAN_DISTANCE([2.0, 1.0], [4.0, 3.0])
+--
+QueryStatement [0-49]
+  Query [0-49]
+    Select [0-49]
+      SelectList [7-49]
+        SelectColumn [7-49]
+          FunctionCall [7-49]
+            PathExpression [7-25]
+              Identifier(EUCLIDEAN_DISTANCE) [7-25]
+            ArrayConstructor [26-36]
+              FloatLiteral(2.0) [27-30]
+              FloatLiteral(1.0) [32-35]
+            ArrayConstructor [38-48]
+              FloatLiteral(4.0) [39-42]
+              FloatLiteral(3.0) [44-47]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#euclidean_distance
+-- ERROR
+SELECT EUCLIDEAN_DISTANCE([9.0, 7.0], [8.0, 4.0, 5.0]) AS results
+--
+QueryStatement [9-74]
+  Query [9-74]
+    Select [9-74]
+      SelectList [16-74]
+        SelectColumn [16-74]
+          FunctionCall [16-63]
+            PathExpression [16-34]
+              Identifier(EUCLIDEAN_DISTANCE) [16-34]
+            ArrayConstructor [35-45]
+              FloatLiteral(9.0) [36-39]
+              FloatLiteral(7.0) [41-44]
+            ArrayConstructor [47-62]
+              FloatLiteral(8.0) [48-51]
+              FloatLiteral(4.0) [53-56]
+              FloatLiteral(5.0) [58-61]
+          Alias [64-74]
+            Identifier(results) [67-74]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#floor
+[mode=expression]
+FLOOR(X)
+--
+FunctionCall [0-8]
+  PathExpression [0-5]
+    Identifier(FLOOR) [0-5]
+  PathExpression [6-7]
+    Identifier(X) [6-7]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#ieee_divide
+[mode=expression]
+IEEE_DIVIDE(X, Y)
+--
+FunctionCall [0-17]
+  PathExpression [0-11]
+    Identifier(IEEE_DIVIDE) [0-11]
+  PathExpression [12-13]
+    Identifier(X) [12-13]
+  PathExpression [15-16]
+    Identifier(Y) [15-16]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#is_inf
+[mode=expression]
+IS_INF(X)
+--
+FunctionCall [0-9]
+  PathExpression [0-6]
+    Identifier(IS_INF) [0-6]
+  PathExpression [7-8]
+    Identifier(X) [7-8]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#is_nan
+[mode=expression]
+IS_NAN(X)
+--
+FunctionCall [0-9]
+  PathExpression [0-6]
+    Identifier(IS_NAN) [0-6]
+  PathExpression [7-8]
+    Identifier(X) [7-8]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#ln
+[mode=expression]
+LN(X)
+--
+FunctionCall [0-5]
+  PathExpression [0-2]
+    Identifier(LN) [0-2]
+  PathExpression [3-4]
+    Identifier(X) [3-4]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#log10
+[mode=expression]
+LOG10(X)
+--
+FunctionCall [0-8]
+  PathExpression [0-5]
+    Identifier(LOG10) [0-5]
+  PathExpression [6-7]
+    Identifier(X) [6-7]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#mod
+[mode=expression]
+MOD(X, Y)
+--
+FunctionCall [0-9]
+  PathExpression [0-3]
+    Identifier(MOD) [0-3]
+  PathExpression [4-5]
+    Identifier(X) [4-5]
+  PathExpression [7-8]
+    Identifier(Y) [7-8]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#pow
+[mode=expression]
+POW(X, Y)
+--
+FunctionCall [0-9]
+  PathExpression [0-3]
+    Identifier(POW) [0-3]
+  PathExpression [4-5]
+    Identifier(X) [4-5]
+  PathExpression [7-8]
+    Identifier(Y) [7-8]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#power
+[mode=expression]
+POWER(X, Y)
+--
+FunctionCall [0-11]
+  PathExpression [0-5]
+    Identifier(POWER) [0-5]
+  PathExpression [6-7]
+    Identifier(X) [6-7]
+  PathExpression [9-10]
+    Identifier(Y) [9-10]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#safe_add
+[mode=expression]
+SAFE_ADD(X, Y)
+--
+FunctionCall [0-14]
+  PathExpression [0-8]
+    Identifier(SAFE_ADD) [0-8]
+  PathExpression [9-10]
+    Identifier(X) [9-10]
+  PathExpression [12-13]
+    Identifier(Y) [12-13]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#safe_divide
+[mode=expression]
+SAFE_DIVIDE(X, Y)
+--
+FunctionCall [0-17]
+  PathExpression [0-11]
+    Identifier(SAFE_DIVIDE) [0-11]
+  PathExpression [12-13]
+    Identifier(X) [12-13]
+  PathExpression [15-16]
+    Identifier(Y) [15-16]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#safe_multiply
+[mode=expression]
+SAFE_MULTIPLY(X, Y)
+--
+FunctionCall [0-19]
+  PathExpression [0-13]
+    Identifier(SAFE_MULTIPLY) [0-13]
+  PathExpression [14-15]
+    Identifier(X) [14-15]
+  PathExpression [17-18]
+    Identifier(Y) [17-18]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#safe_negate
+[mode=expression]
+SAFE_NEGATE(X)
+--
+FunctionCall [0-14]
+  PathExpression [0-11]
+    Identifier(SAFE_NEGATE) [0-11]
+  PathExpression [12-13]
+    Identifier(X) [12-13]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#safe_subtract
+[mode=expression]
+SAFE_SUBTRACT(X, Y)
+--
+FunctionCall [0-19]
+  PathExpression [0-13]
+    Identifier(SAFE_SUBTRACT) [0-13]
+  PathExpression [14-15]
+    Identifier(X) [14-15]
+  PathExpression [17-18]
+    Identifier(Y) [17-18]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#sign
+[mode=expression]
+SIGN(X)
+--
+FunctionCall [0-7]
+  PathExpression [0-4]
+    Identifier(SIGN) [0-4]
+  PathExpression [5-6]
+    Identifier(X) [5-6]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#sin
+[mode=expression]
+SIN(X)
+--
+FunctionCall [0-6]
+  PathExpression [0-3]
+    Identifier(SIN) [0-3]
+  PathExpression [4-5]
+    Identifier(X) [4-5]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#sinh
+[mode=expression]
+SINH(X)
+--
+FunctionCall [0-7]
+  PathExpression [0-4]
+    Identifier(SINH) [0-4]
+  PathExpression [5-6]
+    Identifier(X) [5-6]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#sqrt
+[mode=expression]
+SQRT(X)
+--
+FunctionCall [0-7]
+  PathExpression [0-4]
+    Identifier(SQRT) [0-4]
+  PathExpression [5-6]
+    Identifier(X) [5-6]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#tan
+[mode=expression]
+TAN(X)
+--
+FunctionCall [0-6]
+  PathExpression [0-3]
+    Identifier(TAN) [0-3]
+  PathExpression [4-5]
+    Identifier(X) [4-5]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/mathematical_functions#tanh
+[mode=expression]
+TANH(X)
+--
+FunctionCall [0-7]
+  PathExpression [0-4]
+    Identifier(TANH) [0-4]
+  PathExpression [5-6]
+    Identifier(X) [5-6]
diff --git a/parser/testdata/spanner/ml-functions.test b/parser/testdata/spanner/ml-functions.test
new file mode 100644
index 0000000..c9b1735
--- /dev/null
+++ b/parser/testdata/spanner/ml-functions.test
@@ -0,0 +1,756 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/ml-functions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/ml-functions#aiclassify
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+AI.CLASSIFY(prompt, categories)
+--
+FunctionCall [0-31]
+  PathExpression [0-11]
+    Identifier(AI) [0-2]
+    Identifier(CLASSIFY) [3-11]
+  PathExpression [12-18]
+    Identifier(prompt) [12-18]
+  PathExpression [20-30]
+    Identifier(categories) [20-30]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/ml-functions#aiclassify
+[mode=expression]
+\[('green', 'positive'), ('yellow', 'neutral'), ('red', 'negative')]
+--
+ArrayConstructor [0-67]
+  StructConstructorWithParens [1-22]
+    StringLiteral [2-9]
+      StringLiteralComponent('green') [2-9]
+    StringLiteral [11-21]
+      StringLiteralComponent('positive') [11-21]
+  StructConstructorWithParens [24-45]
+    StringLiteral [25-33]
+      StringLiteralComponent('yellow') [25-33]
+    StringLiteral [35-44]
+      StringLiteralComponent('neutral') [35-44]
+  StructConstructorWithParens [47-66]
+    StringLiteral [48-53]
+      StringLiteralComponent('red') [48-53]
+    StringLiteral [55-65]
+      StringLiteralComponent('negative') [55-65]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/ml-functions#aiclassify
+[mode=expression]
+\[STRUCT('green' AS label, 'positive' AS description),
+STRUCT('yellow' AS label, 'neutral' AS description),
+STRUCT('red' AS label, 'negative' AS description)]
+--
+ArrayConstructor [0-157]
+  StructConstructorWithKeyword [1-52]
+    StructConstructorArg [8-24]
+      StringLiteral [8-15]
+        StringLiteralComponent('green') [8-15]
+      Alias [16-24]
+        Identifier(label) [19-24]
+    StructConstructorArg [26-51]
+      StringLiteral [26-36]
+        StringLiteralComponent('positive') [26-36]
+      Alias [37-51]
+        Identifier(description) [40-51]
+  StructConstructorWithKeyword [54-105]
+    StructConstructorArg [61-78]
+      StringLiteral [61-69]
+        StringLiteralComponent('yellow') [61-69]
+      Alias [70-78]
+        Identifier(label) [73-78]
+    StructConstructorArg [80-104]
+      StringLiteral [80-89]
+        StringLiteralComponent('neutral') [80-89]
+      Alias [90-104]
+        Identifier(description) [93-104]
+  StructConstructorWithKeyword [107-156]
+    StructConstructorArg [114-128]
+      StringLiteral [114-119]
+        StringLiteralComponent('red') [114-119]
+      Alias [120-128]
+        Identifier(label) [123-128]
+    StructConstructorArg [130-155]
+      StringLiteral [130-140]
+        StringLiteralComponent('negative') [130-140]
+      Alias [141-155]
+        Identifier(description) [144-155]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/ml-functions#aiclassify
+-- Classify text by topic
+SELECT
+  title,
+  body,
+  AI.CLASSIFY(
+    body,
+\    ['tech', 'sport', 'business', 'politics', 'entertainment', 'other']) AS category
+FROM
+  news
+LIMIT 100
+--
+QueryStatement [26-181]
+  Query [26-181]
+    Select [26-171]
+      SelectList [35-159]
+        SelectColumn [35-40]
+          PathExpression [35-40]
+            Identifier(title) [35-40]
+        SelectColumn [44-48]
+          PathExpression [44-48]
+            Identifier(body) [44-48]
+        SelectColumn [52-159]
+          FunctionCall [52-147]
+            PathExpression [52-63]
+              Identifier(AI) [52-54]
+              Identifier(CLASSIFY) [55-63]
+            PathExpression [69-73]
+              Identifier(body) [69-73]
+            ArrayConstructor [79-146]
+              StringLiteral [80-86]
+                StringLiteralComponent('tech') [80-86]
+              StringLiteral [88-95]
+                StringLiteralComponent('sport') [88-95]
+              StringLiteral [97-107]
+                StringLiteralComponent('business') [97-107]
+              StringLiteral [109-119]
+                StringLiteralComponent('politics') [109-119]
+              StringLiteral [121-136]
+                StringLiteralComponent('entertainment') [121-136]
+              StringLiteral [138-145]
+                StringLiteralComponent('other') [138-145]
+          Alias [148-159]
+            Identifier(category) [151-159]
+      FromClause [160-171]
+        TablePathExpression [167-171]
+          PathExpression [167-171]
+            Identifier(news) [167-171]
+    LimitOffset [172-181]
+      Limit [172-181]
+        IntLiteral(100) [178-181]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/ml-functions#aiclassify
+-- Classify reviews by sentiment
+SELECT
+  AI.CLASSIFY(
+    ('Classify the review by sentiment: ', review),
+    categories =>
+\         [('green', 'The review is positive.'),
+          ('yellow', 'The review is neutral.'),
+          ('red', 'The review is negative.')]) AS ai_review_rating,
+  reviewer_rating AS human_provided_rating,
+  review,
+FROM
+  reviews
+WHERE
+  title = 'The English Patient'
+--
+QueryStatement [33-395]
+  Query [33-395]
+    Select [33-395]
+      SelectList [42-342]
+        SelectColumn [42-287]
+          FunctionCall [42-267]
+            PathExpression [42-53]
+              Identifier(AI) [42-44]
+              Identifier(CLASSIFY) [45-53]
+            StructConstructorWithParens [59-105]
+              StringLiteral [60-96]
+                StringLiteralComponent('Classify the review by sentiment: ') [60-96]
+              PathExpression [98-104]
+                Identifier(review) [98-104]
+            NamedArgument [111-266]
+              Identifier(categories) [111-121]
+              ArrayConstructor [134-266]
+                StructConstructorWithParens [135-171]
+                  StringLiteral [136-143]
+                    StringLiteralComponent('green') [136-143]
+                  StringLiteral [145-170]
+                    StringLiteralComponent('The review is positive.') [145-170]
+                StructConstructorWithParens [183-219]
+                  StringLiteral [184-192]
+                    StringLiteralComponent('yellow') [184-192]
+                  StringLiteral [194-218]
+                    StringLiteralComponent('The review is neutral.') [194-218]
+                StructConstructorWithParens [231-265]
+                  StringLiteral [232-237]
+                    StringLiteralComponent('red') [232-237]
+                  StringLiteral [239-264]
+                    StringLiteralComponent('The review is negative.') [239-264]
+          Alias [268-287]
+            Identifier(ai_review_rating) [271-287]
+        SelectColumn [291-331]
+          PathExpression [291-306]
+            Identifier(reviewer_rating) [291-306]
+          Alias [307-331]
+            Identifier(human_provided_rating) [310-331]
+        SelectColumn [335-341]
+          PathExpression [335-341]
+            Identifier(review) [335-341]
+      FromClause [343-357]
+        TablePathExpression [350-357]
+          PathExpression [350-357]
+            Identifier(reviews) [350-357]
+      WhereClause [358-395]
+        BinaryExpression(=) [366-395]
+          PathExpression [366-371]
+            Identifier(title) [366-371]
+          StringLiteral [374-395]
+            StringLiteralComponent('The English Patient') [374-395]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/ml-functions#aiif
+[mode=expression]
+AI.IF(prompt)
+--
+FunctionCall [0-13]
+  PathExpression [0-5]
+    Identifier(AI) [0-2]
+    Identifier(`IF`) [3-5]
+  PathExpression [6-12]
+    Identifier(prompt) [6-12]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/ml-functions#aiif
+-- Filter text by topic.
+SELECT
+  title, body
+FROM
+  news
+WHERE
+  AI.IF(CONCAT(
+    'The following news story is about a natural disaster: ', body))
+--
+QueryStatement [25-148]
+  Query [25-148]
+    Select [25-148]
+      SelectList [34-45]
+        SelectColumn [34-39]
+          PathExpression [34-39]
+            Identifier(title) [34-39]
+        SelectColumn [41-45]
+          PathExpression [41-45]
+            Identifier(body) [41-45]
+      FromClause [46-57]
+        TablePathExpression [53-57]
+          PathExpression [53-57]
+            Identifier(news) [53-57]
+      WhereClause [58-148]
+        FunctionCall [66-148]
+          PathExpression [66-71]
+            Identifier(AI) [66-68]
+            Identifier(`IF`) [69-71]
+          FunctionCall [72-147]
+            PathExpression [72-78]
+              Identifier(CONCAT) [72-78]
+            StringLiteral [84-140]
+              StringLiteralComponent('The following news story is about a natural disaster: ') [84-140]
+            PathExpression [142-146]
+              Identifier(body) [142-146]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/ml-functions#aiif
+-- Combine filters.
+SELECT
+  title, body
+FROM
+   news
+WHERE
+  AI.IF(CONCAT('This news is related to Google: ', body))
+  AND category = 'tech'
+--
+QueryStatement [20-141]
+  Query [20-141]
+    Select [20-141]
+      SelectList [29-40]
+        SelectColumn [29-34]
+          PathExpression [29-34]
+            Identifier(title) [29-34]
+        SelectColumn [36-40]
+          PathExpression [36-40]
+            Identifier(body) [36-40]
+      FromClause [41-53]
+        TablePathExpression [49-53]
+          PathExpression [49-53]
+            Identifier(news) [49-53]
+      WhereClause [54-141]
+        AndExpr [62-141]
+          FunctionCall [62-117]
+            PathExpression [62-67]
+              Identifier(AI) [62-64]
+              Identifier(`IF`) [65-67]
+            FunctionCall [68-116]
+              PathExpression [68-74]
+                Identifier(CONCAT) [68-74]
+              StringLiteral [75-109]
+                StringLiteralComponent('This news is related to Google: ') [75-109]
+              PathExpression [111-115]
+                Identifier(body) [111-115]
+          BinaryExpression(=) [124-141]
+            PathExpression [124-132]
+              Identifier(category) [124-132]
+            StringLiteral [135-141]
+              StringLiteralComponent('tech') [135-141]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/ml-functions#aiif
+-- Join tables based on semantic understanding.
+SELECT
+  news.title, news.body, products.product_name
+FROM
+  news
+JOIN products
+  ON
+    AI.IF(
+      CONCAT(
+        'Determine if the following news story is about product ',
+        products.product_name,
+        ': ',
+        news.body))
+WHERE
+  category = 'tech'
+--
+QueryStatement [48-315]
+  Query [48-315]
+    Select [48-315]
+      SelectList [57-101]
+        SelectColumn [57-67]
+          PathExpression [57-67]
+            Identifier(news) [57-61]
+            Identifier(title) [62-67]
+        SelectColumn [69-78]
+          PathExpression [69-78]
+            Identifier(news) [69-73]
+            Identifier(body) [74-78]
+        SelectColumn [80-101]
+          PathExpression [80-101]
+            Identifier(products) [80-88]
+            Identifier(product_name) [89-101]
+      FromClause [102-289]
+        Join [109-289]
+          TablePathExpression [109-113]
+            PathExpression [109-113]
+              Identifier(news) [109-113]
+          Location [114-118]
+          TablePathExpression [119-127]
+            PathExpression [119-127]
+              Identifier(products) [119-127]
+          OnClause [130-289]
+            FunctionCall [137-289]
+              PathExpression [137-142]
+                Identifier(AI) [137-139]
+                Identifier(`IF`) [140-142]
+              FunctionCall [150-288]
+                PathExpression [150-156]
+                  Identifier(CONCAT) [150-156]
+                StringLiteral [166-223]
+                  StringLiteralComponent('Determine if the following news story is about product ') [166-223]
+                PathExpression [233-254]
+                  Identifier(products) [233-241]
+                  Identifier(product_name) [242-254]
+                StringLiteral [264-268]
+                  StringLiteralComponent(': ') [264-268]
+                PathExpression [278-287]
+                  Identifier(news) [278-282]
+                  Identifier(body) [283-287]
+      WhereClause [290-315]
+        BinaryExpression(=) [298-315]
+          PathExpression [298-306]
+            Identifier(category) [298-306]
+          StringLiteral [309-315]
+            StringLiteralComponent('tech') [309-315]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/ml-functions#aiscore
+[mode=expression]
+AI.SCORE(prompt)
+--
+FunctionCall [0-16]
+  PathExpression [0-8]
+    Identifier(AI) [0-2]
+    Identifier(SCORE) [3-8]
+  PathExpression [9-15]
+    Identifier(prompt) [9-15]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/ml-functions#aiscore
+-- Rate reviews
+SELECT
+  AI.SCORE(CONCAT(
+    """
+    On a scale from 1 to 10, rate how much the reviewer liked the movie.
+    Review:
+    """, review)) AS ai_rating,
+  reviewer_rating AS human_rating,
+  review
+FROM
+  reviews
+WHERE
+  title = 'The English Patient'
+ORDER BY ai_rating DESC
+LIMIT 10
+--
+QueryStatement [16-296]
+  Query [16-296]
+    Select [16-263]
+      SelectList [25-210]
+        SelectColumn [25-165]
+          FunctionCall [25-152]
+            PathExpression [25-33]
+              Identifier(AI) [25-27]
+              Identifier(SCORE) [28-33]
+            FunctionCall [34-151]
+              PathExpression [34-40]
+                Identifier(CONCAT) [34-40]
+              StringLiteral [46-142]
+                StringLiteralComponent("""
+    On a scale from 1 to 10, rate how much the reviewer liked the movie.
+    Review:
+    """) [46-142]
+              PathExpression [144-150]
+                Identifier(review) [144-150]
+          Alias [153-165]
+            Identifier(ai_rating) [156-165]
+        SelectColumn [169-200]
+          PathExpression [169-184]
+            Identifier(reviewer_rating) [169-184]
+          Alias [185-200]
+            Identifier(human_rating) [188-200]
+        SelectColumn [204-210]
+          PathExpression [204-210]
+            Identifier(review) [204-210]
+      FromClause [211-225]
+        TablePathExpression [218-225]
+          PathExpression [218-225]
+            Identifier(reviews) [218-225]
+      WhereClause [226-263]
+        BinaryExpression(=) [234-263]
+          PathExpression [234-239]
+            Identifier(title) [234-239]
+          StringLiteral [242-263]
+            StringLiteralComponent('The English Patient') [242-263]
+    OrderBy [264-287]
+      OrderingExpression(DESC) [273-287]
+        PathExpression [273-282]
+          Identifier(ai_rating) [273-282]
+    LimitOffset [288-296]
+      Limit [288-296]
+        IntLiteral(10) [294-296]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/ml-functions#aiscore
+-- Rate and filter reviews
+SELECT
+  AI.SCORE(
+    CONCAT(
+      'On a scale from 1 to 10, rate how much the reviewer liked the movie. Review:',
+      review)) AS ai_rating,
+  reviewer_rating AS human_rating,
+  review
+FROM
+  reviews
+WHERE
+  title = 'The English Patient'
+  AND AI.IF(
+    CONCAT("This review mentions at least one of the film's main characters: ", review))
+ORDER BY ai_rating DESC
+LIMIT 10
+--
+QueryStatement [27-404]
+  Query [27-404]
+    Select [27-371]
+      SelectList [36-216]
+        SelectColumn [36-171]
+          FunctionCall [36-158]
+            PathExpression [36-44]
+              Identifier(AI) [36-38]
+              Identifier(SCORE) [39-44]
+            FunctionCall [50-157]
+              PathExpression [50-56]
+                Identifier(CONCAT) [50-56]
+              StringLiteral [64-142]
+                StringLiteralComponent('On a scale from 1 to 10, rate how much the reviewer liked the movie. Review:') [64-142]
+              PathExpression [150-156]
+                Identifier(review) [150-156]
+          Alias [159-171]
+            Identifier(ai_rating) [162-171]
+        SelectColumn [175-206]
+          PathExpression [175-190]
+            Identifier(reviewer_rating) [175-190]
+          Alias [191-206]
+            Identifier(human_rating) [194-206]
+        SelectColumn [210-216]
+          PathExpression [210-216]
+            Identifier(review) [210-216]
+      FromClause [217-231]
+        TablePathExpression [224-231]
+          PathExpression [224-231]
+            Identifier(reviews) [224-231]
+      WhereClause [232-371]
+        AndExpr [240-371]
+          BinaryExpression(=) [240-269]
+            PathExpression [240-245]
+              Identifier(title) [240-245]
+            StringLiteral [248-269]
+              StringLiteralComponent('The English Patient') [248-269]
+          FunctionCall [276-371]
+            PathExpression [276-281]
+              Identifier(AI) [276-278]
+              Identifier(`IF`) [279-281]
+            FunctionCall [287-370]
+              PathExpression [287-293]
+                Identifier(CONCAT) [287-293]
+              StringLiteral [294-361]
+                StringLiteralComponent("This review mentions at least one of the film's main characters: ") [294-361]
+              PathExpression [363-369]
+                Identifier(review) [363-369]
+    OrderBy [372-395]
+      OrderingExpression(DESC) [381-395]
+        PathExpression [381-390]
+          Identifier(ai_rating) [381-390]
+    LimitOffset [396-404]
+      Limit [396-404]
+        IntLiteral(10) [402-404]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/ml-functions#mlpredict
+SELECT id, color, value
+FROM ML.PREDICT(MODEL DiamondAppraise, TABLE Diamonds)
+--
+QueryStatement [0-78]
+  Query [0-78]
+    Select [0-78]
+      SelectList [7-23]
+        SelectColumn [7-9]
+          PathExpression [7-9]
+            Identifier(id) [7-9]
+        SelectColumn [11-16]
+          PathExpression [11-16]
+            Identifier(color) [11-16]
+        SelectColumn [18-23]
+          PathExpression [18-23]
+            Identifier(value) [18-23]
+      FromClause [24-78]
+        TVF [29-78]
+          PathExpression [29-39]
+            Identifier(ML) [29-31]
+            Identifier(PREDICT) [32-39]
+          TVFArgument [40-61]
+            ModelClause [40-61]
+              PathExpression [46-61]
+                Identifier(DiamondAppraise) [46-61]
+          TVFArgument [63-77]
+            TableClause [63-77]
+              PathExpression [69-77]
+                Identifier(Diamonds) [69-77]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/ml-functions#mlpredict
+SELECT prompt, output
+FROM ML.PREDICT(
+  MODEL TextBison,
+  (SELECT "Is 13 prime?" as prompt), STRUCT(10 AS maxOutputTokens))
+--
+QueryStatement [0-125]
+  Query [0-125]
+    Select [0-125]
+      SelectList [7-21]
+        SelectColumn [7-13]
+          PathExpression [7-13]
+            Identifier(prompt) [7-13]
+        SelectColumn [15-21]
+          PathExpression [15-21]
+            Identifier(output) [15-21]
+      FromClause [22-125]
+        TVF [27-125]
+          PathExpression [27-37]
+            Identifier(ML) [27-29]
+            Identifier(PREDICT) [30-37]
+          TVFArgument [41-56]
+            ModelClause [41-56]
+              PathExpression [47-56]
+                Identifier(TextBison) [47-56]
+          TVFArgument [60-93]
+            ExpressionSubquery [60-93]
+              Query [61-92]
+                Select [61-92]
+                  SelectList [68-92]
+                    SelectColumn [68-92]
+                      StringLiteral [68-82]
+                        StringLiteralComponent("Is 13 prime?") [68-82]
+                      Alias [83-92]
+                        Identifier(prompt) [86-92]
+          TVFArgument [95-124]
+            StructConstructorWithKeyword [95-124]
+              StructConstructorArg [102-123]
+                IntLiteral(10) [102-104]
+                Alias [105-123]
+                  Identifier(maxOutputTokens) [108-123]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/ml-functions#mlpredict
+-- Generate the embedding from a natural language prompt
+WITH embedding AS (
+  SELECT embeddings.values
+  FROM ML.PREDICT(
+    MODEL DiamondAppraise,
+      (SELECT "What is the most valuable diamond?" as prompt)
+  )
+)
+-- Use embedding to find the most similar entries in the database
+SELECT id, color, value,
+  (APPROX_COSINE_DISTANCE(valueEmbedding,
+  embedding.values,
+  options => JSON '{"num_leaves_to_search": 10}')) as distance
+FROM products @{force_index=valueEmbeddingIndex}, embedding
+WHERE valueEmbedding IS NOT NULL
+ORDER BY distance
+LIMIT 5
+--
+QueryStatement [57-552]
+  Query [57-552]
+    WithClause [57-217]
+      WithClauseEntry [62-217]
+        AliasedQuery [62-217]
+          Identifier(embedding) [62-71]
+          Query [75-217]
+            Select [79-215]
+              SelectList [86-103]
+                SelectColumn [86-103]
+                  PathExpression [86-103]
+                    Identifier(embeddings) [86-96]
+                    Identifier(values) [97-103]
+              FromClause [106-215]
+                TVF [111-215]
+                  PathExpression [111-121]
+                    Identifier(ML) [111-113]
+                    Identifier(PREDICT) [114-121]
+                  TVFArgument [127-148]
+                    ModelClause [127-148]
+                      PathExpression [133-148]
+                        Identifier(DiamondAppraise) [133-148]
+                  TVFArgument [156-211]
+                    ExpressionSubquery [156-211]
+                      Query [157-210]
+                        Select [157-210]
+                          SelectList [164-210]
+                            SelectColumn [164-210]
+                              StringLiteral [164-200]
+                                StringLiteralComponent("What is the most valuable diamond?") [164-200]
+                              Alias [201-210]
+                                Identifier(prompt) [204-210]
+    Select [284-526]
+      SelectList [291-433]
+        SelectColumn [291-293]
+          PathExpression [291-293]
+            Identifier(id) [291-293]
+        SelectColumn [295-300]
+          PathExpression [295-300]
+            Identifier(color) [295-300]
+        SelectColumn [302-307]
+          PathExpression [302-307]
+            Identifier(value) [302-307]
+        SelectColumn [311-433]
+          FunctionCall [312-420]
+            PathExpression [312-334]
+              Identifier(APPROX_COSINE_DISTANCE) [312-334]
+            PathExpression [335-349]
+              Identifier(valueEmbedding) [335-349]
+            PathExpression [353-369]
+              Identifier(embedding) [353-362]
+              Identifier(values) [363-369]
+            NamedArgument [373-419]
+              Identifier(options) [373-380]
+              JSONLiteral [384-419]
+                StringLiteral [389-419]
+                  StringLiteralComponent('{"num_leaves_to_search": 10}') [389-419]
+          Alias [422-433]
+            Identifier(distance) [425-433]
+      FromClause [434-493]
+        Join(COMMA) [439-493]
+          TablePathExpression [439-482]
+            PathExpression [439-447]
+              Identifier(products) [439-447]
+            Hint [448-482]
+              HintEntry [450-481]
+                Identifier(force_index) [450-461]
+                PathExpression [462-481]
+                  Identifier(valueEmbeddingIndex) [462-481]
+          Location [482-483]
+          TablePathExpression [484-493]
+            PathExpression [484-493]
+              Identifier(embedding) [484-493]
+      WhereClause [494-526]
+        BinaryExpression(IS NOT) [500-526]
+          PathExpression [500-514]
+            Identifier(valueEmbedding) [500-514]
+          NullLiteral(NULL) [522-526]
+    OrderBy [527-544]
+      OrderingExpression(ASC) [536-544]
+        PathExpression [536-544]
+          Identifier(distance) [536-544]
+    LimitOffset [545-552]
+      Limit [545-552]
+        IntLiteral(5) [551-552]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/ml-functions#mlpredict
+INSERT INTO AppraisedDiamond (id, color, carat, value)
+SELECT
+  1 AS id,
+  color,
+  carat,
+  value
+FROM
+  ML.PREDICT(MODEL DiamondAppraise,
+  (
+    SELECT
+      @carat AS carat,
+      @cut AS cut,
+      @color AS color
+  ))
+--
+InsertStatement [0-223]
+  PathExpression [12-28]
+    Identifier(AppraisedDiamond) [12-28]
+  ColumnList [29-54]
+    Identifier(id) [30-32]
+    Identifier(color) [34-39]
+    Identifier(carat) [41-46]
+    Identifier(value) [48-53]
+  Query [55-223]
+    Select [55-223]
+      SelectList [64-98]
+        SelectColumn [64-71]
+          IntLiteral(1) [64-65]
+          Alias [66-71]
+            Identifier(id) [69-71]
+        SelectColumn [75-80]
+          PathExpression [75-80]
+            Identifier(color) [75-80]
+        SelectColumn [84-89]
+          PathExpression [84-89]
+            Identifier(carat) [84-89]
+        SelectColumn [93-98]
+          PathExpression [93-98]
+            Identifier(value) [93-98]
+      FromClause [99-223]
+        TVF [106-223]
+          PathExpression [106-116]
+            Identifier(ML) [106-108]
+            Identifier(PREDICT) [109-116]
+          TVFArgument [117-138]
+            ModelClause [117-138]
+              PathExpression [123-138]
+                Identifier(DiamondAppraise) [123-138]
+          TVFArgument [142-222]
+            ExpressionSubquery [142-222]
+              Query [148-218]
+                Select [148-218]
+                  SelectList [161-218]
+                    SelectColumn [161-176]
+                      ParameterExpr [161-167]
+                        Identifier(carat) [162-167]
+                      Alias [168-176]
+                        Identifier(carat) [171-176]
+                    SelectColumn [184-195]
+                      ParameterExpr [184-188]
+                        Identifier(cut) [185-188]
+                      Alias [189-195]
+                        Identifier(cut) [192-195]
+                    SelectColumn [203-218]
+                      ParameterExpr [203-209]
+                        Identifier(color) [204-209]
+                      Alias [210-218]
+                        Identifier(color) [213-218]
diff --git a/parser/testdata/spanner/net_functions.test b/parser/testdata/spanner/net_functions.test
new file mode 100644
index 0000000..10f30ae
--- /dev/null
+++ b/parser/testdata/spanner/net_functions.test
@@ -0,0 +1,695 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/net_functions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/net_functions#nethost
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+NET.HOST(url)
+--
+FunctionCall [0-13]
+  PathExpression [0-8]
+    Identifier(NET) [0-3]
+    Identifier(HOST) [4-8]
+  PathExpression [9-12]
+    Identifier(url) [9-12]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/net_functions#nethost
+SELECT
+  FORMAT("%T", input) AS input,
+  description,
+  FORMAT("%T", NET.HOST(input)) AS host,
+  FORMAT("%T", NET.PUBLIC_SUFFIX(input)) AS suffix,
+  FORMAT("%T", NET.REG_DOMAIN(input)) AS domain
+FROM (
+  SELECT "" AS input, "invalid input" AS description
+  UNION ALL SELECT "http://abc.xyz", "standard URL"
+  UNION ALL SELECT "//user:password@a.b:80/path?query",
+                   "standard URL with relative scheme, port, path and query, but no public suffix"
+  UNION ALL SELECT "https://[::1]:80", "standard URL with IPv6 host"
+  UNION ALL SELECT "http://例子.卷筒纸.中国", "standard URL with internationalized domain name"
+  UNION ALL SELECT "    www.Example.Co.UK    ",
+                   "non-standard URL with spaces, upper case letters, and without scheme"
+  UNION ALL SELECT "mailto:?to=&subject=&body=", "URI rather than URL--unsupported"
+)
+--
+QueryStatement [0-857]
+  Query [0-857]
+    Select [0-857]
+      SelectList [9-194]
+        SelectColumn [9-37]
+          FunctionCall [9-28]
+            PathExpression [9-15]
+              Identifier(FORMAT) [9-15]
+            StringLiteral [16-20]
+              StringLiteralComponent("%T") [16-20]
+            PathExpression [22-27]
+              Identifier(input) [22-27]
+          Alias [29-37]
+            Identifier(input) [32-37]
+        SelectColumn [41-52]
+          PathExpression [41-52]
+            Identifier(description) [41-52]
+        SelectColumn [56-93]
+          FunctionCall [56-85]
+            PathExpression [56-62]
+              Identifier(FORMAT) [56-62]
+            StringLiteral [63-67]
+              StringLiteralComponent("%T") [63-67]
+            FunctionCall [69-84]
+              PathExpression [69-77]
+                Identifier(NET) [69-72]
+                Identifier(HOST) [73-77]
+              PathExpression [78-83]
+                Identifier(input) [78-83]
+          Alias [86-93]
+            Identifier(host) [89-93]
+        SelectColumn [97-145]
+          FunctionCall [97-135]
+            PathExpression [97-103]
+              Identifier(FORMAT) [97-103]
+            StringLiteral [104-108]
+              StringLiteralComponent("%T") [104-108]
+            FunctionCall [110-134]
+              PathExpression [110-127]
+                Identifier(NET) [110-113]
+                Identifier(PUBLIC_SUFFIX) [114-127]
+              PathExpression [128-133]
+                Identifier(input) [128-133]
+          Alias [136-145]
+            Identifier(suffix) [139-145]
+        SelectColumn [149-194]
+          FunctionCall [149-184]
+            PathExpression [149-155]
+              Identifier(FORMAT) [149-155]
+            StringLiteral [156-160]
+              StringLiteralComponent("%T") [156-160]
+            FunctionCall [162-183]
+              PathExpression [162-176]
+                Identifier(NET) [162-165]
+                Identifier(REG_DOMAIN) [166-176]
+              PathExpression [177-182]
+                Identifier(input) [177-182]
+          Alias [185-194]
+            Identifier(domain) [188-194]
+      FromClause [195-857]
+        TableSubquery [200-857]
+          Query [204-855]
+            SetOperation(UNION ALL) [204-855]
+              SetOperationMetadataList [257-783]
+                SetOperationMetadata [257-266]
+                  SetOperationType [257-262]
+                  SetOperationAllOrDistinct [263-266]
+                SetOperationMetadata [309-318]
+                  SetOperationType [309-314]
+                  SetOperationAllOrDistinct [315-318]
+                SetOperationMetadata [464-473]
+                  SetOperationType [464-469]
+                  SetOperationAllOrDistinct [470-473]
+                SetOperationMetadata [533-542]
+                  SetOperationType [533-538]
+                  SetOperationAllOrDistinct [539-542]
+                SetOperationMetadata [636-645]
+                  SetOperationType [636-641]
+                  SetOperationAllOrDistinct [642-645]
+                SetOperationMetadata [774-783]
+                  SetOperationType [774-779]
+                  SetOperationAllOrDistinct [780-783]
+              Select [204-254]
+                SelectList [211-254]
+                  SelectColumn [211-222]
+                    StringLiteral [211-213]
+                      StringLiteralComponent("") [211-213]
+                    Alias [214-222]
+                      Identifier(input) [217-222]
+                  SelectColumn [224-254]
+                    StringLiteral [224-239]
+                      StringLiteralComponent("invalid input") [224-239]
+                    Alias [240-254]
+                      Identifier(description) [243-254]
+              Select [267-306]
+                SelectList [274-306]
+                  SelectColumn [274-290]
+                    StringLiteral [274-290]
+                      StringLiteralComponent("http://abc.xyz") [274-290]
+                  SelectColumn [292-306]
+                    StringLiteral [292-306]
+                      StringLiteralComponent("standard URL") [292-306]
+              Select [319-461]
+                SelectList [326-461]
+                  SelectColumn [326-361]
+                    StringLiteral [326-361]
+                      StringLiteralComponent("//user:password@a.b:80/path?query") [326-361]
+                  SelectColumn [382-461]
+                    StringLiteral [382-461]
+                      StringLiteralComponent("standard URL with relative scheme, port, path and query, but no public suffix") [382-461]
+              Select [474-530]
+                SelectList [481-530]
+                  SelectColumn [481-499]
+                    StringLiteral [481-499]
+                      StringLiteralComponent("https://[::1]:80") [481-499]
+                  SelectColumn [501-530]
+                    StringLiteral [501-530]
+                      StringLiteralComponent("standard URL with IPv6 host") [501-530]
+              Select [543-633]
+                SelectList [550-633]
+                  SelectColumn [550-582]
+                    StringLiteral [550-582]
+                      StringLiteralComponent("http://例子.卷筒纸.中国") [550-582]
+                  SelectColumn [584-633]
+                    StringLiteral [584-633]
+                      StringLiteralComponent("standard URL with internationalized domain name") [584-633]
+              Select [646-771]
+                SelectList [653-771]
+                  SelectColumn [653-680]
+                    StringLiteral [653-680]
+                      StringLiteralComponent("    www.Example.Co.UK    ") [653-680]
+                  SelectColumn [701-771]
+                    StringLiteral [701-771]
+                      StringLiteralComponent("non-standard URL with spaces, upper case letters, and without scheme") [701-771]
+              Select [784-855]
+                SelectList [791-855]
+                  SelectColumn [791-819]
+                    StringLiteral [791-819]
+                      StringLiteralComponent("mailto:?to=&subject=&body=") [791-819]
+                  SelectColumn [821-855]
+                    StringLiteral [821-855]
+                      StringLiteralComponent("URI rather than URL--unsupported") [821-855]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/net_functions#netip_from_string
+[mode=expression]
+NET.IP_FROM_STRING(addr_str)
+--
+FunctionCall [0-28]
+  PathExpression [0-18]
+    Identifier(NET) [0-3]
+    Identifier(IP_FROM_STRING) [4-18]
+  PathExpression [19-27]
+    Identifier(addr_str) [19-27]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/net_functions#netip_from_string
+SELECT
+  addr_str, FORMAT("%T", NET.IP_FROM_STRING(addr_str)) AS ip_from_string
+FROM UNNEST([
+  '48.49.50.51',
+  '::1',
+  '3031:3233:3435:3637:3839:4041:4243:4445',
+  '::ffff:192.0.2.128'
+]) AS addr_str
+--
+QueryStatement [0-202]
+  Query [0-202]
+    Select [0-202]
+      SelectList [9-79]
+        SelectColumn [9-17]
+          PathExpression [9-17]
+            Identifier(addr_str) [9-17]
+        SelectColumn [19-79]
+          FunctionCall [19-61]
+            PathExpression [19-25]
+              Identifier(FORMAT) [19-25]
+            StringLiteral [26-30]
+              StringLiteralComponent("%T") [26-30]
+            FunctionCall [32-60]
+              PathExpression [32-50]
+                Identifier(NET) [32-35]
+                Identifier(IP_FROM_STRING) [36-50]
+              PathExpression [51-59]
+                Identifier(addr_str) [51-59]
+          Alias [62-79]
+            Identifier(ip_from_string) [65-79]
+      FromClause [80-202]
+        TablePathExpression [85-202]
+          UnnestExpression [85-190]
+            ExpressionWithOptAlias [92-189]
+              ArrayConstructor [92-189]
+                StringLiteral [96-109]
+                  StringLiteralComponent('48.49.50.51') [96-109]
+                StringLiteral [113-118]
+                  StringLiteralComponent('::1') [113-118]
+                StringLiteral [122-163]
+                  StringLiteralComponent('3031:3233:3435:3637:3839:4041:4243:4445') [122-163]
+                StringLiteral [167-187]
+                  StringLiteralComponent('::ffff:192.0.2.128') [167-187]
+          Alias [191-202]
+            Identifier(addr_str) [194-202]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/net_functions#netip_net_mask
+[mode=expression]
+NET.IP_NET_MASK(num_output_bytes, prefix_length)
+--
+FunctionCall [0-48]
+  PathExpression [0-15]
+    Identifier(NET) [0-3]
+    Identifier(IP_NET_MASK) [4-15]
+  PathExpression [16-32]
+    Identifier(num_output_bytes) [16-32]
+  PathExpression [34-47]
+    Identifier(prefix_length) [34-47]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/net_functions#netip_net_mask
+SELECT x, y, FORMAT("%T", NET.IP_NET_MASK(x, y)) AS ip_net_mask
+FROM UNNEST([
+  STRUCT(4 as x, 0 as y),
+  (4, 20),
+  (4, 32),
+  (16, 0),
+  (16, 1),
+  (16, 128)
+])
+--
+QueryStatement [0-162]
+  Query [0-162]
+    Select [0-162]
+      SelectList [7-63]
+        SelectColumn [7-8]
+          PathExpression [7-8]
+            Identifier(x) [7-8]
+        SelectColumn [10-11]
+          PathExpression [10-11]
+            Identifier(y) [10-11]
+        SelectColumn [13-63]
+          FunctionCall [13-48]
+            PathExpression [13-19]
+              Identifier(FORMAT) [13-19]
+            StringLiteral [20-24]
+              StringLiteralComponent("%T") [20-24]
+            FunctionCall [26-47]
+              PathExpression [26-41]
+                Identifier(NET) [26-29]
+                Identifier(IP_NET_MASK) [30-41]
+              PathExpression [42-43]
+                Identifier(x) [42-43]
+              PathExpression [45-46]
+                Identifier(y) [45-46]
+          Alias [49-63]
+            Identifier(ip_net_mask) [52-63]
+      FromClause [64-162]
+        TablePathExpression [69-162]
+          UnnestExpression [69-162]
+            ExpressionWithOptAlias [76-161]
+              ArrayConstructor [76-161]
+                StructConstructorWithKeyword [80-102]
+                  StructConstructorArg [87-93]
+                    IntLiteral(4) [87-88]
+                    Alias [89-93]
+                      Identifier(x) [92-93]
+                  StructConstructorArg [95-101]
+                    IntLiteral(0) [95-96]
+                    Alias [97-101]
+                      Identifier(y) [100-101]
+                StructConstructorWithParens [106-113]
+                  IntLiteral(4) [107-108]
+                  IntLiteral(20) [110-112]
+                StructConstructorWithParens [117-124]
+                  IntLiteral(4) [118-119]
+                  IntLiteral(32) [121-123]
+                StructConstructorWithParens [128-135]
+                  IntLiteral(16) [129-131]
+                  IntLiteral(0) [133-134]
+                StructConstructorWithParens [139-146]
+                  IntLiteral(16) [140-142]
+                  IntLiteral(1) [144-145]
+                StructConstructorWithParens [150-159]
+                  IntLiteral(16) [151-153]
+                  IntLiteral(128) [155-158]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/net_functions#netip_to_string
+[mode=expression]
+NET.IP_TO_STRING(addr_bin)
+--
+FunctionCall [0-26]
+  PathExpression [0-16]
+    Identifier(NET) [0-3]
+    Identifier(IP_TO_STRING) [4-16]
+  PathExpression [17-25]
+    Identifier(addr_bin) [17-25]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/net_functions#netip_to_string
+SELECT FORMAT("%T", x) AS addr_bin, NET.IP_TO_STRING(x) AS ip_to_string
+FROM UNNEST([
+  b"0123",
+  b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
+  b"0123456789@ABCDE",
+  b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xc0\x00\x02\x80"
+]) AS x
+--
+QueryStatement [0-268]
+  Query [0-268]
+    Select [0-268]
+      SelectList [7-71]
+        SelectColumn [7-34]
+          FunctionCall [7-22]
+            PathExpression [7-13]
+              Identifier(FORMAT) [7-13]
+            StringLiteral [14-18]
+              StringLiteralComponent("%T") [14-18]
+            PathExpression [20-21]
+              Identifier(x) [20-21]
+          Alias [23-34]
+            Identifier(addr_bin) [26-34]
+        SelectColumn [36-71]
+          FunctionCall [36-55]
+            PathExpression [36-52]
+              Identifier(NET) [36-39]
+              Identifier(IP_TO_STRING) [40-52]
+            PathExpression [53-54]
+              Identifier(x) [53-54]
+          Alias [56-71]
+            Identifier(ip_to_string) [59-71]
+      FromClause [72-268]
+        TablePathExpression [77-268]
+          UnnestExpression [77-263]
+            ExpressionWithOptAlias [84-262]
+              ArrayConstructor [84-262]
+                BytesLiteral [88-95]
+                  BytesLiteralComponent(b"0123") [88-95]
+                BytesLiteral [99-166]
+                  BytesLiteralComponent(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01") [99-166]
+                BytesLiteral [170-189]
+                  BytesLiteralComponent(b"0123456789@ABCDE") [170-189]
+                BytesLiteral [193-260]
+                  BytesLiteralComponent(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xc0\x00\x02\x80") [193-260]
+          Alias [264-268]
+            Identifier(x) [267-268]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/net_functions#netip_trunc
+[mode=expression]
+NET.IP_TRUNC(addr_bin, prefix_length)
+--
+FunctionCall [0-37]
+  PathExpression [0-12]
+    Identifier(NET) [0-3]
+    Identifier(IP_TRUNC) [4-12]
+  PathExpression [13-21]
+    Identifier(addr_bin) [13-21]
+  PathExpression [23-36]
+    Identifier(prefix_length) [23-36]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/net_functions#netip_trunc
+SELECT
+  FORMAT("%T", x) as addr_bin, prefix_length,
+  FORMAT("%T", NET.IP_TRUNC(x, prefix_length)) AS ip_trunc
+FROM UNNEST([
+  STRUCT(b"\xAA\xBB\xCC\xDD" as x, 0 as prefix_length),
+  (b"\xAA\xBB\xCC\xDD", 11), (b"\xAA\xBB\xCC\xDD", 12),
+  (b"\xAA\xBB\xCC\xDD", 24), (b"\xAA\xBB\xCC\xDD", 32),
+  (b'0123456789@ABCDE', 80)
+])
+--
+QueryStatement [0-324]
+  Query [0-324]
+    Select [0-324]
+      SelectList [9-111]
+        SelectColumn [9-36]
+          FunctionCall [9-24]
+            PathExpression [9-15]
+              Identifier(FORMAT) [9-15]
+            StringLiteral [16-20]
+              StringLiteralComponent("%T") [16-20]
+            PathExpression [22-23]
+              Identifier(x) [22-23]
+          Alias [25-36]
+            Identifier(addr_bin) [28-36]
+        SelectColumn [38-51]
+          PathExpression [38-51]
+            Identifier(prefix_length) [38-51]
+        SelectColumn [55-111]
+          FunctionCall [55-99]
+            PathExpression [55-61]
+              Identifier(FORMAT) [55-61]
+            StringLiteral [62-66]
+              StringLiteralComponent("%T") [62-66]
+            FunctionCall [68-98]
+              PathExpression [68-80]
+                Identifier(NET) [68-71]
+                Identifier(IP_TRUNC) [72-80]
+              PathExpression [81-82]
+                Identifier(x) [81-82]
+              PathExpression [84-97]
+                Identifier(prefix_length) [84-97]
+          Alias [100-111]
+            Identifier(ip_trunc) [103-111]
+      FromClause [112-324]
+        TablePathExpression [117-324]
+          UnnestExpression [117-324]
+            ExpressionWithOptAlias [124-323]
+              ArrayConstructor [124-323]
+                StructConstructorWithKeyword [128-180]
+                  StructConstructorArg [135-159]
+                    BytesLiteral [135-154]
+                      BytesLiteralComponent(b"\xAA\xBB\xCC\xDD") [135-154]
+                    Alias [155-159]
+                      Identifier(x) [158-159]
+                  StructConstructorArg [161-179]
+                    IntLiteral(0) [161-162]
+                    Alias [163-179]
+                      Identifier(prefix_length) [166-179]
+                StructConstructorWithParens [184-209]
+                  BytesLiteral [185-204]
+                    BytesLiteralComponent(b"\xAA\xBB\xCC\xDD") [185-204]
+                  IntLiteral(11) [206-208]
+                StructConstructorWithParens [211-236]
+                  BytesLiteral [212-231]
+                    BytesLiteralComponent(b"\xAA\xBB\xCC\xDD") [212-231]
+                  IntLiteral(12) [233-235]
+                StructConstructorWithParens [240-265]
+                  BytesLiteral [241-260]
+                    BytesLiteralComponent(b"\xAA\xBB\xCC\xDD") [241-260]
+                  IntLiteral(24) [262-264]
+                StructConstructorWithParens [267-292]
+                  BytesLiteral [268-287]
+                    BytesLiteralComponent(b"\xAA\xBB\xCC\xDD") [268-287]
+                  IntLiteral(32) [289-291]
+                StructConstructorWithParens [296-321]
+                  BytesLiteral [297-316]
+                    BytesLiteralComponent(b'0123456789@ABCDE') [297-316]
+                  IntLiteral(80) [318-320]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/net_functions#netipv4_from_int64
+[mode=expression]
+NET.IPV4_FROM_INT64(integer_value)
+--
+FunctionCall [0-34]
+  PathExpression [0-19]
+    Identifier(NET) [0-3]
+    Identifier(IPV4_FROM_INT64) [4-19]
+  PathExpression [20-33]
+    Identifier(integer_value) [20-33]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/net_functions#netipv4_from_int64
+SELECT x, x_hex, FORMAT("%T", NET.IPV4_FROM_INT64(x)) AS ipv4_from_int64
+FROM (
+  SELECT CAST(x_hex AS INT64) x, x_hex
+  FROM UNNEST(["0x0", "0xABCDEF", "0xFFFFFFFF", "-0x1", "-0x2"]) AS x_hex
+)
+--
+QueryStatement [0-194]
+  Query [0-194]
+    Select [0-194]
+      SelectList [7-72]
+        SelectColumn [7-8]
+          PathExpression [7-8]
+            Identifier(x) [7-8]
+        SelectColumn [10-15]
+          PathExpression [10-15]
+            Identifier(x_hex) [10-15]
+        SelectColumn [17-72]
+          FunctionCall [17-53]
+            PathExpression [17-23]
+              Identifier(FORMAT) [17-23]
+            StringLiteral [24-28]
+              StringLiteralComponent("%T") [24-28]
+            FunctionCall [30-52]
+              PathExpression [30-49]
+                Identifier(NET) [30-33]
+                Identifier(IPV4_FROM_INT64) [34-49]
+              PathExpression [50-51]
+                Identifier(x) [50-51]
+          Alias [54-72]
+            Identifier(ipv4_from_int64) [57-72]
+      FromClause [73-194]
+        TableSubquery [78-194]
+          Query [82-192]
+            Select [82-192]
+              SelectList [89-118]
+                SelectColumn [89-111]
+                  CastExpression [89-109]
+                    PathExpression [94-99]
+                      Identifier(x_hex) [94-99]
+                    SimpleType [103-108]
+                      PathExpression [103-108]
+                        Identifier(INT64) [103-108]
+                  Alias [110-111]
+                    Identifier(x) [110-111]
+                SelectColumn [113-118]
+                  PathExpression [113-118]
+                    Identifier(x_hex) [113-118]
+              FromClause [121-192]
+                TablePathExpression [126-192]
+                  UnnestExpression [126-183]
+                    ExpressionWithOptAlias [133-182]
+                      ArrayConstructor [133-182]
+                        StringLiteral [134-139]
+                          StringLiteralComponent("0x0") [134-139]
+                        StringLiteral [141-151]
+                          StringLiteralComponent("0xABCDEF") [141-151]
+                        StringLiteral [153-165]
+                          StringLiteralComponent("0xFFFFFFFF") [153-165]
+                        StringLiteral [167-173]
+                          StringLiteralComponent("-0x1") [167-173]
+                        StringLiteral [175-181]
+                          StringLiteralComponent("-0x2") [175-181]
+                  Alias [184-192]
+                    Identifier(x_hex) [187-192]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/net_functions#netipv4_to_int64
+[mode=expression]
+NET.IPV4_TO_INT64(addr_bin)
+--
+FunctionCall [0-27]
+  PathExpression [0-17]
+    Identifier(NET) [0-3]
+    Identifier(IPV4_TO_INT64) [4-17]
+  PathExpression [18-26]
+    Identifier(addr_bin) [18-26]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/net_functions#netipv4_to_int64
+SELECT
+  FORMAT("%T", x) AS addr_bin,
+  FORMAT("0x%X", NET.IPV4_TO_INT64(x)) AS ipv4_to_int64
+FROM
+UNNEST([b"\x00\x00\x00\x00", b"\x00\xab\xcd\xef", b"\xff\xff\xff\xff"]) AS x
+--
+QueryStatement [0-175]
+  Query [0-175]
+    Select [0-175]
+      SelectList [9-93]
+        SelectColumn [9-36]
+          FunctionCall [9-24]
+            PathExpression [9-15]
+              Identifier(FORMAT) [9-15]
+            StringLiteral [16-20]
+              StringLiteralComponent("%T") [16-20]
+            PathExpression [22-23]
+              Identifier(x) [22-23]
+          Alias [25-36]
+            Identifier(addr_bin) [28-36]
+        SelectColumn [40-93]
+          FunctionCall [40-76]
+            PathExpression [40-46]
+              Identifier(FORMAT) [40-46]
+            StringLiteral [47-53]
+              StringLiteralComponent("0x%X") [47-53]
+            FunctionCall [55-75]
+              PathExpression [55-72]
+                Identifier(NET) [55-58]
+                Identifier(IPV4_TO_INT64) [59-72]
+              PathExpression [73-74]
+                Identifier(x) [73-74]
+          Alias [77-93]
+            Identifier(ipv4_to_int64) [80-93]
+      FromClause [94-175]
+        TablePathExpression [99-175]
+          UnnestExpression [99-170]
+            ExpressionWithOptAlias [106-169]
+              ArrayConstructor [106-169]
+                BytesLiteral [107-126]
+                  BytesLiteralComponent(b"\x00\x00\x00\x00") [107-126]
+                BytesLiteral [128-147]
+                  BytesLiteralComponent(b"\x00\xab\xcd\xef") [128-147]
+                BytesLiteral [149-168]
+                  BytesLiteralComponent(b"\xff\xff\xff\xff") [149-168]
+          Alias [171-175]
+            Identifier(x) [174-175]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/net_functions#netpublic_suffix
+[mode=expression]
+NET.PUBLIC_SUFFIX(url)
+--
+FunctionCall [0-22]
+  PathExpression [0-17]
+    Identifier(NET) [0-3]
+    Identifier(PUBLIC_SUFFIX) [4-17]
+  PathExpression [18-21]
+    Identifier(url) [18-21]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/net_functions#netreg_domain
+[mode=expression]
+NET.REG_DOMAIN(url)
+--
+FunctionCall [0-19]
+  PathExpression [0-14]
+    Identifier(NET) [0-3]
+    Identifier(REG_DOMAIN) [4-14]
+  PathExpression [15-18]
+    Identifier(url) [15-18]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/net_functions#netsafe_ip_from_string
+[mode=expression]
+NET.SAFE_IP_FROM_STRING(addr_str)
+--
+FunctionCall [0-33]
+  PathExpression [0-23]
+    Identifier(NET) [0-3]
+    Identifier(SAFE_IP_FROM_STRING) [4-23]
+  PathExpression [24-32]
+    Identifier(addr_str) [24-32]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/net_functions#netsafe_ip_from_string
+SELECT
+  addr_str,
+  FORMAT("%T", NET.SAFE_IP_FROM_STRING(addr_str)) AS safe_ip_from_string
+FROM UNNEST([
+  '48.49.50.51',
+  '::1',
+  '3031:3233:3435:3637:3839:4041:4243:4445',
+  '::ffff:192.0.2.128',
+  '48.49.50.51/32',
+  '48.49.50',
+  '::wxyz'
+]) AS addr_str
+--
+QueryStatement [0-260]
+  Query [0-260]
+    Select [0-260]
+      SelectList [9-91]
+        SelectColumn [9-17]
+          PathExpression [9-17]
+            Identifier(addr_str) [9-17]
+        SelectColumn [21-91]
+          FunctionCall [21-68]
+            PathExpression [21-27]
+              Identifier(FORMAT) [21-27]
+            StringLiteral [28-32]
+              StringLiteralComponent("%T") [28-32]
+            FunctionCall [34-67]
+              PathExpression [34-57]
+                Identifier(NET) [34-37]
+                Identifier(SAFE_IP_FROM_STRING) [38-57]
+              PathExpression [58-66]
+                Identifier(addr_str) [58-66]
+          Alias [69-91]
+            Identifier(safe_ip_from_string) [72-91]
+      FromClause [92-260]
+        TablePathExpression [97-260]
+          UnnestExpression [97-248]
+            ExpressionWithOptAlias [104-247]
+              ArrayConstructor [104-247]
+                StringLiteral [108-121]
+                  StringLiteralComponent('48.49.50.51') [108-121]
+                StringLiteral [125-130]
+                  StringLiteralComponent('::1') [125-130]
+                StringLiteral [134-175]
+                  StringLiteralComponent('3031:3233:3435:3637:3839:4041:4243:4445') [134-175]
+                StringLiteral [179-199]
+                  StringLiteralComponent('::ffff:192.0.2.128') [179-199]
+                StringLiteral [203-219]
+                  StringLiteralComponent('48.49.50.51/32') [203-219]
+                StringLiteral [223-233]
+                  StringLiteralComponent('48.49.50') [223-233]
+                StringLiteral [237-245]
+                  StringLiteralComponent('::wxyz') [237-245]
+          Alias [249-260]
+            Identifier(addr_str) [252-260]
diff --git a/parser/testdata/spanner/operators.test b/parser/testdata/spanner/operators.test
new file mode 100644
index 0000000..028679d
--- /dev/null
+++ b/parser/testdata/spanner/operators.test
@@ -0,0 +1,1175 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#field_access_operator
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT
+  STRUCT(
+    STRUCT('Yonge Street' AS street, 'Canada' AS country)
+      AS address).address.country
+--
+QueryStatement [0-108]
+  Query [0-108]
+    Select [0-108]
+      SelectList [9-108]
+        SelectColumn [9-108]
+          DotIdentifier [9-108]
+            DotIdentifier [9-100]
+              StructConstructorWithKeyword [9-92]
+                StructConstructorArg [21-91]
+                  StructConstructorWithKeyword [21-74]
+                    StructConstructorArg [28-52]
+                      StringLiteral [28-42]
+                        StringLiteralComponent('Yonge Street') [28-42]
+                      Alias [43-52]
+                        Identifier(street) [46-52]
+                    StructConstructorArg [54-73]
+                      StringLiteral [54-62]
+                        StringLiteralComponent('Canada') [54-62]
+                      Alias [63-73]
+                        Identifier(country) [66-73]
+                  Alias [81-91]
+                    Identifier(address) [84-91]
+              Identifier(address) [93-100]
+            Identifier(country) [101-108]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#array_subscript_operator
+SELECT
+\  ["coffee", "tea", "milk"] AS item_array,
+\  ["coffee", "tea", "milk"][OFFSET(0)] AS item_offset,
+\  ["coffee", "tea", "milk"][ORDINAL(1)] AS item_ordinal,
+\  ["coffee", "tea", "milk"][SAFE_OFFSET(6)] AS item_safe_offset
+--
+QueryStatement [0-225]
+  Query [0-225]
+    Select [0-225]
+      SelectList [9-225]
+        SelectColumn [9-48]
+          ArrayConstructor [9-34]
+            StringLiteral [10-18]
+              StringLiteralComponent("coffee") [10-18]
+            StringLiteral [20-25]
+              StringLiteralComponent("tea") [20-25]
+            StringLiteral [27-33]
+              StringLiteralComponent("milk") [27-33]
+          Alias [35-48]
+            Identifier(item_array) [38-48]
+        SelectColumn [52-103]
+          ArrayElement [52-88]
+            ArrayConstructor [52-77]
+              StringLiteral [53-61]
+                StringLiteralComponent("coffee") [53-61]
+              StringLiteral [63-68]
+                StringLiteralComponent("tea") [63-68]
+              StringLiteral [70-76]
+                StringLiteralComponent("milk") [70-76]
+            Location [77-78]
+            FunctionCall [78-87]
+              PathExpression [78-84]
+                Identifier(OFFSET) [78-84]
+              IntLiteral(0) [85-86]
+          Alias [89-103]
+            Identifier(item_offset) [92-103]
+        SelectColumn [107-160]
+          ArrayElement [107-144]
+            ArrayConstructor [107-132]
+              StringLiteral [108-116]
+                StringLiteralComponent("coffee") [108-116]
+              StringLiteral [118-123]
+                StringLiteralComponent("tea") [118-123]
+              StringLiteral [125-131]
+                StringLiteralComponent("milk") [125-131]
+            Location [132-133]
+            FunctionCall [133-143]
+              PathExpression [133-140]
+                Identifier(ORDINAL) [133-140]
+              IntLiteral(1) [141-142]
+          Alias [145-160]
+            Identifier(item_ordinal) [148-160]
+        SelectColumn [164-225]
+          ArrayElement [164-205]
+            ArrayConstructor [164-189]
+              StringLiteral [165-173]
+                StringLiteralComponent("coffee") [165-173]
+              StringLiteral [175-180]
+                StringLiteralComponent("tea") [175-180]
+              StringLiteral [182-188]
+                StringLiteralComponent("milk") [182-188]
+            Location [189-190]
+            FunctionCall [190-204]
+              PathExpression [190-201]
+                Identifier(SAFE_OFFSET) [190-201]
+              IntLiteral(6) [202-203]
+          Alias [206-225]
+            Identifier(item_safe_offset) [209-225]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#array_subscript_operator
+-- Error. Array index 6 is out of bounds.
+SELECT ["coffee", "tea", "milk"][OFFSET(6)] AS item_offset
+--
+QueryStatement [42-100]
+  Query [42-100]
+    Select [42-100]
+      SelectList [49-100]
+        SelectColumn [49-100]
+          ArrayElement [49-85]
+            ArrayConstructor [49-74]
+              StringLiteral [50-58]
+                StringLiteralComponent("coffee") [50-58]
+              StringLiteral [60-65]
+                StringLiteralComponent("tea") [60-65]
+              StringLiteral [67-73]
+                StringLiteralComponent("milk") [67-73]
+            Location [74-75]
+            FunctionCall [75-84]
+              PathExpression [75-81]
+                Identifier(OFFSET) [75-81]
+              IntLiteral(6) [82-83]
+          Alias [86-100]
+            Identifier(item_offset) [89-100]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#json_subscript_operator
+SELECT json_value.class.students[0]['name'] AS first_student
+FROM
+  UNNEST(
+\    [
+      JSON '{"class" : {"students" : [{"name" : "Jane"}]}}',
+      JSON '{"class" : {"students" : []}}',
+      JSON '{"class" : {"students" : [{"name" : "John"}, {"name": "Jamie"}]}}'])
+    AS json_value
+--
+QueryStatement [0-285]
+  Query [0-285]
+    Select [0-285]
+      SelectList [7-60]
+        SelectColumn [7-60]
+          ArrayElement [7-43]
+            ArrayElement [7-35]
+              PathExpression [7-32]
+                Identifier(json_value) [7-17]
+                Identifier(class) [18-23]
+                Identifier(students) [24-32]
+              Location [32-33]
+              IntLiteral(0) [33-34]
+            Location [35-36]
+            StringLiteral [36-42]
+              StringLiteralComponent('name') [36-42]
+          Alias [44-60]
+            Identifier(first_student) [47-60]
+      FromClause [61-285]
+        TablePathExpression [68-285]
+          UnnestExpression [68-267]
+            ExpressionWithOptAlias [80-266]
+              ArrayConstructor [80-266]
+                JSONLiteral [88-141]
+                  StringLiteral [93-141]
+                    StringLiteralComponent('{"class" : {"students" : [{"name" : "Jane"}]}}') [93-141]
+                JSONLiteral [149-185]
+                  StringLiteral [154-185]
+                    StringLiteralComponent('{"class" : {"students" : []}}') [154-185]
+                JSONLiteral [193-265]
+                  StringLiteral [198-265]
+                    StringLiteralComponent('{"class" : {"students" : [{"name" : "John"}, {"name": "Jamie"}]}}') [198-265]
+          Alias [272-285]
+            Identifier(json_value) [275-285]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#datetime_subtraction
+SELECT
+  DATE "2021-05-20" - DATE "2020-04-19" AS date_diff,
+  TIMESTAMP "2021-06-01 12:34:56.789" - TIMESTAMP "2021-05-31 00:00:00" AS time_diff
+--
+QueryStatement [0-145]
+  Query [0-145]
+    Select [0-145]
+      SelectList [9-145]
+        SelectColumn [9-59]
+          BinaryExpression(-) [9-46]
+            DateOrTimeLiteral(TYPE_DATE) [9-26]
+              StringLiteral [14-26]
+                StringLiteralComponent("2021-05-20") [14-26]
+            DateOrTimeLiteral(TYPE_DATE) [29-46]
+              StringLiteral [34-46]
+                StringLiteralComponent("2020-04-19") [34-46]
+          Alias [47-59]
+            Identifier(date_diff) [50-59]
+        SelectColumn [63-145]
+          BinaryExpression(-) [63-132]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [63-98]
+              StringLiteral [73-98]
+                StringLiteralComponent("2021-06-01 12:34:56.789") [73-98]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [101-132]
+              StringLiteral [111-132]
+                StringLiteralComponent("2021-05-31 00:00:00") [111-132]
+          Alias [133-145]
+            Identifier(time_diff) [136-145]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#interval_arithmetic_operators
+SELECT
+  TIMESTAMP "2021-05-02 00:01:02.345+00" + INTERVAL 25 HOUR AS time_plus,
+  TIMESTAMP "2021-05-02 00:01:02.345+00" - INTERVAL 10 SECOND AS time_minus
+--
+QueryStatement [0-156]
+  Query [0-156]
+    Select [0-156]
+      SelectList [9-156]
+        SelectColumn [9-79]
+          BinaryExpression(+) [9-66]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [9-47]
+              StringLiteral [19-47]
+                StringLiteralComponent("2021-05-02 00:01:02.345+00") [19-47]
+            IntervalExpr [50-66]
+              IntLiteral(25) [59-61]
+              Identifier(HOUR) [62-66]
+          Alias [67-79]
+            Identifier(time_plus) [70-79]
+        SelectColumn [83-156]
+          BinaryExpression(-) [83-142]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [83-121]
+              StringLiteral [93-121]
+                StringLiteralComponent("2021-05-02 00:01:02.345+00") [93-121]
+            IntervalExpr [124-142]
+              IntLiteral(10) [133-135]
+              Identifier(SECOND) [136-142]
+          Alias [143-156]
+            Identifier(time_minus) [146-156]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#interval_arithmetic_operators
+SELECT
+  INTERVAL '1:2:3' HOUR TO SECOND * 10 AS mul1,
+  INTERVAL 35 SECOND * 4 AS mul2,
+  INTERVAL 10 YEAR / 3 AS div1,
+  INTERVAL 1 MONTH / 12 AS div2
+--
+QueryStatement [0-152]
+  Query [0-152]
+    Select [0-152]
+      SelectList [9-152]
+        SelectColumn [9-53]
+          BinaryExpression(*) [9-45]
+            IntervalExpr [9-40]
+              StringLiteral [18-25]
+                StringLiteralComponent('1:2:3') [18-25]
+              Identifier(HOUR) [26-30]
+              Identifier(SECOND) [34-40]
+            IntLiteral(10) [43-45]
+          Alias [46-53]
+            Identifier(mul1) [49-53]
+        SelectColumn [57-87]
+          BinaryExpression(*) [57-79]
+            IntervalExpr [57-75]
+              IntLiteral(35) [66-68]
+              Identifier(SECOND) [69-75]
+            IntLiteral(4) [78-79]
+          Alias [80-87]
+            Identifier(mul2) [83-87]
+        SelectColumn [91-119]
+          BinaryExpression(/) [91-111]
+            IntervalExpr [91-107]
+              IntLiteral(10) [100-102]
+              Identifier(YEAR) [103-107]
+            IntLiteral(3) [110-111]
+          Alias [112-119]
+            Identifier(div1) [115-119]
+        SelectColumn [123-152]
+          BinaryExpression(/) [123-144]
+            IntervalExpr [123-139]
+              IntLiteral(1) [132-133]
+              Identifier(MONTH) [134-139]
+            IntLiteral(12) [142-144]
+          Alias [145-152]
+            Identifier(div2) [148-152]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#logical_operators
+SELECT 'a' FROM entry_table WHERE entry = 'a'
+
+-- a => 'a' = 'a' => TRUE
+-- b => 'b' = 'a' => FALSE
+-- NULL => NULL = 'a' => NULL
+--
+QueryStatement [0-45]
+  Query [0-45]
+    Select [0-45]
+      SelectList [7-10]
+        SelectColumn [7-10]
+          StringLiteral [7-10]
+            StringLiteralComponent('a') [7-10]
+      FromClause [11-27]
+        TablePathExpression [16-27]
+          PathExpression [16-27]
+            Identifier(entry_table) [16-27]
+      WhereClause [28-45]
+        BinaryExpression(=) [34-45]
+          PathExpression [34-39]
+            Identifier(entry) [34-39]
+          StringLiteral [42-45]
+            StringLiteralComponent('a') [42-45]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#logical_operators
+SELECT entry FROM entry_table WHERE NOT (entry = 'a')
+
+-- a => NOT('a' = 'a') => NOT(TRUE) => FALSE
+-- b => NOT('b' = 'a') => NOT(FALSE) => TRUE
+-- NULL => NOT(NULL = 'a') => NOT(NULL) => NULL
+--
+QueryStatement [0-53]
+  Query [0-53]
+    Select [0-53]
+      SelectList [7-12]
+        SelectColumn [7-12]
+          PathExpression [7-12]
+            Identifier(entry) [7-12]
+      FromClause [13-29]
+        TablePathExpression [18-29]
+          PathExpression [18-29]
+            Identifier(entry_table) [18-29]
+      WhereClause [30-53]
+        UnaryExpression(NOT) [36-53]
+          BinaryExpression(=) [41-52]
+            PathExpression [41-46]
+              Identifier(entry) [41-46]
+            StringLiteral [49-52]
+              StringLiteralComponent('a') [49-52]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#logical_operators
+SELECT entry FROM entry_table WHERE entry IS NULL
+
+-- a => 'a' IS NULL => FALSE
+-- b => 'b' IS NULL => FALSE
+-- NULL => NULL IS NULL => TRUE
+--
+QueryStatement [0-49]
+  Query [0-49]
+    Select [0-49]
+      SelectList [7-12]
+        SelectColumn [7-12]
+          PathExpression [7-12]
+            Identifier(entry) [7-12]
+      FromClause [13-29]
+        TablePathExpression [18-29]
+          PathExpression [18-29]
+            Identifier(entry_table) [18-29]
+      WhereClause [30-49]
+        BinaryExpression(IS) [36-49]
+          PathExpression [36-41]
+            Identifier(entry) [36-41]
+          NullLiteral(NULL) [45-49]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#exists_operator
+WITH Words AS (
+  SELECT 'Intend' as value, 'east' as direction UNION ALL
+  SELECT 'Secure', 'north' UNION ALL
+  SELECT 'Clarity', 'west'
+ )
+SELECT EXISTS( SELECT value FROM Words WHERE direction = 'south' ) as result
+--
+QueryStatement [0-217]
+  Query [0-217]
+    WithClause [0-140]
+      WithClauseEntry [5-140]
+        AliasedQuery [5-140]
+          Identifier(Words) [5-10]
+          Query [14-140]
+            SetOperation(UNION ALL) [18-137]
+              SetOperationMetadataList [64-110]
+                SetOperationMetadata [64-73]
+                  SetOperationType [64-69]
+                  SetOperationAllOrDistinct [70-73]
+                SetOperationMetadata [101-110]
+                  SetOperationType [101-106]
+                  SetOperationAllOrDistinct [107-110]
+              Select [18-63]
+                SelectList [25-63]
+                  SelectColumn [25-42]
+                    StringLiteral [25-33]
+                      StringLiteralComponent('Intend') [25-33]
+                    Alias [34-42]
+                      Identifier(value) [37-42]
+                  SelectColumn [44-63]
+                    StringLiteral [44-50]
+                      StringLiteralComponent('east') [44-50]
+                    Alias [51-63]
+                      Identifier(direction) [54-63]
+              Select [76-100]
+                SelectList [83-100]
+                  SelectColumn [83-91]
+                    StringLiteral [83-91]
+                      StringLiteralComponent('Secure') [83-91]
+                  SelectColumn [93-100]
+                    StringLiteral [93-100]
+                      StringLiteralComponent('north') [93-100]
+              Select [113-137]
+                SelectList [120-137]
+                  SelectColumn [120-129]
+                    StringLiteral [120-129]
+                      StringLiteralComponent('Clarity') [120-129]
+                  SelectColumn [131-137]
+                    StringLiteral [131-137]
+                      StringLiteralComponent('west') [131-137]
+    Select [141-217]
+      SelectList [148-217]
+        SelectColumn [148-217]
+          ExpressionSubquery(modifier=EXISTS) [148-207]
+            Query [156-205]
+              Select [156-205]
+                SelectList [163-168]
+                  SelectColumn [163-168]
+                    PathExpression [163-168]
+                      Identifier(value) [163-168]
+                FromClause [169-179]
+                  TablePathExpression [174-179]
+                    PathExpression [174-179]
+                      Identifier(Words) [174-179]
+                WhereClause [180-205]
+                  BinaryExpression(=) [186-205]
+                    PathExpression [186-195]
+                      Identifier(direction) [186-195]
+                    StringLiteral [198-205]
+                      StringLiteralComponent('south') [198-205]
+          Alias [208-217]
+            Identifier(result) [211-217]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#in_operators
+[mode=expression]
+x IN UNNEST(@array_parameter)
+--
+InExpression(IN) [0-29]
+  PathExpression [0-1]
+    Identifier(x) [0-1]
+  Location [2-4]
+  UnnestExpression [5-29]
+    ExpressionWithOptAlias [12-28]
+      ParameterExpr [12-28]
+        Identifier(array_parameter) [13-28]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#in_operators
+WITH Words AS (
+  SELECT 'Intend' as value UNION ALL
+  SELECT 'Secure' UNION ALL
+  SELECT 'Clarity' UNION ALL
+  SELECT 'Peace' UNION ALL
+  SELECT 'Intend'
+ )
+SELECT * FROM Words
+--
+QueryStatement [0-177]
+  Query [0-177]
+    WithClause [0-157]
+      WithClauseEntry [5-157]
+        AliasedQuery [5-157]
+          Identifier(Words) [5-10]
+          Query [14-157]
+            SetOperation(UNION ALL) [18-154]
+              SetOperationMetadataList [43-136]
+                SetOperationMetadata [43-52]
+                  SetOperationType [43-48]
+                  SetOperationAllOrDistinct [49-52]
+                SetOperationMetadata [71-80]
+                  SetOperationType [71-76]
+                  SetOperationAllOrDistinct [77-80]
+                SetOperationMetadata [100-109]
+                  SetOperationType [100-105]
+                  SetOperationAllOrDistinct [106-109]
+                SetOperationMetadata [127-136]
+                  SetOperationType [127-132]
+                  SetOperationAllOrDistinct [133-136]
+              Select [18-42]
+                SelectList [25-42]
+                  SelectColumn [25-42]
+                    StringLiteral [25-33]
+                      StringLiteralComponent('Intend') [25-33]
+                    Alias [34-42]
+                      Identifier(value) [37-42]
+              Select [55-70]
+                SelectList [62-70]
+                  SelectColumn [62-70]
+                    StringLiteral [62-70]
+                      StringLiteralComponent('Secure') [62-70]
+              Select [83-99]
+                SelectList [90-99]
+                  SelectColumn [90-99]
+                    StringLiteral [90-99]
+                      StringLiteralComponent('Clarity') [90-99]
+              Select [112-126]
+                SelectList [119-126]
+                  SelectColumn [119-126]
+                    StringLiteral [119-126]
+                      StringLiteralComponent('Peace') [119-126]
+              Select [139-154]
+                SelectList [146-154]
+                  SelectColumn [146-154]
+                    StringLiteral [146-154]
+                      StringLiteralComponent('Intend') [146-154]
+    Select [158-177]
+      SelectList [165-166]
+        SelectColumn [165-166]
+          Star(*) [165-166]
+      FromClause [167-177]
+        TablePathExpression [172-177]
+          PathExpression [172-177]
+            Identifier(Words) [172-177]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#in_operators
+WITH
+  Items AS (
+    SELECT STRUCT('blue' AS color, 'round' AS shape) AS info UNION ALL
+    SELECT STRUCT('blue', 'square') UNION ALL
+    SELECT STRUCT('red', 'round')
+  )
+SELECT * FROM Items
+--
+QueryStatement [0-192]
+  Query [0-192]
+    WithClause [0-172]
+      WithClauseEntry [7-172]
+        AliasedQuery [7-172]
+          Identifier(Items) [7-12]
+          Query [16-172]
+            SetOperation(UNION ALL) [22-168]
+              SetOperationMetadataList [79-134]
+                SetOperationMetadata [79-88]
+                  SetOperationType [79-84]
+                  SetOperationAllOrDistinct [85-88]
+                SetOperationMetadata [125-134]
+                  SetOperationType [125-130]
+                  SetOperationAllOrDistinct [131-134]
+              Select [22-78]
+                SelectList [29-78]
+                  SelectColumn [29-78]
+                    StructConstructorWithKeyword [29-70]
+                      StructConstructorArg [36-51]
+                        StringLiteral [36-42]
+                          StringLiteralComponent('blue') [36-42]
+                        Alias [43-51]
+                          Identifier(color) [46-51]
+                      StructConstructorArg [53-69]
+                        StringLiteral [53-60]
+                          StringLiteralComponent('round') [53-60]
+                        Alias [61-69]
+                          Identifier(shape) [64-69]
+                    Alias [71-78]
+                      Identifier(info) [74-78]
+              Select [93-124]
+                SelectList [100-124]
+                  SelectColumn [100-124]
+                    StructConstructorWithKeyword [100-124]
+                      StructConstructorArg [107-113]
+                        StringLiteral [107-113]
+                          StringLiteralComponent('blue') [107-113]
+                      StructConstructorArg [115-123]
+                        StringLiteral [115-123]
+                          StringLiteralComponent('square') [115-123]
+              Select [139-168]
+                SelectList [146-168]
+                  SelectColumn [146-168]
+                    StructConstructorWithKeyword [146-168]
+                      StructConstructorArg [153-158]
+                        StringLiteral [153-158]
+                          StringLiteralComponent('red') [153-158]
+                      StructConstructorArg [160-167]
+                        StringLiteral [160-167]
+                          StringLiteralComponent('round') [160-167]
+    Select [173-192]
+      SelectList [180-181]
+        SelectColumn [180-181]
+          Star(*) [180-181]
+      FromClause [182-192]
+        TablePathExpression [187-192]
+          PathExpression [187-192]
+            Identifier(Items) [187-192]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#in_operators
+SELECT * FROM Words WHERE value IN ('Intend', 'Secure')
+--
+QueryStatement [0-55]
+  Query [0-55]
+    Select [0-55]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-19]
+        TablePathExpression [14-19]
+          PathExpression [14-19]
+            Identifier(Words) [14-19]
+      WhereClause [20-55]
+        InExpression(IN) [26-55]
+          PathExpression [26-31]
+            Identifier(value) [26-31]
+          Location [32-34]
+          InList [36-54]
+            StringLiteral [36-44]
+              StringLiteralComponent('Intend') [36-44]
+            StringLiteral [46-54]
+              StringLiteralComponent('Secure') [46-54]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#in_operators
+SELECT * FROM Words WHERE value NOT IN ('Intend')
+--
+QueryStatement [0-49]
+  Query [0-49]
+    Select [0-49]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-19]
+        TablePathExpression [14-19]
+          PathExpression [14-19]
+            Identifier(Words) [14-19]
+      WhereClause [20-49]
+        InExpression(NOT IN) [26-49]
+          PathExpression [26-31]
+            Identifier(value) [26-31]
+          Location [36-38]
+          InList [40-48]
+            StringLiteral [40-48]
+              StringLiteralComponent('Intend') [40-48]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#in_operators
+SELECT * FROM Words WHERE value IN ((SELECT 'Intend'), 'Clarity')
+--
+QueryStatement [0-65]
+  Query [0-65]
+    Select [0-65]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-19]
+        TablePathExpression [14-19]
+          PathExpression [14-19]
+            Identifier(Words) [14-19]
+      WhereClause [20-65]
+        InExpression(IN) [26-65]
+          PathExpression [26-31]
+            Identifier(value) [26-31]
+          Location [32-34]
+          InList [36-64]
+            ExpressionSubquery [36-53]
+              Query [37-52]
+                Select [37-52]
+                  SelectList [44-52]
+                    SelectColumn [44-52]
+                      StringLiteral [44-52]
+                        StringLiteralComponent('Intend') [44-52]
+            StringLiteral [55-64]
+              StringLiteralComponent('Clarity') [55-64]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#in_operators
+SELECT * FROM Words WHERE value IN UNNEST(['Secure', 'Clarity'])
+--
+QueryStatement [0-64]
+  Query [0-64]
+    Select [0-64]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-19]
+        TablePathExpression [14-19]
+          PathExpression [14-19]
+            Identifier(Words) [14-19]
+      WhereClause [20-64]
+        InExpression(IN) [26-64]
+          PathExpression [26-31]
+            Identifier(value) [26-31]
+          Location [32-34]
+          UnnestExpression [35-64]
+            ExpressionWithOptAlias [42-63]
+              ArrayConstructor [42-63]
+                StringLiteral [43-51]
+                  StringLiteralComponent('Secure') [43-51]
+                StringLiteral [53-62]
+                  StringLiteralComponent('Clarity') [53-62]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#in_operators
+SELECT
+  Items.info as item
+FROM
+  Items
+WHERE (info.shape, info.color) IN (('round', 'blue'))
+--
+QueryStatement [0-94]
+  Query [0-94]
+    Select [0-94]
+      SelectList [9-27]
+        SelectColumn [9-27]
+          PathExpression [9-19]
+            Identifier(Items) [9-14]
+            Identifier(info) [15-19]
+          Alias [20-27]
+            Identifier(item) [23-27]
+      FromClause [28-40]
+        TablePathExpression [35-40]
+          PathExpression [35-40]
+            Identifier(Items) [35-40]
+      WhereClause [41-94]
+        InExpression(IN) [47-94]
+          StructConstructorWithParens [47-71]
+            PathExpression [48-58]
+              Identifier(info) [48-52]
+              Identifier(shape) [53-58]
+            PathExpression [60-70]
+              Identifier(info) [60-64]
+              Identifier(color) [65-70]
+          Location [72-74]
+          InList [76-93]
+            StructConstructorWithParens [76-93]
+              StringLiteral [77-84]
+                StringLiteralComponent('round') [77-84]
+              StringLiteral [86-92]
+                StringLiteralComponent('blue') [86-92]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#like_operator
+-- Returns TRUE
+SELECT 'apple' LIKE 'a%'
+--
+QueryStatement [16-40]
+  Query [16-40]
+    Select [16-40]
+      SelectList [23-40]
+        SelectColumn [23-40]
+          BinaryExpression(LIKE) [23-40]
+            StringLiteral [23-30]
+              StringLiteralComponent('apple') [23-30]
+            StringLiteral [36-40]
+              StringLiteralComponent('a%') [36-40]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#like_operator
+-- Returns FALSE
+SELECT '%a' LIKE 'apple'
+--
+QueryStatement [17-41]
+  Query [17-41]
+    Select [17-41]
+      SelectList [24-41]
+        SelectColumn [24-41]
+          BinaryExpression(LIKE) [24-41]
+            StringLiteral [24-28]
+              StringLiteralComponent('%a') [24-28]
+            StringLiteral [34-41]
+              StringLiteralComponent('apple') [34-41]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#like_operator
+-- Returns FALSE
+SELECT 'apple' NOT LIKE 'a%'
+--
+QueryStatement [17-45]
+  Query [17-45]
+    Select [17-45]
+      SelectList [24-45]
+        SelectColumn [24-45]
+          BinaryExpression(NOT LIKE) [24-45]
+            StringLiteral [24-31]
+              StringLiteralComponent('apple') [24-31]
+            StringLiteral [41-45]
+              StringLiteralComponent('a%') [41-45]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#like_operator
+-- Returns TRUE
+SELECT '%a' NOT LIKE 'apple'
+--
+QueryStatement [16-44]
+  Query [16-44]
+    Select [16-44]
+      SelectList [23-44]
+        SelectColumn [23-44]
+          BinaryExpression(NOT LIKE) [23-44]
+            StringLiteral [23-27]
+              StringLiteralComponent('%a') [23-27]
+            StringLiteral [37-44]
+              StringLiteralComponent('apple') [37-44]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#like_operator
+-- Produces an error
+SELECT NULL LIKE 'a%'
+--
+QueryStatement [21-42]
+  Query [21-42]
+    Select [21-42]
+      SelectList [28-42]
+        SelectColumn [28-42]
+          BinaryExpression(LIKE) [28-42]
+            NullLiteral(NULL) [28-32]
+            StringLiteral [38-42]
+              StringLiteralComponent('a%') [38-42]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#like_operator
+-- Produces an error
+SELECT 'apple' LIKE NULL
+--
+QueryStatement [21-45]
+  Query [21-45]
+    Select [21-45]
+      SelectList [28-45]
+        SelectColumn [28-45]
+          BinaryExpression(LIKE) [28-45]
+            StringLiteral [28-35]
+              StringLiteralComponent('apple') [28-35]
+            NullLiteral(NULL) [41-45]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#like_operator
+WITH Words AS
+ (SELECT 'Intend with clarity.' as value UNION ALL
+  SELECT 'Secure with intention.' UNION ALL
+  SELECT 'Clarity and security.')
+SELECT value
+FROM Words
+WHERE ARRAY_INCLUDES(['%ity%', '%and%'], pattern->(Words.value LIKE pattern))
+--
+QueryStatement [0-244]
+  Query [0-244]
+    WithClause [0-142]
+      WithClauseEntry [5-142]
+        AliasedQuery [5-142]
+          Identifier(Words) [5-10]
+          Query [15-142]
+            SetOperation(UNION ALL) [16-141]
+              SetOperationMetadataList [55-108]
+                SetOperationMetadata [55-64]
+                  SetOperationType [55-60]
+                  SetOperationAllOrDistinct [61-64]
+                SetOperationMetadata [99-108]
+                  SetOperationType [99-104]
+                  SetOperationAllOrDistinct [105-108]
+              Select [16-54]
+                SelectList [23-54]
+                  SelectColumn [23-54]
+                    StringLiteral [23-45]
+                      StringLiteralComponent('Intend with clarity.') [23-45]
+                    Alias [46-54]
+                      Identifier(value) [49-54]
+              Select [67-98]
+                SelectList [74-98]
+                  SelectColumn [74-98]
+                    StringLiteral [74-98]
+                      StringLiteralComponent('Secure with intention.') [74-98]
+              Select [111-141]
+                SelectList [118-141]
+                  SelectColumn [118-141]
+                    StringLiteral [118-141]
+                      StringLiteralComponent('Clarity and security.') [118-141]
+    Select [143-244]
+      SelectList [150-155]
+        SelectColumn [150-155]
+          PathExpression [150-155]
+            Identifier(value) [150-155]
+      FromClause [156-166]
+        TablePathExpression [161-166]
+          PathExpression [161-166]
+            Identifier(Words) [161-166]
+      WhereClause [167-244]
+        FunctionCall [173-244]
+          PathExpression [173-187]
+            Identifier(ARRAY_INCLUDES) [173-187]
+          ArrayConstructor [188-206]
+            StringLiteral [189-196]
+              StringLiteralComponent('%ity%') [189-196]
+            StringLiteral [198-205]
+              StringLiteralComponent('%and%') [198-205]
+          Lambda [208-243]
+            PathExpression [208-215]
+              Identifier(pattern) [208-215]
+            BinaryExpression(LIKE) [218-242]
+              PathExpression [218-229]
+                Identifier(Words) [218-223]
+                Identifier(value) [224-229]
+              PathExpression [235-242]
+                Identifier(pattern) [235-242]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#new_operator
+SELECT
+  key,
+  name,
+  NEW googlesql.examples.music.Chart(key AS rank, name AS chart_name)
+FROM
+  (SELECT 1 AS key, "2" AS name)
+--
+QueryStatement [0-129]
+  Query [0-129]
+    Select [0-129]
+      SelectList [9-91]
+        SelectColumn [9-12]
+          PathExpression [9-12]
+            Identifier(key) [9-12]
+        SelectColumn [16-20]
+          PathExpression [16-20]
+            Identifier(name) [16-20]
+        SelectColumn [24-91]
+          NewConstructor [24-91]
+            SimpleType [28-58]
+              PathExpression [28-58]
+                Identifier(googlesql) [28-37]
+                Identifier(examples) [38-46]
+                Identifier(music) [47-52]
+                Identifier(Chart) [53-58]
+            NewConstructorArg [59-70]
+              PathExpression [59-62]
+                Identifier(key) [59-62]
+              Identifier(rank) [66-70]
+            NewConstructorArg [72-90]
+              PathExpression [72-76]
+                Identifier(name) [72-76]
+              Identifier(chart_name) [80-90]
+      FromClause [92-129]
+        TableSubquery [99-129]
+          Query [100-128]
+            Select [100-128]
+              SelectList [107-128]
+                SelectColumn [107-115]
+                  IntLiteral(1) [107-108]
+                  Alias [109-115]
+                    Identifier(key) [112-115]
+                SelectColumn [117-128]
+                  StringLiteral [117-120]
+                    StringLiteralComponent("2") [117-120]
+                  Alias [121-128]
+                    Identifier(name) [124-128]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#with_expression
+SELECT WITH(a AS '123',               -- a is '123'
+            b AS CONCAT(a, '456'),    -- b is '123456'
+            c AS '789',               -- c is '789'
+            CONCAT(b, c)) AS result
+--
+QueryStatement [0-194]
+  Query [0-194]
+    Select [0-194]
+      SelectList [7-194]
+        SelectColumn [7-194]
+          WithExpression [7-184]
+            SelectList [12-129]
+              SelectColumn [12-22]
+                StringLiteral [17-22]
+                  StringLiteralComponent('123') [17-22]
+                Alias [12-16]
+                  Identifier(a) [12-13]
+              SelectColumn [64-85]
+                FunctionCall [69-85]
+                  PathExpression [69-75]
+                    Identifier(CONCAT) [69-75]
+                  PathExpression [76-77]
+                    Identifier(a) [76-77]
+                  StringLiteral [79-84]
+                    StringLiteralComponent('456') [79-84]
+                Alias [64-68]
+                  Identifier(b) [64-65]
+              SelectColumn [119-129]
+                StringLiteral [124-129]
+                  StringLiteralComponent('789') [124-129]
+                Alias [119-123]
+                  Identifier(c) [119-120]
+            FunctionCall [171-183]
+              PathExpression [171-177]
+                Identifier(CONCAT) [171-177]
+              PathExpression [178-179]
+                Identifier(b) [178-179]
+              PathExpression [181-182]
+                Identifier(c) [181-182]
+          Alias [185-194]
+            Identifier(result) [188-194]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#with_expression
+SELECT WITH(s AS SUM(input), c AS COUNT(input), s/c)
+FROM UNNEST([1.0, 2.0, 3.0]) AS input
+--
+QueryStatement [0-90]
+  Query [0-90]
+    Select [0-90]
+      SelectList [7-52]
+        SelectColumn [7-52]
+          WithExpression [7-52]
+            SelectList [12-46]
+              SelectColumn [12-27]
+                FunctionCall [17-27]
+                  PathExpression [17-20]
+                    Identifier(SUM) [17-20]
+                  PathExpression [21-26]
+                    Identifier(input) [21-26]
+                Alias [12-16]
+                  Identifier(s) [12-13]
+              SelectColumn [29-46]
+                FunctionCall [34-46]
+                  PathExpression [34-39]
+                    Identifier(COUNT) [34-39]
+                  PathExpression [40-45]
+                    Identifier(input) [40-45]
+                Alias [29-33]
+                  Identifier(c) [29-30]
+            BinaryExpression(/) [48-51]
+              PathExpression [48-49]
+                Identifier(s) [48-49]
+              PathExpression [50-51]
+                Identifier(c) [50-51]
+      FromClause [53-90]
+        TablePathExpression [58-90]
+          UnnestExpression [58-81]
+            ExpressionWithOptAlias [65-80]
+              ArrayConstructor [65-80]
+                FloatLiteral(1.0) [66-69]
+                FloatLiteral(2.0) [71-74]
+                FloatLiteral(3.0) [76-79]
+          Alias [82-90]
+            Identifier(input) [85-90]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#with_expression
+SELECT WITH(diff AS a - b, AVG(diff))
+FROM UNNEST([
+              STRUCT(1 AS a, 2 AS b),
+              STRUCT(3 AS a, 4 AS b),
+              STRUCT(5 AS a, 6 AS b)
+            ])
+--
+QueryStatement [0-179]
+  Query [0-179]
+    Select [0-179]
+      SelectList [7-37]
+        SelectColumn [7-37]
+          WithExpression [7-37]
+            SelectList [12-25]
+              SelectColumn [12-25]
+                BinaryExpression(-) [20-25]
+                  PathExpression [20-21]
+                    Identifier(a) [20-21]
+                  PathExpression [24-25]
+                    Identifier(b) [24-25]
+                Alias [12-19]
+                  Identifier(diff) [12-16]
+            FunctionCall [27-36]
+              PathExpression [27-30]
+                Identifier(AVG) [27-30]
+              PathExpression [31-35]
+                Identifier(diff) [31-35]
+      FromClause [38-179]
+        TablePathExpression [43-179]
+          UnnestExpression [43-179]
+            ExpressionWithOptAlias [50-178]
+              ArrayConstructor [50-178]
+                StructConstructorWithKeyword [66-88]
+                  StructConstructorArg [73-79]
+                    IntLiteral(1) [73-74]
+                    Alias [75-79]
+                      Identifier(a) [78-79]
+                  StructConstructorArg [81-87]
+                    IntLiteral(2) [81-82]
+                    Alias [83-87]
+                      Identifier(b) [86-87]
+                StructConstructorWithKeyword [104-126]
+                  StructConstructorArg [111-117]
+                    IntLiteral(3) [111-112]
+                    Alias [113-117]
+                      Identifier(a) [116-117]
+                  StructConstructorArg [119-125]
+                    IntLiteral(4) [119-120]
+                    Alias [121-125]
+                      Identifier(b) [124-125]
+                StructConstructorWithKeyword [142-164]
+                  StructConstructorArg [149-155]
+                    IntLiteral(5) [149-150]
+                    Alias [151-155]
+                      Identifier(a) [154-155]
+                  StructConstructorArg [157-163]
+                    IntLiteral(6) [157-158]
+                    Alias [159-163]
+                      Identifier(b) [162-163]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/operators#with_expression
+WITH my_table AS (
+  SELECT 1 AS x, 2 AS y
+  UNION ALL
+  SELECT 3 AS x, 4 AS y
+  UNION ALL
+  SELECT 5 AS x, 6 AS y
+)
+SELECT WITH(a AS SUM(x), b AS COUNT(x), a/b) AS avg_x, AVG(y) AS avg_y
+FROM my_table
+WHERE x > 1
+--
+QueryStatement [0-213]
+  Query [0-213]
+    WithClause [0-116]
+      WithClauseEntry [5-116]
+        AliasedQuery [5-116]
+          Identifier(my_table) [5-13]
+          Query [17-116]
+            SetOperation(UNION ALL) [21-114]
+              SetOperationMetadataList [45-90]
+                SetOperationMetadata [45-54]
+                  SetOperationType [45-50]
+                  SetOperationAllOrDistinct [51-54]
+                SetOperationMetadata [81-90]
+                  SetOperationType [81-86]
+                  SetOperationAllOrDistinct [87-90]
+              Select [21-42]
+                SelectList [28-42]
+                  SelectColumn [28-34]
+                    IntLiteral(1) [28-29]
+                    Alias [30-34]
+                      Identifier(x) [33-34]
+                  SelectColumn [36-42]
+                    IntLiteral(2) [36-37]
+                    Alias [38-42]
+                      Identifier(y) [41-42]
+              Select [57-78]
+                SelectList [64-78]
+                  SelectColumn [64-70]
+                    IntLiteral(3) [64-65]
+                    Alias [66-70]
+                      Identifier(x) [69-70]
+                  SelectColumn [72-78]
+                    IntLiteral(4) [72-73]
+                    Alias [74-78]
+                      Identifier(y) [77-78]
+              Select [93-114]
+                SelectList [100-114]
+                  SelectColumn [100-106]
+                    IntLiteral(5) [100-101]
+                    Alias [102-106]
+                      Identifier(x) [105-106]
+                  SelectColumn [108-114]
+                    IntLiteral(6) [108-109]
+                    Alias [110-114]
+                      Identifier(y) [113-114]
+    Select [117-213]
+      SelectList [124-187]
+        SelectColumn [124-170]
+          WithExpression [124-161]
+            SelectList [129-155]
+              SelectColumn [129-140]
+                FunctionCall [134-140]
+                  PathExpression [134-137]
+                    Identifier(SUM) [134-137]
+                  PathExpression [138-139]
+                    Identifier(x) [138-139]
+                Alias [129-133]
+                  Identifier(a) [129-130]
+              SelectColumn [142-155]
+                FunctionCall [147-155]
+                  PathExpression [147-152]
+                    Identifier(COUNT) [147-152]
+                  PathExpression [153-154]
+                    Identifier(x) [153-154]
+                Alias [142-146]
+                  Identifier(b) [142-143]
+            BinaryExpression(/) [157-160]
+              PathExpression [157-158]
+                Identifier(a) [157-158]
+              PathExpression [159-160]
+                Identifier(b) [159-160]
+          Alias [162-170]
+            Identifier(avg_x) [165-170]
+        SelectColumn [172-187]
+          FunctionCall [172-178]
+            PathExpression [172-175]
+              Identifier(AVG) [172-175]
+            PathExpression [176-177]
+              Identifier(y) [176-177]
+          Alias [179-187]
+            Identifier(avg_y) [182-187]
+      FromClause [188-201]
+        TablePathExpression [193-201]
+          PathExpression [193-201]
+            Identifier(my_table) [193-201]
+      WhereClause [202-213]
+        BinaryExpression(>) [208-213]
+          PathExpression [208-209]
+            Identifier(x) [208-209]
+          IntLiteral(1) [212-213]
diff --git a/parser/testdata/spanner/pipe-syntax.test b/parser/testdata/spanner/pipe-syntax.test
new file mode 100644
index 0000000..c3c05d6
--- /dev/null
+++ b/parser/testdata/spanner/pipe-syntax.test
@@ -0,0 +1,1837 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#query_comparison
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CREATE TABLE Produce(
+  item STRING(MAX),
+  sales INT64,
+  category STRING(MAX),
+) PRIMARY KEY (item, sales)
+--
+CreateTableStatement [0-108]
+  PathExpression [13-20]
+    Identifier(Produce) [13-20]
+  TableElementList [20-82]
+    ColumnDefinition [24-40]
+      Identifier(item) [24-28]
+      SimpleColumnSchema [29-40]
+        PathExpression [29-35]
+          Identifier(STRING) [29-35]
+        TypeParameterList [35-39]
+          MaxLiteral() [36-39]
+    ColumnDefinition [44-55]
+      Identifier(sales) [44-49]
+      SimpleColumnSchema [50-55]
+        PathExpression [50-55]
+          Identifier(INT64) [50-55]
+    ColumnDefinition [59-79]
+      Identifier(category) [59-67]
+      SimpleColumnSchema [68-79]
+        PathExpression [68-74]
+          Identifier(STRING) [68-74]
+        TypeParameterList [74-78]
+          MaxLiteral() [75-78]
+  SpannerTableOptions [83-108]
+    PrimaryKey(ENFORCED) [83-108]
+      PrimaryKeyElementList [95-108]
+        PrimaryKeyElement(ASC) [96-100]
+          Identifier(item) [96-100]
+        PrimaryKeyElement(ASC) [102-107]
+          Identifier(sales) [102-107]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#query_comparison
+INSERT INTO Produce (item, sales, category) VALUES
+  ('apples', 2, 'fruit'),
+  ('carrots', 8, 'vegetable'),
+  ('apples', 7, 'fruit'),
+  ('bananas', 5, 'fruit')
+--
+InsertStatement [0-159]
+  PathExpression [12-19]
+    Identifier(Produce) [12-19]
+  ColumnList [20-43]
+    Identifier(item) [21-25]
+    Identifier(sales) [27-32]
+    Identifier(category) [34-42]
+  InsertValuesRowList [44-159]
+    InsertValuesRow [53-75]
+      StringLiteral [54-62]
+        StringLiteralComponent('apples') [54-62]
+      IntLiteral(2) [64-65]
+      StringLiteral [67-74]
+        StringLiteralComponent('fruit') [67-74]
+    InsertValuesRow [79-106]
+      StringLiteral [80-89]
+        StringLiteralComponent('carrots') [80-89]
+      IntLiteral(8) [91-92]
+      StringLiteral [94-105]
+        StringLiteralComponent('vegetable') [94-105]
+    InsertValuesRow [110-132]
+      StringLiteral [111-119]
+        StringLiteralComponent('apples') [111-119]
+      IntLiteral(7) [121-122]
+      StringLiteral [124-131]
+        StringLiteralComponent('fruit') [124-131]
+    InsertValuesRow [136-159]
+      StringLiteral [137-146]
+        StringLiteralComponent('bananas') [137-146]
+      IntLiteral(5) [148-149]
+      StringLiteral [151-158]
+        StringLiteralComponent('fruit') [151-158]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#query_comparison
+SELECT * FROM Produce
+--
+QueryStatement [0-21]
+  Query [0-21]
+    Select [0-21]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-21]
+        TablePathExpression [14-21]
+          PathExpression [14-21]
+            Identifier(Produce) [14-21]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#query_comparison
+FROM Produce
+|> WHERE
+    item != 'bananas'
+    AND category IN ('fruit', 'nut')
+|> AGGREGATE COUNT(*) AS num_items, SUM(sales) AS total_sales
+   GROUP BY item
+|> ORDER BY item DESC
+--
+QueryStatement [0-181]
+  Query [0-181]
+    FromQuery [0-12]
+      FromClause [0-12]
+        TablePathExpression [5-12]
+          PathExpression [5-12]
+            Identifier(Produce) [5-12]
+    PipeWhere [13-80]
+      WhereClause [16-80]
+        AndExpr [26-80]
+          BinaryExpression(!=) [26-43]
+            PathExpression [26-30]
+              Identifier(item) [26-30]
+            StringLiteral [34-43]
+              StringLiteralComponent('bananas') [34-43]
+          InExpression(IN) [52-80]
+            PathExpression [52-60]
+              Identifier(category) [52-60]
+            Location [61-63]
+            InList [65-79]
+              StringLiteral [65-72]
+                StringLiteralComponent('fruit') [65-72]
+              StringLiteral [74-79]
+                StringLiteralComponent('nut') [74-79]
+    PipeAggregate [81-159]
+      Select [84-159]
+        SelectList [94-142]
+          SelectColumn [94-115]
+            FunctionCall [94-102]
+              PathExpression [94-99]
+                Identifier(COUNT) [94-99]
+              Star(*) [100-101]
+            Alias [103-115]
+              Identifier(num_items) [106-115]
+          SelectColumn [117-142]
+            FunctionCall [117-127]
+              PathExpression [117-120]
+                Identifier(SUM) [117-120]
+              PathExpression [121-126]
+                Identifier(sales) [121-126]
+            Alias [128-142]
+              Identifier(total_sales) [131-142]
+        GroupBy [146-159]
+          GroupingItem [155-159]
+            PathExpression [155-159]
+              Identifier(item) [155-159]
+    PipeOrderBy [160-181]
+      OrderBy [163-181]
+        OrderingExpression(DESC) [172-181]
+          PathExpression [172-176]
+            Identifier(item) [172-176]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#from_queries
+FROM Produce
+--
+QueryStatement [0-12]
+  Query [0-12]
+    FromQuery [0-12]
+      FromClause [0-12]
+        TablePathExpression [5-12]
+          PathExpression [5-12]
+            Identifier(Produce) [5-12]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#from_queries
+-- Join tables in the FROM clause and then apply pipe operators.
+FROM
+  Produce AS p1
+  JOIN Produce AS p2
+    USING (item)
+|> WHERE item = 'bananas'
+|> SELECT p1.item, p2.sales
+--
+QueryStatement [65-177]
+  Query [65-177]
+    FromQuery [65-123]
+      FromClause [65-123]
+        Join [72-123]
+          TablePathExpression [72-85]
+            PathExpression [72-79]
+              Identifier(Produce) [72-79]
+            Alias [80-85]
+              Identifier(p1) [83-85]
+          Location [88-92]
+          TablePathExpression [93-106]
+            PathExpression [93-100]
+              Identifier(Produce) [93-100]
+            Alias [101-106]
+              Identifier(p2) [104-106]
+          UsingClause [111-123]
+            Identifier(item) [118-122]
+    PipeWhere [124-149]
+      WhereClause [127-149]
+        BinaryExpression(=) [133-149]
+          PathExpression [133-137]
+            Identifier(item) [133-137]
+          StringLiteral [140-149]
+            StringLiteralComponent('bananas') [140-149]
+    PipeSelect [150-177]
+      Select [153-177]
+        SelectList [160-177]
+          SelectColumn [160-167]
+            PathExpression [160-167]
+              Identifier(p1) [160-162]
+              Identifier(item) [163-167]
+          SelectColumn [169-177]
+            PathExpression [169-177]
+              Identifier(p2) [169-171]
+              Identifier(sales) [172-177]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#select_pipe_operator
+FROM (SELECT 'apples' AS item, 2 AS sales)
+|> SELECT item AS fruit_name
+--
+QueryStatement [0-71]
+  Query [0-71]
+    FromQuery [0-42]
+      FromClause [0-42]
+        TableSubquery [5-42]
+          Query [6-41]
+            Select [6-41]
+              SelectList [13-41]
+                SelectColumn [13-29]
+                  StringLiteral [13-21]
+                    StringLiteralComponent('apples') [13-21]
+                  Alias [22-29]
+                    Identifier(item) [25-29]
+                SelectColumn [31-41]
+                  IntLiteral(2) [31-32]
+                  Alias [33-41]
+                    Identifier(sales) [36-41]
+    PipeSelect [43-71]
+      Select [46-71]
+        SelectList [53-71]
+          SelectColumn [53-71]
+            PathExpression [53-57]
+              Identifier(item) [53-57]
+            Alias [58-71]
+              Identifier(fruit_name) [61-71]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#extend_pipe_operator
+(
+  SELECT 'apples' AS item, 2 AS sales
+  UNION ALL
+  SELECT 'bananas' AS item, 8 AS sales
+)
+|> EXTEND item IN ('bananas', 'lemons') AS is_yellow
+--
+QueryStatement [0-145]
+  Query [0-145]
+    Query [4-90]
+      SetOperation(UNION ALL) [4-90]
+        SetOperationMetadataList [42-51]
+          SetOperationMetadata [42-51]
+            SetOperationType [42-47]
+            SetOperationAllOrDistinct [48-51]
+        Select [4-39]
+          SelectList [11-39]
+            SelectColumn [11-27]
+              StringLiteral [11-19]
+                StringLiteralComponent('apples') [11-19]
+              Alias [20-27]
+                Identifier(item) [23-27]
+            SelectColumn [29-39]
+              IntLiteral(2) [29-30]
+              Alias [31-39]
+                Identifier(sales) [34-39]
+        Select [54-90]
+          SelectList [61-90]
+            SelectColumn [61-78]
+              StringLiteral [61-70]
+                StringLiteralComponent('bananas') [61-70]
+              Alias [71-78]
+                Identifier(item) [74-78]
+            SelectColumn [80-90]
+              IntLiteral(8) [80-81]
+              Alias [82-90]
+                Identifier(sales) [85-90]
+    PipeExtend [93-145]
+      Select [96-145]
+        SelectList [103-145]
+          SelectColumn [103-145]
+            InExpression(IN) [103-132]
+              PathExpression [103-107]
+                Identifier(item) [103-107]
+              Location [108-110]
+              InList [112-131]
+                StringLiteral [112-121]
+                  StringLiteralComponent('bananas') [112-121]
+                StringLiteral [123-131]
+                  StringLiteralComponent('lemons') [123-131]
+            Alias [133-145]
+              Identifier(is_yellow) [136-145]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#set_pipe_operator
+(
+  SELECT 1 AS x, 11 AS y
+  UNION ALL
+  SELECT 2 AS x, 22 AS y
+)
+|> SET x = x * x, y = 3
+--
+QueryStatement [0-89]
+  Query [0-89]
+    Query [4-63]
+      SetOperation(UNION ALL) [4-63]
+        SetOperationMetadataList [29-38]
+          SetOperationMetadata [29-38]
+            SetOperationType [29-34]
+            SetOperationAllOrDistinct [35-38]
+        Select [4-26]
+          SelectList [11-26]
+            SelectColumn [11-17]
+              IntLiteral(1) [11-12]
+              Alias [13-17]
+                Identifier(x) [16-17]
+            SelectColumn [19-26]
+              IntLiteral(11) [19-21]
+              Alias [22-26]
+                Identifier(y) [25-26]
+        Select [41-63]
+          SelectList [48-63]
+            SelectColumn [48-54]
+              IntLiteral(2) [48-49]
+              Alias [50-54]
+                Identifier(x) [53-54]
+            SelectColumn [56-63]
+              IntLiteral(22) [56-58]
+              Alias [59-63]
+                Identifier(y) [62-63]
+    PipeSet [66-89]
+      PipeSetItem [73-82]
+        Identifier(x) [73-74]
+        BinaryExpression(*) [77-82]
+          PathExpression [77-78]
+            Identifier(x) [77-78]
+          PathExpression [81-82]
+            Identifier(x) [81-82]
+      PipeSetItem [84-89]
+        Identifier(y) [84-85]
+        IntLiteral(3) [88-89]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#set_pipe_operator
+FROM (SELECT 2 AS x, 3 AS y) AS t
+|> SET x = x * x, y = 8
+|> SELECT t.x AS original_x, x, y
+--
+QueryStatement [0-91]
+  Query [0-91]
+    FromQuery [0-33]
+      FromClause [0-33]
+        TableSubquery [5-33]
+          Query [6-27]
+            Select [6-27]
+              SelectList [13-27]
+                SelectColumn [13-19]
+                  IntLiteral(2) [13-14]
+                  Alias [15-19]
+                    Identifier(x) [18-19]
+                SelectColumn [21-27]
+                  IntLiteral(3) [21-22]
+                  Alias [23-27]
+                    Identifier(y) [26-27]
+          Alias [29-33]
+            Identifier(t) [32-33]
+    PipeSet [34-57]
+      PipeSetItem [41-50]
+        Identifier(x) [41-42]
+        BinaryExpression(*) [45-50]
+          PathExpression [45-46]
+            Identifier(x) [45-46]
+          PathExpression [49-50]
+            Identifier(x) [49-50]
+      PipeSetItem [52-57]
+        Identifier(y) [52-53]
+        IntLiteral(8) [56-57]
+    PipeSelect [58-91]
+      Select [61-91]
+        SelectList [68-91]
+          SelectColumn [68-85]
+            PathExpression [68-71]
+              Identifier(t) [68-69]
+              Identifier(x) [70-71]
+            Alias [72-85]
+              Identifier(original_x) [75-85]
+          SelectColumn [87-88]
+            PathExpression [87-88]
+              Identifier(x) [87-88]
+          SelectColumn [90-91]
+            PathExpression [90-91]
+              Identifier(y) [90-91]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#drop_pipe_operator
+SELECT 'apples' AS item, 2 AS sales, 'fruit' AS category
+|> DROP sales, category
+--
+QueryStatement [0-80]
+  Query [0-80]
+    Select [0-56]
+      SelectList [7-56]
+        SelectColumn [7-23]
+          StringLiteral [7-15]
+            StringLiteralComponent('apples') [7-15]
+          Alias [16-23]
+            Identifier(item) [19-23]
+        SelectColumn [25-35]
+          IntLiteral(2) [25-26]
+          Alias [27-35]
+            Identifier(sales) [30-35]
+        SelectColumn [37-56]
+          StringLiteral [37-44]
+            StringLiteralComponent('fruit') [37-44]
+          Alias [45-56]
+            Identifier(category) [48-56]
+    PipeDrop [57-80]
+      IdentifierList [65-80]
+        Identifier(sales) [65-70]
+        Identifier(category) [72-80]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#drop_pipe_operator
+FROM (SELECT 1 AS x, 2 AS y) AS t
+|> DROP x
+|> SELECT t.x AS original_x, y
+--
+QueryStatement [0-74]
+  Query [0-74]
+    FromQuery [0-33]
+      FromClause [0-33]
+        TableSubquery [5-33]
+          Query [6-27]
+            Select [6-27]
+              SelectList [13-27]
+                SelectColumn [13-19]
+                  IntLiteral(1) [13-14]
+                  Alias [15-19]
+                    Identifier(x) [18-19]
+                SelectColumn [21-27]
+                  IntLiteral(2) [21-22]
+                  Alias [23-27]
+                    Identifier(y) [26-27]
+          Alias [29-33]
+            Identifier(t) [32-33]
+    PipeDrop [34-43]
+      IdentifierList [42-43]
+        Identifier(x) [42-43]
+    PipeSelect [44-74]
+      Select [47-74]
+        SelectList [54-74]
+          SelectColumn [54-71]
+            PathExpression [54-57]
+              Identifier(t) [54-55]
+              Identifier(x) [56-57]
+            Alias [58-71]
+              Identifier(original_x) [61-71]
+          SelectColumn [73-74]
+            PathExpression [73-74]
+              Identifier(y) [73-74]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#rename_pipe_operator
+SELECT 1 AS x, 2 AS y, 3 AS z
+|> AS t
+|> RENAME y AS renamed_y
+|> SELECT *, t.y AS t_y
+--
+QueryStatement [0-86]
+  Query [0-86]
+    Select [0-29]
+      SelectList [7-29]
+        SelectColumn [7-13]
+          IntLiteral(1) [7-8]
+          Alias [9-13]
+            Identifier(x) [12-13]
+        SelectColumn [15-21]
+          IntLiteral(2) [15-16]
+          Alias [17-21]
+            Identifier(y) [20-21]
+        SelectColumn [23-29]
+          IntLiteral(3) [23-24]
+          Alias [25-29]
+            Identifier(z) [28-29]
+    PipeAs [30-37]
+      Alias [36-37]
+        Identifier(t) [36-37]
+    PipeRename [38-62]
+      PipeRenameItem [48-62]
+        Identifier(y) [48-49]
+        Identifier(renamed_y) [53-62]
+    PipeSelect [63-86]
+      Select [66-86]
+        SelectList [73-86]
+          SelectColumn [73-74]
+            Star(*) [73-74]
+          SelectColumn [76-86]
+            PathExpression [76-79]
+              Identifier(t) [76-77]
+              Identifier(y) [78-79]
+            Alias [80-86]
+              Identifier(t_y) [83-86]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#as_pipe_operator
+|> AS alias
+--
+SubpipelineStatement [0-11]
+  Subpipeline [0-11]
+    PipeAs [0-11]
+      Alias [6-11]
+        Identifier(alias) [6-11]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#as_pipe_operator
+(
+  SELECT "000123" AS id, "apples" AS item, 2 AS sales
+  UNION ALL
+  SELECT "000456" AS id, "bananas" AS item, 5 AS sales
+) AS sales_table
+|> AGGREGATE SUM(sales) AS total_sales GROUP BY id, item
+-- AGGREGATE creates an output table, so the sales_table alias is now out of
+-- scope. Add a t1 alias so the join can refer to its id column.
+|> AS t1
+|> JOIN (SELECT 456 AS id, "yellow" AS color) AS t2
+   ON CAST(t1.id AS INT64) = t2.id
+|> SELECT t2.id, total_sales, color
+--
+QueryStatement [0-470]
+  Query [0-470]
+    AliasedQueryExpression [0-139]
+      Query [4-122]
+        SetOperation(UNION ALL) [4-122]
+          SetOperationMetadataList [58-67]
+            SetOperationMetadata [58-67]
+              SetOperationType [58-63]
+              SetOperationAllOrDistinct [64-67]
+          Select [4-55]
+            SelectList [11-55]
+              SelectColumn [11-25]
+                StringLiteral [11-19]
+                  StringLiteralComponent("000123") [11-19]
+                Alias [20-25]
+                  Identifier(id) [23-25]
+              SelectColumn [27-43]
+                StringLiteral [27-35]
+                  StringLiteralComponent("apples") [27-35]
+                Alias [36-43]
+                  Identifier(item) [39-43]
+              SelectColumn [45-55]
+                IntLiteral(2) [45-46]
+                Alias [47-55]
+                  Identifier(sales) [50-55]
+          Select [70-122]
+            SelectList [77-122]
+              SelectColumn [77-91]
+                StringLiteral [77-85]
+                  StringLiteralComponent("000456") [77-85]
+                Alias [86-91]
+                  Identifier(id) [89-91]
+              SelectColumn [93-110]
+                StringLiteral [93-102]
+                  StringLiteralComponent("bananas") [93-102]
+                Alias [103-110]
+                  Identifier(item) [106-110]
+              SelectColumn [112-122]
+                IntLiteral(5) [112-113]
+                Alias [114-122]
+                  Identifier(sales) [117-122]
+      Alias [125-139]
+        Identifier(sales_table) [128-139]
+    PipeAggregate [140-196]
+      Select [143-196]
+        SelectList [153-178]
+          SelectColumn [153-178]
+            FunctionCall [153-163]
+              PathExpression [153-156]
+                Identifier(SUM) [153-156]
+              PathExpression [157-162]
+                Identifier(sales) [157-162]
+            Alias [164-178]
+              Identifier(total_sales) [167-178]
+        GroupBy [179-196]
+          GroupingItem [188-190]
+            PathExpression [188-190]
+              Identifier(id) [188-190]
+          GroupingItem [192-196]
+            PathExpression [192-196]
+              Identifier(item) [192-196]
+    PipeAs [339-347]
+      Alias [345-347]
+        Identifier(t1) [345-347]
+    PipeJoin [348-434]
+      Join [350-434]
+        PipeJoinLhsPlaceholder [351-434]
+        Location [351-355]
+        TableSubquery [356-399]
+          Query [357-392]
+            Select [357-392]
+              SelectList [364-392]
+                SelectColumn [364-373]
+                  IntLiteral(456) [364-367]
+                  Alias [368-373]
+                    Identifier(id) [371-373]
+                SelectColumn [375-392]
+                  StringLiteral [375-383]
+                    StringLiteralComponent("yellow") [375-383]
+                  Alias [384-392]
+                    Identifier(color) [387-392]
+          Alias [394-399]
+            Identifier(t2) [397-399]
+        OnClause [403-434]
+          BinaryExpression(=) [406-434]
+            CastExpression [406-426]
+              PathExpression [411-416]
+                Identifier(t1) [411-413]
+                Identifier(id) [414-416]
+              SimpleType [420-425]
+                PathExpression [420-425]
+                  Identifier(INT64) [420-425]
+            PathExpression [429-434]
+              Identifier(t2) [429-431]
+              Identifier(id) [432-434]
+    PipeSelect [435-470]
+      Select [438-470]
+        SelectList [445-470]
+          SelectColumn [445-450]
+            PathExpression [445-450]
+              Identifier(t2) [445-447]
+              Identifier(id) [448-450]
+          SelectColumn [452-463]
+            PathExpression [452-463]
+              Identifier(total_sales) [452-463]
+          SelectColumn [465-470]
+            PathExpression [465-470]
+              Identifier(color) [465-470]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#where_pipe_operator
+|> WHERE boolean_expression
+--
+SubpipelineStatement [0-27]
+  Subpipeline [0-27]
+    PipeWhere [0-27]
+      WhereClause [3-27]
+        PathExpression [9-27]
+          Identifier(boolean_expression) [9-27]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#where_pipe_operator
+(
+  SELECT 'apples' AS item, 2 AS sales
+  UNION ALL
+  SELECT 'bananas' AS item, 5 AS sales
+  UNION ALL
+  SELECT 'carrots' AS item, 8 AS sales
+)
+|> WHERE sales >= 3
+--
+QueryStatement [0-163]
+  Query [0-163]
+    Query [4-141]
+      SetOperation(UNION ALL) [4-141]
+        SetOperationMetadataList [42-102]
+          SetOperationMetadata [42-51]
+            SetOperationType [42-47]
+            SetOperationAllOrDistinct [48-51]
+          SetOperationMetadata [93-102]
+            SetOperationType [93-98]
+            SetOperationAllOrDistinct [99-102]
+        Select [4-39]
+          SelectList [11-39]
+            SelectColumn [11-27]
+              StringLiteral [11-19]
+                StringLiteralComponent('apples') [11-19]
+              Alias [20-27]
+                Identifier(item) [23-27]
+            SelectColumn [29-39]
+              IntLiteral(2) [29-30]
+              Alias [31-39]
+                Identifier(sales) [34-39]
+        Select [54-90]
+          SelectList [61-90]
+            SelectColumn [61-78]
+              StringLiteral [61-70]
+                StringLiteralComponent('bananas') [61-70]
+              Alias [71-78]
+                Identifier(item) [74-78]
+            SelectColumn [80-90]
+              IntLiteral(5) [80-81]
+              Alias [82-90]
+                Identifier(sales) [85-90]
+        Select [105-141]
+          SelectList [112-141]
+            SelectColumn [112-129]
+              StringLiteral [112-121]
+                StringLiteralComponent('carrots') [112-121]
+              Alias [122-129]
+                Identifier(item) [125-129]
+            SelectColumn [131-141]
+              IntLiteral(8) [131-132]
+              Alias [133-141]
+                Identifier(sales) [136-141]
+    PipeWhere [144-163]
+      WhereClause [147-163]
+        BinaryExpression(>=) [153-163]
+          PathExpression [153-158]
+            Identifier(sales) [153-158]
+          IntLiteral(3) [162-163]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#aggregate_pipe_operator
+-- Full-table aggregation
+(
+  SELECT 'apples' AS item, 2 AS sales
+  UNION ALL
+  SELECT 'bananas' AS item, 5 AS sales
+  UNION ALL
+  SELECT 'apples' AS item, 7 AS sales
+)
+|> AGGREGATE COUNT(*) AS num_items, SUM(sales) AS total_sales
+--
+QueryStatement [26-230]
+  Query [26-230]
+    Query [30-166]
+      SetOperation(UNION ALL) [30-166]
+        SetOperationMetadataList [68-128]
+          SetOperationMetadata [68-77]
+            SetOperationType [68-73]
+            SetOperationAllOrDistinct [74-77]
+          SetOperationMetadata [119-128]
+            SetOperationType [119-124]
+            SetOperationAllOrDistinct [125-128]
+        Select [30-65]
+          SelectList [37-65]
+            SelectColumn [37-53]
+              StringLiteral [37-45]
+                StringLiteralComponent('apples') [37-45]
+              Alias [46-53]
+                Identifier(item) [49-53]
+            SelectColumn [55-65]
+              IntLiteral(2) [55-56]
+              Alias [57-65]
+                Identifier(sales) [60-65]
+        Select [80-116]
+          SelectList [87-116]
+            SelectColumn [87-104]
+              StringLiteral [87-96]
+                StringLiteralComponent('bananas') [87-96]
+              Alias [97-104]
+                Identifier(item) [100-104]
+            SelectColumn [106-116]
+              IntLiteral(5) [106-107]
+              Alias [108-116]
+                Identifier(sales) [111-116]
+        Select [131-166]
+          SelectList [138-166]
+            SelectColumn [138-154]
+              StringLiteral [138-146]
+                StringLiteralComponent('apples') [138-146]
+              Alias [147-154]
+                Identifier(item) [150-154]
+            SelectColumn [156-166]
+              IntLiteral(7) [156-157]
+              Alias [158-166]
+                Identifier(sales) [161-166]
+    PipeAggregate [169-230]
+      Select [172-230]
+        SelectList [182-230]
+          SelectColumn [182-203]
+            FunctionCall [182-190]
+              PathExpression [182-187]
+                Identifier(COUNT) [182-187]
+              Star(*) [188-189]
+            Alias [191-203]
+              Identifier(num_items) [194-203]
+          SelectColumn [205-230]
+            FunctionCall [205-215]
+              PathExpression [205-208]
+                Identifier(SUM) [205-208]
+              PathExpression [209-214]
+                Identifier(sales) [209-214]
+            Alias [216-230]
+              Identifier(total_sales) [219-230]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#aggregate_pipe_operator
+-- Aggregation with grouping
+(
+  SELECT 'apples' AS item, 2 AS sales
+  UNION ALL
+  SELECT 'bananas' AS item, 5 AS sales
+  UNION ALL
+  SELECT 'apples' AS item, 7 AS sales
+)
+|> AGGREGATE COUNT(*) AS num_items, SUM(sales) AS total_sales
+   GROUP BY item
+--
+QueryStatement [29-250]
+  Query [29-250]
+    Query [33-169]
+      SetOperation(UNION ALL) [33-169]
+        SetOperationMetadataList [71-131]
+          SetOperationMetadata [71-80]
+            SetOperationType [71-76]
+            SetOperationAllOrDistinct [77-80]
+          SetOperationMetadata [122-131]
+            SetOperationType [122-127]
+            SetOperationAllOrDistinct [128-131]
+        Select [33-68]
+          SelectList [40-68]
+            SelectColumn [40-56]
+              StringLiteral [40-48]
+                StringLiteralComponent('apples') [40-48]
+              Alias [49-56]
+                Identifier(item) [52-56]
+            SelectColumn [58-68]
+              IntLiteral(2) [58-59]
+              Alias [60-68]
+                Identifier(sales) [63-68]
+        Select [83-119]
+          SelectList [90-119]
+            SelectColumn [90-107]
+              StringLiteral [90-99]
+                StringLiteralComponent('bananas') [90-99]
+              Alias [100-107]
+                Identifier(item) [103-107]
+            SelectColumn [109-119]
+              IntLiteral(5) [109-110]
+              Alias [111-119]
+                Identifier(sales) [114-119]
+        Select [134-169]
+          SelectList [141-169]
+            SelectColumn [141-157]
+              StringLiteral [141-149]
+                StringLiteralComponent('apples') [141-149]
+              Alias [150-157]
+                Identifier(item) [153-157]
+            SelectColumn [159-169]
+              IntLiteral(7) [159-160]
+              Alias [161-169]
+                Identifier(sales) [164-169]
+    PipeAggregate [172-250]
+      Select [175-250]
+        SelectList [185-233]
+          SelectColumn [185-206]
+            FunctionCall [185-193]
+              PathExpression [185-190]
+                Identifier(COUNT) [185-190]
+              Star(*) [191-192]
+            Alias [194-206]
+              Identifier(num_items) [197-206]
+          SelectColumn [208-233]
+            FunctionCall [208-218]
+              PathExpression [208-211]
+                Identifier(SUM) [208-211]
+              PathExpression [212-217]
+                Identifier(sales) [212-217]
+            Alias [219-233]
+              Identifier(total_sales) [222-233]
+        GroupBy [237-250]
+          GroupingItem [246-250]
+            PathExpression [246-250]
+              Identifier(item) [246-250]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#shorthand_order_pipe_syntax
+-- Order by all grouping columns using GROUP AND ORDER BY.
+FROM Produce
+|> AGGREGATE SUM(sales) AS total_sales
+   GROUP AND ORDER BY category, item DESC
+--
+QueryStatement [59-152]
+  Query [59-152]
+    FromQuery [59-71]
+      FromClause [59-71]
+        TablePathExpression [64-71]
+          PathExpression [64-71]
+            Identifier(Produce) [64-71]
+    PipeAggregate [72-152]
+      Select [75-152]
+        SelectList [85-110]
+          SelectColumn [85-110]
+            FunctionCall [85-95]
+              PathExpression [85-88]
+                Identifier(SUM) [85-88]
+              PathExpression [89-94]
+                Identifier(sales) [89-94]
+            Alias [96-110]
+              Identifier(total_sales) [99-110]
+        GroupBy(and_order_by=true) [114-152]
+          GroupingItem [133-141]
+            PathExpression [133-141]
+              Identifier(category) [133-141]
+          GroupingItem [143-152]
+            PathExpression [143-147]
+              Identifier(item) [143-147]
+            GroupingItemOrder(DESC) [148-152]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#shorthand_order_pipe_syntax
+--Order by columns using ORDER BY after performing aggregation.
+FROM Produce
+|> AGGREGATE SUM(sales) AS total_sales
+   GROUP BY category, item
+|> ORDER BY category, item DESC
+--
+QueryStatement [64-174]
+  Query [64-174]
+    FromQuery [64-76]
+      FromClause [64-76]
+        TablePathExpression [69-76]
+          PathExpression [69-76]
+            Identifier(Produce) [69-76]
+    PipeAggregate [77-142]
+      Select [80-142]
+        SelectList [90-115]
+          SelectColumn [90-115]
+            FunctionCall [90-100]
+              PathExpression [90-93]
+                Identifier(SUM) [90-93]
+              PathExpression [94-99]
+                Identifier(sales) [94-99]
+            Alias [101-115]
+              Identifier(total_sales) [104-115]
+        GroupBy [119-142]
+          GroupingItem [128-136]
+            PathExpression [128-136]
+              Identifier(category) [128-136]
+          GroupingItem [138-142]
+            PathExpression [138-142]
+              Identifier(item) [138-142]
+    PipeOrderBy [143-174]
+      OrderBy [146-174]
+        OrderingExpression(ASC) [155-163]
+          PathExpression [155-163]
+            Identifier(category) [155-163]
+        OrderingExpression(DESC) [165-174]
+          PathExpression [165-169]
+            Identifier(item) [165-169]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#shorthand_order_pipe_syntax
+FROM Produce
+|> AGGREGATE SUM(sales) AS total_sales ASC
+   GROUP BY item, category DESC
+--
+QueryStatement [0-87]
+  Query [0-87]
+    FromQuery [0-12]
+      FromClause [0-12]
+        TablePathExpression [5-12]
+          PathExpression [5-12]
+            Identifier(Produce) [5-12]
+    PipeAggregate [13-87]
+      Select [16-87]
+        SelectList [26-55]
+          SelectColumn [26-55]
+            FunctionCall [26-36]
+              PathExpression [26-29]
+                Identifier(SUM) [26-29]
+              PathExpression [30-35]
+                Identifier(sales) [30-35]
+            Alias [37-51]
+              Identifier(total_sales) [40-51]
+            GroupingItemOrder(ASC) [52-55]
+        GroupBy [59-87]
+          GroupingItem [68-72]
+            PathExpression [68-72]
+              Identifier(item) [68-72]
+          GroupingItem [74-87]
+            PathExpression [74-82]
+              Identifier(category) [74-82]
+            GroupingItemOrder(DESC) [83-87]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#shorthand_order_pipe_syntax
+-- Order by specified grouping and aggregate columns.
+FROM Produce
+|> AGGREGATE SUM(sales) AS total_sales
+   GROUP BY item, category
+|> ORDER BY category DESC, total_sales
+--
+QueryStatement [54-171]
+  Query [54-171]
+    FromQuery [54-66]
+      FromClause [54-66]
+        TablePathExpression [59-66]
+          PathExpression [59-66]
+            Identifier(Produce) [59-66]
+    PipeAggregate [67-132]
+      Select [70-132]
+        SelectList [80-105]
+          SelectColumn [80-105]
+            FunctionCall [80-90]
+              PathExpression [80-83]
+                Identifier(SUM) [80-83]
+              PathExpression [84-89]
+                Identifier(sales) [84-89]
+            Alias [91-105]
+              Identifier(total_sales) [94-105]
+        GroupBy [109-132]
+          GroupingItem [118-122]
+            PathExpression [118-122]
+              Identifier(item) [118-122]
+          GroupingItem [124-132]
+            PathExpression [124-132]
+              Identifier(category) [124-132]
+    PipeOrderBy [133-171]
+      OrderBy [136-171]
+        OrderingExpression(DESC) [145-158]
+          PathExpression [145-153]
+            Identifier(category) [145-153]
+        OrderingExpression(ASC) [160-171]
+          PathExpression [160-171]
+            Identifier(total_sales) [160-171]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#join_pipe_operator
+(
+  SELECT 'apples' AS item, 2 AS sales
+  UNION ALL
+  SELECT 'bananas' AS item, 5 AS sales
+)
+|> AS produce_sales
+|> LEFT JOIN
+     (
+       SELECT "apples" AS item, 123 AS id
+     ) AS produce_data
+   ON produce_sales.item = produce_data.item
+|> SELECT produce_sales.item, sales, id
+--
+QueryStatement [0-282]
+  Query [0-282]
+    Query [4-90]
+      SetOperation(UNION ALL) [4-90]
+        SetOperationMetadataList [42-51]
+          SetOperationMetadata [42-51]
+            SetOperationType [42-47]
+            SetOperationAllOrDistinct [48-51]
+        Select [4-39]
+          SelectList [11-39]
+            SelectColumn [11-27]
+              StringLiteral [11-19]
+                StringLiteralComponent('apples') [11-19]
+              Alias [20-27]
+                Identifier(item) [23-27]
+            SelectColumn [29-39]
+              IntLiteral(2) [29-30]
+              Alias [31-39]
+                Identifier(sales) [34-39]
+        Select [54-90]
+          SelectList [61-90]
+            SelectColumn [61-78]
+              StringLiteral [61-70]
+                StringLiteralComponent('bananas') [61-70]
+              Alias [71-78]
+                Identifier(item) [74-78]
+            SelectColumn [80-90]
+              IntLiteral(5) [80-81]
+              Alias [82-90]
+                Identifier(sales) [85-90]
+    PipeAs [93-112]
+      Alias [99-112]
+        Identifier(produce_sales) [99-112]
+    PipeJoin [113-242]
+      Join(LEFT) [115-242]
+        PipeJoinLhsPlaceholder [116-242]
+        Location [116-125]
+        TableSubquery [131-197]
+          Query [140-174]
+            Select [140-174]
+              SelectList [147-174]
+                SelectColumn [147-163]
+                  StringLiteral [147-155]
+                    StringLiteralComponent("apples") [147-155]
+                  Alias [156-163]
+                    Identifier(item) [159-163]
+                SelectColumn [165-174]
+                  IntLiteral(123) [165-168]
+                  Alias [169-174]
+                    Identifier(id) [172-174]
+          Alias [182-197]
+            Identifier(produce_data) [185-197]
+        OnClause [201-242]
+          BinaryExpression(=) [204-242]
+            PathExpression [204-222]
+              Identifier(produce_sales) [204-217]
+              Identifier(item) [218-222]
+            PathExpression [225-242]
+              Identifier(produce_data) [225-237]
+              Identifier(item) [238-242]
+    PipeSelect [243-282]
+      Select [246-282]
+        SelectList [253-282]
+          SelectColumn [253-271]
+            PathExpression [253-271]
+              Identifier(produce_sales) [253-266]
+              Identifier(item) [267-271]
+          SelectColumn [273-278]
+            PathExpression [273-278]
+              Identifier(sales) [273-278]
+          SelectColumn [280-282]
+            PathExpression [280-282]
+              Identifier(id) [280-282]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#order_by_pipe_operator
+(
+  SELECT 1 AS x
+  UNION ALL
+  SELECT 3 AS x
+  UNION ALL
+  SELECT 2 AS x
+)
+|> ORDER BY x DESC
+--
+QueryStatement [0-94]
+  Query [0-94]
+    Query [4-73]
+      SetOperation(UNION ALL) [4-73]
+        SetOperationMetadataList [20-57]
+          SetOperationMetadata [20-29]
+            SetOperationType [20-25]
+            SetOperationAllOrDistinct [26-29]
+          SetOperationMetadata [48-57]
+            SetOperationType [48-53]
+            SetOperationAllOrDistinct [54-57]
+        Select [4-17]
+          SelectList [11-17]
+            SelectColumn [11-17]
+              IntLiteral(1) [11-12]
+              Alias [13-17]
+                Identifier(x) [16-17]
+        Select [32-45]
+          SelectList [39-45]
+            SelectColumn [39-45]
+              IntLiteral(3) [39-40]
+              Alias [41-45]
+                Identifier(x) [44-45]
+        Select [60-73]
+          SelectList [67-73]
+            SelectColumn [67-73]
+              IntLiteral(2) [67-68]
+              Alias [69-73]
+                Identifier(x) [72-73]
+    PipeOrderBy [76-94]
+      OrderBy [79-94]
+        OrderingExpression(DESC) [88-94]
+          PathExpression [88-89]
+            Identifier(x) [88-89]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#limit_pipe_operator
+(
+  SELECT 'apples' AS item, 2 AS sales
+  UNION ALL
+  SELECT 'bananas' AS item, 5 AS sales
+  UNION ALL
+  SELECT 'carrots' AS item, 8 AS sales
+)
+|> ORDER BY item
+|> LIMIT 1
+--
+QueryStatement [0-171]
+  Query [0-171]
+    Query [4-141]
+      SetOperation(UNION ALL) [4-141]
+        SetOperationMetadataList [42-102]
+          SetOperationMetadata [42-51]
+            SetOperationType [42-47]
+            SetOperationAllOrDistinct [48-51]
+          SetOperationMetadata [93-102]
+            SetOperationType [93-98]
+            SetOperationAllOrDistinct [99-102]
+        Select [4-39]
+          SelectList [11-39]
+            SelectColumn [11-27]
+              StringLiteral [11-19]
+                StringLiteralComponent('apples') [11-19]
+              Alias [20-27]
+                Identifier(item) [23-27]
+            SelectColumn [29-39]
+              IntLiteral(2) [29-30]
+              Alias [31-39]
+                Identifier(sales) [34-39]
+        Select [54-90]
+          SelectList [61-90]
+            SelectColumn [61-78]
+              StringLiteral [61-70]
+                StringLiteralComponent('bananas') [61-70]
+              Alias [71-78]
+                Identifier(item) [74-78]
+            SelectColumn [80-90]
+              IntLiteral(5) [80-81]
+              Alias [82-90]
+                Identifier(sales) [85-90]
+        Select [105-141]
+          SelectList [112-141]
+            SelectColumn [112-129]
+              StringLiteral [112-121]
+                StringLiteralComponent('carrots') [112-121]
+              Alias [122-129]
+                Identifier(item) [125-129]
+            SelectColumn [131-141]
+              IntLiteral(8) [131-132]
+              Alias [133-141]
+                Identifier(sales) [136-141]
+    PipeOrderBy [144-160]
+      OrderBy [147-160]
+        OrderingExpression(ASC) [156-160]
+          PathExpression [156-160]
+            Identifier(item) [156-160]
+    PipeLimitOffset [161-171]
+      LimitOffset [164-171]
+        Limit [164-171]
+          IntLiteral(1) [170-171]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#limit_pipe_operator
+(
+  SELECT 'apples' AS item, 2 AS sales
+  UNION ALL
+  SELECT 'bananas' AS item, 5 AS sales
+  UNION ALL
+  SELECT 'carrots' AS item, 8 AS sales
+)
+|> ORDER BY item
+|> LIMIT 1 OFFSET 2
+--
+QueryStatement [0-180]
+  Query [0-180]
+    Query [4-141]
+      SetOperation(UNION ALL) [4-141]
+        SetOperationMetadataList [42-102]
+          SetOperationMetadata [42-51]
+            SetOperationType [42-47]
+            SetOperationAllOrDistinct [48-51]
+          SetOperationMetadata [93-102]
+            SetOperationType [93-98]
+            SetOperationAllOrDistinct [99-102]
+        Select [4-39]
+          SelectList [11-39]
+            SelectColumn [11-27]
+              StringLiteral [11-19]
+                StringLiteralComponent('apples') [11-19]
+              Alias [20-27]
+                Identifier(item) [23-27]
+            SelectColumn [29-39]
+              IntLiteral(2) [29-30]
+              Alias [31-39]
+                Identifier(sales) [34-39]
+        Select [54-90]
+          SelectList [61-90]
+            SelectColumn [61-78]
+              StringLiteral [61-70]
+                StringLiteralComponent('bananas') [61-70]
+              Alias [71-78]
+                Identifier(item) [74-78]
+            SelectColumn [80-90]
+              IntLiteral(5) [80-81]
+              Alias [82-90]
+                Identifier(sales) [85-90]
+        Select [105-141]
+          SelectList [112-141]
+            SelectColumn [112-129]
+              StringLiteral [112-121]
+                StringLiteralComponent('carrots') [112-121]
+              Alias [122-129]
+                Identifier(item) [125-129]
+            SelectColumn [131-141]
+              IntLiteral(8) [131-132]
+              Alias [133-141]
+                Identifier(sales) [136-141]
+    PipeOrderBy [144-160]
+      OrderBy [147-160]
+        OrderingExpression(ASC) [156-160]
+          PathExpression [156-160]
+            Identifier(item) [156-160]
+    PipeLimitOffset [161-180]
+      LimitOffset [164-180]
+        Limit [164-171]
+          IntLiteral(1) [170-171]
+        IntLiteral(2) [179-180]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#union_pipe_operator
+SELECT * FROM UNNEST(ARRAY[1, 2, 3]) AS number
+|> UNION ALL (SELECT 1)
+--
+QueryStatement [0-77]
+  Query [0-77]
+    Select [0-53]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-53]
+        TablePathExpression [14-53]
+          UnnestExpression [14-43]
+            ExpressionWithOptAlias [21-42]
+              ArrayConstructor [21-42]
+                ArrayType [21-33]
+                  SimpleType [27-32]
+                    PathExpression [27-32]
+                      Identifier(INT64) [27-32]
+                IntLiteral(1) [34-35]
+                IntLiteral(2) [37-38]
+                IntLiteral(3) [40-41]
+          Alias [44-53]
+            Identifier(number) [47-53]
+    PipeSetOperation [54-77]
+      SetOperationMetadata [57-66]
+        SetOperationType [57-62]
+        SetOperationAllOrDistinct [63-66]
+      Query [68-76]
+        Select [68-76]
+          SelectList [75-76]
+            SelectColumn [75-76]
+              IntLiteral(1) [75-76]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#union_pipe_operator
+SELECT * FROM UNNEST(ARRAY[1, 2, 3]) AS number
+|> UNION DISTINCT (SELECT 1)
+--
+QueryStatement [0-82]
+  Query [0-82]
+    Select [0-53]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-53]
+        TablePathExpression [14-53]
+          UnnestExpression [14-43]
+            ExpressionWithOptAlias [21-42]
+              ArrayConstructor [21-42]
+                ArrayType [21-33]
+                  SimpleType [27-32]
+                    PathExpression [27-32]
+                      Identifier(INT64) [27-32]
+                IntLiteral(1) [34-35]
+                IntLiteral(2) [37-38]
+                IntLiteral(3) [40-41]
+          Alias [44-53]
+            Identifier(number) [47-53]
+    PipeSetOperation [54-82]
+      SetOperationMetadata [57-71]
+        SetOperationType [57-62]
+        SetOperationAllOrDistinct [63-71]
+      Query [73-81]
+        Select [73-81]
+          SelectList [80-81]
+            SelectColumn [80-81]
+              IntLiteral(1) [80-81]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#union_pipe_operator
+SELECT * FROM UNNEST(ARRAY[1, 2, 3]) AS number
+|> UNION DISTINCT
+    (SELECT 1),
+    (SELECT 2)
+--
+QueryStatement [0-102]
+  Query [0-101]
+    Select [0-53]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-53]
+        TablePathExpression [14-53]
+          UnnestExpression [14-43]
+            ExpressionWithOptAlias [21-42]
+              ArrayConstructor [21-42]
+                ArrayType [21-33]
+                  SimpleType [27-32]
+                    PathExpression [27-32]
+                      Identifier(INT64) [27-32]
+                IntLiteral(1) [34-35]
+                IntLiteral(2) [37-38]
+                IntLiteral(3) [40-41]
+          Alias [44-53]
+            Identifier(number) [47-53]
+    PipeSetOperation [54-101]
+      SetOperationMetadata [57-71]
+        SetOperationType [57-62]
+        SetOperationAllOrDistinct [63-71]
+      Query [77-85]
+        Select [77-85]
+          SelectList [84-85]
+            SelectColumn [84-85]
+              IntLiteral(1) [84-85]
+      Query [93-101]
+        Select [93-101]
+          SelectList [100-101]
+            SelectColumn [100-101]
+              IntLiteral(2) [100-101]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#intersect_pipe_operator
+SELECT * FROM UNNEST(ARRAY[1, 2, 3, 3, 4]) AS number
+|> INTERSECT ALL
+    (SELECT * FROM UNNEST(ARRAY[2, 3, 3, 5]) AS number)
+--
+QueryStatement [0-139]
+  Query [0-139]
+    Select [0-59]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-59]
+        TablePathExpression [14-59]
+          UnnestExpression [14-49]
+            ExpressionWithOptAlias [21-48]
+              ArrayConstructor [21-48]
+                ArrayType [21-33]
+                  SimpleType [27-32]
+                    PathExpression [27-32]
+                      Identifier(INT64) [27-32]
+                IntLiteral(1) [34-35]
+                IntLiteral(2) [37-38]
+                IntLiteral(3) [40-41]
+                IntLiteral(3) [43-44]
+                IntLiteral(4) [46-47]
+          Alias [50-59]
+            Identifier(number) [53-59]
+    PipeSetOperation [60-139]
+      SetOperationMetadata [63-76]
+        SetOperationType [63-72]
+        SetOperationAllOrDistinct [73-76]
+      Query [82-138]
+        Select [82-138]
+          SelectList [89-90]
+            SelectColumn [89-90]
+              Star(*) [89-90]
+          FromClause [91-138]
+            TablePathExpression [96-138]
+              UnnestExpression [96-128]
+                ExpressionWithOptAlias [103-127]
+                  ArrayConstructor [103-127]
+                    ArrayType [103-115]
+                      SimpleType [109-114]
+                        PathExpression [109-114]
+                          Identifier(INT64) [109-114]
+                    IntLiteral(2) [116-117]
+                    IntLiteral(3) [119-120]
+                    IntLiteral(3) [122-123]
+                    IntLiteral(5) [125-126]
+              Alias [129-138]
+                Identifier(number) [132-138]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#intersect_pipe_operator
+SELECT * FROM UNNEST(ARRAY[1, 2, 3, 3, 4]) AS number
+|> INTERSECT DISTINCT
+    (SELECT * FROM UNNEST(ARRAY[2, 3, 3, 5]) AS number)
+--
+QueryStatement [0-144]
+  Query [0-144]
+    Select [0-59]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-59]
+        TablePathExpression [14-59]
+          UnnestExpression [14-49]
+            ExpressionWithOptAlias [21-48]
+              ArrayConstructor [21-48]
+                ArrayType [21-33]
+                  SimpleType [27-32]
+                    PathExpression [27-32]
+                      Identifier(INT64) [27-32]
+                IntLiteral(1) [34-35]
+                IntLiteral(2) [37-38]
+                IntLiteral(3) [40-41]
+                IntLiteral(3) [43-44]
+                IntLiteral(4) [46-47]
+          Alias [50-59]
+            Identifier(number) [53-59]
+    PipeSetOperation [60-144]
+      SetOperationMetadata [63-81]
+        SetOperationType [63-72]
+        SetOperationAllOrDistinct [73-81]
+      Query [87-143]
+        Select [87-143]
+          SelectList [94-95]
+            SelectColumn [94-95]
+              Star(*) [94-95]
+          FromClause [96-143]
+            TablePathExpression [101-143]
+              UnnestExpression [101-133]
+                ExpressionWithOptAlias [108-132]
+                  ArrayConstructor [108-132]
+                    ArrayType [108-120]
+                      SimpleType [114-119]
+                        PathExpression [114-119]
+                          Identifier(INT64) [114-119]
+                    IntLiteral(2) [121-122]
+                    IntLiteral(3) [124-125]
+                    IntLiteral(3) [127-128]
+                    IntLiteral(5) [130-131]
+              Alias [134-143]
+                Identifier(number) [137-143]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#intersect_pipe_operator
+SELECT * FROM UNNEST(ARRAY[1, 2, 3, 3, 4]) AS number
+|> INTERSECT DISTINCT
+    (SELECT * FROM UNNEST(ARRAY[2, 3, 3, 5]) AS number),
+    (SELECT * FROM UNNEST(ARRAY[3, 3, 4, 5]) AS number)
+--
+QueryStatement [0-208]
+  Query [0-207]
+    Select [0-59]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-59]
+        TablePathExpression [14-59]
+          UnnestExpression [14-49]
+            ExpressionWithOptAlias [21-48]
+              ArrayConstructor [21-48]
+                ArrayType [21-33]
+                  SimpleType [27-32]
+                    PathExpression [27-32]
+                      Identifier(INT64) [27-32]
+                IntLiteral(1) [34-35]
+                IntLiteral(2) [37-38]
+                IntLiteral(3) [40-41]
+                IntLiteral(3) [43-44]
+                IntLiteral(4) [46-47]
+          Alias [50-59]
+            Identifier(number) [53-59]
+    PipeSetOperation [60-207]
+      SetOperationMetadata [63-81]
+        SetOperationType [63-72]
+        SetOperationAllOrDistinct [73-81]
+      Query [87-143]
+        Select [87-143]
+          SelectList [94-95]
+            SelectColumn [94-95]
+              Star(*) [94-95]
+          FromClause [96-143]
+            TablePathExpression [101-143]
+              UnnestExpression [101-133]
+                ExpressionWithOptAlias [108-132]
+                  ArrayConstructor [108-132]
+                    ArrayType [108-120]
+                      SimpleType [114-119]
+                        PathExpression [114-119]
+                          Identifier(INT64) [114-119]
+                    IntLiteral(2) [121-122]
+                    IntLiteral(3) [124-125]
+                    IntLiteral(3) [127-128]
+                    IntLiteral(5) [130-131]
+              Alias [134-143]
+                Identifier(number) [137-143]
+      Query [151-207]
+        Select [151-207]
+          SelectList [158-159]
+            SelectColumn [158-159]
+              Star(*) [158-159]
+          FromClause [160-207]
+            TablePathExpression [165-207]
+              UnnestExpression [165-197]
+                ExpressionWithOptAlias [172-196]
+                  ArrayConstructor [172-196]
+                    ArrayType [172-184]
+                      SimpleType [178-183]
+                        PathExpression [178-183]
+                          Identifier(INT64) [178-183]
+                    IntLiteral(3) [185-186]
+                    IntLiteral(3) [188-189]
+                    IntLiteral(4) [191-192]
+                    IntLiteral(5) [194-195]
+              Alias [198-207]
+                Identifier(number) [201-207]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#except_pipe_operator
+SELECT * FROM UNNEST(ARRAY[1, 2, 3, 3, 4]) AS number
+|> EXCEPT ALL
+    (SELECT * FROM UNNEST(ARRAY[1, 2]) AS number)
+|> ORDER BY number
+--
+QueryStatement [0-149]
+  Query [0-149]
+    Select [0-59]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-59]
+        TablePathExpression [14-59]
+          UnnestExpression [14-49]
+            ExpressionWithOptAlias [21-48]
+              ArrayConstructor [21-48]
+                ArrayType [21-33]
+                  SimpleType [27-32]
+                    PathExpression [27-32]
+                      Identifier(INT64) [27-32]
+                IntLiteral(1) [34-35]
+                IntLiteral(2) [37-38]
+                IntLiteral(3) [40-41]
+                IntLiteral(3) [43-44]
+                IntLiteral(4) [46-47]
+          Alias [50-59]
+            Identifier(number) [53-59]
+    PipeSetOperation [60-130]
+      SetOperationMetadata [63-73]
+        SetOperationType [63-69]
+        SetOperationAllOrDistinct [70-73]
+      Query [79-129]
+        Select [79-129]
+          SelectList [86-87]
+            SelectColumn [86-87]
+              Star(*) [86-87]
+          FromClause [88-129]
+            TablePathExpression [93-129]
+              UnnestExpression [93-119]
+                ExpressionWithOptAlias [100-118]
+                  ArrayConstructor [100-118]
+                    ArrayType [100-112]
+                      SimpleType [106-111]
+                        PathExpression [106-111]
+                          Identifier(INT64) [106-111]
+                    IntLiteral(1) [113-114]
+                    IntLiteral(2) [116-117]
+              Alias [120-129]
+                Identifier(number) [123-129]
+    PipeOrderBy [131-149]
+      OrderBy [134-149]
+        OrderingExpression(ASC) [143-149]
+          PathExpression [143-149]
+            Identifier(number) [143-149]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#except_pipe_operator
+SELECT * FROM UNNEST(ARRAY[1, 2, 3, 3, 4]) AS number
+|> EXCEPT DISTINCT
+    (SELECT * FROM UNNEST(ARRAY[1, 2]) AS number)
+|> ORDER BY number
+--
+QueryStatement [0-154]
+  Query [0-154]
+    Select [0-59]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-59]
+        TablePathExpression [14-59]
+          UnnestExpression [14-49]
+            ExpressionWithOptAlias [21-48]
+              ArrayConstructor [21-48]
+                ArrayType [21-33]
+                  SimpleType [27-32]
+                    PathExpression [27-32]
+                      Identifier(INT64) [27-32]
+                IntLiteral(1) [34-35]
+                IntLiteral(2) [37-38]
+                IntLiteral(3) [40-41]
+                IntLiteral(3) [43-44]
+                IntLiteral(4) [46-47]
+          Alias [50-59]
+            Identifier(number) [53-59]
+    PipeSetOperation [60-135]
+      SetOperationMetadata [63-78]
+        SetOperationType [63-69]
+        SetOperationAllOrDistinct [70-78]
+      Query [84-134]
+        Select [84-134]
+          SelectList [91-92]
+            SelectColumn [91-92]
+              Star(*) [91-92]
+          FromClause [93-134]
+            TablePathExpression [98-134]
+              UnnestExpression [98-124]
+                ExpressionWithOptAlias [105-123]
+                  ArrayConstructor [105-123]
+                    ArrayType [105-117]
+                      SimpleType [111-116]
+                        PathExpression [111-116]
+                          Identifier(INT64) [111-116]
+                    IntLiteral(1) [118-119]
+                    IntLiteral(2) [121-122]
+              Alias [125-134]
+                Identifier(number) [128-134]
+    PipeOrderBy [136-154]
+      OrderBy [139-154]
+        OrderingExpression(ASC) [148-154]
+          PathExpression [148-154]
+            Identifier(number) [148-154]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#except_pipe_operator
+SELECT * FROM UNNEST(ARRAY[1, 2, 3, 3, 4]) AS number
+|> EXCEPT DISTINCT
+    (SELECT * FROM UNNEST(ARRAY[1, 2]) AS number),
+    (SELECT * FROM UNNEST(ARRAY[1, 4]) AS number)
+--
+QueryStatement [0-193]
+  Query [0-192]
+    Select [0-59]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-59]
+        TablePathExpression [14-59]
+          UnnestExpression [14-49]
+            ExpressionWithOptAlias [21-48]
+              ArrayConstructor [21-48]
+                ArrayType [21-33]
+                  SimpleType [27-32]
+                    PathExpression [27-32]
+                      Identifier(INT64) [27-32]
+                IntLiteral(1) [34-35]
+                IntLiteral(2) [37-38]
+                IntLiteral(3) [40-41]
+                IntLiteral(3) [43-44]
+                IntLiteral(4) [46-47]
+          Alias [50-59]
+            Identifier(number) [53-59]
+    PipeSetOperation [60-192]
+      SetOperationMetadata [63-78]
+        SetOperationType [63-69]
+        SetOperationAllOrDistinct [70-78]
+      Query [84-134]
+        Select [84-134]
+          SelectList [91-92]
+            SelectColumn [91-92]
+              Star(*) [91-92]
+          FromClause [93-134]
+            TablePathExpression [98-134]
+              UnnestExpression [98-124]
+                ExpressionWithOptAlias [105-123]
+                  ArrayConstructor [105-123]
+                    ArrayType [105-117]
+                      SimpleType [111-116]
+                        PathExpression [111-116]
+                          Identifier(INT64) [111-116]
+                    IntLiteral(1) [118-119]
+                    IntLiteral(2) [121-122]
+              Alias [125-134]
+                Identifier(number) [128-134]
+      Query [142-192]
+        Select [142-192]
+          SelectList [149-150]
+            SelectColumn [149-150]
+              Star(*) [149-150]
+          FromClause [151-192]
+            TablePathExpression [156-192]
+              UnnestExpression [156-182]
+                ExpressionWithOptAlias [163-181]
+                  ArrayConstructor [163-181]
+                    ArrayType [163-175]
+                      SimpleType [169-174]
+                        PathExpression [169-174]
+                          Identifier(INT64) [169-174]
+                    IntLiteral(1) [176-177]
+                    IntLiteral(4) [179-180]
+              Alias [183-192]
+                Identifier(number) [186-192]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#except_pipe_operator
+SELECT * FROM UNNEST(ARRAY[1, 2, 3, 3, 4]) AS number
+|> EXCEPT DISTINCT
+(
+  SELECT * FROM UNNEST(ARRAY[1, 2]) AS number
+  |> EXCEPT DISTINCT
+      (SELECT * FROM UNNEST(ARRAY[1, 4]) AS number)
+) |> ORDER BY number
+--
+QueryStatement [0-234]
+  Query [0-234]
+    Select [0-59]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-59]
+        TablePathExpression [14-59]
+          UnnestExpression [14-49]
+            ExpressionWithOptAlias [21-48]
+              ArrayConstructor [21-48]
+                ArrayType [21-33]
+                  SimpleType [27-32]
+                    PathExpression [27-32]
+                      Identifier(INT64) [27-32]
+                IntLiteral(1) [34-35]
+                IntLiteral(2) [37-38]
+                IntLiteral(3) [40-41]
+                IntLiteral(3) [43-44]
+                IntLiteral(4) [46-47]
+          Alias [50-59]
+            Identifier(number) [53-59]
+    PipeSetOperation [60-215]
+      SetOperationMetadata [63-78]
+        SetOperationType [63-69]
+        SetOperationAllOrDistinct [70-78]
+      Query [83-213]
+        Select [83-133]
+          SelectList [90-91]
+            SelectColumn [90-91]
+              Star(*) [90-91]
+          FromClause [92-133]
+            TablePathExpression [97-133]
+              UnnestExpression [97-123]
+                ExpressionWithOptAlias [104-122]
+                  ArrayConstructor [104-122]
+                    ArrayType [104-116]
+                      SimpleType [110-115]
+                        PathExpression [110-115]
+                          Identifier(INT64) [110-115]
+                    IntLiteral(1) [117-118]
+                    IntLiteral(2) [120-121]
+              Alias [124-133]
+                Identifier(number) [127-133]
+        PipeSetOperation [136-213]
+          SetOperationMetadata [139-154]
+            SetOperationType [139-145]
+            SetOperationAllOrDistinct [146-154]
+          Query [162-212]
+            Select [162-212]
+              SelectList [169-170]
+                SelectColumn [169-170]
+                  Star(*) [169-170]
+              FromClause [171-212]
+                TablePathExpression [176-212]
+                  UnnestExpression [176-202]
+                    ExpressionWithOptAlias [183-201]
+                      ArrayConstructor [183-201]
+                        ArrayType [183-195]
+                          SimpleType [189-194]
+                            PathExpression [189-194]
+                              Identifier(INT64) [189-194]
+                        IntLiteral(1) [196-197]
+                        IntLiteral(4) [199-200]
+                  Alias [203-212]
+                    Identifier(number) [206-212]
+    PipeOrderBy [216-234]
+      OrderBy [219-234]
+        OrderingExpression(ASC) [228-234]
+          PathExpression [228-234]
+            Identifier(number) [228-234]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/pipe-syntax#tablesample_pipe_operator
+FROM LargeTable
+|> TABLESAMPLE SYSTEM (1 PERCENT)
+--
+QueryStatement [0-49]
+  Query [0-49]
+    FromQuery [0-15]
+      FromClause [0-15]
+        TablePathExpression [5-15]
+          PathExpression [5-15]
+            Identifier(LargeTable) [5-15]
+    PipeTablesample [16-49]
+      SampleClause [19-49]
+        Identifier(SYSTEM) [31-37]
+        SampleSize [39-48]
+          IntLiteral(1) [39-40]
diff --git a/parser/testdata/spanner/procedural-language.test b/parser/testdata/spanner/procedural-language.test
new file mode 100644
index 0000000..d4e3c59
--- /dev/null
+++ b/parser/testdata/spanner/procedural-language.test
@@ -0,0 +1,15 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/procedural-language
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/procedural-language#call
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CALL cancel_query("12345")
+--
+CallStatement [0-26]
+  PathExpression [5-17]
+    Identifier(cancel_query) [5-17]
+  TVFArgument [18-25]
+    StringLiteral [18-25]
+      StringLiteralComponent("12345") [18-25]
diff --git a/parser/testdata/spanner/protocol-buffers.spanner.sql b/parser/testdata/spanner/protocol-buffers.spanner.sql
new file mode 100644
index 0000000..281cfeb
--- /dev/null
+++ b/parser/testdata/spanner/protocol-buffers.spanner.sql
@@ -0,0 +1,43 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers#create_a_protocol_buffer
+# id f26727d5910e: proto bundles (protocol-buffers)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected keyword PROTO [at 1:8]
+CREATE PROTO BUNDLE (
+  examples.shipping.Order,
+  examples.shipping.Order.Address,
+  examples.shipping.Order.Item
+);
+
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers#add_a_message_to_a_protocol_buffer
+# id 9879725f4dbc: proto bundles (protocol-buffers)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected keyword PROTO [at 1:7]
+ALTER PROTO BUNDLE INSERT (
+  examples.shipping.OrderHistory
+);
+
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers#update_a_message_for_a_protocol_buffer
+# id 50c66ca6ed0e: proto bundles (protocol-buffers)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected keyword PROTO [at 1:7]
+ALTER PROTO BUNDLE UPDATE (
+  examples.shipping.Order,
+);
+
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers#delete_a_message_from_a_protocol_buffer
+# id d6be672a4744: proto bundles (protocol-buffers)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected keyword PROTO [at 1:7]
+ALTER PROTO BUNDLE DELETE (
+  examples.shipping.OrderHistory,
+);
+
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers#proto_primary_keys
+# id b456ed9cdb3e: proto bundles (protocol-buffers)
+# execute_query: INVALID_ARGUMENT: Syntax error: Unexpected keyword PROTO [at 1:8]
+CREATE PROTO BUNDLE (example.music.SingerInfo, example.music.SingerInfo.Residence);
diff --git a/parser/testdata/spanner/protocol-buffers.test b/parser/testdata/spanner/protocol-buffers.test
new file mode 100644
index 0000000..105d930
--- /dev/null
+++ b/parser/testdata/spanner/protocol-buffers.test
@@ -0,0 +1,556 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers#cast_to_or_from_a_protocol_buffer
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT CAST('first_name: "Alana", last_name: "Yah", customer_no: 1234'
+  AS example.CustomerInfo)
+--
+QueryStatement [0-97]
+  Query [0-97]
+    Select [0-97]
+      SelectList [7-97]
+        SelectColumn [7-97]
+          CastExpression [7-97]
+            StringLiteral [12-70]
+              StringLiteralComponent('first_name: "Alana", last_name: "Yah", customer_no: 1234') [12-70]
+            SimpleType [76-96]
+              PathExpression [76-96]
+                Identifier(example) [76-83]
+                Identifier(CustomerInfo) [84-96]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers#create_a_protocol_buffer
+[mode=expression]
+protoc --include_imports --descriptor_set_out=order_descriptors.pb order_protos.proto
+--
+PathExpression [0-6]
+  Identifier(protoc) [0-6]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers#create_a_protocol_buffer
+CREATE TABLE Orders (
+  Id INT64,
+  OrderInfo `examples.shipping.Order`,
+) PRIMARY KEY(Id)
+--
+CreateTableStatement [0-90]
+  PathExpression [13-19]
+    Identifier(Orders) [13-19]
+  TableElementList [20-74]
+    ColumnDefinition [24-32]
+      Identifier(Id) [24-26]
+      SimpleColumnSchema [27-32]
+        PathExpression [27-32]
+          Identifier(INT64) [27-32]
+    ColumnDefinition [36-71]
+      Identifier(OrderInfo) [36-45]
+      SimpleColumnSchema [46-71]
+        PathExpression [46-71]
+          Identifier(`examples.shipping.Order`) [46-71]
+  SpannerTableOptions [75-90]
+    PrimaryKey(ENFORCED) [75-90]
+      PrimaryKeyElementList [86-90]
+        PrimaryKeyElement(ASC) [87-89]
+          Identifier(Id) [87-89]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers#use_a_protocol_buffer_message_in_a_column_definition
+CREATE TABLE OrderHistory (
+  Id INT64,
+  OrderHistoryInfo examples.shipping.OrderHistory,
+) PRIMARY KEY(Id)
+--
+CreateTableStatement [0-108]
+  PathExpression [13-25]
+    Identifier(OrderHistory) [13-25]
+  TableElementList [26-92]
+    ColumnDefinition [30-38]
+      Identifier(Id) [30-32]
+      SimpleColumnSchema [33-38]
+        PathExpression [33-38]
+          Identifier(INT64) [33-38]
+    ColumnDefinition [42-89]
+      Identifier(OrderHistoryInfo) [42-58]
+      SimpleColumnSchema [59-89]
+        PathExpression [59-89]
+          Identifier(examples) [59-67]
+          Identifier(shipping) [68-76]
+          Identifier(OrderHistory) [77-89]
+  SpannerTableOptions [93-108]
+    PrimaryKey(ENFORCED) [93-108]
+      PrimaryKeyElementList [104-108]
+        PrimaryKeyElement(ASC) [105-107]
+          Identifier(Id) [105-107]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers#example_protocol_buffer_message
+CREATE TABLE Customers (
+  Id INT64,
+  Orders examples.shipping.Order,
+) PRIMARY KEY(Id)
+--
+CreateTableStatement [0-88]
+  PathExpression [13-22]
+    Identifier(Customers) [13-22]
+  TableElementList [23-72]
+    ColumnDefinition [27-35]
+      Identifier(Id) [27-29]
+      SimpleColumnSchema [30-35]
+        PathExpression [30-35]
+          Identifier(INT64) [30-35]
+    ColumnDefinition [39-69]
+      Identifier(Orders) [39-45]
+      SimpleColumnSchema [46-69]
+        PathExpression [46-69]
+          Identifier(examples) [46-54]
+          Identifier(shipping) [55-63]
+          Identifier(`Order`) [64-69]
+  SpannerTableOptions [73-88]
+    PrimaryKey(ENFORCED) [73-88]
+      PrimaryKeyElementList [84-88]
+        PrimaryKeyElement(ASC) [85-87]
+          Identifier(Id) [85-87]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers#example_protocol_buffer_message
+[mode=expression]
+{
+  order_number: 1234567
+  date: 16242
+  shipping_address: {
+      street: "1234 Main St"
+      city: "AnyCity"
+      state: "AnyState"
+      country: "United States"
+  }
+  line_item: [
+    {
+      product_name: "Foo"
+      quantity: 10
+    },
+    {
+      product_name: "Bar"
+      quantity: 5
+    }
+  ]
+}
+--
+BracedConstructor [0-306]
+  BracedConstructorField [4-25]
+    BracedConstructorLhs [4-16]
+      PathExpression [4-16]
+        Identifier(order_number) [4-16]
+    BracedConstructorFieldValue [18-25]
+      IntLiteral(1234567) [18-25]
+  BracedConstructorField [28-39]
+    BracedConstructorLhs [28-32]
+      PathExpression [28-32]
+        Identifier(date) [28-32]
+    BracedConstructorFieldValue [34-39]
+      IntLiteral(16242) [34-39]
+  BracedConstructorField [42-171]
+    BracedConstructorLhs [42-58]
+      PathExpression [42-58]
+        Identifier(shipping_address) [42-58]
+    BracedConstructorFieldValue [60-171]
+      BracedConstructor [60-171]
+        BracedConstructorField [68-90]
+          BracedConstructorLhs [68-74]
+            PathExpression [68-74]
+              Identifier(street) [68-74]
+          BracedConstructorFieldValue [76-90]
+            StringLiteral [76-90]
+              StringLiteralComponent("1234 Main St") [76-90]
+        BracedConstructorField [97-112]
+          BracedConstructorLhs [97-101]
+            PathExpression [97-101]
+              Identifier(city) [97-101]
+          BracedConstructorFieldValue [103-112]
+            StringLiteral [103-112]
+              StringLiteralComponent("AnyCity") [103-112]
+        BracedConstructorField [119-136]
+          BracedConstructorLhs [119-124]
+            PathExpression [119-124]
+              Identifier(state) [119-124]
+          BracedConstructorFieldValue [126-136]
+            StringLiteral [126-136]
+              StringLiteralComponent("AnyState") [126-136]
+        BracedConstructorField [143-167]
+          BracedConstructorLhs [143-150]
+            PathExpression [143-150]
+              Identifier(country) [143-150]
+          BracedConstructorFieldValue [152-167]
+            StringLiteral [152-167]
+              StringLiteralComponent("United States") [152-167]
+  BracedConstructorField [174-304]
+    BracedConstructorLhs [174-183]
+      PathExpression [174-183]
+        Identifier(line_item) [174-183]
+    BracedConstructorFieldValue [185-304]
+      ArrayConstructor [185-304]
+        BracedConstructor [191-243]
+          BracedConstructorField [199-218]
+            BracedConstructorLhs [199-211]
+              PathExpression [199-211]
+                Identifier(product_name) [199-211]
+            BracedConstructorFieldValue [213-218]
+              StringLiteral [213-218]
+                StringLiteralComponent("Foo") [213-218]
+          BracedConstructorField [225-237]
+            BracedConstructorLhs [225-233]
+              PathExpression [225-233]
+                Identifier(quantity) [225-233]
+            BracedConstructorFieldValue [235-237]
+              IntLiteral(10) [235-237]
+        BracedConstructor [249-300]
+          BracedConstructorField [257-276]
+            BracedConstructorLhs [257-269]
+              PathExpression [257-269]
+                Identifier(product_name) [257-269]
+            BracedConstructorFieldValue [271-276]
+              StringLiteral [271-276]
+                StringLiteralComponent("Bar") [271-276]
+          BracedConstructorField [283-294]
+            BracedConstructorLhs [283-291]
+              PathExpression [283-291]
+                Identifier(quantity) [283-291]
+            BracedConstructorFieldValue [293-294]
+              IntLiteral(5) [293-294]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers#querying_top-level_fields
+SELECT
+  c.Orders
+FROM
+  Customers AS c
+--
+QueryStatement [0-39]
+  Query [0-39]
+    Select [0-39]
+      SelectList [9-17]
+        SelectColumn [9-17]
+          PathExpression [9-17]
+            Identifier(c) [9-10]
+            Identifier(Orders) [11-17]
+      FromClause [18-39]
+        TablePathExpression [25-39]
+          PathExpression [25-34]
+            Identifier(Customers) [25-34]
+          Alias [35-39]
+            Identifier(c) [38-39]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers#querying_top-level_fields
+SELECT
+  c.Orders.order_number
+FROM
+  Customers AS c
+--
+QueryStatement [0-52]
+  Query [0-52]
+    Select [0-52]
+      SelectList [9-30]
+        SelectColumn [9-30]
+          PathExpression [9-30]
+            Identifier(c) [9-10]
+            Identifier(Orders) [11-17]
+            Identifier(order_number) [18-30]
+      FromClause [31-52]
+        TablePathExpression [38-52]
+          PathExpression [38-47]
+            Identifier(Customers) [38-47]
+          Alias [48-52]
+            Identifier(c) [51-52]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers#querying_nested_paths
+SELECT
+  c.Orders.order_number,
+  c.Orders.shipping_address
+FROM
+  Customers AS c
+WHERE
+  c.Orders.shipping_address.country = "United States"
+--
+QueryStatement [0-141]
+  Query [0-141]
+    Select [0-141]
+      SelectList [9-59]
+        SelectColumn [9-30]
+          PathExpression [9-30]
+            Identifier(c) [9-10]
+            Identifier(Orders) [11-17]
+            Identifier(order_number) [18-30]
+        SelectColumn [34-59]
+          PathExpression [34-59]
+            Identifier(c) [34-35]
+            Identifier(Orders) [36-42]
+            Identifier(shipping_address) [43-59]
+      FromClause [60-81]
+        TablePathExpression [67-81]
+          PathExpression [67-76]
+            Identifier(Customers) [67-76]
+          Alias [77-81]
+            Identifier(c) [80-81]
+      WhereClause [82-141]
+        BinaryExpression(=) [90-141]
+          PathExpression [90-123]
+            Identifier(c) [90-91]
+            Identifier(Orders) [92-98]
+            Identifier(shipping_address) [99-115]
+            Identifier(country) [116-123]
+          StringLiteral [126-141]
+            StringLiteralComponent("United States") [126-141]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers#return_repeated_fields
+SELECT
+  c.Orders.line_item
+FROM
+  Customers AS c
+--
+QueryStatement [0-49]
+  Query [0-49]
+    Select [0-49]
+      SelectList [9-27]
+        SelectColumn [9-27]
+          PathExpression [9-27]
+            Identifier(c) [9-10]
+            Identifier(Orders) [11-17]
+            Identifier(line_item) [18-27]
+      FromClause [28-49]
+        TablePathExpression [35-49]
+          PathExpression [35-44]
+            Identifier(Customers) [35-44]
+          Alias [45-49]
+            Identifier(c) [48-49]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers#return_the_number_of_elements_in_an_array
+SELECT
+  c.Orders.order_number,
+  ARRAY_LENGTH(c.Orders.line_item)
+FROM
+  Customers AS c
+--
+QueryStatement [0-88]
+  Query [0-88]
+    Select [0-88]
+      SelectList [9-66]
+        SelectColumn [9-30]
+          PathExpression [9-30]
+            Identifier(c) [9-10]
+            Identifier(Orders) [11-17]
+            Identifier(order_number) [18-30]
+        SelectColumn [34-66]
+          FunctionCall [34-66]
+            PathExpression [34-46]
+              Identifier(ARRAY_LENGTH) [34-46]
+            PathExpression [47-65]
+              Identifier(c) [47-48]
+              Identifier(Orders) [49-55]
+              Identifier(line_item) [56-65]
+      FromClause [67-88]
+        TablePathExpression [74-88]
+          PathExpression [74-83]
+            Identifier(Customers) [74-83]
+          Alias [84-88]
+            Identifier(c) [87-88]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers#checking_if_a_field_has_a_value
+SELECT
+  c.Orders.shipping_address.has_country
+FROM
+  Customer AS c
+--
+QueryStatement [0-67]
+  Query [0-67]
+    Select [0-67]
+      SelectList [9-46]
+        SelectColumn [9-46]
+          PathExpression [9-46]
+            Identifier(c) [9-10]
+            Identifier(Orders) [11-17]
+            Identifier(shipping_address) [18-34]
+            Identifier(has_country) [35-46]
+      FromClause [47-67]
+        TablePathExpression [54-67]
+          PathExpression [54-62]
+            Identifier(Customer) [54-62]
+          Alias [63-67]
+            Identifier(c) [66-67]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers#checking_for_a_repeated_value
+SELECT
+  C.Id
+FROM
+  Customers AS C
+WHERE
+  EXISTS(
+    SELECT
+      *
+    FROM
+      C.Orders.line_item AS item
+    WHERE
+      item.product_name = 'Foo'
+  )
+--
+QueryStatement [0-158]
+  Query [0-158]
+    Select [0-158]
+      SelectList [9-13]
+        SelectColumn [9-13]
+          PathExpression [9-13]
+            Identifier(C) [9-10]
+            Identifier(Id) [11-13]
+      FromClause [14-35]
+        TablePathExpression [21-35]
+          PathExpression [21-30]
+            Identifier(Customers) [21-30]
+          Alias [31-35]
+            Identifier(C) [34-35]
+      WhereClause [36-158]
+        ExpressionSubquery(modifier=EXISTS) [44-158]
+          Query [56-154]
+            Select [56-154]
+              SelectList [69-70]
+                SelectColumn [69-70]
+                  Star(*) [69-70]
+              FromClause [75-112]
+                TablePathExpression [86-112]
+                  PathExpression [86-104]
+                    Identifier(C) [86-87]
+                    Identifier(Orders) [88-94]
+                    Identifier(line_item) [95-104]
+                  Alias [105-112]
+                    Identifier(item) [108-112]
+              WhereClause [117-154]
+                BinaryExpression(=) [129-154]
+                  PathExpression [129-146]
+                    Identifier(item) [129-133]
+                    Identifier(product_name) [134-146]
+                  StringLiteral [149-154]
+                    StringLiteralComponent('Foo') [149-154]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers#nullness_and_nested_fields
+SELECT
+  proto_field.nested.value
+FROM
+  (SELECT
+     CAST("nested { value: 5 }" AS examples.package.OuterMessage) AS proto_field)
+--
+QueryStatement [0-130]
+  Query [0-130]
+    Select [0-130]
+      SelectList [9-33]
+        SelectColumn [9-33]
+          PathExpression [9-33]
+            Identifier(proto_field) [9-20]
+            Identifier(nested) [21-27]
+            Identifier(value) [28-33]
+      FromClause [34-130]
+        TableSubquery [41-130]
+          Query [42-129]
+            Select [42-129]
+              SelectList [54-129]
+                SelectColumn [54-129]
+                  CastExpression [54-114]
+                    StringLiteral [59-80]
+                      StringLiteralComponent("nested { value: 5 }") [59-80]
+                    SimpleType [84-113]
+                      PathExpression [84-113]
+                        Identifier(examples) [84-92]
+                        Identifier(package) [93-100]
+                        Identifier(OuterMessage) [101-113]
+                  Alias [115-129]
+                    Identifier(proto_field) [118-129]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers#nullness_and_nested_fields
+SELECT
+  proto_field.nested.value
+FROM
+  (SELECT
+     CAST("nested { }" AS examples.package.OuterMessage) AS proto_field)
+--
+QueryStatement [0-121]
+  Query [0-121]
+    Select [0-121]
+      SelectList [9-33]
+        SelectColumn [9-33]
+          PathExpression [9-33]
+            Identifier(proto_field) [9-20]
+            Identifier(nested) [21-27]
+            Identifier(value) [28-33]
+      FromClause [34-121]
+        TableSubquery [41-121]
+          Query [42-120]
+            Select [42-120]
+              SelectList [54-120]
+                SelectColumn [54-120]
+                  CastExpression [54-105]
+                    StringLiteral [59-71]
+                      StringLiteralComponent("nested { }") [59-71]
+                    SimpleType [75-104]
+                      PathExpression [75-104]
+                        Identifier(examples) [75-83]
+                        Identifier(package) [84-91]
+                        Identifier(OuterMessage) [92-104]
+                  Alias [106-120]
+                    Identifier(proto_field) [109-120]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers#nullness_and_nested_fields
+SELECT
+  proto_field.nested.value
+FROM
+  (SELECT
+     CAST("" AS examples.package.OuterMessage) AS proto_field)
+--
+QueryStatement [0-111]
+  Query [0-111]
+    Select [0-111]
+      SelectList [9-33]
+        SelectColumn [9-33]
+          PathExpression [9-33]
+            Identifier(proto_field) [9-20]
+            Identifier(nested) [21-27]
+            Identifier(value) [28-33]
+      FromClause [34-111]
+        TableSubquery [41-111]
+          Query [42-110]
+            Select [42-110]
+              SelectList [54-110]
+                SelectColumn [54-110]
+                  CastExpression [54-95]
+                    StringLiteral [59-61]
+                      StringLiteralComponent("") [59-61]
+                    SimpleType [65-94]
+                      PathExpression [65-94]
+                        Identifier(examples) [65-73]
+                        Identifier(package) [74-81]
+                        Identifier(OuterMessage) [82-94]
+                  Alias [96-110]
+                    Identifier(proto_field) [99-110]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers#proto_primary_keys
+SELECT s.SingerId, s.SingerInfo.name
+FROM Singers AS s
+WHERE s.SingerId = 34
+--
+QueryStatement [0-76]
+  Query [0-76]
+    Select [0-76]
+      SelectList [7-36]
+        SelectColumn [7-17]
+          PathExpression [7-17]
+            Identifier(s) [7-8]
+            Identifier(SingerId) [9-17]
+        SelectColumn [19-36]
+          PathExpression [19-36]
+            Identifier(s) [19-20]
+            Identifier(SingerInfo) [21-31]
+            Identifier(name) [32-36]
+      FromClause [37-54]
+        TablePathExpression [42-54]
+          PathExpression [42-49]
+            Identifier(Singers) [42-49]
+          Alias [50-54]
+            Identifier(s) [53-54]
+      WhereClause [55-76]
+        BinaryExpression(=) [61-76]
+          PathExpression [61-71]
+            Identifier(s) [61-62]
+            Identifier(SingerId) [63-71]
+          IntLiteral(34) [74-76]
diff --git a/parser/testdata/spanner/query-syntax.test b/parser/testdata/spanner/query-syntax.test
new file mode 100644
index 0000000..a7b5050
--- /dev/null
+++ b/parser/testdata/spanner/query-syntax.test
@@ -0,0 +1,8202 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#select_star
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT * FROM (SELECT "apple" AS fruit, "carrot" AS vegetable)
+--
+QueryStatement [0-62]
+  Query [0-62]
+    Select [0-62]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-62]
+        TableSubquery [14-62]
+          Query [15-61]
+            Select [15-61]
+              SelectList [22-61]
+                SelectColumn [22-38]
+                  StringLiteral [22-29]
+                    StringLiteralComponent("apple") [22-29]
+                  Alias [30-38]
+                    Identifier(fruit) [33-38]
+                SelectColumn [40-61]
+                  StringLiteral [40-48]
+                    StringLiteralComponent("carrot") [40-48]
+                  Alias [49-61]
+                    Identifier(vegetable) [52-61]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#select_expression_star
+WITH groceries AS
+  (SELECT "milk" AS dairy,
+   "eggs" AS protein,
+   "bread" AS grain)
+SELECT g.*
+FROM groceries AS g
+--
+QueryStatement [0-118]
+  Query [0-118]
+    WithClause [0-87]
+      WithClauseEntry [5-87]
+        AliasedQuery [5-87]
+          Identifier(groceries) [5-14]
+          Query [20-87]
+            Select [21-86]
+              SelectList [28-86]
+                SelectColumn [28-43]
+                  StringLiteral [28-34]
+                    StringLiteralComponent("milk") [28-34]
+                  Alias [35-43]
+                    Identifier(dairy) [38-43]
+                SelectColumn [48-65]
+                  StringLiteral [48-54]
+                    StringLiteralComponent("eggs") [48-54]
+                  Alias [55-65]
+                    Identifier(protein) [58-65]
+                SelectColumn [70-86]
+                  StringLiteral [70-77]
+                    StringLiteralComponent("bread") [70-77]
+                  Alias [78-86]
+                    Identifier(grain) [81-86]
+    Select [88-118]
+      SelectList [95-98]
+        SelectColumn [95-98]
+          DotStar [95-98]
+            PathExpression [95-96]
+              Identifier(g) [95-96]
+      FromClause [99-118]
+        TablePathExpression [104-118]
+          PathExpression [104-113]
+            Identifier(groceries) [104-113]
+          Alias [114-118]
+            Identifier(g) [117-118]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#select_expression_star
+WITH locations AS
+  (SELECT STRUCT("Seattle" AS city, "Washington" AS state) AS location
+  UNION ALL
+  SELECT STRUCT("Phoenix" AS city, "Arizona" AS state) AS location)
+SELECT l.location.*
+FROM locations l
+--
+QueryStatement [0-205]
+  Query [0-205]
+    WithClause [0-168]
+      WithClauseEntry [5-168]
+        AliasedQuery [5-168]
+          Identifier(locations) [5-14]
+          Query [20-168]
+            SetOperation(UNION ALL) [21-167]
+              SetOperationMetadataList [91-100]
+                SetOperationMetadata [91-100]
+                  SetOperationType [91-96]
+                  SetOperationAllOrDistinct [97-100]
+              Select [21-88]
+                SelectList [28-88]
+                  SelectColumn [28-88]
+                    StructConstructorWithKeyword [28-76]
+                      StructConstructorArg [35-52]
+                        StringLiteral [35-44]
+                          StringLiteralComponent("Seattle") [35-44]
+                        Alias [45-52]
+                          Identifier(city) [48-52]
+                      StructConstructorArg [54-75]
+                        StringLiteral [54-66]
+                          StringLiteralComponent("Washington") [54-66]
+                        Alias [67-75]
+                          Identifier(state) [70-75]
+                    Alias [77-88]
+                      Identifier(location) [80-88]
+              Select [103-167]
+                SelectList [110-167]
+                  SelectColumn [110-167]
+                    StructConstructorWithKeyword [110-155]
+                      StructConstructorArg [117-134]
+                        StringLiteral [117-126]
+                          StringLiteralComponent("Phoenix") [117-126]
+                        Alias [127-134]
+                          Identifier(city) [130-134]
+                      StructConstructorArg [136-154]
+                        StringLiteral [136-145]
+                          StringLiteralComponent("Arizona") [136-145]
+                        Alias [146-154]
+                          Identifier(state) [149-154]
+                    Alias [156-167]
+                      Identifier(location) [159-167]
+    Select [169-205]
+      SelectList [176-188]
+        SelectColumn [176-188]
+          DotStar [176-188]
+            PathExpression [176-186]
+              Identifier(l) [176-177]
+              Identifier(location) [178-186]
+      FromClause [189-205]
+        TablePathExpression [194-205]
+          PathExpression [194-203]
+            Identifier(locations) [194-203]
+          Alias [204-205]
+            Identifier(l) [204-205]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#select_expression_star
+WITH locations AS
+  (SELECT ARRAY>[("Seattle", "Washington"),
+    ("Phoenix", "Arizona")] AS location)
+SELECT l.LOCATION[offset(0)].*
+FROM locations l
+--
+QueryStatement [0-184]
+  Query [0-184]
+    WithClause [0-136]
+      WithClauseEntry [5-136]
+        AliasedQuery [5-136]
+          Identifier(locations) [5-14]
+          Query [20-136]
+            Select [21-135]
+              SelectList [28-135]
+                SelectColumn [28-135]
+                  ArrayConstructor [28-123]
+                    ArrayType [28-68]
+                      StructType [34-67]
+                        StructField [41-52]
+                          Identifier(city) [41-45]
+                          SimpleType [46-52]
+                            PathExpression [46-52]
+                              Identifier(STRING) [46-52]
+                        StructField [54-66]
+                          Identifier(state) [54-59]
+                          SimpleType [60-66]
+                            PathExpression [60-66]
+                              Identifier(STRING) [60-66]
+                    StructConstructorWithParens [69-94]
+                      StringLiteral [70-79]
+                        StringLiteralComponent("Seattle") [70-79]
+                      StringLiteral [81-93]
+                        StringLiteralComponent("Washington") [81-93]
+                    StructConstructorWithParens [100-122]
+                      StringLiteral [101-110]
+                        StringLiteralComponent("Phoenix") [101-110]
+                      StringLiteral [112-121]
+                        StringLiteralComponent("Arizona") [112-121]
+                  Alias [124-135]
+                    Identifier(location) [127-135]
+    Select [137-184]
+      SelectList [144-167]
+        SelectColumn [144-167]
+          DotStar [144-167]
+            ArrayElement [144-165]
+              PathExpression [144-154]
+                Identifier(l) [144-145]
+                Identifier(LOCATION) [146-154]
+              Location [154-155]
+              FunctionCall [155-164]
+                PathExpression [155-161]
+                  Identifier(offset) [155-161]
+                IntLiteral(0) [162-163]
+      FromClause [168-184]
+        TablePathExpression [173-184]
+          PathExpression [173-182]
+            Identifier(locations) [173-182]
+          Alias [183-184]
+            Identifier(l) [183-184]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#select_except
+WITH orders AS
+  (SELECT 5 as order_id,
+  "sprocket" as item_name,
+  200 as quantity)
+SELECT * EXCEPT (order_id)
+FROM orders
+--
+QueryStatement [0-124]
+  Query [0-124]
+    WithClause [0-85]
+      WithClauseEntry [5-85]
+        AliasedQuery [5-85]
+          Identifier(orders) [5-11]
+          Query [17-85]
+            Select [18-84]
+              SelectList [25-84]
+                SelectColumn [25-38]
+                  IntLiteral(5) [25-26]
+                  Alias [27-38]
+                    Identifier(order_id) [30-38]
+                SelectColumn [42-65]
+                  StringLiteral [42-52]
+                    StringLiteralComponent("sprocket") [42-52]
+                  Alias [53-65]
+                    Identifier(item_name) [56-65]
+                SelectColumn [69-84]
+                  IntLiteral(200) [69-72]
+                  Alias [73-84]
+                    Identifier(quantity) [76-84]
+    Select [86-124]
+      SelectList [93-112]
+        SelectColumn [93-112]
+          StarWithModifiers [93-112]
+            StarModifiers [95-112]
+              StarExceptList [95-112]
+                Identifier(order_id) [103-111]
+      FromClause [113-124]
+        TablePathExpression [118-124]
+          PathExpression [118-124]
+            Identifier(orders) [118-124]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#select_replace
+WITH orders AS
+  (SELECT 5 as order_id,
+  "sprocket" as item_name,
+  200 as quantity)
+SELECT * REPLACE ("widget" AS item_name)
+FROM orders
+--
+QueryStatement [0-138]
+  Query [0-138]
+    WithClause [0-85]
+      WithClauseEntry [5-85]
+        AliasedQuery [5-85]
+          Identifier(orders) [5-11]
+          Query [17-85]
+            Select [18-84]
+              SelectList [25-84]
+                SelectColumn [25-38]
+                  IntLiteral(5) [25-26]
+                  Alias [27-38]
+                    Identifier(order_id) [30-38]
+                SelectColumn [42-65]
+                  StringLiteral [42-52]
+                    StringLiteralComponent("sprocket") [42-52]
+                  Alias [53-65]
+                    Identifier(item_name) [56-65]
+                SelectColumn [69-84]
+                  IntLiteral(200) [69-72]
+                  Alias [73-84]
+                    Identifier(quantity) [76-84]
+    Select [86-138]
+      SelectList [93-126]
+        SelectColumn [93-126]
+          StarWithModifiers [93-126]
+            StarModifiers [95-126]
+              StarReplaceItem [104-125]
+                StringLiteral [104-112]
+                  StringLiteralComponent("widget") [104-112]
+                Identifier(item_name) [116-125]
+      FromClause [127-138]
+        TablePathExpression [132-138]
+          PathExpression [132-138]
+            Identifier(orders) [132-138]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#select_replace
+WITH orders AS
+  (SELECT 5 as order_id,
+  "sprocket" as item_name,
+  200 as quantity)
+SELECT * REPLACE (quantity/2 AS quantity)
+FROM orders
+--
+QueryStatement [0-139]
+  Query [0-139]
+    WithClause [0-85]
+      WithClauseEntry [5-85]
+        AliasedQuery [5-85]
+          Identifier(orders) [5-11]
+          Query [17-85]
+            Select [18-84]
+              SelectList [25-84]
+                SelectColumn [25-38]
+                  IntLiteral(5) [25-26]
+                  Alias [27-38]
+                    Identifier(order_id) [30-38]
+                SelectColumn [42-65]
+                  StringLiteral [42-52]
+                    StringLiteralComponent("sprocket") [42-52]
+                  Alias [53-65]
+                    Identifier(item_name) [56-65]
+                SelectColumn [69-84]
+                  IntLiteral(200) [69-72]
+                  Alias [73-84]
+                    Identifier(quantity) [76-84]
+    Select [86-139]
+      SelectList [93-127]
+        SelectColumn [93-127]
+          StarWithModifiers [93-127]
+            StarModifiers [95-127]
+              StarReplaceItem [104-126]
+                BinaryExpression(/) [104-114]
+                  PathExpression [104-112]
+                    Identifier(quantity) [104-112]
+                  IntLiteral(2) [113-114]
+                Identifier(quantity) [118-126]
+      FromClause [128-139]
+        TablePathExpression [133-139]
+          PathExpression [133-139]
+            Identifier(orders) [133-139]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#using_structs_with_select
+SELECT STRUCT(1, 2) FROM Users
+--
+QueryStatement [0-30]
+  Query [0-30]
+    Select [0-30]
+      SelectList [7-19]
+        SelectColumn [7-19]
+          StructConstructorWithKeyword [7-19]
+            StructConstructorArg [14-15]
+              IntLiteral(1) [14-15]
+            StructConstructorArg [17-18]
+              IntLiteral(2) [17-18]
+      FromClause [20-30]
+        TablePathExpression [25-30]
+          PathExpression [25-30]
+            Identifier(Users) [25-30]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#using_structs_with_select
+SELECT ARRAY(SELECT STRUCT(1 AS A, 2 AS B)) FROM Users
+--
+QueryStatement [0-54]
+  Query [0-54]
+    Select [0-54]
+      SelectList [7-43]
+        SelectColumn [7-43]
+          ExpressionSubquery(modifier=ARRAY) [7-43]
+            Query [13-42]
+              Select [13-42]
+                SelectList [20-42]
+                  SelectColumn [20-42]
+                    StructConstructorWithKeyword [20-42]
+                      StructConstructorArg [27-33]
+                        IntLiteral(1) [27-28]
+                        Alias [29-33]
+                          Identifier(A) [32-33]
+                      StructConstructorArg [35-41]
+                        IntLiteral(2) [35-36]
+                        Alias [37-41]
+                          Identifier(B) [40-41]
+      FromClause [44-54]
+        TablePathExpression [49-54]
+          PathExpression [49-54]
+            Identifier(Users) [49-54]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#using_structs_with_select
+SELECT ARRAY(SELECT AS STRUCT 1 AS a, 2 AS b) FROM Users
+--
+QueryStatement [0-56]
+  Query [0-56]
+    Select [0-56]
+      SelectList [7-45]
+        SelectColumn [7-45]
+          ExpressionSubquery(modifier=ARRAY) [7-45]
+            Query [13-44]
+              Select [13-44]
+                SelectAs(as_mode=STRUCT) [20-29]
+                SelectList [30-44]
+                  SelectColumn [30-36]
+                    IntLiteral(1) [30-31]
+                    Alias [32-36]
+                      Identifier(a) [35-36]
+                  SelectColumn [38-44]
+                    IntLiteral(2) [38-39]
+                    Alias [40-44]
+                      Identifier(b) [43-44]
+      FromClause [46-56]
+        TablePathExpression [51-56]
+          PathExpression [51-56]
+            Identifier(Users) [51-56]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#using_structs_with_select
+SELECT ARRAY(SELECT IF(STARTS_WITH(Users.username, "a"), NULL, STRUCT(1, 2)))
+FROM Users
+--
+QueryStatement [0-88]
+  Query [0-88]
+    Select [0-88]
+      SelectList [7-77]
+        SelectColumn [7-77]
+          ExpressionSubquery(modifier=ARRAY) [7-77]
+            Query [13-76]
+              Select [13-76]
+                SelectList [20-76]
+                  SelectColumn [20-76]
+                    FunctionCall [20-76]
+                      PathExpression [20-22]
+                        Identifier(`IF`) [20-22]
+                      FunctionCall [23-55]
+                        PathExpression [23-34]
+                          Identifier(STARTS_WITH) [23-34]
+                        PathExpression [35-49]
+                          Identifier(Users) [35-40]
+                          Identifier(username) [41-49]
+                        StringLiteral [51-54]
+                          StringLiteralComponent("a") [51-54]
+                      NullLiteral(NULL) [57-61]
+                      StructConstructorWithKeyword [63-75]
+                        StructConstructorArg [70-71]
+                          IntLiteral(1) [70-71]
+                        StructConstructorArg [73-74]
+                          IntLiteral(2) [73-74]
+      FromClause [78-88]
+        TablePathExpression [83-88]
+          PathExpression [83-88]
+            Identifier(Users) [83-88]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#select_as_struct
+SELECT ARRAY(SELECT AS STRUCT 1 a, 2 b)
+--
+QueryStatement [0-39]
+  Query [0-39]
+    Select [0-39]
+      SelectList [7-39]
+        SelectColumn [7-39]
+          ExpressionSubquery(modifier=ARRAY) [7-39]
+            Query [13-38]
+              Select [13-38]
+                SelectAs(as_mode=STRUCT) [20-29]
+                SelectList [30-38]
+                  SelectColumn [30-33]
+                    IntLiteral(1) [30-31]
+                    Alias [32-33]
+                      Identifier(a) [32-33]
+                  SelectColumn [35-38]
+                    IntLiteral(2) [35-36]
+                    Alias [37-38]
+                      Identifier(b) [37-38]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#select_as_struct
+SELECT AS STRUCT 1 x, 2, 3
+--
+QueryStatement [0-26]
+  Query [0-26]
+    Select [0-26]
+      SelectAs(as_mode=STRUCT) [7-16]
+      SelectList [17-26]
+        SelectColumn [17-20]
+          IntLiteral(1) [17-18]
+          Alias [19-20]
+            Identifier(x) [19-20]
+        SelectColumn [22-23]
+          IntLiteral(2) [22-23]
+        SelectColumn [25-26]
+          IntLiteral(3) [25-26]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#select_as_struct
+SELECT AS VALUE STRUCT(1 AS x, 2, 3)
+--
+QueryStatement [0-36]
+  Query [0-36]
+    Select [0-36]
+      SelectAs(as_mode=VALUE) [7-15]
+      SelectList [16-36]
+        SelectColumn [16-36]
+          StructConstructorWithKeyword [16-36]
+            StructConstructorArg [23-29]
+              IntLiteral(1) [23-24]
+              Alias [25-29]
+                Identifier(x) [28-29]
+            StructConstructorArg [31-32]
+              IntLiteral(2) [31-32]
+            StructConstructorArg [34-35]
+              IntLiteral(3) [34-35]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#select_as_struct
+SELECT AS STRUCT 1 x, 2 y, 3 x
+--
+QueryStatement [0-30]
+  Query [0-30]
+    Select [0-30]
+      SelectAs(as_mode=STRUCT) [7-16]
+      SelectList [17-30]
+        SelectColumn [17-20]
+          IntLiteral(1) [17-18]
+          Alias [19-20]
+            Identifier(x) [19-20]
+        SelectColumn [22-25]
+          IntLiteral(2) [22-23]
+          Alias [24-25]
+            Identifier(y) [24-25]
+        SelectColumn [27-30]
+          IntLiteral(3) [27-28]
+          Alias [29-30]
+            Identifier(x) [29-30]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#select_as_struct
+SELECT AS VALUE STRUCT(1 AS x, 2 AS y, 3 AS x)
+--
+QueryStatement [0-46]
+  Query [0-46]
+    Select [0-46]
+      SelectAs(as_mode=VALUE) [7-15]
+      SelectList [16-46]
+        SelectColumn [16-46]
+          StructConstructorWithKeyword [16-46]
+            StructConstructorArg [23-29]
+              IntLiteral(1) [23-24]
+              Alias [25-29]
+                Identifier(x) [28-29]
+            StructConstructorArg [31-37]
+              IntLiteral(2) [31-32]
+              Alias [33-37]
+                Identifier(y) [36-37]
+            StructConstructorArg [39-45]
+              IntLiteral(3) [39-40]
+              Alias [41-45]
+                Identifier(x) [44-45]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#select_as_typename
+SELECT AS tests.TestProtocolBuffer mytable.key int64_val, mytable.name string_val
+FROM mytable
+--
+QueryStatement [0-94]
+  Query [0-94]
+    Select [0-94]
+      SelectAs [7-34]
+        PathExpression [10-34]
+          Identifier(tests) [10-15]
+          Identifier(TestProtocolBuffer) [16-34]
+      SelectList [35-81]
+        SelectColumn [35-56]
+          PathExpression [35-46]
+            Identifier(mytable) [35-42]
+            Identifier(key) [43-46]
+          Alias [47-56]
+            Identifier(int64_val) [47-56]
+        SelectColumn [58-81]
+          PathExpression [58-70]
+            Identifier(mytable) [58-65]
+            Identifier(name) [66-70]
+          Alias [71-81]
+            Identifier(string_val) [71-81]
+      FromClause [82-94]
+        TablePathExpression [87-94]
+          PathExpression [87-94]
+            Identifier(mytable) [87-94]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#select_as_value
+SELECT AS VALUE 1
+--
+QueryStatement [0-17]
+  Query [0-17]
+    Select [0-17]
+      SelectAs(as_mode=VALUE) [7-15]
+      SelectList [16-17]
+        SelectColumn [16-17]
+          IntLiteral(1) [16-17]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#select_as_value
+SELECT AS VALUE STRUCT(1 AS a, 2 AS b) xyz
+--
+QueryStatement [0-42]
+  Query [0-42]
+    Select [0-42]
+      SelectAs(as_mode=VALUE) [7-15]
+      SelectList [16-42]
+        SelectColumn [16-42]
+          StructConstructorWithKeyword [16-38]
+            StructConstructorArg [23-29]
+              IntLiteral(1) [23-24]
+              Alias [25-29]
+                Identifier(a) [28-29]
+            StructConstructorArg [31-37]
+              IntLiteral(2) [31-32]
+              Alias [33-37]
+                Identifier(b) [36-37]
+          Alias [39-42]
+            Identifier(xyz) [39-42]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#select_as_value
+SELECT AS VALUE v FROM (SELECT AS STRUCT 1 a, true b) v WHERE v.b
+--
+QueryStatement [0-65]
+  Query [0-65]
+    Select [0-65]
+      SelectAs(as_mode=VALUE) [7-15]
+      SelectList [16-17]
+        SelectColumn [16-17]
+          PathExpression [16-17]
+            Identifier(v) [16-17]
+      FromClause [18-55]
+        TableSubquery [23-55]
+          Query [24-52]
+            Select [24-52]
+              SelectAs(as_mode=STRUCT) [31-40]
+              SelectList [41-52]
+                SelectColumn [41-44]
+                  IntLiteral(1) [41-42]
+                  Alias [43-44]
+                    Identifier(a) [43-44]
+                SelectColumn [46-52]
+                  BooleanLiteral(true) [46-50]
+                  Alias [51-52]
+                    Identifier(b) [51-52]
+          Alias [54-55]
+            Identifier(v) [54-55]
+      WhereClause [56-65]
+        PathExpression [62-65]
+          Identifier(v) [62-63]
+          Identifier(b) [64-65]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#field_path
+SELECT * FROM T1 t1, t1.array_column
+--
+QueryStatement [0-36]
+  Query [0-36]
+    Select [0-36]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-36]
+        Join(COMMA) [14-36]
+          TablePathExpression [14-19]
+            PathExpression [14-16]
+              Identifier(T1) [14-16]
+            Alias [17-19]
+              Identifier(t1) [17-19]
+          Location [19-20]
+          TablePathExpression [21-36]
+            PathExpression [21-36]
+              Identifier(t1) [21-23]
+              Identifier(array_column) [24-36]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#field_path
+SELECT * FROM T1 t1, t1.struct_column.array_field
+--
+QueryStatement [0-49]
+  Query [0-49]
+    Select [0-49]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-49]
+        Join(COMMA) [14-49]
+          TablePathExpression [14-19]
+            PathExpression [14-16]
+              Identifier(T1) [14-16]
+            Alias [17-19]
+              Identifier(t1) [17-19]
+          Location [19-20]
+          TablePathExpression [21-49]
+            PathExpression [21-49]
+              Identifier(t1) [21-23]
+              Identifier(struct_column) [24-37]
+              Identifier(array_field) [38-49]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#field_path
+SELECT (SELECT ARRAY_AGG(c) FROM t1.array_column c) FROM T1 t1
+--
+QueryStatement [0-62]
+  Query [0-62]
+    Select [0-62]
+      SelectList [7-51]
+        SelectColumn [7-51]
+          ExpressionSubquery [7-51]
+            Query [8-50]
+              Select [8-50]
+                SelectList [15-27]
+                  SelectColumn [15-27]
+                    FunctionCall [15-27]
+                      PathExpression [15-24]
+                        Identifier(ARRAY_AGG) [15-24]
+                      PathExpression [25-26]
+                        Identifier(c) [25-26]
+                FromClause [28-50]
+                  TablePathExpression [33-50]
+                    PathExpression [33-48]
+                      Identifier(t1) [33-35]
+                      Identifier(array_column) [36-48]
+                    Alias [49-50]
+                      Identifier(c) [49-50]
+      FromClause [52-62]
+        TablePathExpression [57-62]
+          PathExpression [57-59]
+            Identifier(T1) [57-59]
+          Alias [60-62]
+            Identifier(t1) [60-62]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#field_path
+SELECT a.struct_field1 FROM T1 t1, t1.array_of_structs a
+--
+QueryStatement [0-56]
+  Query [0-56]
+    Select [0-56]
+      SelectList [7-22]
+        SelectColumn [7-22]
+          PathExpression [7-22]
+            Identifier(a) [7-8]
+            Identifier(struct_field1) [9-22]
+      FromClause [23-56]
+        Join(COMMA) [28-56]
+          TablePathExpression [28-33]
+            PathExpression [28-30]
+              Identifier(T1) [28-30]
+            Alias [31-33]
+              Identifier(t1) [31-33]
+          Location [33-34]
+          TablePathExpression [35-56]
+            PathExpression [35-54]
+              Identifier(t1) [35-37]
+              Identifier(array_of_structs) [38-54]
+            Alias [55-56]
+              Identifier(a) [55-56]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#field_path
+SELECT (SELECT STRING_AGG(a.struct_field1) FROM t1.array_of_structs a) FROM T1 t1
+--
+QueryStatement [0-81]
+  Query [0-81]
+    Select [0-81]
+      SelectList [7-70]
+        SelectColumn [7-70]
+          ExpressionSubquery [7-70]
+            Query [8-69]
+              Select [8-69]
+                SelectList [15-42]
+                  SelectColumn [15-42]
+                    FunctionCall [15-42]
+                      PathExpression [15-25]
+                        Identifier(STRING_AGG) [15-25]
+                      PathExpression [26-41]
+                        Identifier(a) [26-27]
+                        Identifier(struct_field1) [28-41]
+                FromClause [43-69]
+                  TablePathExpression [48-69]
+                    PathExpression [48-67]
+                      Identifier(t1) [48-50]
+                      Identifier(array_of_structs) [51-67]
+                    Alias [68-69]
+                      Identifier(a) [68-69]
+      FromClause [71-81]
+        TablePathExpression [76-81]
+          PathExpression [76-78]
+            Identifier(T1) [76-78]
+          Alias [79-81]
+            Identifier(t1) [79-81]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#cte_name
+WITH
+  subQ1 AS (SELECT * FROM Roster WHERE SchoolID = 52),
+  subQ2 AS (SELECT SchoolID FROM subQ1)
+SELECT DISTINCT * FROM subQ2
+--
+QueryStatement [0-128]
+  Query [0-128]
+    WithClause [0-99]
+      WithClauseEntry [7-58]
+        AliasedQuery [7-58]
+          Identifier(subQ1) [7-12]
+          Query [16-58]
+            Select [17-57]
+              SelectList [24-25]
+                SelectColumn [24-25]
+                  Star(*) [24-25]
+              FromClause [26-37]
+                TablePathExpression [31-37]
+                  PathExpression [31-37]
+                    Identifier(Roster) [31-37]
+              WhereClause [38-57]
+                BinaryExpression(=) [44-57]
+                  PathExpression [44-52]
+                    Identifier(SchoolID) [44-52]
+                  IntLiteral(52) [55-57]
+      WithClauseEntry [62-99]
+        AliasedQuery [62-99]
+          Identifier(subQ2) [62-67]
+          Query [71-99]
+            Select [72-98]
+              SelectList [79-87]
+                SelectColumn [79-87]
+                  PathExpression [79-87]
+                    Identifier(SchoolID) [79-87]
+              FromClause [88-98]
+                TablePathExpression [93-98]
+                  PathExpression [93-98]
+                    Identifier(subQ1) [93-98]
+    Select(distinct=true) [100-128]
+      SelectList [116-117]
+        SelectColumn [116-117]
+          Star(*) [116-117]
+      FromClause [118-128]
+        TablePathExpression [123-128]
+          PathExpression [123-128]
+            Identifier(subQ2) [123-128]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#unnest_operator
+SELECT * FROM UNNEST ([10,20,30]) as numbers WITH OFFSET
+--
+QueryStatement [0-56]
+  Query [0-56]
+    Select [0-56]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-56]
+        TablePathExpression [14-56]
+          UnnestExpression [14-33]
+            ExpressionWithOptAlias [22-32]
+              ArrayConstructor [22-32]
+                IntLiteral(10) [23-25]
+                IntLiteral(20) [26-28]
+                IntLiteral(30) [29-31]
+          Alias [34-44]
+            Identifier(numbers) [37-44]
+          WithOffset [45-56]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#unnest_and_structs
+SELECT *
+FROM UNNEST(
+  ARRAY<
+    STRUCT<
+      x INT64,
+      y STRING,
+      z ARRAY>>[
+        (1, 'foo', [10, 11]),
+        (3, 'bar', [20, 21])])
+--
+QueryStatement [0-158]
+  Query [0-158]
+    Select [0-158]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-158]
+        TablePathExpression [14-158]
+          UnnestExpression [14-158]
+            ExpressionWithOptAlias [24-157]
+              ArrayConstructor [24-157]
+                ArrayType [24-96]
+                  StructType [35-95]
+                    StructField [49-56]
+                      Identifier(x) [49-50]
+                      SimpleType [51-56]
+                        PathExpression [51-56]
+                          Identifier(INT64) [51-56]
+                    StructField [64-72]
+                      Identifier(y) [64-65]
+                      SimpleType [66-72]
+                        PathExpression [66-72]
+                          Identifier(STRING) [66-72]
+                    StructField [80-94]
+                      Identifier(z) [80-81]
+                      ArrayType [82-94]
+                        SimpleType [88-93]
+                          PathExpression [88-93]
+                            Identifier(INT64) [88-93]
+                StructConstructorWithParens [106-126]
+                  IntLiteral(1) [107-108]
+                  StringLiteral [110-115]
+                    StringLiteralComponent('foo') [110-115]
+                  ArrayConstructor [117-125]
+                    IntLiteral(10) [118-120]
+                    IntLiteral(11) [122-124]
+                StructConstructorWithParens [136-156]
+                  IntLiteral(3) [137-138]
+                  StringLiteral [140-145]
+                    StringLiteralComponent('bar') [140-145]
+                  ArrayConstructor [147-155]
+                    IntLiteral(20) [148-150]
+                    IntLiteral(21) [152-154]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#unnest_and_protocol_buffers
+SELECT *
+FROM UNNEST(
+  ARRAY[
+    NEW googlesql.examples.music.Album (
+      'The Goldberg Variations' AS album_name,
+\      ['Aria', 'Variation 1', 'Variation 2'] AS song
+    )
+  ]
+)
+--
+QueryStatement [0-215]
+  Query [0-215]
+    Select [0-215]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-215]
+        TablePathExpression [14-215]
+          UnnestExpression [14-215]
+            ExpressionWithOptAlias [24-213]
+              ArrayConstructor [24-213]
+                ArrayType [24-61]
+                  SimpleType [30-60]
+                    PathExpression [30-60]
+                      Identifier(googlesql) [30-39]
+                      Identifier(examples) [40-48]
+                      Identifier(music) [49-54]
+                      Identifier(Album) [55-60]
+                NewConstructor [67-209]
+                  SimpleType [71-101]
+                    PathExpression [71-101]
+                      Identifier(googlesql) [71-80]
+                      Identifier(examples) [81-89]
+                      Identifier(music) [90-95]
+                      Identifier(Album) [96-101]
+                  NewConstructorArg [110-149]
+                    StringLiteral [110-135]
+                      StringLiteralComponent('The Goldberg Variations') [110-135]
+                    Identifier(album_name) [139-149]
+                  NewConstructorArg [157-203]
+                    ArrayConstructor [157-195]
+                      StringLiteral [158-164]
+                        StringLiteralComponent('Aria') [158-164]
+                      StringLiteral [166-179]
+                        StringLiteralComponent('Variation 1') [166-179]
+                      StringLiteral [181-194]
+                        StringLiteralComponent('Variation 2') [181-194]
+                    Identifier(song) [199-203]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#unnest_and_protocol_buffers
+SELECT proto_value
+FROM UNNEST(
+  ARRAY[
+    NEW googlesql.examples.music.Album (
+      'The Goldberg Variations' AS album_name,
+\      ['Aria', 'Var. 1'] AS song
+    )
+  ]
+) AS proto_value
+--
+QueryStatement [0-220]
+  Query [0-220]
+    Select [0-220]
+      SelectList [7-18]
+        SelectColumn [7-18]
+          PathExpression [7-18]
+            Identifier(proto_value) [7-18]
+      FromClause [19-220]
+        TablePathExpression [24-220]
+          UnnestExpression [24-205]
+            ExpressionWithOptAlias [34-203]
+              ArrayConstructor [34-203]
+                ArrayType [34-71]
+                  SimpleType [40-70]
+                    PathExpression [40-70]
+                      Identifier(googlesql) [40-49]
+                      Identifier(examples) [50-58]
+                      Identifier(music) [59-64]
+                      Identifier(Album) [65-70]
+                NewConstructor [77-199]
+                  SimpleType [81-111]
+                    PathExpression [81-111]
+                      Identifier(googlesql) [81-90]
+                      Identifier(examples) [91-99]
+                      Identifier(music) [100-105]
+                      Identifier(Album) [106-111]
+                  NewConstructorArg [120-159]
+                    StringLiteral [120-145]
+                      StringLiteralComponent('The Goldberg Variations') [120-145]
+                    Identifier(album_name) [149-159]
+                  NewConstructorArg [167-193]
+                    ArrayConstructor [167-185]
+                      StringLiteral [168-174]
+                        StringLiteralComponent('Aria') [168-174]
+                      StringLiteral [176-184]
+                        StringLiteralComponent('Var. 1') [176-184]
+                    Identifier(song) [189-193]
+          Alias [206-220]
+            Identifier(proto_value) [209-220]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#explicit_unnesting
+WITH Coordinates AS (SELECT [1,2] AS position)
+SELECT results FROM Coordinates, UNNEST(Coordinates.position) AS results
+--
+QueryStatement [0-119]
+  Query [0-119]
+    WithClause [0-46]
+      WithClauseEntry [5-46]
+        AliasedQuery [5-46]
+          Identifier(Coordinates) [5-16]
+          Query [20-46]
+            Select [21-45]
+              SelectList [28-45]
+                SelectColumn [28-45]
+                  ArrayConstructor [28-33]
+                    IntLiteral(1) [29-30]
+                    IntLiteral(2) [31-32]
+                  Alias [34-45]
+                    Identifier(position) [37-45]
+    Select [47-119]
+      SelectList [54-61]
+        SelectColumn [54-61]
+          PathExpression [54-61]
+            Identifier(results) [54-61]
+      FromClause [62-119]
+        Join(COMMA) [67-119]
+          TablePathExpression [67-78]
+            PathExpression [67-78]
+              Identifier(Coordinates) [67-78]
+          Location [78-79]
+          TablePathExpression [80-119]
+            UnnestExpression [80-108]
+              ExpressionWithOptAlias [87-107]
+                PathExpression [87-107]
+                  Identifier(Coordinates) [87-98]
+                  Identifier(position) [99-107]
+            Alias [109-119]
+              Identifier(results) [112-119]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#implicit_unnesting
+WITH Coordinates AS (SELECT [1,2] AS position)
+SELECT results FROM Coordinates, Coordinates.position AS results
+--
+QueryStatement [0-111]
+  Query [0-111]
+    WithClause [0-46]
+      WithClauseEntry [5-46]
+        AliasedQuery [5-46]
+          Identifier(Coordinates) [5-16]
+          Query [20-46]
+            Select [21-45]
+              SelectList [28-45]
+                SelectColumn [28-45]
+                  ArrayConstructor [28-33]
+                    IntLiteral(1) [29-30]
+                    IntLiteral(2) [31-32]
+                  Alias [34-45]
+                    Identifier(position) [37-45]
+    Select [47-111]
+      SelectList [54-61]
+        SelectColumn [54-61]
+          PathExpression [54-61]
+            Identifier(results) [54-61]
+      FromClause [62-111]
+        Join(COMMA) [67-111]
+          TablePathExpression [67-78]
+            PathExpression [67-78]
+              Identifier(Coordinates) [67-78]
+          Location [78-79]
+          TablePathExpression [80-111]
+            PathExpression [80-100]
+              Identifier(Coordinates) [80-91]
+              Identifier(position) [92-100]
+            Alias [101-111]
+              Identifier(results) [104-111]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#tablesample_operator
+SELECT MessageId
+FROM Messages TABLESAMPLE RESERVOIR (100 ROWS)
+--
+QueryStatement [0-63]
+  Query [0-63]
+    Select [0-63]
+      SelectList [7-16]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(MessageId) [7-16]
+      FromClause [17-63]
+        TablePathExpression [22-63]
+          PathExpression [22-30]
+            Identifier(Messages) [22-30]
+          SampleClause [31-63]
+            Identifier(RESERVOIR) [43-52]
+            SampleSize [54-62]
+              IntLiteral(100) [54-57]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#tablesample_operator
+SELECT MessageId
+FROM Messages TABLESAMPLE BERNOULLI (0.1 PERCENT)
+--
+QueryStatement [0-66]
+  Query [0-66]
+    Select [0-66]
+      SelectList [7-16]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(MessageId) [7-16]
+      FromClause [17-66]
+        TablePathExpression [22-66]
+          PathExpression [22-30]
+            Identifier(Messages) [22-30]
+          SampleClause [31-66]
+            Identifier(BERNOULLI) [43-52]
+            SampleSize [54-65]
+              FloatLiteral(0.1) [54-57]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#tablesample_operator
+SELECT Subject FROM
+(SELECT MessageId, Subject FROM Messages WHERE ServerId="test")
+TABLESAMPLE BERNOULLI(50 PERCENT)
+WHERE MessageId > 3
+--
+QueryStatement [0-137]
+  Query [0-137]
+    Select [0-137]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(Subject) [7-14]
+      FromClause [15-117]
+        TableSubquery [20-117]
+          Query [21-82]
+            Select [21-82]
+              SelectList [28-46]
+                SelectColumn [28-37]
+                  PathExpression [28-37]
+                    Identifier(MessageId) [28-37]
+                SelectColumn [39-46]
+                  PathExpression [39-46]
+                    Identifier(Subject) [39-46]
+              FromClause [47-60]
+                TablePathExpression [52-60]
+                  PathExpression [52-60]
+                    Identifier(Messages) [52-60]
+              WhereClause [61-82]
+                BinaryExpression(=) [67-82]
+                  PathExpression [67-75]
+                    Identifier(ServerId) [67-75]
+                  StringLiteral [76-82]
+                    StringLiteralComponent("test") [76-82]
+          SampleClause [84-117]
+            Identifier(BERNOULLI) [96-105]
+            SampleSize [106-116]
+              IntLiteral(50) [106-108]
+      WhereClause [118-137]
+        BinaryExpression(>) [124-137]
+          PathExpression [124-133]
+            Identifier(MessageId) [124-133]
+          IntLiteral(3) [136-137]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#tablesample_operator
+SELECT S.Subject
+FROM
+(SELECT MessageId, ThreadId FROM Messages WHERE ServerId="test") AS R
+TABLESAMPLE RESERVOIR(5 ROWS),
+Threads AS S
+WHERE S.ServerId="test" AND R.ThreadId = S.ThreadId
+--
+QueryStatement [0-187]
+  Query [0-187]
+    Select [0-187]
+      SelectList [7-16]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(S) [7-8]
+            Identifier(Subject) [9-16]
+      FromClause [17-135]
+        Join(COMMA) [22-135]
+          TableSubquery [22-121]
+            Query [23-85]
+              Select [23-85]
+                SelectList [30-49]
+                  SelectColumn [30-39]
+                    PathExpression [30-39]
+                      Identifier(MessageId) [30-39]
+                  SelectColumn [41-49]
+                    PathExpression [41-49]
+                      Identifier(ThreadId) [41-49]
+                FromClause [50-63]
+                  TablePathExpression [55-63]
+                    PathExpression [55-63]
+                      Identifier(Messages) [55-63]
+                WhereClause [64-85]
+                  BinaryExpression(=) [70-85]
+                    PathExpression [70-78]
+                      Identifier(ServerId) [70-78]
+                    StringLiteral [79-85]
+                      StringLiteralComponent("test") [79-85]
+            Alias [87-91]
+              Identifier(R) [90-91]
+            SampleClause [92-121]
+              Identifier(RESERVOIR) [104-113]
+              SampleSize [114-120]
+                IntLiteral(5) [114-115]
+          Location [121-122]
+          TablePathExpression [123-135]
+            PathExpression [123-130]
+              Identifier(Threads) [123-130]
+            Alias [131-135]
+              Identifier(S) [134-135]
+      WhereClause [136-187]
+        AndExpr [142-187]
+          BinaryExpression(=) [142-159]
+            PathExpression [142-152]
+              Identifier(S) [142-143]
+              Identifier(ServerId) [144-152]
+            StringLiteral [153-159]
+              StringLiteralComponent("test") [153-159]
+          BinaryExpression(=) [164-187]
+            PathExpression [164-174]
+              Identifier(R) [164-165]
+              Identifier(ThreadId) [166-174]
+            PathExpression [177-187]
+              Identifier(S) [177-178]
+              Identifier(ThreadId) [179-187]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#inner_join
+FROM A INNER JOIN B ON A.w = B.y
+
+/*
+Table A       Table B       Result
++-------+     +-------+     +---------------+
+| w | x |  *  | y | z |  =  | w | x | y | z |
++-------+     +-------+     +---------------+
+| 1 | a |     | 2 | k |     | 2 | b | 2 | k |
+| 2 | b |     | 3 | m |     | 3 | c | 3 | m |
+| 3 | c |     | 3 | n |     | 3 | c | 3 | n |
+| 3 | d |     | 4 | p |     | 3 | d | 3 | m |
++-------+     +-------+     | 3 | d | 3 | n |
+                            +---------------+
+*/
+--
+QueryStatement [0-32]
+  Query [0-32]
+    FromQuery [0-32]
+      FromClause [0-32]
+        Join(INNER) [5-32]
+          TablePathExpression [5-6]
+            PathExpression [5-6]
+              Identifier(A) [5-6]
+          Location [7-17]
+          TablePathExpression [18-19]
+            PathExpression [18-19]
+              Identifier(B) [18-19]
+          OnClause [20-32]
+            BinaryExpression(=) [23-32]
+              PathExpression [23-26]
+                Identifier(A) [23-24]
+                Identifier(w) [25-26]
+              PathExpression [29-32]
+                Identifier(B) [29-30]
+                Identifier(y) [31-32]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#inner_join
+FROM A INNER JOIN B USING (x)
+
+/*
+Table A       Table B       Result
++-------+     +-------+     +-----------+
+| x | y |  *  | x | z |  =  | x | y | z |
++-------+     +-------+     +-----------+
+| 1 | a |     | 2 | k |     | 2 | b | k |
+| 2 | b |     | 3 | m |     | 3 | c | m |
+| 3 | c |     | 3 | n |     | 3 | c | n |
+| 3 | d |     | 4 | p |     | 3 | d | m |
++-------+     +-------+     | 3 | d | n |
+                            +-----------+
+*/
+--
+QueryStatement [0-29]
+  Query [0-29]
+    FromQuery [0-29]
+      FromClause [0-29]
+        Join(INNER) [5-29]
+          TablePathExpression [5-6]
+            PathExpression [5-6]
+              Identifier(A) [5-6]
+          Location [7-17]
+          TablePathExpression [18-19]
+            PathExpression [18-19]
+              Identifier(B) [18-19]
+          UsingClause [20-29]
+            Identifier(x) [27-28]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#inner_join
+SELECT Roster.LastName, TeamMascot.Mascot
+FROM Roster JOIN TeamMascot ON Roster.SchoolID = TeamMascot.SchoolID
+--
+QueryStatement [0-110]
+  Query [0-110]
+    Select [0-110]
+      SelectList [7-41]
+        SelectColumn [7-22]
+          PathExpression [7-22]
+            Identifier(Roster) [7-13]
+            Identifier(LastName) [14-22]
+        SelectColumn [24-41]
+          PathExpression [24-41]
+            Identifier(TeamMascot) [24-34]
+            Identifier(Mascot) [35-41]
+      FromClause [42-110]
+        Join [47-110]
+          TablePathExpression [47-53]
+            PathExpression [47-53]
+              Identifier(Roster) [47-53]
+          Location [54-58]
+          TablePathExpression [59-69]
+            PathExpression [59-69]
+              Identifier(TeamMascot) [59-69]
+          OnClause [70-110]
+            BinaryExpression(=) [73-110]
+              PathExpression [73-88]
+                Identifier(Roster) [73-79]
+                Identifier(SchoolID) [80-88]
+              PathExpression [91-110]
+                Identifier(TeamMascot) [91-101]
+                Identifier(SchoolID) [102-110]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#cross_join
+FROM A CROSS JOIN B
+
+/*
+Table A       Table B       Result
++-------+     +-------+     +---------------+
+| w | x |  *  | y | z |  =  | w | x | y | z |
++-------+     +-------+     +---------------+
+| 1 | a |     | 2 | c |     | 1 | a | 2 | c |
+| 2 | b |     | 3 | d |     | 1 | a | 3 | d |
++-------+     +-------+     | 2 | b | 2 | c |
+                            | 2 | b | 3 | d |
+                            +---------------+
+*/
+--
+QueryStatement [0-19]
+  Query [0-19]
+    FromQuery [0-19]
+      FromClause [0-19]
+        Join(CROSS) [5-19]
+          TablePathExpression [5-6]
+            PathExpression [5-6]
+              Identifier(A) [5-6]
+          Location [7-17]
+          TablePathExpression [18-19]
+            PathExpression [18-19]
+              Identifier(B) [18-19]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#cross_join
+SELECT Roster.LastName, TeamMascot.Mascot
+FROM Roster CROSS JOIN TeamMascot
+--
+QueryStatement [0-75]
+  Query [0-75]
+    Select [0-75]
+      SelectList [7-41]
+        SelectColumn [7-22]
+          PathExpression [7-22]
+            Identifier(Roster) [7-13]
+            Identifier(LastName) [14-22]
+        SelectColumn [24-41]
+          PathExpression [24-41]
+            Identifier(TeamMascot) [24-34]
+            Identifier(Mascot) [35-41]
+      FromClause [42-75]
+        Join(CROSS) [47-75]
+          TablePathExpression [47-53]
+            PathExpression [47-53]
+              Identifier(Roster) [47-53]
+          Location [54-64]
+          TablePathExpression [65-75]
+            PathExpression [65-75]
+              Identifier(TeamMascot) [65-75]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#comma_cross_join
+FROM A, B
+
+/*
+Table A       Table B       Result
++-------+     +-------+     +---------------+
+| w | x |  *  | y | z |  =  | w | x | y | z |
++-------+     +-------+     +---------------+
+| 1 | a |     | 2 | c |     | 1 | a | 2 | c |
+| 2 | b |     | 3 | d |     | 1 | a | 3 | d |
++-------+     +-------+     | 2 | b | 2 | c |
+                            | 2 | b | 3 | d |
+                            +---------------+
+*/
+--
+QueryStatement [0-9]
+  Query [0-9]
+    FromQuery [0-9]
+      FromClause [0-9]
+        Join(COMMA) [5-9]
+          TablePathExpression [5-6]
+            PathExpression [5-6]
+              Identifier(A) [5-6]
+          Location [6-7]
+          TablePathExpression [8-9]
+            PathExpression [8-9]
+              Identifier(B) [8-9]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#comma_cross_join
+SELECT Roster.LastName, TeamMascot.Mascot
+FROM Roster, TeamMascot
+--
+QueryStatement [0-65]
+  Query [0-65]
+    Select [0-65]
+      SelectList [7-41]
+        SelectColumn [7-22]
+          PathExpression [7-22]
+            Identifier(Roster) [7-13]
+            Identifier(LastName) [14-22]
+        SelectColumn [24-41]
+          PathExpression [24-41]
+            Identifier(TeamMascot) [24-34]
+            Identifier(Mascot) [35-41]
+      FromClause [42-65]
+        Join(COMMA) [47-65]
+          TablePathExpression [47-53]
+            PathExpression [47-53]
+              Identifier(Roster) [47-53]
+          Location [53-54]
+          TablePathExpression [55-65]
+            PathExpression [55-65]
+              Identifier(TeamMascot) [55-65]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#full_join
+FROM A FULL OUTER JOIN B ON A.w = B.y
+
+/*
+Table A       Table B       Result
++-------+     +-------+     +---------------------------+
+| w | x |  *  | y | z |  =  | w    | x    | y    | z    |
++-------+     +-------+     +---------------------------+
+| 1 | a |     | 2 | k |     | 1    | a    | NULL | NULL |
+| 2 | b |     | 3 | m |     | 2    | b    | 2    | k    |
+| 3 | c |     | 3 | n |     | 3    | c    | 3    | m    |
+| 3 | d |     | 4 | p |     | 3    | c    | 3    | n    |
++-------+     +-------+     | 3    | d    | 3    | m    |
+                            | 3    | d    | 3    | n    |
+                            | NULL | NULL | 4    | p    |
+                            +---------------------------+
+*/
+--
+QueryStatement [0-37]
+  Query [0-37]
+    FromQuery [0-37]
+      FromClause [0-37]
+        Join(FULL) [5-37]
+          TablePathExpression [5-6]
+            PathExpression [5-6]
+              Identifier(A) [5-6]
+          Location [7-22]
+          TablePathExpression [23-24]
+            PathExpression [23-24]
+              Identifier(B) [23-24]
+          OnClause [25-37]
+            BinaryExpression(=) [28-37]
+              PathExpression [28-31]
+                Identifier(A) [28-29]
+                Identifier(w) [30-31]
+              PathExpression [34-37]
+                Identifier(B) [34-35]
+                Identifier(y) [36-37]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#full_join
+FROM A FULL OUTER JOIN B USING (x)
+
+/*
+Table A       Table B       Result
++-------+     +-------+     +--------------------+
+| x | y |  *  | x | z |  =  | x    | y    | z    |
++-------+     +-------+     +--------------------+
+| 1 | a |     | 2 | k |     | 1    | a    | NULL |
+| 2 | b |     | 3 | m |     | 2    | b    | k    |
+| 3 | c |     | 3 | n |     | 3    | c    | m    |
+| 3 | d |     | 4 | p |     | 3    | c    | n    |
++-------+     +-------+     | 3    | d    | m    |
+                            | 3    | d    | n    |
+                            | 4    | NULL | p    |
+                            +--------------------+
+*/
+--
+QueryStatement [0-34]
+  Query [0-34]
+    FromQuery [0-34]
+      FromClause [0-34]
+        Join(FULL) [5-34]
+          TablePathExpression [5-6]
+            PathExpression [5-6]
+              Identifier(A) [5-6]
+          Location [7-22]
+          TablePathExpression [23-24]
+            PathExpression [23-24]
+              Identifier(B) [23-24]
+          UsingClause [25-34]
+            Identifier(x) [32-33]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#full_join
+SELECT Roster.LastName, TeamMascot.Mascot
+FROM Roster FULL JOIN TeamMascot ON Roster.SchoolID = TeamMascot.SchoolID
+--
+QueryStatement [0-115]
+  Query [0-115]
+    Select [0-115]
+      SelectList [7-41]
+        SelectColumn [7-22]
+          PathExpression [7-22]
+            Identifier(Roster) [7-13]
+            Identifier(LastName) [14-22]
+        SelectColumn [24-41]
+          PathExpression [24-41]
+            Identifier(TeamMascot) [24-34]
+            Identifier(Mascot) [35-41]
+      FromClause [42-115]
+        Join(FULL) [47-115]
+          TablePathExpression [47-53]
+            PathExpression [47-53]
+              Identifier(Roster) [47-53]
+          Location [54-63]
+          TablePathExpression [64-74]
+            PathExpression [64-74]
+              Identifier(TeamMascot) [64-74]
+          OnClause [75-115]
+            BinaryExpression(=) [78-115]
+              PathExpression [78-93]
+                Identifier(Roster) [78-84]
+                Identifier(SchoolID) [85-93]
+              PathExpression [96-115]
+                Identifier(TeamMascot) [96-106]
+                Identifier(SchoolID) [107-115]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#left_join
+FROM A LEFT OUTER JOIN B ON A.w = B.y
+
+/*
+Table A       Table B       Result
++-------+     +-------+     +---------------------------+
+| w | x |  *  | y | z |  =  | w    | x    | y    | z    |
++-------+     +-------+     +---------------------------+
+| 1 | a |     | 2 | k |     | 1    | a    | NULL | NULL |
+| 2 | b |     | 3 | m |     | 2    | b    | 2    | k    |
+| 3 | c |     | 3 | n |     | 3    | c    | 3    | m    |
+| 3 | d |     | 4 | p |     | 3    | c    | 3    | n    |
++-------+     +-------+     | 3    | d    | 3    | m    |
+                            | 3    | d    | 3    | n    |
+                            +---------------------------+
+*/
+--
+QueryStatement [0-37]
+  Query [0-37]
+    FromQuery [0-37]
+      FromClause [0-37]
+        Join(LEFT) [5-37]
+          TablePathExpression [5-6]
+            PathExpression [5-6]
+              Identifier(A) [5-6]
+          Location [7-22]
+          TablePathExpression [23-24]
+            PathExpression [23-24]
+              Identifier(B) [23-24]
+          OnClause [25-37]
+            BinaryExpression(=) [28-37]
+              PathExpression [28-31]
+                Identifier(A) [28-29]
+                Identifier(w) [30-31]
+              PathExpression [34-37]
+                Identifier(B) [34-35]
+                Identifier(y) [36-37]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#left_join
+FROM A LEFT OUTER JOIN B USING (x)
+
+/*
+Table A       Table B       Result
++-------+     +-------+     +--------------------+
+| x | y |  *  | x | z |  =  | x    | y    | z    |
++-------+     +-------+     +--------------------+
+| 1 | a |     | 2 | k |     | 1    | a    | NULL |
+| 2 | b |     | 3 | m |     | 2    | b    | k    |
+| 3 | c |     | 3 | n |     | 3    | c    | m    |
+| 3 | d |     | 4 | p |     | 3    | c    | n    |
++-------+     +-------+     | 3    | d    | m    |
+                            | 3    | d    | n    |
+                            +--------------------+
+*/
+--
+QueryStatement [0-34]
+  Query [0-34]
+    FromQuery [0-34]
+      FromClause [0-34]
+        Join(LEFT) [5-34]
+          TablePathExpression [5-6]
+            PathExpression [5-6]
+              Identifier(A) [5-6]
+          Location [7-22]
+          TablePathExpression [23-24]
+            PathExpression [23-24]
+              Identifier(B) [23-24]
+          UsingClause [25-34]
+            Identifier(x) [32-33]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#left_join
+SELECT Roster.LastName, TeamMascot.Mascot
+FROM Roster LEFT JOIN TeamMascot ON Roster.SchoolID = TeamMascot.SchoolID
+--
+QueryStatement [0-115]
+  Query [0-115]
+    Select [0-115]
+      SelectList [7-41]
+        SelectColumn [7-22]
+          PathExpression [7-22]
+            Identifier(Roster) [7-13]
+            Identifier(LastName) [14-22]
+        SelectColumn [24-41]
+          PathExpression [24-41]
+            Identifier(TeamMascot) [24-34]
+            Identifier(Mascot) [35-41]
+      FromClause [42-115]
+        Join(LEFT) [47-115]
+          TablePathExpression [47-53]
+            PathExpression [47-53]
+              Identifier(Roster) [47-53]
+          Location [54-63]
+          TablePathExpression [64-74]
+            PathExpression [64-74]
+              Identifier(TeamMascot) [64-74]
+          OnClause [75-115]
+            BinaryExpression(=) [78-115]
+              PathExpression [78-93]
+                Identifier(Roster) [78-84]
+                Identifier(SchoolID) [85-93]
+              PathExpression [96-115]
+                Identifier(TeamMascot) [96-106]
+                Identifier(SchoolID) [107-115]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#right_join
+FROM A RIGHT OUTER JOIN B ON A.w = B.y
+
+/*
+Table A       Table B       Result
++-------+     +-------+     +---------------------------+
+| w | x |  *  | y | z |  =  | w    | x    | y    | z    |
++-------+     +-------+     +---------------------------+
+| 1 | a |     | 2 | k |     | 2    | b    | 2    | k    |
+| 2 | b |     | 3 | m |     | 3    | c    | 3    | m    |
+| 3 | c |     | 3 | n |     | 3    | c    | 3    | n    |
+| 3 | d |     | 4 | p |     | 3    | d    | 3    | m    |
++-------+     +-------+     | 3    | d    | 3    | n    |
+                            | NULL | NULL | 4    | p    |
+                            +---------------------------+
+*/
+--
+QueryStatement [0-38]
+  Query [0-38]
+    FromQuery [0-38]
+      FromClause [0-38]
+        Join(RIGHT) [5-38]
+          TablePathExpression [5-6]
+            PathExpression [5-6]
+              Identifier(A) [5-6]
+          Location [7-23]
+          TablePathExpression [24-25]
+            PathExpression [24-25]
+              Identifier(B) [24-25]
+          OnClause [26-38]
+            BinaryExpression(=) [29-38]
+              PathExpression [29-32]
+                Identifier(A) [29-30]
+                Identifier(w) [31-32]
+              PathExpression [35-38]
+                Identifier(B) [35-36]
+                Identifier(y) [37-38]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#right_join
+FROM A RIGHT OUTER JOIN B USING (x)
+
+/*
+Table A       Table B       Result
++-------+     +-------+     +--------------------+
+| x | y |  *  | x | z |  =  | x    | y    | z    |
++-------+     +-------+     +--------------------+
+| 1 | a |     | 2 | k |     | 2    | b    | k    |
+| 2 | b |     | 3 | m |     | 3    | c    | m    |
+| 3 | c |     | 3 | n |     | 3    | c    | n    |
+| 3 | d |     | 4 | p |     | 3    | d    | m    |
++-------+     +-------+     | 3    | d    | n    |
+                            | 4    | NULL | p    |
+                            +--------------------+
+*/
+--
+QueryStatement [0-35]
+  Query [0-35]
+    FromQuery [0-35]
+      FromClause [0-35]
+        Join(RIGHT) [5-35]
+          TablePathExpression [5-6]
+            PathExpression [5-6]
+              Identifier(A) [5-6]
+          Location [7-23]
+          TablePathExpression [24-25]
+            PathExpression [24-25]
+              Identifier(B) [24-25]
+          UsingClause [26-35]
+            Identifier(x) [33-34]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#right_join
+SELECT Roster.LastName, TeamMascot.Mascot
+FROM Roster RIGHT JOIN TeamMascot ON Roster.SchoolID = TeamMascot.SchoolID
+--
+QueryStatement [0-116]
+  Query [0-116]
+    Select [0-116]
+      SelectList [7-41]
+        SelectColumn [7-22]
+          PathExpression [7-22]
+            Identifier(Roster) [7-13]
+            Identifier(LastName) [14-22]
+        SelectColumn [24-41]
+          PathExpression [24-41]
+            Identifier(TeamMascot) [24-34]
+            Identifier(Mascot) [35-41]
+      FromClause [42-116]
+        Join(RIGHT) [47-116]
+          TablePathExpression [47-53]
+            PathExpression [47-53]
+              Identifier(Roster) [47-53]
+          Location [54-64]
+          TablePathExpression [65-75]
+            PathExpression [65-75]
+              Identifier(TeamMascot) [65-75]
+          OnClause [76-116]
+            BinaryExpression(=) [79-116]
+              PathExpression [79-94]
+                Identifier(Roster) [79-85]
+                Identifier(SchoolID) [86-94]
+              PathExpression [97-116]
+                Identifier(TeamMascot) [97-107]
+                Identifier(SchoolID) [108-116]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#on_clause
+WITH
+  A AS ( SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 3),
+  B AS ( SELECT 2 as x UNION ALL SELECT 3 UNION ALL SELECT 4)
+SELECT * FROM A INNER JOIN B ON A.x = B.x
+--
+QueryStatement [0-171]
+  Query [0-171]
+    WithClause [0-129]
+      WithClauseEntry [7-66]
+        AliasedQuery [7-66]
+          Identifier(A) [7-8]
+          Query [12-66]
+            SetOperation(UNION ALL) [14-65]
+              SetOperationMetadataList [28-56]
+                SetOperationMetadata [28-37]
+                  SetOperationType [28-33]
+                  SetOperationAllOrDistinct [34-37]
+                SetOperationMetadata [47-56]
+                  SetOperationType [47-52]
+                  SetOperationAllOrDistinct [53-56]
+              Select [14-27]
+                SelectList [21-27]
+                  SelectColumn [21-27]
+                    IntLiteral(1) [21-22]
+                    Alias [23-27]
+                      Identifier(x) [26-27]
+              Select [38-46]
+                SelectList [45-46]
+                  SelectColumn [45-46]
+                    IntLiteral(2) [45-46]
+              Select [57-65]
+                SelectList [64-65]
+                  SelectColumn [64-65]
+                    IntLiteral(3) [64-65]
+      WithClauseEntry [70-129]
+        AliasedQuery [70-129]
+          Identifier(B) [70-71]
+          Query [75-129]
+            SetOperation(UNION ALL) [77-128]
+              SetOperationMetadataList [91-119]
+                SetOperationMetadata [91-100]
+                  SetOperationType [91-96]
+                  SetOperationAllOrDistinct [97-100]
+                SetOperationMetadata [110-119]
+                  SetOperationType [110-115]
+                  SetOperationAllOrDistinct [116-119]
+              Select [77-90]
+                SelectList [84-90]
+                  SelectColumn [84-90]
+                    IntLiteral(2) [84-85]
+                    Alias [86-90]
+                      Identifier(x) [89-90]
+              Select [101-109]
+                SelectList [108-109]
+                  SelectColumn [108-109]
+                    IntLiteral(3) [108-109]
+              Select [120-128]
+                SelectList [127-128]
+                  SelectColumn [127-128]
+                    IntLiteral(4) [127-128]
+    Select [130-171]
+      SelectList [137-138]
+        SelectColumn [137-138]
+          Star(*) [137-138]
+      FromClause [139-171]
+        Join(INNER) [144-171]
+          TablePathExpression [144-145]
+            PathExpression [144-145]
+              Identifier(A) [144-145]
+          Location [146-156]
+          TablePathExpression [157-158]
+            PathExpression [157-158]
+              Identifier(B) [157-158]
+          OnClause [159-171]
+            BinaryExpression(=) [162-171]
+              PathExpression [162-165]
+                Identifier(A) [162-163]
+                Identifier(x) [164-165]
+              PathExpression [168-171]
+                Identifier(B) [168-169]
+                Identifier(x) [170-171]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#on_clause
+WITH
+  A AS ( SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 3),
+  B AS ( SELECT 2 as x UNION ALL SELECT 3 UNION ALL SELECT 4)
+SELECT A.x, B.x FROM A INNER JOIN B ON A.x = B.x
+--
+QueryStatement [0-178]
+  Query [0-178]
+    WithClause [0-129]
+      WithClauseEntry [7-66]
+        AliasedQuery [7-66]
+          Identifier(A) [7-8]
+          Query [12-66]
+            SetOperation(UNION ALL) [14-65]
+              SetOperationMetadataList [28-56]
+                SetOperationMetadata [28-37]
+                  SetOperationType [28-33]
+                  SetOperationAllOrDistinct [34-37]
+                SetOperationMetadata [47-56]
+                  SetOperationType [47-52]
+                  SetOperationAllOrDistinct [53-56]
+              Select [14-27]
+                SelectList [21-27]
+                  SelectColumn [21-27]
+                    IntLiteral(1) [21-22]
+                    Alias [23-27]
+                      Identifier(x) [26-27]
+              Select [38-46]
+                SelectList [45-46]
+                  SelectColumn [45-46]
+                    IntLiteral(2) [45-46]
+              Select [57-65]
+                SelectList [64-65]
+                  SelectColumn [64-65]
+                    IntLiteral(3) [64-65]
+      WithClauseEntry [70-129]
+        AliasedQuery [70-129]
+          Identifier(B) [70-71]
+          Query [75-129]
+            SetOperation(UNION ALL) [77-128]
+              SetOperationMetadataList [91-119]
+                SetOperationMetadata [91-100]
+                  SetOperationType [91-96]
+                  SetOperationAllOrDistinct [97-100]
+                SetOperationMetadata [110-119]
+                  SetOperationType [110-115]
+                  SetOperationAllOrDistinct [116-119]
+              Select [77-90]
+                SelectList [84-90]
+                  SelectColumn [84-90]
+                    IntLiteral(2) [84-85]
+                    Alias [86-90]
+                      Identifier(x) [89-90]
+              Select [101-109]
+                SelectList [108-109]
+                  SelectColumn [108-109]
+                    IntLiteral(3) [108-109]
+              Select [120-128]
+                SelectList [127-128]
+                  SelectColumn [127-128]
+                    IntLiteral(4) [127-128]
+    Select [130-178]
+      SelectList [137-145]
+        SelectColumn [137-140]
+          PathExpression [137-140]
+            Identifier(A) [137-138]
+            Identifier(x) [139-140]
+        SelectColumn [142-145]
+          PathExpression [142-145]
+            Identifier(B) [142-143]
+            Identifier(x) [144-145]
+      FromClause [146-178]
+        Join(INNER) [151-178]
+          TablePathExpression [151-152]
+            PathExpression [151-152]
+              Identifier(A) [151-152]
+          Location [153-163]
+          TablePathExpression [164-165]
+            PathExpression [164-165]
+              Identifier(B) [164-165]
+          OnClause [166-178]
+            BinaryExpression(=) [169-178]
+              PathExpression [169-172]
+                Identifier(A) [169-170]
+                Identifier(x) [171-172]
+              PathExpression [175-178]
+                Identifier(B) [175-176]
+                Identifier(x) [177-178]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#on_clause
+WITH
+  A AS ( SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT NULL),
+  B AS ( SELECT 2 as x UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5)
+SELECT * FROM A LEFT OUTER JOIN B ON A.x = B.x
+--
+QueryStatement [0-217]
+  Query [0-217]
+    WithClause [0-170]
+      WithClauseEntry [7-88]
+        AliasedQuery [7-88]
+          Identifier(A) [7-8]
+          Query [12-88]
+            SetOperation(UNION ALL) [14-87]
+              SetOperationMetadataList [28-75]
+                SetOperationMetadata [28-37]
+                  SetOperationType [28-33]
+                  SetOperationAllOrDistinct [34-37]
+                SetOperationMetadata [47-56]
+                  SetOperationType [47-52]
+                  SetOperationAllOrDistinct [53-56]
+                SetOperationMetadata [66-75]
+                  SetOperationType [66-71]
+                  SetOperationAllOrDistinct [72-75]
+              Select [14-27]
+                SelectList [21-27]
+                  SelectColumn [21-27]
+                    IntLiteral(1) [21-22]
+                    Alias [23-27]
+                      Identifier(x) [26-27]
+              Select [38-46]
+                SelectList [45-46]
+                  SelectColumn [45-46]
+                    IntLiteral(2) [45-46]
+              Select [57-65]
+                SelectList [64-65]
+                  SelectColumn [64-65]
+                    IntLiteral(3) [64-65]
+              Select [76-87]
+                SelectList [83-87]
+                  SelectColumn [83-87]
+                    NullLiteral(NULL) [83-87]
+      WithClauseEntry [92-170]
+        AliasedQuery [92-170]
+          Identifier(B) [92-93]
+          Query [97-170]
+            SetOperation(UNION ALL) [99-169]
+              SetOperationMetadataList [113-160]
+                SetOperationMetadata [113-122]
+                  SetOperationType [113-118]
+                  SetOperationAllOrDistinct [119-122]
+                SetOperationMetadata [132-141]
+                  SetOperationType [132-137]
+                  SetOperationAllOrDistinct [138-141]
+                SetOperationMetadata [151-160]
+                  SetOperationType [151-156]
+                  SetOperationAllOrDistinct [157-160]
+              Select [99-112]
+                SelectList [106-112]
+                  SelectColumn [106-112]
+                    IntLiteral(2) [106-107]
+                    Alias [108-112]
+                      Identifier(x) [111-112]
+              Select [123-131]
+                SelectList [130-131]
+                  SelectColumn [130-131]
+                    IntLiteral(3) [130-131]
+              Select [142-150]
+                SelectList [149-150]
+                  SelectColumn [149-150]
+                    IntLiteral(4) [149-150]
+              Select [161-169]
+                SelectList [168-169]
+                  SelectColumn [168-169]
+                    IntLiteral(5) [168-169]
+    Select [171-217]
+      SelectList [178-179]
+        SelectColumn [178-179]
+          Star(*) [178-179]
+      FromClause [180-217]
+        Join(LEFT) [185-217]
+          TablePathExpression [185-186]
+            PathExpression [185-186]
+              Identifier(A) [185-186]
+          Location [187-202]
+          TablePathExpression [203-204]
+            PathExpression [203-204]
+              Identifier(B) [203-204]
+          OnClause [205-217]
+            BinaryExpression(=) [208-217]
+              PathExpression [208-211]
+                Identifier(A) [208-209]
+                Identifier(x) [210-211]
+              PathExpression [214-217]
+                Identifier(B) [214-215]
+                Identifier(x) [216-217]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#on_clause
+WITH
+  A AS ( SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT NULL),
+  B AS ( SELECT 2 as x UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5)
+SELECT A.x, B.x FROM A LEFT OUTER JOIN B ON A.x = B.x
+--
+QueryStatement [0-224]
+  Query [0-224]
+    WithClause [0-170]
+      WithClauseEntry [7-88]
+        AliasedQuery [7-88]
+          Identifier(A) [7-8]
+          Query [12-88]
+            SetOperation(UNION ALL) [14-87]
+              SetOperationMetadataList [28-75]
+                SetOperationMetadata [28-37]
+                  SetOperationType [28-33]
+                  SetOperationAllOrDistinct [34-37]
+                SetOperationMetadata [47-56]
+                  SetOperationType [47-52]
+                  SetOperationAllOrDistinct [53-56]
+                SetOperationMetadata [66-75]
+                  SetOperationType [66-71]
+                  SetOperationAllOrDistinct [72-75]
+              Select [14-27]
+                SelectList [21-27]
+                  SelectColumn [21-27]
+                    IntLiteral(1) [21-22]
+                    Alias [23-27]
+                      Identifier(x) [26-27]
+              Select [38-46]
+                SelectList [45-46]
+                  SelectColumn [45-46]
+                    IntLiteral(2) [45-46]
+              Select [57-65]
+                SelectList [64-65]
+                  SelectColumn [64-65]
+                    IntLiteral(3) [64-65]
+              Select [76-87]
+                SelectList [83-87]
+                  SelectColumn [83-87]
+                    NullLiteral(NULL) [83-87]
+      WithClauseEntry [92-170]
+        AliasedQuery [92-170]
+          Identifier(B) [92-93]
+          Query [97-170]
+            SetOperation(UNION ALL) [99-169]
+              SetOperationMetadataList [113-160]
+                SetOperationMetadata [113-122]
+                  SetOperationType [113-118]
+                  SetOperationAllOrDistinct [119-122]
+                SetOperationMetadata [132-141]
+                  SetOperationType [132-137]
+                  SetOperationAllOrDistinct [138-141]
+                SetOperationMetadata [151-160]
+                  SetOperationType [151-156]
+                  SetOperationAllOrDistinct [157-160]
+              Select [99-112]
+                SelectList [106-112]
+                  SelectColumn [106-112]
+                    IntLiteral(2) [106-107]
+                    Alias [108-112]
+                      Identifier(x) [111-112]
+              Select [123-131]
+                SelectList [130-131]
+                  SelectColumn [130-131]
+                    IntLiteral(3) [130-131]
+              Select [142-150]
+                SelectList [149-150]
+                  SelectColumn [149-150]
+                    IntLiteral(4) [149-150]
+              Select [161-169]
+                SelectList [168-169]
+                  SelectColumn [168-169]
+                    IntLiteral(5) [168-169]
+    Select [171-224]
+      SelectList [178-186]
+        SelectColumn [178-181]
+          PathExpression [178-181]
+            Identifier(A) [178-179]
+            Identifier(x) [180-181]
+        SelectColumn [183-186]
+          PathExpression [183-186]
+            Identifier(B) [183-184]
+            Identifier(x) [185-186]
+      FromClause [187-224]
+        Join(LEFT) [192-224]
+          TablePathExpression [192-193]
+            PathExpression [192-193]
+              Identifier(A) [192-193]
+          Location [194-209]
+          TablePathExpression [210-211]
+            PathExpression [210-211]
+              Identifier(B) [210-211]
+          OnClause [212-224]
+            BinaryExpression(=) [215-224]
+              PathExpression [215-218]
+                Identifier(A) [215-216]
+                Identifier(x) [217-218]
+              PathExpression [221-224]
+                Identifier(B) [221-222]
+                Identifier(x) [223-224]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#on_clause
+WITH
+  A AS ( SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT NULL),
+  B AS ( SELECT 2 as x UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5)
+SELECT * FROM A FULL OUTER JOIN B ON A.x = B.x
+--
+QueryStatement [0-217]
+  Query [0-217]
+    WithClause [0-170]
+      WithClauseEntry [7-88]
+        AliasedQuery [7-88]
+          Identifier(A) [7-8]
+          Query [12-88]
+            SetOperation(UNION ALL) [14-87]
+              SetOperationMetadataList [28-75]
+                SetOperationMetadata [28-37]
+                  SetOperationType [28-33]
+                  SetOperationAllOrDistinct [34-37]
+                SetOperationMetadata [47-56]
+                  SetOperationType [47-52]
+                  SetOperationAllOrDistinct [53-56]
+                SetOperationMetadata [66-75]
+                  SetOperationType [66-71]
+                  SetOperationAllOrDistinct [72-75]
+              Select [14-27]
+                SelectList [21-27]
+                  SelectColumn [21-27]
+                    IntLiteral(1) [21-22]
+                    Alias [23-27]
+                      Identifier(x) [26-27]
+              Select [38-46]
+                SelectList [45-46]
+                  SelectColumn [45-46]
+                    IntLiteral(2) [45-46]
+              Select [57-65]
+                SelectList [64-65]
+                  SelectColumn [64-65]
+                    IntLiteral(3) [64-65]
+              Select [76-87]
+                SelectList [83-87]
+                  SelectColumn [83-87]
+                    NullLiteral(NULL) [83-87]
+      WithClauseEntry [92-170]
+        AliasedQuery [92-170]
+          Identifier(B) [92-93]
+          Query [97-170]
+            SetOperation(UNION ALL) [99-169]
+              SetOperationMetadataList [113-160]
+                SetOperationMetadata [113-122]
+                  SetOperationType [113-118]
+                  SetOperationAllOrDistinct [119-122]
+                SetOperationMetadata [132-141]
+                  SetOperationType [132-137]
+                  SetOperationAllOrDistinct [138-141]
+                SetOperationMetadata [151-160]
+                  SetOperationType [151-156]
+                  SetOperationAllOrDistinct [157-160]
+              Select [99-112]
+                SelectList [106-112]
+                  SelectColumn [106-112]
+                    IntLiteral(2) [106-107]
+                    Alias [108-112]
+                      Identifier(x) [111-112]
+              Select [123-131]
+                SelectList [130-131]
+                  SelectColumn [130-131]
+                    IntLiteral(3) [130-131]
+              Select [142-150]
+                SelectList [149-150]
+                  SelectColumn [149-150]
+                    IntLiteral(4) [149-150]
+              Select [161-169]
+                SelectList [168-169]
+                  SelectColumn [168-169]
+                    IntLiteral(5) [168-169]
+    Select [171-217]
+      SelectList [178-179]
+        SelectColumn [178-179]
+          Star(*) [178-179]
+      FromClause [180-217]
+        Join(FULL) [185-217]
+          TablePathExpression [185-186]
+            PathExpression [185-186]
+              Identifier(A) [185-186]
+          Location [187-202]
+          TablePathExpression [203-204]
+            PathExpression [203-204]
+              Identifier(B) [203-204]
+          OnClause [205-217]
+            BinaryExpression(=) [208-217]
+              PathExpression [208-211]
+                Identifier(A) [208-209]
+                Identifier(x) [210-211]
+              PathExpression [214-217]
+                Identifier(B) [214-215]
+                Identifier(x) [216-217]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#on_clause
+WITH
+  A AS ( SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT NULL),
+  B AS ( SELECT 2 as x UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5)
+SELECT A.x, B.x FROM A FULL OUTER JOIN B ON A.x = B.x
+--
+QueryStatement [0-224]
+  Query [0-224]
+    WithClause [0-170]
+      WithClauseEntry [7-88]
+        AliasedQuery [7-88]
+          Identifier(A) [7-8]
+          Query [12-88]
+            SetOperation(UNION ALL) [14-87]
+              SetOperationMetadataList [28-75]
+                SetOperationMetadata [28-37]
+                  SetOperationType [28-33]
+                  SetOperationAllOrDistinct [34-37]
+                SetOperationMetadata [47-56]
+                  SetOperationType [47-52]
+                  SetOperationAllOrDistinct [53-56]
+                SetOperationMetadata [66-75]
+                  SetOperationType [66-71]
+                  SetOperationAllOrDistinct [72-75]
+              Select [14-27]
+                SelectList [21-27]
+                  SelectColumn [21-27]
+                    IntLiteral(1) [21-22]
+                    Alias [23-27]
+                      Identifier(x) [26-27]
+              Select [38-46]
+                SelectList [45-46]
+                  SelectColumn [45-46]
+                    IntLiteral(2) [45-46]
+              Select [57-65]
+                SelectList [64-65]
+                  SelectColumn [64-65]
+                    IntLiteral(3) [64-65]
+              Select [76-87]
+                SelectList [83-87]
+                  SelectColumn [83-87]
+                    NullLiteral(NULL) [83-87]
+      WithClauseEntry [92-170]
+        AliasedQuery [92-170]
+          Identifier(B) [92-93]
+          Query [97-170]
+            SetOperation(UNION ALL) [99-169]
+              SetOperationMetadataList [113-160]
+                SetOperationMetadata [113-122]
+                  SetOperationType [113-118]
+                  SetOperationAllOrDistinct [119-122]
+                SetOperationMetadata [132-141]
+                  SetOperationType [132-137]
+                  SetOperationAllOrDistinct [138-141]
+                SetOperationMetadata [151-160]
+                  SetOperationType [151-156]
+                  SetOperationAllOrDistinct [157-160]
+              Select [99-112]
+                SelectList [106-112]
+                  SelectColumn [106-112]
+                    IntLiteral(2) [106-107]
+                    Alias [108-112]
+                      Identifier(x) [111-112]
+              Select [123-131]
+                SelectList [130-131]
+                  SelectColumn [130-131]
+                    IntLiteral(3) [130-131]
+              Select [142-150]
+                SelectList [149-150]
+                  SelectColumn [149-150]
+                    IntLiteral(4) [149-150]
+              Select [161-169]
+                SelectList [168-169]
+                  SelectColumn [168-169]
+                    IntLiteral(5) [168-169]
+    Select [171-224]
+      SelectList [178-186]
+        SelectColumn [178-181]
+          PathExpression [178-181]
+            Identifier(A) [178-179]
+            Identifier(x) [180-181]
+        SelectColumn [183-186]
+          PathExpression [183-186]
+            Identifier(B) [183-184]
+            Identifier(x) [185-186]
+      FromClause [187-224]
+        Join(FULL) [192-224]
+          TablePathExpression [192-193]
+            PathExpression [192-193]
+              Identifier(A) [192-193]
+          Location [194-209]
+          TablePathExpression [210-211]
+            PathExpression [210-211]
+              Identifier(B) [210-211]
+          OnClause [212-224]
+            BinaryExpression(=) [215-224]
+              PathExpression [215-218]
+                Identifier(A) [215-216]
+                Identifier(x) [217-218]
+              PathExpression [221-224]
+                Identifier(B) [221-222]
+                Identifier(x) [223-224]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#using_clause
+WITH
+  A AS ( SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 9 UNION ALL SELECT NULL),
+  B AS ( SELECT 2 as x UNION ALL SELECT 9 UNION ALL SELECT 9 UNION ALL SELECT 5)
+SELECT * FROM A INNER JOIN B USING (x)
+--
+QueryStatement [0-209]
+  Query [0-209]
+    WithClause [0-170]
+      WithClauseEntry [7-88]
+        AliasedQuery [7-88]
+          Identifier(A) [7-8]
+          Query [12-88]
+            SetOperation(UNION ALL) [14-87]
+              SetOperationMetadataList [28-75]
+                SetOperationMetadata [28-37]
+                  SetOperationType [28-33]
+                  SetOperationAllOrDistinct [34-37]
+                SetOperationMetadata [47-56]
+                  SetOperationType [47-52]
+                  SetOperationAllOrDistinct [53-56]
+                SetOperationMetadata [66-75]
+                  SetOperationType [66-71]
+                  SetOperationAllOrDistinct [72-75]
+              Select [14-27]
+                SelectList [21-27]
+                  SelectColumn [21-27]
+                    IntLiteral(1) [21-22]
+                    Alias [23-27]
+                      Identifier(x) [26-27]
+              Select [38-46]
+                SelectList [45-46]
+                  SelectColumn [45-46]
+                    IntLiteral(2) [45-46]
+              Select [57-65]
+                SelectList [64-65]
+                  SelectColumn [64-65]
+                    IntLiteral(9) [64-65]
+              Select [76-87]
+                SelectList [83-87]
+                  SelectColumn [83-87]
+                    NullLiteral(NULL) [83-87]
+      WithClauseEntry [92-170]
+        AliasedQuery [92-170]
+          Identifier(B) [92-93]
+          Query [97-170]
+            SetOperation(UNION ALL) [99-169]
+              SetOperationMetadataList [113-160]
+                SetOperationMetadata [113-122]
+                  SetOperationType [113-118]
+                  SetOperationAllOrDistinct [119-122]
+                SetOperationMetadata [132-141]
+                  SetOperationType [132-137]
+                  SetOperationAllOrDistinct [138-141]
+                SetOperationMetadata [151-160]
+                  SetOperationType [151-156]
+                  SetOperationAllOrDistinct [157-160]
+              Select [99-112]
+                SelectList [106-112]
+                  SelectColumn [106-112]
+                    IntLiteral(2) [106-107]
+                    Alias [108-112]
+                      Identifier(x) [111-112]
+              Select [123-131]
+                SelectList [130-131]
+                  SelectColumn [130-131]
+                    IntLiteral(9) [130-131]
+              Select [142-150]
+                SelectList [149-150]
+                  SelectColumn [149-150]
+                    IntLiteral(9) [149-150]
+              Select [161-169]
+                SelectList [168-169]
+                  SelectColumn [168-169]
+                    IntLiteral(5) [168-169]
+    Select [171-209]
+      SelectList [178-179]
+        SelectColumn [178-179]
+          Star(*) [178-179]
+      FromClause [180-209]
+        Join(INNER) [185-209]
+          TablePathExpression [185-186]
+            PathExpression [185-186]
+              Identifier(A) [185-186]
+          Location [187-197]
+          TablePathExpression [198-199]
+            PathExpression [198-199]
+              Identifier(B) [198-199]
+          UsingClause [200-209]
+            Identifier(x) [207-208]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#using_clause
+WITH
+  A AS (
+    SELECT 1 as x, 15 as y UNION ALL
+    SELECT 2, 10 UNION ALL
+    SELECT 9, 16 UNION ALL
+    SELECT NULL, 12),
+  B AS (
+    SELECT 2 as x, 10 as y UNION ALL
+    SELECT 9, 17 UNION ALL
+    SELECT 9, 16 UNION ALL
+    SELECT 5, 15)
+SELECT * FROM A INNER JOIN B USING (x, y)
+--
+QueryStatement [0-286]
+  Query [0-286]
+    WithClause [0-244]
+      WithClauseEntry [7-125]
+        AliasedQuery [7-125]
+          Identifier(A) [7-8]
+          Query [12-125]
+            SetOperation(UNION ALL) [18-124]
+              SetOperationMetadataList [41-104]
+                SetOperationMetadata [41-50]
+                  SetOperationType [41-46]
+                  SetOperationAllOrDistinct [47-50]
+                SetOperationMetadata [68-77]
+                  SetOperationType [68-73]
+                  SetOperationAllOrDistinct [74-77]
+                SetOperationMetadata [95-104]
+                  SetOperationType [95-100]
+                  SetOperationAllOrDistinct [101-104]
+              Select [18-40]
+                SelectList [25-40]
+                  SelectColumn [25-31]
+                    IntLiteral(1) [25-26]
+                    Alias [27-31]
+                      Identifier(x) [30-31]
+                  SelectColumn [33-40]
+                    IntLiteral(15) [33-35]
+                    Alias [36-40]
+                      Identifier(y) [39-40]
+              Select [55-67]
+                SelectList [62-67]
+                  SelectColumn [62-63]
+                    IntLiteral(2) [62-63]
+                  SelectColumn [65-67]
+                    IntLiteral(10) [65-67]
+              Select [82-94]
+                SelectList [89-94]
+                  SelectColumn [89-90]
+                    IntLiteral(9) [89-90]
+                  SelectColumn [92-94]
+                    IntLiteral(16) [92-94]
+              Select [109-124]
+                SelectList [116-124]
+                  SelectColumn [116-120]
+                    NullLiteral(NULL) [116-120]
+                  SelectColumn [122-124]
+                    IntLiteral(12) [122-124]
+      WithClauseEntry [129-244]
+        AliasedQuery [129-244]
+          Identifier(B) [129-130]
+          Query [134-244]
+            SetOperation(UNION ALL) [140-243]
+              SetOperationMetadataList [163-226]
+                SetOperationMetadata [163-172]
+                  SetOperationType [163-168]
+                  SetOperationAllOrDistinct [169-172]
+                SetOperationMetadata [190-199]
+                  SetOperationType [190-195]
+                  SetOperationAllOrDistinct [196-199]
+                SetOperationMetadata [217-226]
+                  SetOperationType [217-222]
+                  SetOperationAllOrDistinct [223-226]
+              Select [140-162]
+                SelectList [147-162]
+                  SelectColumn [147-153]
+                    IntLiteral(2) [147-148]
+                    Alias [149-153]
+                      Identifier(x) [152-153]
+                  SelectColumn [155-162]
+                    IntLiteral(10) [155-157]
+                    Alias [158-162]
+                      Identifier(y) [161-162]
+              Select [177-189]
+                SelectList [184-189]
+                  SelectColumn [184-185]
+                    IntLiteral(9) [184-185]
+                  SelectColumn [187-189]
+                    IntLiteral(17) [187-189]
+              Select [204-216]
+                SelectList [211-216]
+                  SelectColumn [211-212]
+                    IntLiteral(9) [211-212]
+                  SelectColumn [214-216]
+                    IntLiteral(16) [214-216]
+              Select [231-243]
+                SelectList [238-243]
+                  SelectColumn [238-239]
+                    IntLiteral(5) [238-239]
+                  SelectColumn [241-243]
+                    IntLiteral(15) [241-243]
+    Select [245-286]
+      SelectList [252-253]
+        SelectColumn [252-253]
+          Star(*) [252-253]
+      FromClause [254-286]
+        Join(INNER) [259-286]
+          TablePathExpression [259-260]
+            PathExpression [259-260]
+              Identifier(A) [259-260]
+          Location [261-271]
+          TablePathExpression [272-273]
+            PathExpression [272-273]
+              Identifier(B) [272-273]
+          UsingClause [274-286]
+            Identifier(x) [281-282]
+            Identifier(y) [284-285]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#using_clause
+WITH
+  A AS ( SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 9 UNION ALL SELECT NULL),
+  B AS ( SELECT 2 as x UNION ALL SELECT 9 UNION ALL SELECT 9 UNION ALL SELECT 5)
+SELECT x, A.x, B.x FROM A INNER JOIN B USING (x)
+
+/*
+Table A    Table B   Result
++------+   +---+     +--------------------+
+| x    | * | x |  =  | x    | A.x  | B.x  |
++------+   +---+     +--------------------+
+| 1    |   | 2 |     | 2    | 2    | 2    |
+| 2    |   | 9 |     | 9    | 9    | 9    |
+| 9    |   | 9 |     | 9    | 9    | 9    |
+| NULL |   | 5 |     +--------------------+
++------+   +---+
+*/
+--
+QueryStatement [0-219]
+  Query [0-219]
+    WithClause [0-170]
+      WithClauseEntry [7-88]
+        AliasedQuery [7-88]
+          Identifier(A) [7-8]
+          Query [12-88]
+            SetOperation(UNION ALL) [14-87]
+              SetOperationMetadataList [28-75]
+                SetOperationMetadata [28-37]
+                  SetOperationType [28-33]
+                  SetOperationAllOrDistinct [34-37]
+                SetOperationMetadata [47-56]
+                  SetOperationType [47-52]
+                  SetOperationAllOrDistinct [53-56]
+                SetOperationMetadata [66-75]
+                  SetOperationType [66-71]
+                  SetOperationAllOrDistinct [72-75]
+              Select [14-27]
+                SelectList [21-27]
+                  SelectColumn [21-27]
+                    IntLiteral(1) [21-22]
+                    Alias [23-27]
+                      Identifier(x) [26-27]
+              Select [38-46]
+                SelectList [45-46]
+                  SelectColumn [45-46]
+                    IntLiteral(2) [45-46]
+              Select [57-65]
+                SelectList [64-65]
+                  SelectColumn [64-65]
+                    IntLiteral(9) [64-65]
+              Select [76-87]
+                SelectList [83-87]
+                  SelectColumn [83-87]
+                    NullLiteral(NULL) [83-87]
+      WithClauseEntry [92-170]
+        AliasedQuery [92-170]
+          Identifier(B) [92-93]
+          Query [97-170]
+            SetOperation(UNION ALL) [99-169]
+              SetOperationMetadataList [113-160]
+                SetOperationMetadata [113-122]
+                  SetOperationType [113-118]
+                  SetOperationAllOrDistinct [119-122]
+                SetOperationMetadata [132-141]
+                  SetOperationType [132-137]
+                  SetOperationAllOrDistinct [138-141]
+                SetOperationMetadata [151-160]
+                  SetOperationType [151-156]
+                  SetOperationAllOrDistinct [157-160]
+              Select [99-112]
+                SelectList [106-112]
+                  SelectColumn [106-112]
+                    IntLiteral(2) [106-107]
+                    Alias [108-112]
+                      Identifier(x) [111-112]
+              Select [123-131]
+                SelectList [130-131]
+                  SelectColumn [130-131]
+                    IntLiteral(9) [130-131]
+              Select [142-150]
+                SelectList [149-150]
+                  SelectColumn [149-150]
+                    IntLiteral(9) [149-150]
+              Select [161-169]
+                SelectList [168-169]
+                  SelectColumn [168-169]
+                    IntLiteral(5) [168-169]
+    Select [171-219]
+      SelectList [178-189]
+        SelectColumn [178-179]
+          PathExpression [178-179]
+            Identifier(x) [178-179]
+        SelectColumn [181-184]
+          PathExpression [181-184]
+            Identifier(A) [181-182]
+            Identifier(x) [183-184]
+        SelectColumn [186-189]
+          PathExpression [186-189]
+            Identifier(B) [186-187]
+            Identifier(x) [188-189]
+      FromClause [190-219]
+        Join(INNER) [195-219]
+          TablePathExpression [195-196]
+            PathExpression [195-196]
+              Identifier(A) [195-196]
+          Location [197-207]
+          TablePathExpression [208-209]
+            PathExpression [208-209]
+              Identifier(B) [208-209]
+          UsingClause [210-219]
+            Identifier(x) [217-218]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#using_clause
+WITH
+  A AS ( SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 9 UNION ALL SELECT NULL),
+  B AS ( SELECT 2 as x UNION ALL SELECT 9 UNION ALL SELECT 9 UNION ALL SELECT 5)
+SELECT x, A.x, B.x FROM A LEFT OUTER JOIN B USING (x)
+
+/*
+Table A    Table B   Result
++------+   +---+     +--------------------+
+| x    | * | x |  =  | x    | A.x  | B.x  |
++------+   +---+     +--------------------+
+| 1    |   | 2 |     | 1    | 1    | NULL |
+| 2    |   | 9 |     | 2    | 2    | 2    |
+| 9    |   | 9 |     | 9    | 9    | 9    |
+| NULL |   | 5 |     | 9    | 9    | 9    |
++------+   +---+     | NULL | NULL | NULL |
+                     +--------------------+
+*/
+--
+QueryStatement [0-224]
+  Query [0-224]
+    WithClause [0-170]
+      WithClauseEntry [7-88]
+        AliasedQuery [7-88]
+          Identifier(A) [7-8]
+          Query [12-88]
+            SetOperation(UNION ALL) [14-87]
+              SetOperationMetadataList [28-75]
+                SetOperationMetadata [28-37]
+                  SetOperationType [28-33]
+                  SetOperationAllOrDistinct [34-37]
+                SetOperationMetadata [47-56]
+                  SetOperationType [47-52]
+                  SetOperationAllOrDistinct [53-56]
+                SetOperationMetadata [66-75]
+                  SetOperationType [66-71]
+                  SetOperationAllOrDistinct [72-75]
+              Select [14-27]
+                SelectList [21-27]
+                  SelectColumn [21-27]
+                    IntLiteral(1) [21-22]
+                    Alias [23-27]
+                      Identifier(x) [26-27]
+              Select [38-46]
+                SelectList [45-46]
+                  SelectColumn [45-46]
+                    IntLiteral(2) [45-46]
+              Select [57-65]
+                SelectList [64-65]
+                  SelectColumn [64-65]
+                    IntLiteral(9) [64-65]
+              Select [76-87]
+                SelectList [83-87]
+                  SelectColumn [83-87]
+                    NullLiteral(NULL) [83-87]
+      WithClauseEntry [92-170]
+        AliasedQuery [92-170]
+          Identifier(B) [92-93]
+          Query [97-170]
+            SetOperation(UNION ALL) [99-169]
+              SetOperationMetadataList [113-160]
+                SetOperationMetadata [113-122]
+                  SetOperationType [113-118]
+                  SetOperationAllOrDistinct [119-122]
+                SetOperationMetadata [132-141]
+                  SetOperationType [132-137]
+                  SetOperationAllOrDistinct [138-141]
+                SetOperationMetadata [151-160]
+                  SetOperationType [151-156]
+                  SetOperationAllOrDistinct [157-160]
+              Select [99-112]
+                SelectList [106-112]
+                  SelectColumn [106-112]
+                    IntLiteral(2) [106-107]
+                    Alias [108-112]
+                      Identifier(x) [111-112]
+              Select [123-131]
+                SelectList [130-131]
+                  SelectColumn [130-131]
+                    IntLiteral(9) [130-131]
+              Select [142-150]
+                SelectList [149-150]
+                  SelectColumn [149-150]
+                    IntLiteral(9) [149-150]
+              Select [161-169]
+                SelectList [168-169]
+                  SelectColumn [168-169]
+                    IntLiteral(5) [168-169]
+    Select [171-224]
+      SelectList [178-189]
+        SelectColumn [178-179]
+          PathExpression [178-179]
+            Identifier(x) [178-179]
+        SelectColumn [181-184]
+          PathExpression [181-184]
+            Identifier(A) [181-182]
+            Identifier(x) [183-184]
+        SelectColumn [186-189]
+          PathExpression [186-189]
+            Identifier(B) [186-187]
+            Identifier(x) [188-189]
+      FromClause [190-224]
+        Join(LEFT) [195-224]
+          TablePathExpression [195-196]
+            PathExpression [195-196]
+              Identifier(A) [195-196]
+          Location [197-212]
+          TablePathExpression [213-214]
+            PathExpression [213-214]
+              Identifier(B) [213-214]
+          UsingClause [215-224]
+            Identifier(x) [222-223]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#using_clause
+WITH
+  A AS ( SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 2 UNION ALL SELECT NULL),
+  B AS ( SELECT 2 as x UNION ALL SELECT 9 UNION ALL SELECT 9 UNION ALL SELECT 5)
+SELECT x, A.x, B.x FROM A RIGHT OUTER JOIN B USING (x)
+
+/*
+Table A    Table B   Result
++------+   +---+     +--------------------+
+| x    | * | x |  =  | x    | A.x  | B.x  |
++------+   +---+     +--------------------+
+| 1    |   | 2 |     | 2    | 2    | 2    |
+| 2    |   | 9 |     | 2    | 2    | 2    |
+| 2    |   | 9 |     | 9    | NULL | 9    |
+| NULL |   | 5 |     | 9    | NULL | 9    |
++------+   +---+     | 5    | NULL | 5    |
+                     +--------------------+
+*/
+--
+QueryStatement [0-225]
+  Query [0-225]
+    WithClause [0-170]
+      WithClauseEntry [7-88]
+        AliasedQuery [7-88]
+          Identifier(A) [7-8]
+          Query [12-88]
+            SetOperation(UNION ALL) [14-87]
+              SetOperationMetadataList [28-75]
+                SetOperationMetadata [28-37]
+                  SetOperationType [28-33]
+                  SetOperationAllOrDistinct [34-37]
+                SetOperationMetadata [47-56]
+                  SetOperationType [47-52]
+                  SetOperationAllOrDistinct [53-56]
+                SetOperationMetadata [66-75]
+                  SetOperationType [66-71]
+                  SetOperationAllOrDistinct [72-75]
+              Select [14-27]
+                SelectList [21-27]
+                  SelectColumn [21-27]
+                    IntLiteral(1) [21-22]
+                    Alias [23-27]
+                      Identifier(x) [26-27]
+              Select [38-46]
+                SelectList [45-46]
+                  SelectColumn [45-46]
+                    IntLiteral(2) [45-46]
+              Select [57-65]
+                SelectList [64-65]
+                  SelectColumn [64-65]
+                    IntLiteral(2) [64-65]
+              Select [76-87]
+                SelectList [83-87]
+                  SelectColumn [83-87]
+                    NullLiteral(NULL) [83-87]
+      WithClauseEntry [92-170]
+        AliasedQuery [92-170]
+          Identifier(B) [92-93]
+          Query [97-170]
+            SetOperation(UNION ALL) [99-169]
+              SetOperationMetadataList [113-160]
+                SetOperationMetadata [113-122]
+                  SetOperationType [113-118]
+                  SetOperationAllOrDistinct [119-122]
+                SetOperationMetadata [132-141]
+                  SetOperationType [132-137]
+                  SetOperationAllOrDistinct [138-141]
+                SetOperationMetadata [151-160]
+                  SetOperationType [151-156]
+                  SetOperationAllOrDistinct [157-160]
+              Select [99-112]
+                SelectList [106-112]
+                  SelectColumn [106-112]
+                    IntLiteral(2) [106-107]
+                    Alias [108-112]
+                      Identifier(x) [111-112]
+              Select [123-131]
+                SelectList [130-131]
+                  SelectColumn [130-131]
+                    IntLiteral(9) [130-131]
+              Select [142-150]
+                SelectList [149-150]
+                  SelectColumn [149-150]
+                    IntLiteral(9) [149-150]
+              Select [161-169]
+                SelectList [168-169]
+                  SelectColumn [168-169]
+                    IntLiteral(5) [168-169]
+    Select [171-225]
+      SelectList [178-189]
+        SelectColumn [178-179]
+          PathExpression [178-179]
+            Identifier(x) [178-179]
+        SelectColumn [181-184]
+          PathExpression [181-184]
+            Identifier(A) [181-182]
+            Identifier(x) [183-184]
+        SelectColumn [186-189]
+          PathExpression [186-189]
+            Identifier(B) [186-187]
+            Identifier(x) [188-189]
+      FromClause [190-225]
+        Join(RIGHT) [195-225]
+          TablePathExpression [195-196]
+            PathExpression [195-196]
+              Identifier(A) [195-196]
+          Location [197-213]
+          TablePathExpression [214-215]
+            PathExpression [214-215]
+              Identifier(B) [214-215]
+          UsingClause [216-225]
+            Identifier(x) [223-224]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#using_clause
+WITH
+  A AS ( SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 2 UNION ALL SELECT NULL),
+  B AS ( SELECT 2 as x UNION ALL SELECT 9 UNION ALL SELECT 9 UNION ALL SELECT 5)
+SELECT x, A.x, B.x FROM A FULL OUTER JOIN B USING (x)
+--
+QueryStatement [0-224]
+  Query [0-224]
+    WithClause [0-170]
+      WithClauseEntry [7-88]
+        AliasedQuery [7-88]
+          Identifier(A) [7-8]
+          Query [12-88]
+            SetOperation(UNION ALL) [14-87]
+              SetOperationMetadataList [28-75]
+                SetOperationMetadata [28-37]
+                  SetOperationType [28-33]
+                  SetOperationAllOrDistinct [34-37]
+                SetOperationMetadata [47-56]
+                  SetOperationType [47-52]
+                  SetOperationAllOrDistinct [53-56]
+                SetOperationMetadata [66-75]
+                  SetOperationType [66-71]
+                  SetOperationAllOrDistinct [72-75]
+              Select [14-27]
+                SelectList [21-27]
+                  SelectColumn [21-27]
+                    IntLiteral(1) [21-22]
+                    Alias [23-27]
+                      Identifier(x) [26-27]
+              Select [38-46]
+                SelectList [45-46]
+                  SelectColumn [45-46]
+                    IntLiteral(2) [45-46]
+              Select [57-65]
+                SelectList [64-65]
+                  SelectColumn [64-65]
+                    IntLiteral(2) [64-65]
+              Select [76-87]
+                SelectList [83-87]
+                  SelectColumn [83-87]
+                    NullLiteral(NULL) [83-87]
+      WithClauseEntry [92-170]
+        AliasedQuery [92-170]
+          Identifier(B) [92-93]
+          Query [97-170]
+            SetOperation(UNION ALL) [99-169]
+              SetOperationMetadataList [113-160]
+                SetOperationMetadata [113-122]
+                  SetOperationType [113-118]
+                  SetOperationAllOrDistinct [119-122]
+                SetOperationMetadata [132-141]
+                  SetOperationType [132-137]
+                  SetOperationAllOrDistinct [138-141]
+                SetOperationMetadata [151-160]
+                  SetOperationType [151-156]
+                  SetOperationAllOrDistinct [157-160]
+              Select [99-112]
+                SelectList [106-112]
+                  SelectColumn [106-112]
+                    IntLiteral(2) [106-107]
+                    Alias [108-112]
+                      Identifier(x) [111-112]
+              Select [123-131]
+                SelectList [130-131]
+                  SelectColumn [130-131]
+                    IntLiteral(9) [130-131]
+              Select [142-150]
+                SelectList [149-150]
+                  SelectColumn [149-150]
+                    IntLiteral(9) [149-150]
+              Select [161-169]
+                SelectList [168-169]
+                  SelectColumn [168-169]
+                    IntLiteral(5) [168-169]
+    Select [171-224]
+      SelectList [178-189]
+        SelectColumn [178-179]
+          PathExpression [178-179]
+            Identifier(x) [178-179]
+        SelectColumn [181-184]
+          PathExpression [181-184]
+            Identifier(A) [181-182]
+            Identifier(x) [183-184]
+        SelectColumn [186-189]
+          PathExpression [186-189]
+            Identifier(B) [186-187]
+            Identifier(x) [188-189]
+      FromClause [190-224]
+        Join(FULL) [195-224]
+          TablePathExpression [195-196]
+            PathExpression [195-196]
+              Identifier(A) [195-196]
+          Location [197-212]
+          TablePathExpression [213-214]
+            PathExpression [213-214]
+              Identifier(B) [213-214]
+          UsingClause [215-224]
+            Identifier(x) [222-223]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#using_clause
+WITH
+  A AS (
+    SELECT 1 as x, 15 as y UNION ALL
+    SELECT 2, 10 UNION ALL
+    SELECT 9, 16 UNION ALL
+    SELECT NULL, 12),
+  B AS (
+    SELECT 2 as x, 10 as y UNION ALL
+    SELECT 9, 17 UNION ALL
+    SELECT 9, 16 UNION ALL
+    SELECT 5, 15)
+SELECT * FROM A INNER JOIN B USING (x)
+--
+QueryStatement [0-283]
+  Query [0-283]
+    WithClause [0-244]
+      WithClauseEntry [7-125]
+        AliasedQuery [7-125]
+          Identifier(A) [7-8]
+          Query [12-125]
+            SetOperation(UNION ALL) [18-124]
+              SetOperationMetadataList [41-104]
+                SetOperationMetadata [41-50]
+                  SetOperationType [41-46]
+                  SetOperationAllOrDistinct [47-50]
+                SetOperationMetadata [68-77]
+                  SetOperationType [68-73]
+                  SetOperationAllOrDistinct [74-77]
+                SetOperationMetadata [95-104]
+                  SetOperationType [95-100]
+                  SetOperationAllOrDistinct [101-104]
+              Select [18-40]
+                SelectList [25-40]
+                  SelectColumn [25-31]
+                    IntLiteral(1) [25-26]
+                    Alias [27-31]
+                      Identifier(x) [30-31]
+                  SelectColumn [33-40]
+                    IntLiteral(15) [33-35]
+                    Alias [36-40]
+                      Identifier(y) [39-40]
+              Select [55-67]
+                SelectList [62-67]
+                  SelectColumn [62-63]
+                    IntLiteral(2) [62-63]
+                  SelectColumn [65-67]
+                    IntLiteral(10) [65-67]
+              Select [82-94]
+                SelectList [89-94]
+                  SelectColumn [89-90]
+                    IntLiteral(9) [89-90]
+                  SelectColumn [92-94]
+                    IntLiteral(16) [92-94]
+              Select [109-124]
+                SelectList [116-124]
+                  SelectColumn [116-120]
+                    NullLiteral(NULL) [116-120]
+                  SelectColumn [122-124]
+                    IntLiteral(12) [122-124]
+      WithClauseEntry [129-244]
+        AliasedQuery [129-244]
+          Identifier(B) [129-130]
+          Query [134-244]
+            SetOperation(UNION ALL) [140-243]
+              SetOperationMetadataList [163-226]
+                SetOperationMetadata [163-172]
+                  SetOperationType [163-168]
+                  SetOperationAllOrDistinct [169-172]
+                SetOperationMetadata [190-199]
+                  SetOperationType [190-195]
+                  SetOperationAllOrDistinct [196-199]
+                SetOperationMetadata [217-226]
+                  SetOperationType [217-222]
+                  SetOperationAllOrDistinct [223-226]
+              Select [140-162]
+                SelectList [147-162]
+                  SelectColumn [147-153]
+                    IntLiteral(2) [147-148]
+                    Alias [149-153]
+                      Identifier(x) [152-153]
+                  SelectColumn [155-162]
+                    IntLiteral(10) [155-157]
+                    Alias [158-162]
+                      Identifier(y) [161-162]
+              Select [177-189]
+                SelectList [184-189]
+                  SelectColumn [184-185]
+                    IntLiteral(9) [184-185]
+                  SelectColumn [187-189]
+                    IntLiteral(17) [187-189]
+              Select [204-216]
+                SelectList [211-216]
+                  SelectColumn [211-212]
+                    IntLiteral(9) [211-212]
+                  SelectColumn [214-216]
+                    IntLiteral(16) [214-216]
+              Select [231-243]
+                SelectList [238-243]
+                  SelectColumn [238-239]
+                    IntLiteral(5) [238-239]
+                  SelectColumn [241-243]
+                    IntLiteral(15) [241-243]
+    Select [245-283]
+      SelectList [252-253]
+        SelectColumn [252-253]
+          Star(*) [252-253]
+      FromClause [254-283]
+        Join(INNER) [259-283]
+          TablePathExpression [259-260]
+            PathExpression [259-260]
+              Identifier(A) [259-260]
+          Location [261-271]
+          TablePathExpression [272-273]
+            PathExpression [272-273]
+              Identifier(B) [272-273]
+          UsingClause [274-283]
+            Identifier(x) [281-282]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#using_clause
+SELECT * FROM Roster INNER JOIN TeamMascot USING (SchoolID)
+--
+QueryStatement [0-59]
+  Query [0-59]
+    Select [0-59]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-59]
+        Join(INNER) [14-59]
+          TablePathExpression [14-20]
+            PathExpression [14-20]
+              Identifier(Roster) [14-20]
+          Location [21-31]
+          TablePathExpression [32-42]
+            PathExpression [32-42]
+              Identifier(TeamMascot) [32-42]
+          UsingClause [43-59]
+            Identifier(SchoolID) [50-58]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#on_and_using_equivalency
+WITH
+  A AS (SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 3),
+  B AS (SELECT 2 as x UNION ALL SELECT 3 UNION ALL SELECT 4)
+SELECT * FROM A INNER JOIN B ON A.x = B.x
+--
+QueryStatement [0-169]
+  Query [0-169]
+    WithClause [0-127]
+      WithClauseEntry [7-65]
+        AliasedQuery [7-65]
+          Identifier(A) [7-8]
+          Query [12-65]
+            SetOperation(UNION ALL) [13-64]
+              SetOperationMetadataList [27-55]
+                SetOperationMetadata [27-36]
+                  SetOperationType [27-32]
+                  SetOperationAllOrDistinct [33-36]
+                SetOperationMetadata [46-55]
+                  SetOperationType [46-51]
+                  SetOperationAllOrDistinct [52-55]
+              Select [13-26]
+                SelectList [20-26]
+                  SelectColumn [20-26]
+                    IntLiteral(1) [20-21]
+                    Alias [22-26]
+                      Identifier(x) [25-26]
+              Select [37-45]
+                SelectList [44-45]
+                  SelectColumn [44-45]
+                    IntLiteral(2) [44-45]
+              Select [56-64]
+                SelectList [63-64]
+                  SelectColumn [63-64]
+                    IntLiteral(3) [63-64]
+      WithClauseEntry [69-127]
+        AliasedQuery [69-127]
+          Identifier(B) [69-70]
+          Query [74-127]
+            SetOperation(UNION ALL) [75-126]
+              SetOperationMetadataList [89-117]
+                SetOperationMetadata [89-98]
+                  SetOperationType [89-94]
+                  SetOperationAllOrDistinct [95-98]
+                SetOperationMetadata [108-117]
+                  SetOperationType [108-113]
+                  SetOperationAllOrDistinct [114-117]
+              Select [75-88]
+                SelectList [82-88]
+                  SelectColumn [82-88]
+                    IntLiteral(2) [82-83]
+                    Alias [84-88]
+                      Identifier(x) [87-88]
+              Select [99-107]
+                SelectList [106-107]
+                  SelectColumn [106-107]
+                    IntLiteral(3) [106-107]
+              Select [118-126]
+                SelectList [125-126]
+                  SelectColumn [125-126]
+                    IntLiteral(4) [125-126]
+    Select [128-169]
+      SelectList [135-136]
+        SelectColumn [135-136]
+          Star(*) [135-136]
+      FromClause [137-169]
+        Join(INNER) [142-169]
+          TablePathExpression [142-143]
+            PathExpression [142-143]
+              Identifier(A) [142-143]
+          Location [144-154]
+          TablePathExpression [155-156]
+            PathExpression [155-156]
+              Identifier(B) [155-156]
+          OnClause [157-169]
+            BinaryExpression(=) [160-169]
+              PathExpression [160-163]
+                Identifier(A) [160-161]
+                Identifier(x) [162-163]
+              PathExpression [166-169]
+                Identifier(B) [166-167]
+                Identifier(x) [168-169]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#on_and_using_equivalency
+WITH
+  A AS (SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 3),
+  B AS (SELECT 2 as x UNION ALL SELECT 3 UNION ALL SELECT 4)
+SELECT * FROM A INNER JOIN B USING (x)
+--
+QueryStatement [0-166]
+  Query [0-166]
+    WithClause [0-127]
+      WithClauseEntry [7-65]
+        AliasedQuery [7-65]
+          Identifier(A) [7-8]
+          Query [12-65]
+            SetOperation(UNION ALL) [13-64]
+              SetOperationMetadataList [27-55]
+                SetOperationMetadata [27-36]
+                  SetOperationType [27-32]
+                  SetOperationAllOrDistinct [33-36]
+                SetOperationMetadata [46-55]
+                  SetOperationType [46-51]
+                  SetOperationAllOrDistinct [52-55]
+              Select [13-26]
+                SelectList [20-26]
+                  SelectColumn [20-26]
+                    IntLiteral(1) [20-21]
+                    Alias [22-26]
+                      Identifier(x) [25-26]
+              Select [37-45]
+                SelectList [44-45]
+                  SelectColumn [44-45]
+                    IntLiteral(2) [44-45]
+              Select [56-64]
+                SelectList [63-64]
+                  SelectColumn [63-64]
+                    IntLiteral(3) [63-64]
+      WithClauseEntry [69-127]
+        AliasedQuery [69-127]
+          Identifier(B) [69-70]
+          Query [74-127]
+            SetOperation(UNION ALL) [75-126]
+              SetOperationMetadataList [89-117]
+                SetOperationMetadata [89-98]
+                  SetOperationType [89-94]
+                  SetOperationAllOrDistinct [95-98]
+                SetOperationMetadata [108-117]
+                  SetOperationType [108-113]
+                  SetOperationAllOrDistinct [114-117]
+              Select [75-88]
+                SelectList [82-88]
+                  SelectColumn [82-88]
+                    IntLiteral(2) [82-83]
+                    Alias [84-88]
+                      Identifier(x) [87-88]
+              Select [99-107]
+                SelectList [106-107]
+                  SelectColumn [106-107]
+                    IntLiteral(3) [106-107]
+              Select [118-126]
+                SelectList [125-126]
+                  SelectColumn [125-126]
+                    IntLiteral(4) [125-126]
+    Select [128-166]
+      SelectList [135-136]
+        SelectColumn [135-136]
+          Star(*) [135-136]
+      FromClause [137-166]
+        Join(INNER) [142-166]
+          TablePathExpression [142-143]
+            PathExpression [142-143]
+              Identifier(A) [142-143]
+          Location [144-154]
+          TablePathExpression [155-156]
+            PathExpression [155-156]
+              Identifier(B) [155-156]
+          UsingClause [157-166]
+            Identifier(x) [164-165]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#on_and_using_equivalency
+WITH
+  A AS (SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT NULL),
+  B AS (SELECT 2 as x UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5)
+SELECT * FROM A LEFT OUTER JOIN B ON A.x = B.x
+--
+QueryStatement [0-215]
+  Query [0-215]
+    WithClause [0-168]
+      WithClauseEntry [7-87]
+        AliasedQuery [7-87]
+          Identifier(A) [7-8]
+          Query [12-87]
+            SetOperation(UNION ALL) [13-86]
+              SetOperationMetadataList [27-74]
+                SetOperationMetadata [27-36]
+                  SetOperationType [27-32]
+                  SetOperationAllOrDistinct [33-36]
+                SetOperationMetadata [46-55]
+                  SetOperationType [46-51]
+                  SetOperationAllOrDistinct [52-55]
+                SetOperationMetadata [65-74]
+                  SetOperationType [65-70]
+                  SetOperationAllOrDistinct [71-74]
+              Select [13-26]
+                SelectList [20-26]
+                  SelectColumn [20-26]
+                    IntLiteral(1) [20-21]
+                    Alias [22-26]
+                      Identifier(x) [25-26]
+              Select [37-45]
+                SelectList [44-45]
+                  SelectColumn [44-45]
+                    IntLiteral(2) [44-45]
+              Select [56-64]
+                SelectList [63-64]
+                  SelectColumn [63-64]
+                    IntLiteral(3) [63-64]
+              Select [75-86]
+                SelectList [82-86]
+                  SelectColumn [82-86]
+                    NullLiteral(NULL) [82-86]
+      WithClauseEntry [91-168]
+        AliasedQuery [91-168]
+          Identifier(B) [91-92]
+          Query [96-168]
+            SetOperation(UNION ALL) [97-167]
+              SetOperationMetadataList [111-158]
+                SetOperationMetadata [111-120]
+                  SetOperationType [111-116]
+                  SetOperationAllOrDistinct [117-120]
+                SetOperationMetadata [130-139]
+                  SetOperationType [130-135]
+                  SetOperationAllOrDistinct [136-139]
+                SetOperationMetadata [149-158]
+                  SetOperationType [149-154]
+                  SetOperationAllOrDistinct [155-158]
+              Select [97-110]
+                SelectList [104-110]
+                  SelectColumn [104-110]
+                    IntLiteral(2) [104-105]
+                    Alias [106-110]
+                      Identifier(x) [109-110]
+              Select [121-129]
+                SelectList [128-129]
+                  SelectColumn [128-129]
+                    IntLiteral(3) [128-129]
+              Select [140-148]
+                SelectList [147-148]
+                  SelectColumn [147-148]
+                    IntLiteral(4) [147-148]
+              Select [159-167]
+                SelectList [166-167]
+                  SelectColumn [166-167]
+                    IntLiteral(5) [166-167]
+    Select [169-215]
+      SelectList [176-177]
+        SelectColumn [176-177]
+          Star(*) [176-177]
+      FromClause [178-215]
+        Join(LEFT) [183-215]
+          TablePathExpression [183-184]
+            PathExpression [183-184]
+              Identifier(A) [183-184]
+          Location [185-200]
+          TablePathExpression [201-202]
+            PathExpression [201-202]
+              Identifier(B) [201-202]
+          OnClause [203-215]
+            BinaryExpression(=) [206-215]
+              PathExpression [206-209]
+                Identifier(A) [206-207]
+                Identifier(x) [208-209]
+              PathExpression [212-215]
+                Identifier(B) [212-213]
+                Identifier(x) [214-215]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#on_and_using_equivalency
+WITH
+  A AS (SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT NULL),
+  B AS (SELECT 2 as x UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5)
+SELECT * FROM A LEFT OUTER JOIN B USING (x)
+--
+QueryStatement [0-212]
+  Query [0-212]
+    WithClause [0-168]
+      WithClauseEntry [7-87]
+        AliasedQuery [7-87]
+          Identifier(A) [7-8]
+          Query [12-87]
+            SetOperation(UNION ALL) [13-86]
+              SetOperationMetadataList [27-74]
+                SetOperationMetadata [27-36]
+                  SetOperationType [27-32]
+                  SetOperationAllOrDistinct [33-36]
+                SetOperationMetadata [46-55]
+                  SetOperationType [46-51]
+                  SetOperationAllOrDistinct [52-55]
+                SetOperationMetadata [65-74]
+                  SetOperationType [65-70]
+                  SetOperationAllOrDistinct [71-74]
+              Select [13-26]
+                SelectList [20-26]
+                  SelectColumn [20-26]
+                    IntLiteral(1) [20-21]
+                    Alias [22-26]
+                      Identifier(x) [25-26]
+              Select [37-45]
+                SelectList [44-45]
+                  SelectColumn [44-45]
+                    IntLiteral(2) [44-45]
+              Select [56-64]
+                SelectList [63-64]
+                  SelectColumn [63-64]
+                    IntLiteral(3) [63-64]
+              Select [75-86]
+                SelectList [82-86]
+                  SelectColumn [82-86]
+                    NullLiteral(NULL) [82-86]
+      WithClauseEntry [91-168]
+        AliasedQuery [91-168]
+          Identifier(B) [91-92]
+          Query [96-168]
+            SetOperation(UNION ALL) [97-167]
+              SetOperationMetadataList [111-158]
+                SetOperationMetadata [111-120]
+                  SetOperationType [111-116]
+                  SetOperationAllOrDistinct [117-120]
+                SetOperationMetadata [130-139]
+                  SetOperationType [130-135]
+                  SetOperationAllOrDistinct [136-139]
+                SetOperationMetadata [149-158]
+                  SetOperationType [149-154]
+                  SetOperationAllOrDistinct [155-158]
+              Select [97-110]
+                SelectList [104-110]
+                  SelectColumn [104-110]
+                    IntLiteral(2) [104-105]
+                    Alias [106-110]
+                      Identifier(x) [109-110]
+              Select [121-129]
+                SelectList [128-129]
+                  SelectColumn [128-129]
+                    IntLiteral(3) [128-129]
+              Select [140-148]
+                SelectList [147-148]
+                  SelectColumn [147-148]
+                    IntLiteral(4) [147-148]
+              Select [159-167]
+                SelectList [166-167]
+                  SelectColumn [166-167]
+                    IntLiteral(5) [166-167]
+    Select [169-212]
+      SelectList [176-177]
+        SelectColumn [176-177]
+          Star(*) [176-177]
+      FromClause [178-212]
+        Join(LEFT) [183-212]
+          TablePathExpression [183-184]
+            PathExpression [183-184]
+              Identifier(A) [183-184]
+          Location [185-200]
+          TablePathExpression [201-202]
+            PathExpression [201-202]
+              Identifier(B) [201-202]
+          UsingClause [203-212]
+            Identifier(x) [210-211]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#on_and_using_equivalency
+WITH
+  A AS (SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 3),
+  B AS (SELECT 2 as x UNION ALL SELECT 3 UNION ALL SELECT 4)
+SELECT * FROM A FULL OUTER JOIN B ON A.x = B.x
+--
+QueryStatement [0-174]
+  Query [0-174]
+    WithClause [0-127]
+      WithClauseEntry [7-65]
+        AliasedQuery [7-65]
+          Identifier(A) [7-8]
+          Query [12-65]
+            SetOperation(UNION ALL) [13-64]
+              SetOperationMetadataList [27-55]
+                SetOperationMetadata [27-36]
+                  SetOperationType [27-32]
+                  SetOperationAllOrDistinct [33-36]
+                SetOperationMetadata [46-55]
+                  SetOperationType [46-51]
+                  SetOperationAllOrDistinct [52-55]
+              Select [13-26]
+                SelectList [20-26]
+                  SelectColumn [20-26]
+                    IntLiteral(1) [20-21]
+                    Alias [22-26]
+                      Identifier(x) [25-26]
+              Select [37-45]
+                SelectList [44-45]
+                  SelectColumn [44-45]
+                    IntLiteral(2) [44-45]
+              Select [56-64]
+                SelectList [63-64]
+                  SelectColumn [63-64]
+                    IntLiteral(3) [63-64]
+      WithClauseEntry [69-127]
+        AliasedQuery [69-127]
+          Identifier(B) [69-70]
+          Query [74-127]
+            SetOperation(UNION ALL) [75-126]
+              SetOperationMetadataList [89-117]
+                SetOperationMetadata [89-98]
+                  SetOperationType [89-94]
+                  SetOperationAllOrDistinct [95-98]
+                SetOperationMetadata [108-117]
+                  SetOperationType [108-113]
+                  SetOperationAllOrDistinct [114-117]
+              Select [75-88]
+                SelectList [82-88]
+                  SelectColumn [82-88]
+                    IntLiteral(2) [82-83]
+                    Alias [84-88]
+                      Identifier(x) [87-88]
+              Select [99-107]
+                SelectList [106-107]
+                  SelectColumn [106-107]
+                    IntLiteral(3) [106-107]
+              Select [118-126]
+                SelectList [125-126]
+                  SelectColumn [125-126]
+                    IntLiteral(4) [125-126]
+    Select [128-174]
+      SelectList [135-136]
+        SelectColumn [135-136]
+          Star(*) [135-136]
+      FromClause [137-174]
+        Join(FULL) [142-174]
+          TablePathExpression [142-143]
+            PathExpression [142-143]
+              Identifier(A) [142-143]
+          Location [144-159]
+          TablePathExpression [160-161]
+            PathExpression [160-161]
+              Identifier(B) [160-161]
+          OnClause [162-174]
+            BinaryExpression(=) [165-174]
+              PathExpression [165-168]
+                Identifier(A) [165-166]
+                Identifier(x) [167-168]
+              PathExpression [171-174]
+                Identifier(B) [171-172]
+                Identifier(x) [173-174]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#on_and_using_equivalency
+WITH
+  A AS (SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 3),
+  B AS (SELECT 2 as x UNION ALL SELECT 3 UNION ALL SELECT 4)
+SELECT * FROM A FULL OUTER JOIN B USING (x)
+--
+QueryStatement [0-171]
+  Query [0-171]
+    WithClause [0-127]
+      WithClauseEntry [7-65]
+        AliasedQuery [7-65]
+          Identifier(A) [7-8]
+          Query [12-65]
+            SetOperation(UNION ALL) [13-64]
+              SetOperationMetadataList [27-55]
+                SetOperationMetadata [27-36]
+                  SetOperationType [27-32]
+                  SetOperationAllOrDistinct [33-36]
+                SetOperationMetadata [46-55]
+                  SetOperationType [46-51]
+                  SetOperationAllOrDistinct [52-55]
+              Select [13-26]
+                SelectList [20-26]
+                  SelectColumn [20-26]
+                    IntLiteral(1) [20-21]
+                    Alias [22-26]
+                      Identifier(x) [25-26]
+              Select [37-45]
+                SelectList [44-45]
+                  SelectColumn [44-45]
+                    IntLiteral(2) [44-45]
+              Select [56-64]
+                SelectList [63-64]
+                  SelectColumn [63-64]
+                    IntLiteral(3) [63-64]
+      WithClauseEntry [69-127]
+        AliasedQuery [69-127]
+          Identifier(B) [69-70]
+          Query [74-127]
+            SetOperation(UNION ALL) [75-126]
+              SetOperationMetadataList [89-117]
+                SetOperationMetadata [89-98]
+                  SetOperationType [89-94]
+                  SetOperationAllOrDistinct [95-98]
+                SetOperationMetadata [108-117]
+                  SetOperationType [108-113]
+                  SetOperationAllOrDistinct [114-117]
+              Select [75-88]
+                SelectList [82-88]
+                  SelectColumn [82-88]
+                    IntLiteral(2) [82-83]
+                    Alias [84-88]
+                      Identifier(x) [87-88]
+              Select [99-107]
+                SelectList [106-107]
+                  SelectColumn [106-107]
+                    IntLiteral(3) [106-107]
+              Select [118-126]
+                SelectList [125-126]
+                  SelectColumn [125-126]
+                    IntLiteral(4) [125-126]
+    Select [128-171]
+      SelectList [135-136]
+        SelectColumn [135-136]
+          Star(*) [135-136]
+      FromClause [137-171]
+        Join(FULL) [142-171]
+          TablePathExpression [142-143]
+            PathExpression [142-143]
+              Identifier(A) [142-143]
+          Location [144-159]
+          TablePathExpression [160-161]
+            PathExpression [160-161]
+              Identifier(B) [160-161]
+          UsingClause [162-171]
+            Identifier(x) [169-170]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#on_and_using_equivalency
+WITH
+  A AS (SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT NULL),
+  B AS (SELECT 2 as x UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5)
+SELECT A.x FROM A INNER JOIN B ON A.x = B.x
+--
+QueryStatement [0-212]
+  Query [0-212]
+    WithClause [0-168]
+      WithClauseEntry [7-87]
+        AliasedQuery [7-87]
+          Identifier(A) [7-8]
+          Query [12-87]
+            SetOperation(UNION ALL) [13-86]
+              SetOperationMetadataList [27-74]
+                SetOperationMetadata [27-36]
+                  SetOperationType [27-32]
+                  SetOperationAllOrDistinct [33-36]
+                SetOperationMetadata [46-55]
+                  SetOperationType [46-51]
+                  SetOperationAllOrDistinct [52-55]
+                SetOperationMetadata [65-74]
+                  SetOperationType [65-70]
+                  SetOperationAllOrDistinct [71-74]
+              Select [13-26]
+                SelectList [20-26]
+                  SelectColumn [20-26]
+                    IntLiteral(1) [20-21]
+                    Alias [22-26]
+                      Identifier(x) [25-26]
+              Select [37-45]
+                SelectList [44-45]
+                  SelectColumn [44-45]
+                    IntLiteral(2) [44-45]
+              Select [56-64]
+                SelectList [63-64]
+                  SelectColumn [63-64]
+                    IntLiteral(3) [63-64]
+              Select [75-86]
+                SelectList [82-86]
+                  SelectColumn [82-86]
+                    NullLiteral(NULL) [82-86]
+      WithClauseEntry [91-168]
+        AliasedQuery [91-168]
+          Identifier(B) [91-92]
+          Query [96-168]
+            SetOperation(UNION ALL) [97-167]
+              SetOperationMetadataList [111-158]
+                SetOperationMetadata [111-120]
+                  SetOperationType [111-116]
+                  SetOperationAllOrDistinct [117-120]
+                SetOperationMetadata [130-139]
+                  SetOperationType [130-135]
+                  SetOperationAllOrDistinct [136-139]
+                SetOperationMetadata [149-158]
+                  SetOperationType [149-154]
+                  SetOperationAllOrDistinct [155-158]
+              Select [97-110]
+                SelectList [104-110]
+                  SelectColumn [104-110]
+                    IntLiteral(2) [104-105]
+                    Alias [106-110]
+                      Identifier(x) [109-110]
+              Select [121-129]
+                SelectList [128-129]
+                  SelectColumn [128-129]
+                    IntLiteral(3) [128-129]
+              Select [140-148]
+                SelectList [147-148]
+                  SelectColumn [147-148]
+                    IntLiteral(4) [147-148]
+              Select [159-167]
+                SelectList [166-167]
+                  SelectColumn [166-167]
+                    IntLiteral(5) [166-167]
+    Select [169-212]
+      SelectList [176-179]
+        SelectColumn [176-179]
+          PathExpression [176-179]
+            Identifier(A) [176-177]
+            Identifier(x) [178-179]
+      FromClause [180-212]
+        Join(INNER) [185-212]
+          TablePathExpression [185-186]
+            PathExpression [185-186]
+              Identifier(A) [185-186]
+          Location [187-197]
+          TablePathExpression [198-199]
+            PathExpression [198-199]
+              Identifier(B) [198-199]
+          OnClause [200-212]
+            BinaryExpression(=) [203-212]
+              PathExpression [203-206]
+                Identifier(A) [203-204]
+                Identifier(x) [205-206]
+              PathExpression [209-212]
+                Identifier(B) [209-210]
+                Identifier(x) [211-212]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#on_and_using_equivalency
+WITH
+  A AS (SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT NULL),
+  B AS (SELECT 2 as x UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5)
+SELECT x FROM A INNER JOIN B USING (x)
+--
+QueryStatement [0-207]
+  Query [0-207]
+    WithClause [0-168]
+      WithClauseEntry [7-87]
+        AliasedQuery [7-87]
+          Identifier(A) [7-8]
+          Query [12-87]
+            SetOperation(UNION ALL) [13-86]
+              SetOperationMetadataList [27-74]
+                SetOperationMetadata [27-36]
+                  SetOperationType [27-32]
+                  SetOperationAllOrDistinct [33-36]
+                SetOperationMetadata [46-55]
+                  SetOperationType [46-51]
+                  SetOperationAllOrDistinct [52-55]
+                SetOperationMetadata [65-74]
+                  SetOperationType [65-70]
+                  SetOperationAllOrDistinct [71-74]
+              Select [13-26]
+                SelectList [20-26]
+                  SelectColumn [20-26]
+                    IntLiteral(1) [20-21]
+                    Alias [22-26]
+                      Identifier(x) [25-26]
+              Select [37-45]
+                SelectList [44-45]
+                  SelectColumn [44-45]
+                    IntLiteral(2) [44-45]
+              Select [56-64]
+                SelectList [63-64]
+                  SelectColumn [63-64]
+                    IntLiteral(3) [63-64]
+              Select [75-86]
+                SelectList [82-86]
+                  SelectColumn [82-86]
+                    NullLiteral(NULL) [82-86]
+      WithClauseEntry [91-168]
+        AliasedQuery [91-168]
+          Identifier(B) [91-92]
+          Query [96-168]
+            SetOperation(UNION ALL) [97-167]
+              SetOperationMetadataList [111-158]
+                SetOperationMetadata [111-120]
+                  SetOperationType [111-116]
+                  SetOperationAllOrDistinct [117-120]
+                SetOperationMetadata [130-139]
+                  SetOperationType [130-135]
+                  SetOperationAllOrDistinct [136-139]
+                SetOperationMetadata [149-158]
+                  SetOperationType [149-154]
+                  SetOperationAllOrDistinct [155-158]
+              Select [97-110]
+                SelectList [104-110]
+                  SelectColumn [104-110]
+                    IntLiteral(2) [104-105]
+                    Alias [106-110]
+                      Identifier(x) [109-110]
+              Select [121-129]
+                SelectList [128-129]
+                  SelectColumn [128-129]
+                    IntLiteral(3) [128-129]
+              Select [140-148]
+                SelectList [147-148]
+                  SelectColumn [147-148]
+                    IntLiteral(4) [147-148]
+              Select [159-167]
+                SelectList [166-167]
+                  SelectColumn [166-167]
+                    IntLiteral(5) [166-167]
+    Select [169-207]
+      SelectList [176-177]
+        SelectColumn [176-177]
+          PathExpression [176-177]
+            Identifier(x) [176-177]
+      FromClause [178-207]
+        Join(INNER) [183-207]
+          TablePathExpression [183-184]
+            PathExpression [183-184]
+              Identifier(A) [183-184]
+          Location [185-195]
+          TablePathExpression [196-197]
+            PathExpression [196-197]
+              Identifier(B) [196-197]
+          UsingClause [198-207]
+            Identifier(x) [205-206]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#on_and_using_equivalency
+WITH
+  A AS (SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT NULL),
+  B AS (SELECT 2 as x UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5)
+SELECT A.x FROM A LEFT OUTER JOIN B ON A.x = B.x
+--
+QueryStatement [0-217]
+  Query [0-217]
+    WithClause [0-168]
+      WithClauseEntry [7-87]
+        AliasedQuery [7-87]
+          Identifier(A) [7-8]
+          Query [12-87]
+            SetOperation(UNION ALL) [13-86]
+              SetOperationMetadataList [27-74]
+                SetOperationMetadata [27-36]
+                  SetOperationType [27-32]
+                  SetOperationAllOrDistinct [33-36]
+                SetOperationMetadata [46-55]
+                  SetOperationType [46-51]
+                  SetOperationAllOrDistinct [52-55]
+                SetOperationMetadata [65-74]
+                  SetOperationType [65-70]
+                  SetOperationAllOrDistinct [71-74]
+              Select [13-26]
+                SelectList [20-26]
+                  SelectColumn [20-26]
+                    IntLiteral(1) [20-21]
+                    Alias [22-26]
+                      Identifier(x) [25-26]
+              Select [37-45]
+                SelectList [44-45]
+                  SelectColumn [44-45]
+                    IntLiteral(2) [44-45]
+              Select [56-64]
+                SelectList [63-64]
+                  SelectColumn [63-64]
+                    IntLiteral(3) [63-64]
+              Select [75-86]
+                SelectList [82-86]
+                  SelectColumn [82-86]
+                    NullLiteral(NULL) [82-86]
+      WithClauseEntry [91-168]
+        AliasedQuery [91-168]
+          Identifier(B) [91-92]
+          Query [96-168]
+            SetOperation(UNION ALL) [97-167]
+              SetOperationMetadataList [111-158]
+                SetOperationMetadata [111-120]
+                  SetOperationType [111-116]
+                  SetOperationAllOrDistinct [117-120]
+                SetOperationMetadata [130-139]
+                  SetOperationType [130-135]
+                  SetOperationAllOrDistinct [136-139]
+                SetOperationMetadata [149-158]
+                  SetOperationType [149-154]
+                  SetOperationAllOrDistinct [155-158]
+              Select [97-110]
+                SelectList [104-110]
+                  SelectColumn [104-110]
+                    IntLiteral(2) [104-105]
+                    Alias [106-110]
+                      Identifier(x) [109-110]
+              Select [121-129]
+                SelectList [128-129]
+                  SelectColumn [128-129]
+                    IntLiteral(3) [128-129]
+              Select [140-148]
+                SelectList [147-148]
+                  SelectColumn [147-148]
+                    IntLiteral(4) [147-148]
+              Select [159-167]
+                SelectList [166-167]
+                  SelectColumn [166-167]
+                    IntLiteral(5) [166-167]
+    Select [169-217]
+      SelectList [176-179]
+        SelectColumn [176-179]
+          PathExpression [176-179]
+            Identifier(A) [176-177]
+            Identifier(x) [178-179]
+      FromClause [180-217]
+        Join(LEFT) [185-217]
+          TablePathExpression [185-186]
+            PathExpression [185-186]
+              Identifier(A) [185-186]
+          Location [187-202]
+          TablePathExpression [203-204]
+            PathExpression [203-204]
+              Identifier(B) [203-204]
+          OnClause [205-217]
+            BinaryExpression(=) [208-217]
+              PathExpression [208-211]
+                Identifier(A) [208-209]
+                Identifier(x) [210-211]
+              PathExpression [214-217]
+                Identifier(B) [214-215]
+                Identifier(x) [216-217]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#on_and_using_equivalency
+WITH
+  A AS (SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT NULL),
+  B AS (SELECT 2 as x UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5)
+SELECT x FROM A LEFT OUTER JOIN B USING (x)
+--
+QueryStatement [0-212]
+  Query [0-212]
+    WithClause [0-168]
+      WithClauseEntry [7-87]
+        AliasedQuery [7-87]
+          Identifier(A) [7-8]
+          Query [12-87]
+            SetOperation(UNION ALL) [13-86]
+              SetOperationMetadataList [27-74]
+                SetOperationMetadata [27-36]
+                  SetOperationType [27-32]
+                  SetOperationAllOrDistinct [33-36]
+                SetOperationMetadata [46-55]
+                  SetOperationType [46-51]
+                  SetOperationAllOrDistinct [52-55]
+                SetOperationMetadata [65-74]
+                  SetOperationType [65-70]
+                  SetOperationAllOrDistinct [71-74]
+              Select [13-26]
+                SelectList [20-26]
+                  SelectColumn [20-26]
+                    IntLiteral(1) [20-21]
+                    Alias [22-26]
+                      Identifier(x) [25-26]
+              Select [37-45]
+                SelectList [44-45]
+                  SelectColumn [44-45]
+                    IntLiteral(2) [44-45]
+              Select [56-64]
+                SelectList [63-64]
+                  SelectColumn [63-64]
+                    IntLiteral(3) [63-64]
+              Select [75-86]
+                SelectList [82-86]
+                  SelectColumn [82-86]
+                    NullLiteral(NULL) [82-86]
+      WithClauseEntry [91-168]
+        AliasedQuery [91-168]
+          Identifier(B) [91-92]
+          Query [96-168]
+            SetOperation(UNION ALL) [97-167]
+              SetOperationMetadataList [111-158]
+                SetOperationMetadata [111-120]
+                  SetOperationType [111-116]
+                  SetOperationAllOrDistinct [117-120]
+                SetOperationMetadata [130-139]
+                  SetOperationType [130-135]
+                  SetOperationAllOrDistinct [136-139]
+                SetOperationMetadata [149-158]
+                  SetOperationType [149-154]
+                  SetOperationAllOrDistinct [155-158]
+              Select [97-110]
+                SelectList [104-110]
+                  SelectColumn [104-110]
+                    IntLiteral(2) [104-105]
+                    Alias [106-110]
+                      Identifier(x) [109-110]
+              Select [121-129]
+                SelectList [128-129]
+                  SelectColumn [128-129]
+                    IntLiteral(3) [128-129]
+              Select [140-148]
+                SelectList [147-148]
+                  SelectColumn [147-148]
+                    IntLiteral(4) [147-148]
+              Select [159-167]
+                SelectList [166-167]
+                  SelectColumn [166-167]
+                    IntLiteral(5) [166-167]
+    Select [169-212]
+      SelectList [176-177]
+        SelectColumn [176-177]
+          PathExpression [176-177]
+            Identifier(x) [176-177]
+      FromClause [178-212]
+        Join(LEFT) [183-212]
+          TablePathExpression [183-184]
+            PathExpression [183-184]
+              Identifier(A) [183-184]
+          Location [185-200]
+          TablePathExpression [201-202]
+            PathExpression [201-202]
+              Identifier(B) [201-202]
+          UsingClause [203-212]
+            Identifier(x) [210-211]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#on_and_using_equivalency
+WITH
+  A AS (SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT NULL),
+  B AS (SELECT 2 as x UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5)
+SELECT A.x FROM A FULL OUTER JOIN B ON A.x = B.x
+--
+QueryStatement [0-217]
+  Query [0-217]
+    WithClause [0-168]
+      WithClauseEntry [7-87]
+        AliasedQuery [7-87]
+          Identifier(A) [7-8]
+          Query [12-87]
+            SetOperation(UNION ALL) [13-86]
+              SetOperationMetadataList [27-74]
+                SetOperationMetadata [27-36]
+                  SetOperationType [27-32]
+                  SetOperationAllOrDistinct [33-36]
+                SetOperationMetadata [46-55]
+                  SetOperationType [46-51]
+                  SetOperationAllOrDistinct [52-55]
+                SetOperationMetadata [65-74]
+                  SetOperationType [65-70]
+                  SetOperationAllOrDistinct [71-74]
+              Select [13-26]
+                SelectList [20-26]
+                  SelectColumn [20-26]
+                    IntLiteral(1) [20-21]
+                    Alias [22-26]
+                      Identifier(x) [25-26]
+              Select [37-45]
+                SelectList [44-45]
+                  SelectColumn [44-45]
+                    IntLiteral(2) [44-45]
+              Select [56-64]
+                SelectList [63-64]
+                  SelectColumn [63-64]
+                    IntLiteral(3) [63-64]
+              Select [75-86]
+                SelectList [82-86]
+                  SelectColumn [82-86]
+                    NullLiteral(NULL) [82-86]
+      WithClauseEntry [91-168]
+        AliasedQuery [91-168]
+          Identifier(B) [91-92]
+          Query [96-168]
+            SetOperation(UNION ALL) [97-167]
+              SetOperationMetadataList [111-158]
+                SetOperationMetadata [111-120]
+                  SetOperationType [111-116]
+                  SetOperationAllOrDistinct [117-120]
+                SetOperationMetadata [130-139]
+                  SetOperationType [130-135]
+                  SetOperationAllOrDistinct [136-139]
+                SetOperationMetadata [149-158]
+                  SetOperationType [149-154]
+                  SetOperationAllOrDistinct [155-158]
+              Select [97-110]
+                SelectList [104-110]
+                  SelectColumn [104-110]
+                    IntLiteral(2) [104-105]
+                    Alias [106-110]
+                      Identifier(x) [109-110]
+              Select [121-129]
+                SelectList [128-129]
+                  SelectColumn [128-129]
+                    IntLiteral(3) [128-129]
+              Select [140-148]
+                SelectList [147-148]
+                  SelectColumn [147-148]
+                    IntLiteral(4) [147-148]
+              Select [159-167]
+                SelectList [166-167]
+                  SelectColumn [166-167]
+                    IntLiteral(5) [166-167]
+    Select [169-217]
+      SelectList [176-179]
+        SelectColumn [176-179]
+          PathExpression [176-179]
+            Identifier(A) [176-177]
+            Identifier(x) [178-179]
+      FromClause [180-217]
+        Join(FULL) [185-217]
+          TablePathExpression [185-186]
+            PathExpression [185-186]
+              Identifier(A) [185-186]
+          Location [187-202]
+          TablePathExpression [203-204]
+            PathExpression [203-204]
+              Identifier(B) [203-204]
+          OnClause [205-217]
+            BinaryExpression(=) [208-217]
+              PathExpression [208-211]
+                Identifier(A) [208-209]
+                Identifier(x) [210-211]
+              PathExpression [214-217]
+                Identifier(B) [214-215]
+                Identifier(x) [216-217]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#on_and_using_equivalency
+WITH
+  A AS (SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT NULL),
+  B AS (SELECT 2 as x UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5)
+SELECT x FROM A FULL OUTER JOIN B USING (x)
+--
+QueryStatement [0-212]
+  Query [0-212]
+    WithClause [0-168]
+      WithClauseEntry [7-87]
+        AliasedQuery [7-87]
+          Identifier(A) [7-8]
+          Query [12-87]
+            SetOperation(UNION ALL) [13-86]
+              SetOperationMetadataList [27-74]
+                SetOperationMetadata [27-36]
+                  SetOperationType [27-32]
+                  SetOperationAllOrDistinct [33-36]
+                SetOperationMetadata [46-55]
+                  SetOperationType [46-51]
+                  SetOperationAllOrDistinct [52-55]
+                SetOperationMetadata [65-74]
+                  SetOperationType [65-70]
+                  SetOperationAllOrDistinct [71-74]
+              Select [13-26]
+                SelectList [20-26]
+                  SelectColumn [20-26]
+                    IntLiteral(1) [20-21]
+                    Alias [22-26]
+                      Identifier(x) [25-26]
+              Select [37-45]
+                SelectList [44-45]
+                  SelectColumn [44-45]
+                    IntLiteral(2) [44-45]
+              Select [56-64]
+                SelectList [63-64]
+                  SelectColumn [63-64]
+                    IntLiteral(3) [63-64]
+              Select [75-86]
+                SelectList [82-86]
+                  SelectColumn [82-86]
+                    NullLiteral(NULL) [82-86]
+      WithClauseEntry [91-168]
+        AliasedQuery [91-168]
+          Identifier(B) [91-92]
+          Query [96-168]
+            SetOperation(UNION ALL) [97-167]
+              SetOperationMetadataList [111-158]
+                SetOperationMetadata [111-120]
+                  SetOperationType [111-116]
+                  SetOperationAllOrDistinct [117-120]
+                SetOperationMetadata [130-139]
+                  SetOperationType [130-135]
+                  SetOperationAllOrDistinct [136-139]
+                SetOperationMetadata [149-158]
+                  SetOperationType [149-154]
+                  SetOperationAllOrDistinct [155-158]
+              Select [97-110]
+                SelectList [104-110]
+                  SelectColumn [104-110]
+                    IntLiteral(2) [104-105]
+                    Alias [106-110]
+                      Identifier(x) [109-110]
+              Select [121-129]
+                SelectList [128-129]
+                  SelectColumn [128-129]
+                    IntLiteral(3) [128-129]
+              Select [140-148]
+                SelectList [147-148]
+                  SelectColumn [147-148]
+                    IntLiteral(4) [147-148]
+              Select [159-167]
+                SelectList [166-167]
+                  SelectColumn [166-167]
+                    IntLiteral(5) [166-167]
+    Select [169-212]
+      SelectList [176-177]
+        SelectColumn [176-177]
+          PathExpression [176-177]
+            Identifier(x) [176-177]
+      FromClause [178-212]
+        Join(FULL) [183-212]
+          TablePathExpression [183-184]
+            PathExpression [183-184]
+              Identifier(A) [183-184]
+          Location [185-200]
+          TablePathExpression [201-202]
+            PathExpression [201-202]
+              Identifier(B) [201-202]
+          UsingClause [203-212]
+            Identifier(x) [210-211]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#on_and_using_equivalency
+WITH
+  A AS (SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT NULL),
+  B AS (SELECT 2 as x UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5)
+SELECT B.x FROM A FULL OUTER JOIN B ON A.x = B.x
+--
+QueryStatement [0-217]
+  Query [0-217]
+    WithClause [0-168]
+      WithClauseEntry [7-87]
+        AliasedQuery [7-87]
+          Identifier(A) [7-8]
+          Query [12-87]
+            SetOperation(UNION ALL) [13-86]
+              SetOperationMetadataList [27-74]
+                SetOperationMetadata [27-36]
+                  SetOperationType [27-32]
+                  SetOperationAllOrDistinct [33-36]
+                SetOperationMetadata [46-55]
+                  SetOperationType [46-51]
+                  SetOperationAllOrDistinct [52-55]
+                SetOperationMetadata [65-74]
+                  SetOperationType [65-70]
+                  SetOperationAllOrDistinct [71-74]
+              Select [13-26]
+                SelectList [20-26]
+                  SelectColumn [20-26]
+                    IntLiteral(1) [20-21]
+                    Alias [22-26]
+                      Identifier(x) [25-26]
+              Select [37-45]
+                SelectList [44-45]
+                  SelectColumn [44-45]
+                    IntLiteral(2) [44-45]
+              Select [56-64]
+                SelectList [63-64]
+                  SelectColumn [63-64]
+                    IntLiteral(3) [63-64]
+              Select [75-86]
+                SelectList [82-86]
+                  SelectColumn [82-86]
+                    NullLiteral(NULL) [82-86]
+      WithClauseEntry [91-168]
+        AliasedQuery [91-168]
+          Identifier(B) [91-92]
+          Query [96-168]
+            SetOperation(UNION ALL) [97-167]
+              SetOperationMetadataList [111-158]
+                SetOperationMetadata [111-120]
+                  SetOperationType [111-116]
+                  SetOperationAllOrDistinct [117-120]
+                SetOperationMetadata [130-139]
+                  SetOperationType [130-135]
+                  SetOperationAllOrDistinct [136-139]
+                SetOperationMetadata [149-158]
+                  SetOperationType [149-154]
+                  SetOperationAllOrDistinct [155-158]
+              Select [97-110]
+                SelectList [104-110]
+                  SelectColumn [104-110]
+                    IntLiteral(2) [104-105]
+                    Alias [106-110]
+                      Identifier(x) [109-110]
+              Select [121-129]
+                SelectList [128-129]
+                  SelectColumn [128-129]
+                    IntLiteral(3) [128-129]
+              Select [140-148]
+                SelectList [147-148]
+                  SelectColumn [147-148]
+                    IntLiteral(4) [147-148]
+              Select [159-167]
+                SelectList [166-167]
+                  SelectColumn [166-167]
+                    IntLiteral(5) [166-167]
+    Select [169-217]
+      SelectList [176-179]
+        SelectColumn [176-179]
+          PathExpression [176-179]
+            Identifier(B) [176-177]
+            Identifier(x) [178-179]
+      FromClause [180-217]
+        Join(FULL) [185-217]
+          TablePathExpression [185-186]
+            PathExpression [185-186]
+              Identifier(A) [185-186]
+          Location [187-202]
+          TablePathExpression [203-204]
+            PathExpression [203-204]
+              Identifier(B) [203-204]
+          OnClause [205-217]
+            BinaryExpression(=) [208-217]
+              PathExpression [208-211]
+                Identifier(A) [208-209]
+                Identifier(x) [210-211]
+              PathExpression [214-217]
+                Identifier(B) [214-215]
+                Identifier(x) [216-217]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#on_and_using_equivalency
+WITH
+  A AS (SELECT 1 as x UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT NULL),
+  B AS (SELECT 2 as x UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5)
+SELECT COALESCE(A.x, B.x) FROM A FULL OUTER JOIN B ON A.x = B.x
+--
+QueryStatement [0-232]
+  Query [0-232]
+    WithClause [0-168]
+      WithClauseEntry [7-87]
+        AliasedQuery [7-87]
+          Identifier(A) [7-8]
+          Query [12-87]
+            SetOperation(UNION ALL) [13-86]
+              SetOperationMetadataList [27-74]
+                SetOperationMetadata [27-36]
+                  SetOperationType [27-32]
+                  SetOperationAllOrDistinct [33-36]
+                SetOperationMetadata [46-55]
+                  SetOperationType [46-51]
+                  SetOperationAllOrDistinct [52-55]
+                SetOperationMetadata [65-74]
+                  SetOperationType [65-70]
+                  SetOperationAllOrDistinct [71-74]
+              Select [13-26]
+                SelectList [20-26]
+                  SelectColumn [20-26]
+                    IntLiteral(1) [20-21]
+                    Alias [22-26]
+                      Identifier(x) [25-26]
+              Select [37-45]
+                SelectList [44-45]
+                  SelectColumn [44-45]
+                    IntLiteral(2) [44-45]
+              Select [56-64]
+                SelectList [63-64]
+                  SelectColumn [63-64]
+                    IntLiteral(3) [63-64]
+              Select [75-86]
+                SelectList [82-86]
+                  SelectColumn [82-86]
+                    NullLiteral(NULL) [82-86]
+      WithClauseEntry [91-168]
+        AliasedQuery [91-168]
+          Identifier(B) [91-92]
+          Query [96-168]
+            SetOperation(UNION ALL) [97-167]
+              SetOperationMetadataList [111-158]
+                SetOperationMetadata [111-120]
+                  SetOperationType [111-116]
+                  SetOperationAllOrDistinct [117-120]
+                SetOperationMetadata [130-139]
+                  SetOperationType [130-135]
+                  SetOperationAllOrDistinct [136-139]
+                SetOperationMetadata [149-158]
+                  SetOperationType [149-154]
+                  SetOperationAllOrDistinct [155-158]
+              Select [97-110]
+                SelectList [104-110]
+                  SelectColumn [104-110]
+                    IntLiteral(2) [104-105]
+                    Alias [106-110]
+                      Identifier(x) [109-110]
+              Select [121-129]
+                SelectList [128-129]
+                  SelectColumn [128-129]
+                    IntLiteral(3) [128-129]
+              Select [140-148]
+                SelectList [147-148]
+                  SelectColumn [147-148]
+                    IntLiteral(4) [147-148]
+              Select [159-167]
+                SelectList [166-167]
+                  SelectColumn [166-167]
+                    IntLiteral(5) [166-167]
+    Select [169-232]
+      SelectList [176-194]
+        SelectColumn [176-194]
+          FunctionCall [176-194]
+            PathExpression [176-184]
+              Identifier(COALESCE) [176-184]
+            PathExpression [185-188]
+              Identifier(A) [185-186]
+              Identifier(x) [187-188]
+            PathExpression [190-193]
+              Identifier(B) [190-191]
+              Identifier(x) [192-193]
+      FromClause [195-232]
+        Join(FULL) [200-232]
+          TablePathExpression [200-201]
+            PathExpression [200-201]
+              Identifier(A) [200-201]
+          Location [202-217]
+          TablePathExpression [218-219]
+            PathExpression [218-219]
+              Identifier(B) [218-219]
+          OnClause [220-232]
+            BinaryExpression(=) [223-232]
+              PathExpression [223-226]
+                Identifier(A) [223-224]
+                Identifier(x) [225-226]
+              PathExpression [229-232]
+                Identifier(B) [229-230]
+                Identifier(x) [231-232]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#sequences_of_joins
+FROM A JOIN B USING (x) JOIN C USING (x)
+
+-- A JOIN B USING (x)        = result_1
+-- result_1 JOIN C USING (x) = result_2
+-- result_2                  = return value
+--
+QueryStatement [0-40]
+  Query [0-40]
+    FromQuery [0-40]
+      FromClause [0-40]
+        Join [5-40]
+          Join [5-23]
+            TablePathExpression [5-6]
+              PathExpression [5-6]
+                Identifier(A) [5-6]
+            Location [7-11]
+            TablePathExpression [12-13]
+              PathExpression [12-13]
+                Identifier(B) [12-13]
+            UsingClause [14-23]
+              Identifier(x) [21-22]
+          Location [24-28]
+          TablePathExpression [29-30]
+            PathExpression [29-30]
+              Identifier(C) [29-30]
+          UsingClause [31-40]
+            Identifier(x) [38-39]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#sequences_of_joins
+FROM ( (A JOIN B USING (x)) JOIN C USING (x) )
+
+-- A JOIN B USING (x)        = result_1
+-- result_1 JOIN C USING (x) = result_2
+-- result_2                  = return value
+--
+QueryStatement [0-46]
+  Query [0-46]
+    FromQuery [0-46]
+      FromClause [0-46]
+        ParenthesizedJoin [5-46]
+          Join [7-44]
+            ParenthesizedJoin [7-27]
+              Join [8-26]
+                TablePathExpression [8-9]
+                  PathExpression [8-9]
+                    Identifier(A) [8-9]
+                Location [10-14]
+                TablePathExpression [15-16]
+                  PathExpression [15-16]
+                    Identifier(B) [15-16]
+                UsingClause [17-26]
+                  Identifier(x) [24-25]
+            Location [28-32]
+            TablePathExpression [33-34]
+              PathExpression [33-34]
+                Identifier(C) [33-34]
+            UsingClause [35-44]
+              Identifier(x) [42-43]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#sequences_of_joins
+FROM ( A JOIN (B JOIN C USING (x)) USING (x) )
+
+-- B JOIN C USING (x)       = result_1
+-- A JOIN result_1          = result_2
+-- result_2                 = return value
+--
+QueryStatement [0-46]
+  Query [0-46]
+    FromQuery [0-46]
+      FromClause [0-46]
+        ParenthesizedJoin [5-46]
+          Join [7-44]
+            TablePathExpression [7-8]
+              PathExpression [7-8]
+                Identifier(A) [7-8]
+            Location [9-13]
+            ParenthesizedJoin [14-34]
+              Join [15-33]
+                TablePathExpression [15-16]
+                  PathExpression [15-16]
+                    Identifier(B) [15-16]
+                Location [17-21]
+                TablePathExpression [22-23]
+                  PathExpression [22-23]
+                    Identifier(C) [22-23]
+                UsingClause [24-33]
+                  Identifier(x) [31-32]
+            UsingClause [35-44]
+              Identifier(x) [42-43]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#sequences_of_joins
+FROM A JOIN B USING (x) JOIN C USING (x), D
+
+-- A JOIN B USING (x)        = result_1
+-- result_1 JOIN C USING (x) = result_2
+-- result_2 CROSS JOIN D     = return value
+--
+QueryStatement [0-43]
+  Query [0-43]
+    FromQuery [0-43]
+      FromClause [0-43]
+        Join(COMMA) [5-43]
+          Join [5-40]
+            Join [5-23]
+              TablePathExpression [5-6]
+                PathExpression [5-6]
+                  Identifier(A) [5-6]
+              Location [7-11]
+              TablePathExpression [12-13]
+                PathExpression [12-13]
+                  Identifier(B) [12-13]
+              UsingClause [14-23]
+                Identifier(x) [21-22]
+            Location [24-28]
+            TablePathExpression [29-30]
+              PathExpression [29-30]
+                Identifier(C) [29-30]
+            UsingClause [31-40]
+              Identifier(x) [38-39]
+          Location [40-41]
+          TablePathExpression [42-43]
+            PathExpression [42-43]
+              Identifier(D) [42-43]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#correlated_join
+FROM A JOIN UNNEST(ARRAY(SELECT AS STRUCT * FROM B WHERE A.ID = B.ID)) AS C
+--
+QueryStatement [0-75]
+  Query [0-75]
+    FromQuery [0-75]
+      FromClause [0-75]
+        Join [5-75]
+          TablePathExpression [5-6]
+            PathExpression [5-6]
+              Identifier(A) [5-6]
+          Location [7-11]
+          TablePathExpression [12-75]
+            UnnestExpression [12-70]
+              ExpressionWithOptAlias [19-69]
+                ExpressionSubquery(modifier=ARRAY) [19-69]
+                  Query [25-68]
+                    Select [25-68]
+                      SelectAs(as_mode=STRUCT) [32-41]
+                      SelectList [42-43]
+                        SelectColumn [42-43]
+                          Star(*) [42-43]
+                      FromClause [44-50]
+                        TablePathExpression [49-50]
+                          PathExpression [49-50]
+                            Identifier(B) [49-50]
+                      WhereClause [51-68]
+                        BinaryExpression(=) [57-68]
+                          PathExpression [57-61]
+                            Identifier(A) [57-58]
+                            Identifier(ID) [59-61]
+                          PathExpression [64-68]
+                            Identifier(B) [64-65]
+                            Identifier(ID) [66-68]
+            Alias [71-75]
+              Identifier(C) [74-75]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#correlated_join
+FROM A JOIN UNNEST(A.array_of_IDs) AS C
+--
+QueryStatement [0-39]
+  Query [0-39]
+    FromQuery [0-39]
+      FromClause [0-39]
+        Join [5-39]
+          TablePathExpression [5-6]
+            PathExpression [5-6]
+              Identifier(A) [5-6]
+          Location [7-11]
+          TablePathExpression [12-39]
+            UnnestExpression [12-34]
+              ExpressionWithOptAlias [19-33]
+                PathExpression [19-33]
+                  Identifier(A) [19-20]
+                  Identifier(array_of_IDs) [21-33]
+            Alias [35-39]
+              Identifier(C) [38-39]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#correlated_join
+FROM A JOIN UNNEST(A.array_of_IDs) AS IDs
+--
+QueryStatement [0-41]
+  Query [0-41]
+    FromQuery [0-41]
+      FromClause [0-41]
+        Join [5-41]
+          TablePathExpression [5-6]
+            PathExpression [5-6]
+              Identifier(A) [5-6]
+          Location [7-11]
+          TablePathExpression [12-41]
+            UnnestExpression [12-34]
+              ExpressionWithOptAlias [19-33]
+                PathExpression [19-33]
+                  Identifier(A) [19-20]
+                  Identifier(array_of_IDs) [21-33]
+            Alias [35-41]
+              Identifier(IDs) [38-41]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#correlated_join
+FROM A JOIN A.array_of_IDs AS IDs
+--
+QueryStatement [0-33]
+  Query [0-33]
+    FromQuery [0-33]
+      FromClause [0-33]
+        Join [5-33]
+          TablePathExpression [5-6]
+            PathExpression [5-6]
+              Identifier(A) [5-6]
+          Location [7-11]
+          TablePathExpression [12-33]
+            PathExpression [12-26]
+              Identifier(A) [12-13]
+              Identifier(array_of_IDs) [14-26]
+            Alias [27-33]
+              Identifier(IDs) [30-33]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#correlated_join
+FROM
+  A
+  JOIN
+  UNNEST(ARRAY(SELECT AS STRUCT * FROM B WHERE A.ID = B.ID)) AS C
+  ON A.Name = C.Name
+--
+QueryStatement [0-102]
+  Query [0-102]
+    FromQuery [0-102]
+      FromClause [0-102]
+        Join [7-102]
+          TablePathExpression [7-8]
+            PathExpression [7-8]
+              Identifier(A) [7-8]
+          Location [11-15]
+          TablePathExpression [18-81]
+            UnnestExpression [18-76]
+              ExpressionWithOptAlias [25-75]
+                ExpressionSubquery(modifier=ARRAY) [25-75]
+                  Query [31-74]
+                    Select [31-74]
+                      SelectAs(as_mode=STRUCT) [38-47]
+                      SelectList [48-49]
+                        SelectColumn [48-49]
+                          Star(*) [48-49]
+                      FromClause [50-56]
+                        TablePathExpression [55-56]
+                          PathExpression [55-56]
+                            Identifier(B) [55-56]
+                      WhereClause [57-74]
+                        BinaryExpression(=) [63-74]
+                          PathExpression [63-67]
+                            Identifier(A) [63-64]
+                            Identifier(ID) [65-67]
+                          PathExpression [70-74]
+                            Identifier(B) [70-71]
+                            Identifier(ID) [72-74]
+            Alias [77-81]
+              Identifier(C) [80-81]
+          OnClause [84-102]
+            BinaryExpression(=) [87-102]
+              PathExpression [87-93]
+                Identifier(A) [87-88]
+                Identifier(Name) [89-93]
+              PathExpression [96-102]
+                Identifier(C) [96-97]
+                Identifier(Name) [98-102]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#correlated_join
+SELECT *
+FROM
+  Roster
+JOIN
+  UNNEST(
+    ARRAY(
+      SELECT AS STRUCT *
+      FROM PlayerStats
+      WHERE PlayerStats.OpponentID = Roster.SchoolID
+    )) AS PlayerMatches
+  ON PlayerMatches.LastName = 'Buchanan'
+--
+QueryStatement [0-214]
+  Query [0-214]
+    Select [0-214]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-214]
+        Join [16-214]
+          TablePathExpression [16-22]
+            PathExpression [16-22]
+              Identifier(Roster) [16-22]
+          Location [23-27]
+          TablePathExpression [30-173]
+            UnnestExpression [30-156]
+              ExpressionWithOptAlias [42-155]
+                ExpressionSubquery(modifier=ARRAY) [42-155]
+                  Query [55-149]
+                    Select [55-149]
+                      SelectAs(as_mode=STRUCT) [62-71]
+                      SelectList [72-73]
+                        SelectColumn [72-73]
+                          Star(*) [72-73]
+                      FromClause [80-96]
+                        TablePathExpression [85-96]
+                          PathExpression [85-96]
+                            Identifier(PlayerStats) [85-96]
+                      WhereClause [103-149]
+                        BinaryExpression(=) [109-149]
+                          PathExpression [109-131]
+                            Identifier(PlayerStats) [109-120]
+                            Identifier(OpponentID) [121-131]
+                          PathExpression [134-149]
+                            Identifier(Roster) [134-140]
+                            Identifier(SchoolID) [141-149]
+            Alias [157-173]
+              Identifier(PlayerMatches) [160-173]
+          OnClause [176-214]
+            BinaryExpression(=) [179-214]
+              PathExpression [179-201]
+                Identifier(PlayerMatches) [179-192]
+                Identifier(LastName) [193-201]
+              StringLiteral [204-214]
+                StringLiteralComponent('Buchanan') [204-214]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#correlated_join
+SELECT A.name, item, ARRAY_LENGTH(A.items) item_count_for_name
+FROM
+  UNNEST(
+\    [
+      STRUCT(
+        'first' AS name,
+\        [1, 2, 3, 4] AS items),
+      STRUCT(
+        'second' AS name,
+\        [] AS items)]) AS A
+LEFT JOIN
+  A.items AS item
+--
+QueryStatement [0-250]
+  Query [0-250]
+    Select [0-250]
+      SelectList [7-62]
+        SelectColumn [7-13]
+          PathExpression [7-13]
+            Identifier(A) [7-8]
+            Identifier(name) [9-13]
+        SelectColumn [15-19]
+          PathExpression [15-19]
+            Identifier(item) [15-19]
+        SelectColumn [21-62]
+          FunctionCall [21-42]
+            PathExpression [21-33]
+              Identifier(ARRAY_LENGTH) [21-33]
+            PathExpression [34-41]
+              Identifier(A) [34-35]
+              Identifier(items) [36-41]
+          Alias [43-62]
+            Identifier(item_count_for_name) [43-62]
+      FromClause [63-250]
+        Join(LEFT) [70-250]
+          TablePathExpression [70-222]
+            UnnestExpression [70-217]
+              ExpressionWithOptAlias [82-216]
+                ArrayConstructor [82-216]
+                  StructConstructorWithKeyword [90-153]
+                    StructConstructorArg [106-121]
+                      StringLiteral [106-113]
+                        StringLiteralComponent('first') [106-113]
+                      Alias [114-121]
+                        Identifier(name) [117-121]
+                    StructConstructorArg [131-152]
+                      ArrayConstructor [131-143]
+                        IntLiteral(1) [132-133]
+                        IntLiteral(2) [135-136]
+                        IntLiteral(3) [138-139]
+                        IntLiteral(4) [141-142]
+                      Alias [144-152]
+                        Identifier(items) [147-152]
+                  StructConstructorWithKeyword [161-215]
+                    StructConstructorArg [177-193]
+                      StringLiteral [177-185]
+                        StringLiteralComponent('second') [177-185]
+                      Alias [186-193]
+                        Identifier(name) [189-193]
+                    StructConstructorArg [203-214]
+                      ArrayConstructor [203-205]
+                      Alias [206-214]
+                        Identifier(items) [209-214]
+            Alias [218-222]
+              Identifier(A) [221-222]
+          Location [223-232]
+          TablePathExpression [235-250]
+            PathExpression [235-242]
+              Identifier(A) [235-236]
+              Identifier(items) [237-242]
+            Alias [243-250]
+              Identifier(item) [246-250]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#correlated_join
+SELECT A.name, item
+FROM
+  UNNEST(
+\    [
+      STRUCT(
+        'first' AS name,
+\        [1, 2, 3, 4] AS items),
+      STRUCT(
+        'second' AS name,
+\        [] AS items)]) AS A
+INNER JOIN
+  A.items AS item
+--
+QueryStatement [0-208]
+  Query [0-208]
+    Select [0-208]
+      SelectList [7-19]
+        SelectColumn [7-13]
+          PathExpression [7-13]
+            Identifier(A) [7-8]
+            Identifier(name) [9-13]
+        SelectColumn [15-19]
+          PathExpression [15-19]
+            Identifier(item) [15-19]
+      FromClause [20-208]
+        Join(INNER) [27-208]
+          TablePathExpression [27-179]
+            UnnestExpression [27-174]
+              ExpressionWithOptAlias [39-173]
+                ArrayConstructor [39-173]
+                  StructConstructorWithKeyword [47-110]
+                    StructConstructorArg [63-78]
+                      StringLiteral [63-70]
+                        StringLiteralComponent('first') [63-70]
+                      Alias [71-78]
+                        Identifier(name) [74-78]
+                    StructConstructorArg [88-109]
+                      ArrayConstructor [88-100]
+                        IntLiteral(1) [89-90]
+                        IntLiteral(2) [92-93]
+                        IntLiteral(3) [95-96]
+                        IntLiteral(4) [98-99]
+                      Alias [101-109]
+                        Identifier(items) [104-109]
+                  StructConstructorWithKeyword [118-172]
+                    StructConstructorArg [134-150]
+                      StringLiteral [134-142]
+                        StringLiteralComponent('second') [134-142]
+                      Alias [143-150]
+                        Identifier(name) [146-150]
+                    StructConstructorArg [160-171]
+                      ArrayConstructor [160-162]
+                      Alias [163-171]
+                        Identifier(items) [166-171]
+            Alias [175-179]
+              Identifier(A) [178-179]
+          Location [180-190]
+          TablePathExpression [193-208]
+            PathExpression [193-200]
+              Identifier(A) [193-194]
+              Identifier(items) [195-200]
+            Alias [201-208]
+              Identifier(item) [204-208]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#where_clause
+SELECT * FROM Roster
+WHERE SchoolID = 52
+--
+QueryStatement [0-40]
+  Query [0-40]
+    Select [0-40]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-20]
+        TablePathExpression [14-20]
+          PathExpression [14-20]
+            Identifier(Roster) [14-20]
+      WhereClause [21-40]
+        BinaryExpression(=) [27-40]
+          PathExpression [27-35]
+            Identifier(SchoolID) [27-35]
+          IntLiteral(52) [38-40]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#where_clause
+SELECT * FROM Roster
+WHERE STARTS_WITH(LastName, "Mc") OR STARTS_WITH(LastName, "Mac")
+--
+QueryStatement [0-86]
+  Query [0-86]
+    Select [0-86]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-20]
+        TablePathExpression [14-20]
+          PathExpression [14-20]
+            Identifier(Roster) [14-20]
+      WhereClause [21-86]
+        OrExpr [27-86]
+          FunctionCall [27-54]
+            PathExpression [27-38]
+              Identifier(STARTS_WITH) [27-38]
+            PathExpression [39-47]
+              Identifier(LastName) [39-47]
+            StringLiteral [49-53]
+              StringLiteralComponent("Mc") [49-53]
+          FunctionCall [58-86]
+            PathExpression [58-69]
+              Identifier(STARTS_WITH) [58-69]
+            PathExpression [70-78]
+              Identifier(LastName) [70-78]
+            StringLiteral [80-85]
+              StringLiteralComponent("Mac") [80-85]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#where_clause
+SELECT Roster.LastName, TeamMascot.Mascot
+FROM Roster INNER JOIN TeamMascot
+ON Roster.SchoolID = TeamMascot.SchoolID
+--
+QueryStatement [0-116]
+  Query [0-116]
+    Select [0-116]
+      SelectList [7-41]
+        SelectColumn [7-22]
+          PathExpression [7-22]
+            Identifier(Roster) [7-13]
+            Identifier(LastName) [14-22]
+        SelectColumn [24-41]
+          PathExpression [24-41]
+            Identifier(TeamMascot) [24-34]
+            Identifier(Mascot) [35-41]
+      FromClause [42-116]
+        Join(INNER) [47-116]
+          TablePathExpression [47-53]
+            PathExpression [47-53]
+              Identifier(Roster) [47-53]
+          Location [54-64]
+          TablePathExpression [65-75]
+            PathExpression [65-75]
+              Identifier(TeamMascot) [65-75]
+          OnClause [76-116]
+            BinaryExpression(=) [79-116]
+              PathExpression [79-94]
+                Identifier(Roster) [79-85]
+                Identifier(SchoolID) [86-94]
+              PathExpression [97-116]
+                Identifier(TeamMascot) [97-107]
+                Identifier(SchoolID) [108-116]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#where_clause
+SELECT Roster.LastName, TeamMascot.Mascot
+FROM Roster CROSS JOIN TeamMascot
+WHERE Roster.SchoolID = TeamMascot.SchoolID
+--
+QueryStatement [0-119]
+  Query [0-119]
+    Select [0-119]
+      SelectList [7-41]
+        SelectColumn [7-22]
+          PathExpression [7-22]
+            Identifier(Roster) [7-13]
+            Identifier(LastName) [14-22]
+        SelectColumn [24-41]
+          PathExpression [24-41]
+            Identifier(TeamMascot) [24-34]
+            Identifier(Mascot) [35-41]
+      FromClause [42-75]
+        Join(CROSS) [47-75]
+          TablePathExpression [47-53]
+            PathExpression [47-53]
+              Identifier(Roster) [47-53]
+          Location [54-64]
+          TablePathExpression [65-75]
+            PathExpression [65-75]
+              Identifier(TeamMascot) [65-75]
+      WhereClause [76-119]
+        BinaryExpression(=) [82-119]
+          PathExpression [82-97]
+            Identifier(Roster) [82-88]
+            Identifier(SchoolID) [89-97]
+          PathExpression [100-119]
+            Identifier(TeamMascot) [100-110]
+            Identifier(SchoolID) [111-119]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#group_by_values
+WITH PlayerStats AS (
+  SELECT 'Adams' as LastName, 'Noam' as FirstName, 3 as PointsScored UNION ALL
+  SELECT 'Buchanan', 'Jie', 0 UNION ALL
+  SELECT 'Coolidge', 'Kiran', 1 UNION ALL
+  SELECT 'Adams', 'Noam', 4 UNION ALL
+  SELECT 'Buchanan', 'Jie', 13)
+SELECT SUM(PointsScored) AS total_points, LastName
+FROM PlayerStats
+GROUP BY LastName
+--
+QueryStatement [0-338]
+  Query [0-338]
+    WithClause [0-252]
+      WithClauseEntry [5-252]
+        AliasedQuery [5-252]
+          Identifier(PlayerStats) [5-16]
+          Query [20-252]
+            SetOperation(UNION ALL) [24-251]
+              SetOperationMetadataList [91-220]
+                SetOperationMetadata [91-100]
+                  SetOperationType [91-96]
+                  SetOperationAllOrDistinct [97-100]
+                SetOperationMetadata [131-140]
+                  SetOperationType [131-136]
+                  SetOperationAllOrDistinct [137-140]
+                SetOperationMetadata [173-182]
+                  SetOperationType [173-178]
+                  SetOperationAllOrDistinct [179-182]
+                SetOperationMetadata [211-220]
+                  SetOperationType [211-216]
+                  SetOperationAllOrDistinct [217-220]
+              Select [24-90]
+                SelectList [31-90]
+                  SelectColumn [31-50]
+                    StringLiteral [31-38]
+                      StringLiteralComponent('Adams') [31-38]
+                    Alias [39-50]
+                      Identifier(LastName) [42-50]
+                  SelectColumn [52-71]
+                    StringLiteral [52-58]
+                      StringLiteralComponent('Noam') [52-58]
+                    Alias [59-71]
+                      Identifier(FirstName) [62-71]
+                  SelectColumn [73-90]
+                    IntLiteral(3) [73-74]
+                    Alias [75-90]
+                      Identifier(PointsScored) [78-90]
+              Select [103-130]
+                SelectList [110-130]
+                  SelectColumn [110-120]
+                    StringLiteral [110-120]
+                      StringLiteralComponent('Buchanan') [110-120]
+                  SelectColumn [122-127]
+                    StringLiteral [122-127]
+                      StringLiteralComponent('Jie') [122-127]
+                  SelectColumn [129-130]
+                    IntLiteral(0) [129-130]
+              Select [143-172]
+                SelectList [150-172]
+                  SelectColumn [150-160]
+                    StringLiteral [150-160]
+                      StringLiteralComponent('Coolidge') [150-160]
+                  SelectColumn [162-169]
+                    StringLiteral [162-169]
+                      StringLiteralComponent('Kiran') [162-169]
+                  SelectColumn [171-172]
+                    IntLiteral(1) [171-172]
+              Select [185-210]
+                SelectList [192-210]
+                  SelectColumn [192-199]
+                    StringLiteral [192-199]
+                      StringLiteralComponent('Adams') [192-199]
+                  SelectColumn [201-207]
+                    StringLiteral [201-207]
+                      StringLiteralComponent('Noam') [201-207]
+                  SelectColumn [209-210]
+                    IntLiteral(4) [209-210]
+              Select [223-251]
+                SelectList [230-251]
+                  SelectColumn [230-240]
+                    StringLiteral [230-240]
+                      StringLiteralComponent('Buchanan') [230-240]
+                  SelectColumn [242-247]
+                    StringLiteral [242-247]
+                      StringLiteralComponent('Jie') [242-247]
+                  SelectColumn [249-251]
+                    IntLiteral(13) [249-251]
+    Select [253-338]
+      SelectList [260-303]
+        SelectColumn [260-293]
+          FunctionCall [260-277]
+            PathExpression [260-263]
+              Identifier(SUM) [260-263]
+            PathExpression [264-276]
+              Identifier(PointsScored) [264-276]
+          Alias [278-293]
+            Identifier(total_points) [281-293]
+        SelectColumn [295-303]
+          PathExpression [295-303]
+            Identifier(LastName) [295-303]
+      FromClause [304-320]
+        TablePathExpression [309-320]
+          PathExpression [309-320]
+            Identifier(PlayerStats) [309-320]
+      GroupBy [321-338]
+        GroupingItem [330-338]
+          PathExpression [330-338]
+            Identifier(LastName) [330-338]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#group_by_values
+WITH PlayerStats AS (
+  SELECT 'Adams' as LastName, 'Noam' as FirstName, 3 as PointsScored UNION ALL
+  SELECT 'Buchanan', 'Jie', 0 UNION ALL
+  SELECT 'Coolidge', 'Kiran', 1 UNION ALL
+  SELECT 'Adams', 'Noam', 4 UNION ALL
+  SELECT 'Buchanan', 'Jie', 13)
+SELECT SUM(PointsScored) AS total_points, LastName AS last_name
+FROM PlayerStats
+GROUP BY last_name
+--
+QueryStatement [0-352]
+  Query [0-352]
+    WithClause [0-252]
+      WithClauseEntry [5-252]
+        AliasedQuery [5-252]
+          Identifier(PlayerStats) [5-16]
+          Query [20-252]
+            SetOperation(UNION ALL) [24-251]
+              SetOperationMetadataList [91-220]
+                SetOperationMetadata [91-100]
+                  SetOperationType [91-96]
+                  SetOperationAllOrDistinct [97-100]
+                SetOperationMetadata [131-140]
+                  SetOperationType [131-136]
+                  SetOperationAllOrDistinct [137-140]
+                SetOperationMetadata [173-182]
+                  SetOperationType [173-178]
+                  SetOperationAllOrDistinct [179-182]
+                SetOperationMetadata [211-220]
+                  SetOperationType [211-216]
+                  SetOperationAllOrDistinct [217-220]
+              Select [24-90]
+                SelectList [31-90]
+                  SelectColumn [31-50]
+                    StringLiteral [31-38]
+                      StringLiteralComponent('Adams') [31-38]
+                    Alias [39-50]
+                      Identifier(LastName) [42-50]
+                  SelectColumn [52-71]
+                    StringLiteral [52-58]
+                      StringLiteralComponent('Noam') [52-58]
+                    Alias [59-71]
+                      Identifier(FirstName) [62-71]
+                  SelectColumn [73-90]
+                    IntLiteral(3) [73-74]
+                    Alias [75-90]
+                      Identifier(PointsScored) [78-90]
+              Select [103-130]
+                SelectList [110-130]
+                  SelectColumn [110-120]
+                    StringLiteral [110-120]
+                      StringLiteralComponent('Buchanan') [110-120]
+                  SelectColumn [122-127]
+                    StringLiteral [122-127]
+                      StringLiteralComponent('Jie') [122-127]
+                  SelectColumn [129-130]
+                    IntLiteral(0) [129-130]
+              Select [143-172]
+                SelectList [150-172]
+                  SelectColumn [150-160]
+                    StringLiteral [150-160]
+                      StringLiteralComponent('Coolidge') [150-160]
+                  SelectColumn [162-169]
+                    StringLiteral [162-169]
+                      StringLiteralComponent('Kiran') [162-169]
+                  SelectColumn [171-172]
+                    IntLiteral(1) [171-172]
+              Select [185-210]
+                SelectList [192-210]
+                  SelectColumn [192-199]
+                    StringLiteral [192-199]
+                      StringLiteralComponent('Adams') [192-199]
+                  SelectColumn [201-207]
+                    StringLiteral [201-207]
+                      StringLiteralComponent('Noam') [201-207]
+                  SelectColumn [209-210]
+                    IntLiteral(4) [209-210]
+              Select [223-251]
+                SelectList [230-251]
+                  SelectColumn [230-240]
+                    StringLiteral [230-240]
+                      StringLiteralComponent('Buchanan') [230-240]
+                  SelectColumn [242-247]
+                    StringLiteral [242-247]
+                      StringLiteralComponent('Jie') [242-247]
+                  SelectColumn [249-251]
+                    IntLiteral(13) [249-251]
+    Select [253-352]
+      SelectList [260-316]
+        SelectColumn [260-293]
+          FunctionCall [260-277]
+            PathExpression [260-263]
+              Identifier(SUM) [260-263]
+            PathExpression [264-276]
+              Identifier(PointsScored) [264-276]
+          Alias [278-293]
+            Identifier(total_points) [281-293]
+        SelectColumn [295-316]
+          PathExpression [295-303]
+            Identifier(LastName) [295-303]
+          Alias [304-316]
+            Identifier(last_name) [307-316]
+      FromClause [317-333]
+        TablePathExpression [322-333]
+          PathExpression [322-333]
+            Identifier(PlayerStats) [322-333]
+      GroupBy [334-352]
+        GroupingItem [343-352]
+          PathExpression [343-352]
+            Identifier(last_name) [343-352]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#group_by_col_ordinals
+WITH PlayerStats AS (
+  SELECT 'Adams' as LastName, 'Noam' as FirstName, 3 as PointsScored UNION ALL
+  SELECT 'Buchanan', 'Jie', 0 UNION ALL
+  SELECT 'Coolidge', 'Kiran', 1 UNION ALL
+  SELECT 'Adams', 'Noam', 4 UNION ALL
+  SELECT 'Buchanan', 'Jie', 13)
+SELECT SUM(PointsScored) AS total_points, LastName, FirstName
+FROM PlayerStats
+GROUP BY LastName, FirstName
+--
+QueryStatement [0-360]
+  Query [0-360]
+    WithClause [0-252]
+      WithClauseEntry [5-252]
+        AliasedQuery [5-252]
+          Identifier(PlayerStats) [5-16]
+          Query [20-252]
+            SetOperation(UNION ALL) [24-251]
+              SetOperationMetadataList [91-220]
+                SetOperationMetadata [91-100]
+                  SetOperationType [91-96]
+                  SetOperationAllOrDistinct [97-100]
+                SetOperationMetadata [131-140]
+                  SetOperationType [131-136]
+                  SetOperationAllOrDistinct [137-140]
+                SetOperationMetadata [173-182]
+                  SetOperationType [173-178]
+                  SetOperationAllOrDistinct [179-182]
+                SetOperationMetadata [211-220]
+                  SetOperationType [211-216]
+                  SetOperationAllOrDistinct [217-220]
+              Select [24-90]
+                SelectList [31-90]
+                  SelectColumn [31-50]
+                    StringLiteral [31-38]
+                      StringLiteralComponent('Adams') [31-38]
+                    Alias [39-50]
+                      Identifier(LastName) [42-50]
+                  SelectColumn [52-71]
+                    StringLiteral [52-58]
+                      StringLiteralComponent('Noam') [52-58]
+                    Alias [59-71]
+                      Identifier(FirstName) [62-71]
+                  SelectColumn [73-90]
+                    IntLiteral(3) [73-74]
+                    Alias [75-90]
+                      Identifier(PointsScored) [78-90]
+              Select [103-130]
+                SelectList [110-130]
+                  SelectColumn [110-120]
+                    StringLiteral [110-120]
+                      StringLiteralComponent('Buchanan') [110-120]
+                  SelectColumn [122-127]
+                    StringLiteral [122-127]
+                      StringLiteralComponent('Jie') [122-127]
+                  SelectColumn [129-130]
+                    IntLiteral(0) [129-130]
+              Select [143-172]
+                SelectList [150-172]
+                  SelectColumn [150-160]
+                    StringLiteral [150-160]
+                      StringLiteralComponent('Coolidge') [150-160]
+                  SelectColumn [162-169]
+                    StringLiteral [162-169]
+                      StringLiteralComponent('Kiran') [162-169]
+                  SelectColumn [171-172]
+                    IntLiteral(1) [171-172]
+              Select [185-210]
+                SelectList [192-210]
+                  SelectColumn [192-199]
+                    StringLiteral [192-199]
+                      StringLiteralComponent('Adams') [192-199]
+                  SelectColumn [201-207]
+                    StringLiteral [201-207]
+                      StringLiteralComponent('Noam') [201-207]
+                  SelectColumn [209-210]
+                    IntLiteral(4) [209-210]
+              Select [223-251]
+                SelectList [230-251]
+                  SelectColumn [230-240]
+                    StringLiteral [230-240]
+                      StringLiteralComponent('Buchanan') [230-240]
+                  SelectColumn [242-247]
+                    StringLiteral [242-247]
+                      StringLiteralComponent('Jie') [242-247]
+                  SelectColumn [249-251]
+                    IntLiteral(13) [249-251]
+    Select [253-360]
+      SelectList [260-314]
+        SelectColumn [260-293]
+          FunctionCall [260-277]
+            PathExpression [260-263]
+              Identifier(SUM) [260-263]
+            PathExpression [264-276]
+              Identifier(PointsScored) [264-276]
+          Alias [278-293]
+            Identifier(total_points) [281-293]
+        SelectColumn [295-303]
+          PathExpression [295-303]
+            Identifier(LastName) [295-303]
+        SelectColumn [305-314]
+          PathExpression [305-314]
+            Identifier(FirstName) [305-314]
+      FromClause [315-331]
+        TablePathExpression [320-331]
+          PathExpression [320-331]
+            Identifier(PlayerStats) [320-331]
+      GroupBy [332-360]
+        GroupingItem [341-349]
+          PathExpression [341-349]
+            Identifier(LastName) [341-349]
+        GroupingItem [351-360]
+          PathExpression [351-360]
+            Identifier(FirstName) [351-360]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#group_by_col_ordinals
+WITH PlayerStats AS (
+  SELECT 'Adams' as LastName, 'Noam' as FirstName, 3 as PointsScored UNION ALL
+  SELECT 'Buchanan', 'Jie', 0 UNION ALL
+  SELECT 'Coolidge', 'Kiran', 1 UNION ALL
+  SELECT 'Adams', 'Noam', 4 UNION ALL
+  SELECT 'Buchanan', 'Jie', 13)
+SELECT SUM(PointsScored) AS total_points, LastName, FirstName
+FROM PlayerStats
+GROUP BY 2, 3
+--
+QueryStatement [0-345]
+  Query [0-345]
+    WithClause [0-252]
+      WithClauseEntry [5-252]
+        AliasedQuery [5-252]
+          Identifier(PlayerStats) [5-16]
+          Query [20-252]
+            SetOperation(UNION ALL) [24-251]
+              SetOperationMetadataList [91-220]
+                SetOperationMetadata [91-100]
+                  SetOperationType [91-96]
+                  SetOperationAllOrDistinct [97-100]
+                SetOperationMetadata [131-140]
+                  SetOperationType [131-136]
+                  SetOperationAllOrDistinct [137-140]
+                SetOperationMetadata [173-182]
+                  SetOperationType [173-178]
+                  SetOperationAllOrDistinct [179-182]
+                SetOperationMetadata [211-220]
+                  SetOperationType [211-216]
+                  SetOperationAllOrDistinct [217-220]
+              Select [24-90]
+                SelectList [31-90]
+                  SelectColumn [31-50]
+                    StringLiteral [31-38]
+                      StringLiteralComponent('Adams') [31-38]
+                    Alias [39-50]
+                      Identifier(LastName) [42-50]
+                  SelectColumn [52-71]
+                    StringLiteral [52-58]
+                      StringLiteralComponent('Noam') [52-58]
+                    Alias [59-71]
+                      Identifier(FirstName) [62-71]
+                  SelectColumn [73-90]
+                    IntLiteral(3) [73-74]
+                    Alias [75-90]
+                      Identifier(PointsScored) [78-90]
+              Select [103-130]
+                SelectList [110-130]
+                  SelectColumn [110-120]
+                    StringLiteral [110-120]
+                      StringLiteralComponent('Buchanan') [110-120]
+                  SelectColumn [122-127]
+                    StringLiteral [122-127]
+                      StringLiteralComponent('Jie') [122-127]
+                  SelectColumn [129-130]
+                    IntLiteral(0) [129-130]
+              Select [143-172]
+                SelectList [150-172]
+                  SelectColumn [150-160]
+                    StringLiteral [150-160]
+                      StringLiteralComponent('Coolidge') [150-160]
+                  SelectColumn [162-169]
+                    StringLiteral [162-169]
+                      StringLiteralComponent('Kiran') [162-169]
+                  SelectColumn [171-172]
+                    IntLiteral(1) [171-172]
+              Select [185-210]
+                SelectList [192-210]
+                  SelectColumn [192-199]
+                    StringLiteral [192-199]
+                      StringLiteralComponent('Adams') [192-199]
+                  SelectColumn [201-207]
+                    StringLiteral [201-207]
+                      StringLiteralComponent('Noam') [201-207]
+                  SelectColumn [209-210]
+                    IntLiteral(4) [209-210]
+              Select [223-251]
+                SelectList [230-251]
+                  SelectColumn [230-240]
+                    StringLiteral [230-240]
+                      StringLiteralComponent('Buchanan') [230-240]
+                  SelectColumn [242-247]
+                    StringLiteral [242-247]
+                      StringLiteralComponent('Jie') [242-247]
+                  SelectColumn [249-251]
+                    IntLiteral(13) [249-251]
+    Select [253-345]
+      SelectList [260-314]
+        SelectColumn [260-293]
+          FunctionCall [260-277]
+            PathExpression [260-263]
+              Identifier(SUM) [260-263]
+            PathExpression [264-276]
+              Identifier(PointsScored) [264-276]
+          Alias [278-293]
+            Identifier(total_points) [281-293]
+        SelectColumn [295-303]
+          PathExpression [295-303]
+            Identifier(LastName) [295-303]
+        SelectColumn [305-314]
+          PathExpression [305-314]
+            Identifier(FirstName) [305-314]
+      FromClause [315-331]
+        TablePathExpression [320-331]
+          PathExpression [320-331]
+            Identifier(PlayerStats) [320-331]
+      GroupBy [332-345]
+        GroupingItem [341-342]
+          IntLiteral(2) [341-342]
+        GroupingItem [344-345]
+          IntLiteral(3) [344-345]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#having_clause
+SELECT LastName
+FROM Roster
+GROUP BY LastName
+HAVING SUM(PointsScored) > 15
+--
+QueryStatement [0-75]
+  Query [0-75]
+    Select [0-75]
+      SelectList [7-15]
+        SelectColumn [7-15]
+          PathExpression [7-15]
+            Identifier(LastName) [7-15]
+      FromClause [16-27]
+        TablePathExpression [21-27]
+          PathExpression [21-27]
+            Identifier(Roster) [21-27]
+      GroupBy [28-45]
+        GroupingItem [37-45]
+          PathExpression [37-45]
+            Identifier(LastName) [37-45]
+      Having [46-75]
+        BinaryExpression(>) [53-75]
+          FunctionCall [53-70]
+            PathExpression [53-56]
+              Identifier(SUM) [53-56]
+            PathExpression [57-69]
+              Identifier(PointsScored) [57-69]
+          IntLiteral(15) [73-75]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#having_clause
+SELECT LastName, SUM(PointsScored) AS ps
+FROM Roster
+GROUP BY LastName
+HAVING ps > 0
+--
+QueryStatement [0-84]
+  Query [0-84]
+    Select [0-84]
+      SelectList [7-40]
+        SelectColumn [7-15]
+          PathExpression [7-15]
+            Identifier(LastName) [7-15]
+        SelectColumn [17-40]
+          FunctionCall [17-34]
+            PathExpression [17-20]
+              Identifier(SUM) [17-20]
+            PathExpression [21-33]
+              Identifier(PointsScored) [21-33]
+          Alias [35-40]
+            Identifier(ps) [38-40]
+      FromClause [41-52]
+        TablePathExpression [46-52]
+          PathExpression [46-52]
+            Identifier(Roster) [46-52]
+      GroupBy [53-70]
+        GroupingItem [62-70]
+          PathExpression [62-70]
+            Identifier(LastName) [62-70]
+      Having [71-84]
+        BinaryExpression(>) [78-84]
+          PathExpression [78-80]
+            Identifier(ps) [78-80]
+          IntLiteral(0) [83-84]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#aggregation_function_in_the_select_list
+SELECT LastName, SUM(PointsScored) AS total
+FROM PlayerStats
+GROUP BY LastName
+HAVING total > 15
+--
+QueryStatement [0-96]
+  Query [0-96]
+    Select [0-96]
+      SelectList [7-43]
+        SelectColumn [7-15]
+          PathExpression [7-15]
+            Identifier(LastName) [7-15]
+        SelectColumn [17-43]
+          FunctionCall [17-34]
+            PathExpression [17-20]
+              Identifier(SUM) [17-20]
+            PathExpression [21-33]
+              Identifier(PointsScored) [21-33]
+          Alias [35-43]
+            Identifier(total) [38-43]
+      FromClause [44-60]
+        TablePathExpression [49-60]
+          PathExpression [49-60]
+            Identifier(PlayerStats) [49-60]
+      GroupBy [61-78]
+        GroupingItem [70-78]
+          PathExpression [70-78]
+            Identifier(LastName) [70-78]
+      Having [79-96]
+        BinaryExpression(>) [86-96]
+          PathExpression [86-91]
+            Identifier(total) [86-91]
+          IntLiteral(15) [94-96]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#aggregation_function_in_the_having_clause
+SELECT LastName
+FROM PlayerStats
+GROUP BY LastName
+HAVING SUM(PointsScored) > 15
+--
+QueryStatement [0-80]
+  Query [0-80]
+    Select [0-80]
+      SelectList [7-15]
+        SelectColumn [7-15]
+          PathExpression [7-15]
+            Identifier(LastName) [7-15]
+      FromClause [16-32]
+        TablePathExpression [21-32]
+          PathExpression [21-32]
+            Identifier(PlayerStats) [21-32]
+      GroupBy [33-50]
+        GroupingItem [42-50]
+          PathExpression [42-50]
+            Identifier(LastName) [42-50]
+      Having [51-80]
+        BinaryExpression(>) [58-80]
+          FunctionCall [58-75]
+            PathExpression [58-61]
+              Identifier(SUM) [58-61]
+            PathExpression [62-74]
+              Identifier(PointsScored) [62-74]
+          IntLiteral(15) [78-80]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#aggregation_in_both_the_select_list_and_having_clause
+SELECT LastName, COUNT(*)
+FROM PlayerStats
+GROUP BY LastName
+HAVING SUM(PointsScored) > 15
+--
+QueryStatement [0-90]
+  Query [0-90]
+    Select [0-90]
+      SelectList [7-25]
+        SelectColumn [7-15]
+          PathExpression [7-15]
+            Identifier(LastName) [7-15]
+        SelectColumn [17-25]
+          FunctionCall [17-25]
+            PathExpression [17-22]
+              Identifier(COUNT) [17-22]
+            Star(*) [23-24]
+      FromClause [26-42]
+        TablePathExpression [31-42]
+          PathExpression [31-42]
+            Identifier(PlayerStats) [31-42]
+      GroupBy [43-60]
+        GroupingItem [52-60]
+          PathExpression [52-60]
+            Identifier(LastName) [52-60]
+      Having [61-90]
+        BinaryExpression(>) [68-90]
+          FunctionCall [68-85]
+            PathExpression [68-71]
+              Identifier(SUM) [68-71]
+            PathExpression [72-84]
+              Identifier(PointsScored) [72-84]
+          IntLiteral(15) [88-90]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#order_by_clause
+SELECT x, y
+FROM (SELECT 1 AS x, true AS y UNION ALL
+      SELECT 9, true)
+ORDER BY x
+--
+QueryStatement [0-85]
+  Query [0-85]
+    Select [0-74]
+      SelectList [7-11]
+        SelectColumn [7-8]
+          PathExpression [7-8]
+            Identifier(x) [7-8]
+        SelectColumn [10-11]
+          PathExpression [10-11]
+            Identifier(y) [10-11]
+      FromClause [12-74]
+        TableSubquery [17-74]
+          Query [18-73]
+            SetOperation(UNION ALL) [18-73]
+              SetOperationMetadataList [43-52]
+                SetOperationMetadata [43-52]
+                  SetOperationType [43-48]
+                  SetOperationAllOrDistinct [49-52]
+              Select [18-42]
+                SelectList [25-42]
+                  SelectColumn [25-31]
+                    IntLiteral(1) [25-26]
+                    Alias [27-31]
+                      Identifier(x) [30-31]
+                  SelectColumn [33-42]
+                    BooleanLiteral(true) [33-37]
+                    Alias [38-42]
+                      Identifier(y) [41-42]
+              Select [59-73]
+                SelectList [66-73]
+                  SelectColumn [66-67]
+                    IntLiteral(9) [66-67]
+                  SelectColumn [69-73]
+                    BooleanLiteral(true) [69-73]
+    OrderBy [75-85]
+      OrderingExpression(ASC) [84-85]
+        PathExpression [84-85]
+          Identifier(x) [84-85]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#order_by_clause
+SELECT x, y
+FROM (SELECT 1 AS x, true AS y UNION ALL
+      SELECT 9, true)
+ORDER BY x DESC
+--
+QueryStatement [0-90]
+  Query [0-90]
+    Select [0-74]
+      SelectList [7-11]
+        SelectColumn [7-8]
+          PathExpression [7-8]
+            Identifier(x) [7-8]
+        SelectColumn [10-11]
+          PathExpression [10-11]
+            Identifier(y) [10-11]
+      FromClause [12-74]
+        TableSubquery [17-74]
+          Query [18-73]
+            SetOperation(UNION ALL) [18-73]
+              SetOperationMetadataList [43-52]
+                SetOperationMetadata [43-52]
+                  SetOperationType [43-48]
+                  SetOperationAllOrDistinct [49-52]
+              Select [18-42]
+                SelectList [25-42]
+                  SelectColumn [25-31]
+                    IntLiteral(1) [25-26]
+                    Alias [27-31]
+                      Identifier(x) [30-31]
+                  SelectColumn [33-42]
+                    BooleanLiteral(true) [33-37]
+                    Alias [38-42]
+                      Identifier(y) [41-42]
+              Select [59-73]
+                SelectList [66-73]
+                  SelectColumn [66-67]
+                    IntLiteral(9) [66-67]
+                  SelectColumn [69-73]
+                    BooleanLiteral(true) [69-73]
+    OrderBy [75-90]
+      OrderingExpression(DESC) [84-90]
+        PathExpression [84-85]
+          Identifier(x) [84-85]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#order_by_clause
+SELECT LastName, PointsScored, OpponentID
+FROM PlayerStats
+ORDER BY SchoolID, LastName
+--
+QueryStatement [0-86]
+  Query [0-86]
+    Select [0-58]
+      SelectList [7-41]
+        SelectColumn [7-15]
+          PathExpression [7-15]
+            Identifier(LastName) [7-15]
+        SelectColumn [17-29]
+          PathExpression [17-29]
+            Identifier(PointsScored) [17-29]
+        SelectColumn [31-41]
+          PathExpression [31-41]
+            Identifier(OpponentID) [31-41]
+      FromClause [42-58]
+        TablePathExpression [47-58]
+          PathExpression [47-58]
+            Identifier(PlayerStats) [47-58]
+    OrderBy [59-86]
+      OrderingExpression(ASC) [68-76]
+        PathExpression [68-76]
+          Identifier(SchoolID) [68-76]
+      OrderingExpression(ASC) [78-86]
+        PathExpression [78-86]
+          Identifier(LastName) [78-86]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#order_by_clause
+SELECT * FROM Roster
+UNION ALL
+SELECT * FROM TeamMascot
+ORDER BY SchoolID
+--
+QueryStatement [0-73]
+  Query [0-73]
+    SetOperation(UNION ALL) [0-55]
+      SetOperationMetadataList [21-30]
+        SetOperationMetadata [21-30]
+          SetOperationType [21-26]
+          SetOperationAllOrDistinct [27-30]
+      Select [0-20]
+        SelectList [7-8]
+          SelectColumn [7-8]
+            Star(*) [7-8]
+        FromClause [9-20]
+          TablePathExpression [14-20]
+            PathExpression [14-20]
+              Identifier(Roster) [14-20]
+      Select [31-55]
+        SelectList [38-39]
+          SelectColumn [38-39]
+            Star(*) [38-39]
+        FromClause [40-55]
+          TablePathExpression [45-55]
+            PathExpression [45-55]
+              Identifier(TeamMascot) [45-55]
+    OrderBy [56-73]
+      OrderingExpression(ASC) [65-73]
+        PathExpression [65-73]
+          Identifier(SchoolID) [65-73]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#order_by_clause
+( SELECT * FROM Roster
+  UNION ALL
+  SELECT * FROM TeamMascot )
+ORDER BY SchoolID
+--
+QueryStatement [0-81]
+  Query [0-81]
+    Query [2-61]
+      SetOperation(UNION ALL) [2-61]
+        SetOperationMetadataList [25-34]
+          SetOperationMetadata [25-34]
+            SetOperationType [25-30]
+            SetOperationAllOrDistinct [31-34]
+        Select [2-22]
+          SelectList [9-10]
+            SelectColumn [9-10]
+              Star(*) [9-10]
+          FromClause [11-22]
+            TablePathExpression [16-22]
+              PathExpression [16-22]
+                Identifier(Roster) [16-22]
+        Select [37-61]
+          SelectList [44-45]
+            SelectColumn [44-45]
+              Star(*) [44-45]
+          FromClause [46-61]
+            TablePathExpression [51-61]
+              PathExpression [51-61]
+                Identifier(TeamMascot) [51-61]
+    OrderBy [64-81]
+      OrderingExpression(ASC) [73-81]
+        PathExpression [73-81]
+          Identifier(SchoolID) [73-81]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#order_by_clause
+SELECT * FROM Roster
+UNION ALL
+( SELECT * FROM TeamMascot
+  ORDER BY SchoolID )
+--
+QueryStatement [0-79]
+  Query [0-79]
+    SetOperation(UNION ALL) [0-79]
+      SetOperationMetadataList [21-30]
+        SetOperationMetadata [21-30]
+          SetOperationType [21-26]
+          SetOperationAllOrDistinct [27-30]
+      Select [0-20]
+        SelectList [7-8]
+          SelectColumn [7-8]
+            Star(*) [7-8]
+        FromClause [9-20]
+          TablePathExpression [14-20]
+            PathExpression [14-20]
+              Identifier(Roster) [14-20]
+      Query [33-77]
+        Select [33-57]
+          SelectList [40-41]
+            SelectColumn [40-41]
+              Star(*) [40-41]
+          FromClause [42-57]
+            TablePathExpression [47-57]
+              PathExpression [47-57]
+                Identifier(TeamMascot) [47-57]
+        OrderBy [60-77]
+          OrderingExpression(ASC) [69-77]
+            PathExpression [69-77]
+              Identifier(SchoolID) [69-77]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#order_by_clause
+SELECT SUM(PointsScored), LastName
+FROM PlayerStats
+GROUP BY LastName
+ORDER BY LastName
+--
+QueryStatement [0-87]
+  Query [0-87]
+    Select [0-69]
+      SelectList [7-34]
+        SelectColumn [7-24]
+          FunctionCall [7-24]
+            PathExpression [7-10]
+              Identifier(SUM) [7-10]
+            PathExpression [11-23]
+              Identifier(PointsScored) [11-23]
+        SelectColumn [26-34]
+          PathExpression [26-34]
+            Identifier(LastName) [26-34]
+      FromClause [35-51]
+        TablePathExpression [40-51]
+          PathExpression [40-51]
+            Identifier(PlayerStats) [40-51]
+      GroupBy [52-69]
+        GroupingItem [61-69]
+          PathExpression [61-69]
+            Identifier(LastName) [61-69]
+    OrderBy [70-87]
+      OrderingExpression(ASC) [79-87]
+        PathExpression [79-87]
+          Identifier(LastName) [79-87]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#order_by_clause
+SELECT SUM(PointsScored), LastName
+FROM PlayerStats
+GROUP BY 2
+ORDER BY 2
+--
+QueryStatement [0-73]
+  Query [0-73]
+    Select [0-62]
+      SelectList [7-34]
+        SelectColumn [7-24]
+          FunctionCall [7-24]
+            PathExpression [7-10]
+              Identifier(SUM) [7-10]
+            PathExpression [11-23]
+              Identifier(PointsScored) [11-23]
+        SelectColumn [26-34]
+          PathExpression [26-34]
+            Identifier(LastName) [26-34]
+      FromClause [35-51]
+        TablePathExpression [40-51]
+          PathExpression [40-51]
+            Identifier(PlayerStats) [40-51]
+      GroupBy [52-62]
+        GroupingItem [61-62]
+          IntLiteral(2) [61-62]
+    OrderBy [63-73]
+      OrderingExpression(ASC) [72-73]
+        IntLiteral(2) [72-73]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#order_by_clause
+SELECT Place
+FROM Locations
+ORDER BY Place COLLATE "en_CA"
+--
+QueryStatement [0-58]
+  Query [0-58]
+    Select [0-27]
+      SelectList [7-12]
+        SelectColumn [7-12]
+          PathExpression [7-12]
+            Identifier(Place) [7-12]
+      FromClause [13-27]
+        TablePathExpression [18-27]
+          PathExpression [18-27]
+            Identifier(Locations) [18-27]
+    OrderBy [28-58]
+      OrderingExpression(ASC) [37-58]
+        PathExpression [37-42]
+          Identifier(Place) [37-42]
+        Collate [43-58]
+          StringLiteral [51-58]
+            StringLiteralComponent("en_CA") [51-58]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#order_by_clause
+\#@collate_param = "arg_EG"
+SELECT Place
+FROM Locations
+ORDER BY Place COLLATE @collate_param
+--
+QueryStatement [27-92]
+  Query [27-92]
+    Select [27-54]
+      SelectList [34-39]
+        SelectColumn [34-39]
+          PathExpression [34-39]
+            Identifier(Place) [34-39]
+      FromClause [40-54]
+        TablePathExpression [45-54]
+          PathExpression [45-54]
+            Identifier(Locations) [45-54]
+    OrderBy [55-92]
+      OrderingExpression(ASC) [64-92]
+        PathExpression [64-69]
+          Identifier(Place) [64-69]
+        Collate [70-92]
+          ParameterExpr [78-92]
+            Identifier(collate_param) [79-92]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#order_by_clause
+SELECT APlace, BPlace, CPlace
+FROM Locations
+ORDER BY APlace COLLATE "en_US" ASC,
+         BPlace COLLATE "ar_EG" DESC,
+         CPlace COLLATE "en" DESC
+--
+QueryStatement [0-153]
+  Query [0-153]
+    Select [0-44]
+      SelectList [7-29]
+        SelectColumn [7-13]
+          PathExpression [7-13]
+            Identifier(APlace) [7-13]
+        SelectColumn [15-21]
+          PathExpression [15-21]
+            Identifier(BPlace) [15-21]
+        SelectColumn [23-29]
+          PathExpression [23-29]
+            Identifier(CPlace) [23-29]
+      FromClause [30-44]
+        TablePathExpression [35-44]
+          PathExpression [35-44]
+            Identifier(Locations) [35-44]
+    OrderBy [45-153]
+      OrderingExpression(ASC EXPLICITLY) [54-80]
+        PathExpression [54-60]
+          Identifier(APlace) [54-60]
+        Collate [61-76]
+          StringLiteral [69-76]
+            StringLiteralComponent("en_US") [69-76]
+      OrderingExpression(DESC) [91-118]
+        PathExpression [91-97]
+          Identifier(BPlace) [91-97]
+        Collate [98-113]
+          StringLiteral [106-113]
+            StringLiteralComponent("ar_EG") [106-113]
+      OrderingExpression(DESC) [129-153]
+        PathExpression [129-135]
+          Identifier(CPlace) [129-135]
+        Collate [136-148]
+          StringLiteral [144-148]
+            StringLiteralComponent("en") [144-148]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#order_by_clause
+SELECT Place
+FROM Locations
+ORDER BY Place COLLATE "en_US:ci"
+--
+QueryStatement [0-61]
+  Query [0-61]
+    Select [0-27]
+      SelectList [7-12]
+        SelectColumn [7-12]
+          PathExpression [7-12]
+            Identifier(Place) [7-12]
+      FromClause [13-27]
+        TablePathExpression [18-27]
+          PathExpression [18-27]
+            Identifier(Locations) [18-27]
+    OrderBy [28-61]
+      OrderingExpression(ASC) [37-61]
+        PathExpression [37-42]
+          Identifier(Place) [37-42]
+        Collate [43-61]
+          StringLiteral [51-61]
+            StringLiteralComponent("en_US:ci") [51-61]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#order_by_clause
+SELECT Place
+FROM Locations
+ORDER BY Place COLLATE "und:ci"
+--
+QueryStatement [0-59]
+  Query [0-59]
+    Select [0-27]
+      SelectList [7-12]
+        SelectColumn [7-12]
+          PathExpression [7-12]
+            Identifier(Place) [7-12]
+      FromClause [13-27]
+        TablePathExpression [18-27]
+          PathExpression [18-27]
+            Identifier(Locations) [18-27]
+    OrderBy [28-59]
+      OrderingExpression(ASC) [37-59]
+        PathExpression [37-42]
+          Identifier(Place) [37-42]
+        Collate [43-59]
+          StringLiteral [51-59]
+            StringLiteralComponent("und:ci") [51-59]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#union
+SELECT * FROM UNNEST(ARRAY[1, 2, 3]) AS number
+UNION ALL
+SELECT 1
+--
+QueryStatement [0-72]
+  Query [0-72]
+    SetOperation(UNION ALL) [0-72]
+      SetOperationMetadataList [54-63]
+        SetOperationMetadata [54-63]
+          SetOperationType [54-59]
+          SetOperationAllOrDistinct [60-63]
+      Select [0-53]
+        SelectList [7-8]
+          SelectColumn [7-8]
+            Star(*) [7-8]
+        FromClause [9-53]
+          TablePathExpression [14-53]
+            UnnestExpression [14-43]
+              ExpressionWithOptAlias [21-42]
+                ArrayConstructor [21-42]
+                  ArrayType [21-33]
+                    SimpleType [27-32]
+                      PathExpression [27-32]
+                        Identifier(INT64) [27-32]
+                  IntLiteral(1) [34-35]
+                  IntLiteral(2) [37-38]
+                  IntLiteral(3) [40-41]
+            Alias [44-53]
+              Identifier(number) [47-53]
+      Select [64-72]
+        SelectList [71-72]
+          SelectColumn [71-72]
+            IntLiteral(1) [71-72]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#union
+SELECT * FROM UNNEST(ARRAY[1, 2, 3]) AS number
+UNION DISTINCT
+SELECT 1
+--
+QueryStatement [0-77]
+  Query [0-77]
+    SetOperation(UNION DISTINCT) [0-77]
+      SetOperationMetadataList [54-68]
+        SetOperationMetadata [54-68]
+          SetOperationType [54-59]
+          SetOperationAllOrDistinct [60-68]
+      Select [0-53]
+        SelectList [7-8]
+          SelectColumn [7-8]
+            Star(*) [7-8]
+        FromClause [9-53]
+          TablePathExpression [14-53]
+            UnnestExpression [14-43]
+              ExpressionWithOptAlias [21-42]
+                ArrayConstructor [21-42]
+                  ArrayType [21-33]
+                    SimpleType [27-32]
+                      PathExpression [27-32]
+                        Identifier(INT64) [27-32]
+                  IntLiteral(1) [34-35]
+                  IntLiteral(2) [37-38]
+                  IntLiteral(3) [40-41]
+            Alias [44-53]
+              Identifier(number) [47-53]
+      Select [69-77]
+        SelectList [76-77]
+          SelectColumn [76-77]
+            IntLiteral(1) [76-77]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#union
+SELECT * FROM UNNEST(ARRAY[1, 2, 3]) AS number
+UNION DISTINCT
+SELECT 1
+UNION DISTINCT
+SELECT 2
+--
+QueryStatement [0-101]
+  Query [0-101]
+    SetOperation(UNION DISTINCT) [0-101]
+      SetOperationMetadataList [54-92]
+        SetOperationMetadata [54-68]
+          SetOperationType [54-59]
+          SetOperationAllOrDistinct [60-68]
+        SetOperationMetadata [78-92]
+          SetOperationType [78-83]
+          SetOperationAllOrDistinct [84-92]
+      Select [0-53]
+        SelectList [7-8]
+          SelectColumn [7-8]
+            Star(*) [7-8]
+        FromClause [9-53]
+          TablePathExpression [14-53]
+            UnnestExpression [14-43]
+              ExpressionWithOptAlias [21-42]
+                ArrayConstructor [21-42]
+                  ArrayType [21-33]
+                    SimpleType [27-32]
+                      PathExpression [27-32]
+                        Identifier(INT64) [27-32]
+                  IntLiteral(1) [34-35]
+                  IntLiteral(2) [37-38]
+                  IntLiteral(3) [40-41]
+            Alias [44-53]
+              Identifier(number) [47-53]
+      Select [69-77]
+        SelectList [76-77]
+          SelectColumn [76-77]
+            IntLiteral(1) [76-77]
+      Select [93-101]
+        SelectList [100-101]
+          SelectColumn [100-101]
+            IntLiteral(2) [100-101]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#intersect
+SELECT * FROM UNNEST(ARRAY[1, 2, 3, 3, 4]) AS number
+INTERSECT ALL
+SELECT * FROM UNNEST(ARRAY[2, 3, 3, 5]) AS number
+--
+QueryStatement [0-130]
+  Query [0-130]
+    SetOperation(INTERSECT ALL) [0-130]
+      SetOperationMetadataList [60-73]
+        SetOperationMetadata [60-73]
+          SetOperationType [60-69]
+          SetOperationAllOrDistinct [70-73]
+      Select [0-59]
+        SelectList [7-8]
+          SelectColumn [7-8]
+            Star(*) [7-8]
+        FromClause [9-59]
+          TablePathExpression [14-59]
+            UnnestExpression [14-49]
+              ExpressionWithOptAlias [21-48]
+                ArrayConstructor [21-48]
+                  ArrayType [21-33]
+                    SimpleType [27-32]
+                      PathExpression [27-32]
+                        Identifier(INT64) [27-32]
+                  IntLiteral(1) [34-35]
+                  IntLiteral(2) [37-38]
+                  IntLiteral(3) [40-41]
+                  IntLiteral(3) [43-44]
+                  IntLiteral(4) [46-47]
+            Alias [50-59]
+              Identifier(number) [53-59]
+      Select [74-130]
+        SelectList [81-82]
+          SelectColumn [81-82]
+            Star(*) [81-82]
+        FromClause [83-130]
+          TablePathExpression [88-130]
+            UnnestExpression [88-120]
+              ExpressionWithOptAlias [95-119]
+                ArrayConstructor [95-119]
+                  ArrayType [95-107]
+                    SimpleType [101-106]
+                      PathExpression [101-106]
+                        Identifier(INT64) [101-106]
+                  IntLiteral(2) [108-109]
+                  IntLiteral(3) [111-112]
+                  IntLiteral(3) [114-115]
+                  IntLiteral(5) [117-118]
+            Alias [121-130]
+              Identifier(number) [124-130]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#intersect
+SELECT * FROM UNNEST(ARRAY[1, 2, 3, 3, 4]) AS number
+INTERSECT DISTINCT
+SELECT * FROM UNNEST(ARRAY[2, 3, 3, 5]) AS number
+--
+QueryStatement [0-135]
+  Query [0-135]
+    SetOperation(INTERSECT DISTINCT) [0-135]
+      SetOperationMetadataList [60-78]
+        SetOperationMetadata [60-78]
+          SetOperationType [60-69]
+          SetOperationAllOrDistinct [70-78]
+      Select [0-59]
+        SelectList [7-8]
+          SelectColumn [7-8]
+            Star(*) [7-8]
+        FromClause [9-59]
+          TablePathExpression [14-59]
+            UnnestExpression [14-49]
+              ExpressionWithOptAlias [21-48]
+                ArrayConstructor [21-48]
+                  ArrayType [21-33]
+                    SimpleType [27-32]
+                      PathExpression [27-32]
+                        Identifier(INT64) [27-32]
+                  IntLiteral(1) [34-35]
+                  IntLiteral(2) [37-38]
+                  IntLiteral(3) [40-41]
+                  IntLiteral(3) [43-44]
+                  IntLiteral(4) [46-47]
+            Alias [50-59]
+              Identifier(number) [53-59]
+      Select [79-135]
+        SelectList [86-87]
+          SelectColumn [86-87]
+            Star(*) [86-87]
+        FromClause [88-135]
+          TablePathExpression [93-135]
+            UnnestExpression [93-125]
+              ExpressionWithOptAlias [100-124]
+                ArrayConstructor [100-124]
+                  ArrayType [100-112]
+                    SimpleType [106-111]
+                      PathExpression [106-111]
+                        Identifier(INT64) [106-111]
+                  IntLiteral(2) [113-114]
+                  IntLiteral(3) [116-117]
+                  IntLiteral(3) [119-120]
+                  IntLiteral(5) [122-123]
+            Alias [126-135]
+              Identifier(number) [129-135]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#intersect
+SELECT * FROM UNNEST(ARRAY[1, 2, 3, 3, 4]) AS number
+INTERSECT DISTINCT
+SELECT * FROM UNNEST(ARRAY[2, 3, 3, 5]) AS number
+INTERSECT DISTINCT
+SELECT * FROM UNNEST(ARRAY[3, 3, 4, 5]) AS number
+--
+QueryStatement [0-211]
+  Query [0-211]
+    SetOperation(INTERSECT DISTINCT) [0-211]
+      SetOperationMetadataList [60-154]
+        SetOperationMetadata [60-78]
+          SetOperationType [60-69]
+          SetOperationAllOrDistinct [70-78]
+        SetOperationMetadata [136-154]
+          SetOperationType [136-145]
+          SetOperationAllOrDistinct [146-154]
+      Select [0-59]
+        SelectList [7-8]
+          SelectColumn [7-8]
+            Star(*) [7-8]
+        FromClause [9-59]
+          TablePathExpression [14-59]
+            UnnestExpression [14-49]
+              ExpressionWithOptAlias [21-48]
+                ArrayConstructor [21-48]
+                  ArrayType [21-33]
+                    SimpleType [27-32]
+                      PathExpression [27-32]
+                        Identifier(INT64) [27-32]
+                  IntLiteral(1) [34-35]
+                  IntLiteral(2) [37-38]
+                  IntLiteral(3) [40-41]
+                  IntLiteral(3) [43-44]
+                  IntLiteral(4) [46-47]
+            Alias [50-59]
+              Identifier(number) [53-59]
+      Select [79-135]
+        SelectList [86-87]
+          SelectColumn [86-87]
+            Star(*) [86-87]
+        FromClause [88-135]
+          TablePathExpression [93-135]
+            UnnestExpression [93-125]
+              ExpressionWithOptAlias [100-124]
+                ArrayConstructor [100-124]
+                  ArrayType [100-112]
+                    SimpleType [106-111]
+                      PathExpression [106-111]
+                        Identifier(INT64) [106-111]
+                  IntLiteral(2) [113-114]
+                  IntLiteral(3) [116-117]
+                  IntLiteral(3) [119-120]
+                  IntLiteral(5) [122-123]
+            Alias [126-135]
+              Identifier(number) [129-135]
+      Select [155-211]
+        SelectList [162-163]
+          SelectColumn [162-163]
+            Star(*) [162-163]
+        FromClause [164-211]
+          TablePathExpression [169-211]
+            UnnestExpression [169-201]
+              ExpressionWithOptAlias [176-200]
+                ArrayConstructor [176-200]
+                  ArrayType [176-188]
+                    SimpleType [182-187]
+                      PathExpression [182-187]
+                        Identifier(INT64) [182-187]
+                  IntLiteral(3) [189-190]
+                  IntLiteral(3) [192-193]
+                  IntLiteral(4) [195-196]
+                  IntLiteral(5) [198-199]
+            Alias [202-211]
+              Identifier(number) [205-211]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#except
+SELECT * FROM UNNEST(ARRAY[1, 2, 3, 3, 4]) AS number
+EXCEPT ALL
+SELECT * FROM UNNEST(ARRAY[1, 2]) AS number
+--
+QueryStatement [0-121]
+  Query [0-121]
+    SetOperation(EXCEPT ALL) [0-121]
+      SetOperationMetadataList [60-70]
+        SetOperationMetadata [60-70]
+          SetOperationType [60-66]
+          SetOperationAllOrDistinct [67-70]
+      Select [0-59]
+        SelectList [7-8]
+          SelectColumn [7-8]
+            Star(*) [7-8]
+        FromClause [9-59]
+          TablePathExpression [14-59]
+            UnnestExpression [14-49]
+              ExpressionWithOptAlias [21-48]
+                ArrayConstructor [21-48]
+                  ArrayType [21-33]
+                    SimpleType [27-32]
+                      PathExpression [27-32]
+                        Identifier(INT64) [27-32]
+                  IntLiteral(1) [34-35]
+                  IntLiteral(2) [37-38]
+                  IntLiteral(3) [40-41]
+                  IntLiteral(3) [43-44]
+                  IntLiteral(4) [46-47]
+            Alias [50-59]
+              Identifier(number) [53-59]
+      Select [71-121]
+        SelectList [78-79]
+          SelectColumn [78-79]
+            Star(*) [78-79]
+        FromClause [80-121]
+          TablePathExpression [85-121]
+            UnnestExpression [85-111]
+              ExpressionWithOptAlias [92-110]
+                ArrayConstructor [92-110]
+                  ArrayType [92-104]
+                    SimpleType [98-103]
+                      PathExpression [98-103]
+                        Identifier(INT64) [98-103]
+                  IntLiteral(1) [105-106]
+                  IntLiteral(2) [108-109]
+            Alias [112-121]
+              Identifier(number) [115-121]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#except
+SELECT * FROM UNNEST(ARRAY[1, 2, 3, 3, 4]) AS number
+EXCEPT DISTINCT
+SELECT * FROM UNNEST(ARRAY[1, 2]) AS number
+--
+QueryStatement [0-126]
+  Query [0-126]
+    SetOperation(EXCEPT DISTINCT) [0-126]
+      SetOperationMetadataList [60-75]
+        SetOperationMetadata [60-75]
+          SetOperationType [60-66]
+          SetOperationAllOrDistinct [67-75]
+      Select [0-59]
+        SelectList [7-8]
+          SelectColumn [7-8]
+            Star(*) [7-8]
+        FromClause [9-59]
+          TablePathExpression [14-59]
+            UnnestExpression [14-49]
+              ExpressionWithOptAlias [21-48]
+                ArrayConstructor [21-48]
+                  ArrayType [21-33]
+                    SimpleType [27-32]
+                      PathExpression [27-32]
+                        Identifier(INT64) [27-32]
+                  IntLiteral(1) [34-35]
+                  IntLiteral(2) [37-38]
+                  IntLiteral(3) [40-41]
+                  IntLiteral(3) [43-44]
+                  IntLiteral(4) [46-47]
+            Alias [50-59]
+              Identifier(number) [53-59]
+      Select [76-126]
+        SelectList [83-84]
+          SelectColumn [83-84]
+            Star(*) [83-84]
+        FromClause [85-126]
+          TablePathExpression [90-126]
+            UnnestExpression [90-116]
+              ExpressionWithOptAlias [97-115]
+                ArrayConstructor [97-115]
+                  ArrayType [97-109]
+                    SimpleType [103-108]
+                      PathExpression [103-108]
+                        Identifier(INT64) [103-108]
+                  IntLiteral(1) [110-111]
+                  IntLiteral(2) [113-114]
+            Alias [117-126]
+              Identifier(number) [120-126]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#except
+SELECT * FROM UNNEST(ARRAY[1, 2, 3, 3, 4]) AS number
+EXCEPT DISTINCT
+SELECT * FROM UNNEST(ARRAY[1, 2]) AS number
+EXCEPT DISTINCT
+SELECT * FROM UNNEST(ARRAY[1, 4]) AS number
+--
+QueryStatement [0-193]
+  Query [0-193]
+    SetOperation(EXCEPT DISTINCT) [0-193]
+      SetOperationMetadataList [60-142]
+        SetOperationMetadata [60-75]
+          SetOperationType [60-66]
+          SetOperationAllOrDistinct [67-75]
+        SetOperationMetadata [127-142]
+          SetOperationType [127-133]
+          SetOperationAllOrDistinct [134-142]
+      Select [0-59]
+        SelectList [7-8]
+          SelectColumn [7-8]
+            Star(*) [7-8]
+        FromClause [9-59]
+          TablePathExpression [14-59]
+            UnnestExpression [14-49]
+              ExpressionWithOptAlias [21-48]
+                ArrayConstructor [21-48]
+                  ArrayType [21-33]
+                    SimpleType [27-32]
+                      PathExpression [27-32]
+                        Identifier(INT64) [27-32]
+                  IntLiteral(1) [34-35]
+                  IntLiteral(2) [37-38]
+                  IntLiteral(3) [40-41]
+                  IntLiteral(3) [43-44]
+                  IntLiteral(4) [46-47]
+            Alias [50-59]
+              Identifier(number) [53-59]
+      Select [76-126]
+        SelectList [83-84]
+          SelectColumn [83-84]
+            Star(*) [83-84]
+        FromClause [85-126]
+          TablePathExpression [90-126]
+            UnnestExpression [90-116]
+              ExpressionWithOptAlias [97-115]
+                ArrayConstructor [97-115]
+                  ArrayType [97-109]
+                    SimpleType [103-108]
+                      PathExpression [103-108]
+                        Identifier(INT64) [103-108]
+                  IntLiteral(1) [110-111]
+                  IntLiteral(2) [113-114]
+            Alias [117-126]
+              Identifier(number) [120-126]
+      Select [143-193]
+        SelectList [150-151]
+          SelectColumn [150-151]
+            Star(*) [150-151]
+        FromClause [152-193]
+          TablePathExpression [157-193]
+            UnnestExpression [157-183]
+              ExpressionWithOptAlias [164-182]
+                ArrayConstructor [164-182]
+                  ArrayType [164-176]
+                    SimpleType [170-175]
+                      PathExpression [170-175]
+                        Identifier(INT64) [170-175]
+                  IntLiteral(1) [177-178]
+                  IntLiteral(4) [180-181]
+            Alias [184-193]
+              Identifier(number) [187-193]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#except
+SELECT * FROM UNNEST(ARRAY[1, 2, 3, 3, 4]) AS number
+EXCEPT DISTINCT
+(
+  SELECT * FROM UNNEST(ARRAY[1, 2]) AS number
+  EXCEPT DISTINCT
+  SELECT * FROM UNNEST(ARRAY[1, 4]) AS number
+)
+--
+QueryStatement [0-203]
+  Query [0-203]
+    SetOperation(EXCEPT DISTINCT) [0-203]
+      SetOperationMetadataList [60-75]
+        SetOperationMetadata [60-75]
+          SetOperationType [60-66]
+          SetOperationAllOrDistinct [67-75]
+      Select [0-59]
+        SelectList [7-8]
+          SelectColumn [7-8]
+            Star(*) [7-8]
+        FromClause [9-59]
+          TablePathExpression [14-59]
+            UnnestExpression [14-49]
+              ExpressionWithOptAlias [21-48]
+                ArrayConstructor [21-48]
+                  ArrayType [21-33]
+                    SimpleType [27-32]
+                      PathExpression [27-32]
+                        Identifier(INT64) [27-32]
+                  IntLiteral(1) [34-35]
+                  IntLiteral(2) [37-38]
+                  IntLiteral(3) [40-41]
+                  IntLiteral(3) [43-44]
+                  IntLiteral(4) [46-47]
+            Alias [50-59]
+              Identifier(number) [53-59]
+      Query [80-201]
+        SetOperation(EXCEPT DISTINCT) [80-201]
+          SetOperationMetadataList [133-148]
+            SetOperationMetadata [133-148]
+              SetOperationType [133-139]
+              SetOperationAllOrDistinct [140-148]
+          Select [80-130]
+            SelectList [87-88]
+              SelectColumn [87-88]
+                Star(*) [87-88]
+            FromClause [89-130]
+              TablePathExpression [94-130]
+                UnnestExpression [94-120]
+                  ExpressionWithOptAlias [101-119]
+                    ArrayConstructor [101-119]
+                      ArrayType [101-113]
+                        SimpleType [107-112]
+                          PathExpression [107-112]
+                            Identifier(INT64) [107-112]
+                      IntLiteral(1) [114-115]
+                      IntLiteral(2) [117-118]
+                Alias [121-130]
+                  Identifier(number) [124-130]
+          Select [151-201]
+            SelectList [158-159]
+              SelectColumn [158-159]
+                Star(*) [158-159]
+            FromClause [160-201]
+              TablePathExpression [165-201]
+                UnnestExpression [165-191]
+                  ExpressionWithOptAlias [172-190]
+                    ArrayConstructor [172-190]
+                      ArrayType [172-184]
+                        SimpleType [178-183]
+                          PathExpression [178-183]
+                            Identifier(INT64) [178-183]
+                      IntLiteral(1) [185-186]
+                      IntLiteral(4) [188-189]
+                Alias [192-201]
+                  Identifier(number) [195-201]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#limit_and_offset_clause
+SELECT *
+FROM UNNEST(ARRAY['a', 'b', 'c', 'd', 'e']) AS letter
+ORDER BY letter ASC LIMIT 2
+--
+QueryStatement [0-98]
+  Query [0-98]
+    Select [0-70]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-70]
+        TablePathExpression [14-70]
+          UnnestExpression [14-60]
+            ExpressionWithOptAlias [21-59]
+              ArrayConstructor [21-59]
+                ArrayType [21-34]
+                  SimpleType [27-33]
+                    PathExpression [27-33]
+                      Identifier(STRING) [27-33]
+                StringLiteral [35-38]
+                  StringLiteralComponent('a') [35-38]
+                StringLiteral [40-43]
+                  StringLiteralComponent('b') [40-43]
+                StringLiteral [45-48]
+                  StringLiteralComponent('c') [45-48]
+                StringLiteral [50-53]
+                  StringLiteralComponent('d') [50-53]
+                StringLiteral [55-58]
+                  StringLiteralComponent('e') [55-58]
+          Alias [61-70]
+            Identifier(letter) [64-70]
+    OrderBy [71-90]
+      OrderingExpression(ASC EXPLICITLY) [80-90]
+        PathExpression [80-86]
+          Identifier(letter) [80-86]
+    LimitOffset [91-98]
+      Limit [91-98]
+        IntLiteral(2) [97-98]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#limit_and_offset_clause
+SELECT *
+FROM UNNEST(ARRAY['a', 'b', 'c', 'd', 'e']) AS letter
+ORDER BY letter ASC LIMIT 3 OFFSET 1
+--
+QueryStatement [0-107]
+  Query [0-107]
+    Select [0-70]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-70]
+        TablePathExpression [14-70]
+          UnnestExpression [14-60]
+            ExpressionWithOptAlias [21-59]
+              ArrayConstructor [21-59]
+                ArrayType [21-34]
+                  SimpleType [27-33]
+                    PathExpression [27-33]
+                      Identifier(STRING) [27-33]
+                StringLiteral [35-38]
+                  StringLiteralComponent('a') [35-38]
+                StringLiteral [40-43]
+                  StringLiteralComponent('b') [40-43]
+                StringLiteral [45-48]
+                  StringLiteralComponent('c') [45-48]
+                StringLiteral [50-53]
+                  StringLiteralComponent('d') [50-53]
+                StringLiteral [55-58]
+                  StringLiteralComponent('e') [55-58]
+          Alias [61-70]
+            Identifier(letter) [64-70]
+    OrderBy [71-90]
+      OrderingExpression(ASC EXPLICITLY) [80-90]
+        PathExpression [80-86]
+          Identifier(letter) [80-86]
+    LimitOffset [91-107]
+      Limit [91-98]
+        IntLiteral(3) [97-98]
+      IntLiteral(1) [106-107]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#for_update_clause
+SELECT expression
+FOR UPDATE
+--
+QueryStatement [0-28]
+  Query [0-28]
+    Select [0-17]
+      SelectList [7-17]
+        SelectColumn [7-17]
+          PathExpression [7-17]
+            Identifier(expression) [7-17]
+    LockMode [18-28]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#for_update_clause
+SELECT MarketingBudget
+FROM Albums
+WHERE SingerId = 1 and AlbumId = 1
+FOR UPDATE
+--
+QueryStatement [0-80]
+  Query [0-80]
+    Select [0-69]
+      SelectList [7-22]
+        SelectColumn [7-22]
+          PathExpression [7-22]
+            Identifier(MarketingBudget) [7-22]
+      FromClause [23-34]
+        TablePathExpression [28-34]
+          PathExpression [28-34]
+            Identifier(Albums) [28-34]
+      WhereClause [35-69]
+        AndExpr [41-69]
+          BinaryExpression(=) [41-53]
+            PathExpression [41-49]
+              Identifier(SingerId) [41-49]
+            IntLiteral(1) [52-53]
+          BinaryExpression(=) [58-69]
+            PathExpression [58-65]
+              Identifier(AlbumId) [58-65]
+            IntLiteral(1) [68-69]
+    LockMode [70-80]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#for_update_clause
+UPDATE Albums
+SET MarketingBudget = 100000
+WHERE SingerId = 1 and AlbumId = 1
+--
+UpdateStatement [0-77]
+  PathExpression [7-13]
+    Identifier(Albums) [7-13]
+  UpdateItemList [18-42]
+    UpdateItem [18-42]
+      UpdateSetValue [18-42]
+        PathExpression [18-33]
+          Identifier(MarketingBudget) [18-33]
+        IntLiteral(100000) [36-42]
+  AndExpr [49-77]
+    BinaryExpression(=) [49-61]
+      PathExpression [49-57]
+        Identifier(SingerId) [49-57]
+      IntLiteral(1) [60-61]
+    BinaryExpression(=) [66-77]
+      PathExpression [66-73]
+        Identifier(AlbumId) [66-73]
+      IntLiteral(1) [76-77]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#examples
+WITH subQ1 AS (SELECT SchoolID FROM Roster),
+     subQ2 AS (SELECT OpponentID FROM PlayerStats)
+SELECT * FROM subQ1
+UNION ALL
+SELECT * FROM subQ2
+--
+QueryStatement [0-145]
+  Query [0-145]
+    WithClause [0-95]
+      WithClauseEntry [5-43]
+        AliasedQuery [5-43]
+          Identifier(subQ1) [5-10]
+          Query [14-43]
+            Select [15-42]
+              SelectList [22-30]
+                SelectColumn [22-30]
+                  PathExpression [22-30]
+                    Identifier(SchoolID) [22-30]
+              FromClause [31-42]
+                TablePathExpression [36-42]
+                  PathExpression [36-42]
+                    Identifier(Roster) [36-42]
+      WithClauseEntry [50-95]
+        AliasedQuery [50-95]
+          Identifier(subQ2) [50-55]
+          Query [59-95]
+            Select [60-94]
+              SelectList [67-77]
+                SelectColumn [67-77]
+                  PathExpression [67-77]
+                    Identifier(OpponentID) [67-77]
+              FromClause [78-94]
+                TablePathExpression [83-94]
+                  PathExpression [83-94]
+                    Identifier(PlayerStats) [83-94]
+    SetOperation(UNION ALL) [96-145]
+      SetOperationMetadataList [116-125]
+        SetOperationMetadata [116-125]
+          SetOperationType [116-121]
+          SetOperationAllOrDistinct [122-125]
+      Select [96-115]
+        SelectList [103-104]
+          SelectColumn [103-104]
+            Star(*) [103-104]
+        FromClause [105-115]
+          TablePathExpression [110-115]
+            PathExpression [110-115]
+              Identifier(subQ1) [110-115]
+      Select [126-145]
+        SelectList [133-134]
+          SelectColumn [133-134]
+            Star(*) [133-134]
+        FromClause [135-145]
+          TablePathExpression [140-145]
+            PathExpression [140-145]
+              Identifier(subQ2) [140-145]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#examples
+SELECT account
+FROM (
+  WITH result AS (SELECT * FROM NPCs)
+  SELECT *
+  FROM result)
+--
+QueryStatement [0-85]
+  Query [0-85]
+    Select [0-85]
+      SelectList [7-14]
+        SelectColumn [7-14]
+          PathExpression [7-14]
+            Identifier(account) [7-14]
+      FromClause [15-85]
+        TableSubquery [20-85]
+          Query [24-84]
+            WithClause [24-59]
+              WithClauseEntry [29-59]
+                AliasedQuery [29-59]
+                  Identifier(result) [29-35]
+                  Query [39-59]
+                    Select [40-58]
+                      SelectList [47-48]
+                        SelectColumn [47-48]
+                          Star(*) [47-48]
+                      FromClause [49-58]
+                        TablePathExpression [54-58]
+                          PathExpression [54-58]
+                            Identifier(NPCs) [54-58]
+            Select [62-84]
+              SelectList [69-70]
+                SelectColumn [69-70]
+                  Star(*) [69-70]
+              FromClause [73-84]
+                TablePathExpression [78-84]
+                  PathExpression [78-84]
+                    Identifier(result) [78-84]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#examples
+WITH t1 AS (SELECT col1, col2 FROM t FOR UPDATE)
+SELECT * FROM t1
+--
+QueryStatement [0-65]
+  Query [0-65]
+    WithClause [0-48]
+      WithClauseEntry [5-48]
+        AliasedQuery [5-48]
+          Identifier(t1) [5-7]
+          Query [11-48]
+            Select [12-36]
+              SelectList [19-29]
+                SelectColumn [19-23]
+                  PathExpression [19-23]
+                    Identifier(col1) [19-23]
+                SelectColumn [25-29]
+                  PathExpression [25-29]
+                    Identifier(col2) [25-29]
+              FromClause [30-36]
+                TablePathExpression [35-36]
+                  PathExpression [35-36]
+                    Identifier(t) [35-36]
+            LockMode [37-47]
+    Select [49-65]
+      SelectList [56-57]
+        SelectColumn [56-57]
+          Star(*) [56-57]
+      FromClause [58-65]
+        TablePathExpression [63-65]
+          PathExpression [63-65]
+            Identifier(t1) [63-65]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#examples
+WITH t2 AS (SELECT col1, col2 FROM t)
+SELECT * FROM t2 FOR UPDATE
+--
+QueryStatement [0-65]
+  Query [0-65]
+    WithClause [0-37]
+      WithClauseEntry [5-37]
+        AliasedQuery [5-37]
+          Identifier(t2) [5-7]
+          Query [11-37]
+            Select [12-36]
+              SelectList [19-29]
+                SelectColumn [19-23]
+                  PathExpression [19-23]
+                    Identifier(col1) [19-23]
+                SelectColumn [25-29]
+                  PathExpression [25-29]
+                    Identifier(col2) [25-29]
+              FromClause [30-36]
+                TablePathExpression [35-36]
+                  PathExpression [35-36]
+                    Identifier(t) [35-36]
+    Select [38-54]
+      SelectList [45-46]
+        SelectColumn [45-46]
+          Star(*) [45-46]
+      FromClause [47-54]
+        TablePathExpression [52-54]
+          PathExpression [52-54]
+            Identifier(t2) [52-54]
+    LockMode [55-65]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#cte_visibility
+WITH
+  A AS (SELECT 1 AS n UNION ALL (SELECT n + 1 FROM A WHERE n < 3))
+SELECT * FROM A
+
+-- Error
+--
+QueryStatement [0-87]
+  Query [0-87]
+    WithClause [0-71]
+      WithClauseEntry [7-71]
+        AliasedQuery [7-71]
+          Identifier(A) [7-8]
+          Query [12-71]
+            SetOperation(UNION ALL) [13-70]
+              SetOperationMetadataList [27-36]
+                SetOperationMetadata [27-36]
+                  SetOperationType [27-32]
+                  SetOperationAllOrDistinct [33-36]
+              Select [13-26]
+                SelectList [20-26]
+                  SelectColumn [20-26]
+                    IntLiteral(1) [20-21]
+                    Alias [22-26]
+                      Identifier(n) [25-26]
+              Query [38-69]
+                Select [38-69]
+                  SelectList [45-50]
+                    SelectColumn [45-50]
+                      BinaryExpression(+) [45-50]
+                        PathExpression [45-46]
+                          Identifier(n) [45-46]
+                        IntLiteral(1) [49-50]
+                  FromClause [51-57]
+                    TablePathExpression [56-57]
+                      PathExpression [56-57]
+                        Identifier(A) [56-57]
+                  WhereClause [58-69]
+                    BinaryExpression(<) [64-69]
+                      PathExpression [64-65]
+                        Identifier(n) [64-65]
+                      IntLiteral(3) [68-69]
+    Select [72-87]
+      SelectList [79-80]
+        SelectColumn [79-80]
+          Star(*) [79-80]
+      FromClause [81-87]
+        TablePathExpression [86-87]
+          PathExpression [86-87]
+            Identifier(A) [86-87]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#cte_visibility
+WITH
+  A AS (SELECT * FROM B),
+  B AS (SELECT 1 AS n)
+SELECT * FROM B
+
+-- Error
+--
+QueryStatement [0-69]
+  Query [0-69]
+    WithClause [0-53]
+      WithClauseEntry [7-29]
+        AliasedQuery [7-29]
+          Identifier(A) [7-8]
+          Query [12-29]
+            Select [13-28]
+              SelectList [20-21]
+                SelectColumn [20-21]
+                  Star(*) [20-21]
+              FromClause [22-28]
+                TablePathExpression [27-28]
+                  PathExpression [27-28]
+                    Identifier(B) [27-28]
+      WithClauseEntry [33-53]
+        AliasedQuery [33-53]
+          Identifier(B) [33-34]
+          Query [38-53]
+            Select [39-52]
+              SelectList [46-52]
+                SelectColumn [46-52]
+                  IntLiteral(1) [46-47]
+                  Alias [48-52]
+                    Identifier(n) [51-52]
+    Select [54-69]
+      SelectList [61-62]
+        SelectColumn [61-62]
+          Star(*) [61-62]
+      FromClause [63-69]
+        TablePathExpression [68-69]
+          PathExpression [68-69]
+            Identifier(B) [68-69]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#cte_visibility
+WITH
+  A AS (SELECT 1 AS n),
+  B AS (SELECT * FROM A)
+SELECT * FROM B
+--
+QueryStatement [0-69]
+  Query [0-69]
+    WithClause [0-53]
+      WithClauseEntry [7-27]
+        AliasedQuery [7-27]
+          Identifier(A) [7-8]
+          Query [12-27]
+            Select [13-26]
+              SelectList [20-26]
+                SelectColumn [20-26]
+                  IntLiteral(1) [20-21]
+                  Alias [22-26]
+                    Identifier(n) [25-26]
+      WithClauseEntry [31-53]
+        AliasedQuery [31-53]
+          Identifier(B) [31-32]
+          Query [36-53]
+            Select [37-52]
+              SelectList [44-45]
+                SelectColumn [44-45]
+                  Star(*) [44-45]
+              FromClause [46-52]
+                TablePathExpression [51-52]
+                  PathExpression [51-52]
+                    Identifier(A) [51-52]
+    Select [54-69]
+      SelectList [61-62]
+        SelectColumn [61-62]
+          Star(*) [61-62]
+      FromClause [63-69]
+        TablePathExpression [68-69]
+          PathExpression [68-69]
+            Identifier(B) [68-69]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#cte_visibility
+WITH
+  A AS (SELECT * FROM B),
+  B AS (SELECT * FROM A)
+SELECT * FROM B
+
+-- Error
+--
+QueryStatement [0-71]
+  Query [0-71]
+    WithClause [0-55]
+      WithClauseEntry [7-29]
+        AliasedQuery [7-29]
+          Identifier(A) [7-8]
+          Query [12-29]
+            Select [13-28]
+              SelectList [20-21]
+                SelectColumn [20-21]
+                  Star(*) [20-21]
+              FromClause [22-28]
+                TablePathExpression [27-28]
+                  PathExpression [27-28]
+                    Identifier(B) [27-28]
+      WithClauseEntry [33-55]
+        AliasedQuery [33-55]
+          Identifier(B) [33-34]
+          Query [38-55]
+            Select [39-54]
+              SelectList [46-47]
+                SelectColumn [46-47]
+                  Star(*) [46-47]
+              FromClause [48-54]
+                TablePathExpression [53-54]
+                  PathExpression [53-54]
+                    Identifier(A) [53-54]
+    Select [56-71]
+      SelectList [63-64]
+        SelectColumn [63-64]
+          Star(*) [63-64]
+      FromClause [65-71]
+        TablePathExpression [70-71]
+          PathExpression [70-71]
+            Identifier(B) [70-71]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#explicit_alias_syntax
+SELECT s.FirstName, s2.SongName
+FROM Singers AS s, (SELECT * FROM Songs) AS s2
+--
+QueryStatement [0-78]
+  Query [0-78]
+    Select [0-78]
+      SelectList [7-31]
+        SelectColumn [7-18]
+          PathExpression [7-18]
+            Identifier(s) [7-8]
+            Identifier(FirstName) [9-18]
+        SelectColumn [20-31]
+          PathExpression [20-31]
+            Identifier(s2) [20-22]
+            Identifier(SongName) [23-31]
+      FromClause [32-78]
+        Join(COMMA) [37-78]
+          TablePathExpression [37-49]
+            PathExpression [37-44]
+              Identifier(Singers) [37-44]
+            Alias [45-49]
+              Identifier(s) [48-49]
+          Location [49-50]
+          TableSubquery [51-78]
+            Query [52-71]
+              Select [52-71]
+                SelectList [59-60]
+                  SelectColumn [59-60]
+                    Star(*) [59-60]
+                FromClause [61-71]
+                  TablePathExpression [66-71]
+                    PathExpression [66-71]
+                      Identifier(Songs) [66-71]
+            Alias [73-78]
+              Identifier(s2) [76-78]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#explicit_alias_syntax
+SELECT s.FirstName AS name, LOWER(s.FirstName) AS lname
+FROM Singers s
+--
+QueryStatement [0-70]
+  Query [0-70]
+    Select [0-70]
+      SelectList [7-55]
+        SelectColumn [7-26]
+          PathExpression [7-18]
+            Identifier(s) [7-8]
+            Identifier(FirstName) [9-18]
+          Alias [19-26]
+            Identifier(name) [22-26]
+        SelectColumn [28-55]
+          FunctionCall [28-46]
+            PathExpression [28-33]
+              Identifier(LOWER) [28-33]
+            PathExpression [34-45]
+              Identifier(s) [34-35]
+              Identifier(FirstName) [36-45]
+          Alias [47-55]
+            Identifier(lname) [50-55]
+      FromClause [56-70]
+        TablePathExpression [61-70]
+          PathExpression [61-68]
+            Identifier(Singers) [61-68]
+          Alias [69-70]
+            Identifier(s) [69-70]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#from_clause_aliases
+SELECT FirstName
+FROM Singers AS s, s.Concerts
+--
+QueryStatement [0-46]
+  Query [0-46]
+    Select [0-46]
+      SelectList [7-16]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(FirstName) [7-16]
+      FromClause [17-46]
+        Join(COMMA) [22-46]
+          TablePathExpression [22-34]
+            PathExpression [22-29]
+              Identifier(Singers) [22-29]
+            Alias [30-34]
+              Identifier(s) [33-34]
+          Location [34-35]
+          TablePathExpression [36-46]
+            PathExpression [36-46]
+              Identifier(s) [36-37]
+              Identifier(Concerts) [38-46]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#from_clause_aliases
+SELECT FirstName
+FROM s.Concerts, Singers AS s
+--
+QueryStatement [0-46]
+  Query [0-46]
+    Select [0-46]
+      SelectList [7-16]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(FirstName) [7-16]
+      FromClause [17-46]
+        Join(COMMA) [22-46]
+          TablePathExpression [22-32]
+            PathExpression [22-32]
+              Identifier(s) [22-23]
+              Identifier(Concerts) [24-32]
+          Location [32-33]
+          TablePathExpression [34-46]
+            PathExpression [34-41]
+              Identifier(Singers) [34-41]
+            Alias [42-46]
+              Identifier(s) [45-46]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#from_clause_aliases
+SELECT FirstName, s.ReleaseDate
+FROM Singers s WHERE ReleaseDate = 1975
+--
+QueryStatement [0-71]
+  Query [0-71]
+    Select [0-71]
+      SelectList [7-31]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(FirstName) [7-16]
+        SelectColumn [18-31]
+          PathExpression [18-31]
+            Identifier(s) [18-19]
+            Identifier(ReleaseDate) [20-31]
+      FromClause [32-46]
+        TablePathExpression [37-46]
+          PathExpression [37-44]
+            Identifier(Singers) [37-44]
+          Alias [45-46]
+            Identifier(s) [45-46]
+      WhereClause [47-71]
+        BinaryExpression(=) [53-71]
+          PathExpression [53-64]
+            Identifier(ReleaseDate) [53-64]
+          IntLiteral(1975) [67-71]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#from_clause_aliases
+SELECT * FROM Singers as s, Songs as s2
+ORDER BY s.LastName
+--
+QueryStatement [0-59]
+  Query [0-59]
+    Select [0-39]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-39]
+        Join(COMMA) [14-39]
+          TablePathExpression [14-26]
+            PathExpression [14-21]
+              Identifier(Singers) [14-21]
+            Alias [22-26]
+              Identifier(s) [25-26]
+          Location [26-27]
+          TablePathExpression [28-39]
+            PathExpression [28-33]
+              Identifier(Songs) [28-33]
+            Alias [34-39]
+              Identifier(s2) [37-39]
+    OrderBy [40-59]
+      OrderingExpression(ASC) [49-59]
+        PathExpression [49-59]
+          Identifier(s) [49-50]
+          Identifier(LastName) [51-59]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#from_clause_aliases
+SELECT * FROM Singers as s, Songs as s2
+ORDER BY Singers.LastName
+--
+QueryStatement [0-65]
+  Query [0-65]
+    Select [0-39]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-39]
+        Join(COMMA) [14-39]
+          TablePathExpression [14-26]
+            PathExpression [14-21]
+              Identifier(Singers) [14-21]
+            Alias [22-26]
+              Identifier(s) [25-26]
+          Location [26-27]
+          TablePathExpression [28-39]
+            PathExpression [28-33]
+              Identifier(Songs) [28-33]
+            Alias [34-39]
+              Identifier(s2) [37-39]
+    OrderBy [40-65]
+      OrderingExpression(ASC) [49-65]
+        PathExpression [49-65]
+          Identifier(Singers) [49-56]
+          Identifier(LastName) [57-65]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#select-list_aliases
+SELECT LastName AS last, SingerID
+FROM Singers
+ORDER BY last
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-46]
+      SelectList [7-33]
+        SelectColumn [7-23]
+          PathExpression [7-15]
+            Identifier(LastName) [7-15]
+          Alias [16-23]
+            Identifier(last) [19-23]
+        SelectColumn [25-33]
+          PathExpression [25-33]
+            Identifier(SingerID) [25-33]
+      FromClause [34-46]
+        TablePathExpression [39-46]
+          PathExpression [39-46]
+            Identifier(Singers) [39-46]
+    OrderBy [47-60]
+      OrderingExpression(ASC) [56-60]
+        PathExpression [56-60]
+          Identifier(last) [56-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#select-list_aliases
+SELECT LastName AS last, SingerID
+FROM Singers
+WHERE last = "Smith"
+--
+QueryStatement [0-67]
+  Query [0-67]
+    Select [0-67]
+      SelectList [7-33]
+        SelectColumn [7-23]
+          PathExpression [7-15]
+            Identifier(LastName) [7-15]
+          Alias [16-23]
+            Identifier(last) [19-23]
+        SelectColumn [25-33]
+          PathExpression [25-33]
+            Identifier(SingerID) [25-33]
+      FromClause [34-46]
+        TablePathExpression [39-46]
+          PathExpression [39-46]
+            Identifier(Singers) [39-46]
+      WhereClause [47-67]
+        BinaryExpression(=) [53-67]
+          PathExpression [53-57]
+            Identifier(last) [53-57]
+          StringLiteral [60-67]
+            StringLiteralComponent("Smith") [60-67]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#aliases_clauses
+SELECT SingerID AS sid, COUNT(Songid) AS s2id
+FROM Songs
+GROUP BY 1
+ORDER BY 2 DESC
+--
+QueryStatement [0-83]
+  Query [0-83]
+    Select [0-67]
+      SelectList [7-45]
+        SelectColumn [7-22]
+          PathExpression [7-15]
+            Identifier(SingerID) [7-15]
+          Alias [16-22]
+            Identifier(sid) [19-22]
+        SelectColumn [24-45]
+          FunctionCall [24-37]
+            PathExpression [24-29]
+              Identifier(COUNT) [24-29]
+            PathExpression [30-36]
+              Identifier(Songid) [30-36]
+          Alias [38-45]
+            Identifier(s2id) [41-45]
+      FromClause [46-56]
+        TablePathExpression [51-56]
+          PathExpression [51-56]
+            Identifier(Songs) [51-56]
+      GroupBy [57-67]
+        GroupingItem [66-67]
+          IntLiteral(1) [66-67]
+    OrderBy [68-83]
+      OrderingExpression(DESC) [77-83]
+        IntLiteral(2) [77-78]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#aliases_clauses
+SELECT SingerID AS sid, COUNT(Songid) AS s2id
+FROM Songs
+GROUP BY sid
+ORDER BY s2id DESC
+--
+QueryStatement [0-88]
+  Query [0-88]
+    Select [0-69]
+      SelectList [7-45]
+        SelectColumn [7-22]
+          PathExpression [7-15]
+            Identifier(SingerID) [7-15]
+          Alias [16-22]
+            Identifier(sid) [19-22]
+        SelectColumn [24-45]
+          FunctionCall [24-37]
+            PathExpression [24-29]
+              Identifier(COUNT) [24-29]
+            PathExpression [30-36]
+              Identifier(Songid) [30-36]
+          Alias [38-45]
+            Identifier(s2id) [41-45]
+      FromClause [46-56]
+        TablePathExpression [51-56]
+          PathExpression [51-56]
+            Identifier(Songs) [51-56]
+      GroupBy [57-69]
+        GroupingItem [66-69]
+          PathExpression [66-69]
+            Identifier(sid) [66-69]
+    OrderBy [70-88]
+      OrderingExpression(DESC) [79-88]
+        PathExpression [79-83]
+          Identifier(s2id) [79-83]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#duplicate_aliases
+SELECT 1 AS a, 2 AS a
+--
+QueryStatement [0-21]
+  Query [0-21]
+    Select [0-21]
+      SelectList [7-21]
+        SelectColumn [7-13]
+          IntLiteral(1) [7-8]
+          Alias [9-13]
+            Identifier(a) [12-13]
+        SelectColumn [15-21]
+          IntLiteral(2) [15-16]
+          Alias [17-21]
+            Identifier(a) [20-21]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#ambiguous_aliases
+SELECT SingerID
+FROM Singers, Songs
+--
+QueryStatement [0-35]
+  Query [0-35]
+    Select [0-35]
+      SelectList [7-15]
+        SelectColumn [7-15]
+          PathExpression [7-15]
+            Identifier(SingerID) [7-15]
+      FromClause [16-35]
+        Join(COMMA) [21-35]
+          TablePathExpression [21-28]
+            PathExpression [21-28]
+              Identifier(Singers) [21-28]
+          Location [28-29]
+          TablePathExpression [30-35]
+            PathExpression [30-35]
+              Identifier(Songs) [30-35]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#ambiguous_aliases
+SELECT FirstName AS name, LastName AS name,
+FROM Singers
+GROUP BY name
+--
+QueryStatement [0-70]
+  Query [0-70]
+    Select [0-70]
+      SelectList [7-43]
+        SelectColumn [7-24]
+          PathExpression [7-16]
+            Identifier(FirstName) [7-16]
+          Alias [17-24]
+            Identifier(name) [20-24]
+        SelectColumn [26-42]
+          PathExpression [26-34]
+            Identifier(LastName) [26-34]
+          Alias [35-42]
+            Identifier(name) [38-42]
+      FromClause [44-56]
+        TablePathExpression [49-56]
+          PathExpression [49-56]
+            Identifier(Singers) [49-56]
+      GroupBy [57-70]
+        GroupingItem [66-70]
+          PathExpression [66-70]
+            Identifier(name) [66-70]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#ambiguous_aliases
+SELECT FirstName, LastName, PrimaryContact AS P
+FROM Person AS P
+GROUP BY P.FirstName
+--
+QueryStatement [0-85]
+  Query [0-85]
+    Select [0-85]
+      SelectList [7-47]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(FirstName) [7-16]
+        SelectColumn [18-26]
+          PathExpression [18-26]
+            Identifier(LastName) [18-26]
+        SelectColumn [28-47]
+          PathExpression [28-42]
+            Identifier(PrimaryContact) [28-42]
+          Alias [43-47]
+            Identifier(P) [46-47]
+      FromClause [48-64]
+        TablePathExpression [53-64]
+          PathExpression [53-59]
+            Identifier(Person) [53-59]
+          Alias [60-64]
+            Identifier(P) [63-64]
+      GroupBy [65-85]
+        GroupingItem [74-85]
+          PathExpression [74-85]
+            Identifier(P) [74-75]
+            Identifier(FirstName) [76-85]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#ambiguous_aliases
+SELECT LastName, BirthYear AS BirthYear
+FROM Singers
+GROUP BY BirthYear
+--
+QueryStatement [0-71]
+  Query [0-71]
+    Select [0-71]
+      SelectList [7-39]
+        SelectColumn [7-15]
+          PathExpression [7-15]
+            Identifier(LastName) [7-15]
+        SelectColumn [17-39]
+          PathExpression [17-26]
+            Identifier(BirthYear) [17-26]
+          Alias [27-39]
+            Identifier(BirthYear) [30-39]
+      FromClause [40-52]
+        TablePathExpression [45-52]
+          PathExpression [45-52]
+            Identifier(Singers) [45-52]
+      GroupBy [53-71]
+        GroupingItem [62-71]
+          PathExpression [62-71]
+            Identifier(BirthYear) [62-71]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#range_variables
+WITH Grid AS (SELECT 1 x, 2 y)
+SELECT Coordinate.x FROM Grid AS Coordinate
+--
+QueryStatement [0-74]
+  Query [0-74]
+    WithClause [0-30]
+      WithClauseEntry [5-30]
+        AliasedQuery [5-30]
+          Identifier(Grid) [5-9]
+          Query [13-30]
+            Select [14-29]
+              SelectList [21-29]
+                SelectColumn [21-24]
+                  IntLiteral(1) [21-22]
+                  Alias [23-24]
+                    Identifier(x) [23-24]
+                SelectColumn [26-29]
+                  IntLiteral(2) [26-27]
+                  Alias [28-29]
+                    Identifier(y) [28-29]
+    Select [31-74]
+      SelectList [38-50]
+        SelectColumn [38-50]
+          PathExpression [38-50]
+            Identifier(Coordinate) [38-48]
+            Identifier(x) [49-50]
+      FromClause [51-74]
+        TablePathExpression [56-74]
+          PathExpression [56-60]
+            Identifier(Grid) [56-60]
+          Alias [61-74]
+            Identifier(Coordinate) [64-74]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#range_variables
+WITH Grid AS (SELECT 1 x, 2 y)
+SELECT Coordinate.* FROM Grid AS Coordinate
+--
+QueryStatement [0-74]
+  Query [0-74]
+    WithClause [0-30]
+      WithClauseEntry [5-30]
+        AliasedQuery [5-30]
+          Identifier(Grid) [5-9]
+          Query [13-30]
+            Select [14-29]
+              SelectList [21-29]
+                SelectColumn [21-24]
+                  IntLiteral(1) [21-22]
+                  Alias [23-24]
+                    Identifier(x) [23-24]
+                SelectColumn [26-29]
+                  IntLiteral(2) [26-27]
+                  Alias [28-29]
+                    Identifier(y) [28-29]
+    Select [31-74]
+      SelectList [38-50]
+        SelectColumn [38-50]
+          DotStar [38-50]
+            PathExpression [38-48]
+              Identifier(Coordinate) [38-48]
+      FromClause [51-74]
+        TablePathExpression [56-74]
+          PathExpression [56-60]
+            Identifier(Grid) [56-60]
+          Alias [61-74]
+            Identifier(Coordinate) [64-74]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#range_variables
+WITH Grid AS (SELECT 1 x, 2 y)
+SELECT Coordinate FROM Grid AS Coordinate
+--
+QueryStatement [0-72]
+  Query [0-72]
+    WithClause [0-30]
+      WithClauseEntry [5-30]
+        AliasedQuery [5-30]
+          Identifier(Grid) [5-9]
+          Query [13-30]
+            Select [14-29]
+              SelectList [21-29]
+                SelectColumn [21-24]
+                  IntLiteral(1) [21-22]
+                  Alias [23-24]
+                    Identifier(x) [23-24]
+                SelectColumn [26-29]
+                  IntLiteral(2) [26-27]
+                  Alias [28-29]
+                    Identifier(y) [28-29]
+    Select [31-72]
+      SelectList [38-48]
+        SelectColumn [38-48]
+          PathExpression [38-48]
+            Identifier(Coordinate) [38-48]
+      FromClause [49-72]
+        TablePathExpression [54-72]
+          PathExpression [54-58]
+            Identifier(Grid) [54-58]
+          Alias [59-72]
+            Identifier(Coordinate) [62-72]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#table_hints
+SELECT s.SingerId, s.FirstName, s.LastName, s.SingerInfo
+FROM Singers@{FORCE_INDEX=SingersByFirstLastName} AS s
+WHERE s.FirstName = "Catalina" AND s.LastName > "M"
+--
+QueryStatement [0-163]
+  Query [0-163]
+    Select [0-163]
+      SelectList [7-56]
+        SelectColumn [7-17]
+          PathExpression [7-17]
+            Identifier(s) [7-8]
+            Identifier(SingerId) [9-17]
+        SelectColumn [19-30]
+          PathExpression [19-30]
+            Identifier(s) [19-20]
+            Identifier(FirstName) [21-30]
+        SelectColumn [32-42]
+          PathExpression [32-42]
+            Identifier(s) [32-33]
+            Identifier(LastName) [34-42]
+        SelectColumn [44-56]
+          PathExpression [44-56]
+            Identifier(s) [44-45]
+            Identifier(SingerInfo) [46-56]
+      FromClause [57-111]
+        TablePathExpression [62-111]
+          PathExpression [62-69]
+            Identifier(Singers) [62-69]
+          Hint [69-106]
+            HintEntry [71-105]
+              Identifier(FORCE_INDEX) [71-82]
+              PathExpression [83-105]
+                Identifier(SingersByFirstLastName) [83-105]
+          Alias [107-111]
+            Identifier(s) [110-111]
+      WhereClause [112-163]
+        AndExpr [118-163]
+          BinaryExpression(=) [118-142]
+            PathExpression [118-129]
+              Identifier(s) [118-119]
+              Identifier(FirstName) [120-129]
+            StringLiteral [132-142]
+              StringLiteralComponent("Catalina") [132-142]
+          BinaryExpression(>) [147-163]
+            PathExpression [147-157]
+              Identifier(s) [147-148]
+              Identifier(LastName) [149-157]
+            StringLiteral [160-163]
+              StringLiteralComponent("M") [160-163]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#table_hints
+SELECT s.SingerId, s.FirstName, s.LastName, s.SingerInfo, c.ConcertDate
+FROM Singers@{FORCE_INDEX=SingersByFirstLastName} AS s JOIN
+     Concerts@{FORCE_INDEX=ConcertsBySingerId} AS c ON s.SingerId = c.SingerId
+WHERE s.FirstName = "Catalina" AND s.LastName > "M"
+--
+QueryStatement [0-262]
+  Query [0-262]
+    Select [0-262]
+      SelectList [7-71]
+        SelectColumn [7-17]
+          PathExpression [7-17]
+            Identifier(s) [7-8]
+            Identifier(SingerId) [9-17]
+        SelectColumn [19-30]
+          PathExpression [19-30]
+            Identifier(s) [19-20]
+            Identifier(FirstName) [21-30]
+        SelectColumn [32-42]
+          PathExpression [32-42]
+            Identifier(s) [32-33]
+            Identifier(LastName) [34-42]
+        SelectColumn [44-56]
+          PathExpression [44-56]
+            Identifier(s) [44-45]
+            Identifier(SingerInfo) [46-56]
+        SelectColumn [58-71]
+          PathExpression [58-71]
+            Identifier(c) [58-59]
+            Identifier(ConcertDate) [60-71]
+      FromClause [72-210]
+        Join [77-210]
+          TablePathExpression [77-126]
+            PathExpression [77-84]
+              Identifier(Singers) [77-84]
+            Hint [84-121]
+              HintEntry [86-120]
+                Identifier(FORCE_INDEX) [86-97]
+                PathExpression [98-120]
+                  Identifier(SingersByFirstLastName) [98-120]
+            Alias [122-126]
+              Identifier(s) [125-126]
+          Location [127-131]
+          TablePathExpression [137-183]
+            PathExpression [137-145]
+              Identifier(Concerts) [137-145]
+            Hint [145-178]
+              HintEntry [147-177]
+                Identifier(FORCE_INDEX) [147-158]
+                PathExpression [159-177]
+                  Identifier(ConcertsBySingerId) [159-177]
+            Alias [179-183]
+              Identifier(c) [182-183]
+          OnClause [184-210]
+            BinaryExpression(=) [187-210]
+              PathExpression [187-197]
+                Identifier(s) [187-188]
+                Identifier(SingerId) [189-197]
+              PathExpression [200-210]
+                Identifier(c) [200-201]
+                Identifier(SingerId) [202-210]
+      WhereClause [211-262]
+        AndExpr [217-262]
+          BinaryExpression(=) [217-241]
+            PathExpression [217-228]
+              Identifier(s) [217-218]
+              Identifier(FirstName) [219-228]
+            StringLiteral [231-241]
+              StringLiteralComponent("Catalina") [231-241]
+          BinaryExpression(>) [246-262]
+            PathExpression [246-256]
+              Identifier(s) [246-247]
+              Identifier(LastName) [248-256]
+            StringLiteral [259-262]
+              StringLiteralComponent("M") [259-262]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#group_hints
+SELECT SingerId, COUNT(*) AS SongCount
+FROM Songs
+GROUP @{GROUP_METHOD=STREAM_GROUP} BY SingerId
+--
+QueryStatement [0-96]
+  Query [0-96]
+    Select [0-96]
+      SelectList [7-38]
+        SelectColumn [7-15]
+          PathExpression [7-15]
+            Identifier(SingerId) [7-15]
+        SelectColumn [17-38]
+          FunctionCall [17-25]
+            PathExpression [17-22]
+              Identifier(COUNT) [17-22]
+            Star(*) [23-24]
+          Alias [26-38]
+            Identifier(SongCount) [29-38]
+      FromClause [39-49]
+        TablePathExpression [44-49]
+          PathExpression [44-49]
+            Identifier(Songs) [44-49]
+      GroupBy [50-96]
+        Hint [56-84]
+          HintEntry [58-83]
+            Identifier(GROUP_METHOD) [58-70]
+            PathExpression [71-83]
+              Identifier(STREAM_GROUP) [71-83]
+        GroupingItem [88-96]
+          PathExpression [88-96]
+            Identifier(SingerId) [88-96]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#value_tables
+SELECT * FROM (SELECT AS VALUE STRUCT(123 AS a, FALSE AS b))
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-60]
+      SelectList [7-8]
+        SelectColumn [7-8]
+          Star(*) [7-8]
+      FromClause [9-60]
+        TableSubquery [14-60]
+          Query [15-59]
+            Select [15-59]
+              SelectAs(as_mode=VALUE) [22-30]
+              SelectList [31-59]
+                SelectColumn [31-59]
+                  StructConstructorWithKeyword [31-59]
+                    StructConstructorArg [38-46]
+                      IntLiteral(123) [38-41]
+                      Alias [42-46]
+                        Identifier(a) [45-46]
+                    StructConstructorArg [48-58]
+                      BooleanLiteral(FALSE) [48-53]
+                      Alias [54-58]
+                        Identifier(b) [57-58]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#roster_table
+WITH Roster AS
+ (SELECT 'Adams' as LastName, 50 as SchoolID UNION ALL
+  SELECT 'Buchanan', 52 UNION ALL
+  SELECT 'Coolidge', 52 UNION ALL
+  SELECT 'Davis', 51 UNION ALL
+  SELECT 'Eisenhower', 77)
+SELECT * FROM Roster
+--
+QueryStatement [0-216]
+  Query [0-216]
+    WithClause [0-195]
+      WithClauseEntry [5-195]
+        AliasedQuery [5-195]
+          Identifier(Roster) [5-11]
+          Query [16-195]
+            SetOperation(UNION ALL) [17-194]
+              SetOperationMetadataList [60-168]
+                SetOperationMetadata [60-69]
+                  SetOperationType [60-65]
+                  SetOperationAllOrDistinct [66-69]
+                SetOperationMetadata [94-103]
+                  SetOperationType [94-99]
+                  SetOperationAllOrDistinct [100-103]
+                SetOperationMetadata [128-137]
+                  SetOperationType [128-133]
+                  SetOperationAllOrDistinct [134-137]
+                SetOperationMetadata [159-168]
+                  SetOperationType [159-164]
+                  SetOperationAllOrDistinct [165-168]
+              Select [17-59]
+                SelectList [24-59]
+                  SelectColumn [24-43]
+                    StringLiteral [24-31]
+                      StringLiteralComponent('Adams') [24-31]
+                    Alias [32-43]
+                      Identifier(LastName) [35-43]
+                  SelectColumn [45-59]
+                    IntLiteral(50) [45-47]
+                    Alias [48-59]
+                      Identifier(SchoolID) [51-59]
+              Select [72-93]
+                SelectList [79-93]
+                  SelectColumn [79-89]
+                    StringLiteral [79-89]
+                      StringLiteralComponent('Buchanan') [79-89]
+                  SelectColumn [91-93]
+                    IntLiteral(52) [91-93]
+              Select [106-127]
+                SelectList [113-127]
+                  SelectColumn [113-123]
+                    StringLiteral [113-123]
+                      StringLiteralComponent('Coolidge') [113-123]
+                  SelectColumn [125-127]
+                    IntLiteral(52) [125-127]
+              Select [140-158]
+                SelectList [147-158]
+                  SelectColumn [147-154]
+                    StringLiteral [147-154]
+                      StringLiteralComponent('Davis') [147-154]
+                  SelectColumn [156-158]
+                    IntLiteral(51) [156-158]
+              Select [171-194]
+                SelectList [178-194]
+                  SelectColumn [178-190]
+                    StringLiteral [178-190]
+                      StringLiteralComponent('Eisenhower') [178-190]
+                  SelectColumn [192-194]
+                    IntLiteral(77) [192-194]
+    Select [196-216]
+      SelectList [203-204]
+        SelectColumn [203-204]
+          Star(*) [203-204]
+      FromClause [205-216]
+        TablePathExpression [210-216]
+          PathExpression [210-216]
+            Identifier(Roster) [210-216]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#playerstats_table
+WITH PlayerStats AS
+ (SELECT 'Adams' as LastName, 51 as OpponentID, 3 as PointsScored UNION ALL
+  SELECT 'Buchanan', 77, 0 UNION ALL
+  SELECT 'Coolidge', 77, 1 UNION ALL
+  SELECT 'Adams', 52, 4 UNION ALL
+  SELECT 'Buchanan', 50, 13)
+SELECT * FROM PlayerStats
+--
+QueryStatement [0-258]
+  Query [0-258]
+    WithClause [0-232]
+      WithClauseEntry [5-232]
+        AliasedQuery [5-232]
+          Identifier(PlayerStats) [5-16]
+          Query [21-232]
+            SetOperation(UNION ALL) [22-231]
+              SetOperationMetadataList [86-203]
+                SetOperationMetadata [86-95]
+                  SetOperationType [86-91]
+                  SetOperationAllOrDistinct [92-95]
+                SetOperationMetadata [123-132]
+                  SetOperationType [123-128]
+                  SetOperationAllOrDistinct [129-132]
+                SetOperationMetadata [160-169]
+                  SetOperationType [160-165]
+                  SetOperationAllOrDistinct [166-169]
+                SetOperationMetadata [194-203]
+                  SetOperationType [194-199]
+                  SetOperationAllOrDistinct [200-203]
+              Select [22-85]
+                SelectList [29-85]
+                  SelectColumn [29-48]
+                    StringLiteral [29-36]
+                      StringLiteralComponent('Adams') [29-36]
+                    Alias [37-48]
+                      Identifier(LastName) [40-48]
+                  SelectColumn [50-66]
+                    IntLiteral(51) [50-52]
+                    Alias [53-66]
+                      Identifier(OpponentID) [56-66]
+                  SelectColumn [68-85]
+                    IntLiteral(3) [68-69]
+                    Alias [70-85]
+                      Identifier(PointsScored) [73-85]
+              Select [98-122]
+                SelectList [105-122]
+                  SelectColumn [105-115]
+                    StringLiteral [105-115]
+                      StringLiteralComponent('Buchanan') [105-115]
+                  SelectColumn [117-119]
+                    IntLiteral(77) [117-119]
+                  SelectColumn [121-122]
+                    IntLiteral(0) [121-122]
+              Select [135-159]
+                SelectList [142-159]
+                  SelectColumn [142-152]
+                    StringLiteral [142-152]
+                      StringLiteralComponent('Coolidge') [142-152]
+                  SelectColumn [154-156]
+                    IntLiteral(77) [154-156]
+                  SelectColumn [158-159]
+                    IntLiteral(1) [158-159]
+              Select [172-193]
+                SelectList [179-193]
+                  SelectColumn [179-186]
+                    StringLiteral [179-186]
+                      StringLiteralComponent('Adams') [179-186]
+                  SelectColumn [188-190]
+                    IntLiteral(52) [188-190]
+                  SelectColumn [192-193]
+                    IntLiteral(4) [192-193]
+              Select [206-231]
+                SelectList [213-231]
+                  SelectColumn [213-223]
+                    StringLiteral [213-223]
+                      StringLiteralComponent('Buchanan') [213-223]
+                  SelectColumn [225-227]
+                    IntLiteral(50) [225-227]
+                  SelectColumn [229-231]
+                    IntLiteral(13) [229-231]
+    Select [233-258]
+      SelectList [240-241]
+        SelectColumn [240-241]
+          Star(*) [240-241]
+      FromClause [242-258]
+        TablePathExpression [247-258]
+          PathExpression [247-258]
+            Identifier(PlayerStats) [247-258]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#teammascot_table
+WITH TeamMascot AS
+ (SELECT 50 as SchoolID, 'Jaguars' as Mascot UNION ALL
+  SELECT 51, 'Knights' UNION ALL
+  SELECT 52, 'Lakers' UNION ALL
+  SELECT 53, 'Mustangs')
+SELECT * FROM TeamMascot
+--
+QueryStatement [0-188]
+  Query [0-188]
+    WithClause [0-163]
+      WithClauseEntry [5-163]
+        AliasedQuery [5-163]
+          Identifier(TeamMascot) [5-15]
+          Query [20-163]
+            SetOperation(UNION ALL) [21-162]
+              SetOperationMetadataList [64-138]
+                SetOperationMetadata [64-73]
+                  SetOperationType [64-69]
+                  SetOperationAllOrDistinct [70-73]
+                SetOperationMetadata [97-106]
+                  SetOperationType [97-102]
+                  SetOperationAllOrDistinct [103-106]
+                SetOperationMetadata [129-138]
+                  SetOperationType [129-134]
+                  SetOperationAllOrDistinct [135-138]
+              Select [21-63]
+                SelectList [28-63]
+                  SelectColumn [28-42]
+                    IntLiteral(50) [28-30]
+                    Alias [31-42]
+                      Identifier(SchoolID) [34-42]
+                  SelectColumn [44-63]
+                    StringLiteral [44-53]
+                      StringLiteralComponent('Jaguars') [44-53]
+                    Alias [54-63]
+                      Identifier(Mascot) [57-63]
+              Select [76-96]
+                SelectList [83-96]
+                  SelectColumn [83-85]
+                    IntLiteral(51) [83-85]
+                  SelectColumn [87-96]
+                    StringLiteral [87-96]
+                      StringLiteralComponent('Knights') [87-96]
+              Select [109-128]
+                SelectList [116-128]
+                  SelectColumn [116-118]
+                    IntLiteral(52) [116-118]
+                  SelectColumn [120-128]
+                    StringLiteral [120-128]
+                      StringLiteralComponent('Lakers') [120-128]
+              Select [141-162]
+                SelectList [148-162]
+                  SelectColumn [148-150]
+                    IntLiteral(53) [148-150]
+                  SelectColumn [152-162]
+                    StringLiteral [152-162]
+                      StringLiteralComponent('Mustangs') [152-162]
+    Select [164-188]
+      SelectList [171-172]
+        SelectColumn [171-172]
+          Star(*) [171-172]
+      FromClause [173-188]
+        TablePathExpression [178-188]
+          PathExpression [178-188]
+            Identifier(TeamMascot) [178-188]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#group_by_clause_example
+SELECT LastName, SUM(PointsScored)
+FROM PlayerStats
+GROUP BY LastName
+--
+QueryStatement [0-69]
+  Query [0-69]
+    Select [0-69]
+      SelectList [7-34]
+        SelectColumn [7-15]
+          PathExpression [7-15]
+            Identifier(LastName) [7-15]
+        SelectColumn [17-34]
+          FunctionCall [17-34]
+            PathExpression [17-20]
+              Identifier(SUM) [17-20]
+            PathExpression [21-33]
+              Identifier(PointsScored) [21-33]
+      FromClause [35-51]
+        TablePathExpression [40-51]
+          PathExpression [40-51]
+            Identifier(PlayerStats) [40-51]
+      GroupBy [52-69]
+        GroupingItem [61-69]
+          PathExpression [61-69]
+            Identifier(LastName) [61-69]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#union_example
+SELECT Mascot AS X, SchoolID AS Y
+FROM TeamMascot
+UNION ALL
+SELECT LastName, PointsScored
+FROM PlayerStats
+--
+QueryStatement [0-106]
+  Query [0-106]
+    SetOperation(UNION ALL) [0-106]
+      SetOperationMetadataList [50-59]
+        SetOperationMetadata [50-59]
+          SetOperationType [50-55]
+          SetOperationAllOrDistinct [56-59]
+      Select [0-49]
+        SelectList [7-33]
+          SelectColumn [7-18]
+            PathExpression [7-13]
+              Identifier(Mascot) [7-13]
+            Alias [14-18]
+              Identifier(X) [17-18]
+          SelectColumn [20-33]
+            PathExpression [20-28]
+              Identifier(SchoolID) [20-28]
+            Alias [29-33]
+              Identifier(Y) [32-33]
+        FromClause [34-49]
+          TablePathExpression [39-49]
+            PathExpression [39-49]
+              Identifier(TeamMascot) [39-49]
+      Select [60-106]
+        SelectList [67-89]
+          SelectColumn [67-75]
+            PathExpression [67-75]
+              Identifier(LastName) [67-75]
+          SelectColumn [77-89]
+            PathExpression [77-89]
+              Identifier(PointsScored) [77-89]
+        FromClause [90-106]
+          TablePathExpression [95-106]
+            PathExpression [95-106]
+              Identifier(PlayerStats) [95-106]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#intersect_example
+SELECT LastName
+FROM Roster
+INTERSECT ALL
+SELECT LastName
+FROM PlayerStats
+--
+QueryStatement [0-74]
+  Query [0-74]
+    SetOperation(INTERSECT ALL) [0-74]
+      SetOperationMetadataList [28-41]
+        SetOperationMetadata [28-41]
+          SetOperationType [28-37]
+          SetOperationAllOrDistinct [38-41]
+      Select [0-27]
+        SelectList [7-15]
+          SelectColumn [7-15]
+            PathExpression [7-15]
+              Identifier(LastName) [7-15]
+        FromClause [16-27]
+          TablePathExpression [21-27]
+            PathExpression [21-27]
+              Identifier(Roster) [21-27]
+      Select [42-74]
+        SelectList [49-57]
+          SelectColumn [49-57]
+            PathExpression [49-57]
+              Identifier(LastName) [49-57]
+        FromClause [58-74]
+          TablePathExpression [63-74]
+            PathExpression [63-74]
+              Identifier(PlayerStats) [63-74]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#except_example
+SELECT LastName
+FROM Roster
+EXCEPT DISTINCT
+SELECT LastName
+FROM PlayerStats
+--
+QueryStatement [0-76]
+  Query [0-76]
+    SetOperation(EXCEPT DISTINCT) [0-76]
+      SetOperationMetadataList [28-43]
+        SetOperationMetadata [28-43]
+          SetOperationType [28-34]
+          SetOperationAllOrDistinct [35-43]
+      Select [0-27]
+        SelectList [7-15]
+          SelectColumn [7-15]
+            PathExpression [7-15]
+              Identifier(LastName) [7-15]
+        FromClause [16-27]
+          TablePathExpression [21-27]
+            PathExpression [21-27]
+              Identifier(Roster) [21-27]
+      Select [44-76]
+        SelectList [51-59]
+          SelectColumn [51-59]
+            PathExpression [51-59]
+              Identifier(LastName) [51-59]
+        FromClause [60-76]
+          TablePathExpression [65-76]
+            PathExpression [65-76]
+              Identifier(PlayerStats) [65-76]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#except_example
+SELECT LastName
+FROM PlayerStats
+EXCEPT DISTINCT
+SELECT LastName
+FROM Roster
+--
+QueryStatement [0-76]
+  Query [0-76]
+    SetOperation(EXCEPT DISTINCT) [0-76]
+      SetOperationMetadataList [33-48]
+        SetOperationMetadata [33-48]
+          SetOperationType [33-39]
+          SetOperationAllOrDistinct [40-48]
+      Select [0-32]
+        SelectList [7-15]
+          SelectColumn [7-15]
+            PathExpression [7-15]
+              Identifier(LastName) [7-15]
+        FromClause [16-32]
+          TablePathExpression [21-32]
+            PathExpression [21-32]
+              Identifier(PlayerStats) [21-32]
+      Select [49-76]
+        SelectList [56-64]
+          SelectColumn [56-64]
+            PathExpression [56-64]
+              Identifier(LastName) [56-64]
+        FromClause [65-76]
+          TablePathExpression [70-76]
+            PathExpression [70-76]
+              Identifier(Roster) [70-76]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/query-syntax#except_example
+[mode=expression]
+(empty)
+--
+PathExpression [1-6]
+  Identifier(empty) [1-6]
diff --git a/parser/testdata/spanner/search_functions.metadata.json b/parser/testdata/spanner/search_functions.metadata.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/parser/testdata/spanner/search_functions.metadata.json
@@ -0,0 +1 @@
+{}
diff --git a/parser/testdata/spanner/search_functions.test b/parser/testdata/spanner/search_functions.test
new file mode 100644
index 0000000..ae3e5e0
--- /dev/null
+++ b/parser/testdata/spanner/search_functions.test
@@ -0,0 +1,2523 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#debug_tokenlist
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+DEBUG_TOKENLIST(tokenlist)
+--
+FunctionCall [0-26]
+  PathExpression [0-15]
+    Identifier(DEBUG_TOKENLIST) [0-15]
+  PathExpression [16-25]
+    Identifier(tokenlist) [16-25]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#debug_tokenlist
+SELECT DEBUG_TOKENLIST(TOKENIZE_FULLTEXT('Hello DB #World')) AS Result
+--
+QueryStatement [0-70]
+  Query [0-70]
+    Select [0-70]
+      SelectList [7-70]
+        SelectColumn [7-70]
+          FunctionCall [7-60]
+            PathExpression [7-22]
+              Identifier(DEBUG_TOKENLIST) [7-22]
+            FunctionCall [23-59]
+              PathExpression [23-40]
+                Identifier(TOKENIZE_FULLTEXT) [23-40]
+              StringLiteral [41-58]
+                StringLiteralComponent('Hello DB #World') [41-58]
+          Alias [61-70]
+            Identifier(Result) [64-70]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#debug_tokenlist
+SELECT DEBUG_TOKENLIST(TOKENIZE_NUMBER([1, 10], min=> 1, max=>10)) AS Result
+--
+QueryStatement [0-76]
+  Query [0-76]
+    Select [0-76]
+      SelectList [7-76]
+        SelectColumn [7-76]
+          FunctionCall [7-66]
+            PathExpression [7-22]
+              Identifier(DEBUG_TOKENLIST) [7-22]
+            FunctionCall [23-65]
+              PathExpression [23-38]
+                Identifier(TOKENIZE_NUMBER) [23-38]
+              ArrayConstructor [39-46]
+                IntLiteral(1) [40-41]
+                IntLiteral(10) [43-45]
+              NamedArgument [48-55]
+                Identifier(min) [48-51]
+                IntLiteral(1) [54-55]
+              NamedArgument [57-64]
+                Identifier(max) [57-60]
+                IntLiteral(10) [62-64]
+          Alias [67-76]
+            Identifier(Result) [70-76]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#score
+CREATE TABLE Albums (
+  SingerId INT64 NOT NULL,
+  AlbumId INT64 NOT NULL,
+  Description STRING(MAX),
+  DescriptionTokens TOKENLIST AS (TOKENIZE_FULLTEXT(Description)) HIDDEN
+) PRIMARY KEY (SingerId, AlbumId)
+--
+CreateTableStatement [0-208]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-176]
+    ColumnDefinition [24-47]
+      Identifier(SingerId) [24-32]
+      SimpleColumnSchema [33-47]
+        PathExpression [33-38]
+          Identifier(INT64) [33-38]
+        ColumnAttributeList [39-47]
+          NotNullColumnAttribute [39-47]
+    ColumnDefinition [51-73]
+      Identifier(AlbumId) [51-58]
+      SimpleColumnSchema [59-73]
+        PathExpression [59-64]
+          Identifier(INT64) [59-64]
+        ColumnAttributeList [65-73]
+          NotNullColumnAttribute [65-73]
+    ColumnDefinition [77-100]
+      Identifier(Description) [77-88]
+      SimpleColumnSchema [89-100]
+        PathExpression [89-95]
+          Identifier(STRING) [89-95]
+        TypeParameterList [95-99]
+          MaxLiteral() [96-99]
+    ColumnDefinition [104-174]
+      Identifier(DescriptionTokens) [104-121]
+      SimpleColumnSchema [122-174]
+        PathExpression [122-131]
+          Identifier(TOKENLIST) [122-131]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [132-167]
+          FunctionCall [136-166]
+            PathExpression [136-153]
+              Identifier(TOKENIZE_FULLTEXT) [136-153]
+            PathExpression [154-165]
+              Identifier(Description) [154-165]
+        ColumnAttributeList [168-174]
+          HiddenColumnAttribute [168-174]
+  SpannerTableOptions [177-208]
+    PrimaryKey(ENFORCED) [177-208]
+      PrimaryKeyElementList [189-208]
+        PrimaryKeyElement(ASC) [190-198]
+          Identifier(SingerId) [190-198]
+        PrimaryKeyElement(ASC) [200-207]
+          Identifier(AlbumId) [200-207]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#score
+CREATE SEARCH INDEX AlbumsIndex ON Albums(DescriptionTokens)
+--
+CreateIndexStatement(SEARCH) [0-60]
+  PathExpression [20-31]
+    Identifier(AlbumsIndex) [20-31]
+  PathExpression [35-41]
+    Identifier(Albums) [35-41]
+  IndexItemList [41-60]
+    OrderingExpression(ASC) [42-59]
+      PathExpression [42-59]
+        Identifier(DescriptionTokens) [42-59]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#score
+INSERT INTO Albums (SingerId, AlbumId, Description) VALUES (1, 1, 'classical album')
+--
+InsertStatement [0-84]
+  PathExpression [12-18]
+    Identifier(Albums) [12-18]
+  ColumnList [19-51]
+    Identifier(SingerId) [20-28]
+    Identifier(AlbumId) [30-37]
+    Identifier(Description) [39-50]
+  InsertValuesRowList [52-84]
+    InsertValuesRow [59-84]
+      IntLiteral(1) [60-61]
+      IntLiteral(1) [63-64]
+      StringLiteral [66-83]
+        StringLiteralComponent('classical album') [66-83]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#score
+INSERT INTO Albums (SingerId, AlbumId, Description) VALUES (1, 2, 'classical and rock album')
+--
+InsertStatement [0-93]
+  PathExpression [12-18]
+    Identifier(Albums) [12-18]
+  ColumnList [19-51]
+    Identifier(SingerId) [20-28]
+    Identifier(AlbumId) [30-37]
+    Identifier(Description) [39-50]
+  InsertValuesRowList [52-93]
+    InsertValuesRow [59-93]
+      IntLiteral(1) [60-61]
+      IntLiteral(2) [63-64]
+      StringLiteral [66-92]
+        StringLiteralComponent('classical and rock album') [66-92]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#score
+SELECT
+  a.Description, SCORE(a.DescriptionTokens, 'classical album') AS Score
+FROM
+  Albums a
+WHERE
+  SEARCH(a.DescriptionTokens, 'classical album')
+--
+QueryStatement [0-149]
+  Query [0-149]
+    Select [0-149]
+      SelectList [9-78]
+        SelectColumn [9-22]
+          PathExpression [9-22]
+            Identifier(a) [9-10]
+            Identifier(Description) [11-22]
+        SelectColumn [24-78]
+          FunctionCall [24-69]
+            PathExpression [24-29]
+              Identifier(SCORE) [24-29]
+            PathExpression [30-49]
+              Identifier(a) [30-31]
+              Identifier(DescriptionTokens) [32-49]
+            StringLiteral [51-68]
+              StringLiteralComponent('classical album') [51-68]
+          Alias [70-78]
+            Identifier(Score) [73-78]
+      FromClause [79-94]
+        TablePathExpression [86-94]
+          PathExpression [86-92]
+            Identifier(Albums) [86-92]
+          Alias [93-94]
+            Identifier(a) [93-94]
+      WhereClause [95-149]
+        FunctionCall [103-149]
+          PathExpression [103-109]
+            Identifier(SEARCH) [103-109]
+          PathExpression [110-129]
+            Identifier(a) [110-111]
+            Identifier(DescriptionTokens) [112-129]
+          StringLiteral [131-148]
+            StringLiteralComponent('classical album') [131-148]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#score
+SELECT
+  a.Description,
+  SCORE(
+    a.DescriptionTokens,
+    'classical album',
+    options=>JSON '{"bigram_weight": 3.0}'
+  ) AS Score
+FROM Albums a
+WHERE SEARCH(a.DescriptionTokens, 'classical album')
+--
+QueryStatement [0-203]
+  Query [0-203]
+    Select [0-203]
+      SelectList [9-136]
+        SelectColumn [9-22]
+          PathExpression [9-22]
+            Identifier(a) [9-10]
+            Identifier(Description) [11-22]
+        SelectColumn [26-136]
+          FunctionCall [26-127]
+            PathExpression [26-31]
+              Identifier(SCORE) [26-31]
+            PathExpression [37-56]
+              Identifier(a) [37-38]
+              Identifier(DescriptionTokens) [39-56]
+            StringLiteral [62-79]
+              StringLiteralComponent('classical album') [62-79]
+            NamedArgument [85-123]
+              Identifier(options) [85-92]
+              JSONLiteral [94-123]
+                StringLiteral [99-123]
+                  StringLiteralComponent('{"bigram_weight": 3.0}') [99-123]
+          Alias [128-136]
+            Identifier(Score) [131-136]
+      FromClause [137-150]
+        TablePathExpression [142-150]
+          PathExpression [142-148]
+            Identifier(Albums) [142-148]
+          Alias [149-150]
+            Identifier(a) [149-150]
+      WhereClause [151-203]
+        FunctionCall [157-203]
+          PathExpression [157-163]
+            Identifier(SEARCH) [157-163]
+          PathExpression [164-183]
+            Identifier(a) [164-165]
+            Identifier(DescriptionTokens) [166-183]
+          StringLiteral [185-202]
+            StringLiteralComponent('classical album') [185-202]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#score
+SELECT a.Description
+FROM Albums a
+WHERE SEARCH(a.DescriptionTokens, 'classical album')
+ORDER BY SCORE(a.DescriptionTokens, 'classical album') DESC
+LIMIT 1
+--
+QueryStatement [0-155]
+  Query [0-155]
+    Select [0-87]
+      SelectList [7-20]
+        SelectColumn [7-20]
+          PathExpression [7-20]
+            Identifier(a) [7-8]
+            Identifier(Description) [9-20]
+      FromClause [21-34]
+        TablePathExpression [26-34]
+          PathExpression [26-32]
+            Identifier(Albums) [26-32]
+          Alias [33-34]
+            Identifier(a) [33-34]
+      WhereClause [35-87]
+        FunctionCall [41-87]
+          PathExpression [41-47]
+            Identifier(SEARCH) [41-47]
+          PathExpression [48-67]
+            Identifier(a) [48-49]
+            Identifier(DescriptionTokens) [50-67]
+          StringLiteral [69-86]
+            StringLiteralComponent('classical album') [69-86]
+    OrderBy [88-147]
+      OrderingExpression(DESC) [97-147]
+        FunctionCall [97-142]
+          PathExpression [97-102]
+            Identifier(SCORE) [97-102]
+          PathExpression [103-122]
+            Identifier(a) [103-104]
+            Identifier(DescriptionTokens) [105-122]
+          StringLiteral [124-141]
+            StringLiteralComponent('classical album') [124-141]
+    LimitOffset [148-155]
+      Limit [148-155]
+        IntLiteral(1) [154-155]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#score_ngrams
+CREATE TABLE Albums (
+  AlbumId INT64 NOT NULL,
+  Description STRING(MAX),
+  DescriptionSubstrTokens TOKENLIST AS
+    (TOKENIZE_SUBSTRING(Description, ngram_size_max=>3)) HIDDEN
+) PRIMARY KEY (AlbumId)
+--
+CreateTableStatement [0-201]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-179]
+    ColumnDefinition [24-46]
+      Identifier(AlbumId) [24-31]
+      SimpleColumnSchema [32-46]
+        PathExpression [32-37]
+          Identifier(INT64) [32-37]
+        ColumnAttributeList [38-46]
+          NotNullColumnAttribute [38-46]
+    ColumnDefinition [50-73]
+      Identifier(Description) [50-61]
+      SimpleColumnSchema [62-73]
+        PathExpression [62-68]
+          Identifier(STRING) [62-68]
+        TypeParameterList [68-72]
+          MaxLiteral() [69-72]
+    ColumnDefinition [77-177]
+      Identifier(DescriptionSubstrTokens) [77-100]
+      SimpleColumnSchema [101-177]
+        PathExpression [101-110]
+          Identifier(TOKENLIST) [101-110]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [111-170]
+          FunctionCall [119-169]
+            PathExpression [119-137]
+              Identifier(TOKENIZE_SUBSTRING) [119-137]
+            PathExpression [138-149]
+              Identifier(Description) [138-149]
+            NamedArgument [151-168]
+              Identifier(ngram_size_max) [151-165]
+              IntLiteral(3) [167-168]
+        ColumnAttributeList [171-177]
+          HiddenColumnAttribute [171-177]
+  SpannerTableOptions [180-201]
+    PrimaryKey(ENFORCED) [180-201]
+      PrimaryKeyElementList [192-201]
+        PrimaryKeyElement(ASC) [193-200]
+          Identifier(AlbumId) [193-200]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#score_ngrams
+CREATE SEARCH INDEX AlbumsIndex ON Albums(DescriptionSubstrTokens)
+  STORING(Description)
+--
+CreateIndexStatement(SEARCH) [0-89]
+  PathExpression [20-31]
+    Identifier(AlbumsIndex) [20-31]
+  PathExpression [35-41]
+    Identifier(Albums) [35-41]
+  IndexItemList [41-66]
+    OrderingExpression(ASC) [42-65]
+      PathExpression [42-65]
+        Identifier(DescriptionSubstrTokens) [42-65]
+  IndexStoringExpressionList [76-89]
+    PathExpression [77-88]
+      Identifier(Description) [77-88]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#score_ngrams
+INSERT INTO Albums (AlbumId, Description) VALUES (1, 'rock album')
+--
+InsertStatement [0-66]
+  PathExpression [12-18]
+    Identifier(Albums) [12-18]
+  ColumnList [19-41]
+    Identifier(AlbumId) [20-27]
+    Identifier(Description) [29-40]
+  InsertValuesRowList [42-66]
+    InsertValuesRow [49-66]
+      IntLiteral(1) [50-51]
+      StringLiteral [53-65]
+        StringLiteralComponent('rock album') [53-65]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#score_ngrams
+INSERT INTO Albums (AlbumId, Description) VALUES (2, 'classical album')
+--
+InsertStatement [0-71]
+  PathExpression [12-18]
+    Identifier(Albums) [12-18]
+  ColumnList [19-41]
+    Identifier(AlbumId) [20-27]
+    Identifier(Description) [29-40]
+  InsertValuesRowList [42-71]
+    InsertValuesRow [49-71]
+      IntLiteral(2) [50-51]
+      StringLiteral [53-70]
+        StringLiteralComponent('classical album') [53-70]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#score_ngrams
+SELECT
+  a.Description, SCORE_NGRAMS(a.DescriptionSubstrTokens, 'clasic albun') AS Score
+FROM
+  Albums a
+--
+QueryStatement [0-104]
+  Query [0-104]
+    Select [0-104]
+      SelectList [9-88]
+        SelectColumn [9-22]
+          PathExpression [9-22]
+            Identifier(a) [9-10]
+            Identifier(Description) [11-22]
+        SelectColumn [24-88]
+          FunctionCall [24-79]
+            PathExpression [24-36]
+              Identifier(SCORE_NGRAMS) [24-36]
+            PathExpression [37-62]
+              Identifier(a) [37-38]
+              Identifier(DescriptionSubstrTokens) [39-62]
+            StringLiteral [64-78]
+              StringLiteralComponent('clasic albun') [64-78]
+          Alias [80-88]
+            Identifier(Score) [83-88]
+      FromClause [89-104]
+        TablePathExpression [96-104]
+          PathExpression [96-102]
+            Identifier(Albums) [96-102]
+          Alias [103-104]
+            Identifier(a) [103-104]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#score_ngrams
+SELECT a.Description
+FROM Albums a
+WHERE SEARCH_NGRAMS(a.DescriptionSubstrTokens, 'clasic albun')
+ORDER BY SCORE_NGRAMS(a.DescriptionSubstrTokens, 'clasic albun') DESC
+LIMIT 1
+--
+QueryStatement [0-175]
+  Query [0-175]
+    Select [0-97]
+      SelectList [7-20]
+        SelectColumn [7-20]
+          PathExpression [7-20]
+            Identifier(a) [7-8]
+            Identifier(Description) [9-20]
+      FromClause [21-34]
+        TablePathExpression [26-34]
+          PathExpression [26-32]
+            Identifier(Albums) [26-32]
+          Alias [33-34]
+            Identifier(a) [33-34]
+      WhereClause [35-97]
+        FunctionCall [41-97]
+          PathExpression [41-54]
+            Identifier(SEARCH_NGRAMS) [41-54]
+          PathExpression [55-80]
+            Identifier(a) [55-56]
+            Identifier(DescriptionSubstrTokens) [57-80]
+          StringLiteral [82-96]
+            StringLiteralComponent('clasic albun') [82-96]
+    OrderBy [98-167]
+      OrderingExpression(DESC) [107-167]
+        FunctionCall [107-162]
+          PathExpression [107-119]
+            Identifier(SCORE_NGRAMS) [107-119]
+          PathExpression [120-145]
+            Identifier(a) [120-121]
+            Identifier(DescriptionSubstrTokens) [122-145]
+          StringLiteral [147-161]
+            StringLiteralComponent('clasic albun') [147-161]
+    LimitOffset [168-175]
+      Limit [168-175]
+        IntLiteral(1) [174-175]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#search_fulltext
+[mode=expression]
+SEARCH(tl, 'big')
+AND (SEARCH(tl, 'time')
+    OR SEARCH(tl, 'fast'))
+AND SEARCH(tl, 'car')
+--
+AndExpr [0-90]
+  FunctionCall [0-17]
+    PathExpression [0-6]
+      Identifier(SEARCH) [0-6]
+    PathExpression [7-9]
+      Identifier(tl) [7-9]
+    StringLiteral [11-16]
+      StringLiteralComponent('big') [11-16]
+  OrExpr [23-67]
+    FunctionCall [23-41]
+      PathExpression [23-29]
+        Identifier(SEARCH) [23-29]
+      PathExpression [30-32]
+        Identifier(tl) [30-32]
+      StringLiteral [34-40]
+        StringLiteralComponent('time') [34-40]
+    FunctionCall [49-67]
+      PathExpression [49-55]
+        Identifier(SEARCH) [49-55]
+      PathExpression [56-58]
+        Identifier(tl) [56-58]
+      StringLiteral [60-66]
+        StringLiteralComponent('fast') [60-66]
+  FunctionCall [73-90]
+    PathExpression [73-79]
+      Identifier(SEARCH) [73-79]
+    PathExpression [80-82]
+      Identifier(tl) [80-82]
+    StringLiteral [84-89]
+      StringLiteralComponent('car') [84-89]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#search_fulltext
+CREATE SEARCH INDEX AlbumsIndex ON Albums(DescriptionTokens)
+PARTITION BY SingerId
+--
+CreateIndexStatement(SEARCH) [0-82]
+  PathExpression [20-31]
+    Identifier(AlbumsIndex) [20-31]
+  PathExpression [35-41]
+    Identifier(Albums) [35-41]
+  IndexItemList [41-60]
+    OrderingExpression(ASC) [42-59]
+      PathExpression [42-59]
+        Identifier(DescriptionTokens) [42-59]
+  PartitionBy [61-82]
+    PathExpression [74-82]
+      Identifier(SingerId) [74-82]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#search_fulltext
+INSERT INTO Albums (SingerId, AlbumId, Description) VALUES (1, 1, 'rock album')
+--
+InsertStatement [0-79]
+  PathExpression [12-18]
+    Identifier(Albums) [12-18]
+  ColumnList [19-51]
+    Identifier(SingerId) [20-28]
+    Identifier(AlbumId) [30-37]
+    Identifier(Description) [39-50]
+  InsertValuesRowList [52-79]
+    InsertValuesRow [59-79]
+      IntLiteral(1) [60-61]
+      IntLiteral(1) [63-64]
+      StringLiteral [66-78]
+        StringLiteralComponent('rock album') [66-78]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#search_fulltext
+INSERT INTO Albums (SingerId, AlbumId, Description) VALUES (1, 2, 'classical album')
+--
+InsertStatement [0-84]
+  PathExpression [12-18]
+    Identifier(Albums) [12-18]
+  ColumnList [19-51]
+    Identifier(SingerId) [20-28]
+    Identifier(AlbumId) [30-37]
+    Identifier(Description) [39-50]
+  InsertValuesRowList [52-84]
+    InsertValuesRow [59-84]
+      IntLiteral(1) [60-61]
+      IntLiteral(2) [63-64]
+      StringLiteral [66-83]
+        StringLiteralComponent('classical album') [66-83]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#search_fulltext
+SELECT a.AlbumId, a.Description
+FROM Albums a
+WHERE a.SingerId = 1 AND SEARCH(a.DescriptionTokens, 'classical')
+--
+QueryStatement [0-111]
+  Query [0-111]
+    Select [0-111]
+      SelectList [7-31]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(a) [7-8]
+            Identifier(AlbumId) [9-16]
+        SelectColumn [18-31]
+          PathExpression [18-31]
+            Identifier(a) [18-19]
+            Identifier(Description) [20-31]
+      FromClause [32-45]
+        TablePathExpression [37-45]
+          PathExpression [37-43]
+            Identifier(Albums) [37-43]
+          Alias [44-45]
+            Identifier(a) [44-45]
+      WhereClause [46-111]
+        AndExpr [52-111]
+          BinaryExpression(=) [52-66]
+            PathExpression [52-62]
+              Identifier(a) [52-53]
+              Identifier(SingerId) [54-62]
+            IntLiteral(1) [65-66]
+          FunctionCall [71-111]
+            PathExpression [71-77]
+              Identifier(SEARCH) [71-77]
+            PathExpression [78-97]
+              Identifier(a) [78-79]
+              Identifier(DescriptionTokens) [80-97]
+            StringLiteral [99-110]
+              StringLiteralComponent('classical') [99-110]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#search_fulltext
+SELECT a.AlbumId, a.Description
+FROM Albums a
+WHERE a.SingerId = 1 AND SEARCH(a.DescriptionTokens, 'classical OR rock')
+--
+QueryStatement [0-119]
+  Query [0-119]
+    Select [0-119]
+      SelectList [7-31]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(a) [7-8]
+            Identifier(AlbumId) [9-16]
+        SelectColumn [18-31]
+          PathExpression [18-31]
+            Identifier(a) [18-19]
+            Identifier(Description) [20-31]
+      FromClause [32-45]
+        TablePathExpression [37-45]
+          PathExpression [37-43]
+            Identifier(Albums) [37-43]
+          Alias [44-45]
+            Identifier(a) [44-45]
+      WhereClause [46-119]
+        AndExpr [52-119]
+          BinaryExpression(=) [52-66]
+            PathExpression [52-62]
+              Identifier(a) [52-53]
+              Identifier(SingerId) [54-62]
+            IntLiteral(1) [65-66]
+          FunctionCall [71-119]
+            PathExpression [71-77]
+              Identifier(SEARCH) [71-77]
+            PathExpression [78-97]
+              Identifier(a) [78-79]
+              Identifier(DescriptionTokens) [80-97]
+            StringLiteral [99-118]
+              StringLiteralComponent('classical OR rock') [99-118]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#search_fulltext
+SELECT a.AlbumId, a.Description
+FROM Albums a
+WHERE a.SingerId = 1 AND SEARCH(a.DescriptionTokens, 'classic albums', enhance_query => TRUE)
+--
+QueryStatement [0-139]
+  Query [0-139]
+    Select [0-139]
+      SelectList [7-31]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(a) [7-8]
+            Identifier(AlbumId) [9-16]
+        SelectColumn [18-31]
+          PathExpression [18-31]
+            Identifier(a) [18-19]
+            Identifier(Description) [20-31]
+      FromClause [32-45]
+        TablePathExpression [37-45]
+          PathExpression [37-43]
+            Identifier(Albums) [37-43]
+          Alias [44-45]
+            Identifier(a) [44-45]
+      WhereClause [46-139]
+        AndExpr [52-139]
+          BinaryExpression(=) [52-66]
+            PathExpression [52-62]
+              Identifier(a) [52-53]
+              Identifier(SingerId) [54-62]
+            IntLiteral(1) [65-66]
+          FunctionCall [71-139]
+            PathExpression [71-77]
+              Identifier(SEARCH) [71-77]
+            PathExpression [78-97]
+              Identifier(a) [78-79]
+              Identifier(DescriptionTokens) [80-97]
+            StringLiteral [99-115]
+              StringLiteralComponent('classic albums') [99-115]
+            NamedArgument [117-138]
+              Identifier(enhance_query) [117-130]
+              BooleanLiteral(TRUE) [134-138]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#search_ngrams
+CREATE TABLE Albums (
+  AlbumId INT64 NOT NULL,
+  Description STRING(MAX),
+  DescriptionSubstrTokens TOKENLIST AS
+    (TOKENIZE_SUBSTRING(Description, ngram_size_min=>3, ngram_size_max=>3)) HIDDEN,
+  DescriptionNgramsTokens TOKENLIST AS
+    (TOKENIZE_NGRAMS(Description, ngram_size_min=>3, ngram_size_max=>3)) HIDDEN
+) PRIMARY KEY (SingerId, AlbumId)
+--
+CreateTableStatement [0-350]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-318]
+    ColumnDefinition [24-46]
+      Identifier(AlbumId) [24-31]
+      SimpleColumnSchema [32-46]
+        PathExpression [32-37]
+          Identifier(INT64) [32-37]
+        ColumnAttributeList [38-46]
+          NotNullColumnAttribute [38-46]
+    ColumnDefinition [50-73]
+      Identifier(Description) [50-61]
+      SimpleColumnSchema [62-73]
+        PathExpression [62-68]
+          Identifier(STRING) [62-68]
+        TypeParameterList [68-72]
+          MaxLiteral() [69-72]
+    ColumnDefinition [77-196]
+      Identifier(DescriptionSubstrTokens) [77-100]
+      SimpleColumnSchema [101-196]
+        PathExpression [101-110]
+          Identifier(TOKENLIST) [101-110]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [111-189]
+          FunctionCall [119-188]
+            PathExpression [119-137]
+              Identifier(TOKENIZE_SUBSTRING) [119-137]
+            PathExpression [138-149]
+              Identifier(Description) [138-149]
+            NamedArgument [151-168]
+              Identifier(ngram_size_min) [151-165]
+              IntLiteral(3) [167-168]
+            NamedArgument [170-187]
+              Identifier(ngram_size_max) [170-184]
+              IntLiteral(3) [186-187]
+        ColumnAttributeList [190-196]
+          HiddenColumnAttribute [190-196]
+    ColumnDefinition [200-316]
+      Identifier(DescriptionNgramsTokens) [200-223]
+      SimpleColumnSchema [224-316]
+        PathExpression [224-233]
+          Identifier(TOKENLIST) [224-233]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [234-309]
+          FunctionCall [242-308]
+            PathExpression [242-257]
+              Identifier(TOKENIZE_NGRAMS) [242-257]
+            PathExpression [258-269]
+              Identifier(Description) [258-269]
+            NamedArgument [271-288]
+              Identifier(ngram_size_min) [271-285]
+              IntLiteral(3) [287-288]
+            NamedArgument [290-307]
+              Identifier(ngram_size_max) [290-304]
+              IntLiteral(3) [306-307]
+        ColumnAttributeList [310-316]
+          HiddenColumnAttribute [310-316]
+  SpannerTableOptions [319-350]
+    PrimaryKey(ENFORCED) [319-350]
+      PrimaryKeyElementList [331-350]
+        PrimaryKeyElement(ASC) [332-340]
+          Identifier(SingerId) [332-340]
+        PrimaryKeyElement(ASC) [342-349]
+          Identifier(AlbumId) [342-349]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#search_ngrams
+CREATE SEARCH INDEX AlbumsIndex ON Albums(DescriptionSubstrTokens, DescriptionNgramsTokens)
+--
+CreateIndexStatement(SEARCH) [0-91]
+  PathExpression [20-31]
+    Identifier(AlbumsIndex) [20-31]
+  PathExpression [35-41]
+    Identifier(Albums) [35-41]
+  IndexItemList [41-91]
+    OrderingExpression(ASC) [42-65]
+      PathExpression [42-65]
+        Identifier(DescriptionSubstrTokens) [42-65]
+    OrderingExpression(ASC) [67-90]
+      PathExpression [67-90]
+        Identifier(DescriptionNgramsTokens) [67-90]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#search_ngrams
+INSERT INTO Albums (AlbumId, Description) VALUES (3, 'last note')
+--
+InsertStatement [0-65]
+  PathExpression [12-18]
+    Identifier(Albums) [12-18]
+  ColumnList [19-41]
+    Identifier(AlbumId) [20-27]
+    Identifier(Description) [29-40]
+  InsertValuesRowList [42-65]
+    InsertValuesRow [49-65]
+      IntLiteral(3) [50-51]
+      StringLiteral [53-64]
+        StringLiteralComponent('last note') [53-64]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#search_ngrams
+SELECT
+  a.AlbumId, a.Description
+FROM
+  Albums a
+WHERE
+  SEARCH_NGRAMS(a.DescriptionSubstrTokens, 'clasic')
+--
+QueryStatement [0-108]
+  Query [0-108]
+    Select [0-108]
+      SelectList [9-33]
+        SelectColumn [9-18]
+          PathExpression [9-18]
+            Identifier(a) [9-10]
+            Identifier(AlbumId) [11-18]
+        SelectColumn [20-33]
+          PathExpression [20-33]
+            Identifier(a) [20-21]
+            Identifier(Description) [22-33]
+      FromClause [34-49]
+        TablePathExpression [41-49]
+          PathExpression [41-47]
+            Identifier(Albums) [41-47]
+          Alias [48-49]
+            Identifier(a) [48-49]
+      WhereClause [50-108]
+        FunctionCall [58-108]
+          PathExpression [58-71]
+            Identifier(SEARCH_NGRAMS) [58-71]
+          PathExpression [72-97]
+            Identifier(a) [72-73]
+            Identifier(DescriptionSubstrTokens) [74-97]
+          StringLiteral [99-107]
+            StringLiteralComponent('clasic') [99-107]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#search_ngrams
+SELECT
+  a.AlbumId, a.Description
+FROM
+  Albums a
+WHERE
+  SEARCH_NGRAMS(a.DescriptionSubstrTokens, 'clasic', min_ngrams=>1)
+--
+QueryStatement [0-123]
+  Query [0-123]
+    Select [0-123]
+      SelectList [9-33]
+        SelectColumn [9-18]
+          PathExpression [9-18]
+            Identifier(a) [9-10]
+            Identifier(AlbumId) [11-18]
+        SelectColumn [20-33]
+          PathExpression [20-33]
+            Identifier(a) [20-21]
+            Identifier(Description) [22-33]
+      FromClause [34-49]
+        TablePathExpression [41-49]
+          PathExpression [41-47]
+            Identifier(Albums) [41-47]
+          Alias [48-49]
+            Identifier(a) [48-49]
+      WhereClause [50-123]
+        FunctionCall [58-123]
+          PathExpression [58-71]
+            Identifier(SEARCH_NGRAMS) [58-71]
+          PathExpression [72-97]
+            Identifier(a) [72-73]
+            Identifier(DescriptionSubstrTokens) [74-97]
+          StringLiteral [99-107]
+            StringLiteralComponent('clasic') [99-107]
+          NamedArgument [109-122]
+            Identifier(min_ngrams) [109-119]
+            IntLiteral(1) [121-122]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#search_ngrams
+SELECT
+  a.AlbumId, a.Description
+FROM
+  Albums a
+WHERE
+  SEARCH_NGRAMS(a.DescriptionSubstrTokens, 'clasic albun')
+--
+QueryStatement [0-114]
+  Query [0-114]
+    Select [0-114]
+      SelectList [9-33]
+        SelectColumn [9-18]
+          PathExpression [9-18]
+            Identifier(a) [9-10]
+            Identifier(AlbumId) [11-18]
+        SelectColumn [20-33]
+          PathExpression [20-33]
+            Identifier(a) [20-21]
+            Identifier(Description) [22-33]
+      FromClause [34-49]
+        TablePathExpression [41-49]
+          PathExpression [41-47]
+            Identifier(Albums) [41-47]
+          Alias [48-49]
+            Identifier(a) [48-49]
+      WhereClause [50-114]
+        FunctionCall [58-114]
+          PathExpression [58-71]
+            Identifier(SEARCH_NGRAMS) [58-71]
+          PathExpression [72-97]
+            Identifier(a) [72-73]
+            Identifier(DescriptionSubstrTokens) [74-97]
+          StringLiteral [99-113]
+            StringLiteralComponent('clasic albun') [99-113]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#search_ngrams
+SELECT
+  a.AlbumId, a.Description
+FROM
+  Albums a
+WHERE
+  SEARCH_NGRAMS(a.DescriptionNgramsTokens, 'l al')
+--
+QueryStatement [0-106]
+  Query [0-106]
+    Select [0-106]
+      SelectList [9-33]
+        SelectColumn [9-18]
+          PathExpression [9-18]
+            Identifier(a) [9-10]
+            Identifier(AlbumId) [11-18]
+        SelectColumn [20-33]
+          PathExpression [20-33]
+            Identifier(a) [20-21]
+            Identifier(Description) [22-33]
+      FromClause [34-49]
+        TablePathExpression [41-49]
+          PathExpression [41-47]
+            Identifier(Albums) [41-47]
+          Alias [48-49]
+            Identifier(a) [48-49]
+      WhereClause [50-106]
+        FunctionCall [58-106]
+          PathExpression [58-71]
+            Identifier(SEARCH_NGRAMS) [58-71]
+          PathExpression [72-97]
+            Identifier(a) [72-73]
+            Identifier(DescriptionNgramsTokens) [74-97]
+          StringLiteral [99-105]
+            StringLiteralComponent('l al') [99-105]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#search_substring
+CREATE TABLE Albums (
+  SingerId INT64 NOT NULL,
+  AlbumId INT64 NOT NULL,
+  Description STRING(MAX),
+  DescriptionSubstrTokens TOKENLIST AS (TOKENIZE_SUBSTRING(Description, relative_search_types => ['all'])) HIDDEN
+) PRIMARY KEY (SingerId, AlbumId)
+--
+CreateTableStatement [0-249]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-217]
+    ColumnDefinition [24-47]
+      Identifier(SingerId) [24-32]
+      SimpleColumnSchema [33-47]
+        PathExpression [33-38]
+          Identifier(INT64) [33-38]
+        ColumnAttributeList [39-47]
+          NotNullColumnAttribute [39-47]
+    ColumnDefinition [51-73]
+      Identifier(AlbumId) [51-58]
+      SimpleColumnSchema [59-73]
+        PathExpression [59-64]
+          Identifier(INT64) [59-64]
+        ColumnAttributeList [65-73]
+          NotNullColumnAttribute [65-73]
+    ColumnDefinition [77-100]
+      Identifier(Description) [77-88]
+      SimpleColumnSchema [89-100]
+        PathExpression [89-95]
+          Identifier(STRING) [89-95]
+        TypeParameterList [95-99]
+          MaxLiteral() [96-99]
+    ColumnDefinition [104-215]
+      Identifier(DescriptionSubstrTokens) [104-127]
+      SimpleColumnSchema [128-215]
+        PathExpression [128-137]
+          Identifier(TOKENLIST) [128-137]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [138-208]
+          FunctionCall [142-207]
+            PathExpression [142-160]
+              Identifier(TOKENIZE_SUBSTRING) [142-160]
+            PathExpression [161-172]
+              Identifier(Description) [161-172]
+            NamedArgument [174-206]
+              Identifier(relative_search_types) [174-195]
+              ArrayConstructor [199-206]
+                StringLiteral [200-205]
+                  StringLiteralComponent('all') [200-205]
+        ColumnAttributeList [209-215]
+          HiddenColumnAttribute [209-215]
+  SpannerTableOptions [218-249]
+    PrimaryKey(ENFORCED) [218-249]
+      PrimaryKeyElementList [230-249]
+        PrimaryKeyElement(ASC) [231-239]
+          Identifier(SingerId) [231-239]
+        PrimaryKeyElement(ASC) [241-248]
+          Identifier(AlbumId) [241-248]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#search_substring
+CREATE SEARCH INDEX AlbumsIndex ON Albums(DescriptionSubstrTokens)
+PARTITION BY SingerId
+--
+CreateIndexStatement(SEARCH) [0-88]
+  PathExpression [20-31]
+    Identifier(AlbumsIndex) [20-31]
+  PathExpression [35-41]
+    Identifier(Albums) [35-41]
+  IndexItemList [41-66]
+    OrderingExpression(ASC) [42-65]
+      PathExpression [42-65]
+        Identifier(DescriptionSubstrTokens) [42-65]
+  PartitionBy [67-88]
+    PathExpression [80-88]
+      Identifier(SingerId) [80-88]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#search_substring
+SELECT
+  a.AlbumId, a.Description
+FROM
+  Albums a
+WHERE
+  a.SingerId = 1 AND SEARCH_SUBSTRING(a.DescriptionSubstrTokens, 'ssic')
+--
+QueryStatement [0-128]
+  Query [0-128]
+    Select [0-128]
+      SelectList [9-33]
+        SelectColumn [9-18]
+          PathExpression [9-18]
+            Identifier(a) [9-10]
+            Identifier(AlbumId) [11-18]
+        SelectColumn [20-33]
+          PathExpression [20-33]
+            Identifier(a) [20-21]
+            Identifier(Description) [22-33]
+      FromClause [34-49]
+        TablePathExpression [41-49]
+          PathExpression [41-47]
+            Identifier(Albums) [41-47]
+          Alias [48-49]
+            Identifier(a) [48-49]
+      WhereClause [50-128]
+        AndExpr [58-128]
+          BinaryExpression(=) [58-72]
+            PathExpression [58-68]
+              Identifier(a) [58-59]
+              Identifier(SingerId) [60-68]
+            IntLiteral(1) [71-72]
+          FunctionCall [77-128]
+            PathExpression [77-93]
+              Identifier(SEARCH_SUBSTRING) [77-93]
+            PathExpression [94-119]
+              Identifier(a) [94-95]
+              Identifier(DescriptionSubstrTokens) [96-119]
+            StringLiteral [121-127]
+              StringLiteralComponent('ssic') [121-127]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#search_substring
+SELECT
+  a.AlbumId, a.Description
+FROM
+  Albums a
+WHERE
+  a.SingerId = 1 AND SEARCH_SUBSTRING(a.DescriptionSubstrTokens, 'lbu oc')
+--
+QueryStatement [0-130]
+  Query [0-130]
+    Select [0-130]
+      SelectList [9-33]
+        SelectColumn [9-18]
+          PathExpression [9-18]
+            Identifier(a) [9-10]
+            Identifier(AlbumId) [11-18]
+        SelectColumn [20-33]
+          PathExpression [20-33]
+            Identifier(a) [20-21]
+            Identifier(Description) [22-33]
+      FromClause [34-49]
+        TablePathExpression [41-49]
+          PathExpression [41-47]
+            Identifier(Albums) [41-47]
+          Alias [48-49]
+            Identifier(a) [48-49]
+      WhereClause [50-130]
+        AndExpr [58-130]
+          BinaryExpression(=) [58-72]
+            PathExpression [58-68]
+              Identifier(a) [58-59]
+              Identifier(SingerId) [60-68]
+            IntLiteral(1) [71-72]
+          FunctionCall [77-130]
+            PathExpression [77-93]
+              Identifier(SEARCH_SUBSTRING) [77-93]
+            PathExpression [94-119]
+              Identifier(a) [94-95]
+              Identifier(DescriptionSubstrTokens) [96-119]
+            StringLiteral [121-129]
+              StringLiteralComponent('lbu oc') [121-129]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#search_substring
+SELECT
+  a.AlbumId, a.Description
+FROM
+  Albums a
+WHERE
+  a.SingerId = 1 AND SEARCH_SUBSTRING(a.DescriptionSubstrTokens, 'al', relative_search_type=>'word_prefix')
+--
+QueryStatement [0-163]
+  Query [0-163]
+    Select [0-163]
+      SelectList [9-33]
+        SelectColumn [9-18]
+          PathExpression [9-18]
+            Identifier(a) [9-10]
+            Identifier(AlbumId) [11-18]
+        SelectColumn [20-33]
+          PathExpression [20-33]
+            Identifier(a) [20-21]
+            Identifier(Description) [22-33]
+      FromClause [34-49]
+        TablePathExpression [41-49]
+          PathExpression [41-47]
+            Identifier(Albums) [41-47]
+          Alias [48-49]
+            Identifier(a) [48-49]
+      WhereClause [50-163]
+        AndExpr [58-163]
+          BinaryExpression(=) [58-72]
+            PathExpression [58-68]
+              Identifier(a) [58-59]
+              Identifier(SingerId) [60-68]
+            IntLiteral(1) [71-72]
+          FunctionCall [77-163]
+            PathExpression [77-93]
+              Identifier(SEARCH_SUBSTRING) [77-93]
+            PathExpression [94-119]
+              Identifier(a) [94-95]
+              Identifier(DescriptionSubstrTokens) [96-119]
+            StringLiteral [121-125]
+              StringLiteralComponent('al') [121-125]
+            NamedArgument [127-162]
+              Identifier(relative_search_type) [127-147]
+              StringLiteral [149-162]
+                StringLiteralComponent('word_prefix') [149-162]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#search_substring
+SELECT
+  a.AlbumId, a.Description
+FROM
+  Albums a
+WHERE
+  a.SingerId = 1 AND SEARCH_SUBSTRING(a.DescriptionSubstrTokens, 'al', relative_search_type=>'value_prefix')
+--
+QueryStatement [0-164]
+  Query [0-164]
+    Select [0-164]
+      SelectList [9-33]
+        SelectColumn [9-18]
+          PathExpression [9-18]
+            Identifier(a) [9-10]
+            Identifier(AlbumId) [11-18]
+        SelectColumn [20-33]
+          PathExpression [20-33]
+            Identifier(a) [20-21]
+            Identifier(Description) [22-33]
+      FromClause [34-49]
+        TablePathExpression [41-49]
+          PathExpression [41-47]
+            Identifier(Albums) [41-47]
+          Alias [48-49]
+            Identifier(a) [48-49]
+      WhereClause [50-164]
+        AndExpr [58-164]
+          BinaryExpression(=) [58-72]
+            PathExpression [58-68]
+              Identifier(a) [58-59]
+              Identifier(SingerId) [60-68]
+            IntLiteral(1) [71-72]
+          FunctionCall [77-164]
+            PathExpression [77-93]
+              Identifier(SEARCH_SUBSTRING) [77-93]
+            PathExpression [94-119]
+              Identifier(a) [94-95]
+              Identifier(DescriptionSubstrTokens) [96-119]
+            StringLiteral [121-125]
+              StringLiteralComponent('al') [121-125]
+            NamedArgument [127-163]
+              Identifier(relative_search_type) [127-147]
+              StringLiteral [149-163]
+                StringLiteralComponent('value_prefix') [149-163]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#snippet
+SELECT SNIPPET('Rock albums rock.', 'rock') AS Snippet
+--
+QueryStatement [0-54]
+  Query [0-54]
+    Select [0-54]
+      SelectList [7-54]
+        SelectColumn [7-54]
+          FunctionCall [7-43]
+            PathExpression [7-14]
+              Identifier(SNIPPET) [7-14]
+            StringLiteral [15-34]
+              StringLiteralComponent('Rock albums rock.') [15-34]
+            StringLiteral [36-42]
+              StringLiteralComponent('rock') [36-42]
+          Alias [44-54]
+            Identifier(Snippet) [47-54]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#token
+[mode=expression]
+TOKEN(value_to_tokenize)
+--
+FunctionCall [0-24]
+  PathExpression [0-5]
+    Identifier(TOKEN) [0-5]
+  PathExpression [6-23]
+    Identifier(value_to_tokenize) [6-23]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#token
+CREATE TABLE Albums (
+  SingerId INT64 NOT NULL,
+  AlbumId INT64 NOT NULL,
+  SingerName STRING(MAX),
+  SingerNameToken TOKENLIST AS (TOKEN(SingerName)) HIDDEN,
+  SongTitles ARRAY,
+  SongTitlesToken TOKENLIST AS (TOKEN(SongTitles)) HIDDEN
+) PRIMARY KEY (SingerId, AlbumId)
+--
+CreateTableStatement [0-284]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-252]
+    ColumnDefinition [24-47]
+      Identifier(SingerId) [24-32]
+      SimpleColumnSchema [33-47]
+        PathExpression [33-38]
+          Identifier(INT64) [33-38]
+        ColumnAttributeList [39-47]
+          NotNullColumnAttribute [39-47]
+    ColumnDefinition [51-73]
+      Identifier(AlbumId) [51-58]
+      SimpleColumnSchema [59-73]
+        PathExpression [59-64]
+          Identifier(INT64) [59-64]
+        ColumnAttributeList [65-73]
+          NotNullColumnAttribute [65-73]
+    ColumnDefinition [77-99]
+      Identifier(SingerName) [77-87]
+      SimpleColumnSchema [88-99]
+        PathExpression [88-94]
+          Identifier(STRING) [88-94]
+        TypeParameterList [94-98]
+          MaxLiteral() [95-98]
+    ColumnDefinition [103-158]
+      Identifier(SingerNameToken) [103-118]
+      SimpleColumnSchema [119-158]
+        PathExpression [119-128]
+          Identifier(TOKENLIST) [119-128]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [129-151]
+          FunctionCall [133-150]
+            PathExpression [133-138]
+              Identifier(TOKEN) [133-138]
+            PathExpression [139-149]
+              Identifier(SingerName) [139-149]
+        ColumnAttributeList [152-158]
+          HiddenColumnAttribute [152-158]
+    ColumnDefinition [162-191]
+      Identifier(SongTitles) [162-172]
+      ArrayColumnSchema [173-191]
+        SimpleColumnSchema [179-190]
+          PathExpression [179-185]
+            Identifier(STRING) [179-185]
+          TypeParameterList [185-189]
+            MaxLiteral() [186-189]
+    ColumnDefinition [195-250]
+      Identifier(SongTitlesToken) [195-210]
+      SimpleColumnSchema [211-250]
+        PathExpression [211-220]
+          Identifier(TOKENLIST) [211-220]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [221-243]
+          FunctionCall [225-242]
+            PathExpression [225-230]
+              Identifier(TOKEN) [225-230]
+            PathExpression [231-241]
+              Identifier(SongTitles) [231-241]
+        ColumnAttributeList [244-250]
+          HiddenColumnAttribute [244-250]
+  SpannerTableOptions [253-284]
+    PrimaryKey(ENFORCED) [253-284]
+      PrimaryKeyElementList [265-284]
+        PrimaryKeyElement(ASC) [266-274]
+          Identifier(SingerId) [266-274]
+        PrimaryKeyElement(ASC) [276-283]
+          Identifier(AlbumId) [276-283]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#token
+CREATE SEARCH INDEX AlbumsIndex ON Albums(SingerNameToken, SongTitlesToken)
+--
+CreateIndexStatement(SEARCH) [0-75]
+  PathExpression [20-31]
+    Identifier(AlbumsIndex) [20-31]
+  PathExpression [35-41]
+    Identifier(Albums) [35-41]
+  IndexItemList [41-75]
+    OrderingExpression(ASC) [42-57]
+      PathExpression [42-57]
+        Identifier(SingerNameToken) [42-57]
+    OrderingExpression(ASC) [59-74]
+      PathExpression [59-74]
+        Identifier(SongTitlesToken) [59-74]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#token
+-- For example, the INSERT statement below generates SingerNameToken of
+-- 'Catalina Smith', and SongTitlesToken of
+-- ['Starting Again', 'The Second Title'].
+INSERT INTO Albums (SingerId, AlbumId, SingerName, SongTitles)
+  VALUES (1, 1, 'Catalina Smith', ['Starting Again', 'The Second Time'])
+--
+InsertStatement [159-294]
+  PathExpression [171-177]
+    Identifier(Albums) [171-177]
+  ColumnList [178-221]
+    Identifier(SingerId) [179-187]
+    Identifier(AlbumId) [189-196]
+    Identifier(SingerName) [198-208]
+    Identifier(SongTitles) [210-220]
+  InsertValuesRowList [224-294]
+    InsertValuesRow [231-294]
+      IntLiteral(1) [232-233]
+      IntLiteral(1) [235-236]
+      StringLiteral [238-254]
+        StringLiteralComponent('Catalina Smith') [238-254]
+      ArrayConstructor [256-293]
+        StringLiteral [257-273]
+          StringLiteralComponent('Starting Again') [257-273]
+        StringLiteral [275-292]
+          StringLiteralComponent('The Second Time') [275-292]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#token
+SELECT a.AlbumId
+FROM Albums @{force_index = AlbumsIndex} a
+WHERE a.SingerName = 'Catalina Smith'
+--
+QueryStatement [0-97]
+  Query [0-97]
+    Select [0-97]
+      SelectList [7-16]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(a) [7-8]
+            Identifier(AlbumId) [9-16]
+      FromClause [17-59]
+        TablePathExpression [22-59]
+          PathExpression [22-28]
+            Identifier(Albums) [22-28]
+          Hint [29-57]
+            HintEntry [31-56]
+              Identifier(force_index) [31-42]
+              PathExpression [45-56]
+                Identifier(AlbumsIndex) [45-56]
+          Alias [58-59]
+            Identifier(a) [58-59]
+      WhereClause [60-97]
+        BinaryExpression(=) [66-97]
+          PathExpression [66-78]
+            Identifier(a) [66-67]
+            Identifier(SingerName) [68-78]
+          StringLiteral [81-97]
+            StringLiteralComponent('Catalina Smith') [81-97]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#token
+SELECT a.AlbumId
+FROM Albums a
+WHERE ARRAY_INCLUDES(a.SongTitles, 'Starting Again')
+--
+QueryStatement [0-83]
+  Query [0-83]
+    Select [0-83]
+      SelectList [7-16]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(a) [7-8]
+            Identifier(AlbumId) [9-16]
+      FromClause [17-30]
+        TablePathExpression [22-30]
+          PathExpression [22-28]
+            Identifier(Albums) [22-28]
+          Alias [29-30]
+            Identifier(a) [29-30]
+      WhereClause [31-83]
+        FunctionCall [37-83]
+          PathExpression [37-51]
+            Identifier(ARRAY_INCLUDES) [37-51]
+          PathExpression [52-64]
+            Identifier(a) [52-53]
+            Identifier(SongTitles) [54-64]
+          StringLiteral [66-82]
+            StringLiteralComponent('Starting Again') [66-82]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_bool
+[mode=expression]
+TOKENIZE_BOOL(value_to_tokenize)
+--
+FunctionCall [0-32]
+  PathExpression [0-13]
+    Identifier(TOKENIZE_BOOL) [0-13]
+  PathExpression [14-31]
+    Identifier(value_to_tokenize) [14-31]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_bool
+CREATE TABLE Albums (
+  SingerId INT64 NOT NULL,
+  AlbumId INT64 NOT NULL,
+  IsAwarded BOOL,
+  IsAwardedToken TOKENLIST AS (TOKENIZE_BOOL(IsAwarded)) HIDDEN
+) PRIMARY KEY (SingerId, AlbumId)
+--
+CreateTableStatement [0-190]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-158]
+    ColumnDefinition [24-47]
+      Identifier(SingerId) [24-32]
+      SimpleColumnSchema [33-47]
+        PathExpression [33-38]
+          Identifier(INT64) [33-38]
+        ColumnAttributeList [39-47]
+          NotNullColumnAttribute [39-47]
+    ColumnDefinition [51-73]
+      Identifier(AlbumId) [51-58]
+      SimpleColumnSchema [59-73]
+        PathExpression [59-64]
+          Identifier(INT64) [59-64]
+        ColumnAttributeList [65-73]
+          NotNullColumnAttribute [65-73]
+    ColumnDefinition [77-91]
+      Identifier(IsAwarded) [77-86]
+      SimpleColumnSchema [87-91]
+        PathExpression [87-91]
+          Identifier(BOOL) [87-91]
+    ColumnDefinition [95-156]
+      Identifier(IsAwardedToken) [95-109]
+      SimpleColumnSchema [110-156]
+        PathExpression [110-119]
+          Identifier(TOKENLIST) [110-119]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [120-149]
+          FunctionCall [124-148]
+            PathExpression [124-137]
+              Identifier(TOKENIZE_BOOL) [124-137]
+            PathExpression [138-147]
+              Identifier(IsAwarded) [138-147]
+        ColumnAttributeList [150-156]
+          HiddenColumnAttribute [150-156]
+  SpannerTableOptions [159-190]
+    PrimaryKey(ENFORCED) [159-190]
+      PrimaryKeyElementList [171-190]
+        PrimaryKeyElement(ASC) [172-180]
+          Identifier(SingerId) [172-180]
+        PrimaryKeyElement(ASC) [182-189]
+          Identifier(AlbumId) [182-189]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_bool
+CREATE SEARCH INDEX AlbumsIndex ON Albums(IsAwardedToken)
+--
+CreateIndexStatement(SEARCH) [0-57]
+  PathExpression [20-31]
+    Identifier(AlbumsIndex) [20-31]
+  PathExpression [35-41]
+    Identifier(Albums) [35-41]
+  IndexItemList [41-57]
+    OrderingExpression(ASC) [42-56]
+      PathExpression [42-56]
+        Identifier(IsAwardedToken) [42-56]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_bool
+-- IsAwarded with TRUE generates IsAwardedToken with value 'y'.
+INSERT INTO Albums (SingerId, AlbumId, IsAwarded) VALUES (1, 1, TRUE)
+--
+InsertStatement [64-133]
+  PathExpression [76-82]
+    Identifier(Albums) [76-82]
+  ColumnList [83-113]
+    Identifier(SingerId) [84-92]
+    Identifier(AlbumId) [94-101]
+    Identifier(IsAwarded) [103-112]
+  InsertValuesRowList [114-133]
+    InsertValuesRow [121-133]
+      IntLiteral(1) [122-123]
+      IntLiteral(1) [125-126]
+      BooleanLiteral(TRUE) [128-132]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_bool
+-- IsAwarded with FALSE generates IsAwardedToken with value 'n'.
+INSERT INTO Albums (SingerId, AlbumId, IsAwarded) VALUES (1, 2, FALSE)
+--
+InsertStatement [65-135]
+  PathExpression [77-83]
+    Identifier(Albums) [77-83]
+  ColumnList [84-114]
+    Identifier(SingerId) [85-93]
+    Identifier(AlbumId) [95-102]
+    Identifier(IsAwarded) [104-113]
+  InsertValuesRowList [115-135]
+    InsertValuesRow [122-135]
+      IntLiteral(1) [123-124]
+      IntLiteral(2) [126-127]
+      BooleanLiteral(FALSE) [129-134]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_bool
+-- NULL IsAwarded generates IsAwardedToken with value NULL.
+INSERT INTO Albums (SingerId, AlbumId) VALUES (1, 3)
+--
+InsertStatement [60-112]
+  PathExpression [72-78]
+    Identifier(Albums) [72-78]
+  ColumnList [79-98]
+    Identifier(SingerId) [80-88]
+    Identifier(AlbumId) [90-97]
+  InsertValuesRowList [99-112]
+    InsertValuesRow [106-112]
+      IntLiteral(1) [107-108]
+      IntLiteral(3) [110-111]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_bool
+SELECT a.AlbumId
+FROM Albums @{force_index = AlbumsIndex} a
+WHERE IsAwarded = TRUE
+--
+QueryStatement [0-82]
+  Query [0-82]
+    Select [0-82]
+      SelectList [7-16]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(a) [7-8]
+            Identifier(AlbumId) [9-16]
+      FromClause [17-59]
+        TablePathExpression [22-59]
+          PathExpression [22-28]
+            Identifier(Albums) [22-28]
+          Hint [29-57]
+            HintEntry [31-56]
+              Identifier(force_index) [31-42]
+              PathExpression [45-56]
+                Identifier(AlbumsIndex) [45-56]
+          Alias [58-59]
+            Identifier(a) [58-59]
+      WhereClause [60-82]
+        BinaryExpression(=) [66-82]
+          PathExpression [66-75]
+            Identifier(IsAwarded) [66-75]
+          BooleanLiteral(TRUE) [78-82]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_fulltext
+CREATE TABLE Albums (
+  SingerId INT64 NOT NULL,
+  AlbumId INT64 NOT NULL,
+  Title STRING(MAX),
+  Description STRING(MAX),
+  DescriptionTokens TOKENLIST AS (TOKENIZE_FULLTEXT(Description)) HIDDEN,
+  TitleTokens TOKENLIST AS (
+    TOKENIZE_FULLTEXT(Title, token_category=>"title")) HIDDEN
+) PRIMARY KEY (SingerId, AlbumId)
+--
+CreateTableStatement [0-321]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-289]
+    ColumnDefinition [24-47]
+      Identifier(SingerId) [24-32]
+      SimpleColumnSchema [33-47]
+        PathExpression [33-38]
+          Identifier(INT64) [33-38]
+        ColumnAttributeList [39-47]
+          NotNullColumnAttribute [39-47]
+    ColumnDefinition [51-73]
+      Identifier(AlbumId) [51-58]
+      SimpleColumnSchema [59-73]
+        PathExpression [59-64]
+          Identifier(INT64) [59-64]
+        ColumnAttributeList [65-73]
+          NotNullColumnAttribute [65-73]
+    ColumnDefinition [77-94]
+      Identifier(Title) [77-82]
+      SimpleColumnSchema [83-94]
+        PathExpression [83-89]
+          Identifier(STRING) [83-89]
+        TypeParameterList [89-93]
+          MaxLiteral() [90-93]
+    ColumnDefinition [98-121]
+      Identifier(Description) [98-109]
+      SimpleColumnSchema [110-121]
+        PathExpression [110-116]
+          Identifier(STRING) [110-116]
+        TypeParameterList [116-120]
+          MaxLiteral() [117-120]
+    ColumnDefinition [125-195]
+      Identifier(DescriptionTokens) [125-142]
+      SimpleColumnSchema [143-195]
+        PathExpression [143-152]
+          Identifier(TOKENLIST) [143-152]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [153-188]
+          FunctionCall [157-187]
+            PathExpression [157-174]
+              Identifier(TOKENIZE_FULLTEXT) [157-174]
+            PathExpression [175-186]
+              Identifier(Description) [175-186]
+        ColumnAttributeList [189-195]
+          HiddenColumnAttribute [189-195]
+    ColumnDefinition [199-287]
+      Identifier(TitleTokens) [199-210]
+      SimpleColumnSchema [211-287]
+        PathExpression [211-220]
+          Identifier(TOKENLIST) [211-220]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [221-280]
+          FunctionCall [230-279]
+            PathExpression [230-247]
+              Identifier(TOKENIZE_FULLTEXT) [230-247]
+            PathExpression [248-253]
+              Identifier(Title) [248-253]
+            NamedArgument [255-278]
+              Identifier(token_category) [255-269]
+              StringLiteral [271-278]
+                StringLiteralComponent("title") [271-278]
+        ColumnAttributeList [281-287]
+          HiddenColumnAttribute [281-287]
+  SpannerTableOptions [290-321]
+    PrimaryKey(ENFORCED) [290-321]
+      PrimaryKeyElementList [302-321]
+        PrimaryKeyElement(ASC) [303-311]
+          Identifier(SingerId) [303-311]
+        PrimaryKeyElement(ASC) [313-320]
+          Identifier(AlbumId) [313-320]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_fulltext
+-- DescriptionTokens is generated from the Description value, using the
+-- TOKENIZE_FULLTEXT function. For example, the following INSERT statement
+-- generates DescriptionTokens with the tokens ['rock', 'album']. TitleTokens
+-- will contain ['abbey', 'road'] and these tokens will be assigned to the
+-- "title" token category.
+INSERT INTO Albums (SingerId, AlbumId, Description) VALUES (1, 1, 'rock album')
+--
+InsertStatement [327-406]
+  PathExpression [339-345]
+    Identifier(Albums) [339-345]
+  ColumnList [346-378]
+    Identifier(SingerId) [347-355]
+    Identifier(AlbumId) [357-364]
+    Identifier(Description) [366-377]
+  InsertValuesRowList [379-406]
+    InsertValuesRow [386-406]
+      IntLiteral(1) [387-388]
+      IntLiteral(1) [390-391]
+      StringLiteral [393-405]
+        StringLiteralComponent('rock album') [393-405]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_fulltext
+-- Capitalization and delimiters are removed during tokenization. For example,
+-- the following INSERT statement generates DescriptionTokens with the tokens
+-- ['classical', 'albums'].
+INSERT INTO Albums (SingerId, AlbumId, Description) VALUES (1, 1, 'Classical, Albums.')
+--
+InsertStatement [185-272]
+  PathExpression [197-203]
+    Identifier(Albums) [197-203]
+  ColumnList [204-236]
+    Identifier(SingerId) [205-213]
+    Identifier(AlbumId) [215-222]
+    Identifier(Description) [224-235]
+  InsertValuesRowList [237-272]
+    InsertValuesRow [244-272]
+      IntLiteral(1) [245-246]
+      IntLiteral(1) [248-249]
+      StringLiteral [251-271]
+        StringLiteralComponent('Classical, Albums.') [251-271]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_json
+[mode=expression]
+TOKENIZE_JSON(value_to_tokenize)
+--
+FunctionCall [0-32]
+  PathExpression [0-13]
+    Identifier(TOKENIZE_JSON) [0-13]
+  PathExpression [14-31]
+    Identifier(value_to_tokenize) [14-31]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_json
+CREATE TABLE Albums (
+  SingerId INT64 NOT NULL,
+  AlbumId INT64 NOT NULL,
+  Metadata JSON,
+  MetadataTokens TOKENLIST AS (TOKENIZE_JSON(Metadata)) HIDDEN
+) PRIMARY KEY (SingerId, AlbumId)
+--
+CreateTableStatement [0-188]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-156]
+    ColumnDefinition [24-47]
+      Identifier(SingerId) [24-32]
+      SimpleColumnSchema [33-47]
+        PathExpression [33-38]
+          Identifier(INT64) [33-38]
+        ColumnAttributeList [39-47]
+          NotNullColumnAttribute [39-47]
+    ColumnDefinition [51-73]
+      Identifier(AlbumId) [51-58]
+      SimpleColumnSchema [59-73]
+        PathExpression [59-64]
+          Identifier(INT64) [59-64]
+        ColumnAttributeList [65-73]
+          NotNullColumnAttribute [65-73]
+    ColumnDefinition [77-90]
+      Identifier(Metadata) [77-85]
+      SimpleColumnSchema [86-90]
+        PathExpression [86-90]
+          Identifier(JSON) [86-90]
+    ColumnDefinition [94-154]
+      Identifier(MetadataTokens) [94-108]
+      SimpleColumnSchema [109-154]
+        PathExpression [109-118]
+          Identifier(TOKENLIST) [109-118]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [119-147]
+          FunctionCall [123-146]
+            PathExpression [123-136]
+              Identifier(TOKENIZE_JSON) [123-136]
+            PathExpression [137-145]
+              Identifier(Metadata) [137-145]
+        ColumnAttributeList [148-154]
+          HiddenColumnAttribute [148-154]
+  SpannerTableOptions [157-188]
+    PrimaryKey(ENFORCED) [157-188]
+      PrimaryKeyElementList [169-188]
+        PrimaryKeyElement(ASC) [170-178]
+          Identifier(SingerId) [170-178]
+        PrimaryKeyElement(ASC) [180-187]
+          Identifier(AlbumId) [180-187]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_json
+CREATE SEARCH INDEX AlbumsIndex ON Albums(MetadataTokens)
+--
+CreateIndexStatement(SEARCH) [0-57]
+  PathExpression [20-31]
+    Identifier(AlbumsIndex) [20-31]
+  PathExpression [35-41]
+    Identifier(Albums) [35-41]
+  IndexItemList [41-57]
+    OrderingExpression(ASC) [42-56]
+      PathExpression [42-56]
+        Identifier(MetadataTokens) [42-56]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_json
+-- Albums can be stored with varying metadata.
+INSERT INTO Albums (SingerId, AlbumId, Metadata)
+  VALUES (1, 1, JSON '{"AvailableFormats": ["vinyl", "cd"]}'),
+         (1, 2, JSON '{"ReissueDate": "1999-07-13", "MultiDiscCount": 2}'),
+         (1, 3, JSON '{"RegionalReleases": [{"Region": "Japan", "ReleaseDate": "2025-01-05"}]}')
+--
+InsertStatement [47-331]
+  PathExpression [59-65]
+    Identifier(Albums) [59-65]
+  ColumnList [66-95]
+    Identifier(SingerId) [67-75]
+    Identifier(AlbumId) [77-84]
+    Identifier(Metadata) [86-94]
+  InsertValuesRowList [98-331]
+    InsertValuesRow [105-157]
+      IntLiteral(1) [106-107]
+      IntLiteral(1) [109-110]
+      JSONLiteral [112-156]
+        StringLiteral [117-156]
+          StringLiteralComponent('{"AvailableFormats": ["vinyl", "cd"]}') [117-156]
+    InsertValuesRow [168-233]
+      IntLiteral(1) [169-170]
+      IntLiteral(2) [172-173]
+      JSONLiteral [175-232]
+        StringLiteral [180-232]
+          StringLiteralComponent('{"ReissueDate": "1999-07-13", "MultiDiscCount": 2}') [180-232]
+    InsertValuesRow [244-331]
+      IntLiteral(1) [245-246]
+      IntLiteral(3) [248-249]
+      JSONLiteral [251-330]
+        StringLiteral [256-330]
+          StringLiteralComponent('{"RegionalReleases": [{"Region": "Japan", "ReleaseDate": "2025-01-05"}]}') [256-330]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_json
+-- Query for albums with a multi-disc count of 2.
+SELECT a.AlbumId
+FROM Albums a
+WHERE a.Metadata.MultiDiscCount = 2
+--
+QueryStatement [50-116]
+  Query [50-116]
+    Select [50-116]
+      SelectList [57-66]
+        SelectColumn [57-66]
+          PathExpression [57-66]
+            Identifier(a) [57-58]
+            Identifier(AlbumId) [59-66]
+      FromClause [67-80]
+        TablePathExpression [72-80]
+          PathExpression [72-78]
+            Identifier(Albums) [72-78]
+          Alias [79-80]
+            Identifier(a) [79-80]
+      WhereClause [81-116]
+        BinaryExpression(=) [87-116]
+          PathExpression [87-112]
+            Identifier(a) [87-88]
+            Identifier(Metadata) [89-97]
+            Identifier(MultiDiscCount) [98-112]
+          IntLiteral(2) [115-116]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_json
+-- Query for albums available on vinyl.
+SELECT a.AlbumId
+FROM Albums a
+WHERE JSON_CONTAINS(a.Metadata, JSON '{"AvailableFormats": ["vinyl"]}')
+--
+QueryStatement [40-142]
+  Query [40-142]
+    Select [40-142]
+      SelectList [47-56]
+        SelectColumn [47-56]
+          PathExpression [47-56]
+            Identifier(a) [47-48]
+            Identifier(AlbumId) [49-56]
+      FromClause [57-70]
+        TablePathExpression [62-70]
+          PathExpression [62-68]
+            Identifier(Albums) [62-68]
+          Alias [69-70]
+            Identifier(a) [69-70]
+      WhereClause [71-142]
+        FunctionCall [77-142]
+          PathExpression [77-90]
+            Identifier(JSON_CONTAINS) [77-90]
+          PathExpression [91-101]
+            Identifier(a) [91-92]
+            Identifier(Metadata) [93-101]
+          JSONLiteral [103-141]
+            StringLiteral [108-141]
+              StringLiteralComponent('{"AvailableFormats": ["vinyl"]}') [108-141]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_json
+-- Query for albums with a regional release in Japan.
+SELECT a.AlbumId
+FROM Albums a
+WHERE JSON_CONTAINS(a.Metadata, JSON '{"RegionalReleases": [{"Region": "Japan"}]}')
+--
+QueryStatement [54-168]
+  Query [54-168]
+    Select [54-168]
+      SelectList [61-70]
+        SelectColumn [61-70]
+          PathExpression [61-70]
+            Identifier(a) [61-62]
+            Identifier(AlbumId) [63-70]
+      FromClause [71-84]
+        TablePathExpression [76-84]
+          PathExpression [76-82]
+            Identifier(Albums) [76-82]
+          Alias [83-84]
+            Identifier(a) [83-84]
+      WhereClause [85-168]
+        FunctionCall [91-168]
+          PathExpression [91-104]
+            Identifier(JSON_CONTAINS) [91-104]
+          PathExpression [105-115]
+            Identifier(a) [105-106]
+            Identifier(Metadata) [107-115]
+          JSONLiteral [117-167]
+            StringLiteral [122-167]
+              StringLiteralComponent('{"RegionalReleases": [{"Region": "Japan"}]}') [122-167]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_json
+-- Query for reissued albums (those with a reissue date).
+SELECT a.AlbumId
+FROM Albums a
+WHERE a.Metadata.ReissueDate IS NOT NULL
+--
+QueryStatement [58-129]
+  Query [58-129]
+    Select [58-129]
+      SelectList [65-74]
+        SelectColumn [65-74]
+          PathExpression [65-74]
+            Identifier(a) [65-66]
+            Identifier(AlbumId) [67-74]
+      FromClause [75-88]
+        TablePathExpression [80-88]
+          PathExpression [80-86]
+            Identifier(Albums) [80-86]
+          Alias [87-88]
+            Identifier(a) [87-88]
+      WhereClause [89-129]
+        BinaryExpression(IS NOT) [95-129]
+          PathExpression [95-117]
+            Identifier(a) [95-96]
+            Identifier(Metadata) [97-105]
+            Identifier(ReissueDate) [106-117]
+          NullLiteral(NULL) [125-129]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_ngrams
+CREATE TABLE Albums (
+  AlbumId INT64 NOT NULL,
+  Description STRING(MAX),
+  DescriptionNgramTokens TOKENLIST AS (TOKENIZE_NGRAMS(Description)) HIDDEN
+) PRIMARY KEY (AlbumId)
+--
+CreateTableStatement [0-174]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-152]
+    ColumnDefinition [24-46]
+      Identifier(AlbumId) [24-31]
+      SimpleColumnSchema [32-46]
+        PathExpression [32-37]
+          Identifier(INT64) [32-37]
+        ColumnAttributeList [38-46]
+          NotNullColumnAttribute [38-46]
+    ColumnDefinition [50-73]
+      Identifier(Description) [50-61]
+      SimpleColumnSchema [62-73]
+        PathExpression [62-68]
+          Identifier(STRING) [62-68]
+        TypeParameterList [68-72]
+          MaxLiteral() [69-72]
+    ColumnDefinition [77-150]
+      Identifier(DescriptionNgramTokens) [77-99]
+      SimpleColumnSchema [100-150]
+        PathExpression [100-109]
+          Identifier(TOKENLIST) [100-109]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [110-143]
+          FunctionCall [114-142]
+            PathExpression [114-129]
+              Identifier(TOKENIZE_NGRAMS) [114-129]
+            PathExpression [130-141]
+              Identifier(Description) [130-141]
+        ColumnAttributeList [144-150]
+          HiddenColumnAttribute [144-150]
+  SpannerTableOptions [153-174]
+    PrimaryKey(ENFORCED) [153-174]
+      PrimaryKeyElementList [165-174]
+        PrimaryKeyElement(ASC) [166-173]
+          Identifier(AlbumId) [166-173]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_ngrams
+CREATE SEARCH INDEX AlbumsIndex ON Albums(DescriptionNgramTokens)
+--
+CreateIndexStatement(SEARCH) [0-65]
+  PathExpression [20-31]
+    Identifier(AlbumsIndex) [20-31]
+  PathExpression [35-41]
+    Identifier(Albums) [35-41]
+  IndexItemList [41-65]
+    OrderingExpression(ASC) [42-64]
+      PathExpression [42-64]
+        Identifier(DescriptionNgramTokens) [42-64]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_ngrams
+INSERT INTO Albums (AlbumId, Description) VALUES (1, 'Heavy Metal')
+--
+InsertStatement [0-67]
+  PathExpression [12-18]
+    Identifier(Albums) [12-18]
+  ColumnList [19-41]
+    Identifier(AlbumId) [20-27]
+    Identifier(Description) [29-40]
+  InsertValuesRowList [42-67]
+    InsertValuesRow [49-67]
+      IntLiteral(1) [50-51]
+      StringLiteral [53-66]
+        StringLiteralComponent('Heavy Metal') [53-66]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_number
+CREATE TABLE Albums (
+  SingerId INT64 NOT NULL,
+  AlbumId INT64 NOT NULL,
+  Rating INT64,
+  RatingTokens TOKENLIST AS (TOKENIZE_NUMBER(Rating)) HIDDEN,
+  TrackRating ARRAY,
+  TrackRatingTokens TOKENLIST AS (TOKENIZE_NUMBER(TrackRating)) HIDDEN
+) PRIMARY KEY (SingerId, AlbumId)
+--
+CreateTableStatement [0-285]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-253]
+    ColumnDefinition [24-47]
+      Identifier(SingerId) [24-32]
+      SimpleColumnSchema [33-47]
+        PathExpression [33-38]
+          Identifier(INT64) [33-38]
+        ColumnAttributeList [39-47]
+          NotNullColumnAttribute [39-47]
+    ColumnDefinition [51-73]
+      Identifier(AlbumId) [51-58]
+      SimpleColumnSchema [59-73]
+        PathExpression [59-64]
+          Identifier(INT64) [59-64]
+        ColumnAttributeList [65-73]
+          NotNullColumnAttribute [65-73]
+    ColumnDefinition [77-89]
+      Identifier(Rating) [77-83]
+      SimpleColumnSchema [84-89]
+        PathExpression [84-89]
+          Identifier(INT64) [84-89]
+    ColumnDefinition [93-151]
+      Identifier(RatingTokens) [93-105]
+      SimpleColumnSchema [106-151]
+        PathExpression [106-115]
+          Identifier(TOKENLIST) [106-115]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [116-144]
+          FunctionCall [120-143]
+            PathExpression [120-135]
+              Identifier(TOKENIZE_NUMBER) [120-135]
+            PathExpression [136-142]
+              Identifier(Rating) [136-142]
+        ColumnAttributeList [145-151]
+          HiddenColumnAttribute [145-151]
+    ColumnDefinition [155-179]
+      Identifier(TrackRating) [155-166]
+      ArrayColumnSchema [167-179]
+        SimpleColumnSchema [173-178]
+          PathExpression [173-178]
+            Identifier(INT64) [173-178]
+    ColumnDefinition [183-251]
+      Identifier(TrackRatingTokens) [183-200]
+      SimpleColumnSchema [201-251]
+        PathExpression [201-210]
+          Identifier(TOKENLIST) [201-210]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [211-244]
+          FunctionCall [215-243]
+            PathExpression [215-230]
+              Identifier(TOKENIZE_NUMBER) [215-230]
+            PathExpression [231-242]
+              Identifier(TrackRating) [231-242]
+        ColumnAttributeList [245-251]
+          HiddenColumnAttribute [245-251]
+  SpannerTableOptions [254-285]
+    PrimaryKey(ENFORCED) [254-285]
+      PrimaryKeyElementList [266-285]
+        PrimaryKeyElement(ASC) [267-275]
+          Identifier(SingerId) [267-275]
+        PrimaryKeyElement(ASC) [277-284]
+          Identifier(AlbumId) [277-284]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_number
+CREATE SEARCH INDEX AlbumsIndex ON Albums(RatingTokens, TrackRatingTokens)
+--
+CreateIndexStatement(SEARCH) [0-74]
+  PathExpression [20-31]
+    Identifier(AlbumsIndex) [20-31]
+  PathExpression [35-41]
+    Identifier(Albums) [35-41]
+  IndexItemList [41-74]
+    OrderingExpression(ASC) [42-54]
+      PathExpression [42-54]
+        Identifier(RatingTokens) [42-54]
+    OrderingExpression(ASC) [56-73]
+      PathExpression [56-73]
+        Identifier(TrackRatingTokens) [56-73]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_number
+-- RatingTokens and TrackRatingTokens are generated from Rating and TrackRating
+-- values, respectively, using the TOKENIZE_NUMBER function.
+INSERT INTO Albums (SingerId, AlbumId, Rating, TrackRating) VALUES (1, 1, 2, [2, 3])
+--
+InsertStatement [141-225]
+  PathExpression [153-159]
+    Identifier(Albums) [153-159]
+  ColumnList [160-200]
+    Identifier(SingerId) [161-169]
+    Identifier(AlbumId) [171-178]
+    Identifier(Rating) [180-186]
+    Identifier(TrackRating) [188-199]
+  InsertValuesRowList [201-225]
+    InsertValuesRow [208-225]
+      IntLiteral(1) [209-210]
+      IntLiteral(1) [212-213]
+      IntLiteral(2) [215-216]
+      ArrayConstructor [218-224]
+        IntLiteral(2) [219-220]
+        IntLiteral(3) [222-223]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_number
+INSERT INTO Albums (SingerId, AlbumId, Rating, TrackRating) VALUES (1, 2, 5, [3, 5])
+--
+InsertStatement [0-84]
+  PathExpression [12-18]
+    Identifier(Albums) [12-18]
+  ColumnList [19-59]
+    Identifier(SingerId) [20-28]
+    Identifier(AlbumId) [30-37]
+    Identifier(Rating) [39-45]
+    Identifier(TrackRating) [47-58]
+  InsertValuesRowList [60-84]
+    InsertValuesRow [67-84]
+      IntLiteral(1) [68-69]
+      IntLiteral(2) [71-72]
+      IntLiteral(5) [74-75]
+      ArrayConstructor [77-83]
+        IntLiteral(3) [78-79]
+        IntLiteral(5) [81-82]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_number
+SELECT a.AlbumId
+FROM Albums @{force_index = AlbumsIndex} a
+WHERE a.Rating = 5
+--
+QueryStatement [0-78]
+  Query [0-78]
+    Select [0-78]
+      SelectList [7-16]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(a) [7-8]
+            Identifier(AlbumId) [9-16]
+      FromClause [17-59]
+        TablePathExpression [22-59]
+          PathExpression [22-28]
+            Identifier(Albums) [22-28]
+          Hint [29-57]
+            HintEntry [31-56]
+              Identifier(force_index) [31-42]
+              PathExpression [45-56]
+                Identifier(AlbumsIndex) [45-56]
+          Alias [58-59]
+            Identifier(a) [58-59]
+      WhereClause [60-78]
+        BinaryExpression(=) [66-78]
+          PathExpression [66-74]
+            Identifier(a) [66-67]
+            Identifier(Rating) [68-74]
+          IntLiteral(5) [77-78]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_number
+SELECT a.AlbumId
+FROM Albums a
+WHERE ARRAY_INCLUDES_ALL(a.TrackRating, [2, 3])
+--
+QueryStatement [0-78]
+  Query [0-78]
+    Select [0-78]
+      SelectList [7-16]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(a) [7-8]
+            Identifier(AlbumId) [9-16]
+      FromClause [17-30]
+        TablePathExpression [22-30]
+          PathExpression [22-28]
+            Identifier(Albums) [22-28]
+          Alias [29-30]
+            Identifier(a) [29-30]
+      WhereClause [31-78]
+        FunctionCall [37-78]
+          PathExpression [37-55]
+            Identifier(ARRAY_INCLUDES_ALL) [37-55]
+          PathExpression [56-69]
+            Identifier(a) [56-57]
+            Identifier(TrackRating) [58-69]
+          ArrayConstructor [71-77]
+            IntLiteral(2) [72-73]
+            IntLiteral(3) [75-76]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_number
+SELECT a.AlbumId
+FROM Albums a
+WHERE ARRAY_INCLUDES_ANY(a.TrackRating, [3, 4, 5])
+--
+QueryStatement [0-81]
+  Query [0-81]
+    Select [0-81]
+      SelectList [7-16]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(a) [7-8]
+            Identifier(AlbumId) [9-16]
+      FromClause [17-30]
+        TablePathExpression [22-30]
+          PathExpression [22-28]
+            Identifier(Albums) [22-28]
+          Alias [29-30]
+            Identifier(a) [29-30]
+      WhereClause [31-81]
+        FunctionCall [37-81]
+          PathExpression [37-55]
+            Identifier(ARRAY_INCLUDES_ANY) [37-55]
+          PathExpression [56-69]
+            Identifier(a) [56-57]
+            Identifier(TrackRating) [58-69]
+          ArrayConstructor [71-80]
+            IntLiteral(3) [72-73]
+            IntLiteral(4) [75-76]
+            IntLiteral(5) [78-79]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_number
+SELECT a.AlbumId
+FROM Albums a
+WHERE a.Rating >= 2
+--
+QueryStatement [0-50]
+  Query [0-50]
+    Select [0-50]
+      SelectList [7-16]
+        SelectColumn [7-16]
+          PathExpression [7-16]
+            Identifier(a) [7-8]
+            Identifier(AlbumId) [9-16]
+      FromClause [17-30]
+        TablePathExpression [22-30]
+          PathExpression [22-28]
+            Identifier(Albums) [22-28]
+          Alias [29-30]
+            Identifier(a) [29-30]
+      WhereClause [31-50]
+        BinaryExpression(>=) [37-50]
+          PathExpression [37-45]
+            Identifier(a) [37-38]
+            Identifier(Rating) [39-45]
+          IntLiteral(2) [49-50]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_substring
+CREATE TABLE Albums (
+  SingerId INT64 NOT NULL,
+  AlbumId INT64 NOT NULL,
+  Description STRING(MAX),
+  DescriptionSubstrTokens TOKENLIST
+    AS (TOKENIZE_SUBSTRING(Description, ngram_size_min=>1, ngram_size_max=>4)) HIDDEN
+) PRIMARY KEY (SingerId, AlbumId)
+--
+CreateTableStatement [0-257]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-225]
+    ColumnDefinition [24-47]
+      Identifier(SingerId) [24-32]
+      SimpleColumnSchema [33-47]
+        PathExpression [33-38]
+          Identifier(INT64) [33-38]
+        ColumnAttributeList [39-47]
+          NotNullColumnAttribute [39-47]
+    ColumnDefinition [51-73]
+      Identifier(AlbumId) [51-58]
+      SimpleColumnSchema [59-73]
+        PathExpression [59-64]
+          Identifier(INT64) [59-64]
+        ColumnAttributeList [65-73]
+          NotNullColumnAttribute [65-73]
+    ColumnDefinition [77-100]
+      Identifier(Description) [77-88]
+      SimpleColumnSchema [89-100]
+        PathExpression [89-95]
+          Identifier(STRING) [89-95]
+        TypeParameterList [95-99]
+          MaxLiteral() [96-99]
+    ColumnDefinition [104-223]
+      Identifier(DescriptionSubstrTokens) [104-127]
+      SimpleColumnSchema [128-223]
+        PathExpression [128-137]
+          Identifier(TOKENLIST) [128-137]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [142-216]
+          FunctionCall [146-215]
+            PathExpression [146-164]
+              Identifier(TOKENIZE_SUBSTRING) [146-164]
+            PathExpression [165-176]
+              Identifier(Description) [165-176]
+            NamedArgument [178-195]
+              Identifier(ngram_size_min) [178-192]
+              IntLiteral(1) [194-195]
+            NamedArgument [197-214]
+              Identifier(ngram_size_max) [197-211]
+              IntLiteral(4) [213-214]
+        ColumnAttributeList [217-223]
+          HiddenColumnAttribute [217-223]
+  SpannerTableOptions [226-257]
+    PrimaryKey(ENFORCED) [226-257]
+      PrimaryKeyElementList [238-257]
+        PrimaryKeyElement(ASC) [239-247]
+          Identifier(SingerId) [239-247]
+        PrimaryKeyElement(ASC) [249-256]
+          Identifier(AlbumId) [249-256]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenize_substring
+INSERT INTO Albums (SingerId, AlbumId, Description)
+  VALUES (1, 1, 'Heavy Metal')
+--
+InsertStatement [0-82]
+  PathExpression [12-18]
+    Identifier(Albums) [12-18]
+  ColumnList [19-51]
+    Identifier(SingerId) [20-28]
+    Identifier(AlbumId) [30-37]
+    Identifier(Description) [39-50]
+  InsertValuesRowList [54-82]
+    InsertValuesRow [61-82]
+      IntLiteral(1) [62-63]
+      IntLiteral(1) [65-66]
+      StringLiteral [68-81]
+        StringLiteralComponent('Heavy Metal') [68-81]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenlist_concat
+CREATE TABLE Albums (
+  SingerId INT64 NOT NULL,
+  AlbumId INT64 NOT NULL,
+  SingerName STRING(MAX),
+  SingerNameTokens TOKENLIST AS (TOKENIZE_FULLTEXT(SingerName)) HIDDEN,
+  AlbumName STRING(MAX),
+  AlbumNameTokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumName)) HIDDEN,
+  SingerOrAlbumNameTokens TOKENLIST AS (TOKENLIST_CONCAT([SingerNameTokens, AlbumNameTokens])) HIDDEN
+) PRIMARY KEY (SingerId, AlbumId)
+--
+CreateTableStatement [0-403]
+  PathExpression [13-19]
+    Identifier(Albums) [13-19]
+  TableElementList [20-371]
+    ColumnDefinition [24-47]
+      Identifier(SingerId) [24-32]
+      SimpleColumnSchema [33-47]
+        PathExpression [33-38]
+          Identifier(INT64) [33-38]
+        ColumnAttributeList [39-47]
+          NotNullColumnAttribute [39-47]
+    ColumnDefinition [51-73]
+      Identifier(AlbumId) [51-58]
+      SimpleColumnSchema [59-73]
+        PathExpression [59-64]
+          Identifier(INT64) [59-64]
+        ColumnAttributeList [65-73]
+          NotNullColumnAttribute [65-73]
+    ColumnDefinition [77-99]
+      Identifier(SingerName) [77-87]
+      SimpleColumnSchema [88-99]
+        PathExpression [88-94]
+          Identifier(STRING) [88-94]
+        TypeParameterList [94-98]
+          MaxLiteral() [95-98]
+    ColumnDefinition [103-171]
+      Identifier(SingerNameTokens) [103-119]
+      SimpleColumnSchema [120-171]
+        PathExpression [120-129]
+          Identifier(TOKENLIST) [120-129]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [130-164]
+          FunctionCall [134-163]
+            PathExpression [134-151]
+              Identifier(TOKENIZE_FULLTEXT) [134-151]
+            PathExpression [152-162]
+              Identifier(SingerName) [152-162]
+        ColumnAttributeList [165-171]
+          HiddenColumnAttribute [165-171]
+    ColumnDefinition [175-196]
+      Identifier(AlbumName) [175-184]
+      SimpleColumnSchema [185-196]
+        PathExpression [185-191]
+          Identifier(STRING) [185-191]
+        TypeParameterList [191-195]
+          MaxLiteral() [192-195]
+    ColumnDefinition [200-266]
+      Identifier(AlbumNameTokens) [200-215]
+      SimpleColumnSchema [216-266]
+        PathExpression [216-225]
+          Identifier(TOKENLIST) [216-225]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [226-259]
+          FunctionCall [230-258]
+            PathExpression [230-247]
+              Identifier(TOKENIZE_FULLTEXT) [230-247]
+            PathExpression [248-257]
+              Identifier(AlbumName) [248-257]
+        ColumnAttributeList [260-266]
+          HiddenColumnAttribute [260-266]
+    ColumnDefinition [270-369]
+      Identifier(SingerOrAlbumNameTokens) [270-293]
+      SimpleColumnSchema [294-369]
+        PathExpression [294-303]
+          Identifier(TOKENLIST) [294-303]
+        GeneratedColumnInfo(generated_mode=ALWAYS) [304-362]
+          FunctionCall [308-361]
+            PathExpression [308-324]
+              Identifier(TOKENLIST_CONCAT) [308-324]
+            ArrayConstructor [325-360]
+              PathExpression [326-342]
+                Identifier(SingerNameTokens) [326-342]
+              PathExpression [344-359]
+                Identifier(AlbumNameTokens) [344-359]
+        ColumnAttributeList [363-369]
+          HiddenColumnAttribute [363-369]
+  SpannerTableOptions [372-403]
+    PrimaryKey(ENFORCED) [372-403]
+      PrimaryKeyElementList [384-403]
+        PrimaryKeyElement(ASC) [385-393]
+          Identifier(SingerId) [385-393]
+        PrimaryKeyElement(ASC) [395-402]
+          Identifier(AlbumId) [395-402]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenlist_concat
+CREATE SEARCH INDEX AlbumsIndex ON Albums(SingerNameTokens, AlbumNameTokens, SingerOrAlbumNameTokens)
+--
+CreateIndexStatement(SEARCH) [0-101]
+  PathExpression [20-31]
+    Identifier(AlbumsIndex) [20-31]
+  PathExpression [35-41]
+    Identifier(Albums) [35-41]
+  IndexItemList [41-101]
+    OrderingExpression(ASC) [42-58]
+      PathExpression [42-58]
+        Identifier(SingerNameTokens) [42-58]
+    OrderingExpression(ASC) [60-75]
+      PathExpression [60-75]
+        Identifier(AlbumNameTokens) [60-75]
+    OrderingExpression(ASC) [77-100]
+      PathExpression [77-100]
+        Identifier(SingerOrAlbumNameTokens) [77-100]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenlist_concat
+-- The INSERT statement below generates SingerOrAlbumNameTokens by concatenating
+-- all the tokens in SingerNameTokens and AlbumNameTokens.
+INSERT INTO Albums (SingerId, AlbumId, SingerName, AlbumName) VALUES (1, 1, 'Alice Trentor', 'Go Go Go')
+--
+InsertStatement [140-244]
+  PathExpression [152-158]
+    Identifier(Albums) [152-158]
+  ColumnList [159-201]
+    Identifier(SingerId) [160-168]
+    Identifier(AlbumId) [170-177]
+    Identifier(SingerName) [179-189]
+    Identifier(AlbumName) [191-200]
+  InsertValuesRowList [202-244]
+    InsertValuesRow [209-244]
+      IntLiteral(1) [210-211]
+      IntLiteral(1) [213-214]
+      StringLiteral [216-231]
+        StringLiteralComponent('Alice Trentor') [216-231]
+      StringLiteral [233-243]
+        StringLiteralComponent('Go Go Go') [233-243]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenlist_concat
+INSERT INTO Albums (SingerId, AlbumId, SingerName, AlbumName) VALUES (2, 1, 'Catalina Smith', 'Alice Wonderland')
+--
+InsertStatement [0-113]
+  PathExpression [12-18]
+    Identifier(Albums) [12-18]
+  ColumnList [19-61]
+    Identifier(SingerId) [20-28]
+    Identifier(AlbumId) [30-37]
+    Identifier(SingerName) [39-49]
+    Identifier(AlbumName) [51-60]
+  InsertValuesRowList [62-113]
+    InsertValuesRow [69-113]
+      IntLiteral(2) [70-71]
+      IntLiteral(1) [73-74]
+      StringLiteral [76-92]
+        StringLiteralComponent('Catalina Smith') [76-92]
+      StringLiteral [94-112]
+        StringLiteralComponent('Alice Wonderland') [94-112]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenlist_concat
+SELECT a.SingerId, a.AlbumId
+FROM Albums a
+WHERE SEARCH(a.SingerOrAlbumNameTokens, 'alice')
+--
+QueryStatement [0-91]
+  Query [0-91]
+    Select [0-91]
+      SelectList [7-28]
+        SelectColumn [7-17]
+          PathExpression [7-17]
+            Identifier(a) [7-8]
+            Identifier(SingerId) [9-17]
+        SelectColumn [19-28]
+          PathExpression [19-28]
+            Identifier(a) [19-20]
+            Identifier(AlbumId) [21-28]
+      FromClause [29-42]
+        TablePathExpression [34-42]
+          PathExpression [34-40]
+            Identifier(Albums) [34-40]
+          Alias [41-42]
+            Identifier(a) [41-42]
+      WhereClause [43-91]
+        FunctionCall [49-91]
+          PathExpression [49-55]
+            Identifier(SEARCH) [49-55]
+          PathExpression [56-81]
+            Identifier(a) [56-57]
+            Identifier(SingerOrAlbumNameTokens) [58-81]
+          StringLiteral [83-90]
+            StringLiteralComponent('alice') [83-90]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/search_functions#tokenlist_concat
+SELECT a.SingerId, a.AlbumId
+FROM Albums a
+WHERE SEARCH(TOKENLIST_CONCAT([a.SingerNameTokens, a.AlbumNameTokens]), 'alice')
+--
+QueryStatement [0-123]
+  Query [0-123]
+    Select [0-123]
+      SelectList [7-28]
+        SelectColumn [7-17]
+          PathExpression [7-17]
+            Identifier(a) [7-8]
+            Identifier(SingerId) [9-17]
+        SelectColumn [19-28]
+          PathExpression [19-28]
+            Identifier(a) [19-20]
+            Identifier(AlbumId) [21-28]
+      FromClause [29-42]
+        TablePathExpression [34-42]
+          PathExpression [34-40]
+            Identifier(Albums) [34-40]
+          Alias [41-42]
+            Identifier(a) [41-42]
+      WhereClause [43-123]
+        FunctionCall [49-123]
+          PathExpression [49-55]
+            Identifier(SEARCH) [49-55]
+          FunctionCall [56-113]
+            PathExpression [56-72]
+              Identifier(TOKENLIST_CONCAT) [56-72]
+            ArrayConstructor [73-112]
+              PathExpression [74-92]
+                Identifier(a) [74-75]
+                Identifier(SingerNameTokens) [76-92]
+              PathExpression [94-111]
+                Identifier(a) [94-95]
+                Identifier(AlbumNameTokens) [96-111]
+          StringLiteral [115-122]
+            StringLiteralComponent('alice') [115-122]
diff --git a/parser/testdata/spanner/security_functions.test b/parser/testdata/spanner/security_functions.test
new file mode 100644
index 0000000..e1c5faf
--- /dev/null
+++ b/parser/testdata/spanner/security_functions.test
@@ -0,0 +1,27 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/security_functions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/security_functions#session_user
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+SESSION_USER()
+--
+FunctionCall [0-14]
+  PathExpression [0-12]
+    Identifier(SESSION_USER) [0-12]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/security_functions#session_user
+SELECT SESSION_USER() as user
+--
+QueryStatement [0-29]
+  Query [0-29]
+    Select [0-29]
+      SelectList [7-29]
+        SelectColumn [7-29]
+          FunctionCall [7-21]
+            PathExpression [7-19]
+              Identifier(SESSION_USER) [7-19]
+          Alias [22-29]
+            Identifier(user) [25-29]
diff --git a/parser/testdata/spanner/sequence_functions.test b/parser/testdata/spanner/sequence_functions.test
new file mode 100644
index 0000000..3d378fc
--- /dev/null
+++ b/parser/testdata/spanner/sequence_functions.test
@@ -0,0 +1,127 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/sequence_functions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/sequence_functions#get_internal_sequence_state
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+GET_INTERNAL_SEQUENCE_STATE(SEQUENCE sequence_identifier)
+--
+FunctionCall [0-57]
+  PathExpression [0-27]
+    Identifier(GET_INTERNAL_SEQUENCE_STATE) [0-27]
+  SequenceArg [28-56]
+    PathExpression [37-56]
+      Identifier(sequence_identifier) [37-56]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/sequence_functions#get_internal_sequence_state
+SELECT GET_NEXT_SEQUENCE_VALUE(SEQUENCE MySequence) AS next_value
+--
+QueryStatement [0-65]
+  Query [0-65]
+    Select [0-65]
+      SelectList [7-65]
+        SelectColumn [7-65]
+          FunctionCall [7-51]
+            PathExpression [7-30]
+              Identifier(GET_NEXT_SEQUENCE_VALUE) [7-30]
+            SequenceArg [31-50]
+              PathExpression [40-50]
+                Identifier(MySequence) [40-50]
+          Alias [52-65]
+            Identifier(next_value) [55-65]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/sequence_functions#get_internal_sequence_state
+SELECT GET_INTERNAL_SEQUENCE_STATE(SEQUENCE MySequence) AS sequence_state
+--
+QueryStatement [0-73]
+  Query [0-73]
+    Select [0-73]
+      SelectList [7-73]
+        SelectColumn [7-73]
+          FunctionCall [7-55]
+            PathExpression [7-34]
+              Identifier(GET_INTERNAL_SEQUENCE_STATE) [7-34]
+            SequenceArg [35-54]
+              PathExpression [44-54]
+                Identifier(MySequence) [44-54]
+          Alias [56-73]
+            Identifier(sequence_state) [59-73]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/sequence_functions#get_next_sequence_value
+[mode=expression]
+GET_NEXT_SEQUENCE_VALUE(SEQUENCE sequence_identifier)
+--
+FunctionCall [0-53]
+  PathExpression [0-23]
+    Identifier(GET_NEXT_SEQUENCE_VALUE) [0-23]
+  SequenceArg [24-52]
+    PathExpression [33-52]
+      Identifier(sequence_identifier) [33-52]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/sequence_functions#get_next_sequence_value
+CREATE TABLE Singers (
+  SingerId INT64 DEFAULT (GET_NEXT_SEQUENCE_VALUE(SEQUENCE MySequence)),
+  a STRING(MAX),
+) PRIMARY KEY (SingerId)
+--
+CreateTableStatement [0-137]
+  PathExpression [13-20]
+    Identifier(Singers) [13-20]
+  TableElementList [21-114]
+    ColumnDefinition [25-94]
+      Identifier(SingerId) [25-33]
+      SimpleColumnSchema [34-94]
+        PathExpression [34-39]
+          Identifier(INT64) [34-39]
+        FunctionCall [49-93]
+          PathExpression [49-72]
+            Identifier(GET_NEXT_SEQUENCE_VALUE) [49-72]
+          SequenceArg [73-92]
+            PathExpression [82-92]
+              Identifier(MySequence) [82-92]
+    ColumnDefinition [98-111]
+      Identifier(a) [98-99]
+      SimpleColumnSchema [100-111]
+        PathExpression [100-106]
+          Identifier(STRING) [100-106]
+        TypeParameterList [106-110]
+          MaxLiteral() [107-110]
+  SpannerTableOptions [115-137]
+    PrimaryKey(ENFORCED) [115-137]
+      PrimaryKeyElementList [127-137]
+        PrimaryKeyElement(ASC) [128-136]
+          Identifier(SingerId) [128-136]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/sequence_functions#get_next_sequence_value
+SELECT GET_NEXT_SEQUENCE_VALUE(SEQUENCE MySequence) as next_id
+--
+QueryStatement [0-62]
+  Query [0-62]
+    Select [0-62]
+      SelectList [7-62]
+        SelectColumn [7-62]
+          FunctionCall [7-51]
+            PathExpression [7-30]
+              Identifier(GET_NEXT_SEQUENCE_VALUE) [7-30]
+            SequenceArg [31-50]
+              PathExpression [40-50]
+                Identifier(MySequence) [40-50]
+          Alias [52-62]
+            Identifier(next_id) [55-62]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/sequence_functions#get_next_sequence_value
+INSERT INTO Singers(SingerId, a) VALUES (next_id, 1)
+--
+InsertStatement [0-52]
+  PathExpression [12-19]
+    Identifier(Singers) [12-19]
+  ColumnList [19-32]
+    Identifier(SingerId) [20-28]
+    Identifier(a) [30-31]
+  InsertValuesRowList [33-52]
+    InsertValuesRow [40-52]
+      PathExpression [41-48]
+        Identifier(next_id) [41-48]
+      IntLiteral(1) [50-51]
diff --git a/parser/testdata/spanner/statistical_aggregate_functions.test b/parser/testdata/spanner/statistical_aggregate_functions.test
new file mode 100644
index 0000000..191582c
--- /dev/null
+++ b/parser/testdata/spanner/statistical_aggregate_functions.test
@@ -0,0 +1,269 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/statistical_aggregate_functions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/statistical_aggregate_functions#stddev_samp
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT STDDEV_SAMP(x) AS results FROM UNNEST([10, 14, 18]) AS x
+--
+QueryStatement [0-63]
+  Query [0-63]
+    Select [0-63]
+      SelectList [7-32]
+        SelectColumn [7-32]
+          FunctionCall [7-21]
+            PathExpression [7-18]
+              Identifier(STDDEV_SAMP) [7-18]
+            PathExpression [19-20]
+              Identifier(x) [19-20]
+          Alias [22-32]
+            Identifier(results) [25-32]
+      FromClause [33-63]
+        TablePathExpression [38-63]
+          UnnestExpression [38-58]
+            ExpressionWithOptAlias [45-57]
+              ArrayConstructor [45-57]
+                IntLiteral(10) [46-48]
+                IntLiteral(14) [50-52]
+                IntLiteral(18) [54-56]
+          Alias [59-63]
+            Identifier(x) [62-63]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/statistical_aggregate_functions#stddev_samp
+SELECT STDDEV_SAMP(x) AS results FROM UNNEST([10, 14, NULL]) AS x
+--
+QueryStatement [0-65]
+  Query [0-65]
+    Select [0-65]
+      SelectList [7-32]
+        SelectColumn [7-32]
+          FunctionCall [7-21]
+            PathExpression [7-18]
+              Identifier(STDDEV_SAMP) [7-18]
+            PathExpression [19-20]
+              Identifier(x) [19-20]
+          Alias [22-32]
+            Identifier(results) [25-32]
+      FromClause [33-65]
+        TablePathExpression [38-65]
+          UnnestExpression [38-60]
+            ExpressionWithOptAlias [45-59]
+              ArrayConstructor [45-59]
+                IntLiteral(10) [46-48]
+                IntLiteral(14) [50-52]
+                NullLiteral(NULL) [54-58]
+          Alias [61-65]
+            Identifier(x) [64-65]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/statistical_aggregate_functions#stddev_samp
+SELECT STDDEV_SAMP(x) AS results FROM UNNEST([10, NULL]) AS x
+--
+QueryStatement [0-61]
+  Query [0-61]
+    Select [0-61]
+      SelectList [7-32]
+        SelectColumn [7-32]
+          FunctionCall [7-21]
+            PathExpression [7-18]
+              Identifier(STDDEV_SAMP) [7-18]
+            PathExpression [19-20]
+              Identifier(x) [19-20]
+          Alias [22-32]
+            Identifier(results) [25-32]
+      FromClause [33-61]
+        TablePathExpression [38-61]
+          UnnestExpression [38-56]
+            ExpressionWithOptAlias [45-55]
+              ArrayConstructor [45-55]
+                IntLiteral(10) [46-48]
+                NullLiteral(NULL) [50-54]
+          Alias [57-61]
+            Identifier(x) [60-61]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/statistical_aggregate_functions#stddev_samp
+SELECT STDDEV_SAMP(x) AS results FROM UNNEST([NULL]) AS x
+--
+QueryStatement [0-57]
+  Query [0-57]
+    Select [0-57]
+      SelectList [7-32]
+        SelectColumn [7-32]
+          FunctionCall [7-21]
+            PathExpression [7-18]
+              Identifier(STDDEV_SAMP) [7-18]
+            PathExpression [19-20]
+              Identifier(x) [19-20]
+          Alias [22-32]
+            Identifier(results) [25-32]
+      FromClause [33-57]
+        TablePathExpression [38-57]
+          UnnestExpression [38-52]
+            ExpressionWithOptAlias [45-51]
+              ArrayConstructor [45-51]
+                NullLiteral(NULL) [46-50]
+          Alias [53-57]
+            Identifier(x) [56-57]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/statistical_aggregate_functions#stddev_samp
+SELECT STDDEV_SAMP(x) AS results FROM UNNEST([10, 14, CAST('Infinity' as FLOAT64)]) AS x
+--
+QueryStatement [0-88]
+  Query [0-88]
+    Select [0-88]
+      SelectList [7-32]
+        SelectColumn [7-32]
+          FunctionCall [7-21]
+            PathExpression [7-18]
+              Identifier(STDDEV_SAMP) [7-18]
+            PathExpression [19-20]
+              Identifier(x) [19-20]
+          Alias [22-32]
+            Identifier(results) [25-32]
+      FromClause [33-88]
+        TablePathExpression [38-88]
+          UnnestExpression [38-83]
+            ExpressionWithOptAlias [45-82]
+              ArrayConstructor [45-82]
+                IntLiteral(10) [46-48]
+                IntLiteral(14) [50-52]
+                CastExpression [54-81]
+                  StringLiteral [59-69]
+                    StringLiteralComponent('Infinity') [59-69]
+                  SimpleType [73-80]
+                    PathExpression [73-80]
+                      Identifier(FLOAT64) [73-80]
+          Alias [84-88]
+            Identifier(x) [87-88]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/statistical_aggregate_functions#var_samp
+SELECT VAR_SAMP(x) AS results FROM UNNEST([10, 14, 18]) AS x
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-60]
+      SelectList [7-29]
+        SelectColumn [7-29]
+          FunctionCall [7-18]
+            PathExpression [7-15]
+              Identifier(VAR_SAMP) [7-15]
+            PathExpression [16-17]
+              Identifier(x) [16-17]
+          Alias [19-29]
+            Identifier(results) [22-29]
+      FromClause [30-60]
+        TablePathExpression [35-60]
+          UnnestExpression [35-55]
+            ExpressionWithOptAlias [42-54]
+              ArrayConstructor [42-54]
+                IntLiteral(10) [43-45]
+                IntLiteral(14) [47-49]
+                IntLiteral(18) [51-53]
+          Alias [56-60]
+            Identifier(x) [59-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/statistical_aggregate_functions#var_samp
+SELECT VAR_SAMP(x) AS results FROM UNNEST([10, 14, NULL]) AS x
+--
+QueryStatement [0-62]
+  Query [0-62]
+    Select [0-62]
+      SelectList [7-29]
+        SelectColumn [7-29]
+          FunctionCall [7-18]
+            PathExpression [7-15]
+              Identifier(VAR_SAMP) [7-15]
+            PathExpression [16-17]
+              Identifier(x) [16-17]
+          Alias [19-29]
+            Identifier(results) [22-29]
+      FromClause [30-62]
+        TablePathExpression [35-62]
+          UnnestExpression [35-57]
+            ExpressionWithOptAlias [42-56]
+              ArrayConstructor [42-56]
+                IntLiteral(10) [43-45]
+                IntLiteral(14) [47-49]
+                NullLiteral(NULL) [51-55]
+          Alias [58-62]
+            Identifier(x) [61-62]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/statistical_aggregate_functions#var_samp
+SELECT VAR_SAMP(x) AS results FROM UNNEST([10, NULL]) AS x
+--
+QueryStatement [0-58]
+  Query [0-58]
+    Select [0-58]
+      SelectList [7-29]
+        SelectColumn [7-29]
+          FunctionCall [7-18]
+            PathExpression [7-15]
+              Identifier(VAR_SAMP) [7-15]
+            PathExpression [16-17]
+              Identifier(x) [16-17]
+          Alias [19-29]
+            Identifier(results) [22-29]
+      FromClause [30-58]
+        TablePathExpression [35-58]
+          UnnestExpression [35-53]
+            ExpressionWithOptAlias [42-52]
+              ArrayConstructor [42-52]
+                IntLiteral(10) [43-45]
+                NullLiteral(NULL) [47-51]
+          Alias [54-58]
+            Identifier(x) [57-58]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/statistical_aggregate_functions#var_samp
+SELECT VAR_SAMP(x) AS results FROM UNNEST([NULL]) AS x
+--
+QueryStatement [0-54]
+  Query [0-54]
+    Select [0-54]
+      SelectList [7-29]
+        SelectColumn [7-29]
+          FunctionCall [7-18]
+            PathExpression [7-15]
+              Identifier(VAR_SAMP) [7-15]
+            PathExpression [16-17]
+              Identifier(x) [16-17]
+          Alias [19-29]
+            Identifier(results) [22-29]
+      FromClause [30-54]
+        TablePathExpression [35-54]
+          UnnestExpression [35-49]
+            ExpressionWithOptAlias [42-48]
+              ArrayConstructor [42-48]
+                NullLiteral(NULL) [43-47]
+          Alias [50-54]
+            Identifier(x) [53-54]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/statistical_aggregate_functions#var_samp
+SELECT VAR_SAMP(x) AS results FROM UNNEST([10, 14, CAST('Infinity' as FLOAT64)]) AS x
+--
+QueryStatement [0-85]
+  Query [0-85]
+    Select [0-85]
+      SelectList [7-29]
+        SelectColumn [7-29]
+          FunctionCall [7-18]
+            PathExpression [7-15]
+              Identifier(VAR_SAMP) [7-15]
+            PathExpression [16-17]
+              Identifier(x) [16-17]
+          Alias [19-29]
+            Identifier(results) [22-29]
+      FromClause [30-85]
+        TablePathExpression [35-85]
+          UnnestExpression [35-80]
+            ExpressionWithOptAlias [42-79]
+              ArrayConstructor [42-79]
+                IntLiteral(10) [43-45]
+                IntLiteral(14) [47-49]
+                CastExpression [51-78]
+                  StringLiteral [56-66]
+                    StringLiteralComponent('Infinity') [56-66]
+                  SimpleType [70-77]
+                    PathExpression [70-77]
+                      Identifier(FLOAT64) [70-77]
+          Alias [81-85]
+            Identifier(x) [84-85]
diff --git a/parser/testdata/spanner/stored-procedures.test b/parser/testdata/spanner/stored-procedures.test
new file mode 100644
index 0000000..ac69249
--- /dev/null
+++ b/parser/testdata/spanner/stored-procedures.test
@@ -0,0 +1,12 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/stored-procedures
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/stored-procedures#syntax_2
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+CALL compact_all()
+--
+CallStatement [0-18]
+  PathExpression [5-16]
+    Identifier(compact_all) [5-16]
diff --git a/parser/testdata/spanner/string_functions.test b/parser/testdata/spanner/string_functions.test
new file mode 100644
index 0000000..148aff8
--- /dev/null
+++ b/parser/testdata/spanner/string_functions.test
@@ -0,0 +1,3136 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#byte_length
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+BYTE_LENGTH(value)
+--
+FunctionCall [0-18]
+  PathExpression [0-11]
+    Identifier(BYTE_LENGTH) [0-11]
+  PathExpression [12-17]
+    Identifier(value) [12-17]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#byte_length
+SELECT BYTE_LENGTH('абвгд') AS string_example
+--
+QueryStatement [0-50]
+  Query [0-50]
+    Select [0-50]
+      SelectList [7-50]
+        SelectColumn [7-50]
+          FunctionCall [7-32]
+            PathExpression [7-18]
+              Identifier(BYTE_LENGTH) [7-18]
+            StringLiteral [19-31]
+              StringLiteralComponent('абвгд') [19-31]
+          Alias [33-50]
+            Identifier(string_example) [36-50]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#byte_length
+SELECT BYTE_LENGTH(b'абвгд') AS bytes_example
+--
+QueryStatement [0-50]
+  Query [0-50]
+    Select [0-50]
+      SelectList [7-50]
+        SelectColumn [7-50]
+          FunctionCall [7-33]
+            PathExpression [7-18]
+              Identifier(BYTE_LENGTH) [7-18]
+            BytesLiteral [19-32]
+              BytesLiteralComponent(b'абвгд') [19-32]
+          Alias [34-50]
+            Identifier(bytes_example) [37-50]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#char_length
+[mode=expression]
+CHAR_LENGTH(value)
+--
+FunctionCall [0-18]
+  PathExpression [0-11]
+    Identifier(CHAR_LENGTH) [0-11]
+  PathExpression [12-17]
+    Identifier(value) [12-17]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#char_length
+SELECT CHAR_LENGTH('абвгд') AS char_length
+--
+QueryStatement [0-47]
+  Query [0-47]
+    Select [0-47]
+      SelectList [7-47]
+        SelectColumn [7-47]
+          FunctionCall [7-32]
+            PathExpression [7-18]
+              Identifier(CHAR_LENGTH) [7-18]
+            StringLiteral [19-31]
+              StringLiteralComponent('абвгд') [19-31]
+          Alias [33-47]
+            Identifier(char_length) [36-47]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#character_length
+[mode=expression]
+CHARACTER_LENGTH(value)
+--
+FunctionCall [0-23]
+  PathExpression [0-16]
+    Identifier(CHARACTER_LENGTH) [0-16]
+  PathExpression [17-22]
+    Identifier(value) [17-22]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#character_length
+SELECT
+  'абвгд' AS characters,
+  CHARACTER_LENGTH('абвгд') AS char_length_example
+--
+QueryStatement [0-92]
+  Query [0-92]
+    Select [0-92]
+      SelectList [9-92]
+        SelectColumn [9-35]
+          StringLiteral [9-21]
+            StringLiteralComponent('абвгд') [9-21]
+          Alias [22-35]
+            Identifier(characters) [25-35]
+        SelectColumn [39-92]
+          FunctionCall [39-69]
+            PathExpression [39-55]
+              Identifier(CHARACTER_LENGTH) [39-55]
+            StringLiteral [56-68]
+              StringLiteralComponent('абвгд') [56-68]
+          Alias [70-92]
+            Identifier(char_length_example) [73-92]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#code_points_to_bytes
+[mode=expression]
+CODE_POINTS_TO_BYTES(ascii_code_points)
+--
+FunctionCall [0-39]
+  PathExpression [0-20]
+    Identifier(CODE_POINTS_TO_BYTES) [0-20]
+  PathExpression [21-38]
+    Identifier(ascii_code_points) [21-38]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#code_points_to_bytes
+SELECT CODE_POINTS_TO_BYTES([65, 98, 67, 100]) AS bytes
+--
+QueryStatement [0-55]
+  Query [0-55]
+    Select [0-55]
+      SelectList [7-55]
+        SelectColumn [7-55]
+          FunctionCall [7-46]
+            PathExpression [7-27]
+              Identifier(CODE_POINTS_TO_BYTES) [7-27]
+            ArrayConstructor [28-45]
+              IntLiteral(65) [29-31]
+              IntLiteral(98) [33-35]
+              IntLiteral(67) [37-39]
+              IntLiteral(100) [41-44]
+          Alias [47-55]
+            Identifier(bytes) [50-55]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#code_points_to_bytes
+SELECT CODE_POINTS_TO_BYTES(ARRAY_AGG(
+  (SELECT
+      CASE
+        WHEN chr BETWEEN b'a' and b'z'
+          THEN TO_CODE_POINTS(b'a')[offset(0)] +
+            MOD(code+13-TO_CODE_POINTS(b'a')[offset(0)],26)
+        WHEN chr BETWEEN b'A' and b'Z'
+          THEN TO_CODE_POINTS(b'A')[offset(0)] +
+            MOD(code+13-TO_CODE_POINTS(b'A')[offset(0)],26)
+        ELSE code
+      END
+   FROM
+     (SELECT code, CODE_POINTS_TO_BYTES([code]) chr)
+  ) ORDER BY OFFSET)) AS encoded_string
+FROM UNNEST(TO_CODE_POINTS(b'Test String!')) code WITH OFFSET
+--
+QueryStatement [0-546]
+  Query [0-546]
+    Select [0-546]
+      SelectList [7-484]
+        SelectColumn [7-484]
+          FunctionCall [7-466]
+            PathExpression [7-27]
+              Identifier(CODE_POINTS_TO_BYTES) [7-27]
+            FunctionCall [28-465]
+              PathExpression [28-37]
+                Identifier(ARRAY_AGG) [28-37]
+              ExpressionSubquery [41-448]
+                Query [42-444]
+                  Select [42-444]
+                    SelectList [55-383]
+                      SelectColumn [55-383]
+                        CaseNoValueExpression [55-383]
+                          BetweenExpression(BETWEEN) [73-98]
+                            PathExpression [73-76]
+                              Identifier(chr) [73-76]
+                            Location [77-84]
+                            BytesLiteral [85-89]
+                              BytesLiteralComponent(b'a') [85-89]
+                            BytesLiteral [94-98]
+                              BytesLiteralComponent(b'z') [94-98]
+                          BinaryExpression(+) [114-207]
+                            ArrayElement [114-145]
+                              FunctionCall [114-134]
+                                PathExpression [114-128]
+                                  Identifier(TO_CODE_POINTS) [114-128]
+                                BytesLiteral [129-133]
+                                  BytesLiteralComponent(b'a') [129-133]
+                              Location [134-135]
+                              FunctionCall [135-144]
+                                PathExpression [135-141]
+                                  Identifier(offset) [135-141]
+                                IntLiteral(0) [142-143]
+                            FunctionCall [160-207]
+                              PathExpression [160-163]
+                                Identifier(MOD) [160-163]
+                              BinaryExpression(-) [164-203]
+                                BinaryExpression(+) [164-171]
+                                  PathExpression [164-168]
+                                    Identifier(code) [164-168]
+                                  IntLiteral(13) [169-171]
+                                ArrayElement [172-203]
+                                  FunctionCall [172-192]
+                                    PathExpression [172-186]
+                                      Identifier(TO_CODE_POINTS) [172-186]
+                                    BytesLiteral [187-191]
+                                      BytesLiteralComponent(b'a') [187-191]
+                                  Location [192-193]
+                                  FunctionCall [193-202]
+                                    PathExpression [193-199]
+                                      Identifier(offset) [193-199]
+                                    IntLiteral(0) [200-201]
+                              IntLiteral(26) [204-206]
+                          BetweenExpression(BETWEEN) [221-246]
+                            PathExpression [221-224]
+                              Identifier(chr) [221-224]
+                            Location [225-232]
+                            BytesLiteral [233-237]
+                              BytesLiteralComponent(b'A') [233-237]
+                            BytesLiteral [242-246]
+                              BytesLiteralComponent(b'Z') [242-246]
+                          BinaryExpression(+) [262-355]
+                            ArrayElement [262-293]
+                              FunctionCall [262-282]
+                                PathExpression [262-276]
+                                  Identifier(TO_CODE_POINTS) [262-276]
+                                BytesLiteral [277-281]
+                                  BytesLiteralComponent(b'A') [277-281]
+                              Location [282-283]
+                              FunctionCall [283-292]
+                                PathExpression [283-289]
+                                  Identifier(offset) [283-289]
+                                IntLiteral(0) [290-291]
+                            FunctionCall [308-355]
+                              PathExpression [308-311]
+                                Identifier(MOD) [308-311]
+                              BinaryExpression(-) [312-351]
+                                BinaryExpression(+) [312-319]
+                                  PathExpression [312-316]
+                                    Identifier(code) [312-316]
+                                  IntLiteral(13) [317-319]
+                                ArrayElement [320-351]
+                                  FunctionCall [320-340]
+                                    PathExpression [320-334]
+                                      Identifier(TO_CODE_POINTS) [320-334]
+                                    BytesLiteral [335-339]
+                                      BytesLiteralComponent(b'A') [335-339]
+                                  Location [340-341]
+                                  FunctionCall [341-350]
+                                    PathExpression [341-347]
+                                      Identifier(offset) [341-347]
+                                    IntLiteral(0) [348-349]
+                              IntLiteral(26) [352-354]
+                          PathExpression [369-373]
+                            Identifier(code) [369-373]
+                    FromClause [387-444]
+                      TableSubquery [397-444]
+                        Query [398-443]
+                          Select [398-443]
+                            SelectList [405-443]
+                              SelectColumn [405-409]
+                                PathExpression [405-409]
+                                  Identifier(code) [405-409]
+                              SelectColumn [411-443]
+                                FunctionCall [411-439]
+                                  PathExpression [411-431]
+                                    Identifier(CODE_POINTS_TO_BYTES) [411-431]
+                                  ArrayConstructor [432-438]
+                                    PathExpression [433-437]
+                                      Identifier(code) [433-437]
+                                Alias [440-443]
+                                  Identifier(chr) [440-443]
+              OrderBy [449-464]
+                OrderingExpression(ASC) [458-464]
+                  PathExpression [458-464]
+                    Identifier(OFFSET) [458-464]
+          Alias [467-484]
+            Identifier(encoded_string) [470-484]
+      FromClause [485-546]
+        TablePathExpression [490-546]
+          UnnestExpression [490-529]
+            ExpressionWithOptAlias [497-528]
+              FunctionCall [497-528]
+                PathExpression [497-511]
+                  Identifier(TO_CODE_POINTS) [497-511]
+                BytesLiteral [512-527]
+                  BytesLiteralComponent(b'Test String!') [512-527]
+          Alias [530-534]
+            Identifier(code) [530-534]
+          WithOffset [535-546]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#code_points_to_string
+[mode=expression]
+CODE_POINTS_TO_STRING(unicode_code_points)
+--
+FunctionCall [0-42]
+  PathExpression [0-21]
+    Identifier(CODE_POINTS_TO_STRING) [0-21]
+  PathExpression [22-41]
+    Identifier(unicode_code_points) [22-41]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#code_points_to_string
+SELECT CODE_POINTS_TO_STRING([65, 255, 513, 1024]) AS string
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-60]
+      SelectList [7-60]
+        SelectColumn [7-60]
+          FunctionCall [7-50]
+            PathExpression [7-28]
+              Identifier(CODE_POINTS_TO_STRING) [7-28]
+            ArrayConstructor [29-49]
+              IntLiteral(65) [30-32]
+              IntLiteral(255) [34-37]
+              IntLiteral(513) [39-42]
+              IntLiteral(1024) [44-48]
+          Alias [51-60]
+            Identifier(string) [54-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#code_points_to_string
+SELECT CODE_POINTS_TO_STRING([97, 0, 0xF9B5]) AS string
+--
+QueryStatement [0-55]
+  Query [0-55]
+    Select [0-55]
+      SelectList [7-55]
+        SelectColumn [7-55]
+          FunctionCall [7-45]
+            PathExpression [7-28]
+              Identifier(CODE_POINTS_TO_STRING) [7-28]
+            ArrayConstructor [29-44]
+              IntLiteral(97) [30-32]
+              IntLiteral(0) [34-35]
+              IntLiteral(0xF9B5) [37-43]
+          Alias [46-55]
+            Identifier(string) [49-55]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#code_points_to_string
+SELECT CODE_POINTS_TO_STRING([65, 255, NULL, 1024]) AS string
+--
+QueryStatement [0-61]
+  Query [0-61]
+    Select [0-61]
+      SelectList [7-61]
+        SelectColumn [7-61]
+          FunctionCall [7-51]
+            PathExpression [7-28]
+              Identifier(CODE_POINTS_TO_STRING) [7-28]
+            ArrayConstructor [29-50]
+              IntLiteral(65) [30-32]
+              IntLiteral(255) [34-37]
+              NullLiteral(NULL) [39-43]
+              IntLiteral(1024) [45-49]
+          Alias [52-61]
+            Identifier(string) [55-61]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#code_points_to_string
+WITH Words AS (
+  SELECT word
+  FROM UNNEST(['foo', 'bar', 'baz', 'giraffe', 'llama']) AS word
+)
+SELECT
+  CODE_POINTS_TO_STRING([code_point]) AS letter,
+  COUNT(*) AS letter_count
+FROM Words,
+  UNNEST(TO_CODE_POINTS(word)) AS code_point
+GROUP BY 1
+ORDER BY 2 DESC
+--
+QueryStatement [0-263]
+  Query [0-263]
+    WithClause [0-96]
+      WithClauseEntry [5-96]
+        AliasedQuery [5-96]
+          Identifier(Words) [5-10]
+          Query [14-96]
+            Select [18-94]
+              SelectList [25-29]
+                SelectColumn [25-29]
+                  PathExpression [25-29]
+                    Identifier(word) [25-29]
+              FromClause [32-94]
+                TablePathExpression [37-94]
+                  UnnestExpression [37-86]
+                    ExpressionWithOptAlias [44-85]
+                      ArrayConstructor [44-85]
+                        StringLiteral [45-50]
+                          StringLiteralComponent('foo') [45-50]
+                        StringLiteral [52-57]
+                          StringLiteralComponent('bar') [52-57]
+                        StringLiteral [59-64]
+                          StringLiteralComponent('baz') [59-64]
+                        StringLiteral [66-75]
+                          StringLiteralComponent('giraffe') [66-75]
+                        StringLiteral [77-84]
+                          StringLiteralComponent('llama') [77-84]
+                  Alias [87-94]
+                    Identifier(word) [90-94]
+    Select [97-247]
+      SelectList [106-179]
+        SelectColumn [106-151]
+          FunctionCall [106-141]
+            PathExpression [106-127]
+              Identifier(CODE_POINTS_TO_STRING) [106-127]
+            ArrayConstructor [128-140]
+              PathExpression [129-139]
+                Identifier(code_point) [129-139]
+          Alias [142-151]
+            Identifier(letter) [145-151]
+        SelectColumn [155-179]
+          FunctionCall [155-163]
+            PathExpression [155-160]
+              Identifier(COUNT) [155-160]
+            Star(*) [161-162]
+          Alias [164-179]
+            Identifier(letter_count) [167-179]
+      FromClause [180-236]
+        Join(COMMA) [185-236]
+          TablePathExpression [185-190]
+            PathExpression [185-190]
+              Identifier(Words) [185-190]
+          Location [190-191]
+          TablePathExpression [194-236]
+            UnnestExpression [194-222]
+              ExpressionWithOptAlias [201-221]
+                FunctionCall [201-221]
+                  PathExpression [201-215]
+                    Identifier(TO_CODE_POINTS) [201-215]
+                  PathExpression [216-220]
+                    Identifier(word) [216-220]
+            Alias [223-236]
+              Identifier(code_point) [226-236]
+      GroupBy [237-247]
+        GroupingItem [246-247]
+          IntLiteral(1) [246-247]
+    OrderBy [248-263]
+      OrderingExpression(DESC) [257-263]
+        IntLiteral(2) [257-258]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#concat
+SELECT CONCAT('T.P.', ' ', 'Bar') as author
+--
+QueryStatement [0-43]
+  Query [0-43]
+    Select [0-43]
+      SelectList [7-43]
+        SelectColumn [7-43]
+          FunctionCall [7-33]
+            PathExpression [7-13]
+              Identifier(CONCAT) [7-13]
+            StringLiteral [14-20]
+              StringLiteralComponent('T.P.') [14-20]
+            StringLiteral [22-25]
+              StringLiteralComponent(' ') [22-25]
+            StringLiteral [27-32]
+              StringLiteralComponent('Bar') [27-32]
+          Alias [34-43]
+            Identifier(author) [37-43]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#concat
+With Employees AS
+  (SELECT
+    'John' AS first_name,
+    'Doe' AS last_name
+  UNION ALL
+  SELECT
+    'Jane' AS first_name,
+    'Smith' AS last_name
+  UNION ALL
+  SELECT
+    'Joe' AS first_name,
+    'Jackson' AS last_name)
+
+SELECT
+  CONCAT(first_name, ' ', last_name)
+  AS full_name
+FROM Employees
+--
+QueryStatement [0-297]
+  Query [0-297]
+    WithClause [0-222]
+      WithClauseEntry [5-222]
+        AliasedQuery [5-222]
+          Identifier(Employees) [5-14]
+          Query [20-222]
+            SetOperation(UNION ALL) [21-221]
+              SetOperationMetadataList [79-160]
+                SetOperationMetadata [79-88]
+                  SetOperationType [79-84]
+                  SetOperationAllOrDistinct [85-88]
+                SetOperationMetadata [151-160]
+                  SetOperationType [151-156]
+                  SetOperationAllOrDistinct [157-160]
+              Select [21-76]
+                SelectList [32-76]
+                  SelectColumn [32-52]
+                    StringLiteral [32-38]
+                      StringLiteralComponent('John') [32-38]
+                    Alias [39-52]
+                      Identifier(first_name) [42-52]
+                  SelectColumn [58-76]
+                    StringLiteral [58-63]
+                      StringLiteralComponent('Doe') [58-63]
+                    Alias [64-76]
+                      Identifier(last_name) [67-76]
+              Select [91-148]
+                SelectList [102-148]
+                  SelectColumn [102-122]
+                    StringLiteral [102-108]
+                      StringLiteralComponent('Jane') [102-108]
+                    Alias [109-122]
+                      Identifier(first_name) [112-122]
+                  SelectColumn [128-148]
+                    StringLiteral [128-135]
+                      StringLiteralComponent('Smith') [128-135]
+                    Alias [136-148]
+                      Identifier(last_name) [139-148]
+              Select [163-221]
+                SelectList [174-221]
+                  SelectColumn [174-193]
+                    StringLiteral [174-179]
+                      StringLiteralComponent('Joe') [174-179]
+                    Alias [180-193]
+                      Identifier(first_name) [183-193]
+                  SelectColumn [199-221]
+                    StringLiteral [199-208]
+                      StringLiteralComponent('Jackson') [199-208]
+                    Alias [209-221]
+                      Identifier(last_name) [212-221]
+    Select [224-297]
+      SelectList [233-282]
+        SelectColumn [233-282]
+          FunctionCall [233-267]
+            PathExpression [233-239]
+              Identifier(CONCAT) [233-239]
+            PathExpression [240-250]
+              Identifier(first_name) [240-250]
+            StringLiteral [252-255]
+              StringLiteralComponent(' ') [252-255]
+            PathExpression [257-266]
+              Identifier(last_name) [257-266]
+          Alias [270-282]
+            Identifier(full_name) [273-282]
+      FromClause [283-297]
+        TablePathExpression [288-297]
+          PathExpression [288-297]
+            Identifier(Employees) [288-297]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#ends_with
+[mode=expression]
+ENDS_WITH(value, suffix)
+--
+FunctionCall [0-24]
+  PathExpression [0-9]
+    Identifier(ENDS_WITH) [0-9]
+  PathExpression [10-15]
+    Identifier(value) [10-15]
+  PathExpression [17-23]
+    Identifier(suffix) [17-23]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#ends_with
+SELECT ENDS_WITH('apple', 'e') as example
+--
+QueryStatement [0-41]
+  Query [0-41]
+    Select [0-41]
+      SelectList [7-41]
+        SelectColumn [7-41]
+          FunctionCall [7-30]
+            PathExpression [7-16]
+              Identifier(ENDS_WITH) [7-16]
+            StringLiteral [17-24]
+              StringLiteralComponent('apple') [17-24]
+            StringLiteral [26-29]
+              StringLiteralComponent('e') [26-29]
+          Alias [31-41]
+            Identifier(example) [34-41]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#format_string
+SELECT FORMAT('date: %s!', FORMAT_DATE('%B %d, %Y', date '2015-01-02'))
+--
+QueryStatement [0-71]
+  Query [0-71]
+    Select [0-71]
+      SelectList [7-71]
+        SelectColumn [7-71]
+          FunctionCall [7-71]
+            PathExpression [7-13]
+              Identifier(FORMAT) [7-13]
+            StringLiteral [14-25]
+              StringLiteralComponent('date: %s!') [14-25]
+            FunctionCall [27-70]
+              PathExpression [27-38]
+                Identifier(FORMAT_DATE) [27-38]
+              StringLiteral [39-50]
+                StringLiteralComponent('%B %d, %Y') [39-50]
+              DateOrTimeLiteral(TYPE_DATE) [52-69]
+                StringLiteral [57-69]
+                  StringLiteralComponent('2015-01-02') [57-69]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#t_and_t_behavior
+[mode=expression]
+JSON '{"name":"apple","stock":3}'
+--
+JSONLiteral [0-33]
+  StringLiteral [5-33]
+    StringLiteralComponent('{"name":"apple","stock":3}') [5-33]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#error_format_specifiers
+[mode=expression]
+FORMAT('%s', 1)
+--
+FunctionCall [0-15]
+  PathExpression [0-6]
+    Identifier(FORMAT) [0-6]
+  StringLiteral [7-11]
+    StringLiteralComponent('%s') [7-11]
+  IntLiteral(1) [13-14]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#error_format_specifiers
+[mode=expression]
+FORMAT('%')
+--
+FunctionCall [0-11]
+  PathExpression [0-6]
+    Identifier(FORMAT) [0-6]
+  StringLiteral [7-10]
+    StringLiteralComponent('%') [7-10]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#null_format_specifiers
+[mode=expression]
+FORMAT('00-%t-00', NULL_expression)
+--
+FunctionCall [0-35]
+  PathExpression [0-6]
+    Identifier(FORMAT) [0-6]
+  StringLiteral [7-17]
+    StringLiteralComponent('00-%t-00') [7-17]
+  PathExpression [19-34]
+    Identifier(NULL_expression) [19-34]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#null_format_specifiers
+[mode=expression]
+00-NULL-00
+--
+BinaryExpression(-) [0-10]
+  BinaryExpression(-) [0-7]
+    IntLiteral(00) [0-2]
+    NullLiteral(NULL) [3-7]
+  IntLiteral(00) [8-10]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#from_base32
+[mode=expression]
+FROM_BASE32(string_expr)
+--
+FunctionCall [0-24]
+  PathExpression [0-11]
+    Identifier(FROM_BASE32) [0-11]
+  PathExpression [12-23]
+    Identifier(string_expr) [12-23]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#from_base32
+SELECT FROM_BASE32('MFRGGZDF74======') AS byte_data
+--
+QueryStatement [0-51]
+  Query [0-51]
+    Select [0-51]
+      SelectList [7-51]
+        SelectColumn [7-51]
+          FunctionCall [7-38]
+            PathExpression [7-18]
+              Identifier(FROM_BASE32) [7-18]
+            StringLiteral [19-37]
+              StringLiteralComponent('MFRGGZDF74======') [19-37]
+          Alias [39-51]
+            Identifier(byte_data) [42-51]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#from_base64
+[mode=expression]
+FROM_BASE64(string_expr)
+--
+FunctionCall [0-24]
+  PathExpression [0-11]
+    Identifier(FROM_BASE64) [0-11]
+  PathExpression [12-23]
+    Identifier(string_expr) [12-23]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#from_base64
+SELECT FROM_BASE64('/+A=') AS byte_data
+--
+QueryStatement [0-39]
+  Query [0-39]
+    Select [0-39]
+      SelectList [7-39]
+        SelectColumn [7-39]
+          FunctionCall [7-26]
+            PathExpression [7-18]
+              Identifier(FROM_BASE64) [7-18]
+            StringLiteral [19-25]
+              StringLiteralComponent('/+A=') [19-25]
+          Alias [27-39]
+            Identifier(byte_data) [30-39]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#from_base64
+SELECT FROM_BASE64(REPLACE(REPLACE('_-A=', '-', '+'), '_', '/')) AS binary
+--
+QueryStatement [0-74]
+  Query [0-74]
+    Select [0-74]
+      SelectList [7-74]
+        SelectColumn [7-74]
+          FunctionCall [7-64]
+            PathExpression [7-18]
+              Identifier(FROM_BASE64) [7-18]
+            FunctionCall [19-63]
+              PathExpression [19-26]
+                Identifier(`REPLACE`) [19-26]
+              FunctionCall [27-52]
+                PathExpression [27-34]
+                  Identifier(`REPLACE`) [27-34]
+                StringLiteral [35-41]
+                  StringLiteralComponent('_-A=') [35-41]
+                StringLiteral [43-46]
+                  StringLiteralComponent('-') [43-46]
+                StringLiteral [48-51]
+                  StringLiteralComponent('+') [48-51]
+              StringLiteral [54-57]
+                StringLiteralComponent('_') [54-57]
+              StringLiteral [59-62]
+                StringLiteralComponent('/') [59-62]
+          Alias [65-74]
+            Identifier(binary) [68-74]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#from_hex
+[mode=expression]
+FROM_HEX(string)
+--
+FunctionCall [0-16]
+  PathExpression [0-8]
+    Identifier(FROM_HEX) [0-8]
+  PathExpression [9-15]
+    Identifier(string) [9-15]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#from_hex
+WITH Input AS (
+  SELECT '00010203aaeeefff' AS hex_str UNION ALL
+  SELECT '0AF' UNION ALL
+  SELECT '666f6f626172'
+)
+SELECT hex_str, FROM_HEX(hex_str) AS bytes_str
+FROM Input
+--
+QueryStatement [0-173]
+  Query [0-173]
+    WithClause [0-115]
+      WithClauseEntry [5-115]
+        AliasedQuery [5-115]
+          Identifier(Input) [5-10]
+          Query [14-115]
+            SetOperation(UNION ALL) [18-113]
+              SetOperationMetadataList [55-89]
+                SetOperationMetadata [55-64]
+                  SetOperationType [55-60]
+                  SetOperationAllOrDistinct [61-64]
+                SetOperationMetadata [80-89]
+                  SetOperationType [80-85]
+                  SetOperationAllOrDistinct [86-89]
+              Select [18-54]
+                SelectList [25-54]
+                  SelectColumn [25-54]
+                    StringLiteral [25-43]
+                      StringLiteralComponent('00010203aaeeefff') [25-43]
+                    Alias [44-54]
+                      Identifier(hex_str) [47-54]
+              Select [67-79]
+                SelectList [74-79]
+                  SelectColumn [74-79]
+                    StringLiteral [74-79]
+                      StringLiteralComponent('0AF') [74-79]
+              Select [92-113]
+                SelectList [99-113]
+                  SelectColumn [99-113]
+                    StringLiteral [99-113]
+                      StringLiteralComponent('666f6f626172') [99-113]
+    Select [116-173]
+      SelectList [123-162]
+        SelectColumn [123-130]
+          PathExpression [123-130]
+            Identifier(hex_str) [123-130]
+        SelectColumn [132-162]
+          FunctionCall [132-149]
+            PathExpression [132-140]
+              Identifier(FROM_HEX) [132-140]
+            PathExpression [141-148]
+              Identifier(hex_str) [141-148]
+          Alias [150-162]
+            Identifier(bytes_str) [153-162]
+      FromClause [163-173]
+        TablePathExpression [168-173]
+          PathExpression [168-173]
+            Identifier(Input) [168-173]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#lcase
+[mode=expression]
+LCASE(val)
+--
+FunctionCall [0-10]
+  PathExpression [0-5]
+    Identifier(LCASE) [0-5]
+  PathExpression [6-9]
+    Identifier(val) [6-9]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#length
+[mode=expression]
+LENGTH(value)
+--
+FunctionCall [0-13]
+  PathExpression [0-6]
+    Identifier(LENGTH) [0-6]
+  PathExpression [7-12]
+    Identifier(value) [7-12]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#length
+SELECT
+  LENGTH('абвгд') AS string_example,
+  LENGTH(CAST('абвгд' AS BYTES)) AS bytes_example
+--
+QueryStatement [0-103]
+  Query [0-103]
+    Select [0-103]
+      SelectList [9-103]
+        SelectColumn [9-47]
+          FunctionCall [9-29]
+            PathExpression [9-15]
+              Identifier(LENGTH) [9-15]
+            StringLiteral [16-28]
+              StringLiteralComponent('абвгд') [16-28]
+          Alias [30-47]
+            Identifier(string_example) [33-47]
+        SelectColumn [51-103]
+          FunctionCall [51-86]
+            PathExpression [51-57]
+              Identifier(LENGTH) [51-57]
+            CastExpression [58-85]
+              StringLiteral [63-75]
+                StringLiteralComponent('абвгд') [63-75]
+              SimpleType [79-84]
+                PathExpression [79-84]
+                  Identifier(BYTES) [79-84]
+          Alias [87-103]
+            Identifier(bytes_example) [90-103]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#lower
+[mode=expression]
+LOWER(value)
+--
+FunctionCall [0-12]
+  PathExpression [0-5]
+    Identifier(LOWER) [0-5]
+  PathExpression [6-11]
+    Identifier(value) [6-11]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#lower
+SELECT
+  LOWER('FOO BAR BAZ') AS example
+FROM items
+--
+QueryStatement [0-51]
+  Query [0-51]
+    Select [0-51]
+      SelectList [9-40]
+        SelectColumn [9-40]
+          FunctionCall [9-29]
+            PathExpression [9-14]
+              Identifier(LOWER) [9-14]
+            StringLiteral [15-28]
+              StringLiteralComponent('FOO BAR BAZ') [15-28]
+          Alias [30-40]
+            Identifier(example) [33-40]
+      FromClause [41-51]
+        TablePathExpression [46-51]
+          PathExpression [46-51]
+            Identifier(items) [46-51]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#lpad
+SELECT FORMAT('%T', LPAD('c', 5)) AS results
+--
+QueryStatement [0-44]
+  Query [0-44]
+    Select [0-44]
+      SelectList [7-44]
+        SelectColumn [7-44]
+          FunctionCall [7-33]
+            PathExpression [7-13]
+              Identifier(FORMAT) [7-13]
+            StringLiteral [14-18]
+              StringLiteralComponent('%T') [14-18]
+            FunctionCall [20-32]
+              PathExpression [20-24]
+                Identifier(LPAD) [20-24]
+              StringLiteral [25-28]
+                StringLiteralComponent('c') [25-28]
+              IntLiteral(5) [30-31]
+          Alias [34-44]
+            Identifier(results) [37-44]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#lpad
+SELECT LPAD('b', 5, 'a') AS results
+--
+QueryStatement [0-35]
+  Query [0-35]
+    Select [0-35]
+      SelectList [7-35]
+        SelectColumn [7-35]
+          FunctionCall [7-24]
+            PathExpression [7-11]
+              Identifier(LPAD) [7-11]
+            StringLiteral [12-15]
+              StringLiteralComponent('b') [12-15]
+            IntLiteral(5) [17-18]
+            StringLiteral [20-23]
+              StringLiteralComponent('a') [20-23]
+          Alias [25-35]
+            Identifier(results) [28-35]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#lpad
+SELECT LPAD('abc', 10, 'ghd') AS results
+--
+QueryStatement [0-40]
+  Query [0-40]
+    Select [0-40]
+      SelectList [7-40]
+        SelectColumn [7-40]
+          FunctionCall [7-29]
+            PathExpression [7-11]
+              Identifier(LPAD) [7-11]
+            StringLiteral [12-17]
+              StringLiteralComponent('abc') [12-17]
+            IntLiteral(10) [19-21]
+            StringLiteral [23-28]
+              StringLiteralComponent('ghd') [23-28]
+          Alias [30-40]
+            Identifier(results) [33-40]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#lpad
+SELECT LPAD('abc', 2, 'd') AS results
+--
+QueryStatement [0-37]
+  Query [0-37]
+    Select [0-37]
+      SelectList [7-37]
+        SelectColumn [7-37]
+          FunctionCall [7-26]
+            PathExpression [7-11]
+              Identifier(LPAD) [7-11]
+            StringLiteral [12-17]
+              StringLiteralComponent('abc') [12-17]
+            IntLiteral(2) [19-20]
+            StringLiteral [22-25]
+              StringLiteralComponent('d') [22-25]
+          Alias [27-37]
+            Identifier(results) [30-37]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#lpad
+SELECT FORMAT('%T', LPAD(b'abc', 10, b'ghd')) AS results
+--
+QueryStatement [0-56]
+  Query [0-56]
+    Select [0-56]
+      SelectList [7-56]
+        SelectColumn [7-56]
+          FunctionCall [7-45]
+            PathExpression [7-13]
+              Identifier(FORMAT) [7-13]
+            StringLiteral [14-18]
+              StringLiteralComponent('%T') [14-18]
+            FunctionCall [20-44]
+              PathExpression [20-24]
+                Identifier(LPAD) [20-24]
+              BytesLiteral [25-31]
+                BytesLiteralComponent(b'abc') [25-31]
+              IntLiteral(10) [33-35]
+              BytesLiteral [37-43]
+                BytesLiteralComponent(b'ghd') [37-43]
+          Alias [46-56]
+            Identifier(results) [49-56]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#ltrim
+SELECT CONCAT('#', LTRIM('   apple   '), '#') AS example
+--
+QueryStatement [0-56]
+  Query [0-56]
+    Select [0-56]
+      SelectList [7-56]
+        SelectColumn [7-56]
+          FunctionCall [7-45]
+            PathExpression [7-13]
+              Identifier(CONCAT) [7-13]
+            StringLiteral [14-17]
+              StringLiteralComponent('#') [14-17]
+            FunctionCall [19-39]
+              PathExpression [19-24]
+                Identifier(LTRIM) [19-24]
+              StringLiteral [25-38]
+                StringLiteralComponent('   apple   ') [25-38]
+            StringLiteral [41-44]
+              StringLiteralComponent('#') [41-44]
+          Alias [46-56]
+            Identifier(example) [49-56]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#ltrim
+SELECT LTRIM('***apple***', '*') AS example
+--
+QueryStatement [0-43]
+  Query [0-43]
+    Select [0-43]
+      SelectList [7-43]
+        SelectColumn [7-43]
+          FunctionCall [7-32]
+            PathExpression [7-12]
+              Identifier(LTRIM) [7-12]
+            StringLiteral [13-26]
+              StringLiteralComponent('***apple***') [13-26]
+            StringLiteral [28-31]
+              StringLiteralComponent('*') [28-31]
+          Alias [33-43]
+            Identifier(example) [36-43]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#ltrim
+SELECT LTRIM('xxxapplexxx', 'xyz') AS example
+--
+QueryStatement [0-45]
+  Query [0-45]
+    Select [0-45]
+      SelectList [7-45]
+        SelectColumn [7-45]
+          FunctionCall [7-34]
+            PathExpression [7-12]
+              Identifier(LTRIM) [7-12]
+            StringLiteral [13-26]
+              StringLiteralComponent('xxxapplexxx') [13-26]
+            StringLiteral [28-33]
+              StringLiteralComponent('xyz') [28-33]
+          Alias [35-45]
+            Identifier(example) [38-45]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#normalize
+SELECT
+  NORMALIZE('\u00ea') as a,
+  NORMALIZE('\u0065\u0302') as b,
+  NORMALIZE('\u00ea') = NORMALIZE('\u0065\u0302') as normalized
+--
+QueryStatement [0-132]
+  Query [0-132]
+    Select [0-132]
+      SelectList [9-132]
+        SelectColumn [9-33]
+          FunctionCall [9-28]
+            PathExpression [9-18]
+              Identifier(NORMALIZE) [9-18]
+            StringLiteral [19-27]
+              StringLiteralComponent('\u00ea') [19-27]
+          Alias [29-33]
+            Identifier(a) [32-33]
+        SelectColumn [37-67]
+          FunctionCall [37-62]
+            PathExpression [37-46]
+              Identifier(NORMALIZE) [37-46]
+            StringLiteral [47-61]
+              StringLiteralComponent('\u0065\u0302') [47-61]
+          Alias [63-67]
+            Identifier(b) [66-67]
+        SelectColumn [71-132]
+          BinaryExpression(=) [71-118]
+            FunctionCall [71-90]
+              PathExpression [71-80]
+                Identifier(NORMALIZE) [71-80]
+              StringLiteral [81-89]
+                StringLiteralComponent('\u00ea') [81-89]
+            FunctionCall [93-118]
+              PathExpression [93-102]
+                Identifier(NORMALIZE) [93-102]
+              StringLiteral [103-117]
+                StringLiteralComponent('\u0065\u0302') [103-117]
+          Alias [119-132]
+            Identifier(normalized) [122-132]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#normalize
+SELECT NORMALIZE('Raha\u2004Mahan', NFKC) AS normalized_name
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-60]
+      SelectList [7-60]
+        SelectColumn [7-60]
+          FunctionCall [7-41]
+            PathExpression [7-16]
+              Identifier(NORMALIZE) [7-16]
+            StringLiteral [17-34]
+              StringLiteralComponent('Raha\u2004Mahan') [17-34]
+            PathExpression [36-40]
+              Identifier(NFKC) [36-40]
+          Alias [42-60]
+            Identifier(normalized_name) [45-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#normalize
+SELECT NORMALIZE('Raha\u2005Mahan', NFKC) AS normalized_name
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-60]
+      SelectList [7-60]
+        SelectColumn [7-60]
+          FunctionCall [7-41]
+            PathExpression [7-16]
+              Identifier(NORMALIZE) [7-16]
+            StringLiteral [17-34]
+              StringLiteralComponent('Raha\u2005Mahan') [17-34]
+            PathExpression [36-40]
+              Identifier(NFKC) [36-40]
+          Alias [42-60]
+            Identifier(normalized_name) [45-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#normalize
+SELECT NORMALIZE('Raha\u2006Mahan', NFKC) AS normalized_name
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-60]
+      SelectList [7-60]
+        SelectColumn [7-60]
+          FunctionCall [7-41]
+            PathExpression [7-16]
+              Identifier(NORMALIZE) [7-16]
+            StringLiteral [17-34]
+              StringLiteralComponent('Raha\u2006Mahan') [17-34]
+            PathExpression [36-40]
+              Identifier(NFKC) [36-40]
+          Alias [42-60]
+            Identifier(normalized_name) [45-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#normalize
+SELECT NORMALIZE('Raha Mahan', NFKC) AS normalized_name
+--
+QueryStatement [0-55]
+  Query [0-55]
+    Select [0-55]
+      SelectList [7-55]
+        SelectColumn [7-55]
+          FunctionCall [7-36]
+            PathExpression [7-16]
+              Identifier(NORMALIZE) [7-16]
+            StringLiteral [17-29]
+              StringLiteralComponent('Raha Mahan') [17-29]
+            PathExpression [31-35]
+              Identifier(NFKC) [31-35]
+          Alias [37-55]
+            Identifier(normalized_name) [40-55]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#normalize_and_casefold
+SELECT
+  NORMALIZE('The red barn') = NORMALIZE('The Red Barn') AS normalized,
+  NORMALIZE_AND_CASEFOLD('The red barn')
+    = NORMALIZE_AND_CASEFOLD('The Red Barn') AS normalized_with_case_folding
+--
+QueryStatement [0-195]
+  Query [0-195]
+    Select [0-195]
+      SelectList [9-195]
+        SelectColumn [9-76]
+          BinaryExpression(=) [9-62]
+            FunctionCall [9-34]
+              PathExpression [9-18]
+                Identifier(NORMALIZE) [9-18]
+              StringLiteral [19-33]
+                StringLiteralComponent('The red barn') [19-33]
+            FunctionCall [37-62]
+              PathExpression [37-46]
+                Identifier(NORMALIZE) [37-46]
+              StringLiteral [47-61]
+                StringLiteralComponent('The Red Barn') [47-61]
+          Alias [63-76]
+            Identifier(normalized) [66-76]
+        SelectColumn [80-195]
+          BinaryExpression(=) [80-163]
+            FunctionCall [80-118]
+              PathExpression [80-102]
+                Identifier(NORMALIZE_AND_CASEFOLD) [80-102]
+              StringLiteral [103-117]
+                StringLiteralComponent('The red barn') [103-117]
+            FunctionCall [125-163]
+              PathExpression [125-147]
+                Identifier(NORMALIZE_AND_CASEFOLD) [125-147]
+              StringLiteral [148-162]
+                StringLiteralComponent('The Red Barn') [148-162]
+          Alias [164-195]
+            Identifier(normalized_with_case_folding) [167-195]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#normalize_and_casefold
+SELECT
+  '\u2168' AS a,
+  'IX' AS b,
+  NORMALIZE_AND_CASEFOLD('\u2168', NFD)=NORMALIZE_AND_CASEFOLD('IX', NFD) AS nfd,
+  NORMALIZE_AND_CASEFOLD('\u2168', NFC)=NORMALIZE_AND_CASEFOLD('IX', NFC) AS nfc,
+  NORMALIZE_AND_CASEFOLD('\u2168', NFKD)=NORMALIZE_AND_CASEFOLD('IX', NFKD) AS nfkd,
+  NORMALIZE_AND_CASEFOLD('\u2168', NFKC)=NORMALIZE_AND_CASEFOLD('IX', NFKC) AS nfkc
+--
+QueryStatement [0-369]
+  Query [0-369]
+    Select [0-369]
+      SelectList [9-369]
+        SelectColumn [9-22]
+          StringLiteral [9-17]
+            StringLiteralComponent('\u2168') [9-17]
+          Alias [18-22]
+            Identifier(a) [21-22]
+        SelectColumn [26-35]
+          StringLiteral [26-30]
+            StringLiteralComponent('IX') [26-30]
+          Alias [31-35]
+            Identifier(b) [34-35]
+        SelectColumn [39-117]
+          BinaryExpression(=) [39-110]
+            FunctionCall [39-76]
+              PathExpression [39-61]
+                Identifier(NORMALIZE_AND_CASEFOLD) [39-61]
+              StringLiteral [62-70]
+                StringLiteralComponent('\u2168') [62-70]
+              PathExpression [72-75]
+                Identifier(NFD) [72-75]
+            FunctionCall [77-110]
+              PathExpression [77-99]
+                Identifier(NORMALIZE_AND_CASEFOLD) [77-99]
+              StringLiteral [100-104]
+                StringLiteralComponent('IX') [100-104]
+              PathExpression [106-109]
+                Identifier(NFD) [106-109]
+          Alias [111-117]
+            Identifier(nfd) [114-117]
+        SelectColumn [121-199]
+          BinaryExpression(=) [121-192]
+            FunctionCall [121-158]
+              PathExpression [121-143]
+                Identifier(NORMALIZE_AND_CASEFOLD) [121-143]
+              StringLiteral [144-152]
+                StringLiteralComponent('\u2168') [144-152]
+              PathExpression [154-157]
+                Identifier(NFC) [154-157]
+            FunctionCall [159-192]
+              PathExpression [159-181]
+                Identifier(NORMALIZE_AND_CASEFOLD) [159-181]
+              StringLiteral [182-186]
+                StringLiteralComponent('IX') [182-186]
+              PathExpression [188-191]
+                Identifier(NFC) [188-191]
+          Alias [193-199]
+            Identifier(nfc) [196-199]
+        SelectColumn [203-284]
+          BinaryExpression(=) [203-276]
+            FunctionCall [203-241]
+              PathExpression [203-225]
+                Identifier(NORMALIZE_AND_CASEFOLD) [203-225]
+              StringLiteral [226-234]
+                StringLiteralComponent('\u2168') [226-234]
+              PathExpression [236-240]
+                Identifier(NFKD) [236-240]
+            FunctionCall [242-276]
+              PathExpression [242-264]
+                Identifier(NORMALIZE_AND_CASEFOLD) [242-264]
+              StringLiteral [265-269]
+                StringLiteralComponent('IX') [265-269]
+              PathExpression [271-275]
+                Identifier(NFKD) [271-275]
+          Alias [277-284]
+            Identifier(nfkd) [280-284]
+        SelectColumn [288-369]
+          BinaryExpression(=) [288-361]
+            FunctionCall [288-326]
+              PathExpression [288-310]
+                Identifier(NORMALIZE_AND_CASEFOLD) [288-310]
+              StringLiteral [311-319]
+                StringLiteralComponent('\u2168') [311-319]
+              PathExpression [321-325]
+                Identifier(NFKC) [321-325]
+            FunctionCall [327-361]
+              PathExpression [327-349]
+                Identifier(NORMALIZE_AND_CASEFOLD) [327-349]
+              StringLiteral [350-354]
+                StringLiteralComponent('IX') [350-354]
+              PathExpression [356-360]
+                Identifier(NFKC) [356-360]
+          Alias [362-369]
+            Identifier(nfkc) [365-369]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#normalize_and_casefold
+SELECT
+  '\u0041\u030A' AS a,
+  '\u00C5' AS b,
+  NORMALIZE_AND_CASEFOLD('\u0041\u030A', NFD)=NORMALIZE_AND_CASEFOLD('\u00C5', NFD) AS nfd,
+  NORMALIZE_AND_CASEFOLD('\u0041\u030A', NFC)=NORMALIZE_AND_CASEFOLD('\u00C5', NFC) AS nfc,
+  NORMALIZE_AND_CASEFOLD('\u0041\u030A', NFKD)=NORMALIZE_AND_CASEFOLD('\u00C5', NFKD) AS nkfd,
+  NORMALIZE_AND_CASEFOLD('\u0041\u030A', NFKC)=NORMALIZE_AND_CASEFOLD('\u00C5', NFKC) AS nkfc
+--
+QueryStatement [0-419]
+  Query [0-419]
+    Select [0-419]
+      SelectList [9-419]
+        SelectColumn [9-28]
+          StringLiteral [9-23]
+            StringLiteralComponent('\u0041\u030A') [9-23]
+          Alias [24-28]
+            Identifier(a) [27-28]
+        SelectColumn [32-45]
+          StringLiteral [32-40]
+            StringLiteralComponent('\u00C5') [32-40]
+          Alias [41-45]
+            Identifier(b) [44-45]
+        SelectColumn [49-137]
+          BinaryExpression(=) [49-130]
+            FunctionCall [49-92]
+              PathExpression [49-71]
+                Identifier(NORMALIZE_AND_CASEFOLD) [49-71]
+              StringLiteral [72-86]
+                StringLiteralComponent('\u0041\u030A') [72-86]
+              PathExpression [88-91]
+                Identifier(NFD) [88-91]
+            FunctionCall [93-130]
+              PathExpression [93-115]
+                Identifier(NORMALIZE_AND_CASEFOLD) [93-115]
+              StringLiteral [116-124]
+                StringLiteralComponent('\u00C5') [116-124]
+              PathExpression [126-129]
+                Identifier(NFD) [126-129]
+          Alias [131-137]
+            Identifier(nfd) [134-137]
+        SelectColumn [141-229]
+          BinaryExpression(=) [141-222]
+            FunctionCall [141-184]
+              PathExpression [141-163]
+                Identifier(NORMALIZE_AND_CASEFOLD) [141-163]
+              StringLiteral [164-178]
+                StringLiteralComponent('\u0041\u030A') [164-178]
+              PathExpression [180-183]
+                Identifier(NFC) [180-183]
+            FunctionCall [185-222]
+              PathExpression [185-207]
+                Identifier(NORMALIZE_AND_CASEFOLD) [185-207]
+              StringLiteral [208-216]
+                StringLiteralComponent('\u00C5') [208-216]
+              PathExpression [218-221]
+                Identifier(NFC) [218-221]
+          Alias [223-229]
+            Identifier(nfc) [226-229]
+        SelectColumn [233-324]
+          BinaryExpression(=) [233-316]
+            FunctionCall [233-277]
+              PathExpression [233-255]
+                Identifier(NORMALIZE_AND_CASEFOLD) [233-255]
+              StringLiteral [256-270]
+                StringLiteralComponent('\u0041\u030A') [256-270]
+              PathExpression [272-276]
+                Identifier(NFKD) [272-276]
+            FunctionCall [278-316]
+              PathExpression [278-300]
+                Identifier(NORMALIZE_AND_CASEFOLD) [278-300]
+              StringLiteral [301-309]
+                StringLiteralComponent('\u00C5') [301-309]
+              PathExpression [311-315]
+                Identifier(NFKD) [311-315]
+          Alias [317-324]
+            Identifier(nkfd) [320-324]
+        SelectColumn [328-419]
+          BinaryExpression(=) [328-411]
+            FunctionCall [328-372]
+              PathExpression [328-350]
+                Identifier(NORMALIZE_AND_CASEFOLD) [328-350]
+              StringLiteral [351-365]
+                StringLiteralComponent('\u0041\u030A') [351-365]
+              PathExpression [367-371]
+                Identifier(NFKC) [367-371]
+            FunctionCall [373-411]
+              PathExpression [373-395]
+                Identifier(NORMALIZE_AND_CASEFOLD) [373-395]
+              StringLiteral [396-404]
+                StringLiteralComponent('\u00C5') [396-404]
+              PathExpression [406-410]
+                Identifier(NFKC) [406-410]
+          Alias [412-419]
+            Identifier(nkfc) [415-419]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#octet_length
+[mode=expression]
+OCTET_LENGTH(value)
+--
+FunctionCall [0-19]
+  PathExpression [0-12]
+    Identifier(OCTET_LENGTH) [0-12]
+  PathExpression [13-18]
+    Identifier(value) [13-18]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#regexp_contains
+[mode=expression]
+REGEXP_CONTAINS(value, regexp)
+--
+FunctionCall [0-30]
+  PathExpression [0-15]
+    Identifier(REGEXP_CONTAINS) [0-15]
+  PathExpression [16-21]
+    Identifier(value) [16-21]
+  PathExpression [23-29]
+    Identifier(regexp) [23-29]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#regexp_contains
+SELECT
+  'foo@example.com' AS email,
+  REGEXP_CONTAINS('foo@example.com', r'@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+') AS is_valid
+--
+QueryStatement [0-120]
+  Query [0-120]
+    Select [0-120]
+      SelectList [9-120]
+        SelectColumn [9-35]
+          StringLiteral [9-26]
+            StringLiteralComponent('foo@example.com') [9-26]
+          Alias [27-35]
+            Identifier(email) [30-35]
+        SelectColumn [39-120]
+          FunctionCall [39-108]
+            PathExpression [39-54]
+              Identifier(REGEXP_CONTAINS) [39-54]
+            StringLiteral [55-72]
+              StringLiteralComponent('foo@example.com') [55-72]
+            StringLiteral [74-107]
+              StringLiteralComponent(r'@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+') [74-107]
+          Alias [109-120]
+            Identifier(is_valid) [112-120]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#regexp_contains
+SELECT
+  'www.example.net' AS email,
+  REGEXP_CONTAINS('www.example.net', r'@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+') AS is_valid
+--
+QueryStatement [0-120]
+  Query [0-120]
+    Select [0-120]
+      SelectList [9-120]
+        SelectColumn [9-35]
+          StringLiteral [9-26]
+            StringLiteralComponent('www.example.net') [9-26]
+          Alias [27-35]
+            Identifier(email) [30-35]
+        SelectColumn [39-120]
+          FunctionCall [39-108]
+            PathExpression [39-54]
+              Identifier(REGEXP_CONTAINS) [39-54]
+            StringLiteral [55-72]
+              StringLiteralComponent('www.example.net') [55-72]
+            StringLiteral [74-107]
+              StringLiteralComponent(r'@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+') [74-107]
+          Alias [109-120]
+            Identifier(is_valid) [112-120]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#regexp_contains
+SELECT
+  'a@foo.com' AS email,
+  REGEXP_CONTAINS('a@foo.com', r'^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$') AS valid_email_address,
+  REGEXP_CONTAINS('a@foo.com', r'^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$') AS without_parentheses
+--
+QueryStatement [0-225]
+  Query [0-225]
+    Select [0-225]
+      SelectList [9-225]
+        SelectColumn [9-29]
+          StringLiteral [9-20]
+            StringLiteralComponent('a@foo.com') [9-20]
+          Alias [21-29]
+            Identifier(email) [24-29]
+        SelectColumn [33-128]
+          FunctionCall [33-105]
+            PathExpression [33-48]
+              Identifier(REGEXP_CONTAINS) [33-48]
+            StringLiteral [49-60]
+              StringLiteralComponent('a@foo.com') [49-60]
+            StringLiteral [62-104]
+              StringLiteralComponent(r'^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$') [62-104]
+          Alias [106-128]
+            Identifier(valid_email_address) [109-128]
+        SelectColumn [132-225]
+          FunctionCall [132-202]
+            PathExpression [132-147]
+              Identifier(REGEXP_CONTAINS) [132-147]
+            StringLiteral [148-159]
+              StringLiteralComponent('a@foo.com') [148-159]
+            StringLiteral [161-201]
+              StringLiteralComponent(r'^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$') [161-201]
+          Alias [203-225]
+            Identifier(without_parentheses) [206-225]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#regexp_contains
+SELECT
+  'a@foo.computer' AS email,
+  REGEXP_CONTAINS('a@foo.computer', r'^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$') AS valid_email_address,
+  REGEXP_CONTAINS('a@foo.computer', r'^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$') AS without_parentheses
+--
+QueryStatement [0-240]
+  Query [0-240]
+    Select [0-240]
+      SelectList [9-240]
+        SelectColumn [9-34]
+          StringLiteral [9-25]
+            StringLiteralComponent('a@foo.computer') [9-25]
+          Alias [26-34]
+            Identifier(email) [29-34]
+        SelectColumn [38-138]
+          FunctionCall [38-115]
+            PathExpression [38-53]
+              Identifier(REGEXP_CONTAINS) [38-53]
+            StringLiteral [54-70]
+              StringLiteralComponent('a@foo.computer') [54-70]
+            StringLiteral [72-114]
+              StringLiteralComponent(r'^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$') [72-114]
+          Alias [116-138]
+            Identifier(valid_email_address) [119-138]
+        SelectColumn [142-240]
+          FunctionCall [142-217]
+            PathExpression [142-157]
+              Identifier(REGEXP_CONTAINS) [142-157]
+            StringLiteral [158-174]
+              StringLiteralComponent('a@foo.computer') [158-174]
+            StringLiteral [176-216]
+              StringLiteralComponent(r'^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$') [176-216]
+          Alias [218-240]
+            Identifier(without_parentheses) [221-240]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#regexp_contains
+SELECT
+  'b@bar.org' AS email,
+  REGEXP_CONTAINS('b@bar.org', r'^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$') AS valid_email_address,
+  REGEXP_CONTAINS('b@bar.org', r'^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$') AS without_parentheses
+--
+QueryStatement [0-225]
+  Query [0-225]
+    Select [0-225]
+      SelectList [9-225]
+        SelectColumn [9-29]
+          StringLiteral [9-20]
+            StringLiteralComponent('b@bar.org') [9-20]
+          Alias [21-29]
+            Identifier(email) [24-29]
+        SelectColumn [33-128]
+          FunctionCall [33-105]
+            PathExpression [33-48]
+              Identifier(REGEXP_CONTAINS) [33-48]
+            StringLiteral [49-60]
+              StringLiteralComponent('b@bar.org') [49-60]
+            StringLiteral [62-104]
+              StringLiteralComponent(r'^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$') [62-104]
+          Alias [106-128]
+            Identifier(valid_email_address) [109-128]
+        SelectColumn [132-225]
+          FunctionCall [132-202]
+            PathExpression [132-147]
+              Identifier(REGEXP_CONTAINS) [132-147]
+            StringLiteral [148-159]
+              StringLiteralComponent('b@bar.org') [148-159]
+            StringLiteral [161-201]
+              StringLiteralComponent(r'^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$') [161-201]
+          Alias [203-225]
+            Identifier(without_parentheses) [206-225]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#regexp_contains
+SELECT
+  '!b@bar.org' AS email,
+  REGEXP_CONTAINS('!b@bar.org', r'^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$') AS valid_email_address,
+  REGEXP_CONTAINS('!b@bar.org', r'^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$') AS without_parentheses
+--
+QueryStatement [0-228]
+  Query [0-228]
+    Select [0-228]
+      SelectList [9-228]
+        SelectColumn [9-30]
+          StringLiteral [9-21]
+            StringLiteralComponent('!b@bar.org') [9-21]
+          Alias [22-30]
+            Identifier(email) [25-30]
+        SelectColumn [34-130]
+          FunctionCall [34-107]
+            PathExpression [34-49]
+              Identifier(REGEXP_CONTAINS) [34-49]
+            StringLiteral [50-62]
+              StringLiteralComponent('!b@bar.org') [50-62]
+            StringLiteral [64-106]
+              StringLiteralComponent(r'^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$') [64-106]
+          Alias [108-130]
+            Identifier(valid_email_address) [111-130]
+        SelectColumn [134-228]
+          FunctionCall [134-205]
+            PathExpression [134-149]
+              Identifier(REGEXP_CONTAINS) [134-149]
+            StringLiteral [150-162]
+              StringLiteralComponent('!b@bar.org') [150-162]
+            StringLiteral [164-204]
+              StringLiteralComponent(r'^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$') [164-204]
+          Alias [206-228]
+            Identifier(without_parentheses) [209-228]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#regexp_contains
+SELECT
+  'c@buz.net' AS email,
+  REGEXP_CONTAINS('c@buz.net', r'^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$') AS valid_email_address,
+  REGEXP_CONTAINS('c@buz.net', r'^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$') AS without_parentheses
+--
+QueryStatement [0-225]
+  Query [0-225]
+    Select [0-225]
+      SelectList [9-225]
+        SelectColumn [9-29]
+          StringLiteral [9-20]
+            StringLiteralComponent('c@buz.net') [9-20]
+          Alias [21-29]
+            Identifier(email) [24-29]
+        SelectColumn [33-128]
+          FunctionCall [33-105]
+            PathExpression [33-48]
+              Identifier(REGEXP_CONTAINS) [33-48]
+            StringLiteral [49-60]
+              StringLiteralComponent('c@buz.net') [49-60]
+            StringLiteral [62-104]
+              StringLiteralComponent(r'^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$') [62-104]
+          Alias [106-128]
+            Identifier(valid_email_address) [109-128]
+        SelectColumn [132-225]
+          FunctionCall [132-202]
+            PathExpression [132-147]
+              Identifier(REGEXP_CONTAINS) [132-147]
+            StringLiteral [148-159]
+              StringLiteralComponent('c@buz.net') [148-159]
+            StringLiteral [161-201]
+              StringLiteralComponent(r'^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$') [161-201]
+          Alias [203-225]
+            Identifier(without_parentheses) [206-225]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#regexp_extract
+[mode=expression]
+REGEXP_EXTRACT(value, regexp)
+--
+FunctionCall [0-29]
+  PathExpression [0-14]
+    Identifier(REGEXP_EXTRACT) [0-14]
+  PathExpression [15-20]
+    Identifier(value) [15-20]
+  PathExpression [22-28]
+    Identifier(regexp) [22-28]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#regexp_extract
+SELECT REGEXP_EXTRACT('foo@example.com', r'^[a-zA-Z0-9_.+-]+') AS user_name
+--
+QueryStatement [0-75]
+  Query [0-75]
+    Select [0-75]
+      SelectList [7-75]
+        SelectColumn [7-75]
+          FunctionCall [7-62]
+            PathExpression [7-21]
+              Identifier(REGEXP_EXTRACT) [7-21]
+            StringLiteral [22-39]
+              StringLiteralComponent('foo@example.com') [22-39]
+            StringLiteral [41-61]
+              StringLiteralComponent(r'^[a-zA-Z0-9_.+-]+') [41-61]
+          Alias [63-75]
+            Identifier(user_name) [66-75]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#regexp_extract
+SELECT REGEXP_EXTRACT('foo@example.com', r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.([a-zA-Z0-9-.]+$)')
+--
+QueryStatement [0-95]
+  Query [0-95]
+    Select [0-95]
+      SelectList [7-95]
+        SelectColumn [7-95]
+          FunctionCall [7-95]
+            PathExpression [7-21]
+              Identifier(REGEXP_EXTRACT) [7-21]
+            StringLiteral [22-39]
+              StringLiteralComponent('foo@example.com') [22-39]
+            StringLiteral [41-94]
+              StringLiteralComponent(r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.([a-zA-Z0-9-.]+$)') [41-94]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#regexp_extract
+SELECT
+  REGEXP_EXTRACT('ab', '.b') AS result_a,
+  REGEXP_EXTRACT('ab', '(.)b') AS result_b,
+  REGEXP_EXTRACT('xyztb', '(.)+b') AS result_c,
+  REGEXP_EXTRACT('ab', '(z)?b') AS result_d
+--
+QueryStatement [0-184]
+  Query [0-184]
+    Select [0-184]
+      SelectList [9-184]
+        SelectColumn [9-47]
+          FunctionCall [9-35]
+            PathExpression [9-23]
+              Identifier(REGEXP_EXTRACT) [9-23]
+            StringLiteral [24-28]
+              StringLiteralComponent('ab') [24-28]
+            StringLiteral [30-34]
+              StringLiteralComponent('.b') [30-34]
+          Alias [36-47]
+            Identifier(result_a) [39-47]
+        SelectColumn [51-91]
+          FunctionCall [51-79]
+            PathExpression [51-65]
+              Identifier(REGEXP_EXTRACT) [51-65]
+            StringLiteral [66-70]
+              StringLiteralComponent('ab') [66-70]
+            StringLiteral [72-78]
+              StringLiteralComponent('(.)b') [72-78]
+          Alias [80-91]
+            Identifier(result_b) [83-91]
+        SelectColumn [95-139]
+          FunctionCall [95-127]
+            PathExpression [95-109]
+              Identifier(REGEXP_EXTRACT) [95-109]
+            StringLiteral [110-117]
+              StringLiteralComponent('xyztb') [110-117]
+            StringLiteral [119-126]
+              StringLiteralComponent('(.)+b') [119-126]
+          Alias [128-139]
+            Identifier(result_c) [131-139]
+        SelectColumn [143-184]
+          FunctionCall [143-172]
+            PathExpression [143-157]
+              Identifier(REGEXP_EXTRACT) [143-157]
+            StringLiteral [158-162]
+              StringLiteralComponent('ab') [158-162]
+            StringLiteral [164-171]
+              StringLiteralComponent('(z)?b') [164-171]
+          Alias [173-184]
+            Identifier(result_d) [176-184]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#regexp_extract_all
+[mode=expression]
+REGEXP_EXTRACT_ALL(value, regexp)
+--
+FunctionCall [0-33]
+  PathExpression [0-18]
+    Identifier(REGEXP_EXTRACT_ALL) [0-18]
+  PathExpression [19-24]
+    Identifier(value) [19-24]
+  PathExpression [26-32]
+    Identifier(regexp) [26-32]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#regexp_extract_all
+SELECT REGEXP_EXTRACT_ALL('Try `func(x)` or `func(y)`', '`(.+?)`') AS example
+--
+QueryStatement [0-77]
+  Query [0-77]
+    Select [0-77]
+      SelectList [7-77]
+        SelectColumn [7-77]
+          FunctionCall [7-66]
+            PathExpression [7-25]
+              Identifier(REGEXP_EXTRACT_ALL) [7-25]
+            StringLiteral [26-54]
+              StringLiteralComponent('Try `func(x)` or `func(y)`') [26-54]
+            StringLiteral [56-65]
+              StringLiteralComponent('`(.+?)`') [56-65]
+          Alias [67-77]
+            Identifier(example) [70-77]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#regexp_extract_all
+SELECT REGEXP_EXTRACT_ALL('123456', r'\d(\d)\d') AS example
+--
+QueryStatement [0-59]
+  Query [0-59]
+    Select [0-59]
+      SelectList [7-59]
+        SelectColumn [7-59]
+          FunctionCall [7-48]
+            PathExpression [7-25]
+              Identifier(REGEXP_EXTRACT_ALL) [7-25]
+            StringLiteral [26-34]
+              StringLiteralComponent('123456') [26-34]
+            StringLiteral [36-47]
+              StringLiteralComponent(r'\d(\d)\d') [36-47]
+          Alias [49-59]
+            Identifier(example) [52-59]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#regexp_replace
+[mode=expression]
+REGEXP_REPLACE(value, regexp, replacement)
+--
+FunctionCall [0-42]
+  PathExpression [0-14]
+    Identifier(REGEXP_REPLACE) [0-14]
+  PathExpression [15-20]
+    Identifier(value) [15-20]
+  PathExpression [22-28]
+    Identifier(regexp) [22-28]
+  PathExpression [30-41]
+    Identifier(replacement) [30-41]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#regexp_replace
+SELECT REGEXP_REPLACE('Jane Doe', r'^([\p{L}\x27-]+)\s+([\p{L}\x27-]+)$', r'\2, \1') AS formatted_name
+--
+QueryStatement [0-102]
+  Query [0-102]
+    Select [0-102]
+      SelectList [7-102]
+        SelectColumn [7-102]
+          FunctionCall [7-84]
+            PathExpression [7-21]
+              Identifier(REGEXP_REPLACE) [7-21]
+            StringLiteral [22-32]
+              StringLiteralComponent('Jane Doe') [22-32]
+            StringLiteral [34-72]
+              StringLiteralComponent(r'^([\p{L}\x27-]+)\s+([\p{L}\x27-]+)$') [34-72]
+            StringLiteral [74-83]
+              StringLiteralComponent(r'\2, \1') [74-83]
+          Alias [85-102]
+            Identifier(formatted_name) [88-102]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#repeat
+[mode=expression]
+REPEAT(original_value, repetitions)
+--
+FunctionCall [0-35]
+  PathExpression [0-6]
+    Identifier(REPEAT) [0-6]
+  PathExpression [7-21]
+    Identifier(original_value) [7-21]
+  PathExpression [23-34]
+    Identifier(repetitions) [23-34]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#repeat
+SELECT REPEAT('abc', 3) AS results
+--
+QueryStatement [0-34]
+  Query [0-34]
+    Select [0-34]
+      SelectList [7-34]
+        SelectColumn [7-34]
+          FunctionCall [7-23]
+            PathExpression [7-13]
+              Identifier(REPEAT) [7-13]
+            StringLiteral [14-19]
+              StringLiteralComponent('abc') [14-19]
+            IntLiteral(3) [21-22]
+          Alias [24-34]
+            Identifier(results) [27-34]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#repeat
+SELECT REPEAT('abc', NULL) AS results
+--
+QueryStatement [0-37]
+  Query [0-37]
+    Select [0-37]
+      SelectList [7-37]
+        SelectColumn [7-37]
+          FunctionCall [7-26]
+            PathExpression [7-13]
+              Identifier(REPEAT) [7-13]
+            StringLiteral [14-19]
+              StringLiteralComponent('abc') [14-19]
+            NullLiteral(NULL) [21-25]
+          Alias [27-37]
+            Identifier(results) [30-37]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#repeat
+SELECT REPEAT(NULL, 3) AS results
+--
+QueryStatement [0-33]
+  Query [0-33]
+    Select [0-33]
+      SelectList [7-33]
+        SelectColumn [7-33]
+          FunctionCall [7-22]
+            PathExpression [7-13]
+              Identifier(REPEAT) [7-13]
+            NullLiteral(NULL) [14-18]
+            IntLiteral(3) [20-21]
+          Alias [23-33]
+            Identifier(results) [26-33]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#replace
+[mode=expression]
+REPLACE(original_value, from_pattern, to_pattern)
+--
+FunctionCall [0-49]
+  PathExpression [0-7]
+    Identifier(`REPLACE`) [0-7]
+  PathExpression [8-22]
+    Identifier(original_value) [8-22]
+  PathExpression [24-36]
+    Identifier(from_pattern) [24-36]
+  PathExpression [38-48]
+    Identifier(to_pattern) [38-48]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#replace
+WITH desserts AS
+  (SELECT 'apple pie' as dessert
+  UNION ALL
+  SELECT 'blackberry pie' as dessert
+  UNION ALL
+  SELECT 'cherry pie' as dessert)
+
+SELECT
+  REPLACE (dessert, 'pie', 'cobbler') as example
+FROM desserts
+--
+QueryStatement [0-215]
+  Query [0-215]
+    WithClause [0-144]
+      WithClauseEntry [5-144]
+        AliasedQuery [5-144]
+          Identifier(desserts) [5-13]
+          Query [19-144]
+            SetOperation(UNION ALL) [20-143]
+              SetOperationMetadataList [52-110]
+                SetOperationMetadata [52-61]
+                  SetOperationType [52-57]
+                  SetOperationAllOrDistinct [58-61]
+                SetOperationMetadata [101-110]
+                  SetOperationType [101-106]
+                  SetOperationAllOrDistinct [107-110]
+              Select [20-49]
+                SelectList [27-49]
+                  SelectColumn [27-49]
+                    StringLiteral [27-38]
+                      StringLiteralComponent('apple pie') [27-38]
+                    Alias [39-49]
+                      Identifier(dessert) [42-49]
+              Select [64-98]
+                SelectList [71-98]
+                  SelectColumn [71-98]
+                    StringLiteral [71-87]
+                      StringLiteralComponent('blackberry pie') [71-87]
+                    Alias [88-98]
+                      Identifier(dessert) [91-98]
+              Select [113-143]
+                SelectList [120-143]
+                  SelectColumn [120-143]
+                    StringLiteral [120-132]
+                      StringLiteralComponent('cherry pie') [120-132]
+                    Alias [133-143]
+                      Identifier(dessert) [136-143]
+    Select [146-215]
+      SelectList [155-201]
+        SelectColumn [155-201]
+          FunctionCall [155-190]
+            PathExpression [155-162]
+              Identifier(`REPLACE`) [155-162]
+            PathExpression [164-171]
+              Identifier(dessert) [164-171]
+            StringLiteral [173-178]
+              StringLiteralComponent('pie') [173-178]
+            StringLiteral [180-189]
+              StringLiteralComponent('cobbler') [180-189]
+          Alias [191-201]
+            Identifier(example) [194-201]
+      FromClause [202-215]
+        TablePathExpression [207-215]
+          PathExpression [207-215]
+            Identifier(desserts) [207-215]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#reverse
+[mode=expression]
+REVERSE(value)
+--
+FunctionCall [0-14]
+  PathExpression [0-7]
+    Identifier(REVERSE) [0-7]
+  PathExpression [8-13]
+    Identifier(value) [8-13]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#reverse
+SELECT REVERSE('abc') AS results
+--
+QueryStatement [0-32]
+  Query [0-32]
+    Select [0-32]
+      SelectList [7-32]
+        SelectColumn [7-32]
+          FunctionCall [7-21]
+            PathExpression [7-14]
+              Identifier(REVERSE) [7-14]
+            StringLiteral [15-20]
+              StringLiteralComponent('abc') [15-20]
+          Alias [22-32]
+            Identifier(results) [25-32]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#reverse
+SELECT FORMAT('%T', REVERSE(b'1a3')) AS results
+--
+QueryStatement [0-47]
+  Query [0-47]
+    Select [0-47]
+      SelectList [7-47]
+        SelectColumn [7-47]
+          FunctionCall [7-36]
+            PathExpression [7-13]
+              Identifier(FORMAT) [7-13]
+            StringLiteral [14-18]
+              StringLiteralComponent('%T') [14-18]
+            FunctionCall [20-35]
+              PathExpression [20-27]
+                Identifier(REVERSE) [20-27]
+              BytesLiteral [28-34]
+                BytesLiteralComponent(b'1a3') [28-34]
+          Alias [37-47]
+            Identifier(results) [40-47]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#rpad
+SELECT FORMAT('%T', RPAD('c', 5)) AS results
+--
+QueryStatement [0-44]
+  Query [0-44]
+    Select [0-44]
+      SelectList [7-44]
+        SelectColumn [7-44]
+          FunctionCall [7-33]
+            PathExpression [7-13]
+              Identifier(FORMAT) [7-13]
+            StringLiteral [14-18]
+              StringLiteralComponent('%T') [14-18]
+            FunctionCall [20-32]
+              PathExpression [20-24]
+                Identifier(RPAD) [20-24]
+              StringLiteral [25-28]
+                StringLiteralComponent('c') [25-28]
+              IntLiteral(5) [30-31]
+          Alias [34-44]
+            Identifier(results) [37-44]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#rpad
+SELECT RPAD('b', 5, 'a') AS results
+--
+QueryStatement [0-35]
+  Query [0-35]
+    Select [0-35]
+      SelectList [7-35]
+        SelectColumn [7-35]
+          FunctionCall [7-24]
+            PathExpression [7-11]
+              Identifier(RPAD) [7-11]
+            StringLiteral [12-15]
+              StringLiteralComponent('b') [12-15]
+            IntLiteral(5) [17-18]
+            StringLiteral [20-23]
+              StringLiteralComponent('a') [20-23]
+          Alias [25-35]
+            Identifier(results) [28-35]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#rpad
+SELECT RPAD('abc', 10, 'ghd') AS results
+--
+QueryStatement [0-40]
+  Query [0-40]
+    Select [0-40]
+      SelectList [7-40]
+        SelectColumn [7-40]
+          FunctionCall [7-29]
+            PathExpression [7-11]
+              Identifier(RPAD) [7-11]
+            StringLiteral [12-17]
+              StringLiteralComponent('abc') [12-17]
+            IntLiteral(10) [19-21]
+            StringLiteral [23-28]
+              StringLiteralComponent('ghd') [23-28]
+          Alias [30-40]
+            Identifier(results) [33-40]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#rpad
+SELECT RPAD('abc', 2, 'd') AS results
+--
+QueryStatement [0-37]
+  Query [0-37]
+    Select [0-37]
+      SelectList [7-37]
+        SelectColumn [7-37]
+          FunctionCall [7-26]
+            PathExpression [7-11]
+              Identifier(RPAD) [7-11]
+            StringLiteral [12-17]
+              StringLiteralComponent('abc') [12-17]
+            IntLiteral(2) [19-20]
+            StringLiteral [22-25]
+              StringLiteralComponent('d') [22-25]
+          Alias [27-37]
+            Identifier(results) [30-37]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#rpad
+SELECT FORMAT('%T', RPAD(b'abc', 10, b'ghd')) AS results
+--
+QueryStatement [0-56]
+  Query [0-56]
+    Select [0-56]
+      SelectList [7-56]
+        SelectColumn [7-56]
+          FunctionCall [7-45]
+            PathExpression [7-13]
+              Identifier(FORMAT) [7-13]
+            StringLiteral [14-18]
+              StringLiteralComponent('%T') [14-18]
+            FunctionCall [20-44]
+              PathExpression [20-24]
+                Identifier(RPAD) [20-24]
+              BytesLiteral [25-31]
+                BytesLiteralComponent(b'abc') [25-31]
+              IntLiteral(10) [33-35]
+              BytesLiteral [37-43]
+                BytesLiteralComponent(b'ghd') [37-43]
+          Alias [46-56]
+            Identifier(results) [49-56]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#rtrim
+SELECT RTRIM('***apple***', '*') AS example
+--
+QueryStatement [0-43]
+  Query [0-43]
+    Select [0-43]
+      SelectList [7-43]
+        SelectColumn [7-43]
+          FunctionCall [7-32]
+            PathExpression [7-12]
+              Identifier(RTRIM) [7-12]
+            StringLiteral [13-26]
+              StringLiteralComponent('***apple***') [13-26]
+            StringLiteral [28-31]
+              StringLiteralComponent('*') [28-31]
+          Alias [33-43]
+            Identifier(example) [36-43]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#rtrim
+SELECT RTRIM('applexxz', 'xyz') AS example
+--
+QueryStatement [0-42]
+  Query [0-42]
+    Select [0-42]
+      SelectList [7-42]
+        SelectColumn [7-42]
+          FunctionCall [7-31]
+            PathExpression [7-12]
+              Identifier(RTRIM) [7-12]
+            StringLiteral [13-23]
+              StringLiteralComponent('applexxz') [13-23]
+            StringLiteral [25-30]
+              StringLiteralComponent('xyz') [25-30]
+          Alias [32-42]
+            Identifier(example) [35-42]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#safe_convert_bytes_to_string
+[mode=expression]
+SAFE_CONVERT_BYTES_TO_STRING(value)
+--
+FunctionCall [0-35]
+  PathExpression [0-28]
+    Identifier(SAFE_CONVERT_BYTES_TO_STRING) [0-28]
+  PathExpression [29-34]
+    Identifier(value) [29-34]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#safe_convert_bytes_to_string
+SELECT SAFE_CONVERT_BYTES_TO_STRING(b'\xc2') as safe_convert
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-60]
+      SelectList [7-60]
+        SelectColumn [7-60]
+          FunctionCall [7-44]
+            PathExpression [7-35]
+              Identifier(SAFE_CONVERT_BYTES_TO_STRING) [7-35]
+            BytesLiteral [36-43]
+              BytesLiteralComponent(b'\xc2') [36-43]
+          Alias [45-60]
+            Identifier(safe_convert) [48-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#soundex
+[mode=expression]
+SOUNDEX(value)
+--
+FunctionCall [0-14]
+  PathExpression [0-7]
+    Identifier(SOUNDEX) [0-7]
+  PathExpression [8-13]
+    Identifier(value) [8-13]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#soundex
+SELECT 'Ashcraft' AS value, SOUNDEX('Ashcraft') AS soundex
+--
+QueryStatement [0-58]
+  Query [0-58]
+    Select [0-58]
+      SelectList [7-58]
+        SelectColumn [7-26]
+          StringLiteral [7-17]
+            StringLiteralComponent('Ashcraft') [7-17]
+          Alias [18-26]
+            Identifier(value) [21-26]
+        SelectColumn [28-58]
+          FunctionCall [28-47]
+            PathExpression [28-35]
+              Identifier(SOUNDEX) [28-35]
+            StringLiteral [36-46]
+              StringLiteralComponent('Ashcraft') [36-46]
+          Alias [48-58]
+            Identifier(soundex) [51-58]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#split
+WITH letters AS
+  (SELECT '' as letter_group
+  UNION ALL
+  SELECT 'a' as letter_group
+  UNION ALL
+  SELECT 'b c d' as letter_group)
+
+SELECT SPLIT(letter_group, ' ') as example
+FROM letters
+--
+QueryStatement [0-188]
+  Query [0-188]
+    WithClause [0-131]
+      WithClauseEntry [5-131]
+        AliasedQuery [5-131]
+          Identifier(letters) [5-12]
+          Query [18-131]
+            SetOperation(UNION ALL) [19-130]
+              SetOperationMetadataList [47-97]
+                SetOperationMetadata [47-56]
+                  SetOperationType [47-52]
+                  SetOperationAllOrDistinct [53-56]
+                SetOperationMetadata [88-97]
+                  SetOperationType [88-93]
+                  SetOperationAllOrDistinct [94-97]
+              Select [19-44]
+                SelectList [26-44]
+                  SelectColumn [26-44]
+                    StringLiteral [26-28]
+                      StringLiteralComponent('') [26-28]
+                    Alias [29-44]
+                      Identifier(letter_group) [32-44]
+              Select [59-85]
+                SelectList [66-85]
+                  SelectColumn [66-85]
+                    StringLiteral [66-69]
+                      StringLiteralComponent('a') [66-69]
+                    Alias [70-85]
+                      Identifier(letter_group) [73-85]
+              Select [100-130]
+                SelectList [107-130]
+                  SelectColumn [107-130]
+                    StringLiteral [107-114]
+                      StringLiteralComponent('b c d') [107-114]
+                    Alias [115-130]
+                      Identifier(letter_group) [118-130]
+    Select [133-188]
+      SelectList [140-175]
+        SelectColumn [140-175]
+          FunctionCall [140-164]
+            PathExpression [140-145]
+              Identifier(SPLIT) [140-145]
+            PathExpression [146-158]
+              Identifier(letter_group) [146-158]
+            StringLiteral [160-163]
+              StringLiteralComponent(' ') [160-163]
+          Alias [165-175]
+            Identifier(example) [168-175]
+      FromClause [176-188]
+        TablePathExpression [181-188]
+          PathExpression [181-188]
+            Identifier(letters) [181-188]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#split_substr
+SELECT SPLIT_SUBSTR("www.abc.xyz.com", ".", 1, 0) AS example
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-60]
+      SelectList [7-60]
+        SelectColumn [7-60]
+          FunctionCall [7-49]
+            PathExpression [7-19]
+              Identifier(SPLIT_SUBSTR) [7-19]
+            StringLiteral [20-37]
+              StringLiteralComponent("www.abc.xyz.com") [20-37]
+            StringLiteral [39-42]
+              StringLiteralComponent(".") [39-42]
+            IntLiteral(1) [44-45]
+            IntLiteral(0) [47-48]
+          Alias [50-60]
+            Identifier(example) [53-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#split_substr
+SELECT SPLIT_SUBSTR("www.abc.xyz.com", ".", 1, 2) AS example
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-60]
+      SelectList [7-60]
+        SelectColumn [7-60]
+          FunctionCall [7-49]
+            PathExpression [7-19]
+              Identifier(SPLIT_SUBSTR) [7-19]
+            StringLiteral [20-37]
+              StringLiteralComponent("www.abc.xyz.com") [20-37]
+            StringLiteral [39-42]
+              StringLiteralComponent(".") [39-42]
+            IntLiteral(1) [44-45]
+            IntLiteral(2) [47-48]
+          Alias [50-60]
+            Identifier(example) [53-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#split_substr
+SELECT SPLIT_SUBSTR("www.abc.xyz.com", ".", 1, 1) AS example
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-60]
+      SelectList [7-60]
+        SelectColumn [7-60]
+          FunctionCall [7-49]
+            PathExpression [7-19]
+              Identifier(SPLIT_SUBSTR) [7-19]
+            StringLiteral [20-37]
+              StringLiteralComponent("www.abc.xyz.com") [20-37]
+            StringLiteral [39-42]
+              StringLiteralComponent(".") [39-42]
+            IntLiteral(1) [44-45]
+            IntLiteral(1) [47-48]
+          Alias [50-60]
+            Identifier(example) [53-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#split_substr
+SELECT SPLIT_SUBSTR("www.abc.xyz.com", ".", -1, 1) AS example
+--
+QueryStatement [0-61]
+  Query [0-61]
+    Select [0-61]
+      SelectList [7-61]
+        SelectColumn [7-61]
+          FunctionCall [7-50]
+            PathExpression [7-19]
+              Identifier(SPLIT_SUBSTR) [7-19]
+            StringLiteral [20-37]
+              StringLiteralComponent("www.abc.xyz.com") [20-37]
+            StringLiteral [39-42]
+              StringLiteralComponent(".") [39-42]
+            UnaryExpression(-) [44-46]
+              IntLiteral(1) [45-46]
+            IntLiteral(1) [48-49]
+          Alias [51-61]
+            Identifier(example) [54-61]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#split_substr
+SELECT SPLIT_SUBSTR("www.abc.xyz.com", ".", 1, 3) AS example
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-60]
+      SelectList [7-60]
+        SelectColumn [7-60]
+          FunctionCall [7-49]
+            PathExpression [7-19]
+              Identifier(SPLIT_SUBSTR) [7-19]
+            StringLiteral [20-37]
+              StringLiteralComponent("www.abc.xyz.com") [20-37]
+            StringLiteral [39-42]
+              StringLiteralComponent(".") [39-42]
+            IntLiteral(1) [44-45]
+            IntLiteral(3) [47-48]
+          Alias [50-60]
+            Identifier(example) [53-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#split_substr
+SELECT SPLIT_SUBSTR("www.abc.xyz.com", ".", 0, 3) AS example
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-60]
+      SelectList [7-60]
+        SelectColumn [7-60]
+          FunctionCall [7-49]
+            PathExpression [7-19]
+              Identifier(SPLIT_SUBSTR) [7-19]
+            StringLiteral [20-37]
+              StringLiteralComponent("www.abc.xyz.com") [20-37]
+            StringLiteral [39-42]
+              StringLiteralComponent(".") [39-42]
+            IntLiteral(0) [44-45]
+            IntLiteral(3) [47-48]
+          Alias [50-60]
+            Identifier(example) [53-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#split_substr
+SELECT SPLIT_SUBSTR("www.abc.xyz.com", ".", 5, 3) AS example
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-60]
+      SelectList [7-60]
+        SelectColumn [7-60]
+          FunctionCall [7-49]
+            PathExpression [7-19]
+              Identifier(SPLIT_SUBSTR) [7-19]
+            StringLiteral [20-37]
+              StringLiteralComponent("www.abc.xyz.com") [20-37]
+            StringLiteral [39-42]
+              StringLiteralComponent(".") [39-42]
+            IntLiteral(5) [44-45]
+            IntLiteral(3) [47-48]
+          Alias [50-60]
+            Identifier(example) [53-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#split_substr
+SELECT SPLIT_SUBSTR("www.abc.xyz.com", ".", -5, 3) AS example
+--
+QueryStatement [0-61]
+  Query [0-61]
+    Select [0-61]
+      SelectList [7-61]
+        SelectColumn [7-61]
+          FunctionCall [7-50]
+            PathExpression [7-19]
+              Identifier(SPLIT_SUBSTR) [7-19]
+            StringLiteral [20-37]
+              StringLiteralComponent("www.abc.xyz.com") [20-37]
+            StringLiteral [39-42]
+              StringLiteralComponent(".") [39-42]
+            UnaryExpression(-) [44-46]
+              IntLiteral(5) [45-46]
+            IntLiteral(3) [48-49]
+          Alias [51-61]
+            Identifier(example) [54-61]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#split_substr
+SELECT SPLIT_SUBSTR("www.abc.xyz.com", ".", 3) AS example
+--
+QueryStatement [0-57]
+  Query [0-57]
+    Select [0-57]
+      SelectList [7-57]
+        SelectColumn [7-57]
+          FunctionCall [7-46]
+            PathExpression [7-19]
+              Identifier(SPLIT_SUBSTR) [7-19]
+            StringLiteral [20-37]
+              StringLiteralComponent("www.abc.xyz.com") [20-37]
+            StringLiteral [39-42]
+              StringLiteralComponent(".") [39-42]
+            IntLiteral(3) [44-45]
+          Alias [47-57]
+            Identifier(example) [50-57]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#split_substr
+SELECT SPLIT_SUBSTR('aaa***bbb***ccc', '**', 1, 2) AS example
+--
+QueryStatement [0-61]
+  Query [0-61]
+    Select [0-61]
+      SelectList [7-61]
+        SelectColumn [7-61]
+          FunctionCall [7-50]
+            PathExpression [7-19]
+              Identifier(SPLIT_SUBSTR) [7-19]
+            StringLiteral [20-37]
+              StringLiteralComponent('aaa***bbb***ccc') [20-37]
+            StringLiteral [39-43]
+              StringLiteralComponent('**') [39-43]
+            IntLiteral(1) [45-46]
+            IntLiteral(2) [48-49]
+          Alias [51-61]
+            Identifier(example) [54-61]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#split_substr
+SELECT SPLIT_SUBSTR('aaa***bbb***ccc', '**', 2, 2) AS example
+--
+QueryStatement [0-61]
+  Query [0-61]
+    Select [0-61]
+      SelectList [7-61]
+        SelectColumn [7-61]
+          FunctionCall [7-50]
+            PathExpression [7-19]
+              Identifier(SPLIT_SUBSTR) [7-19]
+            StringLiteral [20-37]
+              StringLiteralComponent('aaa***bbb***ccc') [20-37]
+            StringLiteral [39-43]
+              StringLiteralComponent('**') [39-43]
+            IntLiteral(2) [45-46]
+            IntLiteral(2) [48-49]
+          Alias [51-61]
+            Identifier(example) [54-61]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#starts_with
+[mode=expression]
+STARTS_WITH(value, prefix)
+--
+FunctionCall [0-26]
+  PathExpression [0-11]
+    Identifier(STARTS_WITH) [0-11]
+  PathExpression [12-17]
+    Identifier(value) [12-17]
+  PathExpression [19-25]
+    Identifier(prefix) [19-25]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#starts_with
+SELECT STARTS_WITH('bar', 'b') AS example
+--
+QueryStatement [0-41]
+  Query [0-41]
+    Select [0-41]
+      SelectList [7-41]
+        SelectColumn [7-41]
+          FunctionCall [7-30]
+            PathExpression [7-18]
+              Identifier(STARTS_WITH) [7-18]
+            StringLiteral [19-24]
+              StringLiteralComponent('bar') [19-24]
+            StringLiteral [26-29]
+              StringLiteralComponent('b') [26-29]
+          Alias [31-41]
+            Identifier(example) [34-41]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#strpos
+[mode=expression]
+STRPOS(value, subvalue)
+--
+FunctionCall [0-23]
+  PathExpression [0-6]
+    Identifier(STRPOS) [0-6]
+  PathExpression [7-12]
+    Identifier(value) [7-12]
+  PathExpression [14-22]
+    Identifier(subvalue) [14-22]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#strpos
+SELECT STRPOS('foo@example.com', '@') AS example
+--
+QueryStatement [0-48]
+  Query [0-48]
+    Select [0-48]
+      SelectList [7-48]
+        SelectColumn [7-48]
+          FunctionCall [7-37]
+            PathExpression [7-13]
+              Identifier(STRPOS) [7-13]
+            StringLiteral [14-31]
+              StringLiteralComponent('foo@example.com') [14-31]
+            StringLiteral [33-36]
+              StringLiteralComponent('@') [33-36]
+          Alias [38-48]
+            Identifier(example) [41-48]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#substr
+SELECT SUBSTR('apple', 2) AS example
+--
+QueryStatement [0-36]
+  Query [0-36]
+    Select [0-36]
+      SelectList [7-36]
+        SelectColumn [7-36]
+          FunctionCall [7-25]
+            PathExpression [7-13]
+              Identifier(SUBSTR) [7-13]
+            StringLiteral [14-21]
+              StringLiteralComponent('apple') [14-21]
+            IntLiteral(2) [23-24]
+          Alias [26-36]
+            Identifier(example) [29-36]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#substr
+SELECT SUBSTR('apple', 2, 2) AS example
+--
+QueryStatement [0-39]
+  Query [0-39]
+    Select [0-39]
+      SelectList [7-39]
+        SelectColumn [7-39]
+          FunctionCall [7-28]
+            PathExpression [7-13]
+              Identifier(SUBSTR) [7-13]
+            StringLiteral [14-21]
+              StringLiteralComponent('apple') [14-21]
+            IntLiteral(2) [23-24]
+            IntLiteral(2) [26-27]
+          Alias [29-39]
+            Identifier(example) [32-39]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#substr
+SELECT SUBSTR('apple', -2) AS example
+--
+QueryStatement [0-37]
+  Query [0-37]
+    Select [0-37]
+      SelectList [7-37]
+        SelectColumn [7-37]
+          FunctionCall [7-26]
+            PathExpression [7-13]
+              Identifier(SUBSTR) [7-13]
+            StringLiteral [14-21]
+              StringLiteralComponent('apple') [14-21]
+            UnaryExpression(-) [23-25]
+              IntLiteral(2) [24-25]
+          Alias [27-37]
+            Identifier(example) [30-37]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#substr
+SELECT SUBSTR('apple', 1, 123) AS example
+--
+QueryStatement [0-41]
+  Query [0-41]
+    Select [0-41]
+      SelectList [7-41]
+        SelectColumn [7-41]
+          FunctionCall [7-30]
+            PathExpression [7-13]
+              Identifier(SUBSTR) [7-13]
+            StringLiteral [14-21]
+              StringLiteralComponent('apple') [14-21]
+            IntLiteral(1) [23-24]
+            IntLiteral(123) [26-29]
+          Alias [31-41]
+            Identifier(example) [34-41]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#substr
+SELECT SUBSTR('apple', 123) AS example
+--
+QueryStatement [0-38]
+  Query [0-38]
+    Select [0-38]
+      SelectList [7-38]
+        SelectColumn [7-38]
+          FunctionCall [7-27]
+            PathExpression [7-13]
+              Identifier(SUBSTR) [7-13]
+            StringLiteral [14-21]
+              StringLiteralComponent('apple') [14-21]
+            IntLiteral(123) [23-26]
+          Alias [28-38]
+            Identifier(example) [31-38]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#substr
+SELECT SUBSTR('apple', 123, 5) AS example
+--
+QueryStatement [0-41]
+  Query [0-41]
+    Select [0-41]
+      SelectList [7-41]
+        SelectColumn [7-41]
+          FunctionCall [7-30]
+            PathExpression [7-13]
+              Identifier(SUBSTR) [7-13]
+            StringLiteral [14-21]
+              StringLiteralComponent('apple') [14-21]
+            IntLiteral(123) [23-26]
+            IntLiteral(5) [28-29]
+          Alias [31-41]
+            Identifier(example) [34-41]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#to_base32
+[mode=expression]
+TO_BASE32(bytes_expr)
+--
+FunctionCall [0-21]
+  PathExpression [0-9]
+    Identifier(TO_BASE32) [0-9]
+  PathExpression [10-20]
+    Identifier(bytes_expr) [10-20]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#to_base32
+SELECT TO_BASE32(b'abcde\xFF') AS base32_string
+--
+QueryStatement [0-47]
+  Query [0-47]
+    Select [0-47]
+      SelectList [7-47]
+        SelectColumn [7-47]
+          FunctionCall [7-30]
+            PathExpression [7-16]
+              Identifier(TO_BASE32) [7-16]
+            BytesLiteral [17-29]
+              BytesLiteralComponent(b'abcde\xFF') [17-29]
+          Alias [31-47]
+            Identifier(base32_string) [34-47]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#to_base64
+[mode=expression]
+TO_BASE64(bytes_expr)
+--
+FunctionCall [0-21]
+  PathExpression [0-9]
+    Identifier(TO_BASE64) [0-9]
+  PathExpression [10-20]
+    Identifier(bytes_expr) [10-20]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#to_base64
+SELECT TO_BASE64(b'\377\340') AS base64_string
+--
+QueryStatement [0-46]
+  Query [0-46]
+    Select [0-46]
+      SelectList [7-46]
+        SelectColumn [7-46]
+          FunctionCall [7-29]
+            PathExpression [7-16]
+              Identifier(TO_BASE64) [7-16]
+            BytesLiteral [17-28]
+              BytesLiteralComponent(b'\377\340') [17-28]
+          Alias [30-46]
+            Identifier(base64_string) [33-46]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#to_base64
+SELECT REPLACE(REPLACE(TO_BASE64(b'\377\340'), '+', '-'), '/', '_') as websafe_base64
+--
+QueryStatement [0-85]
+  Query [0-85]
+    Select [0-85]
+      SelectList [7-85]
+        SelectColumn [7-85]
+          FunctionCall [7-67]
+            PathExpression [7-14]
+              Identifier(`REPLACE`) [7-14]
+            FunctionCall [15-56]
+              PathExpression [15-22]
+                Identifier(`REPLACE`) [15-22]
+              FunctionCall [23-45]
+                PathExpression [23-32]
+                  Identifier(TO_BASE64) [23-32]
+                BytesLiteral [33-44]
+                  BytesLiteralComponent(b'\377\340') [33-44]
+              StringLiteral [47-50]
+                StringLiteralComponent('+') [47-50]
+              StringLiteral [52-55]
+                StringLiteralComponent('-') [52-55]
+            StringLiteral [58-61]
+              StringLiteralComponent('/') [58-61]
+            StringLiteral [63-66]
+              StringLiteralComponent('_') [63-66]
+          Alias [68-85]
+            Identifier(websafe_base64) [71-85]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#to_code_points
+[mode=expression]
+TO_CODE_POINTS(value)
+--
+FunctionCall [0-21]
+  PathExpression [0-14]
+    Identifier(TO_CODE_POINTS) [0-14]
+  PathExpression [15-20]
+    Identifier(value) [15-20]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#to_code_points
+SELECT
+  'foo' AS word,
+  TO_CODE_POINTS('foo') AS code_points
+--
+QueryStatement [0-62]
+  Query [0-62]
+    Select [0-62]
+      SelectList [9-62]
+        SelectColumn [9-22]
+          StringLiteral [9-14]
+            StringLiteralComponent('foo') [9-14]
+          Alias [15-22]
+            Identifier(word) [18-22]
+        SelectColumn [26-62]
+          FunctionCall [26-47]
+            PathExpression [26-40]
+              Identifier(TO_CODE_POINTS) [26-40]
+            StringLiteral [41-46]
+              StringLiteralComponent('foo') [41-46]
+          Alias [48-62]
+            Identifier(code_points) [51-62]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#to_code_points
+SELECT
+  'bar' AS word,
+  TO_CODE_POINTS('bar') AS code_points
+--
+QueryStatement [0-62]
+  Query [0-62]
+    Select [0-62]
+      SelectList [9-62]
+        SelectColumn [9-22]
+          StringLiteral [9-14]
+            StringLiteralComponent('bar') [9-14]
+          Alias [15-22]
+            Identifier(word) [18-22]
+        SelectColumn [26-62]
+          FunctionCall [26-47]
+            PathExpression [26-40]
+              Identifier(TO_CODE_POINTS) [26-40]
+            StringLiteral [41-46]
+              StringLiteralComponent('bar') [41-46]
+          Alias [48-62]
+            Identifier(code_points) [51-62]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#to_code_points
+SELECT
+  'baz' AS word,
+  TO_CODE_POINTS('baz') AS code_points
+--
+QueryStatement [0-62]
+  Query [0-62]
+    Select [0-62]
+      SelectList [9-62]
+        SelectColumn [9-22]
+          StringLiteral [9-14]
+            StringLiteralComponent('baz') [9-14]
+          Alias [15-22]
+            Identifier(word) [18-22]
+        SelectColumn [26-62]
+          FunctionCall [26-47]
+            PathExpression [26-40]
+              Identifier(TO_CODE_POINTS) [26-40]
+            StringLiteral [41-46]
+              StringLiteralComponent('baz') [41-46]
+          Alias [48-62]
+            Identifier(code_points) [51-62]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#to_code_points
+SELECT
+  'giraffe' AS word,
+  TO_CODE_POINTS('giraffe') AS code_points
+--
+QueryStatement [0-70]
+  Query [0-70]
+    Select [0-70]
+      SelectList [9-70]
+        SelectColumn [9-26]
+          StringLiteral [9-18]
+            StringLiteralComponent('giraffe') [9-18]
+          Alias [19-26]
+            Identifier(word) [22-26]
+        SelectColumn [30-70]
+          FunctionCall [30-55]
+            PathExpression [30-44]
+              Identifier(TO_CODE_POINTS) [30-44]
+            StringLiteral [45-54]
+              StringLiteralComponent('giraffe') [45-54]
+          Alias [56-70]
+            Identifier(code_points) [59-70]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#to_code_points
+SELECT
+  'llama' AS word,
+  TO_CODE_POINTS('llama') AS code_points
+--
+QueryStatement [0-66]
+  Query [0-66]
+    Select [0-66]
+      SelectList [9-66]
+        SelectColumn [9-24]
+          StringLiteral [9-16]
+            StringLiteralComponent('llama') [9-16]
+          Alias [17-24]
+            Identifier(word) [20-24]
+        SelectColumn [28-66]
+          FunctionCall [28-51]
+            PathExpression [28-42]
+              Identifier(TO_CODE_POINTS) [28-42]
+            StringLiteral [43-50]
+              StringLiteralComponent('llama') [43-50]
+          Alias [52-66]
+            Identifier(code_points) [55-66]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#to_code_points
+SELECT
+  b'\x66\x6f\x6f' AS bytes_value,
+  TO_CODE_POINTS(b'\x66\x6f\x6f') AS bytes_value_as_integer
+--
+QueryStatement [0-100]
+  Query [0-100]
+    Select [0-100]
+      SelectList [9-100]
+        SelectColumn [9-39]
+          BytesLiteral [9-24]
+            BytesLiteralComponent(b'\x66\x6f\x6f') [9-24]
+          Alias [25-39]
+            Identifier(bytes_value) [28-39]
+        SelectColumn [43-100]
+          FunctionCall [43-74]
+            PathExpression [43-57]
+              Identifier(TO_CODE_POINTS) [43-57]
+            BytesLiteral [58-73]
+              BytesLiteralComponent(b'\x66\x6f\x6f') [58-73]
+          Alias [75-100]
+            Identifier(bytes_value_as_integer) [78-100]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#to_code_points
+SELECT
+  b'\x00\x01\x10\xff' AS bytes_value,
+  TO_CODE_POINTS(b'\x00\x01\x10\xff') AS bytes_value_as_integer
+--
+QueryStatement [0-108]
+  Query [0-108]
+    Select [0-108]
+      SelectList [9-108]
+        SelectColumn [9-43]
+          BytesLiteral [9-28]
+            BytesLiteralComponent(b'\x00\x01\x10\xff') [9-28]
+          Alias [29-43]
+            Identifier(bytes_value) [32-43]
+        SelectColumn [47-108]
+          FunctionCall [47-82]
+            PathExpression [47-61]
+              Identifier(TO_CODE_POINTS) [47-61]
+            BytesLiteral [62-81]
+              BytesLiteralComponent(b'\x00\x01\x10\xff') [62-81]
+          Alias [83-108]
+            Identifier(bytes_value_as_integer) [86-108]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#to_code_points
+SELECT TO_CODE_POINTS(b'Ā') AS b_result, TO_CODE_POINTS('Ā') AS s_result
+--
+QueryStatement [0-74]
+  Query [0-74]
+    Select [0-74]
+      SelectList [7-74]
+        SelectColumn [7-40]
+          FunctionCall [7-28]
+            PathExpression [7-21]
+              Identifier(TO_CODE_POINTS) [7-21]
+            BytesLiteral [22-27]
+              BytesLiteralComponent(b'Ā') [22-27]
+          Alias [29-40]
+            Identifier(b_result) [32-40]
+        SelectColumn [42-74]
+          FunctionCall [42-62]
+            PathExpression [42-56]
+              Identifier(TO_CODE_POINTS) [42-56]
+            StringLiteral [57-61]
+              StringLiteralComponent('Ā') [57-61]
+          Alias [63-74]
+            Identifier(s_result) [66-74]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#to_hex
+[mode=expression]
+TO_HEX(bytes)
+--
+FunctionCall [0-13]
+  PathExpression [0-6]
+    Identifier(TO_HEX) [0-6]
+  PathExpression [7-12]
+    Identifier(bytes) [7-12]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#to_hex
+SELECT
+  b'\x00\x01\x02\x03\xAA\xEE\xEF\xFF' AS byte_string,
+  TO_HEX(b'\x00\x01\x02\x03\xAA\xEE\xEF\xFF') AS hex_string
+--
+QueryStatement [0-120]
+  Query [0-120]
+    Select [0-120]
+      SelectList [9-120]
+        SelectColumn [9-59]
+          BytesLiteral [9-44]
+            BytesLiteralComponent(b'\x00\x01\x02\x03\xAA\xEE\xEF\xFF') [9-44]
+          Alias [45-59]
+            Identifier(byte_string) [48-59]
+        SelectColumn [63-120]
+          FunctionCall [63-106]
+            PathExpression [63-69]
+              Identifier(TO_HEX) [63-69]
+            BytesLiteral [70-105]
+              BytesLiteralComponent(b'\x00\x01\x02\x03\xAA\xEE\xEF\xFF') [70-105]
+          Alias [107-120]
+            Identifier(hex_string) [110-120]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#trim
+SELECT CONCAT('#', TRIM( '   apple   '), '#') AS example
+--
+QueryStatement [0-56]
+  Query [0-56]
+    Select [0-56]
+      SelectList [7-56]
+        SelectColumn [7-56]
+          FunctionCall [7-45]
+            PathExpression [7-13]
+              Identifier(CONCAT) [7-13]
+            StringLiteral [14-17]
+              StringLiteralComponent('#') [14-17]
+            FunctionCall [19-39]
+              PathExpression [19-23]
+                Identifier(TRIM) [19-23]
+              StringLiteral [25-38]
+                StringLiteralComponent('   apple   ') [25-38]
+            StringLiteral [41-44]
+              StringLiteralComponent('#') [41-44]
+          Alias [46-56]
+            Identifier(example) [49-56]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#trim
+SELECT TRIM('***apple***', '*') AS example
+--
+QueryStatement [0-42]
+  Query [0-42]
+    Select [0-42]
+      SelectList [7-42]
+        SelectColumn [7-42]
+          FunctionCall [7-31]
+            PathExpression [7-11]
+              Identifier(TRIM) [7-11]
+            StringLiteral [12-25]
+              StringLiteralComponent('***apple***') [12-25]
+            StringLiteral [27-30]
+              StringLiteralComponent('*') [27-30]
+          Alias [32-42]
+            Identifier(example) [35-42]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#trim
+SELECT TRIM('xzxapplexxy', 'xyz') as example
+--
+QueryStatement [0-44]
+  Query [0-44]
+    Select [0-44]
+      SelectList [7-44]
+        SelectColumn [7-44]
+          FunctionCall [7-33]
+            PathExpression [7-11]
+              Identifier(TRIM) [7-11]
+            StringLiteral [12-25]
+              StringLiteralComponent('xzxapplexxy') [12-25]
+            StringLiteral [27-32]
+              StringLiteralComponent('xyz') [27-32]
+          Alias [34-44]
+            Identifier(example) [37-44]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#trim
+SELECT
+  TRIM('abaW̊', 'Y̊') AS a,
+  TRIM('W̊aba', 'Y̊') AS b,
+  TRIM('abaŪ̊', 'Y̊') AS c,
+  TRIM('Ū̊aba', 'Y̊') AS d
+--
+QueryStatement [0-127]
+  Query [0-127]
+    Select [0-127]
+      SelectList [9-127]
+        SelectColumn [9-35]
+          FunctionCall [9-30]
+            PathExpression [9-13]
+              Identifier(TRIM) [9-13]
+            StringLiteral [14-22]
+              StringLiteralComponent('abaW̊') [14-22]
+            StringLiteral [24-29]
+              StringLiteralComponent('Y̊') [24-29]
+          Alias [31-35]
+            Identifier(a) [34-35]
+        SelectColumn [39-65]
+          FunctionCall [39-60]
+            PathExpression [39-43]
+              Identifier(TRIM) [39-43]
+            StringLiteral [44-52]
+              StringLiteralComponent('W̊aba') [44-52]
+            StringLiteral [54-59]
+              StringLiteralComponent('Y̊') [54-59]
+          Alias [61-65]
+            Identifier(b) [64-65]
+        SelectColumn [69-96]
+          FunctionCall [69-91]
+            PathExpression [69-73]
+              Identifier(TRIM) [69-73]
+            StringLiteral [74-83]
+              StringLiteralComponent('abaŪ̊') [74-83]
+            StringLiteral [85-90]
+              StringLiteralComponent('Y̊') [85-90]
+          Alias [92-96]
+            Identifier(c) [95-96]
+        SelectColumn [100-127]
+          FunctionCall [100-122]
+            PathExpression [100-104]
+              Identifier(TRIM) [100-104]
+            StringLiteral [105-114]
+              StringLiteralComponent('Ū̊aba') [105-114]
+            StringLiteral [116-121]
+              StringLiteralComponent('Y̊') [116-121]
+          Alias [123-127]
+            Identifier(d) [126-127]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#trim
+SELECT b'apple', TRIM(b'apple', b'na\xab') AS example
+
+-- Note that the result of TRIM is of type BYTES, displayed as a base64-encoded string.
+--
+QueryStatement [0-53]
+  Query [0-53]
+    Select [0-53]
+      SelectList [7-53]
+        SelectColumn [7-15]
+          BytesLiteral [7-15]
+            BytesLiteralComponent(b'apple') [7-15]
+        SelectColumn [17-53]
+          FunctionCall [17-42]
+            PathExpression [17-21]
+              Identifier(TRIM) [17-21]
+            BytesLiteral [22-30]
+              BytesLiteralComponent(b'apple') [22-30]
+            BytesLiteral [32-41]
+              BytesLiteralComponent(b'na\xab') [32-41]
+          Alias [43-53]
+            Identifier(example) [46-53]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#ucase
+[mode=expression]
+UCASE(val)
+--
+FunctionCall [0-10]
+  PathExpression [0-5]
+    Identifier(UCASE) [0-5]
+  PathExpression [6-9]
+    Identifier(val) [6-9]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#upper
+[mode=expression]
+UPPER(value)
+--
+FunctionCall [0-12]
+  PathExpression [0-5]
+    Identifier(UPPER) [0-5]
+  PathExpression [6-11]
+    Identifier(value) [6-11]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/string_functions#upper
+SELECT UPPER('foo') AS example
+--
+QueryStatement [0-30]
+  Query [0-30]
+    Select [0-30]
+      SelectList [7-30]
+        SelectColumn [7-30]
+          FunctionCall [7-19]
+            PathExpression [7-12]
+              Identifier(UPPER) [7-12]
+            StringLiteral [13-18]
+              StringLiteralComponent('foo') [13-18]
+          Alias [20-30]
+            Identifier(example) [23-30]
diff --git a/parser/testdata/spanner/subqueries.test b/parser/testdata/spanner/subqueries.test
new file mode 100644
index 0000000..ef64219
--- /dev/null
+++ b/parser/testdata/spanner/subqueries.test
@@ -0,0 +1,546 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/subqueries
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/subqueries#about_subqueries
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+SELECT username
+FROM (
+  WITH result AS (SELECT * FROM NPCs)
+  SELECT *
+  FROM result)
+--
+QueryStatement [0-86]
+  Query [0-86]
+    Select [0-86]
+      SelectList [7-15]
+        SelectColumn [7-15]
+          PathExpression [7-15]
+            Identifier(username) [7-15]
+      FromClause [16-86]
+        TableSubquery [21-86]
+          Query [25-85]
+            WithClause [25-60]
+              WithClauseEntry [30-60]
+                AliasedQuery [30-60]
+                  Identifier(result) [30-36]
+                  Query [40-60]
+                    Select [41-59]
+                      SelectList [48-49]
+                        SelectColumn [48-49]
+                          Star(*) [48-49]
+                      FromClause [50-59]
+                        TablePathExpression [55-59]
+                          PathExpression [55-59]
+                            Identifier(NPCs) [55-59]
+            Select [63-85]
+              SelectList [70-71]
+                SelectColumn [70-71]
+                  Star(*) [70-71]
+              FromClause [74-85]
+                TablePathExpression [79-85]
+                  PathExpression [79-85]
+                    Identifier(result) [79-85]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/subqueries#example_tables
+WITH
+  Players AS (
+    SELECT 'gorbie' AS username, 29 AS level, 'red' AS team UNION ALL
+    SELECT 'junelyn', 2 , 'blue' UNION ALL
+    SELECT 'corba', 43, 'green'),
+  NPCs AS (
+    SELECT 'niles' AS username, 'red' AS team UNION ALL
+    SELECT 'jujul', 'red' UNION ALL
+    SELECT 'effren', 'blue'),
+  Mascots AS (
+    SELECT 'cardinal' AS mascot , 'red' AS team UNION ALL
+    SELECT 'parrot', 'green' UNION ALL
+    SELECT 'finch', 'blue' UNION ALL
+    SELECT 'sparrow', 'yellow')
+SELECT * FROM (
+  SELECT username, team FROM Players UNION ALL
+  SELECT username, team FROM NPCs)
+--
+QueryStatement [0-579]
+  Query [0-579]
+    WithClause [0-481]
+      WithClauseEntry [7-165]
+        AliasedQuery [7-165]
+          Identifier(Players) [7-14]
+          Query [18-165]
+            SetOperation(UNION ALL) [24-164]
+              SetOperationMetadataList [80-132]
+                SetOperationMetadata [80-89]
+                  SetOperationType [80-85]
+                  SetOperationAllOrDistinct [86-89]
+                SetOperationMetadata [123-132]
+                  SetOperationType [123-128]
+                  SetOperationAllOrDistinct [129-132]
+              Select [24-79]
+                SelectList [31-79]
+                  SelectColumn [31-51]
+                    StringLiteral [31-39]
+                      StringLiteralComponent('gorbie') [31-39]
+                    Alias [40-51]
+                      Identifier(username) [43-51]
+                  SelectColumn [53-64]
+                    IntLiteral(29) [53-55]
+                    Alias [56-64]
+                      Identifier(level) [59-64]
+                  SelectColumn [66-79]
+                    StringLiteral [66-71]
+                      StringLiteralComponent('red') [66-71]
+                    Alias [72-79]
+                      Identifier(team) [75-79]
+              Select [94-122]
+                SelectList [101-122]
+                  SelectColumn [101-110]
+                    StringLiteral [101-110]
+                      StringLiteralComponent('junelyn') [101-110]
+                  SelectColumn [112-113]
+                    IntLiteral(2) [112-113]
+                  SelectColumn [116-122]
+                    StringLiteral [116-122]
+                      StringLiteralComponent('blue') [116-122]
+              Select [137-164]
+                SelectList [144-164]
+                  SelectColumn [144-151]
+                    StringLiteral [144-151]
+                      StringLiteralComponent('corba') [144-151]
+                  SelectColumn [153-155]
+                    IntLiteral(43) [153-155]
+                  SelectColumn [157-164]
+                    StringLiteral [157-164]
+                      StringLiteralComponent('green') [157-164]
+      WithClauseEntry [169-299]
+        AliasedQuery [169-299]
+          Identifier(NPCs) [169-173]
+          Query [177-299]
+            SetOperation(UNION ALL) [183-298]
+              SetOperationMetadataList [225-270]
+                SetOperationMetadata [225-234]
+                  SetOperationType [225-230]
+                  SetOperationAllOrDistinct [231-234]
+                SetOperationMetadata [261-270]
+                  SetOperationType [261-266]
+                  SetOperationAllOrDistinct [267-270]
+              Select [183-224]
+                SelectList [190-224]
+                  SelectColumn [190-209]
+                    StringLiteral [190-197]
+                      StringLiteralComponent('niles') [190-197]
+                    Alias [198-209]
+                      Identifier(username) [201-209]
+                  SelectColumn [211-224]
+                    StringLiteral [211-216]
+                      StringLiteralComponent('red') [211-216]
+                    Alias [217-224]
+                      Identifier(team) [220-224]
+              Select [239-260]
+                SelectList [246-260]
+                  SelectColumn [246-253]
+                    StringLiteral [246-253]
+                      StringLiteralComponent('jujul') [246-253]
+                  SelectColumn [255-260]
+                    StringLiteral [255-260]
+                      StringLiteralComponent('red') [255-260]
+              Select [275-298]
+                SelectList [282-298]
+                  SelectColumn [282-290]
+                    StringLiteral [282-290]
+                      StringLiteralComponent('effren') [282-290]
+                  SelectColumn [292-298]
+                    StringLiteral [292-298]
+                      StringLiteralComponent('blue') [292-298]
+      WithClauseEntry [303-481]
+        AliasedQuery [303-481]
+          Identifier(Mascots) [303-310]
+          Query [314-481]
+            SetOperation(UNION ALL) [320-480]
+              SetOperationMetadataList [364-449]
+                SetOperationMetadata [364-373]
+                  SetOperationType [364-369]
+                  SetOperationAllOrDistinct [370-373]
+                SetOperationMetadata [403-412]
+                  SetOperationType [403-408]
+                  SetOperationAllOrDistinct [409-412]
+                SetOperationMetadata [440-449]
+                  SetOperationType [440-445]
+                  SetOperationAllOrDistinct [446-449]
+              Select [320-363]
+                SelectList [327-363]
+                  SelectColumn [327-347]
+                    StringLiteral [327-337]
+                      StringLiteralComponent('cardinal') [327-337]
+                    Alias [338-347]
+                      Identifier(mascot) [341-347]
+                  SelectColumn [350-363]
+                    StringLiteral [350-355]
+                      StringLiteralComponent('red') [350-355]
+                    Alias [356-363]
+                      Identifier(team) [359-363]
+              Select [378-402]
+                SelectList [385-402]
+                  SelectColumn [385-393]
+                    StringLiteral [385-393]
+                      StringLiteralComponent('parrot') [385-393]
+                  SelectColumn [395-402]
+                    StringLiteral [395-402]
+                      StringLiteralComponent('green') [395-402]
+              Select [417-439]
+                SelectList [424-439]
+                  SelectColumn [424-431]
+                    StringLiteral [424-431]
+                      StringLiteralComponent('finch') [424-431]
+                  SelectColumn [433-439]
+                    StringLiteral [433-439]
+                      StringLiteralComponent('blue') [433-439]
+              Select [454-480]
+                SelectList [461-480]
+                  SelectColumn [461-470]
+                    StringLiteral [461-470]
+                      StringLiteralComponent('sparrow') [461-470]
+                  SelectColumn [472-480]
+                    StringLiteral [472-480]
+                      StringLiteralComponent('yellow') [472-480]
+    Select [482-579]
+      SelectList [489-490]
+        SelectColumn [489-490]
+          Star(*) [489-490]
+      FromClause [491-579]
+        TableSubquery [496-579]
+          Query [500-578]
+            SetOperation(UNION ALL) [500-578]
+              SetOperationMetadataList [535-544]
+                SetOperationMetadata [535-544]
+                  SetOperationType [535-540]
+                  SetOperationAllOrDistinct [541-544]
+              Select [500-534]
+                SelectList [507-521]
+                  SelectColumn [507-515]
+                    PathExpression [507-515]
+                      Identifier(username) [507-515]
+                  SelectColumn [517-521]
+                    PathExpression [517-521]
+                      Identifier(team) [517-521]
+                FromClause [522-534]
+                  TablePathExpression [527-534]
+                    PathExpression [527-534]
+                      Identifier(Players) [527-534]
+              Select [547-578]
+                SelectList [554-568]
+                  SelectColumn [554-562]
+                    PathExpression [554-562]
+                      Identifier(username) [554-562]
+                  SelectColumn [564-568]
+                    PathExpression [564-568]
+                      Identifier(team) [564-568]
+                FromClause [569-578]
+                  TablePathExpression [574-578]
+                    PathExpression [574-578]
+                      Identifier(NPCs) [574-578]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/subqueries#scalar_subquery_concepts
+SELECT
+  username,
+  (SELECT mascot FROM Mascots WHERE Players.team = Mascots.team) AS player_mascot
+FROM
+  Players
+--
+QueryStatement [0-115]
+  Query [0-115]
+    Select [0-115]
+      SelectList [9-100]
+        SelectColumn [9-17]
+          PathExpression [9-17]
+            Identifier(username) [9-17]
+        SelectColumn [21-100]
+          ExpressionSubquery [21-83]
+            Query [22-82]
+              Select [22-82]
+                SelectList [29-35]
+                  SelectColumn [29-35]
+                    PathExpression [29-35]
+                      Identifier(mascot) [29-35]
+                FromClause [36-48]
+                  TablePathExpression [41-48]
+                    PathExpression [41-48]
+                      Identifier(Mascots) [41-48]
+                WhereClause [49-82]
+                  BinaryExpression(=) [55-82]
+                    PathExpression [55-67]
+                      Identifier(Players) [55-62]
+                      Identifier(team) [63-67]
+                    PathExpression [70-82]
+                      Identifier(Mascots) [70-77]
+                      Identifier(team) [78-82]
+          Alias [84-100]
+            Identifier(player_mascot) [87-100]
+      FromClause [101-115]
+        TablePathExpression [108-115]
+          PathExpression [108-115]
+            Identifier(Players) [108-115]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/subqueries#scalar_subquery_concepts
+SELECT
+  username,
+  level,
+  (SELECT AVG(level) FROM Players) AS avg_level
+FROM
+  Players
+--
+QueryStatement [0-90]
+  Query [0-90]
+    Select [0-90]
+      SelectList [9-75]
+        SelectColumn [9-17]
+          PathExpression [9-17]
+            Identifier(username) [9-17]
+        SelectColumn [21-26]
+          PathExpression [21-26]
+            Identifier(level) [21-26]
+        SelectColumn [30-75]
+          ExpressionSubquery [30-62]
+            Query [31-61]
+              Select [31-61]
+                SelectList [38-48]
+                  SelectColumn [38-48]
+                    FunctionCall [38-48]
+                      PathExpression [38-41]
+                        Identifier(AVG) [38-41]
+                      PathExpression [42-47]
+                        Identifier(level) [42-47]
+                FromClause [49-61]
+                  TablePathExpression [54-61]
+                    PathExpression [54-61]
+                      Identifier(Players) [54-61]
+          Alias [63-75]
+            Identifier(avg_level) [66-75]
+      FromClause [76-90]
+        TablePathExpression [83-90]
+          PathExpression [83-90]
+            Identifier(Players) [83-90]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/subqueries#array_subquery_concepts
+SELECT
+  ARRAY(SELECT username FROM NPCs WHERE team = 'red') AS red
+--
+QueryStatement [0-67]
+  Query [0-67]
+    Select [0-67]
+      SelectList [9-67]
+        SelectColumn [9-67]
+          ExpressionSubquery(modifier=ARRAY) [9-60]
+            Query [15-59]
+              Select [15-59]
+                SelectList [22-30]
+                  SelectColumn [22-30]
+                    PathExpression [22-30]
+                      Identifier(username) [22-30]
+                FromClause [31-40]
+                  TablePathExpression [36-40]
+                    PathExpression [36-40]
+                      Identifier(NPCs) [36-40]
+                WhereClause [41-59]
+                  BinaryExpression(=) [47-59]
+                    PathExpression [47-51]
+                      Identifier(team) [47-51]
+                    StringLiteral [54-59]
+                      StringLiteralComponent('red') [54-59]
+          Alias [61-67]
+            Identifier(red) [64-67]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/subqueries#in_subquery_concepts
+SELECT
+  'corba' IN (SELECT username FROM Players) AS result
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-60]
+      SelectList [9-60]
+        SelectColumn [9-60]
+          InExpression(IN) [9-50]
+            StringLiteral [9-16]
+              StringLiteralComponent('corba') [9-16]
+            Location [17-19]
+            Query [21-49]
+              Select [21-49]
+                SelectList [28-36]
+                  SelectColumn [28-36]
+                    PathExpression [28-36]
+                      Identifier(username) [28-36]
+                FromClause [37-49]
+                  TablePathExpression [42-49]
+                    PathExpression [42-49]
+                      Identifier(Players) [42-49]
+          Alias [51-60]
+            Identifier(result) [54-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/subqueries#exists_subquery_concepts
+SELECT
+  EXISTS(SELECT username FROM Players WHERE team = 'yellow') AS result
+--
+QueryStatement [0-77]
+  Query [0-77]
+    Select [0-77]
+      SelectList [9-77]
+        SelectColumn [9-77]
+          ExpressionSubquery(modifier=EXISTS) [9-67]
+            Query [16-66]
+              Select [16-66]
+                SelectList [23-31]
+                  SelectColumn [23-31]
+                    PathExpression [23-31]
+                      Identifier(username) [23-31]
+                FromClause [32-44]
+                  TablePathExpression [37-44]
+                    PathExpression [37-44]
+                      Identifier(Players) [37-44]
+                WhereClause [45-66]
+                  BinaryExpression(=) [51-66]
+                    PathExpression [51-55]
+                      Identifier(team) [51-55]
+                    StringLiteral [58-66]
+                      StringLiteralComponent('yellow') [58-66]
+          Alias [68-77]
+            Identifier(result) [71-77]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/subqueries#table_subquery_concepts
+SELECT results.username
+FROM (SELECT * FROM Players) AS results
+--
+QueryStatement [0-63]
+  Query [0-63]
+    Select [0-63]
+      SelectList [7-23]
+        SelectColumn [7-23]
+          PathExpression [7-23]
+            Identifier(results) [7-14]
+            Identifier(username) [15-23]
+      FromClause [24-63]
+        TableSubquery [29-63]
+          Query [30-51]
+            Select [30-51]
+              SelectList [37-38]
+                SelectColumn [37-38]
+                  Star(*) [37-38]
+              FromClause [39-51]
+                TablePathExpression [44-51]
+                  PathExpression [44-51]
+                    Identifier(Players) [44-51]
+          Alias [53-63]
+            Identifier(results) [56-63]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/subqueries#correlated_subquery_concepts
+SELECT mascot
+FROM Mascots
+WHERE
+  NOT EXISTS(SELECT username FROM Players WHERE Mascots.team = Players.team)
+--
+QueryStatement [0-109]
+  Query [0-109]
+    Select [0-109]
+      SelectList [7-13]
+        SelectColumn [7-13]
+          PathExpression [7-13]
+            Identifier(mascot) [7-13]
+      FromClause [14-26]
+        TablePathExpression [19-26]
+          PathExpression [19-26]
+            Identifier(Mascots) [19-26]
+      WhereClause [27-109]
+        UnaryExpression(NOT) [35-109]
+          ExpressionSubquery(modifier=EXISTS) [39-109]
+            Query [46-108]
+              Select [46-108]
+                SelectList [53-61]
+                  SelectColumn [53-61]
+                    PathExpression [53-61]
+                      Identifier(username) [53-61]
+                FromClause [62-74]
+                  TablePathExpression [67-74]
+                    PathExpression [67-74]
+                      Identifier(Players) [67-74]
+                WhereClause [75-108]
+                  BinaryExpression(=) [81-108]
+                    PathExpression [81-93]
+                      Identifier(Mascots) [81-88]
+                      Identifier(team) [89-93]
+                    PathExpression [96-108]
+                      Identifier(Players) [96-103]
+                      Identifier(team) [104-108]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/subqueries#correlated_subquery_concepts
+SELECT
+  username,
+  (SELECT mascot FROM Mascots WHERE Players.team = Mascots.team) AS player_mascot
+FROM Players
+--
+QueryStatement [0-113]
+  Query [0-113]
+    Select [0-113]
+      SelectList [9-100]
+        SelectColumn [9-17]
+          PathExpression [9-17]
+            Identifier(username) [9-17]
+        SelectColumn [21-100]
+          ExpressionSubquery [21-83]
+            Query [22-82]
+              Select [22-82]
+                SelectList [29-35]
+                  SelectColumn [29-35]
+                    PathExpression [29-35]
+                      Identifier(mascot) [29-35]
+                FromClause [36-48]
+                  TablePathExpression [41-48]
+                    PathExpression [41-48]
+                      Identifier(Mascots) [41-48]
+                WhereClause [49-82]
+                  BinaryExpression(=) [55-82]
+                    PathExpression [55-67]
+                      Identifier(Players) [55-62]
+                      Identifier(team) [63-67]
+                    PathExpression [70-82]
+                      Identifier(Mascots) [70-77]
+                      Identifier(team) [78-82]
+          Alias [84-100]
+            Identifier(player_mascot) [87-100]
+      FromClause [101-113]
+        TablePathExpression [106-113]
+          PathExpression [106-113]
+            Identifier(Players) [106-113]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/subqueries#volatile_subqueries
+SELECT
+  results.username
+FROM
+  (SELECT * FROM Players WHERE RAND() < 0.5) AS results
+--
+QueryStatement [0-86]
+  Query [0-86]
+    Select [0-86]
+      SelectList [9-25]
+        SelectColumn [9-25]
+          PathExpression [9-25]
+            Identifier(results) [9-16]
+            Identifier(username) [17-25]
+      FromClause [26-86]
+        TableSubquery [33-86]
+          Query [34-74]
+            Select [34-74]
+              SelectList [41-42]
+                SelectColumn [41-42]
+                  Star(*) [41-42]
+              FromClause [43-55]
+                TablePathExpression [48-55]
+                  PathExpression [48-55]
+                    Identifier(Players) [48-55]
+              WhereClause [56-74]
+                BinaryExpression(<) [62-74]
+                  FunctionCall [62-68]
+                    PathExpression [62-66]
+                      Identifier(RAND) [62-66]
+                  FloatLiteral(0.5) [71-74]
+          Alias [76-86]
+            Identifier(results) [79-86]
diff --git a/parser/testdata/spanner/timestamp_functions.spanner.sql b/parser/testdata/spanner/timestamp_functions.spanner.sql
new file mode 100644
index 0000000..9297cb0
--- /dev/null
+++ b/parser/testdata/spanner/timestamp_functions.spanner.sql
@@ -0,0 +1,23 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+#
+# Spanner syntax the reference parser rejects with
+# --enabled_language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE
+# (classified "spanner" in cmd/spanner-corpus/triage.txt). There is no
+# golden parse tree for these.
+
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#pending_commit_timestamp
+# id d4c050bd9942: DEFAULT after NOT NULL (timestamp_functions)
+# execute_query: INVALID_ARGUMENT: Syntax error: Expected ")" or "," but got keyword DEFAULT [at 6:40]
+CREATE TABLE Performances (
+    SingerId        INT64 NOT NULL,
+    VenueId         INT64 NOT NULL,
+    EventDate       DATE,
+    Revenue         INT64,
+    CreatedTime     TIMESTAMP NOT NULL DEFAULT (PENDING_COMMIT_TIMESTAMP())
+      OPTIONS (allow_commit_timestamp=true),
+    LastUpdatedTime TIMESTAMP NOT NULL DEFAULT (PENDING_COMMIT_TIMESTAMP())
+      ON UPDATE (PENDING_COMMIT_TIMESTAMP()) OPTIONS (allow_commit_timestamp=true)
+) PRIMARY KEY (SingerId, VenueId, EventDate),
+  INTERLEAVE IN PARENT Singers ON DELETE CASCADE;
diff --git a/parser/testdata/spanner/timestamp_functions.test b/parser/testdata/spanner/timestamp_functions.test
new file mode 100644
index 0000000..10f42cd
--- /dev/null
+++ b/parser/testdata/spanner/timestamp_functions.test
@@ -0,0 +1,1299 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#current_timestamp
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+CURRENT_TIMESTAMP()
+--
+FunctionCall [0-19]
+  PathExpression [0-17]
+    Identifier(`CURRENT_TIMESTAMP`) [0-17]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#current_timestamp
+[mode=expression]
+CURRENT_TIMESTAMP
+--
+FunctionCall [0-17]
+  PathExpression [0-17]
+    Identifier(`CURRENT_TIMESTAMP`) [0-17]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#current_timestamp
+SELECT CURRENT_TIMESTAMP() AS now
+--
+QueryStatement [0-33]
+  Query [0-33]
+    Select [0-33]
+      SelectList [7-33]
+        SelectColumn [7-33]
+          FunctionCall [7-26]
+            PathExpression [7-24]
+              Identifier(`CURRENT_TIMESTAMP`) [7-24]
+          Alias [27-33]
+            Identifier(now) [30-33]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#extract
+SELECT
+  EXTRACT(
+    DAY
+    FROM TIMESTAMP('2008-12-25 05:30:00+00') AT TIME ZONE 'UTC')
+    AS the_day_utc,
+  EXTRACT(
+    DAY
+    FROM TIMESTAMP('2008-12-25 05:30:00+00') AT TIME ZONE 'America/Los_Angeles')
+    AS the_day_california
+--
+QueryStatement [0-236]
+  Query [0-236]
+    Select [0-236]
+      SelectList [9-236]
+        SelectColumn [9-109]
+          ExtractExpression [9-90]
+            PathExpression [22-25]
+              Identifier(DAY) [22-25]
+            FunctionCall [35-70]
+              PathExpression [35-44]
+                Identifier(TIMESTAMP) [35-44]
+              StringLiteral [45-69]
+                StringLiteralComponent('2008-12-25 05:30:00+00') [45-69]
+            StringLiteral [84-89]
+              StringLiteralComponent('UTC') [84-89]
+          Alias [95-109]
+            Identifier(the_day_utc) [98-109]
+        SelectColumn [113-236]
+          ExtractExpression [113-210]
+            PathExpression [126-129]
+              Identifier(DAY) [126-129]
+            FunctionCall [139-174]
+              PathExpression [139-148]
+                Identifier(TIMESTAMP) [139-148]
+              StringLiteral [149-173]
+                StringLiteralComponent('2008-12-25 05:30:00+00') [149-173]
+            StringLiteral [188-209]
+              StringLiteralComponent('America/Los_Angeles') [188-209]
+          Alias [215-236]
+            Identifier(the_day_california) [218-236]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#extract
+SELECT
+  EXTRACT(ISOYEAR FROM TIMESTAMP("2005-01-03 12:34:56+00")) AS isoyear,
+  EXTRACT(ISOWEEK FROM TIMESTAMP("2005-01-03 12:34:56+00")) AS isoweek,
+  EXTRACT(YEAR FROM TIMESTAMP("2005-01-03 12:34:56+00")) AS year,
+  EXTRACT(WEEK FROM TIMESTAMP("2005-01-03 12:34:56+00")) AS week
+
+-- Display of results may differ, depending upon the environment and
+-- time zone where this query was executed.
+--
+QueryStatement [0-281]
+  Query [0-281]
+    Select [0-281]
+      SelectList [9-281]
+        SelectColumn [9-77]
+          ExtractExpression [9-66]
+            PathExpression [17-24]
+              Identifier(ISOYEAR) [17-24]
+            FunctionCall [30-65]
+              PathExpression [30-39]
+                Identifier(TIMESTAMP) [30-39]
+              StringLiteral [40-64]
+                StringLiteralComponent("2005-01-03 12:34:56+00") [40-64]
+          Alias [67-77]
+            Identifier(isoyear) [70-77]
+        SelectColumn [81-149]
+          ExtractExpression [81-138]
+            PathExpression [89-96]
+              Identifier(ISOWEEK) [89-96]
+            FunctionCall [102-137]
+              PathExpression [102-111]
+                Identifier(TIMESTAMP) [102-111]
+              StringLiteral [112-136]
+                StringLiteralComponent("2005-01-03 12:34:56+00") [112-136]
+          Alias [139-149]
+            Identifier(isoweek) [142-149]
+        SelectColumn [153-215]
+          ExtractExpression [153-207]
+            PathExpression [161-165]
+              Identifier(YEAR) [161-165]
+            FunctionCall [171-206]
+              PathExpression [171-180]
+                Identifier(TIMESTAMP) [171-180]
+              StringLiteral [181-205]
+                StringLiteralComponent("2005-01-03 12:34:56+00") [181-205]
+          Alias [208-215]
+            Identifier(year) [211-215]
+        SelectColumn [219-281]
+          ExtractExpression [219-273]
+            PathExpression [227-231]
+              Identifier(WEEK) [227-231]
+            FunctionCall [237-272]
+              PathExpression [237-246]
+                Identifier(TIMESTAMP) [237-246]
+              StringLiteral [247-271]
+                StringLiteralComponent("2005-01-03 12:34:56+00") [247-271]
+          Alias [274-281]
+            Identifier(week) [277-281]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#extract
+SELECT
+  TIMESTAMP("2007-12-31 12:00:00+00") AS timestamp_value,
+  EXTRACT(ISOYEAR FROM TIMESTAMP("2007-12-31 12:00:00+00")) AS isoyear,
+  EXTRACT(ISOWEEK FROM TIMESTAMP("2007-12-31 12:00:00+00")) AS isoweek,
+  EXTRACT(YEAR FROM TIMESTAMP("2007-12-31 12:00:00+00")) AS year,
+  EXTRACT(WEEK FROM TIMESTAMP("2007-12-31 12:00:00+00")) AS week
+
+-- Display of results may differ, depending upon the environment and time zone
+-- where this query was executed.
+--
+QueryStatement [0-339]
+  Query [0-339]
+    Select [0-339]
+      SelectList [9-339]
+        SelectColumn [9-63]
+          FunctionCall [9-44]
+            PathExpression [9-18]
+              Identifier(TIMESTAMP) [9-18]
+            StringLiteral [19-43]
+              StringLiteralComponent("2007-12-31 12:00:00+00") [19-43]
+          Alias [45-63]
+            Identifier(timestamp_value) [48-63]
+        SelectColumn [67-135]
+          ExtractExpression [67-124]
+            PathExpression [75-82]
+              Identifier(ISOYEAR) [75-82]
+            FunctionCall [88-123]
+              PathExpression [88-97]
+                Identifier(TIMESTAMP) [88-97]
+              StringLiteral [98-122]
+                StringLiteralComponent("2007-12-31 12:00:00+00") [98-122]
+          Alias [125-135]
+            Identifier(isoyear) [128-135]
+        SelectColumn [139-207]
+          ExtractExpression [139-196]
+            PathExpression [147-154]
+              Identifier(ISOWEEK) [147-154]
+            FunctionCall [160-195]
+              PathExpression [160-169]
+                Identifier(TIMESTAMP) [160-169]
+              StringLiteral [170-194]
+                StringLiteralComponent("2007-12-31 12:00:00+00") [170-194]
+          Alias [197-207]
+            Identifier(isoweek) [200-207]
+        SelectColumn [211-273]
+          ExtractExpression [211-265]
+            PathExpression [219-223]
+              Identifier(YEAR) [219-223]
+            FunctionCall [229-264]
+              PathExpression [229-238]
+                Identifier(TIMESTAMP) [229-238]
+              StringLiteral [239-263]
+                StringLiteralComponent("2007-12-31 12:00:00+00") [239-263]
+          Alias [266-273]
+            Identifier(year) [269-273]
+        SelectColumn [277-339]
+          ExtractExpression [277-331]
+            PathExpression [285-289]
+              Identifier(WEEK) [285-289]
+            FunctionCall [295-330]
+              PathExpression [295-304]
+                Identifier(TIMESTAMP) [295-304]
+              StringLiteral [305-329]
+                StringLiteralComponent("2007-12-31 12:00:00+00") [305-329]
+          Alias [332-339]
+            Identifier(week) [335-339]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#extract
+SELECT
+  TIMESTAMP("2009-01-01 12:00:00+00") AS timestamp_value,
+  EXTRACT(ISOYEAR FROM TIMESTAMP("2009-01-01 12:00:00+00")) AS isoyear,
+  EXTRACT(ISOWEEK FROM TIMESTAMP("2009-01-01 12:00:00+00")) AS isoweek,
+  EXTRACT(YEAR FROM TIMESTAMP("2009-01-01 12:00:00+00")) AS year,
+  EXTRACT(WEEK FROM TIMESTAMP("2009-01-01 12:00:00+00")) AS week
+
+-- Display of results may differ, depending upon the environment and time zone
+-- where this query was executed.
+--
+QueryStatement [0-339]
+  Query [0-339]
+    Select [0-339]
+      SelectList [9-339]
+        SelectColumn [9-63]
+          FunctionCall [9-44]
+            PathExpression [9-18]
+              Identifier(TIMESTAMP) [9-18]
+            StringLiteral [19-43]
+              StringLiteralComponent("2009-01-01 12:00:00+00") [19-43]
+          Alias [45-63]
+            Identifier(timestamp_value) [48-63]
+        SelectColumn [67-135]
+          ExtractExpression [67-124]
+            PathExpression [75-82]
+              Identifier(ISOYEAR) [75-82]
+            FunctionCall [88-123]
+              PathExpression [88-97]
+                Identifier(TIMESTAMP) [88-97]
+              StringLiteral [98-122]
+                StringLiteralComponent("2009-01-01 12:00:00+00") [98-122]
+          Alias [125-135]
+            Identifier(isoyear) [128-135]
+        SelectColumn [139-207]
+          ExtractExpression [139-196]
+            PathExpression [147-154]
+              Identifier(ISOWEEK) [147-154]
+            FunctionCall [160-195]
+              PathExpression [160-169]
+                Identifier(TIMESTAMP) [160-169]
+              StringLiteral [170-194]
+                StringLiteralComponent("2009-01-01 12:00:00+00") [170-194]
+          Alias [197-207]
+            Identifier(isoweek) [200-207]
+        SelectColumn [211-273]
+          ExtractExpression [211-265]
+            PathExpression [219-223]
+              Identifier(YEAR) [219-223]
+            FunctionCall [229-264]
+              PathExpression [229-238]
+                Identifier(TIMESTAMP) [229-238]
+              StringLiteral [239-263]
+                StringLiteralComponent("2009-01-01 12:00:00+00") [239-263]
+          Alias [266-273]
+            Identifier(year) [269-273]
+        SelectColumn [277-339]
+          ExtractExpression [277-331]
+            PathExpression [285-289]
+              Identifier(WEEK) [285-289]
+            FunctionCall [295-330]
+              PathExpression [295-304]
+                Identifier(TIMESTAMP) [295-304]
+              StringLiteral [305-329]
+                StringLiteralComponent("2009-01-01 12:00:00+00") [305-329]
+          Alias [332-339]
+            Identifier(week) [335-339]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#extract
+SELECT
+  TIMESTAMP("2009-12-31 12:00:00+00") AS timestamp_value,
+  EXTRACT(ISOYEAR FROM TIMESTAMP("2009-12-31 12:00:00+00")) AS isoyear,
+  EXTRACT(ISOWEEK FROM TIMESTAMP("2009-12-31 12:00:00+00")) AS isoweek,
+  EXTRACT(YEAR FROM TIMESTAMP("2009-12-31 12:00:00+00")) AS year,
+  EXTRACT(WEEK FROM TIMESTAMP("2009-12-31 12:00:00+00")) AS week
+
+-- Display of results may differ, depending upon the environment and time zone
+-- where this query was executed.
+--
+QueryStatement [0-339]
+  Query [0-339]
+    Select [0-339]
+      SelectList [9-339]
+        SelectColumn [9-63]
+          FunctionCall [9-44]
+            PathExpression [9-18]
+              Identifier(TIMESTAMP) [9-18]
+            StringLiteral [19-43]
+              StringLiteralComponent("2009-12-31 12:00:00+00") [19-43]
+          Alias [45-63]
+            Identifier(timestamp_value) [48-63]
+        SelectColumn [67-135]
+          ExtractExpression [67-124]
+            PathExpression [75-82]
+              Identifier(ISOYEAR) [75-82]
+            FunctionCall [88-123]
+              PathExpression [88-97]
+                Identifier(TIMESTAMP) [88-97]
+              StringLiteral [98-122]
+                StringLiteralComponent("2009-12-31 12:00:00+00") [98-122]
+          Alias [125-135]
+            Identifier(isoyear) [128-135]
+        SelectColumn [139-207]
+          ExtractExpression [139-196]
+            PathExpression [147-154]
+              Identifier(ISOWEEK) [147-154]
+            FunctionCall [160-195]
+              PathExpression [160-169]
+                Identifier(TIMESTAMP) [160-169]
+              StringLiteral [170-194]
+                StringLiteralComponent("2009-12-31 12:00:00+00") [170-194]
+          Alias [197-207]
+            Identifier(isoweek) [200-207]
+        SelectColumn [211-273]
+          ExtractExpression [211-265]
+            PathExpression [219-223]
+              Identifier(YEAR) [219-223]
+            FunctionCall [229-264]
+              PathExpression [229-238]
+                Identifier(TIMESTAMP) [229-238]
+              StringLiteral [239-263]
+                StringLiteralComponent("2009-12-31 12:00:00+00") [239-263]
+          Alias [266-273]
+            Identifier(year) [269-273]
+        SelectColumn [277-339]
+          ExtractExpression [277-331]
+            PathExpression [285-289]
+              Identifier(WEEK) [285-289]
+            FunctionCall [295-330]
+              PathExpression [295-304]
+                Identifier(TIMESTAMP) [295-304]
+              StringLiteral [305-329]
+                StringLiteralComponent("2009-12-31 12:00:00+00") [305-329]
+          Alias [332-339]
+            Identifier(week) [335-339]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#extract
+SELECT
+  TIMESTAMP("2017-01-02 12:00:00+00") AS timestamp_value,
+  EXTRACT(ISOYEAR FROM TIMESTAMP("2017-01-02 12:00:00+00")) AS isoyear,
+  EXTRACT(ISOWEEK FROM TIMESTAMP("2017-01-02 12:00:00+00")) AS isoweek,
+  EXTRACT(YEAR FROM TIMESTAMP("2017-01-02 12:00:00+00")) AS year,
+  EXTRACT(WEEK FROM TIMESTAMP("2017-01-02 12:00:00+00")) AS week
+
+-- Display of results may differ, depending upon the environment and time zone
+-- where this query was executed.
+--
+QueryStatement [0-339]
+  Query [0-339]
+    Select [0-339]
+      SelectList [9-339]
+        SelectColumn [9-63]
+          FunctionCall [9-44]
+            PathExpression [9-18]
+              Identifier(TIMESTAMP) [9-18]
+            StringLiteral [19-43]
+              StringLiteralComponent("2017-01-02 12:00:00+00") [19-43]
+          Alias [45-63]
+            Identifier(timestamp_value) [48-63]
+        SelectColumn [67-135]
+          ExtractExpression [67-124]
+            PathExpression [75-82]
+              Identifier(ISOYEAR) [75-82]
+            FunctionCall [88-123]
+              PathExpression [88-97]
+                Identifier(TIMESTAMP) [88-97]
+              StringLiteral [98-122]
+                StringLiteralComponent("2017-01-02 12:00:00+00") [98-122]
+          Alias [125-135]
+            Identifier(isoyear) [128-135]
+        SelectColumn [139-207]
+          ExtractExpression [139-196]
+            PathExpression [147-154]
+              Identifier(ISOWEEK) [147-154]
+            FunctionCall [160-195]
+              PathExpression [160-169]
+                Identifier(TIMESTAMP) [160-169]
+              StringLiteral [170-194]
+                StringLiteralComponent("2017-01-02 12:00:00+00") [170-194]
+          Alias [197-207]
+            Identifier(isoweek) [200-207]
+        SelectColumn [211-273]
+          ExtractExpression [211-265]
+            PathExpression [219-223]
+              Identifier(YEAR) [219-223]
+            FunctionCall [229-264]
+              PathExpression [229-238]
+                Identifier(TIMESTAMP) [229-238]
+              StringLiteral [239-263]
+                StringLiteralComponent("2017-01-02 12:00:00+00") [239-263]
+          Alias [266-273]
+            Identifier(year) [269-273]
+        SelectColumn [277-339]
+          ExtractExpression [277-331]
+            PathExpression [285-289]
+              Identifier(WEEK) [285-289]
+            FunctionCall [295-330]
+              PathExpression [295-304]
+                Identifier(TIMESTAMP) [295-304]
+              StringLiteral [305-329]
+                StringLiteralComponent("2017-01-02 12:00:00+00") [305-329]
+          Alias [332-339]
+            Identifier(week) [335-339]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#extract
+SELECT
+  TIMESTAMP("2017-05-26 12:00:00+00") AS timestamp_value,
+  EXTRACT(ISOYEAR FROM TIMESTAMP("2017-05-26 12:00:00+00")) AS isoyear,
+  EXTRACT(ISOWEEK FROM TIMESTAMP("2017-05-26 12:00:00+00")) AS isoweek,
+  EXTRACT(YEAR FROM TIMESTAMP("2017-05-26 12:00:00+00")) AS year,
+  EXTRACT(WEEK FROM TIMESTAMP("2017-05-26 12:00:00+00")) AS week
+
+-- Display of results may differ, depending upon the environment and time zone
+-- where this query was executed.
+--
+QueryStatement [0-339]
+  Query [0-339]
+    Select [0-339]
+      SelectList [9-339]
+        SelectColumn [9-63]
+          FunctionCall [9-44]
+            PathExpression [9-18]
+              Identifier(TIMESTAMP) [9-18]
+            StringLiteral [19-43]
+              StringLiteralComponent("2017-05-26 12:00:00+00") [19-43]
+          Alias [45-63]
+            Identifier(timestamp_value) [48-63]
+        SelectColumn [67-135]
+          ExtractExpression [67-124]
+            PathExpression [75-82]
+              Identifier(ISOYEAR) [75-82]
+            FunctionCall [88-123]
+              PathExpression [88-97]
+                Identifier(TIMESTAMP) [88-97]
+              StringLiteral [98-122]
+                StringLiteralComponent("2017-05-26 12:00:00+00") [98-122]
+          Alias [125-135]
+            Identifier(isoyear) [128-135]
+        SelectColumn [139-207]
+          ExtractExpression [139-196]
+            PathExpression [147-154]
+              Identifier(ISOWEEK) [147-154]
+            FunctionCall [160-195]
+              PathExpression [160-169]
+                Identifier(TIMESTAMP) [160-169]
+              StringLiteral [170-194]
+                StringLiteralComponent("2017-05-26 12:00:00+00") [170-194]
+          Alias [197-207]
+            Identifier(isoweek) [200-207]
+        SelectColumn [211-273]
+          ExtractExpression [211-265]
+            PathExpression [219-223]
+              Identifier(YEAR) [219-223]
+            FunctionCall [229-264]
+              PathExpression [229-238]
+                Identifier(TIMESTAMP) [229-238]
+              StringLiteral [239-263]
+                StringLiteralComponent("2017-05-26 12:00:00+00") [239-263]
+          Alias [266-273]
+            Identifier(year) [269-273]
+        SelectColumn [277-339]
+          ExtractExpression [277-331]
+            PathExpression [285-289]
+              Identifier(WEEK) [285-289]
+            FunctionCall [295-330]
+              PathExpression [295-304]
+                Identifier(TIMESTAMP) [295-304]
+              StringLiteral [305-329]
+                StringLiteralComponent("2017-05-26 12:00:00+00") [305-329]
+          Alias [332-339]
+            Identifier(week) [335-339]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#format_timestamp
+SELECT FORMAT_TIMESTAMP("%c", TIMESTAMP "2050-12-25 15:30:55+00", "UTC")
+  AS formatted
+--
+QueryStatement [0-87]
+  Query [0-87]
+    Select [0-87]
+      SelectList [7-87]
+        SelectColumn [7-87]
+          FunctionCall [7-72]
+            PathExpression [7-23]
+              Identifier(FORMAT_TIMESTAMP) [7-23]
+            StringLiteral [24-28]
+              StringLiteralComponent("%c") [24-28]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [30-64]
+              StringLiteral [40-64]
+                StringLiteralComponent("2050-12-25 15:30:55+00") [40-64]
+            StringLiteral [66-71]
+              StringLiteralComponent("UTC") [66-71]
+          Alias [75-87]
+            Identifier(formatted) [78-87]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#format_timestamp
+SELECT FORMAT_TIMESTAMP("%b-%d-%Y", TIMESTAMP "2050-12-25 15:30:55+00")
+  AS formatted
+--
+QueryStatement [0-86]
+  Query [0-86]
+    Select [0-86]
+      SelectList [7-86]
+        SelectColumn [7-86]
+          FunctionCall [7-71]
+            PathExpression [7-23]
+              Identifier(FORMAT_TIMESTAMP) [7-23]
+            StringLiteral [24-34]
+              StringLiteralComponent("%b-%d-%Y") [24-34]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [36-70]
+              StringLiteral [46-70]
+                StringLiteralComponent("2050-12-25 15:30:55+00") [46-70]
+          Alias [74-86]
+            Identifier(formatted) [77-86]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#format_timestamp
+SELECT FORMAT_TIMESTAMP("%b %Y", TIMESTAMP "2050-12-25 15:30:55+00")
+  AS formatted
+--
+QueryStatement [0-83]
+  Query [0-83]
+    Select [0-83]
+      SelectList [7-83]
+        SelectColumn [7-83]
+          FunctionCall [7-68]
+            PathExpression [7-23]
+              Identifier(FORMAT_TIMESTAMP) [7-23]
+            StringLiteral [24-31]
+              StringLiteralComponent("%b %Y") [24-31]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [33-67]
+              StringLiteral [43-67]
+                StringLiteralComponent("2050-12-25 15:30:55+00") [43-67]
+          Alias [71-83]
+            Identifier(formatted) [74-83]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#format_timestamp
+SELECT FORMAT_TIMESTAMP("%Y-%m-%dT%H:%M:%S%Z", TIMESTAMP "2050-12-25 15:30:55", "UTC")
+  AS formatted
+--
+QueryStatement [0-101]
+  Query [0-101]
+    Select [0-101]
+      SelectList [7-101]
+        SelectColumn [7-101]
+          FunctionCall [7-86]
+            PathExpression [7-23]
+              Identifier(FORMAT_TIMESTAMP) [7-23]
+            StringLiteral [24-45]
+              StringLiteralComponent("%Y-%m-%dT%H:%M:%S%Z") [24-45]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [47-78]
+              StringLiteral [57-78]
+                StringLiteralComponent("2050-12-25 15:30:55") [57-78]
+            StringLiteral [80-85]
+              StringLiteralComponent("UTC") [80-85]
+          Alias [89-101]
+            Identifier(formatted) [92-101]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#parse_timestamp
+-- This works because elements on both sides match.
+SELECT PARSE_TIMESTAMP("%a %b %e %I:%M:%S %Y", "Thu Dec 25 07:30:00 2008")
+--
+QueryStatement [52-126]
+  Query [52-126]
+    Select [52-126]
+      SelectList [59-126]
+        SelectColumn [59-126]
+          FunctionCall [59-126]
+            PathExpression [59-74]
+              Identifier(PARSE_TIMESTAMP) [59-74]
+            StringLiteral [75-97]
+              StringLiteralComponent("%a %b %e %I:%M:%S %Y") [75-97]
+            StringLiteral [99-125]
+              StringLiteralComponent("Thu Dec 25 07:30:00 2008") [99-125]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#parse_timestamp
+-- This produces an error because the year element is in different locations.
+SELECT PARSE_TIMESTAMP("%a %b %e %Y %I:%M:%S", "Thu Dec 25 07:30:00 2008")
+--
+QueryStatement [78-152]
+  Query [78-152]
+    Select [78-152]
+      SelectList [85-152]
+        SelectColumn [85-152]
+          FunctionCall [85-152]
+            PathExpression [85-100]
+              Identifier(PARSE_TIMESTAMP) [85-100]
+            StringLiteral [101-123]
+              StringLiteralComponent("%a %b %e %Y %I:%M:%S") [101-123]
+            StringLiteral [125-151]
+              StringLiteralComponent("Thu Dec 25 07:30:00 2008") [125-151]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#parse_timestamp
+-- This produces an error because one of the year elements is missing.
+SELECT PARSE_TIMESTAMP("%a %b %e %I:%M:%S", "Thu Dec 25 07:30:00 2008")
+--
+QueryStatement [71-142]
+  Query [71-142]
+    Select [71-142]
+      SelectList [78-142]
+        SelectColumn [78-142]
+          FunctionCall [78-142]
+            PathExpression [78-93]
+              Identifier(PARSE_TIMESTAMP) [78-93]
+            StringLiteral [94-113]
+              StringLiteralComponent("%a %b %e %I:%M:%S") [94-113]
+            StringLiteral [115-141]
+              StringLiteralComponent("Thu Dec 25 07:30:00 2008") [115-141]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#parse_timestamp
+-- This works because %c can find all matching elements in timestamp_string.
+SELECT PARSE_TIMESTAMP("%c", "Thu Dec 25 07:30:00 2008")
+--
+QueryStatement [77-133]
+  Query [77-133]
+    Select [77-133]
+      SelectList [84-133]
+        SelectColumn [84-133]
+          FunctionCall [84-133]
+            PathExpression [84-99]
+              Identifier(PARSE_TIMESTAMP) [84-99]
+            StringLiteral [100-104]
+              StringLiteralComponent("%c") [100-104]
+            StringLiteral [106-132]
+              StringLiteralComponent("Thu Dec 25 07:30:00 2008") [106-132]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#parse_timestamp
+SELECT PARSE_TIMESTAMP("%c", "Thu Dec 25 07:30:00 2008") AS parsed
+--
+QueryStatement [0-66]
+  Query [0-66]
+    Select [0-66]
+      SelectList [7-66]
+        SelectColumn [7-66]
+          FunctionCall [7-56]
+            PathExpression [7-22]
+              Identifier(PARSE_TIMESTAMP) [7-22]
+            StringLiteral [23-27]
+              StringLiteralComponent("%c") [23-27]
+            StringLiteral [29-55]
+              StringLiteralComponent("Thu Dec 25 07:30:00 2008") [29-55]
+          Alias [57-66]
+            Identifier(parsed) [60-66]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#pending_commit_timestamp
+[mode=expression]
+PENDING_COMMIT_TIMESTAMP()
+--
+FunctionCall [0-26]
+  PathExpression [0-24]
+    Identifier(PENDING_COMMIT_TIMESTAMP) [0-24]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#pending_commit_timestamp
+UPDATE Performances SET LastUpdatedTime = PENDING_COMMIT_TIMESTAMP()
+   WHERE SingerId=1 AND VenueId=2 AND EventDate="2015-10-21"
+--
+UpdateStatement [0-129]
+  PathExpression [7-19]
+    Identifier(Performances) [7-19]
+  UpdateItemList [24-68]
+    UpdateItem [24-68]
+      UpdateSetValue [24-68]
+        PathExpression [24-39]
+          Identifier(LastUpdatedTime) [24-39]
+        FunctionCall [42-68]
+          PathExpression [42-66]
+            Identifier(PENDING_COMMIT_TIMESTAMP) [42-66]
+  AndExpr [78-129]
+    BinaryExpression(=) [78-88]
+      PathExpression [78-86]
+        Identifier(SingerId) [78-86]
+      IntLiteral(1) [87-88]
+    BinaryExpression(=) [93-102]
+      PathExpression [93-100]
+        Identifier(VenueId) [93-100]
+      IntLiteral(2) [101-102]
+    BinaryExpression(=) [107-129]
+      PathExpression [107-116]
+        Identifier(EventDate) [107-116]
+      StringLiteral [117-129]
+        StringLiteralComponent("2015-10-21") [117-129]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#string
+SELECT STRING(TIMESTAMP "2008-12-25 15:30:00+00", "UTC") AS string
+--
+QueryStatement [0-66]
+  Query [0-66]
+    Select [0-66]
+      SelectList [7-66]
+        SelectColumn [7-66]
+          FunctionCall [7-56]
+            PathExpression [7-13]
+              Identifier(STRING) [7-13]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [14-48]
+              StringLiteral [24-48]
+                StringLiteralComponent("2008-12-25 15:30:00+00") [24-48]
+            StringLiteral [50-55]
+              StringLiteralComponent("UTC") [50-55]
+          Alias [57-66]
+            Identifier(string) [60-66]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#timestamp
+SELECT TIMESTAMP("2008-12-25 15:30:00+00") AS timestamp_str
+--
+QueryStatement [0-59]
+  Query [0-59]
+    Select [0-59]
+      SelectList [7-59]
+        SelectColumn [7-59]
+          FunctionCall [7-42]
+            PathExpression [7-16]
+              Identifier(TIMESTAMP) [7-16]
+            StringLiteral [17-41]
+              StringLiteralComponent("2008-12-25 15:30:00+00") [17-41]
+          Alias [43-59]
+            Identifier(timestamp_str) [46-59]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#timestamp
+SELECT TIMESTAMP("2008-12-25 15:30:00", "America/Los_Angeles") AS timestamp_str
+--
+QueryStatement [0-79]
+  Query [0-79]
+    Select [0-79]
+      SelectList [7-79]
+        SelectColumn [7-79]
+          FunctionCall [7-62]
+            PathExpression [7-16]
+              Identifier(TIMESTAMP) [7-16]
+            StringLiteral [17-38]
+              StringLiteralComponent("2008-12-25 15:30:00") [17-38]
+            StringLiteral [40-61]
+              StringLiteralComponent("America/Los_Angeles") [40-61]
+          Alias [63-79]
+            Identifier(timestamp_str) [66-79]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#timestamp
+SELECT TIMESTAMP("2008-12-25 15:30:00 UTC") AS timestamp_str
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-60]
+      SelectList [7-60]
+        SelectColumn [7-60]
+          FunctionCall [7-43]
+            PathExpression [7-16]
+              Identifier(TIMESTAMP) [7-16]
+            StringLiteral [17-42]
+              StringLiteralComponent("2008-12-25 15:30:00 UTC") [17-42]
+          Alias [44-60]
+            Identifier(timestamp_str) [47-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#timestamp
+SELECT TIMESTAMP(DATE "2008-12-25") AS timestamp_date
+--
+QueryStatement [0-53]
+  Query [0-53]
+    Select [0-53]
+      SelectList [7-53]
+        SelectColumn [7-53]
+          FunctionCall [7-35]
+            PathExpression [7-16]
+              Identifier(TIMESTAMP) [7-16]
+            DateOrTimeLiteral(TYPE_DATE) [17-34]
+              StringLiteral [22-34]
+                StringLiteralComponent("2008-12-25") [22-34]
+          Alias [36-53]
+            Identifier(timestamp_date) [39-53]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#timestamp_add
+[mode=expression]
+TIMESTAMP_ADD(timestamp_expression, INTERVAL step_size step_unit)
+--
+FunctionCall [0-65]
+  PathExpression [0-13]
+    Identifier(TIMESTAMP_ADD) [0-13]
+  PathExpression [14-34]
+    Identifier(timestamp_expression) [14-34]
+  IntervalExpr [36-64]
+    PathExpression [45-54]
+      Identifier(step_size) [45-54]
+    Identifier(step_unit) [55-64]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#timestamp_add
+SELECT
+  TIMESTAMP("2008-12-25 15:30:00+00") AS original,
+  TIMESTAMP_ADD(TIMESTAMP "2008-12-25 15:30:00+00", INTERVAL 10 MINUTE) AS later
+--
+QueryStatement [0-138]
+  Query [0-138]
+    Select [0-138]
+      SelectList [9-138]
+        SelectColumn [9-56]
+          FunctionCall [9-44]
+            PathExpression [9-18]
+              Identifier(TIMESTAMP) [9-18]
+            StringLiteral [19-43]
+              StringLiteralComponent("2008-12-25 15:30:00+00") [19-43]
+          Alias [45-56]
+            Identifier(original) [48-56]
+        SelectColumn [60-138]
+          FunctionCall [60-129]
+            PathExpression [60-73]
+              Identifier(TIMESTAMP_ADD) [60-73]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [74-108]
+              StringLiteral [84-108]
+                StringLiteralComponent("2008-12-25 15:30:00+00") [84-108]
+            IntervalExpr [110-128]
+              IntLiteral(10) [119-121]
+              Identifier(MINUTE) [122-128]
+          Alias [130-138]
+            Identifier(later) [133-138]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#timestamp_diff
+[mode=expression]
+TIMESTAMP_DIFF(end_timestamp, start_timestamp, granularity)
+--
+FunctionCall [0-59]
+  PathExpression [0-14]
+    Identifier(TIMESTAMP_DIFF) [0-14]
+  PathExpression [15-28]
+    Identifier(end_timestamp) [15-28]
+  PathExpression [30-45]
+    Identifier(start_timestamp) [30-45]
+  PathExpression [47-58]
+    Identifier(granularity) [47-58]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#timestamp_diff
+SELECT
+  TIMESTAMP("2010-07-07 10:20:00+00") AS later_timestamp,
+  TIMESTAMP("2008-12-25 15:30:00+00") AS earlier_timestamp,
+  TIMESTAMP_DIFF(TIMESTAMP "2010-07-07 10:20:00+00", TIMESTAMP "2008-12-25 15:30:00+00", HOUR) AS hours
+--
+QueryStatement [0-228]
+  Query [0-228]
+    Select [0-228]
+      SelectList [9-228]
+        SelectColumn [9-63]
+          FunctionCall [9-44]
+            PathExpression [9-18]
+              Identifier(TIMESTAMP) [9-18]
+            StringLiteral [19-43]
+              StringLiteralComponent("2010-07-07 10:20:00+00") [19-43]
+          Alias [45-63]
+            Identifier(later_timestamp) [48-63]
+        SelectColumn [67-123]
+          FunctionCall [67-102]
+            PathExpression [67-76]
+              Identifier(TIMESTAMP) [67-76]
+            StringLiteral [77-101]
+              StringLiteralComponent("2008-12-25 15:30:00+00") [77-101]
+          Alias [103-123]
+            Identifier(earlier_timestamp) [106-123]
+        SelectColumn [127-228]
+          FunctionCall [127-219]
+            PathExpression [127-141]
+              Identifier(TIMESTAMP_DIFF) [127-141]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [142-176]
+              StringLiteral [152-176]
+                StringLiteralComponent("2010-07-07 10:20:00+00") [152-176]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [178-212]
+              StringLiteral [188-212]
+                StringLiteralComponent("2008-12-25 15:30:00+00") [188-212]
+            PathExpression [214-218]
+              Identifier(HOUR) [214-218]
+          Alias [220-228]
+            Identifier(hours) [223-228]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#timestamp_diff
+SELECT TIMESTAMP_DIFF(TIMESTAMP "2018-08-14", TIMESTAMP "2018-10-14", DAY) AS negative_diff
+--
+QueryStatement [0-91]
+  Query [0-91]
+    Select [0-91]
+      SelectList [7-91]
+        SelectColumn [7-91]
+          FunctionCall [7-74]
+            PathExpression [7-21]
+              Identifier(TIMESTAMP_DIFF) [7-21]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [22-44]
+              StringLiteral [32-44]
+                StringLiteralComponent("2018-08-14") [32-44]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [46-68]
+              StringLiteral [56-68]
+                StringLiteralComponent("2018-10-14") [56-68]
+            PathExpression [70-73]
+              Identifier(DAY) [70-73]
+          Alias [75-91]
+            Identifier(negative_diff) [78-91]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#timestamp_diff
+SELECT TIMESTAMP_DIFF("2001-02-01 01:00:00", "2001-02-01 00:00:01", HOUR) AS diff
+--
+QueryStatement [0-81]
+  Query [0-81]
+    Select [0-81]
+      SelectList [7-81]
+        SelectColumn [7-81]
+          FunctionCall [7-73]
+            PathExpression [7-21]
+              Identifier(TIMESTAMP_DIFF) [7-21]
+            StringLiteral [22-43]
+              StringLiteralComponent("2001-02-01 01:00:00") [22-43]
+            StringLiteral [45-66]
+              StringLiteralComponent("2001-02-01 00:00:01") [45-66]
+            PathExpression [68-72]
+              Identifier(HOUR) [68-72]
+          Alias [74-81]
+            Identifier(diff) [77-81]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#timestamp_diff
+SELECT
+  TIMESTAMP_DIFF(TIMESTAMP '2021-05-01 04:54:00+00',
+    TIMESTAMP '2021-05-01 01:00:00+00', HOUR) AS diff_3_9,
+  TIMESTAMP_DIFF(TIMESTAMP '2021-05-01 04:06:00+00',
+    TIMESTAMP '2021-05-01 01:00:00+00', HOUR) AS diff_3_1,
+  TIMESTAMP_DIFF(TIMESTAMP '2021-05-01 01:00:00+00',
+    TIMESTAMP '2021-05-01 04:54:00+00', HOUR) AS diff_negative_3_9,
+  TIMESTAMP_DIFF(TIMESTAMP '2021-05-01 01:00:00+00',
+    TIMESTAMP '2021-05-01 04:06:00+00', HOUR) AS diff_negative_3_1
+--
+QueryStatement [0-471]
+  Query [0-471]
+    Select [0-471]
+      SelectList [9-471]
+        SelectColumn [9-117]
+          FunctionCall [9-105]
+            PathExpression [9-23]
+              Identifier(TIMESTAMP_DIFF) [9-23]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [24-58]
+              StringLiteral [34-58]
+                StringLiteralComponent('2021-05-01 04:54:00+00') [34-58]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [64-98]
+              StringLiteral [74-98]
+                StringLiteralComponent('2021-05-01 01:00:00+00') [74-98]
+            PathExpression [100-104]
+              Identifier(HOUR) [100-104]
+          Alias [106-117]
+            Identifier(diff_3_9) [109-117]
+        SelectColumn [121-229]
+          FunctionCall [121-217]
+            PathExpression [121-135]
+              Identifier(TIMESTAMP_DIFF) [121-135]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [136-170]
+              StringLiteral [146-170]
+                StringLiteralComponent('2021-05-01 04:06:00+00') [146-170]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [176-210]
+              StringLiteral [186-210]
+                StringLiteralComponent('2021-05-01 01:00:00+00') [186-210]
+            PathExpression [212-216]
+              Identifier(HOUR) [212-216]
+          Alias [218-229]
+            Identifier(diff_3_1) [221-229]
+        SelectColumn [233-350]
+          FunctionCall [233-329]
+            PathExpression [233-247]
+              Identifier(TIMESTAMP_DIFF) [233-247]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [248-282]
+              StringLiteral [258-282]
+                StringLiteralComponent('2021-05-01 01:00:00+00') [258-282]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [288-322]
+              StringLiteral [298-322]
+                StringLiteralComponent('2021-05-01 04:54:00+00') [298-322]
+            PathExpression [324-328]
+              Identifier(HOUR) [324-328]
+          Alias [330-350]
+            Identifier(diff_negative_3_9) [333-350]
+        SelectColumn [354-471]
+          FunctionCall [354-450]
+            PathExpression [354-368]
+              Identifier(TIMESTAMP_DIFF) [354-368]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [369-403]
+              StringLiteral [379-403]
+                StringLiteralComponent('2021-05-01 01:00:00+00') [379-403]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [409-443]
+              StringLiteral [419-443]
+                StringLiteralComponent('2021-05-01 04:06:00+00') [419-443]
+            PathExpression [445-449]
+              Identifier(HOUR) [445-449]
+          Alias [451-471]
+            Identifier(diff_negative_3_1) [454-471]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#timestamp_micros
+[mode=expression]
+TIMESTAMP_MICROS(int64_expression)
+--
+FunctionCall [0-34]
+  PathExpression [0-16]
+    Identifier(TIMESTAMP_MICROS) [0-16]
+  PathExpression [17-33]
+    Identifier(int64_expression) [17-33]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#timestamp_micros
+SELECT TIMESTAMP_MICROS(1230219000000000) AS timestamp_value
+--
+QueryStatement [0-60]
+  Query [0-60]
+    Select [0-60]
+      SelectList [7-60]
+        SelectColumn [7-60]
+          FunctionCall [7-41]
+            PathExpression [7-23]
+              Identifier(TIMESTAMP_MICROS) [7-23]
+            IntLiteral(1230219000000000) [24-40]
+          Alias [42-60]
+            Identifier(timestamp_value) [45-60]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#timestamp_millis
+[mode=expression]
+TIMESTAMP_MILLIS(int64_expression)
+--
+FunctionCall [0-34]
+  PathExpression [0-16]
+    Identifier(TIMESTAMP_MILLIS) [0-16]
+  PathExpression [17-33]
+    Identifier(int64_expression) [17-33]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#timestamp_millis
+SELECT TIMESTAMP_MILLIS(1230219000000) AS timestamp_value
+--
+QueryStatement [0-57]
+  Query [0-57]
+    Select [0-57]
+      SelectList [7-57]
+        SelectColumn [7-57]
+          FunctionCall [7-38]
+            PathExpression [7-23]
+              Identifier(TIMESTAMP_MILLIS) [7-23]
+            IntLiteral(1230219000000) [24-37]
+          Alias [39-57]
+            Identifier(timestamp_value) [42-57]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#timestamp_seconds
+[mode=expression]
+TIMESTAMP_SECONDS(int64_expression)
+--
+FunctionCall [0-35]
+  PathExpression [0-17]
+    Identifier(TIMESTAMP_SECONDS) [0-17]
+  PathExpression [18-34]
+    Identifier(int64_expression) [18-34]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#timestamp_seconds
+SELECT TIMESTAMP_SECONDS(1230219000) AS timestamp_value
+--
+QueryStatement [0-55]
+  Query [0-55]
+    Select [0-55]
+      SelectList [7-55]
+        SelectColumn [7-55]
+          FunctionCall [7-36]
+            PathExpression [7-24]
+              Identifier(TIMESTAMP_SECONDS) [7-24]
+            IntLiteral(1230219000) [25-35]
+          Alias [37-55]
+            Identifier(timestamp_value) [40-55]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#timestamp_sub
+[mode=expression]
+TIMESTAMP_SUB(timestamp_expression, INTERVAL step_size step_unit)
+--
+FunctionCall [0-65]
+  PathExpression [0-13]
+    Identifier(TIMESTAMP_SUB) [0-13]
+  PathExpression [14-34]
+    Identifier(timestamp_expression) [14-34]
+  IntervalExpr [36-64]
+    PathExpression [45-54]
+      Identifier(step_size) [45-54]
+    Identifier(step_unit) [55-64]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#timestamp_sub
+SELECT
+  TIMESTAMP("2008-12-25 15:30:00+00") AS original,
+  TIMESTAMP_SUB(TIMESTAMP "2008-12-25 15:30:00+00", INTERVAL 10 MINUTE) AS earlier
+--
+QueryStatement [0-140]
+  Query [0-140]
+    Select [0-140]
+      SelectList [9-140]
+        SelectColumn [9-56]
+          FunctionCall [9-44]
+            PathExpression [9-18]
+              Identifier(TIMESTAMP) [9-18]
+            StringLiteral [19-43]
+              StringLiteralComponent("2008-12-25 15:30:00+00") [19-43]
+          Alias [45-56]
+            Identifier(original) [48-56]
+        SelectColumn [60-140]
+          FunctionCall [60-129]
+            PathExpression [60-73]
+              Identifier(TIMESTAMP_SUB) [60-73]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [74-108]
+              StringLiteral [84-108]
+                StringLiteralComponent("2008-12-25 15:30:00+00") [84-108]
+            IntervalExpr [110-128]
+              IntLiteral(10) [119-121]
+              Identifier(MINUTE) [122-128]
+          Alias [130-140]
+            Identifier(earlier) [133-140]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#timestamp_trunc
+SELECT
+  TIMESTAMP_TRUNC(TIMESTAMP "2008-12-25 15:30:00+00", DAY, "UTC") AS utc,
+  TIMESTAMP_TRUNC(TIMESTAMP "2008-12-25 15:30:00+00", DAY, "America/Los_Angeles") AS la
+--
+QueryStatement [0-168]
+  Query [0-168]
+    Select [0-168]
+      SelectList [9-168]
+        SelectColumn [9-79]
+          FunctionCall [9-72]
+            PathExpression [9-24]
+              Identifier(TIMESTAMP_TRUNC) [9-24]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [25-59]
+              StringLiteral [35-59]
+                StringLiteralComponent("2008-12-25 15:30:00+00") [35-59]
+            PathExpression [61-64]
+              Identifier(DAY) [61-64]
+            StringLiteral [66-71]
+              StringLiteralComponent("UTC") [66-71]
+          Alias [73-79]
+            Identifier(utc) [76-79]
+        SelectColumn [83-168]
+          FunctionCall [83-162]
+            PathExpression [83-98]
+              Identifier(TIMESTAMP_TRUNC) [83-98]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [99-133]
+              StringLiteral [109-133]
+                StringLiteralComponent("2008-12-25 15:30:00+00") [109-133]
+            PathExpression [135-138]
+              Identifier(DAY) [135-138]
+            StringLiteral [140-161]
+              StringLiteralComponent("America/Los_Angeles") [140-161]
+          Alias [163-168]
+            Identifier(la) [166-168]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#timestamp_trunc
+SELECT
+  TIMESTAMP_TRUNC("2015-06-15 00:00:00+00", ISOYEAR) AS isoyear_boundary,
+  EXTRACT(ISOYEAR FROM TIMESTAMP "2015-06-15 00:00:00+00") AS isoyear_number
+--
+QueryStatement [0-157]
+  Query [0-157]
+    Select [0-157]
+      SelectList [9-157]
+        SelectColumn [9-79]
+          FunctionCall [9-59]
+            PathExpression [9-24]
+              Identifier(TIMESTAMP_TRUNC) [9-24]
+            StringLiteral [25-49]
+              StringLiteralComponent("2015-06-15 00:00:00+00") [25-49]
+            PathExpression [51-58]
+              Identifier(ISOYEAR) [51-58]
+          Alias [60-79]
+            Identifier(isoyear_boundary) [63-79]
+        SelectColumn [83-157]
+          ExtractExpression [83-139]
+            PathExpression [91-98]
+              Identifier(ISOYEAR) [91-98]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [104-138]
+              StringLiteral [114-138]
+                StringLiteralComponent("2015-06-15 00:00:00+00") [114-138]
+          Alias [140-157]
+            Identifier(isoyear_number) [143-157]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#unix_micros
+[mode=expression]
+UNIX_MICROS(timestamp_expression)
+--
+FunctionCall [0-33]
+  PathExpression [0-11]
+    Identifier(UNIX_MICROS) [0-11]
+  PathExpression [12-32]
+    Identifier(timestamp_expression) [12-32]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#unix_micros
+SELECT UNIX_MICROS(TIMESTAMP "2008-12-25 15:30:00+00") AS micros
+--
+QueryStatement [0-64]
+  Query [0-64]
+    Select [0-64]
+      SelectList [7-64]
+        SelectColumn [7-64]
+          FunctionCall [7-54]
+            PathExpression [7-18]
+              Identifier(UNIX_MICROS) [7-18]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [19-53]
+              StringLiteral [29-53]
+                StringLiteralComponent("2008-12-25 15:30:00+00") [29-53]
+          Alias [55-64]
+            Identifier(micros) [58-64]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#unix_micros
+SELECT UNIX_MICROS(TIMESTAMP "1970-01-01 00:00:00.0000018+00") AS micros
+--
+QueryStatement [0-72]
+  Query [0-72]
+    Select [0-72]
+      SelectList [7-72]
+        SelectColumn [7-72]
+          FunctionCall [7-62]
+            PathExpression [7-18]
+              Identifier(UNIX_MICROS) [7-18]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [19-61]
+              StringLiteral [29-61]
+                StringLiteralComponent("1970-01-01 00:00:00.0000018+00") [29-61]
+          Alias [63-72]
+            Identifier(micros) [66-72]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#unix_millis
+[mode=expression]
+UNIX_MILLIS(timestamp_expression)
+--
+FunctionCall [0-33]
+  PathExpression [0-11]
+    Identifier(UNIX_MILLIS) [0-11]
+  PathExpression [12-32]
+    Identifier(timestamp_expression) [12-32]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#unix_millis
+SELECT UNIX_MILLIS(TIMESTAMP "2008-12-25 15:30:00+00") AS millis
+--
+QueryStatement [0-64]
+  Query [0-64]
+    Select [0-64]
+      SelectList [7-64]
+        SelectColumn [7-64]
+          FunctionCall [7-54]
+            PathExpression [7-18]
+              Identifier(UNIX_MILLIS) [7-18]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [19-53]
+              StringLiteral [29-53]
+                StringLiteralComponent("2008-12-25 15:30:00+00") [29-53]
+          Alias [55-64]
+            Identifier(millis) [58-64]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#unix_millis
+SELECT UNIX_MILLIS(TIMESTAMP "1970-01-01 00:00:00.0018+00") AS millis
+--
+QueryStatement [0-69]
+  Query [0-69]
+    Select [0-69]
+      SelectList [7-69]
+        SelectColumn [7-69]
+          FunctionCall [7-59]
+            PathExpression [7-18]
+              Identifier(UNIX_MILLIS) [7-18]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [19-58]
+              StringLiteral [29-58]
+                StringLiteralComponent("1970-01-01 00:00:00.0018+00") [29-58]
+          Alias [60-69]
+            Identifier(millis) [63-69]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#unix_seconds
+[mode=expression]
+UNIX_SECONDS(timestamp_expression)
+--
+FunctionCall [0-34]
+  PathExpression [0-12]
+    Identifier(UNIX_SECONDS) [0-12]
+  PathExpression [13-33]
+    Identifier(timestamp_expression) [13-33]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#unix_seconds
+SELECT UNIX_SECONDS(TIMESTAMP "2008-12-25 15:30:00+00") AS seconds
+--
+QueryStatement [0-66]
+  Query [0-66]
+    Select [0-66]
+      SelectList [7-66]
+        SelectColumn [7-66]
+          FunctionCall [7-55]
+            PathExpression [7-19]
+              Identifier(UNIX_SECONDS) [7-19]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [20-54]
+              StringLiteral [30-54]
+                StringLiteralComponent("2008-12-25 15:30:00+00") [30-54]
+          Alias [56-66]
+            Identifier(seconds) [59-66]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/timestamp_functions#unix_seconds
+SELECT UNIX_SECONDS(TIMESTAMP "1970-01-01 00:00:01.8+00") AS seconds
+--
+QueryStatement [0-68]
+  Query [0-68]
+    Select [0-68]
+      SelectList [7-68]
+        SelectColumn [7-68]
+          FunctionCall [7-57]
+            PathExpression [7-19]
+              Identifier(UNIX_SECONDS) [7-19]
+            DateOrTimeLiteral(TYPE_TIMESTAMP) [20-56]
+              StringLiteral [30-56]
+                StringLiteralComponent("1970-01-01 00:00:01.8+00") [30-56]
+          Alias [58-68]
+            Identifier(seconds) [61-68]
diff --git a/parser/testdata/spanner/utility-functions.test b/parser/testdata/spanner/utility-functions.test
new file mode 100644
index 0000000..974cb62
--- /dev/null
+++ b/parser/testdata/spanner/utility-functions.test
@@ -0,0 +1,49 @@
+# Code generated by go run ./cmd/spanner-corpus. DO NOT EDIT.
+# Source: https://docs.cloud.google.com/spanner/docs/reference/standard-sql/utility-functions
+# Examples from Google Cloud documentation, licensed under Apache 2.0.
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/utility-functions#generate_uuid
+[default language_features=MAXIMUM,+SPANNER_LEGACY_DDL,+SQL_GRAPH,+SQL_GRAPH_ADVANCED_QUERY,+SQL_GRAPH_BOUNDED_PATH_QUANTIFICATION,+SQL_GRAPH_PATH_TYPE,+SQL_GRAPH_PATH_MODE]
+[default no_show_parse_location_text]
+[default reserve_graph_table]
+[mode=expression]
+GENERATE_UUID()
+--
+FunctionCall [0-15]
+  PathExpression [0-13]
+    Identifier(GENERATE_UUID) [0-13]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/utility-functions#generate_uuid
+SELECT GENERATE_UUID() AS uuid
+--
+QueryStatement [0-30]
+  Query [0-30]
+    Select [0-30]
+      SelectList [7-30]
+        SelectColumn [7-30]
+          FunctionCall [7-22]
+            PathExpression [7-20]
+              Identifier(GENERATE_UUID) [7-20]
+          Alias [23-30]
+            Identifier(uuid) [26-30]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/utility-functions#new_uuid
+[mode=expression]
+NEW_UUID()
+--
+FunctionCall [0-10]
+  PathExpression [0-8]
+    Identifier(NEW_UUID) [0-8]
+==
+# https://docs.cloud.google.com/spanner/docs/reference/standard-sql/utility-functions#new_uuid
+SELECT NEW_UUID() AS uuid
+--
+QueryStatement [0-25]
+  Query [0-25]
+    Select [0-25]
+      SelectList [7-25]
+        SelectColumn [7-25]
+          FunctionCall [7-17]
+            PathExpression [7-15]
+              Identifier(NEW_UUID) [7-15]
+          Alias [18-25]
+            Identifier(uuid) [21-25]