Repository: cloudspannerecosystem/memefish
Pull request #179 (2024-10-27 03:06:24)

Support SCHEMA DDL statements
This PR implements `SCHEMA` DDL statements, `CREATE SCHEMA` and `DROP SCHEMA`.

Note

The official docs says `DROP SCHEMA` can target multiple schema names.

```
DROP SCHEMA schema_name [, ...]
```

but, it seems not to be true.

```
spanner> DROP SCHEMA schema1, schema2;
ERROR: rpc error: code = InvalidArgument desc = Error parsing Spanner DDL statement: DROP SCHEMA schema1, schema2 : Syntax error on line 1, column 20: Expecting 'EOF' but found ','
```

* part of #100 


Patch:
diff --git a/ast/ast.go b/ast/ast.go
index 2dbdfff..12c7e23 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -60,6 +60,8 @@ type Statement interface {
 // - https://cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax
 
 func (QueryStatement) isStatement()     {}
+func (CreateSchema) isStatement()       {}
+func (DropSchema) isStatement()         {}
 func (CreateDatabase) isStatement()     {}
 func (AlterDatabase) isStatement()      {}
 func (CreateTable) isStatement()        {}
@@ -150,38 +152,43 @@ type Expr interface {
 	isExpr()
 }
 
-func (BinaryExpr) isExpr()       {}
-func (UnaryExpr) isExpr()        {}
-func (InExpr) isExpr()           {}
-func (IsNullExpr) isExpr()       {}
-func (IsBoolExpr) isExpr()       {}
-func (BetweenExpr) isExpr()      {}
-func (SelectorExpr) isExpr()     {}
-func (IndexExpr) isExpr()        {}
-func (CallExpr) isExpr()         {}
-func (CountStarExpr) isExpr()    {}
-func (CastExpr) isExpr()         {}
-func (ExtractExpr) isExpr()      {}
-func (CaseExpr) isExpr()         {}
-func (ParenExpr) isExpr()        {}
-func (ScalarSubQuery) isExpr()   {}
-func (ArraySubQuery) isExpr()    {}
-func (ExistsSubQuery) isExpr()   {}
-func (Param) isExpr()            {}
-func (Ident) isExpr()            {}
-func (Path) isExpr()             {}
-func (ArrayLiteral) isExpr()     {}
-func (StructLiteral) isExpr()    {}
-func (NullLiteral) isExpr()      {}
-func (BoolLiteral) isExpr()      {}
-func (IntLiteral) isExpr()       {}
-func (FloatLiteral) isExpr()     {}
-func (StringLiteral) isExpr()    {}
-func (BytesLiteral) isExpr()     {}
-func (DateLiteral) isExpr()      {}
-func (TimestampLiteral) isExpr() {}
-func (NumericLiteral) isExpr()   {}
-func (JSONLiteral) isExpr()      {}
+func (BinaryExpr) isExpr()            {}
+func (UnaryExpr) isExpr()             {}
+func (InExpr) isExpr()                {}
+func (IsNullExpr) isExpr()            {}
+func (IsBoolExpr) isExpr()            {}
+func (BetweenExpr) isExpr()           {}
+func (SelectorExpr) isExpr()          {}
+func (IndexExpr) isExpr()             {}
+func (CallExpr) isExpr()              {}
+func (CountStarExpr) isExpr()         {}
+func (CastExpr) isExpr()              {}
+func (ExtractExpr) isExpr()           {}
+func (CaseExpr) isExpr()              {}
+func (ParenExpr) isExpr()             {}
+func (ScalarSubQuery) isExpr()        {}
+func (ArraySubQuery) isExpr()         {}
+func (ExistsSubQuery) isExpr()        {}
+func (Param) isExpr()                 {}
+func (Ident) isExpr()                 {}
+func (Path) isExpr()                  {}
+func (ArrayLiteral) isExpr()          {}
+func (TupleStructLiteral) isExpr()    {}
+func (TypelessStructLiteral) isExpr() {}
+func (TypedStructLiteral) isExpr()    {}
+func (NullLiteral) isExpr()           {}
+func (BoolLiteral) isExpr()           {}
+func (IntLiteral) isExpr()            {}
+func (FloatLiteral) isExpr()          {}
+func (StringLiteral) isExpr()         {}
+func (BytesLiteral) isExpr()          {}
+func (DateLiteral) isExpr()           {}
+func (TimestampLiteral) isExpr()      {}
+func (NumericLiteral) isExpr()        {}
+func (JSONLiteral) isExpr()           {}
+func (NewConstructor) isExpr()        {}
+func (BracedNewConstructor) isExpr()  {}
+func (BracedConstructor) isExpr()     {}
 
 // Arg represents argument of function call.
 type Arg interface {
@@ -221,6 +228,24 @@ func (UnnestInCondition) isInCondition()   {}
 func (SubQueryInCondition) isInCondition() {}
 func (ValuesInCondition) isInCondition()   {}
 
+// TypelessStructLiteralArg represents an argument of typeless STRUCT literals.
+type TypelessStructLiteralArg interface {
+	Node
+	isTypelessStructLiteralArg()
+}
+
+func (ExprArg) isTypelessStructLiteralArg() {}
+func (Alias) isTypelessStructLiteralArg()   {}
+
+// NewConstructorArg represents an argument of NEW constructors.
+type NewConstructorArg interface {
+	Node
+	isNewConstructorArg()
+}
+
+func (ExprArg) isNewConstructorArg() {}
+func (Alias) isNewConstructorArg()   {}
+
 // Type represents type node.
 type Type interface {
 	Node
@@ -274,6 +299,8 @@ type DDL interface {
 //
 // - https://cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language
 
+func (CreateSchema) isDDL()       {}
+func (DropSchema) isDDL()         {}
 func (CreateDatabase) isDDL()     {}
 func (AlterDatabase) isDDL()      {}
 func (CreateTable) isDDL()        {}
@@ -607,7 +634,10 @@ type DotStar struct {
 	Expr Expr
 }
 
-// Alias is aliased expression by AS clause in SELECT result columns list.
+// Alias is aliased expression by AS clause.
+//
+// Typically, this appears in SELECT result columns list, but this can appear in typeless STRUCT literals
+// and NEW constructors.
 //
 //	{{.Expr | sql}} {{.As | sql}}
 type Alias struct {
@@ -622,16 +652,12 @@ type Alias struct {
 //
 // It is used in Alias node and some JoinExpr nodes.
 //
-// NOTE: Sometime keyword AS can be omited.
-//
-//	  In this case, it.token.Pos() == it.Alias.token.Pos(), so we can detect this.
-//
-//	AS {{.Alias | sql}}
+//	{{if not .As.Invalid}}AS {{end}}{{.Alias | sql}}
 type AsAlias struct {
 	// pos = As || Alias.pos
 	// end = Alias.end
 
-	As token.Pos // position of "AS" keyword
+	As token.Pos // position of "AS" keyword, optional
 
 	Alias *Ident
 }
@@ -1376,22 +1402,45 @@ type ArrayLiteral struct {
 	Values []Expr
 }
 
-// StructLiteral is struct literal node.
+// TupleStructLiteral is tuple syntax struct literal node.
 //
-//	STRUCT{{if not (isnil .Fields)}}<{{.Fields | sqlJoin ","}}>{{end}}({{.Values | sqlJoin ","}})
-type StructLiteral struct {
-	// pos = Struct || Lparen
+//	({{.Values | sqlJoin ","}})
+type TupleStructLiteral struct {
+	// pos = Lparen
 	// end = Rparen + 1
 
-	Struct         token.Pos // position of "STRUCT"
 	Lparen, Rparen token.Pos // position of "(" and ")"
 
-	// NOTE: Distinguish nil from len(Fields) == 0 case.
-	//       nil means type is not specified, or empty slice means this struct has 0 fields.
+	Values []Expr // len(Values) > 1
+}
+
+// TypedStructLiteral is typed struct literal node.
+//
+//	STRUCT<{{.Fields | sqlJoin ","}}>({{.Values | sqlJoin ","}})
+type TypedStructLiteral struct {
+	// pos = Struct
+	// end = Rparen + 1
+
+	Struct token.Pos // position of "STRUCT"
+	Rparen token.Pos // position of ")"
+
 	Fields []*StructField
 	Values []Expr
 }
 
+// TypelessStructLiteral is typeless struct literal node.
+//
+//	STRUCT({{.Values | sqlJoin ","}})
+type TypelessStructLiteral struct {
+	// pos = Struct
+	// end = Rparen + 1
+
+	Struct token.Pos // position of "STRUCT"
+	Rparen token.Pos // position of ")"
+
+	Values []TypelessStructLiteralArg
+}
+
 // NullLiteral is just NULL literal.
 //
 //	NULL
@@ -1511,6 +1560,85 @@ type JSONLiteral struct {
 	Value *StringLiteral
 }
 
+// ================================================================================
+//
+// NEW constructors
+//
+// ================================================================================
+
+// BracedConstructorFieldValue represents value part of fields in BracedNewConstructor.
+type BracedConstructorFieldValue interface {
+	Node
+	isBracedConstructorFieldValue()
+}
+
+func (*BracedConstructor) isBracedConstructorFieldValue()               {}
+func (*BracedConstructorFieldValueExpr) isBracedConstructorFieldValue() {}
+
+// NewConstructor represents NEW operator which creates a protocol buffer using a parenthesized list of arguments.
+//
+//	NEW {{.Type | sql}} ({{.Args | sqlJoin ", "}})
+type NewConstructor struct {
+	// pos = New
+	// end = Rparen + 1
+
+	New  token.Pos
+	Type *NamedType
+
+	Args []NewConstructorArg
+
+	Rparen token.Pos
+}
+
+// BracedNewConstructor represents NEW operator which creates a protocol buffer using a map constructor.
+//
+//	NEW {{.Type | sql}} {{"{"}}{{"}"}}
+type BracedNewConstructor struct {
+	// pos = New
+	// end = Body.end
+
+	New token.Pos
+
+	Type *NamedType
+	Body *BracedConstructor
+}
+
+// BracedConstructor represents a single map constructor which is used in BracedNewConstructor.
+// Actually, it is a top level Expr in syntax, but it is not permitted semantically in other place.
+//
+//	{{"{"}}{{.Fields | sqlJoin ", "}}{{"}"}}
+type BracedConstructor struct {
+	// pos = Lbrace
+	// end = Rbrace + 1
+
+	Lbrace, Rbrace token.Pos
+
+	Fields []*BracedConstructorField
+}
+
+// BracedConstructorField represents a single field in BracedConstructor.
+//
+//	{{.Name | sql}} {{.Value | sql}}
+type BracedConstructorField struct {
+	// pos = Name.pos
+	// end = Value.end
+
+	Name  *Ident
+	Value BracedConstructorFieldValue
+}
+
+// BracedConstructorFieldValueExpr represents a field value node.
+//
+//	: {{.Expr | sql}}
+type BracedConstructorFieldValueExpr struct {
+	// pos = Colon
+	// end = Expr.end
+
+	Colon token.Pos
+
+	Expr Expr
+}
+
 // ================================================================================
 //
 // Type
@@ -1641,6 +1769,30 @@ type OptionsDef struct {
 	Value Expr
 }
 
+// CreateSchema is CREATE SCHEMA statement node.
+//
+//	CREATE SCHEMA {{.Name | sql}}
+type CreateSchema struct {
+	// pos = Create
+	// end = Name.end
+
+	Create token.Pos // position of "CREATE" keyword
+
+	Name *Ident
+}
+
+// DropSchema is DROP SCHEMA statement node.
+//
+//	DROP SCHEMA {{.Name | sql}}
+type DropSchema struct {
+	// pos = Drop
+	// end = Name.end
+
+	Drop token.Pos // position of "DROP" keyword
+
+	Name *Ident
+}
+
 // CreateDatabase is CREATE DATABASE statement node.
 //
 //	CREATE DATABASE {{.Name | sql}}
diff --git a/ast/pos.go b/ast/pos.go
index f287563..4bc700c 100644
--- a/ast/pos.go
+++ b/ast/pos.go
@@ -574,12 +574,28 @@ func (a *ArrayLiteral) End() token.Pos {
 	return posAdd(a.Rbrack, 1)
 }
 
-func (s *StructLiteral) Pos() token.Pos {
-	return posChoice(s.Struct, s.Lparen)
+func (t *TupleStructLiteral) Pos() token.Pos {
+	return t.Lparen
 }
 
-func (s *StructLiteral) End() token.Pos {
-	return posAdd(s.Rparen, 1)
+func (t *TupleStructLiteral) End() token.Pos {
+	return posAdd(t.Rparen, 1)
+}
+
+func (t *TypedStructLiteral) Pos() token.Pos {
+	return t.Struct
+}
+
+func (t *TypedStructLiteral) End() token.Pos {
+	return posAdd(t.Rparen, 1)
+}
+
+func (t *TypelessStructLiteral) Pos() token.Pos {
+	return t.Struct
+}
+
+func (t *TypelessStructLiteral) End() token.Pos {
+	return posAdd(t.Rparen, 1)
 }
 
 func (n *NullLiteral) Pos() token.Pos {
@@ -662,6 +678,46 @@ func (j *JSONLiteral) End() token.Pos {
 	return nodeEnd(wrapNode(j.Value))
 }
 
+func (n *NewConstructor) Pos() token.Pos {
+	return n.New
+}
+
+func (n *NewConstructor) End() token.Pos {
+	return posAdd(n.Rparen, 1)
+}
+
+func (b *BracedNewConstructor) Pos() token.Pos {
+	return b.New
+}
+
+func (b *BracedNewConstructor) End() token.Pos {
+	return nodeEnd(wrapNode(b.Body))
+}
+
+func (b *BracedConstructor) Pos() token.Pos {
+	return b.Lbrace
+}
+
+func (b *BracedConstructor) End() token.Pos {
+	return posAdd(b.Rbrace, 1)
+}
+
+func (b *BracedConstructorField) Pos() token.Pos {
+	return nodePos(wrapNode(b.Name))
+}
+
+func (b *BracedConstructorField) End() token.Pos {
+	return nodeEnd(wrapNode(b.Value))
+}
+
+func (b *BracedConstructorFieldValueExpr) Pos() token.Pos {
+	return b.Colon
+}
+
+func (b *BracedConstructorFieldValueExpr) End() token.Pos {
+	return nodeEnd(wrapNode(b.Expr))
+}
+
 func (s *SimpleType) Pos() token.Pos {
 	return s.NamePos
 }
@@ -734,6 +790,22 @@ func (o *OptionsDef) End() token.Pos {
 	return nodeEnd(wrapNode(o.Value))
 }
 
+func (c *CreateSchema) Pos() token.Pos {
+	return c.Create
+}
+
+func (c *CreateSchema) End() token.Pos {
+	return nodeEnd(wrapNode(c.Name))
+}
+
+func (d *DropSchema) Pos() token.Pos {
+	return d.Drop
+}
+
+func (d *DropSchema) End() token.Pos {
+	return nodeEnd(wrapNode(d.Name))
+}
+
 func (c *CreateDatabase) Pos() token.Pos {
 	return c.Create
 }
diff --git a/ast/sql.go b/ast/sql.go
index dd38f0b..2af3c5f 100644
--- a/ast/sql.go
+++ b/ast/sql.go
@@ -1,9 +1,10 @@
 package ast
 
 import (
-	"github.com/cloudspannerecosystem/memefish/token"
 	"strconv"
 	"strings"
+
+	"github.com/cloudspannerecosystem/memefish/token"
 )
 
 // ================================================================================
@@ -76,7 +77,7 @@ const (
 
 func exprPrec(e Expr) prec {
 	switch e := e.(type) {
-	case *CallExpr, *CountStarExpr, *CastExpr, *ExtractExpr, *CaseExpr, *ParenExpr, *ScalarSubQuery, *ArraySubQuery, *ExistsSubQuery, *Param, *Ident, *Path, *ArrayLiteral, *StructLiteral, *NullLiteral, *BoolLiteral, *IntLiteral, *FloatLiteral, *StringLiteral, *BytesLiteral, *DateLiteral, *TimestampLiteral, *NumericLiteral:
+	case *CallExpr, *CountStarExpr, *CastExpr, *ExtractExpr, *CaseExpr, *ParenExpr, *ScalarSubQuery, *ArraySubQuery, *ExistsSubQuery, *Param, *Ident, *Path, *ArrayLiteral, *TupleStructLiteral, *TypedStructLiteral, *TypelessStructLiteral, *NullLiteral, *BoolLiteral, *IntLiteral, *FloatLiteral, *StringLiteral, *BytesLiteral, *DateLiteral, *TimestampLiteral, *NumericLiteral:
 		return precLit
 	case *IndexExpr, *SelectorExpr:
 		return precSelector
@@ -231,7 +232,7 @@ func (a *Alias) SQL() string {
 }
 
 func (a *AsAlias) SQL() string {
-	return "AS " + a.Alias.SQL()
+	return strOpt(!a.As.Invalid(), "AS ") + a.Alias.SQL()
 }
 
 func (e *ExprSelectItem) SQL() string {
@@ -614,27 +615,16 @@ func (a *ArrayLiteral) SQL() string {
 		"[" + sqlJoin(a.Values, ", ") + "]"
 }
 
-func (s *StructLiteral) SQL() string {
-	sql := "STRUCT"
-	if s.Fields != nil {
-		sql += "<"
-		for i, f := range s.Fields {
-			if i != 0 {
-				sql += ", "
-			}
-			sql += f.SQL()
-		}
-		sql += ">"
-	}
-	sql += "("
-	for i, v := range s.Values {
-		if i != 0 {
-			sql += ", "
-		}
-		sql += v.SQL()
-	}
-	sql += ")"
-	return sql
+func (s *TupleStructLiteral) SQL() string {
+	return "(" + sqlJoin(s.Values, ", ") + ")"
+}
+
+func (s *TypedStructLiteral) SQL() string {
+	return "STRUCT<" + sqlJoin(s.Fields, ", ") + ">(" + sqlJoin(s.Values, ", ") + ")"
+}
+
+func (s *TypelessStructLiteral) SQL() string {
+	return strOpt(!s.Struct.Invalid(), "STRUCT") + "(" + sqlJoin(s.Values, ", ") + ")"
 }
 
 func (*NullLiteral) SQL() string {
@@ -681,6 +671,35 @@ func (t *JSONLiteral) SQL() string {
 	return "JSON " + t.Value.SQL()
 }
 
+// ================================================================================
+//
+// NEW constructors
+//
+// ================================================================================
+
+func (n *NewConstructor) SQL() string {
+	return "NEW " + n.Type.SQL() + "(" + sqlJoin(n.Args, ", ") + ")"
+}
+
+func (b *BracedNewConstructor) SQL() string {
+	return "NEW " + b.Type.SQL() + " " + b.Body.SQL()
+}
+
+func (b *BracedConstructor) SQL() string {
+	return "{" + sqlJoin(b.Fields, ", ") + "}"
+}
+
+func (b *BracedConstructorField) SQL() string {
+	if _, ok := b.Value.(*BracedConstructor); ok {
+		// Name {...}
+		return b.Name.SQL() + " " + b.Value.SQL()
+	}
+	// Name: value
+	return b.Name.SQL() + b.Value.SQL()
+}
+
+func (b *BracedConstructorFieldValueExpr) SQL() string { return ": " + b.Expr.SQL() }
+
 // ================================================================================
 //
 // Type
@@ -767,6 +786,10 @@ func (c *CreateDatabase) SQL() string {
 	return "CREATE DATABASE " + c.Name.SQL()
 }
 
+func (s *CreateSchema) SQL() string { return "CREATE SCHEMA " + s.Name.SQL() }
+
+func (s *DropSchema) SQL() string { return "DROP SCHEMA " + s.Name.SQL() }
+
 func (d *AlterDatabase) SQL() string {
 	return "ALTER DATABASE " + d.Name.SQL() + " SET " + d.Options.SQL()
 }
diff --git a/lexer.go b/lexer.go
index 26d679b..4b96e4b 100644
--- a/lexer.go
+++ b/lexer.go
@@ -109,7 +109,9 @@ func (l *Lexer) consumeToken() {
 	}
 
 	switch l.peek(0) {
-	case '(', ')', '{', '}', ';', ',', '[', ']', '~', '*', '/', '&', '^', '+', '-':
+	case '(', ')', '{', '}', ';', ',', '[', ']', '~', '*', '/', '&', '^', '%', ':',
+		// Belows are not yet used in Spanner.
+		'?', '\\', '$':
 		l.Token.Kind = token.TokenKind([]byte{l.skip()})
 		return
 	case '.':
@@ -151,6 +153,32 @@ func (l *Lexer) consumeToken() {
 			l.Token.Kind = ">"
 		}
 		return
+	case '+':
+		switch {
+		// KW_ADD_ASSIGN in ZetaSQL
+		case l.peekIs(1, '='):
+			l.skipN(2)
+			l.Token.Kind = "+="
+		default:
+			l.skip()
+			l.Token.Kind = "+"
+		}
+		return
+	case '-':
+		switch {
+		// KW_SUB_ASSIGN in ZetaSQL
+		case l.peekIs(1, '='):
+			l.skipN(2)
+			l.Token.Kind = "-="
+		// KW_LAMBDA_ARROW in ZetaSQL
+		case l.peekIs(1, '>'):
+			l.skipN(2)
+			l.Token.Kind = "->"
+		default:
+			l.skip()
+			l.Token.Kind = "-"
+		}
+		return
 	case '=':
 		switch {
 		case l.peekIs(1, '>'):
@@ -163,6 +191,9 @@ func (l *Lexer) consumeToken() {
 		return
 	case '|':
 		switch {
+		case l.peekIs(1, '>'):
+			l.skipN(2)
+			l.Token.Kind = "|>"
 		case l.peekIs(1, '|'):
 			l.skipN(2)
 			l.Token.Kind = "||"
@@ -177,7 +208,16 @@ func (l *Lexer) consumeToken() {
 			l.Token.Kind = "!="
 			return
 		}
+		l.skip()
+		l.Token.Kind = "!"
+		return
 	case '@':
+		// KW_DOUBLE_AT is not yet used in Cloud Spanner, but used in BigQuery.
+		if l.peekIs(1, '@') {
+			l.skipN(2)
+			l.Token.Kind = "@@"
+			return
+		}
 		if l.peekOk(1) && char.IsIdentStart(l.peek(1)) {
 			i := 1
 			for l.peekOk(i) && char.IsIdentPart(l.peek(i)) {
@@ -378,7 +418,7 @@ func (l *Lexer) consumeQuotedContent(q string, raw, unicode bool, name string) s
 	for l.peekOk(i) {
 		if l.slice(i, i+len(q)) == q {
 			if len(content) == 0 && name == "identifier" {
-				l.panicf("invalid empty identifier")
+				l.panicfAtPosition(token.Pos(l.pos), token.Pos(l.pos+i+len(q)), "invalid empty identifier")
 			}
 			l.skipN(i + len(q))
 			return string(content)
@@ -388,7 +428,7 @@ func (l *Lexer) consumeQuotedContent(q string, raw, unicode bool, name string) s
 		if c == '\\' {
 			i++
 			if !l.peekOk(i) {
-				l.panicf("invalid escape sequence: \\<eof>")
+				l.panicfAtPosition(token.Pos(l.pos+i-1), token.Pos(l.pos+i), "invalid escape sequence: \\<eof>")
 			}
 
 			c := l.peek(i)
@@ -417,18 +457,20 @@ func (l *Lexer) consumeQuotedContent(q string, raw, unicode bool, name string) s
 			case '\\', '?', '"', '\'', '`':
 				content = append(content, c)
 			case 'x', 'X':
-				if !(l.peekOk(i+1) && char.IsHexDigit(l.peek(i)) && char.IsHexDigit(l.peek(i+1))) {
-					l.panicf("invalid escape sequence: hex escape sequence must be follwed by 2 hex digits")
+				for j := 0; j < 2; j++ {
+					if !(l.peekOk(i+j) && char.IsHexDigit(l.peek(i+j))) {
+						l.panicfAtPosition(token.Pos(l.pos+i-2), token.Pos(l.pos+i+j+1), "invalid escape sequence: hex escape sequence must be follwed by 2 hex digits")
+					}
 				}
 				u, err := strconv.ParseUint(l.slice(i, i+2), 16, 8)
 				if err != nil {
-					l.panicf("invalid escape sequence: %v", err)
+					l.panicfAtPosition(token.Pos(l.pos+i-2), token.Pos(l.pos+i+2), "invalid escape sequence: %v", err)
 				}
 				content = append(content, byte(u))
 				i += 2
 			case 'u', 'U':
 				if !unicode {
-					l.panicf("invalid escape sequence: \\%c is not allowed in %s", c, name)
+					l.panicfAtPosition(token.Pos(l.pos+i-2), token.Pos(l.pos+i), "invalid escape sequence: \\%c is not allowed in %s", c, name)
 				}
 				size := 4
 				if c == 'U' {
@@ -436,46 +478,48 @@ func (l *Lexer) consumeQuotedContent(q string, raw, unicode bool, name string) s
 				}
 				for j := 0; j < size; j++ {
 					if !(l.peekOk(i+j) && char.IsHexDigit(l.peek(i+j))) {
-						l.panicf("invalid escape sequence: \\%c must be followed by %d hex digits", c, size)
+						l.panicfAtPosition(token.Pos(l.pos+i-2), token.Pos(l.pos+i+j+1), "invalid escape sequence: \\%c must be followed by %d hex digits", c, size)
 					}
 				}
 				u, err := strconv.ParseUint(l.slice(i, i+size), 16, 32)
 				if err != nil {
-					l.panicf("invalid escape sequence: %v", err)
+					l.panicfAtPosition(token.Pos(l.pos+i-2), token.Pos(l.pos+i+size), "invalid escape sequence: %v", err)
 				}
 				if 0xD800 <= u && u <= 0xDFFF || 0x10FFFF < u {
-					l.panicf("invalid escape sequence: invalid code point: U+%04X", u)
+					l.panicfAtPosition(token.Pos(l.pos+i-2), token.Pos(l.pos+i+size), "invalid escape sequence: invalid code point: U+%04X", u)
 				}
 				var buf [utf8.MaxRune]byte
 				n := utf8.EncodeRune(buf[:], rune(u))
 				content = append(content, buf[:n]...)
 				i += size
 			case '0', '1', '2', '3':
-				if !(l.peekOk(i+1) && char.IsOctalDigit(l.peek(i)) && char.IsOctalDigit(l.peek(i+1))) {
-					l.panicf("invalid escape sequence: octal escape sequence must be follwed by 3 octal digits")
+				for j := 0; j < 2; j++ {
+					if !(l.peekOk(i+j) && char.IsOctalDigit(l.peek(i+j))) {
+						l.panicfAtPosition(token.Pos(l.pos+i-2), token.Pos(l.pos+i+j+1), "invalid escape sequence: octal escape sequence must be follwed by 3 octal digits")
+					}
 				}
 				u, err := strconv.ParseUint(l.slice(i-1, i+2), 8, 8)
 				if err != nil {
-					l.panicf("invalid escape sequence: %v", err)
+					l.panicfAtPosition(token.Pos(l.pos+i-2), token.Pos(l.pos+i+2), "invalid escape sequence: %v", err)
 				}
 				content = append(content, byte(u))
 				i += 2
 			default:
-				l.panicf("invalid escape sequence: \\%c", c)
+				l.panicfAtPosition(token.Pos(l.pos+i-2), token.Pos(l.pos+i), "invalid escape sequence: \\%c", c)
 			}
 
 			continue
 		}
 
 		if c == '\n' && len(q) != 3 {
-			l.panicf("unclosed %s: newline appears in non triple-quoted", name)
+			l.panicfAtPosition(token.Pos(l.pos), token.Pos(l.pos+i+1), "unclosed %s: newline appears in non triple-quoted", name)
 		}
 
 		content = append(content, c)
 		i++
 	}
 
-	panic(l.errorf("unclosed %s", name))
+	panic(l.errorfAtPosition(token.Pos(l.pos), token.Pos(l.pos+i), "unclosed %s", name))
 }
 
 func (l *Lexer) skipSpaces() {
@@ -503,6 +547,7 @@ func (l *Lexer) skipComment() {
 }
 
 func (l *Lexer) skipCommentUntil(end string, mustEnd bool) {
+	pos := token.Pos(l.pos)
 	for !l.eof() {
 		if l.slice(0, len(end)) == end {
 			l.skipN(len(end))
@@ -511,8 +556,7 @@ func (l *Lexer) skipCommentUntil(end string, mustEnd bool) {
 		l.skip()
 	}
 	if mustEnd {
-		// TODO: improve error position
-		l.panicf("unclosed comment")
+		l.panicfAtPosition(pos, token.Pos(l.pos), "unclosed comment")
 	}
 }
 
@@ -556,6 +600,17 @@ func (l *Lexer) errorf(msg string, param ...interface{}) *Error {
 	}
 }
 
+func (l *Lexer) errorfAtPosition(pos, end token.Pos, msg string, param ...interface{}) *Error {
+	return &Error{
+		Message:  fmt.Sprintf(msg, param...),
+		Position: l.Position(pos, end),
+	}
+}
+
 func (l *Lexer) panicf(msg string, param ...interface{}) {
 	panic(l.errorf(msg, param...))
 }
+
+func (l *Lexer) panicfAtPosition(pos, end token.Pos, msg string, param ...interface{}) {
+	panic(l.errorfAtPosition(pos, end, msg, param...))
+}
diff --git a/parser.go b/parser.go
index 0d00485..9a3a5c9 100644
--- a/parser.go
+++ b/parser.go
@@ -458,7 +458,7 @@ func (p *Parser) parseSelectItem() ast.SelectItem {
 	}
 
 	expr := p.parseExpr()
-	if as := p.tryParseAsAlias(); as != nil {
+	if as := p.tryParseAsAlias(withOptionalAs); as != nil {
 		return &ast.Alias{
 			Expr: expr,
 			As:   as,
@@ -479,22 +479,33 @@ func (p *Parser) parseSelectItem() ast.SelectItem {
 	}
 }
 
-func (p *Parser) tryParseAsAlias() *ast.AsAlias {
+type withAs bool
+
+const (
+	withRequiredAs withAs = true
+	withOptionalAs withAs = false
+)
+
+func (p *Parser) tryParseAsAlias(requiredAs withAs) *ast.AsAlias {
 	pos := p.Token.Pos
 
 	if p.Token.Kind == "AS" {
 		p.nextToken()
 		id := p.parseIdent()
 		return &ast.AsAlias{
-			As:    token.InvalidPos,
+			As:    pos,
 			Alias: id,
 		}
 	}
 
+	if requiredAs {
+		return nil
+	}
+
 	if p.Token.Kind == token.TokenIdent {
 		id := p.parseIdent()
 		return &ast.AsAlias{
-			As:    pos,
+			As:    token.InvalidPos,
 			Alias: id,
 		}
 	}
@@ -777,7 +788,7 @@ func (p *Parser) parseSimpleTableExpr() ast.TableExpr {
 		lparen := p.expect("(").Pos
 		query := p.parseQueryExpr()
 		rparen := p.expect(")").Pos
-		as := p.tryParseAsAlias()
+		as := p.tryParseAsAlias(withOptionalAs)
 		return p.parseTableExprSuffix(&ast.SubQueryTableExpr{
 			Lparen: lparen,
 			Rparen: rparen,
@@ -804,7 +815,7 @@ func (p *Parser) parseSimpleTableExpr() ast.TableExpr {
 		p.expect("(")
 		expr := p.parseExpr()
 		rparen := p.expect(")").Pos
-		return p.parseUnnestSuffix(false, expr, unnest, rparen)
+		return p.parseUnnestSuffix(expr, unnest, rparen)
 	}
 
 	if p.Token.Kind == token.TokenIdent {
@@ -827,9 +838,9 @@ func (p *Parser) parseIdentOrPath() []*ast.Ident {
 	return ids
 }
 
-func (p *Parser) parseUnnestSuffix(implicit bool, expr ast.Expr, unnest, rparen token.Pos) ast.TableExpr {
+func (p *Parser) parseUnnestSuffix(expr ast.Expr, unnest, rparen token.Pos) ast.TableExpr {
 	hint := p.tryParseHint()
-	as := p.tryParseAsAlias()
+	as := p.tryParseAsAlias(withOptionalAs)
 	withOffset := p.tryParseWithOffset()
 
 	return p.parseTableExprSuffix(&ast.Unnest{
@@ -849,7 +860,7 @@ func (p *Parser) tryParseWithOffset() *ast.WithOffset {
 
 	with := p.expect("WITH").Pos
 	offset := p.expectKeywordLike("OFFSET").Pos
-	as := p.tryParseAsAlias()
+	as := p.tryParseAsAlias(withOptionalAs)
 
 	return &ast.WithOffset{
 		With:   with,
@@ -860,7 +871,7 @@ func (p *Parser) tryParseWithOffset() *ast.WithOffset {
 
 func (p *Parser) parseTableNameSuffix(id *ast.Ident) ast.TableExpr {
 	hint := p.tryParseHint()
-	as := p.tryParseAsAlias()
+	as := p.tryParseAsAlias(withOptionalAs)
 	return p.parseTableExprSuffix(&ast.TableName{
 		Table: id,
 		Hint:  hint,
@@ -870,7 +881,7 @@ func (p *Parser) parseTableNameSuffix(id *ast.Ident) ast.TableExpr {
 
 func (p *Parser) parsePathTableExprSuffix(id *ast.Path) ast.TableExpr {
 	hint := p.tryParseHint()
-	as := p.tryParseAsAlias()
+	as := p.tryParseAsAlias(withOptionalAs)
 	withOffset := p.tryParseWithOffset()
 	return p.parseTableExprSuffix(&ast.PathTableExpr{
 		Path:       id,
@@ -1427,6 +1438,11 @@ func (p *Parser) parseLit() ast.Expr {
 		return p.parseSimpleArrayLiteral()
 	case "(":
 		return p.parseParenExpr()
+	case "NEW":
+		return p.parseNewConstructors()
+	// In parser level, it is a valid ast.Expr, but it is semantically valid only in ast.BracedConstructorFieldExpr.
+	case "{":
+		return p.parseBracedConstructor()
 	case token.TokenIdent:
 		id := p.Token
 		switch {
@@ -1798,16 +1814,15 @@ func (p *Parser) parseParenExpr() ast.Expr {
 	}
 
 	if p.Token.Kind != "," {
-		p.panicfAtToken(&paren, "cannot parse (...) as expression, struct literal or subquery")
+		p.panicfAtToken(&paren, "cannot parse (...) as expression, tuple struct literal or subquery")
 	}
 
-	values := []ast.Expr{expr}
-	for p.Token.Kind == "," {
-		p.nextToken()
-		values = append(values, p.parseExpr())
-	}
+	p.expect(",")
+
+	values := append([]ast.Expr{expr}, parseCommaSeparatedList(p, p.parseExpr)...)
 	rparen := p.expect(")").Pos
-	return &ast.StructLiteral{
+
+	return &ast.TupleStructLiteral{
 		Lparen: paren.Pos,
 		Rparen: rparen,
 		Values: values,
@@ -1870,30 +1885,68 @@ func (p *Parser) parseArrayLiteralBody() (values []ast.Expr, lbrack, rbrack toke
 	return
 }
 
-func (p *Parser) parseStructLiteral() *ast.StructLiteral {
+func (p *Parser) parseStructLiteral() ast.Expr {
+	// Note that tuple struct syntax is handled in parseParenExpr.
+
 	pos := p.expect("STRUCT").Pos
+	if p.Token.Kind == "<" || p.Token.Kind == "<>" {
+		return p.parseTypedStructLiteral(pos)
+	}
+
+	if p.Token.Kind != "(" {
+		p.panicfAtToken(&p.Token, "expected token: <, <>, ( but: %s", p.Token.Kind)
+	}
+
+	return p.parseTypelessStructLiteral(pos)
+}
+
+func (p *Parser) parseTypedStructLiteral(pos token.Pos) *ast.TypedStructLiteral {
 	fields, _ := p.parseStructTypeFields()
-	lparen := p.expect("(").Pos
+
+	p.expect("(")
 	var values []ast.Expr
 	if p.Token.Kind != ")" {
-		for p.Token.Kind != token.TokenEOF {
-			values = append(values, p.parseExpr())
-			if p.Token.Kind != "," {
-				break
-			}
-			p.nextToken()
-		}
+		values = parseCommaSeparatedList(p, p.parseExpr)
 	}
 	rparen := p.expect(")").Pos
-	return &ast.StructLiteral{
+
+	return &ast.TypedStructLiteral{
 		Struct: pos,
-		Lparen: lparen,
 		Rparen: rparen,
 		Fields: fields,
 		Values: values,
 	}
 }
 
+func (p *Parser) parseTypelessStructLiteral(pos token.Pos) *ast.TypelessStructLiteral {
+	p.expect("(")
+	var values []ast.TypelessStructLiteralArg
+	if p.Token.Kind != ")" {
+		values = parseCommaSeparatedList(p, p.parseTypelessStructLiteralArg)
+	}
+	rparen := p.expect(")").Pos
+
+	return &ast.TypelessStructLiteral{
+		Struct: pos,
+		Rparen: rparen,
+		Values: values,
+	}
+}
+
+func (p *Parser) parseTypelessStructLiteralArg() ast.TypelessStructLiteralArg {
+	e := p.parseExpr()
+	as := p.tryParseAsAlias(withRequiredAs)
+	if as != nil {
+		return &ast.Alias{
+			Expr: e,
+			As:   as,
+		}
+	}
+	return &ast.ExprArg{
+		Expr: e,
+	}
+}
+
 func (p *Parser) parseDateLiteral(id token.Token) *ast.DateLiteral {
 	s := p.parseStringLiteral()
 	return &ast.DateLiteral{
@@ -2055,10 +2108,10 @@ func (p *Parser) parseArrayType() *ast.ArrayType {
 
 func (p *Parser) parseStructType() *ast.StructType {
 	pos := p.expect("STRUCT").Pos
-	fields, gt := p.parseStructTypeFields()
-	if fields == nil {
+	if p.Token.Kind != "<" && p.Token.Kind != "<>" {
 		p.panicfAtToken(&p.Token, "expected token: <, <>, but: %s", p.Token.Kind)
 	}
+	fields, gt := p.parseStructTypeFields()
 	return &ast.StructType{
 		Struct: pos,
 		Gt:     gt,
@@ -2066,28 +2119,20 @@ func (p *Parser) parseStructType() *ast.StructType {
 	}
 }
 
-func (p *Parser) parseStructTypeFields() (fields []*ast.StructField, gt token.Pos) {
-	if p.Token.Kind != "<" && p.Token.Kind != "<>" {
-		return
-	}
-
-	fields = make([]*ast.StructField, 0)
+func (p *Parser) parseStructTypeFields() ([]*ast.StructField, token.Pos) {
 	if p.Token.Kind == "<>" {
-		gt = p.expect("<>").Pos + 1
-		return
+		gt := p.Token.Pos + 1
+		p.nextToken()
+		return nil, gt
 	}
 
+	var fields []*ast.StructField
 	p.expect("<")
 	if p.Token.Kind != ">" && p.Token.Kind != ">>" {
-		for p.Token.Kind != token.TokenEOF {
-			fields = append(fields, p.parseFieldType())
-			if p.Token.Kind != "," {
-				break
-			}
-			p.nextToken()
-		}
+		fields = parseCommaSeparatedList(p, p.parseFieldType)
 	}
 
+	var gt token.Pos
 	if p.Token.Kind == ">>" {
 		p.Token.Kind = ">"
 		p.Token.Raw = ">"
@@ -2096,7 +2141,111 @@ func (p *Parser) parseStructTypeFields() (fields []*ast.StructField, gt token.Po
 	} else {
 		gt = p.expect(">").Pos
 	}
-	return
+
+	return fields, gt
+}
+
+func (p *Parser) parseNewConstructor(newPos token.Pos, namedType *ast.NamedType) *ast.NewConstructor {
+	p.expect("(")
+
+	// Args can be empty like `NEW pkg.TypeName ()`.
+	var args []ast.NewConstructorArg
+	if p.Token.Kind != ")" {
+		args = parseCommaSeparatedList(p, p.parseNewConstructorArg)
+	}
+
+	rparen := p.expect(")").Pos
+	return &ast.NewConstructor{
+		New:    newPos,
+		Type:   namedType,
+		Args:   args,
+		Rparen: rparen,
+	}
+}
+
+func (p *Parser) parseNewConstructorArg() ast.NewConstructorArg {
+	// Currently, this method's contents are the same as `parseTypelessStructLiteralArg`.
+	// It exists as an individual method for future extensibility.
+
+	e := p.parseExpr()
+	as := p.tryParseAsAlias(withRequiredAs)
+	if as != nil {
+		return &ast.Alias{
+			Expr: e,
+			As:   as,
+		}
+	}
+	return &ast.ExprArg{
+		Expr: e,
+	}
+}
+
+func (p *Parser) parseBracedNewConstructorField() *ast.BracedConstructorField {
+	name := p.parseIdent()
+	var fieldValue ast.BracedConstructorFieldValue
+	switch p.Token.Kind {
+	case ":":
+		colon := p.expect(":").Pos
+		expr := p.parseExpr()
+		fieldValue = &ast.BracedConstructorFieldValueExpr{Colon: colon, Expr: expr}
+	case "{":
+		fieldValue = p.parseBracedConstructor()
+	}
+	return &ast.BracedConstructorField{Name: name, Value: fieldValue}
+}
+
+func (p *Parser) parseBracedConstructor() *ast.BracedConstructor {
+	lbrace := p.expect("{").Pos
+
+	// Braced constructor permits empty.
+	var fields []*ast.BracedConstructorField
+	for {
+		if p.Token.Kind == "}" {
+			break
+		}
+
+		if p.Token.Kind != token.TokenIdent {
+			p.panicfAtToken(&p.Token, "expect <ident>, but %v", p.Token.Kind)
+		}
+		fields = append(fields, p.parseBracedNewConstructorField())
+
+		// It is an optional comma.
+		if p.Token.Kind == "," {
+			p.nextToken()
+		}
+	}
+
+	rbrace := p.expect("}").Pos
+
+	return &ast.BracedConstructor{
+		Lbrace: lbrace,
+		Rbrace: rbrace,
+		Fields: fields,
+	}
+}
+
+func (p *Parser) parseBracedNewConstructor(newPos token.Pos, namedType *ast.NamedType) *ast.BracedNewConstructor {
+	body := p.parseBracedConstructor()
+	return &ast.BracedNewConstructor{
+		New:  newPos,
+		Type: namedType,
+		Body: body,
+	}
+}
+
+func (p *Parser) parseNewConstructors() ast.Expr {
+	newPos := p.expect("NEW").Pos
+	namedType := p.parseNamedType()
+
+	switch p.Token.Kind {
+	case "(":
+		return p.parseNewConstructor(newPos, namedType)
+	case "{":
+		return p.parseBracedNewConstructor(newPos, namedType)
+	default:
+		p.panicfAtToken(&p.Token, `expect '{' or '(', but %v`, p.Token.Kind)
+	}
+	return nil
 }
 
 func (p *Parser) parseFieldType() *ast.StructField {
@@ -2150,6 +2299,8 @@ func (p *Parser) parseDDL() ast.DDL {
 	case p.Token.Kind == "CREATE":
 		p.nextToken()
 		switch {
+		case p.Token.IsKeywordLike("SCHEMA"):
+			return p.parseCreateSchema(pos)
 		case p.Token.IsKeywordLike("DATABASE"):
 			return p.parseCreateDatabase(pos)
 		case p.Token.IsKeywordLike("TABLE"):
@@ -2192,6 +2343,8 @@ func (p *Parser) parseDDL() ast.DDL {
 	case p.Token.IsKeywordLike("DROP"):
 		p.nextToken()
 		switch {
+		case p.Token.IsKeywordLike("SCHEMA"):
+			return p.parseDropSchema(pos)
 		case p.Token.IsKeywordLike("TABLE"):
 			return p.parseDropTable(pos)
 		case p.Token.IsKeywordLike("INDEX"):
@@ -2228,6 +2381,24 @@ func (p *Parser) parseDDL() ast.DDL {
 	panic(p.errorfAtToken(&p.Token, "expected pseudo keyword: ALTER, DROP, but: %s", p.Token.AsString))
 }
 
+func (p *Parser) parseCreateSchema(pos token.Pos) *ast.CreateSchema {
+	p.expectKeywordLike("SCHEMA")
+	name := p.parseIdent()
+	return &ast.CreateSchema{
+		Create: pos,
+		Name:   name,
+	}
+}
+
+func (p *Parser) parseDropSchema(pos token.Pos) *ast.DropSchema {
+	p.expectKeywordLike("SCHEMA")
+	name := p.parseIdent()
+	return &ast.DropSchema{
+		Drop: pos,
+		Name: name,
+	}
+}
+
 func (p *Parser) parseCreateDatabase(pos token.Pos) *ast.CreateDatabase {
 	p.expectKeywordLike("DATABASE")
 	name := p.parseIdent()
@@ -3720,7 +3891,7 @@ func (p *Parser) parseDelete(pos token.Pos) *ast.Delete {
 	}
 
 	name := p.parseIdent()
-	as := p.tryParseAsAlias()
+	as := p.tryParseAsAlias(withOptionalAs)
 	where := p.parseWhere()
 
 	return &ast.Delete{
@@ -3733,7 +3904,7 @@ func (p *Parser) parseDelete(pos token.Pos) *ast.Delete {
 
 func (p *Parser) parseUpdate(pos token.Pos) *ast.Update {
 	name := p.parseIdent()
-	as := p.tryParseAsAlias()
+	as := p.tryParseAsAlias(withOptionalAs)
 
 	p.expect("SET")
 
diff --git a/tools/gen-ast-pos/main.go b/tools/gen-ast-pos/main.go
index 3bfa6f4..1041e3c 100644
--- a/tools/gen-ast-pos/main.go
+++ b/tools/gen-ast-pos/main.go
@@ -102,6 +102,9 @@ func main() {
 
 	for _, node := range nodes {
 		x := string(unicode.ToLower(rune(node.name[0])))
+		if node.posExpr == nil || node.endExpr == nil {
+			log.Fatalf("pos/end is not defined: node %s", node.name)
+		}
 
 		fmt.Fprintln(&buffer)
 		fmt.Fprintf(&buffer, "func (%s *%s) Pos() token.Pos {\n", x, node.name)
diff --git a/tools/parse/main.go b/tools/parse/main.go
index 1cb36bf..17d4a6a 100644
--- a/tools/parse/main.go
+++ b/tools/parse/main.go
@@ -51,7 +51,7 @@ func main() {
 	}
 
 	flag.Parse()
-	if flag.NArg() < 1 {
+	if flag.NArg() != 1 {
 		flag.Usage()
 		os.Exit(1)
 		return


Test patch:
diff --git a/ast/ast_test.go b/ast/ast_test.go
index 338cd95..cc8ee6d 100644
--- a/ast/ast_test.go
+++ b/ast/ast_test.go
@@ -87,7 +87,7 @@ func TestExpr(t *testing.T) {
 	Expr(&Ident{}).isExpr()
 	Expr(&Path{}).isExpr()
 	Expr(&ArrayLiteral{}).isExpr()
-	Expr(&StructLiteral{}).isExpr()
+	Expr(&TypedStructLiteral{}).isExpr()
 	Expr(&NullLiteral{}).isExpr()
 	Expr(&BoolLiteral{}).isExpr()
 	Expr(&IntLiteral{}).isExpr()
diff --git a/lexer_test.go b/lexer_test.go
index 4919c0d..7f9fc38 100644
--- a/lexer_test.go
+++ b/lexer_test.go
@@ -11,35 +11,48 @@ import (
 	. "github.com/cloudspannerecosystem/memefish/token"
 )
 
+// Keep same order https://github.com/google/zetasql/blob/master/zetasql/parser/flex_tokenizer.l
 var symbols = []string{
-	".",
-	",",
-	";",
 	"(",
-	")",
-	"{",
-	"}",
 	"[",
+	"{",
+	")",
 	"]",
-	"@",
-	"~",
-	"+",
-	"-",
+	"}",
 	"*",
-	"/",
-	"&",
-	"^",
-	"|",
-	"||",
+	",",
 	"=",
-	"<",
-	"<<",
+	"+=",
+	"-=",
+	"!=",
 	"<=",
-	"<>",
+	"<<",
+	"=>",
+	"->",
+	"<",
 	">",
-	">>",
 	">=",
-	"!=",
+	"||",
+	"|",
+	"^",
+	"&",
+	"+",
+	"-",
+	"/",
+	"~",
+	"?",
+	"!",
+	"%",
+	"|>",
+	"@",
+	"@@",
+	".",
+	":",
+	"\\",
+	";",
+	"$",
+	"<>", // <> is not a valid token in ZetaSQL, but it is a token in memefish
+	">>", // >> is not a valid token in ZetaSQL, but it is a token in memefish.
 }
 
 var lexerTestCases = []struct {
@@ -130,26 +143,27 @@ var lexerTestCases = []struct {
 var lexerWrongTestCase = []struct {
 	source  string
 	pos     Pos
+	end     Pos
 	message string
 }{
-	{"?", 0, "illegal input character: '?'"},
-	{`"foo`, 0, "unclosed string literal"},
-	{`R"foo`, 1, "unclosed raw string literal"},
-	{"'foo\n", 0, "unclosed string literal: newline appears in non triple-quoted"},
-	{"R'foo\n", 1, "unclosed raw string literal: newline appears in non triple-quoted"},
-	{"R'foo\\", 1, "invalid escape sequence: \\<eof>"},
-	{`"\400"`, 0, "invalid escape sequence: \\4"},
-	{`"\3xx"`, 0, "invalid escape sequence: octal escape sequence must be follwed by 3 octal digits"},
-	{`"\xZZ"`, 0, "invalid escape sequence: hex escape sequence must be follwed by 2 hex digits"},
-	{`"\XZZ"`, 0, "invalid escape sequence: hex escape sequence must be follwed by 2 hex digits"},
-	{`B"\u0031"`, 1, "invalid escape sequence: \\u is not allowed in bytes literal"},
-	{`B"\U00000031"`, 1, "invalid escape sequence: \\U is not allowed in bytes literal"},
-	{`B"\U00000031"`, 1, "invalid escape sequence: \\U is not allowed in bytes literal"},
-	{`"\UFFFFFFFF"`, 0, "invalid escape sequence: invalid code point: U+FFFFFFFF"},
-	{"``", 0, "invalid empty identifier"},
-	{"1from", 1, "number literal cannot follow identifier without any spaces"},
-	{`'''0`, 0, "unclosed triple-quoted string literal"},
-	{`/*`, 2, "unclosed comment"},
+	{"\b", 0, 0, "illegal input character: '\\b'"},
+	{`"foo`, 0, 4, "unclosed string literal"},
+	{`R"foo`, 1, 5, "unclosed raw string literal"},
+	{"'foo\n", 0, 5, "unclosed string literal: newline appears in non triple-quoted"},
+	{"R'foo\n", 1, 6, "unclosed raw string literal: newline appears in non triple-quoted"},
+	{"R'foo\\", 5, 6, "invalid escape sequence: \\<eof>"},
+	{`"\400"`, 1, 3, "invalid escape sequence: \\4"},
+	{`"\3xx"`, 1, 4, "invalid escape sequence: octal escape sequence must be follwed by 3 octal digits"},
+	{`"\xZZ"`, 1, 4, "invalid escape sequence: hex escape sequence must be follwed by 2 hex digits"},
+	{`"\XZZ"`, 1, 4, "invalid escape sequence: hex escape sequence must be follwed by 2 hex digits"},
+	{`B"\u0031"`, 2, 4, "invalid escape sequence: \\u is not allowed in bytes literal"},
+	{`B"\U00000031"`, 2, 4, "invalid escape sequence: \\U is not allowed in bytes literal"},
+	{`B"\U00000031"`, 2, 4, "invalid escape sequence: \\U is not allowed in bytes literal"},
+	{`"\UFFFFFFFF"`, 1, 11, "invalid escape sequence: invalid code point: U+FFFFFFFF"},
+	{"``", 0, 2, "invalid empty identifier"},
+	{"1from", 1, 1, "number literal cannot follow identifier without any spaces"},
+	{`'''0`, 0, 4, "unclosed triple-quoted string literal"},
+	{`/*`, 0, 2, "unclosed comment"},
 }
 
 func testLexer(t *testing.T, source string, tokens []*Token) {
@@ -227,7 +241,10 @@ func TestLexerWrong(t *testing.T) {
 					t.Errorf("expected error message: %q, but: %q", tc.message, e.Message)
 				}
 				if e.Position.Pos != tc.pos {
-					t.Errorf("expected error position: %v, but: %v", tc.pos, e.Position.Pos)
+					t.Errorf("expected error position (pos): %v, but: %v", tc.pos, e.Position.Pos)
+				}
+				if e.Position.End != tc.end {
+					t.Errorf("expected error position (end): %v, but: %v", tc.end, e.Position.End)
 				}
 			} else {
 				t.Errorf("unexpected error: %v", err)
diff --git a/split_test.go b/split_test.go
index 14d838f..0e7533b 100644
--- a/split_test.go
+++ b/split_test.go
@@ -69,8 +69,6 @@ func TestSplitRawStatements(t *testing.T) {
 			want: []*memefish.RawStatement{
 				{Statement: "SELECT `1;2;3`", End: token.Pos(14)},
 			}},
-		// $` may become a valid token in the future, but it's reasonable to check its current behavior.
-		{desc: "unknown token", input: "SELECT $;", errRe: regexp.MustCompile(`illegal input character: '\$'`)},
 	} {
 		t.Run(test.desc, func(t *testing.T) {
 			stmts, err := memefish.SplitRawStatements("", test.input)
diff --git a/testdata/input/ddl/create_schema.sql b/testdata/input/ddl/create_schema.sql
new file mode 100644
index 0000000..f2c2fc0
--- /dev/null
+++ b/testdata/input/ddl/create_schema.sql
@@ -0,0 +1,1 @@
+CREATE SCHEMA sch1
\ No newline at end of file
diff --git a/testdata/input/ddl/drop_schema.sql b/testdata/input/ddl/drop_schema.sql
new file mode 100644
index 0000000..1c2cdba
--- /dev/null
+++ b/testdata/input/ddl/drop_schema.sql
@@ -0,0 +1,1 @@
+DROP SCHEMA sch1
\ No newline at end of file
diff --git a/testdata/input/expr/braced_new_constructor.sql b/testdata/input/expr/braced_new_constructor.sql
new file mode 100644
index 0000000..28cf679
--- /dev/null
+++ b/testdata/input/expr/braced_new_constructor.sql
@@ -0,0 +1,17 @@
+-- Example from https://cloud.google.com/spanner/docs/reference/standard-sql/operators#new_operator
+NEW Universe {
+  name: "Sol"
+  closest_planets: ["Mercury", "Venus", "Earth" ]
+  star {
+    radius_miles: 432690
+    age: 4603000000
+  }
+  constellations: [{
+    name: "Libra"
+    index: 0
+  }, {
+    name: "Scorpio"
+    index: 1
+  }]
+  all_planets: (SELECT planets FROM SolTable)
+}
\ No newline at end of file
diff --git a/testdata/input/expr/braced_new_constructor_empty.sql b/testdata/input/expr/braced_new_constructor_empty.sql
new file mode 100644
index 0000000..d3d9910
--- /dev/null
+++ b/testdata/input/expr/braced_new_constructor_empty.sql
@@ -0,0 +1,1 @@
+NEW Universe {}
\ No newline at end of file
diff --git a/testdata/input/expr/braced_new_constructor_oneliner.sql b/testdata/input/expr/braced_new_constructor_oneliner.sql
new file mode 100644
index 0000000..a390284
--- /dev/null
+++ b/testdata/input/expr/braced_new_constructor_oneliner.sql
@@ -0,0 +1,1 @@
+NEW Universe {name: "Sol", closest_planets: ["Mercury", "Venus", "Earth" ]}
\ No newline at end of file
diff --git a/testdata/input/expr/new_constructor.sql b/testdata/input/expr/new_constructor.sql
new file mode 100644
index 0000000..c1c8f9e
--- /dev/null
+++ b/testdata/input/expr/new_constructor.sql
@@ -0,0 +1,1 @@
+NEW googlesql.examples.music.Chart(key AS rank, name AS chart_name)
\ No newline at end of file
diff --git a/testdata/input/expr/new_constructor_empty.sql b/testdata/input/expr/new_constructor_empty.sql
new file mode 100644
index 0000000..87bd24b
--- /dev/null
+++ b/testdata/input/expr/new_constructor_empty.sql
@@ -0,0 +1,1 @@
+NEW googlesql.examples.music.Chart()
diff --git a/testdata/input/query/select_literals_struct.sql b/testdata/input/query/select_literals_struct.sql
index d939f98..bd44775 100644
--- a/testdata/input/query/select_literals_struct.sql
+++ b/testdata/input/query/select_literals_struct.sql
@@ -2,8 +2,15 @@ SELECT
   ARRAY(SELECT STRUCT(1, 2, 3)),
   ARRAY(SELECT STRUCT(1, 2, TRUE)),
   ARRAY(SELECT STRUCT<X INT64, Y INT64, Z INT64>(1, 2, 3)),
+  ARRAY(SELECT STRUCT(1 AS X, 2 AS Y, 3 AS Z)),
   ARRAY(SELECT STRUCT<INT64, INT64, INT64>(1, 2, 3)),
   ARRAY(SELECT STRUCT<X INT64, INT64, INT64>(1, 2, 3)),
+  ARRAY(SELECT STRUCT(1 AS X, 2, 3)),
   ARRAY(SELECT (1, 2, 3)),
+  ARRAY(SELECT STRUCT<X INT64>(1)),
+  ARRAY(SELECT STRUCT(1 AS X)),
+  ARRAY(SELECT STRUCT<INT64>(1)),
+  ARRAY(SELECT STRUCT(1)),
   ARRAY(SELECT STRUCT<>()),
+  ARRAY(SELECT STRUCT()),
   EXISTS(SELECT (1, 2))
diff --git a/testdata/result/ddl/create_or_replace_view.sql.txt b/testdata/result/ddl/create_or_replace_view.sql.txt
index 4a611ad..f3b3880 100644
--- a/testdata/result/ddl/create_or_replace_view.sql.txt
+++ b/testdata/result/ddl/create_or_replace_view.sql.txt
@@ -37,7 +37,7 @@ from singers
           },
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    87,
           Alias: &ast.Ident{
             NamePos: 90,
             NameEnd: 98,
@@ -86,7 +86,7 @@ from singers
           },
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    149,
           Alias: &ast.Ident{
             NamePos: 152,
             NameEnd: 156,
diff --git a/testdata/result/ddl/create_schema.sql.txt b/testdata/result/ddl/create_schema.sql.txt
new file mode 100644
index 0000000..2b04f8f
--- /dev/null
+++ b/testdata/result/ddl/create_schema.sql.txt
@@ -0,0 +1,14 @@
+--- create_schema.sql
+CREATE SCHEMA sch1
+--- AST
+&ast.CreateSchema{
+  Create: 0,
+  Name:   &ast.Ident{
+    NamePos: 14,
+    NameEnd: 18,
+    Name:    "sch1",
+  },
+}
+
+--- SQL
+CREATE SCHEMA sch1
diff --git a/testdata/result/ddl/create_view.sql.txt b/testdata/result/ddl/create_view.sql.txt
index da8ea65..e96ff6c 100644
--- a/testdata/result/ddl/create_view.sql.txt
+++ b/testdata/result/ddl/create_view.sql.txt
@@ -37,7 +37,7 @@ from singers
           },
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    76,
           Alias: &ast.Ident{
             NamePos: 79,
             NameEnd: 87,
@@ -86,7 +86,7 @@ from singers
           },
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    138,
           Alias: &ast.Ident{
             NamePos: 141,
             NameEnd: 145,
diff --git a/testdata/result/ddl/create_view_sql_security_definer.sql.txt b/testdata/result/ddl/create_view_sql_security_definer.sql.txt
index 5627d9c..6d134f8 100644
--- a/testdata/result/ddl/create_view_sql_security_definer.sql.txt
+++ b/testdata/result/ddl/create_view_sql_security_definer.sql.txt
@@ -37,7 +37,7 @@ from singers
           },
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    76,
           Alias: &ast.Ident{
             NamePos: 79,
             NameEnd: 87,
@@ -86,7 +86,7 @@ from singers
           },
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    138,
           Alias: &ast.Ident{
             NamePos: 141,
             NameEnd: 145,
diff --git a/testdata/result/ddl/drop_schema.sql.txt b/testdata/result/ddl/drop_schema.sql.txt
new file mode 100644
index 0000000..b935680
--- /dev/null
+++ b/testdata/result/ddl/drop_schema.sql.txt
@@ -0,0 +1,14 @@
+--- drop_schema.sql
+DROP SCHEMA sch1
+--- AST
+&ast.DropSchema{
+  Drop: 0,
+  Name: &ast.Ident{
+    NamePos: 12,
+    NameEnd: 16,
+    Name:    "sch1",
+  },
+}
+
+--- SQL
+DROP SCHEMA sch1
diff --git a/testdata/result/dml/delete_as.sql.txt b/testdata/result/dml/delete_as.sql.txt
index d368be5..2eee871 100644
--- a/testdata/result/dml/delete_as.sql.txt
+++ b/testdata/result/dml/delete_as.sql.txt
@@ -9,7 +9,7 @@ delete foo as F where F.foo = 1
     Name:    "foo",
   },
   As: &ast.AsAlias{
-    As:    -1,
+    As:    11,
     Alias: &ast.Ident{
       NamePos: 14,
       NameEnd: 15,
diff --git a/testdata/result/dml/insert_select.sql.txt b/testdata/result/dml/insert_select.sql.txt
index cc71ca1..6afe8c7 100644
--- a/testdata/result/dml/insert_select.sql.txt
+++ b/testdata/result/dml/insert_select.sql.txt
@@ -43,11 +43,9 @@ select * from unnest([(1, 2), (3, 4)])
             Rbrack: 58,
             Type:   nil,
             Values: []ast.Expr{
-              &ast.StructLiteral{
-                Struct: 0,
+              &ast.TupleStructLiteral{
                 Lparen: 44,
                 Rparen: 49,
-                Fields: []*ast.StructField(nil),
                 Values: []ast.Expr{
                   &ast.IntLiteral{
                     ValuePos: 45,
@@ -63,11 +61,9 @@ select * from unnest([(1, 2), (3, 4)])
                   },
                 },
               },
-              &ast.StructLiteral{
-                Struct: 0,
+              &ast.TupleStructLiteral{
                 Lparen: 52,
                 Rparen: 57,
-                Fields: []*ast.StructField(nil),
                 Values: []ast.Expr{
                   &ast.IntLiteral{
                     ValuePos: 53,
@@ -101,4 +97,4 @@ select * from unnest([(1, 2), (3, 4)])
 }
 
 --- SQL
-INSERT INTO foo (foo, bar) SELECT * FROM UNNEST([STRUCT(1, 2), STRUCT(3, 4)])
+INSERT INTO foo (foo, bar) SELECT * FROM UNNEST([(1, 2), (3, 4)])
diff --git a/testdata/result/dml/update_as.sql.txt b/testdata/result/dml/update_as.sql.txt
index 6f269af..a647ea8 100644
--- a/testdata/result/dml/update_as.sql.txt
+++ b/testdata/result/dml/update_as.sql.txt
@@ -9,7 +9,7 @@ update foo as F set F.foo = F.bar + 1 where foo = F.bar
     Name:    "foo",
   },
   As: &ast.AsAlias{
-    As:    -1,
+    As:    11,
     Alias: &ast.Ident{
       NamePos: 14,
       NameEnd: 15,
diff --git a/testdata/result/expr/braced_new_constructor.sql.txt b/testdata/result/expr/braced_new_constructor.sql.txt
new file mode 100644
index 0000000..18d206e
--- /dev/null
+++ b/testdata/result/expr/braced_new_constructor.sql.txt
@@ -0,0 +1,271 @@
+--- braced_new_constructor.sql
+-- Example from https://cloud.google.com/spanner/docs/reference/standard-sql/operators#new_operator
+NEW Universe {
+  name: "Sol"
+  closest_planets: ["Mercury", "Venus", "Earth" ]
+  star {
+    radius_miles: 432690
+    age: 4603000000
+  }
+  constellations: [{
+    name: "Libra"
+    index: 0
+  }, {
+    name: "Scorpio"
+    index: 1
+  }]
+  all_planets: (SELECT planets FROM SolTable)
+}
+--- AST
+&ast.BracedNewConstructor{
+  New:  100,
+  Type: &ast.NamedType{
+    Path: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 104,
+        NameEnd: 112,
+        Name:    "Universe",
+      },
+    },
+  },
+  Body: &ast.BracedConstructor{
+    Lbrace: 113,
+    Rbrace: 380,
+    Fields: []*ast.BracedConstructorField{
+      &ast.BracedConstructorField{
+        Name: &ast.Ident{
+          NamePos: 117,
+          NameEnd: 121,
+          Name:    "name",
+        },
+        Value: &ast.BracedConstructorFieldValueExpr{
+          Colon: 121,
+          Expr:  &ast.StringLiteral{
+            ValuePos: 123,
+            ValueEnd: 128,
+            Value:    "Sol",
+          },
+        },
+      },
+      &ast.BracedConstructorField{
+        Name: &ast.Ident{
+          NamePos: 131,
+          NameEnd: 146,
+          Name:    "closest_planets",
+        },
+        Value: &ast.BracedConstructorFieldValueExpr{
+          Colon: 146,
+          Expr:  &ast.ArrayLiteral{
+            Array:  -1,
+            Lbrack: 148,
+            Rbrack: 177,
+            Type:   nil,
+            Values: []ast.Expr{
+              &ast.StringLiteral{
+                ValuePos: 149,
+                ValueEnd: 158,
+                Value:    "Mercury",
+              },
+              &ast.StringLiteral{
+                ValuePos: 160,
+                ValueEnd: 167,
+                Value:    "Venus",
+              },
+              &ast.StringLiteral{
+                ValuePos: 169,
+                ValueEnd: 176,
+                Value:    "Earth",
+              },
+            },
+          },
+        },
+      },
+      &ast.BracedConstructorField{
+        Name: &ast.Ident{
+          NamePos: 181,
+          NameEnd: 185,
+          Name:    "star",
+        },
+        Value: &ast.BracedConstructor{
+          Lbrace: 186,
+          Rbrace: 235,
+          Fields: []*ast.BracedConstructorField{
+            &ast.BracedConstructorField{
+              Name: &ast.Ident{
+                NamePos: 192,
+                NameEnd: 204,
+                Name:    "radius_miles",
+              },
+              Value: &ast.BracedConstructorFieldValueExpr{
+                Colon: 204,
+                Expr:  &ast.IntLiteral{
+                  ValuePos: 206,
+                  ValueEnd: 212,
+                  Base:     10,
+                  Value:    "432690",
+                },
+              },
+            },
+            &ast.BracedConstructorField{
+              Name: &ast.Ident{
+                NamePos: 217,
+                NameEnd: 220,
+                Name:    "age",
+              },
+              Value: &ast.BracedConstructorFieldValueExpr{
+                Colon: 220,
+                Expr:  &ast.IntLiteral{
+                  ValuePos: 222,
+                  ValueEnd: 232,
+                  Base:     10,
+                  Value:    "4603000000",
+                },
+              },
+            },
+          },
+        },
+      },
+      &ast.BracedConstructorField{
+        Name: &ast.Ident{
+          NamePos: 239,
+          NameEnd: 253,
+          Name:    "constellations",
+        },
+        Value: &ast.BracedConstructorFieldValueExpr{
+          Colon: 253,
+          Expr:  &ast.ArrayLiteral{
+            Array:  -1,
+            Lbrack: 255,
+            Rbrack: 332,
+            Type:   nil,
+            Values: []ast.Expr{
+              &ast.BracedConstructor{
+                Lbrace: 256,
+                Rbrace: 291,
+                Fields: []*ast.BracedConstructorField{
+                  &ast.BracedConstructorField{
+                    Name: &ast.Ident{
+                      NamePos: 262,
+                      NameEnd: 266,
+                      Name:    "name",
+                    },
+                    Value: &ast.BracedConstructorFieldValueExpr{
+                      Colon: 266,
+                      Expr:  &ast.StringLiteral{
+                        ValuePos: 268,
+                        ValueEnd: 275,
+                        Value:    "Libra",
+                      },
+                    },
+                  },
+                  &ast.BracedConstructorField{
+                    Name: &ast.Ident{
+                      NamePos: 280,
+                      NameEnd: 285,
+                      Name:    "index",
+                    },
+                    Value: &ast.BracedConstructorFieldValueExpr{
+                      Colon: 285,
+                      Expr:  &ast.IntLiteral{
+                        ValuePos: 287,
+                        ValueEnd: 288,
+                        Base:     10,
+                        Value:    "0",
+                      },
+                    },
+                  },
+                },
+              },
+              &ast.BracedConstructor{
+                Lbrace: 294,
+                Rbrace: 331,
+                Fields: []*ast.BracedConstructorField{
+                  &ast.BracedConstructorField{
+                    Name: &ast.Ident{
+                      NamePos: 300,
+                      NameEnd: 304,
+                      Name:    "name",
+                    },
+                    Value: &ast.BracedConstructorFieldValueExpr{
+                      Colon: 304,
+                      Expr:  &ast.StringLiteral{
+                        ValuePos: 306,
+                        ValueEnd: 315,
+                        Value:    "Scorpio",
+                      },
+                    },
+                  },
+                  &ast.BracedConstructorField{
+                    Name: &ast.Ident{
+                      NamePos: 320,
+                      NameEnd: 325,
+                      Name:    "index",
+                    },
+                    Value: &ast.BracedConstructorFieldValueExpr{
+                      Colon: 325,
+                      Expr:  &ast.IntLiteral{
+                        ValuePos: 327,
+                        ValueEnd: 328,
+                        Base:     10,
+                        Value:    "1",
+                      },
+                    },
+                  },
+                },
+              },
+            },
+          },
+        },
+      },
+      &ast.BracedConstructorField{
+        Name: &ast.Ident{
+          NamePos: 336,
+          NameEnd: 347,
+          Name:    "all_planets",
+        },
+        Value: &ast.BracedConstructorFieldValueExpr{
+          Colon: 347,
+          Expr:  &ast.ScalarSubQuery{
+            Lparen: 349,
+            Rparen: 378,
+            Query:  &ast.Select{
+              Select:   350,
+              Distinct: false,
+              As:       nil,
+              Results:  []ast.SelectItem{
+                &ast.ExprSelectItem{
+                  Expr: &ast.Ident{
+                    NamePos: 357,
+                    NameEnd: 364,
+                    Name:    "planets",
+                  },
+                },
+              },
+              From: &ast.From{
+                From:   365,
+                Source: &ast.TableName{
+                  Table: &ast.Ident{
+                    NamePos: 370,
+                    NameEnd: 378,
+                    Name:    "SolTable",
+                  },
+                  Hint:   (*ast.Hint)(nil),
+                  As:     (*ast.AsAlias)(nil),
+                  Sample: (*ast.TableSample)(nil),
+                },
+              },
+              Where:   (*ast.Where)(nil),
+              GroupBy: (*ast.GroupBy)(nil),
+              Having:  (*ast.Having)(nil),
+              OrderBy: (*ast.OrderBy)(nil),
+              Limit:   (*ast.Limit)(nil),
+            },
+          },
+        },
+      },
+    },
+  },
+}
+
+--- SQL
+NEW Universe {name: "Sol", closest_planets: ["Mercury", "Venus", "Earth"], star {radius_miles: 432690, age: 4603000000}, constellations: [{name: "Libra", index: 0}, {name: "Scorpio", index: 1}], all_planets: (SELECT planets FROM SolTable)}
diff --git a/testdata/result/expr/braced_new_constructor_empty.sql.txt b/testdata/result/expr/braced_new_constructor_empty.sql.txt
new file mode 100644
index 0000000..60878f9
--- /dev/null
+++ b/testdata/result/expr/braced_new_constructor_empty.sql.txt
@@ -0,0 +1,23 @@
+--- braced_new_constructor_empty.sql
+NEW Universe {}
+--- AST
+&ast.BracedNewConstructor{
+  New:  0,
+  Type: &ast.NamedType{
+    Path: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 4,
+        NameEnd: 12,
+        Name:    "Universe",
+      },
+    },
+  },
+  Body: &ast.BracedConstructor{
+    Lbrace: 13,
+    Rbrace: 14,
+    Fields: []*ast.BracedConstructorField(nil),
+  },
+}
+
+--- SQL
+NEW Universe {}
diff --git a/testdata/result/expr/braced_new_constructor_oneliner.sql.txt b/testdata/result/expr/braced_new_constructor_oneliner.sql.txt
new file mode 100644
index 0000000..cc34fbe
--- /dev/null
+++ b/testdata/result/expr/braced_new_constructor_oneliner.sql.txt
@@ -0,0 +1,72 @@
+--- braced_new_constructor_oneliner.sql
+NEW Universe {name: "Sol", closest_planets: ["Mercury", "Venus", "Earth" ]}
+--- AST
+&ast.BracedNewConstructor{
+  New:  0,
+  Type: &ast.NamedType{
+    Path: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 4,
+        NameEnd: 12,
+        Name:    "Universe",
+      },
+    },
+  },
+  Body: &ast.BracedConstructor{
+    Lbrace: 13,
+    Rbrace: 74,
+    Fields: []*ast.BracedConstructorField{
+      &ast.BracedConstructorField{
+        Name: &ast.Ident{
+          NamePos: 14,
+          NameEnd: 18,
+          Name:    "name",
+        },
+        Value: &ast.BracedConstructorFieldValueExpr{
+          Colon: 18,
+          Expr:  &ast.StringLiteral{
+            ValuePos: 20,
+            ValueEnd: 25,
+            Value:    "Sol",
+          },
+        },
+      },
+      &ast.BracedConstructorField{
+        Name: &ast.Ident{
+          NamePos: 27,
+          NameEnd: 42,
+          Name:    "closest_planets",
+        },
+        Value: &ast.BracedConstructorFieldValueExpr{
+          Colon: 42,
+          Expr:  &ast.ArrayLiteral{
+            Array:  -1,
+            Lbrack: 44,
+            Rbrack: 73,
+            Type:   nil,
+            Values: []ast.Expr{
+              &ast.StringLiteral{
+                ValuePos: 45,
+                ValueEnd: 54,
+                Value:    "Mercury",
+              },
+              &ast.StringLiteral{
+                ValuePos: 56,
+                ValueEnd: 63,
+                Value:    "Venus",
+              },
+              &ast.StringLiteral{
+                ValuePos: 65,
+                ValueEnd: 72,
+                Value:    "Earth",
+              },
+            },
+          },
+        },
+      },
+    },
+  },
+}
+
+--- SQL
+NEW Universe {name: "Sol", closest_planets: ["Mercury", "Venus", "Earth"]}
diff --git a/testdata/result/expr/new_constructor.sql.txt b/testdata/result/expr/new_constructor.sql.txt
new file mode 100644
index 0000000..63cb5d3
--- /dev/null
+++ b/testdata/result/expr/new_constructor.sql.txt
@@ -0,0 +1,66 @@
+--- new_constructor.sql
+NEW googlesql.examples.music.Chart(key AS rank, name AS chart_name)
+--- AST
+&ast.NewConstructor{
+  New:  0,
+  Type: &ast.NamedType{
+    Path: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 4,
+        NameEnd: 13,
+        Name:    "googlesql",
+      },
+      &ast.Ident{
+        NamePos: 14,
+        NameEnd: 22,
+        Name:    "examples",
+      },
+      &ast.Ident{
+        NamePos: 23,
+        NameEnd: 28,
+        Name:    "music",
+      },
+      &ast.Ident{
+        NamePos: 29,
+        NameEnd: 34,
+        Name:    "Chart",
+      },
+    },
+  },
+  Args: []ast.NewConstructorArg{
+    &ast.Alias{
+      Expr: &ast.Ident{
+        NamePos: 35,
+        NameEnd: 38,
+        Name:    "key",
+      },
+      As: &ast.AsAlias{
+        As:    39,
+        Alias: &ast.Ident{
+          NamePos: 42,
+          NameEnd: 46,
+          Name:    "rank",
+        },
+      },
+    },
+    &ast.Alias{
+      Expr: &ast.Ident{
+        NamePos: 48,
+        NameEnd: 52,
+        Name:    "name",
+      },
+      As: &ast.AsAlias{
+        As:    53,
+        Alias: &ast.Ident{
+          NamePos: 56,
+          NameEnd: 66,
+          Name:    "chart_name",
+        },
+      },
+    },
+  },
+  Rparen: 66,
+}
+
+--- SQL
+NEW googlesql.examples.music.Chart(key AS rank, name AS chart_name)
diff --git a/testdata/result/expr/new_constructor_empty.sql.txt b/testdata/result/expr/new_constructor_empty.sql.txt
new file mode 100644
index 0000000..805f975
--- /dev/null
+++ b/testdata/result/expr/new_constructor_empty.sql.txt
@@ -0,0 +1,36 @@
+--- new_constructor_empty.sql
+NEW googlesql.examples.music.Chart()
+
+--- AST
+&ast.NewConstructor{
+  New:  0,
+  Type: &ast.NamedType{
+    Path: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 4,
+        NameEnd: 13,
+        Name:    "googlesql",
+      },
+      &ast.Ident{
+        NamePos: 14,
+        NameEnd: 22,
+        Name:    "examples",
+      },
+      &ast.Ident{
+        NamePos: 23,
+        NameEnd: 28,
+        Name:    "music",
+      },
+      &ast.Ident{
+        NamePos: 29,
+        NameEnd: 34,
+        Name:    "Chart",
+      },
+    },
+  },
+  Args:   []ast.NewConstructorArg(nil),
+  Rparen: 35,
+}
+
+--- SQL
+NEW googlesql.examples.music.Chart()
diff --git a/testdata/result/expr/value_subquery_select_as_fqn_typename.sql.txt b/testdata/result/expr/value_subquery_select_as_fqn_typename.sql.txt
index f7e0549..73aed2a 100644
--- a/testdata/result/expr/value_subquery_select_as_fqn_typename.sql.txt
+++ b/testdata/result/expr/value_subquery_select_as_fqn_typename.sql.txt
@@ -33,7 +33,7 @@
           Value:    "1",
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    30,
           Alias: &ast.Ident{
             NamePos: 33,
             NameEnd: 34,
diff --git a/testdata/result/expr/value_subquery_select_as_leaf_typename.sql.txt b/testdata/result/expr/value_subquery_select_as_leaf_typename.sql.txt
index f73a77a..e3aeb50 100644
--- a/testdata/result/expr/value_subquery_select_as_leaf_typename.sql.txt
+++ b/testdata/result/expr/value_subquery_select_as_leaf_typename.sql.txt
@@ -28,7 +28,7 @@
           Value:    "1",
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    22,
           Alias: &ast.Ident{
             NamePos: 25,
             NameEnd: 26,
diff --git a/testdata/result/expr/value_subquery_select_as_struct.sql.txt b/testdata/result/expr/value_subquery_select_as_struct.sql.txt
index 0308dc1..f543cd5 100644
--- a/testdata/result/expr/value_subquery_select_as_struct.sql.txt
+++ b/testdata/result/expr/value_subquery_select_as_struct.sql.txt
@@ -21,7 +21,7 @@
           Value:    "1",
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    20,
           Alias: &ast.Ident{
             NamePos: 23,
             NameEnd: 24,
diff --git a/testdata/result/query/select_alias_without_as.sql.txt b/testdata/result/query/select_alias_without_as.sql.txt
index da93020..c4604a9 100644
--- a/testdata/result/query/select_alias_without_as.sql.txt
+++ b/testdata/result/query/select_alias_without_as.sql.txt
@@ -18,7 +18,7 @@ select 1 A
           Value:    "1",
         },
         As: &ast.AsAlias{
-          As:    9,
+          As:    -1,
           Alias: &ast.Ident{
             NamePos: 9,
             NameEnd: 10,
@@ -37,4 +37,4 @@ select 1 A
 }
 
 --- SQL
-SELECT 1 AS A
+SELECT 1 A
diff --git a/testdata/result/query/select_array_with_struct.sql.txt b/testdata/result/query/select_array_with_struct.sql.txt
index 28c893b..32f645d 100644
--- a/testdata/result/query/select_array_with_struct.sql.txt
+++ b/testdata/result/query/select_array_with_struct.sql.txt
@@ -55,11 +55,9 @@ FROM
             },
           },
           Values: []ast.Expr{
-            &ast.StructLiteral{
-              Struct: 0,
+            &ast.TupleStructLiteral{
               Lparen: 58,
               Rparen: 67,
-              Fields: []*ast.StructField(nil),
               Values: []ast.Expr{
                 &ast.IntLiteral{
                   ValuePos: 59,
@@ -74,11 +72,9 @@ FROM
                 },
               },
             },
-            &ast.StructLiteral{
-              Struct: 0,
+            &ast.TupleStructLiteral{
               Lparen: 70,
               Rparen: 79,
-              Fields: []*ast.StructField(nil),
               Values: []ast.Expr{
                 &ast.IntLiteral{
                   ValuePos: 71,
@@ -110,4 +106,4 @@ FROM
 }
 
 --- SQL
-SELECT * FROM UNNEST(ARRAY<STRUCT<x INT64, y STRING>>[STRUCT(1, "foo"), STRUCT(3, "bar")])
+SELECT * FROM UNNEST(ARRAY<STRUCT<x INT64, y STRING>>[(1, "foo"), (3, "bar")])
diff --git a/testdata/result/query/select_call_with_named_expr.sql.txt b/testdata/result/query/select_call_with_named_expr.sql.txt
index 78d1d15..bae8b86 100644
--- a/testdata/result/query/select_call_with_named_expr.sql.txt
+++ b/testdata/result/query/select_call_with_named_expr.sql.txt
@@ -54,7 +54,7 @@ WHERE a.SingerId = 1 AND SEARCH(a.DescriptionTokens, 'classic albums', enhance_q
         },
         Hint: (*ast.Hint)(nil),
         As:   &ast.AsAlias{
-          As:    44,
+          As:    -1,
           Alias: &ast.Ident{
             NamePos: 44,
             NameEnd: 45,
@@ -150,4 +150,4 @@ WHERE a.SingerId = 1 AND SEARCH(a.DescriptionTokens, 'classic albums', enhance_q
 }
 
 --- SQL
-SELECT a.AlbumId, a.Description FROM Albums AS a WHERE a.SingerId = 1 AND SEARCH(a.DescriptionTokens, "classic albums", enhance_query => TRUE)
+SELECT a.AlbumId, a.Description FROM Albums a WHERE a.SingerId = 1 AND SEARCH(a.DescriptionTokens, "classic albums", enhance_query => TRUE)
diff --git a/testdata/result/query/select_cast.sql.txt b/testdata/result/query/select_cast.sql.txt
index d5f6fb9..84154e9 100644
--- a/testdata/result/query/select_cast.sql.txt
+++ b/testdata/result/query/select_cast.sql.txt
@@ -52,18 +52,14 @@ limit cast(1 as INT64) offset cast(@foo as INT64)
           Cast:   47,
           Rparen: 147,
           Safe:   false,
-          Expr:   &ast.StructLiteral{
-            Struct: 0,
+          Expr:   &ast.TupleStructLiteral{
             Lparen: 52,
             Rparen: 84,
-            Fields: []*ast.StructField(nil),
             Values: []ast.Expr{
-              &ast.StructLiteral{
+              &ast.TypelessStructLiteral{
                 Struct: 53,
-                Lparen: 59,
                 Rparen: 60,
-                Fields: []*ast.StructField(nil),
-                Values: []ast.Expr(nil),
+                Values: []ast.TypelessStructLiteralArg(nil),
               },
               &ast.IntLiteral{
                 ValuePos: 63,
@@ -120,7 +116,7 @@ limit cast(1 as INT64) offset cast(@foo as INT64)
                 Type:  &ast.StructType{
                   Struct: 96,
                   Gt:     103,
-                  Fields: []*ast.StructField{},
+                  Fields: []*ast.StructField(nil),
                 },
               },
               &ast.StructField{
@@ -300,4 +296,4 @@ limit cast(1 as INT64) offset cast(@foo as INT64)
 }
 
 --- SQL
-SELECT CAST(1 AS INT64), CAST(0.1 AS FLOAT32), CAST(STRUCT(STRUCT(), 1, [2, 3], ["4", "5"]) AS STRUCT<STRUCT<>, x INT64, y ARRAY<INT64>, z ARRAY<STRING>>) FROM x TABLESAMPLE BERNOULLI (CAST(0.1 AS FLOAT64) PERCENT), y TABLESAMPLE BERNOULLI (CAST(1 AS INT64) ROWS), z TABLESAMPLE BERNOULLI (CAST(@param AS INT64) ROWS) LIMIT CAST(1 AS INT64) OFFSET CAST(@foo AS INT64)
+SELECT CAST(1 AS INT64), CAST(0.1 AS FLOAT32), CAST((STRUCT(), 1, [2, 3], ["4", "5"]) AS STRUCT<STRUCT<>, x INT64, y ARRAY<INT64>, z ARRAY<STRING>>) FROM x TABLESAMPLE BERNOULLI (CAST(0.1 AS FLOAT64) PERCENT), y TABLESAMPLE BERNOULLI (CAST(1 AS INT64) ROWS), z TABLESAMPLE BERNOULLI (CAST(@param AS INT64) ROWS) LIMIT CAST(1 AS INT64) OFFSET CAST(@foo AS INT64)
diff --git a/testdata/result/query/select_expr.sql.txt b/testdata/result/query/select_expr.sql.txt
index e6c89c3..9f1a2bc 100644
--- a/testdata/result/query/select_expr.sql.txt
+++ b/testdata/result/query/select_expr.sql.txt
@@ -340,7 +340,7 @@ select 1 + 2, 1 - 2,
                     Value:    "1",
                   },
                   As: &ast.AsAlias{
-                    As:    204,
+                    As:    -1,
                     Alias: &ast.Ident{
                       NamePos: 204,
                       NameEnd: 207,
@@ -915,7 +915,7 @@ select 1 + 2, 1 - 2,
           },
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    792,
           Alias: &ast.Ident{
             NamePos: 795,
             NameEnd: 801,
@@ -934,4 +934,4 @@ select 1 + 2, 1 - 2,
 }
 
 --- SQL
-SELECT 1 + 2, 1 - 2, 1 * 2, 2 / 2, +1 + +1, -1 + -1, +1.2, -3.4, ~1 ^ ~1, 1 ^ 2, 2 & 1, 2 | 1, 1 << 2, 2 >> 1, foo.bar * +foo.bar * -foo.bar, (SELECT 1 AS `1`).`1`, NOT NOT TRUE, [1, 2, 3][OFFSET(1)], [1, 2, 3][OFFSET(1)], [1, 2, 3][ORDINAL(1)], CASE WHEN 1 = 1 THEN "1 = 1" ELSE "else" END, CASE 1 WHEN 1 THEN "1" WHEN 2 THEN "2" ELSE "other" END, date_add(DATE "2019-09-01", INTERVAL 5 day), timestamp_add(TIMESTAMP "2019-09-01 08:11:22", INTERVAL 5 hour), 1 IN (1, 2, 3), 2 IN UNNEST([1, 2, 3]), 3 IN (SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3), [1] || [2], IF(1 > 1, 1, 2) + 1 AS result
+SELECT 1 + 2, 1 - 2, 1 * 2, 2 / 2, +1 + +1, -1 + -1, +1.2, -3.4, ~1 ^ ~1, 1 ^ 2, 2 & 1, 2 | 1, 1 << 2, 2 >> 1, foo.bar * +foo.bar * -foo.bar, (SELECT 1 `1`).`1`, NOT NOT TRUE, [1, 2, 3][OFFSET(1)], [1, 2, 3][OFFSET(1)], [1, 2, 3][ORDINAL(1)], CASE WHEN 1 = 1 THEN "1 = 1" ELSE "else" END, CASE 1 WHEN 1 THEN "1" WHEN 2 THEN "2" ELSE "other" END, date_add(DATE "2019-09-01", INTERVAL 5 day), timestamp_add(TIMESTAMP "2019-09-01 08:11:22", INTERVAL 5 hour), 1 IN (1, 2, 3), 2 IN UNNEST([1, 2, 3]), 3 IN (SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3), [1] || [2], IF(1 > 1, 1, 2) + 1 AS result
diff --git a/testdata/result/query/select_from_implicit_unnest.sql.txt b/testdata/result/query/select_from_implicit_unnest.sql.txt
index 6cea988..f1d12b8 100644
--- a/testdata/result/query/select_from_implicit_unnest.sql.txt
+++ b/testdata/result/query/select_from_implicit_unnest.sql.txt
@@ -41,9 +41,8 @@ FROM UNNEST([STRUCT<arr ARRAY<STRING>>(["foo"])]) AS t,
             Rbrack: 61,
             Type:   nil,
             Values: []ast.Expr{
-              &ast.StructLiteral{
+              &ast.TypedStructLiteral{
                 Struct: 27,
-                Lparen: 52,
                 Rparen: 60,
                 Fields: []*ast.StructField{
                   &ast.StructField{
@@ -82,7 +81,7 @@ FROM UNNEST([STRUCT<arr ARRAY<STRING>>(["foo"])]) AS t,
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    -1,
+            As:    64,
             Alias: &ast.Ident{
               NamePos: 67,
               NameEnd: 68,
@@ -109,7 +108,7 @@ FROM UNNEST([STRUCT<arr ARRAY<STRING>>(["foo"])]) AS t,
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    -1,
+            As:    81,
             Alias: &ast.Ident{
               NamePos: 84,
               NameEnd: 85,
@@ -120,7 +119,7 @@ FROM UNNEST([STRUCT<arr ARRAY<STRING>>(["foo"])]) AS t,
             With:   86,
             Offset: 91,
             As:     &ast.AsAlias{
-              As:    -1,
+              As:    98,
               Alias: &ast.Ident{
                 NamePos: 101,
                 NameEnd: 104,
diff --git a/testdata/result/query/select_from_path.sql.txt b/testdata/result/query/select_from_path.sql.txt
index f3331a1..d58a1d7 100644
--- a/testdata/result/query/select_from_path.sql.txt
+++ b/testdata/result/query/select_from_path.sql.txt
@@ -35,7 +35,7 @@ FROM
         },
         Hint: (*ast.Hint)(nil),
         As:   &ast.AsAlias{
-          As:    -1,
+          As:    44,
           Alias: &ast.Ident{
             NamePos: 47,
             NameEnd: 48,
diff --git a/testdata/result/query/select_literals_all.sql.txt b/testdata/result/query/select_literals_all.sql.txt
index 3e35cc3..1713799 100644
--- a/testdata/result/query/select_literals_all.sql.txt
+++ b/testdata/result/query/select_literals_all.sql.txt
@@ -392,29 +392,33 @@ lines''',
             As:       nil,
             Results:  []ast.SelectItem{
               &ast.ExprSelectItem{
-                Expr: &ast.StructLiteral{
+                Expr: &ast.TypelessStructLiteral{
                   Struct: 467,
-                  Lparen: 473,
                   Rparen: 481,
-                  Fields: []*ast.StructField(nil),
-                  Values: []ast.Expr{
-                    &ast.IntLiteral{
-                      ValuePos: 474,
-                      ValueEnd: 475,
-                      Base:     10,
-                      Value:    "1",
+                  Values: []ast.TypelessStructLiteralArg{
+                    &ast.ExprArg{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 474,
+                        ValueEnd: 475,
+                        Base:     10,
+                        Value:    "1",
+                      },
                     },
-                    &ast.IntLiteral{
-                      ValuePos: 477,
-                      ValueEnd: 478,
-                      Base:     10,
-                      Value:    "2",
+                    &ast.ExprArg{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 477,
+                        ValueEnd: 478,
+                        Base:     10,
+                        Value:    "2",
+                      },
                     },
-                    &ast.IntLiteral{
-                      ValuePos: 480,
-                      ValueEnd: 481,
-                      Base:     10,
-                      Value:    "3",
+                    &ast.ExprArg{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 480,
+                        ValueEnd: 481,
+                        Base:     10,
+                        Value:    "3",
+                      },
                     },
                   },
                 },
@@ -439,11 +443,9 @@ lines''',
             As:       nil,
             Results:  []ast.SelectItem{
               &ast.ExprSelectItem{
-                Expr: &ast.StructLiteral{
-                  Struct: 0,
+                Expr: &ast.TupleStructLiteral{
                   Lparen: 500,
                   Rparen: 508,
-                  Fields: []*ast.StructField(nil),
                   Values: []ast.Expr{
                     &ast.IntLiteral{
                       ValuePos: 501,
@@ -635,4 +637,4 @@ lines''',
 }
 
 --- SQL
-SELECT "abc", "it\'s", "it\'s", "Title: \"Boy\"", "abc", "it\'s", "Title:\"Boy\"", "two\nlines", "why\?", "abc+", "abc+", "abc+", "f\\(abc,(.*),def\\)", B"abc", B"abc", B"abc", B"abc+", B"abc+", B"abc", 123, 0xABC, -123, -0xABC, 123.456e-67, .1E4, 58., 4e2, [1, 2, 3], ["x", "y", "xy"], ARRAY[1, 2, 3], ARRAY<STRING>["x", "y", "xy"], ARRAY<INT64>[], ARRAY(SELECT STRUCT(1, 2, 3)), ARRAY(SELECT STRUCT(1, 2, 3)), DATE "2014-09-27", DATE("2014-09-27"), TIMESTAMP "2014-09-27 12:30:00.45-08", TIMESTAMP "2014-09-27 12:30:00.45 America/Los_Angeles", TIMESTAMP "2014-09-27", TIMESTAMP("2014-09-27"), JSON "1", JSON "[1, 2]", JSON "{}", NUMERIC "0", NUMERIC "0", TRUE, FALSE
+SELECT "abc", "it\'s", "it\'s", "Title: \"Boy\"", "abc", "it\'s", "Title:\"Boy\"", "two\nlines", "why\?", "abc+", "abc+", "abc+", "f\\(abc,(.*),def\\)", B"abc", B"abc", B"abc", B"abc+", B"abc+", B"abc", 123, 0xABC, -123, -0xABC, 123.456e-67, .1E4, 58., 4e2, [1, 2, 3], ["x", "y", "xy"], ARRAY[1, 2, 3], ARRAY<STRING>["x", "y", "xy"], ARRAY<INT64>[], ARRAY(SELECT STRUCT(1, 2, 3)), ARRAY(SELECT (1, 2, 3)), DATE "2014-09-27", DATE("2014-09-27"), TIMESTAMP "2014-09-27 12:30:00.45-08", TIMESTAMP "2014-09-27 12:30:00.45 America/Los_Angeles", TIMESTAMP "2014-09-27", TIMESTAMP("2014-09-27"), JSON "1", JSON "[1, 2]", JSON "{}", NUMERIC "0", NUMERIC "0", TRUE, FALSE
diff --git a/testdata/result/query/select_literals_date.sql.txt b/testdata/result/query/select_literals_date.sql.txt
index fd6b359..7d21137 100644
--- a/testdata/result/query/select_literals_date.sql.txt
+++ b/testdata/result/query/select_literals_date.sql.txt
@@ -149,7 +149,7 @@ SELECT
           Having:       nil,
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    270,
           Alias: &ast.Ident{
             NamePos: 273,
             NameEnd: 276,
@@ -180,7 +180,7 @@ SELECT
           Having:       nil,
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    304,
           Alias: &ast.Ident{
             NamePos: 307,
             NameEnd: 310,
diff --git a/testdata/result/query/select_literals_struct.sql.txt b/testdata/result/query/select_literals_struct.sql.txt
index 802fb1e..3b45c25 100644
--- a/testdata/result/query/select_literals_struct.sql.txt
+++ b/testdata/result/query/select_literals_struct.sql.txt
@@ -3,10 +3,17 @@ SELECT
   ARRAY(SELECT STRUCT(1, 2, 3)),
   ARRAY(SELECT STRUCT(1, 2, TRUE)),
   ARRAY(SELECT STRUCT<X INT64, Y INT64, Z INT64>(1, 2, 3)),
+  ARRAY(SELECT STRUCT(1 AS X, 2 AS Y, 3 AS Z)),
   ARRAY(SELECT STRUCT<INT64, INT64, INT64>(1, 2, 3)),
   ARRAY(SELECT STRUCT<X INT64, INT64, INT64>(1, 2, 3)),
+  ARRAY(SELECT STRUCT(1 AS X, 2, 3)),
   ARRAY(SELECT (1, 2, 3)),
+  ARRAY(SELECT STRUCT<X INT64>(1)),
+  ARRAY(SELECT STRUCT(1 AS X)),
+  ARRAY(SELECT STRUCT<INT64>(1)),
+  ARRAY(SELECT STRUCT(1)),
   ARRAY(SELECT STRUCT<>()),
+  ARRAY(SELECT STRUCT()),
   EXISTS(SELECT (1, 2))
 
 --- AST
@@ -28,29 +35,33 @@ SELECT
             As:       nil,
             Results:  []ast.SelectItem{
               &ast.ExprSelectItem{
-                Expr: &ast.StructLiteral{
+                Expr: &ast.TypelessStructLiteral{
                   Struct: 22,
-                  Lparen: 28,
                   Rparen: 36,
-                  Fields: []*ast.StructField(nil),
-                  Values: []ast.Expr{
-                    &ast.IntLiteral{
-                      ValuePos: 29,
-                      ValueEnd: 30,
-                      Base:     10,
-                      Value:    "1",
+                  Values: []ast.TypelessStructLiteralArg{
+                    &ast.ExprArg{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 29,
+                        ValueEnd: 30,
+                        Base:     10,
+                        Value:    "1",
+                      },
                     },
-                    &ast.IntLiteral{
-                      ValuePos: 32,
-                      ValueEnd: 33,
-                      Base:     10,
-                      Value:    "2",
+                    &ast.ExprArg{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 32,
+                        ValueEnd: 33,
+                        Base:     10,
+                        Value:    "2",
+                      },
                     },
-                    &ast.IntLiteral{
-                      ValuePos: 35,
-                      ValueEnd: 36,
-                      Base:     10,
-                      Value:    "3",
+                    &ast.ExprArg{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 35,
+                        ValueEnd: 36,
+                        Base:     10,
+                        Value:    "3",
+                      },
                     },
                   },
                 },
@@ -75,27 +86,31 @@ SELECT
             As:       nil,
             Results:  []ast.SelectItem{
               &ast.ExprSelectItem{
-                Expr: &ast.StructLiteral{
+                Expr: &ast.TypelessStructLiteral{
                   Struct: 55,
-                  Lparen: 61,
                   Rparen: 72,
-                  Fields: []*ast.StructField(nil),
-                  Values: []ast.Expr{
-                    &ast.IntLiteral{
-                      ValuePos: 62,
-                      ValueEnd: 63,
-                      Base:     10,
-                      Value:    "1",
+                  Values: []ast.TypelessStructLiteralArg{
+                    &ast.ExprArg{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 62,
+                        ValueEnd: 63,
+                        Base:     10,
+                        Value:    "1",
+                      },
                     },
-                    &ast.IntLiteral{
-                      ValuePos: 65,
-                      ValueEnd: 66,
-                      Base:     10,
-                      Value:    "2",
+                    &ast.ExprArg{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 65,
+                        ValueEnd: 66,
+                        Base:     10,
+                        Value:    "2",
+                      },
                     },
-                    &ast.BoolLiteral{
-                      ValuePos: 68,
-                      Value:    true,
+                    &ast.ExprArg{
+                      Expr: &ast.BoolLiteral{
+                        ValuePos: 68,
+                        Value:    true,
+                      },
                     },
                   },
                 },
@@ -120,9 +135,8 @@ SELECT
             As:       nil,
             Results:  []ast.SelectItem{
               &ast.ExprSelectItem{
-                Expr: &ast.StructLiteral{
+                Expr: &ast.TypedStructLiteral{
                   Struct: 91,
-                  Lparen: 124,
                   Rparen: 132,
                   Fields: []*ast.StructField{
                     &ast.StructField{
@@ -194,56 +208,130 @@ SELECT
       &ast.ExprSelectItem{
         Expr: &ast.ArraySubQuery{
           Array:  138,
-          Rparen: 187,
+          Rparen: 181,
           Query:  &ast.Select{
             Select:   144,
             Distinct: false,
             As:       nil,
             Results:  []ast.SelectItem{
               &ast.ExprSelectItem{
-                Expr: &ast.StructLiteral{
+                Expr: &ast.TypelessStructLiteral{
                   Struct: 151,
-                  Lparen: 178,
-                  Rparen: 186,
+                  Rparen: 180,
+                  Values: []ast.TypelessStructLiteralArg{
+                    &ast.Alias{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 158,
+                        ValueEnd: 159,
+                        Base:     10,
+                        Value:    "1",
+                      },
+                      As: &ast.AsAlias{
+                        As:    160,
+                        Alias: &ast.Ident{
+                          NamePos: 163,
+                          NameEnd: 164,
+                          Name:    "X",
+                        },
+                      },
+                    },
+                    &ast.Alias{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 166,
+                        ValueEnd: 167,
+                        Base:     10,
+                        Value:    "2",
+                      },
+                      As: &ast.AsAlias{
+                        As:    168,
+                        Alias: &ast.Ident{
+                          NamePos: 171,
+                          NameEnd: 172,
+                          Name:    "Y",
+                        },
+                      },
+                    },
+                    &ast.Alias{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 174,
+                        ValueEnd: 175,
+                        Base:     10,
+                        Value:    "3",
+                      },
+                      As: &ast.AsAlias{
+                        As:    176,
+                        Alias: &ast.Ident{
+                          NamePos: 179,
+                          NameEnd: 180,
+                          Name:    "Z",
+                        },
+                      },
+                    },
+                  },
+                },
+              },
+            },
+            From:    (*ast.From)(nil),
+            Where:   (*ast.Where)(nil),
+            GroupBy: (*ast.GroupBy)(nil),
+            Having:  (*ast.Having)(nil),
+            OrderBy: (*ast.OrderBy)(nil),
+            Limit:   (*ast.Limit)(nil),
+          },
+        },
+      },
+      &ast.ExprSelectItem{
+        Expr: &ast.ArraySubQuery{
+          Array:  186,
+          Rparen: 235,
+          Query:  &ast.Select{
+            Select:   192,
+            Distinct: false,
+            As:       nil,
+            Results:  []ast.SelectItem{
+              &ast.ExprSelectItem{
+                Expr: &ast.TypedStructLiteral{
+                  Struct: 199,
+                  Rparen: 234,
                   Fields: []*ast.StructField{
                     &ast.StructField{
                       Ident: (*ast.Ident)(nil),
                       Type:  &ast.SimpleType{
-                        NamePos: 158,
+                        NamePos: 206,
                         Name:    "INT64",
                       },
                     },
                     &ast.StructField{
                       Ident: (*ast.Ident)(nil),
                       Type:  &ast.SimpleType{
-                        NamePos: 165,
+                        NamePos: 213,
                         Name:    "INT64",
                       },
                     },
                     &ast.StructField{
                       Ident: (*ast.Ident)(nil),
                       Type:  &ast.SimpleType{
-                        NamePos: 172,
+                        NamePos: 220,
                         Name:    "INT64",
                       },
                     },
                   },
                   Values: []ast.Expr{
                     &ast.IntLiteral{
-                      ValuePos: 179,
-                      ValueEnd: 180,
+                      ValuePos: 227,
+                      ValueEnd: 228,
                       Base:     10,
                       Value:    "1",
                     },
                     &ast.IntLiteral{
-                      ValuePos: 182,
-                      ValueEnd: 183,
+                      ValuePos: 230,
+                      ValueEnd: 231,
                       Base:     10,
                       Value:    "2",
                     },
                     &ast.IntLiteral{
-                      ValuePos: 185,
-                      ValueEnd: 186,
+                      ValuePos: 233,
+                      ValueEnd: 234,
                       Base:     10,
                       Value:    "3",
                     },
@@ -262,61 +350,60 @@ SELECT
       },
       &ast.ExprSelectItem{
         Expr: &ast.ArraySubQuery{
-          Array:  192,
-          Rparen: 243,
+          Array:  240,
+          Rparen: 291,
           Query:  &ast.Select{
-            Select:   198,
+            Select:   246,
             Distinct: false,
             As:       nil,
             Results:  []ast.SelectItem{
               &ast.ExprSelectItem{
-                Expr: &ast.StructLiteral{
-                  Struct: 205,
-                  Lparen: 234,
-                  Rparen: 242,
+                Expr: &ast.TypedStructLiteral{
+                  Struct: 253,
+                  Rparen: 290,
                   Fields: []*ast.StructField{
                     &ast.StructField{
                       Ident: &ast.Ident{
-                        NamePos: 212,
-                        NameEnd: 213,
+                        NamePos: 260,
+                        NameEnd: 261,
                         Name:    "X",
                       },
                       Type: &ast.SimpleType{
-                        NamePos: 214,
+                        NamePos: 262,
                         Name:    "INT64",
                       },
                     },
                     &ast.StructField{
                       Ident: (*ast.Ident)(nil),
                       Type:  &ast.SimpleType{
-                        NamePos: 221,
+                        NamePos: 269,
                         Name:    "INT64",
                       },
                     },
                     &ast.StructField{
                       Ident: (*ast.Ident)(nil),
                       Type:  &ast.SimpleType{
-                        NamePos: 228,
+                        NamePos: 276,
                         Name:    "INT64",
                       },
                     },
                   },
                   Values: []ast.Expr{
                     &ast.IntLiteral{
-                      ValuePos: 235,
-                      ValueEnd: 236,
+                      ValuePos: 283,
+                      ValueEnd: 284,
                       Base:     10,
                       Value:    "1",
                     },
                     &ast.IntLiteral{
-                      ValuePos: 238,
-                      ValueEnd: 239,
+                      ValuePos: 286,
+                      ValueEnd: 287,
                       Base:     10,
                       Value:    "2",
                     },
                     &ast.IntLiteral{
-                      ValuePos: 241,
-                      ValueEnd: 242,
+                      ValuePos: 289,
+                      ValueEnd: 290,
                       Base:     10,
                       Value:    "3",
                     },
@@ -335,35 +422,92 @@ SELECT
       },
       &ast.ExprSelectItem{
         Expr: &ast.ArraySubQuery{
-          Array:  248,
-          Rparen: 270,
+          Array:  296,
+          Rparen: 329,
           Query:  &ast.Select{
-            Select:   254,
+            Select:   302,
             Distinct: false,
             As:       nil,
             Results:  []ast.SelectItem{
               &ast.ExprSelectItem{
-                Expr: &ast.StructLiteral{
-                  Struct: 0,
-                  Lparen: 261,
-                  Rparen: 269,
-                  Fields: []*ast.StructField(nil),
+                Expr: &ast.TypelessStructLiteral{
+                  Struct: 309,
+                  Rparen: 328,
+                  Values: []ast.TypelessStructLiteralArg{
+                    &ast.Alias{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 316,
+                        ValueEnd: 317,
+                        Base:     10,
+                        Value:    "1",
+                      },
+                      As: &ast.AsAlias{
+                        As:    318,
+                        Alias: &ast.Ident{
+                          NamePos: 321,
+                          NameEnd: 322,
+                          Name:    "X",
+                        },
+                      },
+                    },
+                    &ast.ExprArg{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 324,
+                        ValueEnd: 325,
+                        Base:     10,
+                        Value:    "2",
+                      },
+                    },
+                    &ast.ExprArg{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 327,
+                        ValueEnd: 328,
+                        Base:     10,
+                        Value:    "3",
+                      },
+                    },
+                  },
+                },
+              },
+            },
+            From:    (*ast.From)(nil),
+            Where:   (*ast.Where)(nil),
+            GroupBy: (*ast.GroupBy)(nil),
+            Having:  (*ast.Having)(nil),
+            OrderBy: (*ast.OrderBy)(nil),
+            Limit:   (*ast.Limit)(nil),
+          },
+        },
+      },
+      &ast.ExprSelectItem{
+        Expr: &ast.ArraySubQuery{
+          Array:  334,
+          Rparen: 356,
+          Query:  &ast.Select{
+            Select:   340,
+            Distinct: false,
+            As:       nil,
+            Results:  []ast.SelectItem{
+              &ast.ExprSelectItem{
+                Expr: &ast.TupleStructLiteral{
+                  Lparen: 347,
+                  Rparen: 355,
                   Values: []ast.Expr{
                     &ast.IntLiteral{
-                      ValuePos: 262,
-                      ValueEnd: 263,
+                      ValuePos: 348,
+                      ValueEnd: 349,
                       Base:     10,
                       Value:    "1",
                     },
                     &ast.IntLiteral{
-                      ValuePos: 265,
-                      ValueEnd: 266,
+                      ValuePos: 351,
+                      ValueEnd: 352,
                       Base:     10,
                       Value:    "2",
                     },
                     &ast.IntLiteral{
-                      ValuePos: 268,
-                      ValueEnd: 269,
+                      ValuePos: 354,
+                      ValueEnd: 355,
                       Base:     10,
                       Value:    "3",
                     },
@@ -382,19 +526,184 @@ SELECT
       },
       &ast.ExprSelectItem{
         Expr: &ast.ArraySubQuery{
-          Array:  275,
-          Rparen: 298,
+          Array:  361,
+          Rparen: 392,
+          Query:  &ast.Select{
+            Select:   367,
+            Distinct: false,
+            As:       nil,
+            Results:  []ast.SelectItem{
+              &ast.ExprSelectItem{
+                Expr: &ast.TypedStructLiteral{
+                  Struct: 374,
+                  Rparen: 391,
+                  Fields: []*ast.StructField{
+                    &ast.StructField{
+                      Ident: &ast.Ident{
+                        NamePos: 381,
+                        NameEnd: 382,
+                        Name:    "X",
+                      },
+                      Type: &ast.SimpleType{
+                        NamePos: 383,
+                        Name:    "INT64",
+                      },
+                    },
+                  },
+                  Values: []ast.Expr{
+                    &ast.IntLiteral{
+                      ValuePos: 390,
+                      ValueEnd: 391,
+                      Base:     10,
+                      Value:    "1",
+                    },
+                  },
+                },
+              },
+            },
+            From:    (*ast.From)(nil),
+            Where:   (*ast.Where)(nil),
+            GroupBy: (*ast.GroupBy)(nil),
+            Having:  (*ast.Having)(nil),
+            OrderBy: (*ast.OrderBy)(nil),
+            Limit:   (*ast.Limit)(nil),
+          },
+        },
+      },
+      &ast.ExprSelectItem{
+        Expr: &ast.ArraySubQuery{
+          Array:  397,
+          Rparen: 424,
+          Query:  &ast.Select{
+            Select:   403,
+            Distinct: false,
+            As:       nil,
+            Results:  []ast.SelectItem{
+              &ast.ExprSelectItem{
+                Expr: &ast.TypelessStructLiteral{
+                  Struct: 410,
+                  Rparen: 423,
+                  Values: []ast.TypelessStructLiteralArg{
+                    &ast.Alias{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 417,
+                        ValueEnd: 418,
+                        Base:     10,
+                        Value:    "1",
+                      },
+                      As: &ast.AsAlias{
+                        As:    419,
+                        Alias: &ast.Ident{
+                          NamePos: 422,
+                          NameEnd: 423,
+                          Name:    "X",
+                        },
+                      },
+                    },
+                  },
+                },
+              },
+            },
+            From:    (*ast.From)(nil),
+            Where:   (*ast.Where)(nil),
+            GroupBy: (*ast.GroupBy)(nil),
+            Having:  (*ast.Having)(nil),
+            OrderBy: (*ast.OrderBy)(nil),
+            Limit:   (*ast.Limit)(nil),
+          },
+        },
+      },
+      &ast.ExprSelectItem{
+        Expr: &ast.ArraySubQuery{
+          Array:  429,
+          Rparen: 458,
+          Query:  &ast.Select{
+            Select:   435,
+            Distinct: false,
+            As:       nil,
+            Results:  []ast.SelectItem{
+              &ast.ExprSelectItem{
+                Expr: &ast.TypedStructLiteral{
+                  Struct: 442,
+                  Rparen: 457,
+                  Fields: []*ast.StructField{
+                    &ast.StructField{
+                      Ident: (*ast.Ident)(nil),
+                      Type:  &ast.SimpleType{
+                        NamePos: 449,
+                        Name:    "INT64",
+                      },
+                    },
+                  },
+                  Values: []ast.Expr{
+                    &ast.IntLiteral{
+                      ValuePos: 456,
+                      ValueEnd: 457,
+                      Base:     10,
+                      Value:    "1",
+                    },
+                  },
+                },
+              },
+            },
+            From:    (*ast.From)(nil),
+            Where:   (*ast.Where)(nil),
+            GroupBy: (*ast.GroupBy)(nil),
+            Having:  (*ast.Having)(nil),
+            OrderBy: (*ast.OrderBy)(nil),
+            Limit:   (*ast.Limit)(nil),
+          },
+        },
+      },
+      &ast.ExprSelectItem{
+        Expr: &ast.ArraySubQuery{
+          Array:  463,
+          Rparen: 485,
+          Query:  &ast.Select{
+            Select:   469,
+            Distinct: false,
+            As:       nil,
+            Results:  []ast.SelectItem{
+              &ast.ExprSelectItem{
+                Expr: &ast.TypelessStructLiteral{
+                  Struct: 476,
+                  Rparen: 484,
+                  Values: []ast.TypelessStructLiteralArg{
+                    &ast.ExprArg{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 483,
+                        ValueEnd: 484,
+                        Base:     10,
+                        Value:    "1",
+                      },
+                    },
+                  },
+                },
+              },
+            },
+            From:    (*ast.From)(nil),
+            Where:   (*ast.Where)(nil),
+            GroupBy: (*ast.GroupBy)(nil),
+            Having:  (*ast.Having)(nil),
+            OrderBy: (*ast.OrderBy)(nil),
+            Limit:   (*ast.Limit)(nil),
+          },
+        },
+      },
+      &ast.ExprSelectItem{
+        Expr: &ast.ArraySubQuery{
+          Array:  490,
+          Rparen: 513,
           Query:  &ast.Select{
-            Select:   281,
+            Select:   496,
             Distinct: false,
             As:       nil,
             Results:  []ast.SelectItem{
               &ast.ExprSelectItem{
-                Expr: &ast.StructLiteral{
-                  Struct: 288,
-                  Lparen: 296,
-                  Rparen: 297,
-                  Fields: []*ast.StructField{},
+                Expr: &ast.TypedStructLiteral{
+                  Struct: 503,
+                  Rparen: 512,
+                  Fields: []*ast.StructField(nil),
                   Values: []ast.Expr(nil),
                 },
               },
@@ -408,32 +717,56 @@ SELECT
           },
         },
       },
+      &ast.ExprSelectItem{
+        Expr: &ast.ArraySubQuery{
+          Array:  518,
+          Rparen: 539,
+          Query:  &ast.Select{
+            Select:   524,
+            Distinct: false,
+            As:       nil,
+            Results:  []ast.SelectItem{
+              &ast.ExprSelectItem{
+                Expr: &ast.TypelessStructLiteral{
+                  Struct: 531,
+                  Rparen: 538,
+                  Values: []ast.TypelessStructLiteralArg(nil),
+                },
+              },
+            },
+            From:    (*ast.From)(nil),
+            Where:   (*ast.Where)(nil),
+            GroupBy: (*ast.GroupBy)(nil),
+            Having:  (*ast.Having)(nil),
+            OrderBy: (*ast.OrderBy)(nil),
+            Limit:   (*ast.Limit)(nil),
+          },
+        },
+      },
       &ast.ExprSelectItem{
         Expr: &ast.ExistsSubQuery{
-          Exists: 303,
-          Rparen: 323,
+          Exists: 544,
+          Rparen: 564,
           Hint:   (*ast.Hint)(nil),
           Query:  &ast.Select{
-            Select:   310,
+            Select:   551,
             Distinct: false,
             As:       nil,
             Results:  []ast.SelectItem{
               &ast.ExprSelectItem{
-                Expr: &ast.StructLiteral{
-                  Struct: 0,
-                  Lparen: 317,
-                  Rparen: 322,
-                  Fields: []*ast.StructField(nil),
+                Expr: &ast.TupleStructLiteral{
+                  Lparen: 558,
+                  Rparen: 563,
                   Values: []ast.Expr{
                     &ast.IntLiteral{
-                      ValuePos: 318,
-                      ValueEnd: 319,
+                      ValuePos: 559,
+                      ValueEnd: 560,
                       Base:     10,
                       Value:    "1",
                     },
                     &ast.IntLiteral{
-                      ValuePos: 321,
-                      ValueEnd: 322,
+                      ValuePos: 562,
+                      ValueEnd: 563,
                       Base:     10,
                       Value:    "2",
                     },
@@ -461,4 +794,4 @@ SELECT
 }
 
 --- SQL
-SELECT ARRAY(SELECT STRUCT(1, 2, 3)), ARRAY(SELECT STRUCT(1, 2, TRUE)), ARRAY(SELECT STRUCT<X INT64, Y INT64, Z INT64>(1, 2, 3)), ARRAY(SELECT STRUCT<INT64, INT64, INT64>(1, 2, 3)), ARRAY(SELECT STRUCT<X INT64, INT64, INT64>(1, 2, 3)), ARRAY(SELECT STRUCT(1, 2, 3)), ARRAY(SELECT STRUCT<>()), EXISTS(SELECT STRUCT(1, 2))
+SELECT ARRAY(SELECT STRUCT(1, 2, 3)), ARRAY(SELECT STRUCT(1, 2, TRUE)), ARRAY(SELECT STRUCT<X INT64, Y INT64, Z INT64>(1, 2, 3)), ARRAY(SELECT STRUCT(1 AS X, 2 AS Y, 3 AS Z)), ARRAY(SELECT STRUCT<INT64, INT64, INT64>(1, 2, 3)), ARRAY(SELECT STRUCT<X INT64, INT64, INT64>(1, 2, 3)), ARRAY(SELECT STRUCT(1 AS X, 2, 3)), ARRAY(SELECT (1, 2, 3)), ARRAY(SELECT STRUCT<X INT64>(1)), ARRAY(SELECT STRUCT(1 AS X)), ARRAY(SELECT STRUCT<INT64>(1)), ARRAY(SELECT STRUCT(1)), ARRAY(SELECT STRUCT<>()), ARRAY(SELECT STRUCT()), EXISTS(SELECT (1, 2))
diff --git a/testdata/result/query/select_nest_complex.sql.txt b/testdata/result/query/select_nest_complex.sql.txt
index e328f52..8105941 100644
--- a/testdata/result/query/select_nest_complex.sql.txt
+++ b/testdata/result/query/select_nest_complex.sql.txt
@@ -58,7 +58,7 @@ from (
                                 Value:    "1",
                               },
                               As: &ast.AsAlias{
-                                As:    32,
+                                As:    -1,
                                 Alias: &ast.Ident{
                                   NamePos: 32,
                                   NameEnd: 33,
@@ -170,7 +170,7 @@ from (
                     Value:    "1",
                   },
                   As: &ast.AsAlias{
-                    As:    113,
+                    As:    -1,
                     Alias: &ast.Ident{
                       NamePos: 113,
                       NameEnd: 114,
@@ -186,7 +186,7 @@ from (
                     Value:    "2",
                   },
                   As: &ast.AsAlias{
-                    As:    118,
+                    As:    -1,
                     Alias: &ast.Ident{
                       NamePos: 118,
                       NameEnd: 119,
@@ -229,4 +229,4 @@ from (
 }
 
 --- SQL
-SELECT * FROM ((((SELECT 1 AS A UNION ALL (SELECT 2)) UNION DISTINCT (SELECT 1)) LIMIT 1) INNER JOIN (SELECT 1 AS A, 2 AS B) USING (A))
+SELECT * FROM ((((SELECT 1 A UNION ALL (SELECT 2)) UNION DISTINCT (SELECT 1)) LIMIT 1) INNER JOIN (SELECT 1 A, 2 B) USING (A))
diff --git a/testdata/result/query/select_singer_with_column_names.sql.txt b/testdata/result/query/select_singer_with_column_names.sql.txt
index 2b8d7c1..7bef784 100644
--- a/testdata/result/query/select_singer_with_column_names.sql.txt
+++ b/testdata/result/query/select_singer_with_column_names.sql.txt
@@ -23,7 +23,7 @@ FROM Singers
           Name:    "SingerId",
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    18,
           Alias: &ast.Ident{
             NamePos: 21,
             NameEnd: 23,
diff --git a/testdata/result/query/select_singer_with_cross_join.sql.txt b/testdata/result/query/select_singer_with_cross_join.sql.txt
index b6b1637..f7be0a4 100644
--- a/testdata/result/query/select_singer_with_cross_join.sql.txt
+++ b/testdata/result/query/select_singer_with_cross_join.sql.txt
@@ -33,7 +33,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    26,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 26,
               NameEnd: 27,
@@ -50,7 +50,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    51,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 51,
               NameEnd: 52,
@@ -71,4 +71,4 @@ FROM
 }
 
 --- SQL
-SELECT * FROM Singers AS A CROSS JOIN Singers AS B
+SELECT * FROM Singers A CROSS JOIN Singers B
diff --git a/testdata/result/query/select_singer_with_full_join.sql.txt b/testdata/result/query/select_singer_with_full_join.sql.txt
index 31d9664..8e36402 100644
--- a/testdata/result/query/select_singer_with_full_join.sql.txt
+++ b/testdata/result/query/select_singer_with_full_join.sql.txt
@@ -32,7 +32,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    -1,
+            As:    26,
             Alias: &ast.Ident{
               NamePos: 29,
               NameEnd: 30,
@@ -49,7 +49,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    -1,
+            As:    42,
             Alias: &ast.Ident{
               NamePos: 45,
               NameEnd: 46,
diff --git a/testdata/result/query/select_singer_with_hash_join.sql.txt b/testdata/result/query/select_singer_with_hash_join.sql.txt
index c80ffc1..6a385ba 100644
--- a/testdata/result/query/select_singer_with_hash_join.sql.txt
+++ b/testdata/result/query/select_singer_with_hash_join.sql.txt
@@ -48,7 +48,7 @@ FROM
               },
               Hint: (*ast.Hint)(nil),
               As:   &ast.AsAlias{
-                As:    26,
+                As:    -1,
                 Alias: &ast.Ident{
                   NamePos: 26,
                   NameEnd: 27,
@@ -65,7 +65,7 @@ FROM
               },
               Hint: (*ast.Hint)(nil),
               As:   &ast.AsAlias{
-                As:    50,
+                As:    -1,
                 Alias: &ast.Ident{
                   NamePos: 50,
                   NameEnd: 51,
@@ -117,7 +117,7 @@ FROM
             },
             Hint: (*ast.Hint)(nil),
             As:   &ast.AsAlias{
-              As:    103,
+              As:    -1,
               Alias: &ast.Ident{
                 NamePos: 103,
                 NameEnd: 104,
@@ -169,7 +169,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    155,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 155,
               NameEnd: 156,
@@ -223,4 +223,4 @@ FROM
 }
 
 --- SQL
-SELECT * FROM Singers AS A INNER JOIN Singers AS B ON A.SingerID = B.SingerID INNER JOIN Singer AS C ON B.SingerID = C.SingerID INNER JOIN Singer AS D ON C.SingerID = D.SingerID
+SELECT * FROM Singers A INNER JOIN Singers B ON A.SingerID = B.SingerID INNER JOIN Singer C ON B.SingerID = C.SingerID INNER JOIN Singer D ON C.SingerID = D.SingerID
diff --git a/testdata/result/query/select_singer_with_join.sql.txt b/testdata/result/query/select_singer_with_join.sql.txt
index be653b6..ab573b3 100644
--- a/testdata/result/query/select_singer_with_join.sql.txt
+++ b/testdata/result/query/select_singer_with_join.sql.txt
@@ -34,7 +34,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    26,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 26,
               NameEnd: 27,
@@ -51,7 +51,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    56,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 56,
               NameEnd: 57,
@@ -105,4 +105,4 @@ FROM
 }
 
 --- SQL
-SELECT * FROM Singers AS A LEFT OUTER JOIN Singers AS B ON A.SingerID = B.SingerID
+SELECT * FROM Singers A LEFT OUTER JOIN Singers B ON A.SingerID = B.SingerID
diff --git a/testdata/result/query/select_singer_with_join_hint.sql.txt b/testdata/result/query/select_singer_with_join_hint.sql.txt
index 46983dc..92a14a8 100644
--- a/testdata/result/query/select_singer_with_join_hint.sql.txt
+++ b/testdata/result/query/select_singer_with_join_hint.sql.txt
@@ -122,7 +122,7 @@ FROM
                 },
                 Hint: (*ast.Hint)(nil),
                 As:   &ast.AsAlias{
-                  As:    26,
+                  As:    -1,
                   Alias: &ast.Ident{
                     NamePos: 26,
                     NameEnd: 27,
@@ -139,7 +139,7 @@ FROM
                 },
                 Hint: (*ast.Hint)(nil),
                 As:   &ast.AsAlias{
-                  As:    80,
+                  As:    -1,
                   Alias: &ast.Ident{
                     NamePos: 80,
                     NameEnd: 81,
@@ -191,7 +191,7 @@ FROM
               },
               Hint: (*ast.Hint)(nil),
               As:   &ast.AsAlias{
-                As:    150,
+                As:    -1,
                 Alias: &ast.Ident{
                   NamePos: 150,
                   NameEnd: 151,
@@ -243,7 +243,7 @@ FROM
             },
             Hint: (*ast.Hint)(nil),
             As:   &ast.AsAlias{
-              As:    221,
+              As:    -1,
               Alias: &ast.Ident{
                 NamePos: 221,
                 NameEnd: 222,
@@ -295,7 +295,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    291,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 291,
               NameEnd: 292,
@@ -349,4 +349,4 @@ FROM
 }
 
 --- SQL
-SELECT * FROM Singers AS A LEFT OUTER JOIN @{FORCE_JOIN_ORDER=TRUE} Singers AS B ON A.SingerID = B.SingerID INNER JOIN @{JOIN_TYPE=HASH_JOIN} Singers AS C ON A.SingerID = C.SingerID INNER JOIN @{JOIN_TYPE=APPLY_JOIN} Singers AS D ON A.SingerID = D.SingerID INNER JOIN @{JOIN_TYPE=LOOP_JOIN} Singers AS E ON A.SingerID = E.SingerID
+SELECT * FROM Singers A LEFT OUTER JOIN @{FORCE_JOIN_ORDER=TRUE} Singers B ON A.SingerID = B.SingerID INNER JOIN @{JOIN_TYPE=HASH_JOIN} Singers C ON A.SingerID = C.SingerID INNER JOIN @{JOIN_TYPE=APPLY_JOIN} Singers D ON A.SingerID = D.SingerID INNER JOIN @{JOIN_TYPE=LOOP_JOIN} Singers E ON A.SingerID = E.SingerID
diff --git a/testdata/result/query/select_singer_with_join_twice.sql.txt b/testdata/result/query/select_singer_with_join_twice.sql.txt
index b7d0982..035780a 100644
--- a/testdata/result/query/select_singer_with_join_twice.sql.txt
+++ b/testdata/result/query/select_singer_with_join_twice.sql.txt
@@ -41,7 +41,7 @@ FROM
             },
             Hint: (*ast.Hint)(nil),
             As:   &ast.AsAlias{
-              As:    26,
+              As:    -1,
               Alias: &ast.Ident{
                 NamePos: 26,
                 NameEnd: 27,
@@ -58,7 +58,7 @@ FROM
             },
             Hint: (*ast.Hint)(nil),
             As:   &ast.AsAlias{
-              As:    45,
+              As:    -1,
               Alias: &ast.Ident{
                 NamePos: 45,
                 NameEnd: 46,
@@ -110,7 +110,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    99,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 99,
               NameEnd: 100,
@@ -164,4 +164,4 @@ FROM
 }
 
 --- SQL
-SELECT * FROM Singers AS A INNER JOIN Singers AS B ON A.SingerID = B.SingerID INNER JOIN Singers AS C ON A.SingerID = C.SingerID
+SELECT * FROM Singers A INNER JOIN Singers B ON A.SingerID = B.SingerID INNER JOIN Singers C ON A.SingerID = C.SingerID
diff --git a/testdata/result/query/select_singer_with_join_using.sql.txt b/testdata/result/query/select_singer_with_join_using.sql.txt
index 61f23fe..e37df1c 100644
--- a/testdata/result/query/select_singer_with_join_using.sql.txt
+++ b/testdata/result/query/select_singer_with_join_using.sql.txt
@@ -34,7 +34,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    26,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 26,
               NameEnd: 27,
@@ -51,7 +51,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    56,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 56,
               NameEnd: 57,
@@ -87,4 +87,4 @@ FROM
 }
 
 --- SQL
-SELECT * FROM Singers AS A LEFT OUTER JOIN Singers AS B USING (SingerID, FirstName)
+SELECT * FROM Singers A LEFT OUTER JOIN Singers B USING (SingerID, FirstName)
diff --git a/testdata/result/query/select_singer_with_join_various.sql.txt b/testdata/result/query/select_singer_with_join_various.sql.txt
index 823859c..45d847b 100644
--- a/testdata/result/query/select_singer_with_join_various.sql.txt
+++ b/testdata/result/query/select_singer_with_join_various.sql.txt
@@ -89,7 +89,7 @@ FROM
                           },
                           Hint: (*ast.Hint)(nil),
                           As:   &ast.AsAlias{
-                            As:    26,
+                            As:    -1,
                             Alias: &ast.Ident{
                               NamePos: 26,
                               NameEnd: 27,
@@ -106,7 +106,7 @@ FROM
                           },
                           Hint: (*ast.Hint)(nil),
                           As:   &ast.AsAlias{
-                            As:    45,
+                            As:    -1,
                             Alias: &ast.Ident{
                               NamePos: 45,
                               NameEnd: 46,
@@ -158,7 +158,7 @@ FROM
                         },
                         Hint: (*ast.Hint)(nil),
                         As:   &ast.AsAlias{
-                          As:    99,
+                          As:    -1,
                           Alias: &ast.Ident{
                             NamePos: 99,
                             NameEnd: 100,
@@ -210,7 +210,7 @@ FROM
                       },
                       Hint: (*ast.Hint)(nil),
                       As:   &ast.AsAlias{
-                        As:    153,
+                        As:    -1,
                         Alias: &ast.Ident{
                           NamePos: 153,
                           NameEnd: 154,
@@ -229,7 +229,7 @@ FROM
                     },
                     Hint: (*ast.Hint)(nil),
                     As:   &ast.AsAlias{
-                      As:    177,
+                      As:    -1,
                       Alias: &ast.Ident{
                         NamePos: 177,
                         NameEnd: 178,
@@ -281,7 +281,7 @@ FROM
                   },
                   Hint: (*ast.Hint)(nil),
                   As:   &ast.AsAlias{
-                    As:    236,
+                    As:    -1,
                     Alias: &ast.Ident{
                       NamePos: 236,
                       NameEnd: 237,
@@ -333,7 +333,7 @@ FROM
                 },
                 Hint: (*ast.Hint)(nil),
                 As:   &ast.AsAlias{
-                  As:    289,
+                  As:    -1,
                   Alias: &ast.Ident{
                     NamePos: 289,
                     NameEnd: 290,
@@ -385,7 +385,7 @@ FROM
               },
               Hint: (*ast.Hint)(nil),
               As:   &ast.AsAlias{
-                As:    348,
+                As:    -1,
                 Alias: &ast.Ident{
                   NamePos: 348,
                   NameEnd: 349,
@@ -437,7 +437,7 @@ FROM
             },
             Hint: (*ast.Hint)(nil),
             As:   &ast.AsAlias{
-              As:    402,
+              As:    -1,
               Alias: &ast.Ident{
                 NamePos: 402,
                 NameEnd: 403,
@@ -489,7 +489,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    462,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 462,
               NameEnd: 463,
@@ -543,4 +543,4 @@ FROM
 }
 
 --- SQL
-SELECT * FROM Singers AS A INNER JOIN Singers AS B ON A.SingerID = B.SingerID INNER JOIN Singers AS C ON A.SingerID = C.SingerID CROSS JOIN Singers AS D FULL OUTER JOIN Singers AS E ON A.SingerID = E.SingerID FULL OUTER JOIN Singers AS F ON A.SingerID = F.SingerID LEFT OUTER JOIN Singers AS G ON A.SingerID = G.SingerID LEFT OUTER JOIN Singers AS H ON A.SingerID = H.SingerID RIGHT OUTER JOIN Singers AS I ON A.SingerID = I.SingerID RIGHT OUTER JOIN Singers AS J ON A.SingerID = J.SingerID
+SELECT * FROM Singers A INNER JOIN Singers B ON A.SingerID = B.SingerID INNER JOIN Singers C ON A.SingerID = C.SingerID CROSS JOIN Singers D FULL OUTER JOIN Singers E ON A.SingerID = E.SingerID FULL OUTER JOIN Singers F ON A.SingerID = F.SingerID LEFT OUTER JOIN Singers G ON A.SingerID = G.SingerID LEFT OUTER JOIN Singers H ON A.SingerID = H.SingerID RIGHT OUTER JOIN Singers I ON A.SingerID = I.SingerID RIGHT OUTER JOIN Singers J ON A.SingerID = J.SingerID
diff --git a/testdata/result/query/select_singer_with_select_in_from_and_as.sql.txt b/testdata/result/query/select_singer_with_select_in_from_and_as.sql.txt
index a081c2e..fda2490 100644
--- a/testdata/result/query/select_singer_with_select_in_from_and_as.sql.txt
+++ b/testdata/result/query/select_singer_with_select_in_from_and_as.sql.txt
@@ -73,7 +73,7 @@ FROM (
           Limit:   (*ast.Limit)(nil),
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    79,
           Alias: &ast.Ident{
             NamePos: 82,
             NameEnd: 83,
diff --git a/testdata/result/query/select_singer_with_single_column_subquery_with_at.sql.txt b/testdata/result/query/select_singer_with_single_column_subquery_with_at.sql.txt
index ccbe817..c420f98 100644
--- a/testdata/result/query/select_singer_with_single_column_subquery_with_at.sql.txt
+++ b/testdata/result/query/select_singer_with_single_column_subquery_with_at.sql.txt
@@ -60,7 +60,7 @@ SELECT (
           },
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    55,
           Alias: &ast.Ident{
             NamePos: 58,
             NameEnd: 60,
diff --git a/testdata/result/query/select_singer_with_table_alias.sql.txt b/testdata/result/query/select_singer_with_table_alias.sql.txt
index a491a94..190521f 100644
--- a/testdata/result/query/select_singer_with_table_alias.sql.txt
+++ b/testdata/result/query/select_singer_with_table_alias.sql.txt
@@ -38,7 +38,7 @@ FROM Singers S
           },
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    27,
           Alias: &ast.Ident{
             NamePos: 30,
             NameEnd: 32,
@@ -73,7 +73,7 @@ FROM Singers S
         },
         Hint: (*ast.Hint)(nil),
         As:   &ast.AsAlias{
-          As:    61,
+          As:    -1,
           Alias: &ast.Ident{
             NamePos: 61,
             NameEnd: 62,
@@ -92,4 +92,4 @@ FROM Singers S
 }
 
 --- SQL
-SELECT S.*, S.SingerId AS ID, S.FirstName FROM Singers AS S
+SELECT S.*, S.SingerId AS ID, S.FirstName FROM Singers S
diff --git a/testdata/result/query/select_singer_with_table_alias_with_hint.sql.txt b/testdata/result/query/select_singer_with_table_alias_with_hint.sql.txt
index 3c85380..3d72f5b 100644
--- a/testdata/result/query/select_singer_with_table_alias_with_hint.sql.txt
+++ b/testdata/result/query/select_singer_with_table_alias_with_hint.sql.txt
@@ -44,7 +44,7 @@ FROM
           },
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    63,
           Alias: &ast.Ident{
             NamePos: 66,
             NameEnd: 67,
diff --git a/testdata/result/query/select_singer_with_tablesample.sql.txt b/testdata/result/query/select_singer_with_tablesample.sql.txt
index bcbd612..f333017 100644
--- a/testdata/result/query/select_singer_with_tablesample.sql.txt
+++ b/testdata/result/query/select_singer_with_tablesample.sql.txt
@@ -37,7 +37,7 @@ FROM
             },
             Hint: (*ast.Hint)(nil),
             As:   &ast.AsAlias{
-              As:    -1,
+              As:    26,
               Alias: &ast.Ident{
                 NamePos: 29,
                 NameEnd: 30,
@@ -68,7 +68,7 @@ FROM
             },
             Hint: (*ast.Hint)(nil),
             As:   &ast.AsAlias{
-              As:    -1,
+              As:    75,
               Alias: &ast.Ident{
                 NamePos: 78,
                 NameEnd: 79,
@@ -100,7 +100,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    -1,
+            As:    127,
             Alias: &ast.Ident{
               NamePos: 130,
               NameEnd: 131,
diff --git a/testdata/result/query/select_singer_with_toplevel_join_hint.sql.txt b/testdata/result/query/select_singer_with_toplevel_join_hint.sql.txt
index 2dba175..450aca2 100644
--- a/testdata/result/query/select_singer_with_toplevel_join_hint.sql.txt
+++ b/testdata/result/query/select_singer_with_toplevel_join_hint.sql.txt
@@ -51,7 +51,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    51,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 51,
               NameEnd: 52,
@@ -68,7 +68,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    81,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 81,
               NameEnd: 82,
@@ -122,4 +122,4 @@ FROM
 }
 
 --- SQL
-@{FORCE_JOIN_ORDER=TRUE} SELECT * FROM Singers AS A LEFT OUTER JOIN Singers AS B ON A.SingerID = B.SingerID
+@{FORCE_JOIN_ORDER=TRUE} SELECT * FROM Singers A LEFT OUTER JOIN Singers B ON A.SingerID = B.SingerID
diff --git a/testdata/result/query/select_struct_compare_eq.sql.txt b/testdata/result/query/select_struct_compare_eq.sql.txt
index 0ecdcbf..ff3acbd 100644
--- a/testdata/result/query/select_struct_compare_eq.sql.txt
+++ b/testdata/result/query/select_struct_compare_eq.sql.txt
@@ -40,9 +40,8 @@ SELECT ARRAY(
                   As:       nil,
                   Results:  []ast.SelectItem{
                     &ast.ExprSelectItem{
-                      Expr: &ast.StructLiteral{
+                      Expr: &ast.TypedStructLiteral{
                         Struct: 48,
-                        Lparen: 81,
                         Rparen: 89,
                         Fields: []*ast.StructField{
                           &ast.StructField{
@@ -110,7 +109,7 @@ SELECT ARRAY(
                   Limit:   (*ast.Limit)(nil),
                 },
                 As: &ast.AsAlias{
-                  As:    -1,
+                  As:    92,
                   Alias: &ast.Ident{
                     NamePos: 95,
                     NameEnd: 96,
@@ -129,35 +128,39 @@ SELECT ARRAY(
                   NameEnd: 106,
                   Name:    "S",
                 },
-                Right: &ast.StructLiteral{
+                Right: &ast.TypelessStructLiteral{
                   Struct: 109,
-                  Lparen: 115,
                   Rparen: 131,
-                  Fields: []*ast.StructField(nil),
-                  Values: []ast.Expr{
-                    &ast.StructLiteral{
-                      Struct: 116,
-                      Lparen: 122,
-                      Rparen: 130,
-                      Fields: []*ast.StructField(nil),
-                      Values: []ast.Expr{
-                        &ast.IntLiteral{
-                          ValuePos: 123,
-                          ValueEnd: 124,
-                          Base:     10,
-                          Value:    "1",
-                        },
-                        &ast.IntLiteral{
-                          ValuePos: 126,
-                          ValueEnd: 127,
-                          Base:     10,
-                          Value:    "2",
-                        },
-                        &ast.IntLiteral{
-                          ValuePos: 129,
-                          ValueEnd: 130,
-                          Base:     10,
-                          Value:    "3",
+                  Values: []ast.TypelessStructLiteralArg{
+                    &ast.ExprArg{
+                      Expr: &ast.TypelessStructLiteral{
+                        Struct: 116,
+                        Rparen: 130,
+                        Values: []ast.TypelessStructLiteralArg{
+                          &ast.ExprArg{
+                            Expr: &ast.IntLiteral{
+                              ValuePos: 123,
+                              ValueEnd: 124,
+                              Base:     10,
+                              Value:    "1",
+                            },
+                          },
+                          &ast.ExprArg{
+                            Expr: &ast.IntLiteral{
+                              ValuePos: 126,
+                              ValueEnd: 127,
+                              Base:     10,
+                              Value:    "2",
+                            },
+                          },
+                          &ast.ExprArg{
+                            Expr: &ast.IntLiteral{
+                              ValuePos: 129,
+                              ValueEnd: 130,
+                              Base:     10,
+                              Value:    "3",
+                            },
+                          },
                         },
                       },
                     },
diff --git a/testdata/result/query/select_tablesample_with_cross_join.sql.txt b/testdata/result/query/select_tablesample_with_cross_join.sql.txt
index bf654a1..b1e29e4 100644
--- a/testdata/result/query/select_tablesample_with_cross_join.sql.txt
+++ b/testdata/result/query/select_tablesample_with_cross_join.sql.txt
@@ -34,7 +34,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    26,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 26,
               NameEnd: 27,
@@ -51,7 +51,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    51,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 51,
               NameEnd: 52,
@@ -86,4 +86,4 @@ FROM
 }
 
 --- SQL
-SELECT * FROM Singers AS A CROSS JOIN Singers AS B TABLESAMPLE BERNOULLI (90 PERCENT)
+SELECT * FROM Singers A CROSS JOIN Singers B TABLESAMPLE BERNOULLI (90 PERCENT)
diff --git a/testdata/result/query/select_tablesample_with_table_alias.sql.txt b/testdata/result/query/select_tablesample_with_table_alias.sql.txt
index 1f99e18..102ff2e 100644
--- a/testdata/result/query/select_tablesample_with_table_alias.sql.txt
+++ b/testdata/result/query/select_tablesample_with_table_alias.sql.txt
@@ -27,7 +27,7 @@ FROM
         },
         Hint: (*ast.Hint)(nil),
         As:   &ast.AsAlias{
-          As:    -1,
+          As:    26,
           Alias: &ast.Ident{
             NamePos: 29,
             NameEnd: 30,
diff --git a/testdata/result/query/select_tablesample_with_unnest_invalid.sql.txt b/testdata/result/query/select_tablesample_with_unnest_invalid.sql.txt
index 397274f..44d34a8 100644
--- a/testdata/result/query/select_tablesample_with_unnest_invalid.sql.txt
+++ b/testdata/result/query/select_tablesample_with_unnest_invalid.sql.txt
@@ -55,11 +55,9 @@ FROM
             },
           },
           Values: []ast.Expr{
-            &ast.StructLiteral{
-              Struct: 0,
+            &ast.TupleStructLiteral{
               Lparen: 58,
               Rparen: 67,
-              Fields: []*ast.StructField(nil),
               Values: []ast.Expr{
                 &ast.IntLiteral{
                   ValuePos: 59,
@@ -74,11 +72,9 @@ FROM
                 },
               },
             },
-            &ast.StructLiteral{
-              Struct: 0,
+            &ast.TupleStructLiteral{
               Lparen: 70,
               Rparen: 79,
-              Fields: []*ast.StructField(nil),
               Values: []ast.Expr{
                 &ast.IntLiteral{
                   ValuePos: 71,
@@ -124,4 +120,4 @@ FROM
 }
 
 --- SQL
-SELECT * FROM UNNEST(ARRAY<STRUCT<x INT64, y STRING>>[STRUCT(1, "foo"), STRUCT(3, "bar")]) TABLESAMPLE BERNOULLI (90 PERCENT)
+SELECT * FROM UNNEST(ARRAY<STRUCT<x INT64, y STRING>>[(1, "foo"), (3, "bar")]) TABLESAMPLE BERNOULLI (90 PERCENT)
diff --git a/testdata/result/query/select_unnest_with_offset_and_alias.sql.txt b/testdata/result/query/select_unnest_with_offset_and_alias.sql.txt
index ecedf6a..3b88c15 100644
--- a/testdata/result/query/select_unnest_with_offset_and_alias.sql.txt
+++ b/testdata/result/query/select_unnest_with_offset_and_alias.sql.txt
@@ -50,7 +50,7 @@ FROM
         },
         Hint: (*ast.Hint)(nil),
         As:   &ast.AsAlias{
-          As:    -1,
+          As:    34,
           Alias: &ast.Ident{
             NamePos: 37,
             NameEnd: 42,
@@ -61,7 +61,7 @@ FROM
           With:   43,
           Offset: 48,
           As:     &ast.AsAlias{
-            As:    -1,
+            As:    55,
             Alias: &ast.Ident{
               NamePos: 58,
               NameEnd: 61,
diff --git a/testdata/result/query/select_unnest_with_offset_and_alias_min.sql.txt b/testdata/result/query/select_unnest_with_offset_and_alias_min.sql.txt
index b8d5790..684b111 100644
--- a/testdata/result/query/select_unnest_with_offset_and_alias_min.sql.txt
+++ b/testdata/result/query/select_unnest_with_offset_and_alias_min.sql.txt
@@ -50,7 +50,7 @@ FROM
         },
         Hint: (*ast.Hint)(nil),
         As:   &ast.AsAlias{
-          As:    34,
+          As:    -1,
           Alias: &ast.Ident{
             NamePos: 34,
             NameEnd: 39,
@@ -61,7 +61,7 @@ FROM
           With:   40,
           Offset: 45,
           As:     &ast.AsAlias{
-            As:    52,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 52,
               NameEnd: 55,
@@ -81,4 +81,4 @@ FROM
 }
 
 --- SQL
-SELECT * FROM UNNEST([1, 2, 3]) AS value WITH OFFSET AS num
+SELECT * FROM UNNEST([1, 2, 3]) value WITH OFFSET num
diff --git a/testdata/result/query/select_with_field_path.sql.txt b/testdata/result/query/select_with_field_path.sql.txt
index e977ad1..fb07732 100644
--- a/testdata/result/query/select_with_field_path.sql.txt
+++ b/testdata/result/query/select_with_field_path.sql.txt
@@ -212,11 +212,9 @@ WHERE A.z.a = 2
                     },
                   },
                   Values: []ast.Expr{
-                    &ast.StructLiteral{
-                      Struct: 0,
+                    &ast.TupleStructLiteral{
                       Lparen: 206,
                       Rparen: 223,
-                      Fields: []*ast.StructField(nil),
                       Values: []ast.Expr{
                         &ast.IntLiteral{
                           ValuePos: 207,
@@ -229,11 +227,9 @@ WHERE A.z.a = 2
                           ValueEnd: 215,
                           Value:    "foo",
                         },
-                        &ast.StructLiteral{
-                          Struct: 0,
+                        &ast.TupleStructLiteral{
                           Lparen: 217,
                           Rparen: 222,
-                          Fields: []*ast.StructField(nil),
                           Values: []ast.Expr{
                             &ast.IntLiteral{
                               ValuePos: 218,
@@ -251,11 +247,9 @@ WHERE A.z.a = 2
                         },
                       },
                     },
-                    &ast.StructLiteral{
-                      Struct: 0,
+                    &ast.TupleStructLiteral{
                       Lparen: 226,
                       Rparen: 243,
-                      Fields: []*ast.StructField(nil),
                       Values: []ast.Expr{
                         &ast.IntLiteral{
                           ValuePos: 227,
@@ -268,11 +262,9 @@ WHERE A.z.a = 2
                           ValueEnd: 235,
                           Value:    "bar",
                         },
-                        &ast.StructLiteral{
-                          Struct: 0,
+                        &ast.TupleStructLiteral{
                           Lparen: 237,
                           Rparen: 242,
-                          Fields: []*ast.StructField(nil),
                           Values: []ast.Expr{
                             &ast.IntLiteral{
                               ValuePos: 238,
@@ -307,7 +299,7 @@ WHERE A.z.a = 2
         },
         Hint: (*ast.Hint)(nil),
         As:   &ast.AsAlias{
-          As:    -1,
+          As:    257,
           Alias: &ast.Ident{
             NamePos: 260,
             NameEnd: 261,
@@ -357,4 +349,4 @@ WHERE A.z.a = 2
 }
 
 --- SQL
-SELECT A.x, A.y, A.z.a, A.z.b FROM UNNEST(ARRAY(SELECT AS STRUCT x, y, z FROM UNNEST(ARRAY<STRUCT<x INT64, y STRING, z STRUCT<a INT64, b INT64>>>[STRUCT(1, "foo", STRUCT(2, 3)), STRUCT(3, "bar", STRUCT(4, 5))]))) AS A WHERE A.z.a = 2
+SELECT A.x, A.y, A.z.a, A.z.b FROM UNNEST(ARRAY(SELECT AS STRUCT x, y, z FROM UNNEST(ARRAY<STRUCT<x INT64, y STRING, z STRUCT<a INT64, b INT64>>>[(1, "foo", (2, 3)), (3, "bar", (4, 5))]))) AS A WHERE A.z.a = 2
diff --git a/testdata/result/query/select_with_sequence_function.sql.txt b/testdata/result/query/select_with_sequence_function.sql.txt
index 491d47f..333ec82 100644
--- a/testdata/result/query/select_with_sequence_function.sql.txt
+++ b/testdata/result/query/select_with_sequence_function.sql.txt
@@ -34,7 +34,7 @@ SELECT GET_NEXT_SEQUENCE_VALUE(SEQUENCE my_sequence) as next_id
           Having:       nil,
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    53,
           Alias: &ast.Ident{
             NamePos: 56,
             NameEnd: 63,
diff --git a/testdata/result/statement/create_or_replace_view.sql.txt b/testdata/result/statement/create_or_replace_view.sql.txt
index 4a611ad..f3b3880 100644
--- a/testdata/result/statement/create_or_replace_view.sql.txt
+++ b/testdata/result/statement/create_or_replace_view.sql.txt
@@ -37,7 +37,7 @@ from singers
           },
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    87,
           Alias: &ast.Ident{
             NamePos: 90,
             NameEnd: 98,
@@ -86,7 +86,7 @@ from singers
           },
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    149,
           Alias: &ast.Ident{
             NamePos: 152,
             NameEnd: 156,
diff --git a/testdata/result/statement/create_schema.sql.txt b/testdata/result/statement/create_schema.sql.txt
new file mode 100644
index 0000000..2b04f8f
--- /dev/null
+++ b/testdata/result/statement/create_schema.sql.txt
@@ -0,0 +1,14 @@
+--- create_schema.sql
+CREATE SCHEMA sch1
+--- AST
+&ast.CreateSchema{
+  Create: 0,
+  Name:   &ast.Ident{
+    NamePos: 14,
+    NameEnd: 18,
+    Name:    "sch1",
+  },
+}
+
+--- SQL
+CREATE SCHEMA sch1
diff --git a/testdata/result/statement/create_view.sql.txt b/testdata/result/statement/create_view.sql.txt
index da8ea65..e96ff6c 100644
--- a/testdata/result/statement/create_view.sql.txt
+++ b/testdata/result/statement/create_view.sql.txt
@@ -37,7 +37,7 @@ from singers
           },
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    76,
           Alias: &ast.Ident{
             NamePos: 79,
             NameEnd: 87,
@@ -86,7 +86,7 @@ from singers
           },
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    138,
           Alias: &ast.Ident{
             NamePos: 141,
             NameEnd: 145,
diff --git a/testdata/result/statement/create_view_sql_security_definer.sql.txt b/testdata/result/statement/create_view_sql_security_definer.sql.txt
index 5627d9c..6d134f8 100644
--- a/testdata/result/statement/create_view_sql_security_definer.sql.txt
+++ b/testdata/result/statement/create_view_sql_security_definer.sql.txt
@@ -37,7 +37,7 @@ from singers
           },
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    76,
           Alias: &ast.Ident{
             NamePos: 79,
             NameEnd: 87,
@@ -86,7 +86,7 @@ from singers
           },
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    138,
           Alias: &ast.Ident{
             NamePos: 141,
             NameEnd: 145,
diff --git a/testdata/result/statement/delete_as.sql.txt b/testdata/result/statement/delete_as.sql.txt
index d368be5..2eee871 100644
--- a/testdata/result/statement/delete_as.sql.txt
+++ b/testdata/result/statement/delete_as.sql.txt
@@ -9,7 +9,7 @@ delete foo as F where F.foo = 1
     Name:    "foo",
   },
   As: &ast.AsAlias{
-    As:    -1,
+    As:    11,
     Alias: &ast.Ident{
       NamePos: 14,
       NameEnd: 15,
diff --git a/testdata/result/statement/drop_schema.sql.txt b/testdata/result/statement/drop_schema.sql.txt
new file mode 100644
index 0000000..b935680
--- /dev/null
+++ b/testdata/result/statement/drop_schema.sql.txt
@@ -0,0 +1,14 @@
+--- drop_schema.sql
+DROP SCHEMA sch1
+--- AST
+&ast.DropSchema{
+  Drop: 0,
+  Name: &ast.Ident{
+    NamePos: 12,
+    NameEnd: 16,
+    Name:    "sch1",
+  },
+}
+
+--- SQL
+DROP SCHEMA sch1
diff --git a/testdata/result/statement/insert_select.sql.txt b/testdata/result/statement/insert_select.sql.txt
index cc71ca1..6afe8c7 100644
--- a/testdata/result/statement/insert_select.sql.txt
+++ b/testdata/result/statement/insert_select.sql.txt
@@ -43,11 +43,9 @@ select * from unnest([(1, 2), (3, 4)])
             Rbrack: 58,
             Type:   nil,
             Values: []ast.Expr{
-              &ast.StructLiteral{
-                Struct: 0,
+              &ast.TupleStructLiteral{
                 Lparen: 44,
                 Rparen: 49,
-                Fields: []*ast.StructField(nil),
                 Values: []ast.Expr{
                   &ast.IntLiteral{
                     ValuePos: 45,
@@ -63,11 +61,9 @@ select * from unnest([(1, 2), (3, 4)])
                   },
                 },
               },
-              &ast.StructLiteral{
-                Struct: 0,
+              &ast.TupleStructLiteral{
                 Lparen: 52,
                 Rparen: 57,
-                Fields: []*ast.StructField(nil),
                 Values: []ast.Expr{
                   &ast.IntLiteral{
                     ValuePos: 53,
@@ -101,4 +97,4 @@ select * from unnest([(1, 2), (3, 4)])
 }
 
 --- SQL
-INSERT INTO foo (foo, bar) SELECT * FROM UNNEST([STRUCT(1, 2), STRUCT(3, 4)])
+INSERT INTO foo (foo, bar) SELECT * FROM UNNEST([(1, 2), (3, 4)])
diff --git a/testdata/result/statement/select_alias_without_as.sql.txt b/testdata/result/statement/select_alias_without_as.sql.txt
index da93020..c4604a9 100644
--- a/testdata/result/statement/select_alias_without_as.sql.txt
+++ b/testdata/result/statement/select_alias_without_as.sql.txt
@@ -18,7 +18,7 @@ select 1 A
           Value:    "1",
         },
         As: &ast.AsAlias{
-          As:    9,
+          As:    -1,
           Alias: &ast.Ident{
             NamePos: 9,
             NameEnd: 10,
@@ -37,4 +37,4 @@ select 1 A
 }
 
 --- SQL
-SELECT 1 AS A
+SELECT 1 A
diff --git a/testdata/result/statement/select_array_with_struct.sql.txt b/testdata/result/statement/select_array_with_struct.sql.txt
index 28c893b..32f645d 100644
--- a/testdata/result/statement/select_array_with_struct.sql.txt
+++ b/testdata/result/statement/select_array_with_struct.sql.txt
@@ -55,11 +55,9 @@ FROM
             },
           },
           Values: []ast.Expr{
-            &ast.StructLiteral{
-              Struct: 0,
+            &ast.TupleStructLiteral{
               Lparen: 58,
               Rparen: 67,
-              Fields: []*ast.StructField(nil),
               Values: []ast.Expr{
                 &ast.IntLiteral{
                   ValuePos: 59,
@@ -74,11 +72,9 @@ FROM
                 },
               },
             },
-            &ast.StructLiteral{
-              Struct: 0,
+            &ast.TupleStructLiteral{
               Lparen: 70,
               Rparen: 79,
-              Fields: []*ast.StructField(nil),
               Values: []ast.Expr{
                 &ast.IntLiteral{
                   ValuePos: 71,
@@ -110,4 +106,4 @@ FROM
 }
 
 --- SQL
-SELECT * FROM UNNEST(ARRAY<STRUCT<x INT64, y STRING>>[STRUCT(1, "foo"), STRUCT(3, "bar")])
+SELECT * FROM UNNEST(ARRAY<STRUCT<x INT64, y STRING>>[(1, "foo"), (3, "bar")])
diff --git a/testdata/result/statement/select_call_with_named_expr.sql.txt b/testdata/result/statement/select_call_with_named_expr.sql.txt
index 78d1d15..bae8b86 100644
--- a/testdata/result/statement/select_call_with_named_expr.sql.txt
+++ b/testdata/result/statement/select_call_with_named_expr.sql.txt
@@ -54,7 +54,7 @@ WHERE a.SingerId = 1 AND SEARCH(a.DescriptionTokens, 'classic albums', enhance_q
         },
         Hint: (*ast.Hint)(nil),
         As:   &ast.AsAlias{
-          As:    44,
+          As:    -1,
           Alias: &ast.Ident{
             NamePos: 44,
             NameEnd: 45,
@@ -150,4 +150,4 @@ WHERE a.SingerId = 1 AND SEARCH(a.DescriptionTokens, 'classic albums', enhance_q
 }
 
 --- SQL
-SELECT a.AlbumId, a.Description FROM Albums AS a WHERE a.SingerId = 1 AND SEARCH(a.DescriptionTokens, "classic albums", enhance_query => TRUE)
+SELECT a.AlbumId, a.Description FROM Albums a WHERE a.SingerId = 1 AND SEARCH(a.DescriptionTokens, "classic albums", enhance_query => TRUE)
diff --git a/testdata/result/statement/select_cast.sql.txt b/testdata/result/statement/select_cast.sql.txt
index d5f6fb9..84154e9 100644
--- a/testdata/result/statement/select_cast.sql.txt
+++ b/testdata/result/statement/select_cast.sql.txt
@@ -52,18 +52,14 @@ limit cast(1 as INT64) offset cast(@foo as INT64)
           Cast:   47,
           Rparen: 147,
           Safe:   false,
-          Expr:   &ast.StructLiteral{
-            Struct: 0,
+          Expr:   &ast.TupleStructLiteral{
             Lparen: 52,
             Rparen: 84,
-            Fields: []*ast.StructField(nil),
             Values: []ast.Expr{
-              &ast.StructLiteral{
+              &ast.TypelessStructLiteral{
                 Struct: 53,
-                Lparen: 59,
                 Rparen: 60,
-                Fields: []*ast.StructField(nil),
-                Values: []ast.Expr(nil),
+                Values: []ast.TypelessStructLiteralArg(nil),
               },
               &ast.IntLiteral{
                 ValuePos: 63,
@@ -120,7 +116,7 @@ limit cast(1 as INT64) offset cast(@foo as INT64)
                 Type:  &ast.StructType{
                   Struct: 96,
                   Gt:     103,
-                  Fields: []*ast.StructField{},
+                  Fields: []*ast.StructField(nil),
                 },
               },
               &ast.StructField{
@@ -300,4 +296,4 @@ limit cast(1 as INT64) offset cast(@foo as INT64)
 }
 
 --- SQL
-SELECT CAST(1 AS INT64), CAST(0.1 AS FLOAT32), CAST(STRUCT(STRUCT(), 1, [2, 3], ["4", "5"]) AS STRUCT<STRUCT<>, x INT64, y ARRAY<INT64>, z ARRAY<STRING>>) FROM x TABLESAMPLE BERNOULLI (CAST(0.1 AS FLOAT64) PERCENT), y TABLESAMPLE BERNOULLI (CAST(1 AS INT64) ROWS), z TABLESAMPLE BERNOULLI (CAST(@param AS INT64) ROWS) LIMIT CAST(1 AS INT64) OFFSET CAST(@foo AS INT64)
+SELECT CAST(1 AS INT64), CAST(0.1 AS FLOAT32), CAST((STRUCT(), 1, [2, 3], ["4", "5"]) AS STRUCT<STRUCT<>, x INT64, y ARRAY<INT64>, z ARRAY<STRING>>) FROM x TABLESAMPLE BERNOULLI (CAST(0.1 AS FLOAT64) PERCENT), y TABLESAMPLE BERNOULLI (CAST(1 AS INT64) ROWS), z TABLESAMPLE BERNOULLI (CAST(@param AS INT64) ROWS) LIMIT CAST(1 AS INT64) OFFSET CAST(@foo AS INT64)
diff --git a/testdata/result/statement/select_expr.sql.txt b/testdata/result/statement/select_expr.sql.txt
index e6c89c3..9f1a2bc 100644
--- a/testdata/result/statement/select_expr.sql.txt
+++ b/testdata/result/statement/select_expr.sql.txt
@@ -340,7 +340,7 @@ select 1 + 2, 1 - 2,
                     Value:    "1",
                   },
                   As: &ast.AsAlias{
-                    As:    204,
+                    As:    -1,
                     Alias: &ast.Ident{
                       NamePos: 204,
                       NameEnd: 207,
@@ -915,7 +915,7 @@ select 1 + 2, 1 - 2,
           },
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    792,
           Alias: &ast.Ident{
             NamePos: 795,
             NameEnd: 801,
@@ -934,4 +934,4 @@ select 1 + 2, 1 - 2,
 }
 
 --- SQL
-SELECT 1 + 2, 1 - 2, 1 * 2, 2 / 2, +1 + +1, -1 + -1, +1.2, -3.4, ~1 ^ ~1, 1 ^ 2, 2 & 1, 2 | 1, 1 << 2, 2 >> 1, foo.bar * +foo.bar * -foo.bar, (SELECT 1 AS `1`).`1`, NOT NOT TRUE, [1, 2, 3][OFFSET(1)], [1, 2, 3][OFFSET(1)], [1, 2, 3][ORDINAL(1)], CASE WHEN 1 = 1 THEN "1 = 1" ELSE "else" END, CASE 1 WHEN 1 THEN "1" WHEN 2 THEN "2" ELSE "other" END, date_add(DATE "2019-09-01", INTERVAL 5 day), timestamp_add(TIMESTAMP "2019-09-01 08:11:22", INTERVAL 5 hour), 1 IN (1, 2, 3), 2 IN UNNEST([1, 2, 3]), 3 IN (SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3), [1] || [2], IF(1 > 1, 1, 2) + 1 AS result
+SELECT 1 + 2, 1 - 2, 1 * 2, 2 / 2, +1 + +1, -1 + -1, +1.2, -3.4, ~1 ^ ~1, 1 ^ 2, 2 & 1, 2 | 1, 1 << 2, 2 >> 1, foo.bar * +foo.bar * -foo.bar, (SELECT 1 `1`).`1`, NOT NOT TRUE, [1, 2, 3][OFFSET(1)], [1, 2, 3][OFFSET(1)], [1, 2, 3][ORDINAL(1)], CASE WHEN 1 = 1 THEN "1 = 1" ELSE "else" END, CASE 1 WHEN 1 THEN "1" WHEN 2 THEN "2" ELSE "other" END, date_add(DATE "2019-09-01", INTERVAL 5 day), timestamp_add(TIMESTAMP "2019-09-01 08:11:22", INTERVAL 5 hour), 1 IN (1, 2, 3), 2 IN UNNEST([1, 2, 3]), 3 IN (SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3), [1] || [2], IF(1 > 1, 1, 2) + 1 AS result
diff --git a/testdata/result/statement/select_from_implicit_unnest.sql.txt b/testdata/result/statement/select_from_implicit_unnest.sql.txt
index 6cea988..f1d12b8 100644
--- a/testdata/result/statement/select_from_implicit_unnest.sql.txt
+++ b/testdata/result/statement/select_from_implicit_unnest.sql.txt
@@ -41,9 +41,8 @@ FROM UNNEST([STRUCT<arr ARRAY<STRING>>(["foo"])]) AS t,
             Rbrack: 61,
             Type:   nil,
             Values: []ast.Expr{
-              &ast.StructLiteral{
+              &ast.TypedStructLiteral{
                 Struct: 27,
-                Lparen: 52,
                 Rparen: 60,
                 Fields: []*ast.StructField{
                   &ast.StructField{
@@ -82,7 +81,7 @@ FROM UNNEST([STRUCT<arr ARRAY<STRING>>(["foo"])]) AS t,
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    -1,
+            As:    64,
             Alias: &ast.Ident{
               NamePos: 67,
               NameEnd: 68,
@@ -109,7 +108,7 @@ FROM UNNEST([STRUCT<arr ARRAY<STRING>>(["foo"])]) AS t,
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    -1,
+            As:    81,
             Alias: &ast.Ident{
               NamePos: 84,
               NameEnd: 85,
@@ -120,7 +119,7 @@ FROM UNNEST([STRUCT<arr ARRAY<STRING>>(["foo"])]) AS t,
             With:   86,
             Offset: 91,
             As:     &ast.AsAlias{
-              As:    -1,
+              As:    98,
               Alias: &ast.Ident{
                 NamePos: 101,
                 NameEnd: 104,
diff --git a/testdata/result/statement/select_from_path.sql.txt b/testdata/result/statement/select_from_path.sql.txt
index f3331a1..d58a1d7 100644
--- a/testdata/result/statement/select_from_path.sql.txt
+++ b/testdata/result/statement/select_from_path.sql.txt
@@ -35,7 +35,7 @@ FROM
         },
         Hint: (*ast.Hint)(nil),
         As:   &ast.AsAlias{
-          As:    -1,
+          As:    44,
           Alias: &ast.Ident{
             NamePos: 47,
             NameEnd: 48,
diff --git a/testdata/result/statement/select_literals_all.sql.txt b/testdata/result/statement/select_literals_all.sql.txt
index 3e35cc3..1713799 100644
--- a/testdata/result/statement/select_literals_all.sql.txt
+++ b/testdata/result/statement/select_literals_all.sql.txt
@@ -392,29 +392,33 @@ lines''',
             As:       nil,
             Results:  []ast.SelectItem{
               &ast.ExprSelectItem{
-                Expr: &ast.StructLiteral{
+                Expr: &ast.TypelessStructLiteral{
                   Struct: 467,
-                  Lparen: 473,
                   Rparen: 481,
-                  Fields: []*ast.StructField(nil),
-                  Values: []ast.Expr{
-                    &ast.IntLiteral{
-                      ValuePos: 474,
-                      ValueEnd: 475,
-                      Base:     10,
-                      Value:    "1",
+                  Values: []ast.TypelessStructLiteralArg{
+                    &ast.ExprArg{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 474,
+                        ValueEnd: 475,
+                        Base:     10,
+                        Value:    "1",
+                      },
                     },
-                    &ast.IntLiteral{
-                      ValuePos: 477,
-                      ValueEnd: 478,
-                      Base:     10,
-                      Value:    "2",
+                    &ast.ExprArg{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 477,
+                        ValueEnd: 478,
+                        Base:     10,
+                        Value:    "2",
+                      },
                     },
-                    &ast.IntLiteral{
-                      ValuePos: 480,
-                      ValueEnd: 481,
-                      Base:     10,
-                      Value:    "3",
+                    &ast.ExprArg{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 480,
+                        ValueEnd: 481,
+                        Base:     10,
+                        Value:    "3",
+                      },
                     },
                   },
                 },
@@ -439,11 +443,9 @@ lines''',
             As:       nil,
             Results:  []ast.SelectItem{
               &ast.ExprSelectItem{
-                Expr: &ast.StructLiteral{
-                  Struct: 0,
+                Expr: &ast.TupleStructLiteral{
                   Lparen: 500,
                   Rparen: 508,
-                  Fields: []*ast.StructField(nil),
                   Values: []ast.Expr{
                     &ast.IntLiteral{
                       ValuePos: 501,
@@ -635,4 +637,4 @@ lines''',
 }
 
 --- SQL
-SELECT "abc", "it\'s", "it\'s", "Title: \"Boy\"", "abc", "it\'s", "Title:\"Boy\"", "two\nlines", "why\?", "abc+", "abc+", "abc+", "f\\(abc,(.*),def\\)", B"abc", B"abc", B"abc", B"abc+", B"abc+", B"abc", 123, 0xABC, -123, -0xABC, 123.456e-67, .1E4, 58., 4e2, [1, 2, 3], ["x", "y", "xy"], ARRAY[1, 2, 3], ARRAY<STRING>["x", "y", "xy"], ARRAY<INT64>[], ARRAY(SELECT STRUCT(1, 2, 3)), ARRAY(SELECT STRUCT(1, 2, 3)), DATE "2014-09-27", DATE("2014-09-27"), TIMESTAMP "2014-09-27 12:30:00.45-08", TIMESTAMP "2014-09-27 12:30:00.45 America/Los_Angeles", TIMESTAMP "2014-09-27", TIMESTAMP("2014-09-27"), JSON "1", JSON "[1, 2]", JSON "{}", NUMERIC "0", NUMERIC "0", TRUE, FALSE
+SELECT "abc", "it\'s", "it\'s", "Title: \"Boy\"", "abc", "it\'s", "Title:\"Boy\"", "two\nlines", "why\?", "abc+", "abc+", "abc+", "f\\(abc,(.*),def\\)", B"abc", B"abc", B"abc", B"abc+", B"abc+", B"abc", 123, 0xABC, -123, -0xABC, 123.456e-67, .1E4, 58., 4e2, [1, 2, 3], ["x", "y", "xy"], ARRAY[1, 2, 3], ARRAY<STRING>["x", "y", "xy"], ARRAY<INT64>[], ARRAY(SELECT STRUCT(1, 2, 3)), ARRAY(SELECT (1, 2, 3)), DATE "2014-09-27", DATE("2014-09-27"), TIMESTAMP "2014-09-27 12:30:00.45-08", TIMESTAMP "2014-09-27 12:30:00.45 America/Los_Angeles", TIMESTAMP "2014-09-27", TIMESTAMP("2014-09-27"), JSON "1", JSON "[1, 2]", JSON "{}", NUMERIC "0", NUMERIC "0", TRUE, FALSE
diff --git a/testdata/result/statement/select_literals_date.sql.txt b/testdata/result/statement/select_literals_date.sql.txt
index fd6b359..7d21137 100644
--- a/testdata/result/statement/select_literals_date.sql.txt
+++ b/testdata/result/statement/select_literals_date.sql.txt
@@ -149,7 +149,7 @@ SELECT
           Having:       nil,
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    270,
           Alias: &ast.Ident{
             NamePos: 273,
             NameEnd: 276,
@@ -180,7 +180,7 @@ SELECT
           Having:       nil,
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    304,
           Alias: &ast.Ident{
             NamePos: 307,
             NameEnd: 310,
diff --git a/testdata/result/statement/select_literals_struct.sql.txt b/testdata/result/statement/select_literals_struct.sql.txt
index 802fb1e..3b45c25 100644
--- a/testdata/result/statement/select_literals_struct.sql.txt
+++ b/testdata/result/statement/select_literals_struct.sql.txt
@@ -3,10 +3,17 @@ SELECT
   ARRAY(SELECT STRUCT(1, 2, 3)),
   ARRAY(SELECT STRUCT(1, 2, TRUE)),
   ARRAY(SELECT STRUCT<X INT64, Y INT64, Z INT64>(1, 2, 3)),
+  ARRAY(SELECT STRUCT(1 AS X, 2 AS Y, 3 AS Z)),
   ARRAY(SELECT STRUCT<INT64, INT64, INT64>(1, 2, 3)),
   ARRAY(SELECT STRUCT<X INT64, INT64, INT64>(1, 2, 3)),
+  ARRAY(SELECT STRUCT(1 AS X, 2, 3)),
   ARRAY(SELECT (1, 2, 3)),
+  ARRAY(SELECT STRUCT<X INT64>(1)),
+  ARRAY(SELECT STRUCT(1 AS X)),
+  ARRAY(SELECT STRUCT<INT64>(1)),
+  ARRAY(SELECT STRUCT(1)),
   ARRAY(SELECT STRUCT<>()),
+  ARRAY(SELECT STRUCT()),
   EXISTS(SELECT (1, 2))
 
 --- AST
@@ -28,29 +35,33 @@ SELECT
             As:       nil,
             Results:  []ast.SelectItem{
               &ast.ExprSelectItem{
-                Expr: &ast.StructLiteral{
+                Expr: &ast.TypelessStructLiteral{
                   Struct: 22,
-                  Lparen: 28,
                   Rparen: 36,
-                  Fields: []*ast.StructField(nil),
-                  Values: []ast.Expr{
-                    &ast.IntLiteral{
-                      ValuePos: 29,
-                      ValueEnd: 30,
-                      Base:     10,
-                      Value:    "1",
+                  Values: []ast.TypelessStructLiteralArg{
+                    &ast.ExprArg{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 29,
+                        ValueEnd: 30,
+                        Base:     10,
+                        Value:    "1",
+                      },
                     },
-                    &ast.IntLiteral{
-                      ValuePos: 32,
-                      ValueEnd: 33,
-                      Base:     10,
-                      Value:    "2",
+                    &ast.ExprArg{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 32,
+                        ValueEnd: 33,
+                        Base:     10,
+                        Value:    "2",
+                      },
                     },
-                    &ast.IntLiteral{
-                      ValuePos: 35,
-                      ValueEnd: 36,
-                      Base:     10,
-                      Value:    "3",
+                    &ast.ExprArg{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 35,
+                        ValueEnd: 36,
+                        Base:     10,
+                        Value:    "3",
+                      },
                     },
                   },
                 },
@@ -75,27 +86,31 @@ SELECT
             As:       nil,
             Results:  []ast.SelectItem{
               &ast.ExprSelectItem{
-                Expr: &ast.StructLiteral{
+                Expr: &ast.TypelessStructLiteral{
                   Struct: 55,
-                  Lparen: 61,
                   Rparen: 72,
-                  Fields: []*ast.StructField(nil),
-                  Values: []ast.Expr{
-                    &ast.IntLiteral{
-                      ValuePos: 62,
-                      ValueEnd: 63,
-                      Base:     10,
-                      Value:    "1",
+                  Values: []ast.TypelessStructLiteralArg{
+                    &ast.ExprArg{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 62,
+                        ValueEnd: 63,
+                        Base:     10,
+                        Value:    "1",
+                      },
                     },
-                    &ast.IntLiteral{
-                      ValuePos: 65,
-                      ValueEnd: 66,
-                      Base:     10,
-                      Value:    "2",
+                    &ast.ExprArg{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 65,
+                        ValueEnd: 66,
+                        Base:     10,
+                        Value:    "2",
+                      },
                     },
-                    &ast.BoolLiteral{
-                      ValuePos: 68,
-                      Value:    true,
+                    &ast.ExprArg{
+                      Expr: &ast.BoolLiteral{
+                        ValuePos: 68,
+                        Value:    true,
+                      },
                     },
                   },
                 },
@@ -120,9 +135,8 @@ SELECT
             As:       nil,
             Results:  []ast.SelectItem{
               &ast.ExprSelectItem{
-                Expr: &ast.StructLiteral{
+                Expr: &ast.TypedStructLiteral{
                   Struct: 91,
-                  Lparen: 124,
                   Rparen: 132,
                   Fields: []*ast.StructField{
                     &ast.StructField{
@@ -194,56 +208,130 @@ SELECT
       &ast.ExprSelectItem{
         Expr: &ast.ArraySubQuery{
           Array:  138,
-          Rparen: 187,
+          Rparen: 181,
           Query:  &ast.Select{
             Select:   144,
             Distinct: false,
             As:       nil,
             Results:  []ast.SelectItem{
               &ast.ExprSelectItem{
-                Expr: &ast.StructLiteral{
+                Expr: &ast.TypelessStructLiteral{
                   Struct: 151,
-                  Lparen: 178,
-                  Rparen: 186,
+                  Rparen: 180,
+                  Values: []ast.TypelessStructLiteralArg{
+                    &ast.Alias{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 158,
+                        ValueEnd: 159,
+                        Base:     10,
+                        Value:    "1",
+                      },
+                      As: &ast.AsAlias{
+                        As:    160,
+                        Alias: &ast.Ident{
+                          NamePos: 163,
+                          NameEnd: 164,
+                          Name:    "X",
+                        },
+                      },
+                    },
+                    &ast.Alias{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 166,
+                        ValueEnd: 167,
+                        Base:     10,
+                        Value:    "2",
+                      },
+                      As: &ast.AsAlias{
+                        As:    168,
+                        Alias: &ast.Ident{
+                          NamePos: 171,
+                          NameEnd: 172,
+                          Name:    "Y",
+                        },
+                      },
+                    },
+                    &ast.Alias{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 174,
+                        ValueEnd: 175,
+                        Base:     10,
+                        Value:    "3",
+                      },
+                      As: &ast.AsAlias{
+                        As:    176,
+                        Alias: &ast.Ident{
+                          NamePos: 179,
+                          NameEnd: 180,
+                          Name:    "Z",
+                        },
+                      },
+                    },
+                  },
+                },
+              },
+            },
+            From:    (*ast.From)(nil),
+            Where:   (*ast.Where)(nil),
+            GroupBy: (*ast.GroupBy)(nil),
+            Having:  (*ast.Having)(nil),
+            OrderBy: (*ast.OrderBy)(nil),
+            Limit:   (*ast.Limit)(nil),
+          },
+        },
+      },
+      &ast.ExprSelectItem{
+        Expr: &ast.ArraySubQuery{
+          Array:  186,
+          Rparen: 235,
+          Query:  &ast.Select{
+            Select:   192,
+            Distinct: false,
+            As:       nil,
+            Results:  []ast.SelectItem{
+              &ast.ExprSelectItem{
+                Expr: &ast.TypedStructLiteral{
+                  Struct: 199,
+                  Rparen: 234,
                   Fields: []*ast.StructField{
                     &ast.StructField{
                       Ident: (*ast.Ident)(nil),
                       Type:  &ast.SimpleType{
-                        NamePos: 158,
+                        NamePos: 206,
                         Name:    "INT64",
                       },
                     },
                     &ast.StructField{
                       Ident: (*ast.Ident)(nil),
                       Type:  &ast.SimpleType{
-                        NamePos: 165,
+                        NamePos: 213,
                         Name:    "INT64",
                       },
                     },
                     &ast.StructField{
                       Ident: (*ast.Ident)(nil),
                       Type:  &ast.SimpleType{
-                        NamePos: 172,
+                        NamePos: 220,
                         Name:    "INT64",
                       },
                     },
                   },
                   Values: []ast.Expr{
                     &ast.IntLiteral{
-                      ValuePos: 179,
-                      ValueEnd: 180,
+                      ValuePos: 227,
+                      ValueEnd: 228,
                       Base:     10,
                       Value:    "1",
                     },
                     &ast.IntLiteral{
-                      ValuePos: 182,
-                      ValueEnd: 183,
+                      ValuePos: 230,
+                      ValueEnd: 231,
                       Base:     10,
                       Value:    "2",
                     },
                     &ast.IntLiteral{
-                      ValuePos: 185,
-                      ValueEnd: 186,
+                      ValuePos: 233,
+                      ValueEnd: 234,
                       Base:     10,
                       Value:    "3",
                     },
@@ -262,61 +350,60 @@ SELECT
       },
       &ast.ExprSelectItem{
         Expr: &ast.ArraySubQuery{
-          Array:  192,
-          Rparen: 243,
+          Array:  240,
+          Rparen: 291,
           Query:  &ast.Select{
-            Select:   198,
+            Select:   246,
             Distinct: false,
             As:       nil,
             Results:  []ast.SelectItem{
               &ast.ExprSelectItem{
-                Expr: &ast.StructLiteral{
-                  Struct: 205,
-                  Lparen: 234,
-                  Rparen: 242,
+                Expr: &ast.TypedStructLiteral{
+                  Struct: 253,
+                  Rparen: 290,
                   Fields: []*ast.StructField{
                     &ast.StructField{
                       Ident: &ast.Ident{
-                        NamePos: 212,
-                        NameEnd: 213,
+                        NamePos: 260,
+                        NameEnd: 261,
                         Name:    "X",
                       },
                       Type: &ast.SimpleType{
-                        NamePos: 214,
+                        NamePos: 262,
                         Name:    "INT64",
                       },
                     },
                     &ast.StructField{
                       Ident: (*ast.Ident)(nil),
                       Type:  &ast.SimpleType{
-                        NamePos: 221,
+                        NamePos: 269,
                         Name:    "INT64",
                       },
                     },
                     &ast.StructField{
                       Ident: (*ast.Ident)(nil),
                       Type:  &ast.SimpleType{
-                        NamePos: 228,
+                        NamePos: 276,
                         Name:    "INT64",
                       },
                     },
                   },
                   Values: []ast.Expr{
                     &ast.IntLiteral{
-                      ValuePos: 235,
-                      ValueEnd: 236,
+                      ValuePos: 283,
+                      ValueEnd: 284,
                       Base:     10,
                       Value:    "1",
                     },
                     &ast.IntLiteral{
-                      ValuePos: 238,
-                      ValueEnd: 239,
+                      ValuePos: 286,
+                      ValueEnd: 287,
                       Base:     10,
                       Value:    "2",
                     },
                     &ast.IntLiteral{
-                      ValuePos: 241,
-                      ValueEnd: 242,
+                      ValuePos: 289,
+                      ValueEnd: 290,
                       Base:     10,
                       Value:    "3",
                     },
@@ -335,35 +422,92 @@ SELECT
       },
       &ast.ExprSelectItem{
         Expr: &ast.ArraySubQuery{
-          Array:  248,
-          Rparen: 270,
+          Array:  296,
+          Rparen: 329,
           Query:  &ast.Select{
-            Select:   254,
+            Select:   302,
             Distinct: false,
             As:       nil,
             Results:  []ast.SelectItem{
               &ast.ExprSelectItem{
-                Expr: &ast.StructLiteral{
-                  Struct: 0,
-                  Lparen: 261,
-                  Rparen: 269,
-                  Fields: []*ast.StructField(nil),
+                Expr: &ast.TypelessStructLiteral{
+                  Struct: 309,
+                  Rparen: 328,
+                  Values: []ast.TypelessStructLiteralArg{
+                    &ast.Alias{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 316,
+                        ValueEnd: 317,
+                        Base:     10,
+                        Value:    "1",
+                      },
+                      As: &ast.AsAlias{
+                        As:    318,
+                        Alias: &ast.Ident{
+                          NamePos: 321,
+                          NameEnd: 322,
+                          Name:    "X",
+                        },
+                      },
+                    },
+                    &ast.ExprArg{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 324,
+                        ValueEnd: 325,
+                        Base:     10,
+                        Value:    "2",
+                      },
+                    },
+                    &ast.ExprArg{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 327,
+                        ValueEnd: 328,
+                        Base:     10,
+                        Value:    "3",
+                      },
+                    },
+                  },
+                },
+              },
+            },
+            From:    (*ast.From)(nil),
+            Where:   (*ast.Where)(nil),
+            GroupBy: (*ast.GroupBy)(nil),
+            Having:  (*ast.Having)(nil),
+            OrderBy: (*ast.OrderBy)(nil),
+            Limit:   (*ast.Limit)(nil),
+          },
+        },
+      },
+      &ast.ExprSelectItem{
+        Expr: &ast.ArraySubQuery{
+          Array:  334,
+          Rparen: 356,
+          Query:  &ast.Select{
+            Select:   340,
+            Distinct: false,
+            As:       nil,
+            Results:  []ast.SelectItem{
+              &ast.ExprSelectItem{
+                Expr: &ast.TupleStructLiteral{
+                  Lparen: 347,
+                  Rparen: 355,
                   Values: []ast.Expr{
                     &ast.IntLiteral{
-                      ValuePos: 262,
-                      ValueEnd: 263,
+                      ValuePos: 348,
+                      ValueEnd: 349,
                       Base:     10,
                       Value:    "1",
                     },
                     &ast.IntLiteral{
-                      ValuePos: 265,
-                      ValueEnd: 266,
+                      ValuePos: 351,
+                      ValueEnd: 352,
                       Base:     10,
                       Value:    "2",
                     },
                     &ast.IntLiteral{
-                      ValuePos: 268,
-                      ValueEnd: 269,
+                      ValuePos: 354,
+                      ValueEnd: 355,
                       Base:     10,
                       Value:    "3",
                     },
@@ -382,19 +526,184 @@ SELECT
       },
       &ast.ExprSelectItem{
         Expr: &ast.ArraySubQuery{
-          Array:  275,
-          Rparen: 298,
+          Array:  361,
+          Rparen: 392,
+          Query:  &ast.Select{
+            Select:   367,
+            Distinct: false,
+            As:       nil,
+            Results:  []ast.SelectItem{
+              &ast.ExprSelectItem{
+                Expr: &ast.TypedStructLiteral{
+                  Struct: 374,
+                  Rparen: 391,
+                  Fields: []*ast.StructField{
+                    &ast.StructField{
+                      Ident: &ast.Ident{
+                        NamePos: 381,
+                        NameEnd: 382,
+                        Name:    "X",
+                      },
+                      Type: &ast.SimpleType{
+                        NamePos: 383,
+                        Name:    "INT64",
+                      },
+                    },
+                  },
+                  Values: []ast.Expr{
+                    &ast.IntLiteral{
+                      ValuePos: 390,
+                      ValueEnd: 391,
+                      Base:     10,
+                      Value:    "1",
+                    },
+                  },
+                },
+              },
+            },
+            From:    (*ast.From)(nil),
+            Where:   (*ast.Where)(nil),
+            GroupBy: (*ast.GroupBy)(nil),
+            Having:  (*ast.Having)(nil),
+            OrderBy: (*ast.OrderBy)(nil),
+            Limit:   (*ast.Limit)(nil),
+          },
+        },
+      },
+      &ast.ExprSelectItem{
+        Expr: &ast.ArraySubQuery{
+          Array:  397,
+          Rparen: 424,
+          Query:  &ast.Select{
+            Select:   403,
+            Distinct: false,
+            As:       nil,
+            Results:  []ast.SelectItem{
+              &ast.ExprSelectItem{
+                Expr: &ast.TypelessStructLiteral{
+                  Struct: 410,
+                  Rparen: 423,
+                  Values: []ast.TypelessStructLiteralArg{
+                    &ast.Alias{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 417,
+                        ValueEnd: 418,
+                        Base:     10,
+                        Value:    "1",
+                      },
+                      As: &ast.AsAlias{
+                        As:    419,
+                        Alias: &ast.Ident{
+                          NamePos: 422,
+                          NameEnd: 423,
+                          Name:    "X",
+                        },
+                      },
+                    },
+                  },
+                },
+              },
+            },
+            From:    (*ast.From)(nil),
+            Where:   (*ast.Where)(nil),
+            GroupBy: (*ast.GroupBy)(nil),
+            Having:  (*ast.Having)(nil),
+            OrderBy: (*ast.OrderBy)(nil),
+            Limit:   (*ast.Limit)(nil),
+          },
+        },
+      },
+      &ast.ExprSelectItem{
+        Expr: &ast.ArraySubQuery{
+          Array:  429,
+          Rparen: 458,
+          Query:  &ast.Select{
+            Select:   435,
+            Distinct: false,
+            As:       nil,
+            Results:  []ast.SelectItem{
+              &ast.ExprSelectItem{
+                Expr: &ast.TypedStructLiteral{
+                  Struct: 442,
+                  Rparen: 457,
+                  Fields: []*ast.StructField{
+                    &ast.StructField{
+                      Ident: (*ast.Ident)(nil),
+                      Type:  &ast.SimpleType{
+                        NamePos: 449,
+                        Name:    "INT64",
+                      },
+                    },
+                  },
+                  Values: []ast.Expr{
+                    &ast.IntLiteral{
+                      ValuePos: 456,
+                      ValueEnd: 457,
+                      Base:     10,
+                      Value:    "1",
+                    },
+                  },
+                },
+              },
+            },
+            From:    (*ast.From)(nil),
+            Where:   (*ast.Where)(nil),
+            GroupBy: (*ast.GroupBy)(nil),
+            Having:  (*ast.Having)(nil),
+            OrderBy: (*ast.OrderBy)(nil),
+            Limit:   (*ast.Limit)(nil),
+          },
+        },
+      },
+      &ast.ExprSelectItem{
+        Expr: &ast.ArraySubQuery{
+          Array:  463,
+          Rparen: 485,
+          Query:  &ast.Select{
+            Select:   469,
+            Distinct: false,
+            As:       nil,
+            Results:  []ast.SelectItem{
+              &ast.ExprSelectItem{
+                Expr: &ast.TypelessStructLiteral{
+                  Struct: 476,
+                  Rparen: 484,
+                  Values: []ast.TypelessStructLiteralArg{
+                    &ast.ExprArg{
+                      Expr: &ast.IntLiteral{
+                        ValuePos: 483,
+                        ValueEnd: 484,
+                        Base:     10,
+                        Value:    "1",
+                      },
+                    },
+                  },
+                },
+              },
+            },
+            From:    (*ast.From)(nil),
+            Where:   (*ast.Where)(nil),
+            GroupBy: (*ast.GroupBy)(nil),
+            Having:  (*ast.Having)(nil),
+            OrderBy: (*ast.OrderBy)(nil),
+            Limit:   (*ast.Limit)(nil),
+          },
+        },
+      },
+      &ast.ExprSelectItem{
+        Expr: &ast.ArraySubQuery{
+          Array:  490,
+          Rparen: 513,
           Query:  &ast.Select{
-            Select:   281,
+            Select:   496,
             Distinct: false,
             As:       nil,
             Results:  []ast.SelectItem{
               &ast.ExprSelectItem{
-                Expr: &ast.StructLiteral{
-                  Struct: 288,
-                  Lparen: 296,
-                  Rparen: 297,
-                  Fields: []*ast.StructField{},
+                Expr: &ast.TypedStructLiteral{
+                  Struct: 503,
+                  Rparen: 512,
+                  Fields: []*ast.StructField(nil),
                   Values: []ast.Expr(nil),
                 },
               },
@@ -408,32 +717,56 @@ SELECT
           },
         },
       },
+      &ast.ExprSelectItem{
+        Expr: &ast.ArraySubQuery{
+          Array:  518,
+          Rparen: 539,
+          Query:  &ast.Select{
+            Select:   524,
+            Distinct: false,
+            As:       nil,
+            Results:  []ast.SelectItem{
+              &ast.ExprSelectItem{
+                Expr: &ast.TypelessStructLiteral{
+                  Struct: 531,
+                  Rparen: 538,
+                  Values: []ast.TypelessStructLiteralArg(nil),
+                },
+              },
+            },
+            From:    (*ast.From)(nil),
+            Where:   (*ast.Where)(nil),
+            GroupBy: (*ast.GroupBy)(nil),
+            Having:  (*ast.Having)(nil),
+            OrderBy: (*ast.OrderBy)(nil),
+            Limit:   (*ast.Limit)(nil),
+          },
+        },
+      },
       &ast.ExprSelectItem{
         Expr: &ast.ExistsSubQuery{
-          Exists: 303,
-          Rparen: 323,
+          Exists: 544,
+          Rparen: 564,
           Hint:   (*ast.Hint)(nil),
           Query:  &ast.Select{
-            Select:   310,
+            Select:   551,
             Distinct: false,
             As:       nil,
             Results:  []ast.SelectItem{
               &ast.ExprSelectItem{
-                Expr: &ast.StructLiteral{
-                  Struct: 0,
-                  Lparen: 317,
-                  Rparen: 322,
-                  Fields: []*ast.StructField(nil),
+                Expr: &ast.TupleStructLiteral{
+                  Lparen: 558,
+                  Rparen: 563,
                   Values: []ast.Expr{
                     &ast.IntLiteral{
-                      ValuePos: 318,
-                      ValueEnd: 319,
+                      ValuePos: 559,
+                      ValueEnd: 560,
                       Base:     10,
                       Value:    "1",
                     },
                     &ast.IntLiteral{
-                      ValuePos: 321,
-                      ValueEnd: 322,
+                      ValuePos: 562,
+                      ValueEnd: 563,
                       Base:     10,
                       Value:    "2",
                     },
@@ -461,4 +794,4 @@ SELECT
 }
 
 --- SQL
-SELECT ARRAY(SELECT STRUCT(1, 2, 3)), ARRAY(SELECT STRUCT(1, 2, TRUE)), ARRAY(SELECT STRUCT<X INT64, Y INT64, Z INT64>(1, 2, 3)), ARRAY(SELECT STRUCT<INT64, INT64, INT64>(1, 2, 3)), ARRAY(SELECT STRUCT<X INT64, INT64, INT64>(1, 2, 3)), ARRAY(SELECT STRUCT(1, 2, 3)), ARRAY(SELECT STRUCT<>()), EXISTS(SELECT STRUCT(1, 2))
+SELECT ARRAY(SELECT STRUCT(1, 2, 3)), ARRAY(SELECT STRUCT(1, 2, TRUE)), ARRAY(SELECT STRUCT<X INT64, Y INT64, Z INT64>(1, 2, 3)), ARRAY(SELECT STRUCT(1 AS X, 2 AS Y, 3 AS Z)), ARRAY(SELECT STRUCT<INT64, INT64, INT64>(1, 2, 3)), ARRAY(SELECT STRUCT<X INT64, INT64, INT64>(1, 2, 3)), ARRAY(SELECT STRUCT(1 AS X, 2, 3)), ARRAY(SELECT (1, 2, 3)), ARRAY(SELECT STRUCT<X INT64>(1)), ARRAY(SELECT STRUCT(1 AS X)), ARRAY(SELECT STRUCT<INT64>(1)), ARRAY(SELECT STRUCT(1)), ARRAY(SELECT STRUCT<>()), ARRAY(SELECT STRUCT()), EXISTS(SELECT (1, 2))
diff --git a/testdata/result/statement/select_nest_complex.sql.txt b/testdata/result/statement/select_nest_complex.sql.txt
index e328f52..8105941 100644
--- a/testdata/result/statement/select_nest_complex.sql.txt
+++ b/testdata/result/statement/select_nest_complex.sql.txt
@@ -58,7 +58,7 @@ from (
                                 Value:    "1",
                               },
                               As: &ast.AsAlias{
-                                As:    32,
+                                As:    -1,
                                 Alias: &ast.Ident{
                                   NamePos: 32,
                                   NameEnd: 33,
@@ -170,7 +170,7 @@ from (
                     Value:    "1",
                   },
                   As: &ast.AsAlias{
-                    As:    113,
+                    As:    -1,
                     Alias: &ast.Ident{
                       NamePos: 113,
                       NameEnd: 114,
@@ -186,7 +186,7 @@ from (
                     Value:    "2",
                   },
                   As: &ast.AsAlias{
-                    As:    118,
+                    As:    -1,
                     Alias: &ast.Ident{
                       NamePos: 118,
                       NameEnd: 119,
@@ -229,4 +229,4 @@ from (
 }
 
 --- SQL
-SELECT * FROM ((((SELECT 1 AS A UNION ALL (SELECT 2)) UNION DISTINCT (SELECT 1)) LIMIT 1) INNER JOIN (SELECT 1 AS A, 2 AS B) USING (A))
+SELECT * FROM ((((SELECT 1 A UNION ALL (SELECT 2)) UNION DISTINCT (SELECT 1)) LIMIT 1) INNER JOIN (SELECT 1 A, 2 B) USING (A))
diff --git a/testdata/result/statement/select_singer_with_column_names.sql.txt b/testdata/result/statement/select_singer_with_column_names.sql.txt
index 2b8d7c1..7bef784 100644
--- a/testdata/result/statement/select_singer_with_column_names.sql.txt
+++ b/testdata/result/statement/select_singer_with_column_names.sql.txt
@@ -23,7 +23,7 @@ FROM Singers
           Name:    "SingerId",
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    18,
           Alias: &ast.Ident{
             NamePos: 21,
             NameEnd: 23,
diff --git a/testdata/result/statement/select_singer_with_cross_join.sql.txt b/testdata/result/statement/select_singer_with_cross_join.sql.txt
index b6b1637..f7be0a4 100644
--- a/testdata/result/statement/select_singer_with_cross_join.sql.txt
+++ b/testdata/result/statement/select_singer_with_cross_join.sql.txt
@@ -33,7 +33,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    26,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 26,
               NameEnd: 27,
@@ -50,7 +50,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    51,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 51,
               NameEnd: 52,
@@ -71,4 +71,4 @@ FROM
 }
 
 --- SQL
-SELECT * FROM Singers AS A CROSS JOIN Singers AS B
+SELECT * FROM Singers A CROSS JOIN Singers B
diff --git a/testdata/result/statement/select_singer_with_full_join.sql.txt b/testdata/result/statement/select_singer_with_full_join.sql.txt
index 31d9664..8e36402 100644
--- a/testdata/result/statement/select_singer_with_full_join.sql.txt
+++ b/testdata/result/statement/select_singer_with_full_join.sql.txt
@@ -32,7 +32,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    -1,
+            As:    26,
             Alias: &ast.Ident{
               NamePos: 29,
               NameEnd: 30,
@@ -49,7 +49,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    -1,
+            As:    42,
             Alias: &ast.Ident{
               NamePos: 45,
               NameEnd: 46,
diff --git a/testdata/result/statement/select_singer_with_hash_join.sql.txt b/testdata/result/statement/select_singer_with_hash_join.sql.txt
index c80ffc1..6a385ba 100644
--- a/testdata/result/statement/select_singer_with_hash_join.sql.txt
+++ b/testdata/result/statement/select_singer_with_hash_join.sql.txt
@@ -48,7 +48,7 @@ FROM
               },
               Hint: (*ast.Hint)(nil),
               As:   &ast.AsAlias{
-                As:    26,
+                As:    -1,
                 Alias: &ast.Ident{
                   NamePos: 26,
                   NameEnd: 27,
@@ -65,7 +65,7 @@ FROM
               },
               Hint: (*ast.Hint)(nil),
               As:   &ast.AsAlias{
-                As:    50,
+                As:    -1,
                 Alias: &ast.Ident{
                   NamePos: 50,
                   NameEnd: 51,
@@ -117,7 +117,7 @@ FROM
             },
             Hint: (*ast.Hint)(nil),
             As:   &ast.AsAlias{
-              As:    103,
+              As:    -1,
               Alias: &ast.Ident{
                 NamePos: 103,
                 NameEnd: 104,
@@ -169,7 +169,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    155,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 155,
               NameEnd: 156,
@@ -223,4 +223,4 @@ FROM
 }
 
 --- SQL
-SELECT * FROM Singers AS A INNER JOIN Singers AS B ON A.SingerID = B.SingerID INNER JOIN Singer AS C ON B.SingerID = C.SingerID INNER JOIN Singer AS D ON C.SingerID = D.SingerID
+SELECT * FROM Singers A INNER JOIN Singers B ON A.SingerID = B.SingerID INNER JOIN Singer C ON B.SingerID = C.SingerID INNER JOIN Singer D ON C.SingerID = D.SingerID
diff --git a/testdata/result/statement/select_singer_with_join.sql.txt b/testdata/result/statement/select_singer_with_join.sql.txt
index be653b6..ab573b3 100644
--- a/testdata/result/statement/select_singer_with_join.sql.txt
+++ b/testdata/result/statement/select_singer_with_join.sql.txt
@@ -34,7 +34,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    26,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 26,
               NameEnd: 27,
@@ -51,7 +51,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    56,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 56,
               NameEnd: 57,
@@ -105,4 +105,4 @@ FROM
 }
 
 --- SQL
-SELECT * FROM Singers AS A LEFT OUTER JOIN Singers AS B ON A.SingerID = B.SingerID
+SELECT * FROM Singers A LEFT OUTER JOIN Singers B ON A.SingerID = B.SingerID
diff --git a/testdata/result/statement/select_singer_with_join_hint.sql.txt b/testdata/result/statement/select_singer_with_join_hint.sql.txt
index 46983dc..92a14a8 100644
--- a/testdata/result/statement/select_singer_with_join_hint.sql.txt
+++ b/testdata/result/statement/select_singer_with_join_hint.sql.txt
@@ -122,7 +122,7 @@ FROM
                 },
                 Hint: (*ast.Hint)(nil),
                 As:   &ast.AsAlias{
-                  As:    26,
+                  As:    -1,
                   Alias: &ast.Ident{
                     NamePos: 26,
                     NameEnd: 27,
@@ -139,7 +139,7 @@ FROM
                 },
                 Hint: (*ast.Hint)(nil),
                 As:   &ast.AsAlias{
-                  As:    80,
+                  As:    -1,
                   Alias: &ast.Ident{
                     NamePos: 80,
                     NameEnd: 81,
@@ -191,7 +191,7 @@ FROM
               },
               Hint: (*ast.Hint)(nil),
               As:   &ast.AsAlias{
-                As:    150,
+                As:    -1,
                 Alias: &ast.Ident{
                   NamePos: 150,
                   NameEnd: 151,
@@ -243,7 +243,7 @@ FROM
             },
             Hint: (*ast.Hint)(nil),
             As:   &ast.AsAlias{
-              As:    221,
+              As:    -1,
               Alias: &ast.Ident{
                 NamePos: 221,
                 NameEnd: 222,
@@ -295,7 +295,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    291,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 291,
               NameEnd: 292,
@@ -349,4 +349,4 @@ FROM
 }
 
 --- SQL
-SELECT * FROM Singers AS A LEFT OUTER JOIN @{FORCE_JOIN_ORDER=TRUE} Singers AS B ON A.SingerID = B.SingerID INNER JOIN @{JOIN_TYPE=HASH_JOIN} Singers AS C ON A.SingerID = C.SingerID INNER JOIN @{JOIN_TYPE=APPLY_JOIN} Singers AS D ON A.SingerID = D.SingerID INNER JOIN @{JOIN_TYPE=LOOP_JOIN} Singers AS E ON A.SingerID = E.SingerID
+SELECT * FROM Singers A LEFT OUTER JOIN @{FORCE_JOIN_ORDER=TRUE} Singers B ON A.SingerID = B.SingerID INNER JOIN @{JOIN_TYPE=HASH_JOIN} Singers C ON A.SingerID = C.SingerID INNER JOIN @{JOIN_TYPE=APPLY_JOIN} Singers D ON A.SingerID = D.SingerID INNER JOIN @{JOIN_TYPE=LOOP_JOIN} Singers E ON A.SingerID = E.SingerID
diff --git a/testdata/result/statement/select_singer_with_join_twice.sql.txt b/testdata/result/statement/select_singer_with_join_twice.sql.txt
index b7d0982..035780a 100644
--- a/testdata/result/statement/select_singer_with_join_twice.sql.txt
+++ b/testdata/result/statement/select_singer_with_join_twice.sql.txt
@@ -41,7 +41,7 @@ FROM
             },
             Hint: (*ast.Hint)(nil),
             As:   &ast.AsAlias{
-              As:    26,
+              As:    -1,
               Alias: &ast.Ident{
                 NamePos: 26,
                 NameEnd: 27,
@@ -58,7 +58,7 @@ FROM
             },
             Hint: (*ast.Hint)(nil),
             As:   &ast.AsAlias{
-              As:    45,
+              As:    -1,
               Alias: &ast.Ident{
                 NamePos: 45,
                 NameEnd: 46,
@@ -110,7 +110,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    99,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 99,
               NameEnd: 100,
@@ -164,4 +164,4 @@ FROM
 }
 
 --- SQL
-SELECT * FROM Singers AS A INNER JOIN Singers AS B ON A.SingerID = B.SingerID INNER JOIN Singers AS C ON A.SingerID = C.SingerID
+SELECT * FROM Singers A INNER JOIN Singers B ON A.SingerID = B.SingerID INNER JOIN Singers C ON A.SingerID = C.SingerID
diff --git a/testdata/result/statement/select_singer_with_join_using.sql.txt b/testdata/result/statement/select_singer_with_join_using.sql.txt
index 61f23fe..e37df1c 100644
--- a/testdata/result/statement/select_singer_with_join_using.sql.txt
+++ b/testdata/result/statement/select_singer_with_join_using.sql.txt
@@ -34,7 +34,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    26,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 26,
               NameEnd: 27,
@@ -51,7 +51,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    56,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 56,
               NameEnd: 57,
@@ -87,4 +87,4 @@ FROM
 }
 
 --- SQL
-SELECT * FROM Singers AS A LEFT OUTER JOIN Singers AS B USING (SingerID, FirstName)
+SELECT * FROM Singers A LEFT OUTER JOIN Singers B USING (SingerID, FirstName)
diff --git a/testdata/result/statement/select_singer_with_join_various.sql.txt b/testdata/result/statement/select_singer_with_join_various.sql.txt
index 823859c..45d847b 100644
--- a/testdata/result/statement/select_singer_with_join_various.sql.txt
+++ b/testdata/result/statement/select_singer_with_join_various.sql.txt
@@ -89,7 +89,7 @@ FROM
                           },
                           Hint: (*ast.Hint)(nil),
                           As:   &ast.AsAlias{
-                            As:    26,
+                            As:    -1,
                             Alias: &ast.Ident{
                               NamePos: 26,
                               NameEnd: 27,
@@ -106,7 +106,7 @@ FROM
                           },
                           Hint: (*ast.Hint)(nil),
                           As:   &ast.AsAlias{
-                            As:    45,
+                            As:    -1,
                             Alias: &ast.Ident{
                               NamePos: 45,
                               NameEnd: 46,
@@ -158,7 +158,7 @@ FROM
                         },
                         Hint: (*ast.Hint)(nil),
                         As:   &ast.AsAlias{
-                          As:    99,
+                          As:    -1,
                           Alias: &ast.Ident{
                             NamePos: 99,
                             NameEnd: 100,
@@ -210,7 +210,7 @@ FROM
                       },
                       Hint: (*ast.Hint)(nil),
                       As:   &ast.AsAlias{
-                        As:    153,
+                        As:    -1,
                         Alias: &ast.Ident{
                           NamePos: 153,
                           NameEnd: 154,
@@ -229,7 +229,7 @@ FROM
                     },
                     Hint: (*ast.Hint)(nil),
                     As:   &ast.AsAlias{
-                      As:    177,
+                      As:    -1,
                       Alias: &ast.Ident{
                         NamePos: 177,
                         NameEnd: 178,
@@ -281,7 +281,7 @@ FROM
                   },
                   Hint: (*ast.Hint)(nil),
                   As:   &ast.AsAlias{
-                    As:    236,
+                    As:    -1,
                     Alias: &ast.Ident{
                       NamePos: 236,
                       NameEnd: 237,
@@ -333,7 +333,7 @@ FROM
                 },
                 Hint: (*ast.Hint)(nil),
                 As:   &ast.AsAlias{
-                  As:    289,
+                  As:    -1,
                   Alias: &ast.Ident{
                     NamePos: 289,
                     NameEnd: 290,
@@ -385,7 +385,7 @@ FROM
               },
               Hint: (*ast.Hint)(nil),
               As:   &ast.AsAlias{
-                As:    348,
+                As:    -1,
                 Alias: &ast.Ident{
                   NamePos: 348,
                   NameEnd: 349,
@@ -437,7 +437,7 @@ FROM
             },
             Hint: (*ast.Hint)(nil),
             As:   &ast.AsAlias{
-              As:    402,
+              As:    -1,
               Alias: &ast.Ident{
                 NamePos: 402,
                 NameEnd: 403,
@@ -489,7 +489,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    462,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 462,
               NameEnd: 463,
@@ -543,4 +543,4 @@ FROM
 }
 
 --- SQL
-SELECT * FROM Singers AS A INNER JOIN Singers AS B ON A.SingerID = B.SingerID INNER JOIN Singers AS C ON A.SingerID = C.SingerID CROSS JOIN Singers AS D FULL OUTER JOIN Singers AS E ON A.SingerID = E.SingerID FULL OUTER JOIN Singers AS F ON A.SingerID = F.SingerID LEFT OUTER JOIN Singers AS G ON A.SingerID = G.SingerID LEFT OUTER JOIN Singers AS H ON A.SingerID = H.SingerID RIGHT OUTER JOIN Singers AS I ON A.SingerID = I.SingerID RIGHT OUTER JOIN Singers AS J ON A.SingerID = J.SingerID
+SELECT * FROM Singers A INNER JOIN Singers B ON A.SingerID = B.SingerID INNER JOIN Singers C ON A.SingerID = C.SingerID CROSS JOIN Singers D FULL OUTER JOIN Singers E ON A.SingerID = E.SingerID FULL OUTER JOIN Singers F ON A.SingerID = F.SingerID LEFT OUTER JOIN Singers G ON A.SingerID = G.SingerID LEFT OUTER JOIN Singers H ON A.SingerID = H.SingerID RIGHT OUTER JOIN Singers I ON A.SingerID = I.SingerID RIGHT OUTER JOIN Singers J ON A.SingerID = J.SingerID
diff --git a/testdata/result/statement/select_singer_with_select_in_from_and_as.sql.txt b/testdata/result/statement/select_singer_with_select_in_from_and_as.sql.txt
index a081c2e..fda2490 100644
--- a/testdata/result/statement/select_singer_with_select_in_from_and_as.sql.txt
+++ b/testdata/result/statement/select_singer_with_select_in_from_and_as.sql.txt
@@ -73,7 +73,7 @@ FROM (
           Limit:   (*ast.Limit)(nil),
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    79,
           Alias: &ast.Ident{
             NamePos: 82,
             NameEnd: 83,
diff --git a/testdata/result/statement/select_singer_with_single_column_subquery_with_at.sql.txt b/testdata/result/statement/select_singer_with_single_column_subquery_with_at.sql.txt
index ccbe817..c420f98 100644
--- a/testdata/result/statement/select_singer_with_single_column_subquery_with_at.sql.txt
+++ b/testdata/result/statement/select_singer_with_single_column_subquery_with_at.sql.txt
@@ -60,7 +60,7 @@ SELECT (
           },
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    55,
           Alias: &ast.Ident{
             NamePos: 58,
             NameEnd: 60,
diff --git a/testdata/result/statement/select_singer_with_table_alias.sql.txt b/testdata/result/statement/select_singer_with_table_alias.sql.txt
index a491a94..190521f 100644
--- a/testdata/result/statement/select_singer_with_table_alias.sql.txt
+++ b/testdata/result/statement/select_singer_with_table_alias.sql.txt
@@ -38,7 +38,7 @@ FROM Singers S
           },
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    27,
           Alias: &ast.Ident{
             NamePos: 30,
             NameEnd: 32,
@@ -73,7 +73,7 @@ FROM Singers S
         },
         Hint: (*ast.Hint)(nil),
         As:   &ast.AsAlias{
-          As:    61,
+          As:    -1,
           Alias: &ast.Ident{
             NamePos: 61,
             NameEnd: 62,
@@ -92,4 +92,4 @@ FROM Singers S
 }
 
 --- SQL
-SELECT S.*, S.SingerId AS ID, S.FirstName FROM Singers AS S
+SELECT S.*, S.SingerId AS ID, S.FirstName FROM Singers S
diff --git a/testdata/result/statement/select_singer_with_table_alias_with_hint.sql.txt b/testdata/result/statement/select_singer_with_table_alias_with_hint.sql.txt
index 3c85380..3d72f5b 100644
--- a/testdata/result/statement/select_singer_with_table_alias_with_hint.sql.txt
+++ b/testdata/result/statement/select_singer_with_table_alias_with_hint.sql.txt
@@ -44,7 +44,7 @@ FROM
           },
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    63,
           Alias: &ast.Ident{
             NamePos: 66,
             NameEnd: 67,
diff --git a/testdata/result/statement/select_singer_with_tablesample.sql.txt b/testdata/result/statement/select_singer_with_tablesample.sql.txt
index bcbd612..f333017 100644
--- a/testdata/result/statement/select_singer_with_tablesample.sql.txt
+++ b/testdata/result/statement/select_singer_with_tablesample.sql.txt
@@ -37,7 +37,7 @@ FROM
             },
             Hint: (*ast.Hint)(nil),
             As:   &ast.AsAlias{
-              As:    -1,
+              As:    26,
               Alias: &ast.Ident{
                 NamePos: 29,
                 NameEnd: 30,
@@ -68,7 +68,7 @@ FROM
             },
             Hint: (*ast.Hint)(nil),
             As:   &ast.AsAlias{
-              As:    -1,
+              As:    75,
               Alias: &ast.Ident{
                 NamePos: 78,
                 NameEnd: 79,
@@ -100,7 +100,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    -1,
+            As:    127,
             Alias: &ast.Ident{
               NamePos: 130,
               NameEnd: 131,
diff --git a/testdata/result/statement/select_singer_with_toplevel_join_hint.sql.txt b/testdata/result/statement/select_singer_with_toplevel_join_hint.sql.txt
index 2dba175..450aca2 100644
--- a/testdata/result/statement/select_singer_with_toplevel_join_hint.sql.txt
+++ b/testdata/result/statement/select_singer_with_toplevel_join_hint.sql.txt
@@ -51,7 +51,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    51,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 51,
               NameEnd: 52,
@@ -68,7 +68,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    81,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 81,
               NameEnd: 82,
@@ -122,4 +122,4 @@ FROM
 }
 
 --- SQL
-@{FORCE_JOIN_ORDER=TRUE} SELECT * FROM Singers AS A LEFT OUTER JOIN Singers AS B ON A.SingerID = B.SingerID
+@{FORCE_JOIN_ORDER=TRUE} SELECT * FROM Singers A LEFT OUTER JOIN Singers B ON A.SingerID = B.SingerID
diff --git a/testdata/result/statement/select_struct_compare_eq.sql.txt b/testdata/result/statement/select_struct_compare_eq.sql.txt
index 0ecdcbf..ff3acbd 100644
--- a/testdata/result/statement/select_struct_compare_eq.sql.txt
+++ b/testdata/result/statement/select_struct_compare_eq.sql.txt
@@ -40,9 +40,8 @@ SELECT ARRAY(
                   As:       nil,
                   Results:  []ast.SelectItem{
                     &ast.ExprSelectItem{
-                      Expr: &ast.StructLiteral{
+                      Expr: &ast.TypedStructLiteral{
                         Struct: 48,
-                        Lparen: 81,
                         Rparen: 89,
                         Fields: []*ast.StructField{
                           &ast.StructField{
@@ -110,7 +109,7 @@ SELECT ARRAY(
                   Limit:   (*ast.Limit)(nil),
                 },
                 As: &ast.AsAlias{
-                  As:    -1,
+                  As:    92,
                   Alias: &ast.Ident{
                     NamePos: 95,
                     NameEnd: 96,
@@ -129,35 +128,39 @@ SELECT ARRAY(
                   NameEnd: 106,
                   Name:    "S",
                 },
-                Right: &ast.StructLiteral{
+                Right: &ast.TypelessStructLiteral{
                   Struct: 109,
-                  Lparen: 115,
                   Rparen: 131,
-                  Fields: []*ast.StructField(nil),
-                  Values: []ast.Expr{
-                    &ast.StructLiteral{
-                      Struct: 116,
-                      Lparen: 122,
-                      Rparen: 130,
-                      Fields: []*ast.StructField(nil),
-                      Values: []ast.Expr{
-                        &ast.IntLiteral{
-                          ValuePos: 123,
-                          ValueEnd: 124,
-                          Base:     10,
-                          Value:    "1",
-                        },
-                        &ast.IntLiteral{
-                          ValuePos: 126,
-                          ValueEnd: 127,
-                          Base:     10,
-                          Value:    "2",
-                        },
-                        &ast.IntLiteral{
-                          ValuePos: 129,
-                          ValueEnd: 130,
-                          Base:     10,
-                          Value:    "3",
+                  Values: []ast.TypelessStructLiteralArg{
+                    &ast.ExprArg{
+                      Expr: &ast.TypelessStructLiteral{
+                        Struct: 116,
+                        Rparen: 130,
+                        Values: []ast.TypelessStructLiteralArg{
+                          &ast.ExprArg{
+                            Expr: &ast.IntLiteral{
+                              ValuePos: 123,
+                              ValueEnd: 124,
+                              Base:     10,
+                              Value:    "1",
+                            },
+                          },
+                          &ast.ExprArg{
+                            Expr: &ast.IntLiteral{
+                              ValuePos: 126,
+                              ValueEnd: 127,
+                              Base:     10,
+                              Value:    "2",
+                            },
+                          },
+                          &ast.ExprArg{
+                            Expr: &ast.IntLiteral{
+                              ValuePos: 129,
+                              ValueEnd: 130,
+                              Base:     10,
+                              Value:    "3",
+                            },
+                          },
                         },
                       },
                     },
diff --git a/testdata/result/statement/select_tablesample_with_cross_join.sql.txt b/testdata/result/statement/select_tablesample_with_cross_join.sql.txt
index bf654a1..b1e29e4 100644
--- a/testdata/result/statement/select_tablesample_with_cross_join.sql.txt
+++ b/testdata/result/statement/select_tablesample_with_cross_join.sql.txt
@@ -34,7 +34,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    26,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 26,
               NameEnd: 27,
@@ -51,7 +51,7 @@ FROM
           },
           Hint: (*ast.Hint)(nil),
           As:   &ast.AsAlias{
-            As:    51,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 51,
               NameEnd: 52,
@@ -86,4 +86,4 @@ FROM
 }
 
 --- SQL
-SELECT * FROM Singers AS A CROSS JOIN Singers AS B TABLESAMPLE BERNOULLI (90 PERCENT)
+SELECT * FROM Singers A CROSS JOIN Singers B TABLESAMPLE BERNOULLI (90 PERCENT)
diff --git a/testdata/result/statement/select_tablesample_with_table_alias.sql.txt b/testdata/result/statement/select_tablesample_with_table_alias.sql.txt
index 1f99e18..102ff2e 100644
--- a/testdata/result/statement/select_tablesample_with_table_alias.sql.txt
+++ b/testdata/result/statement/select_tablesample_with_table_alias.sql.txt
@@ -27,7 +27,7 @@ FROM
         },
         Hint: (*ast.Hint)(nil),
         As:   &ast.AsAlias{
-          As:    -1,
+          As:    26,
           Alias: &ast.Ident{
             NamePos: 29,
             NameEnd: 30,
diff --git a/testdata/result/statement/select_tablesample_with_unnest_invalid.sql.txt b/testdata/result/statement/select_tablesample_with_unnest_invalid.sql.txt
index 397274f..44d34a8 100644
--- a/testdata/result/statement/select_tablesample_with_unnest_invalid.sql.txt
+++ b/testdata/result/statement/select_tablesample_with_unnest_invalid.sql.txt
@@ -55,11 +55,9 @@ FROM
             },
           },
           Values: []ast.Expr{
-            &ast.StructLiteral{
-              Struct: 0,
+            &ast.TupleStructLiteral{
               Lparen: 58,
               Rparen: 67,
-              Fields: []*ast.StructField(nil),
               Values: []ast.Expr{
                 &ast.IntLiteral{
                   ValuePos: 59,
@@ -74,11 +72,9 @@ FROM
                 },
               },
             },
-            &ast.StructLiteral{
-              Struct: 0,
+            &ast.TupleStructLiteral{
               Lparen: 70,
               Rparen: 79,
-              Fields: []*ast.StructField(nil),
               Values: []ast.Expr{
                 &ast.IntLiteral{
                   ValuePos: 71,
@@ -124,4 +120,4 @@ FROM
 }
 
 --- SQL
-SELECT * FROM UNNEST(ARRAY<STRUCT<x INT64, y STRING>>[STRUCT(1, "foo"), STRUCT(3, "bar")]) TABLESAMPLE BERNOULLI (90 PERCENT)
+SELECT * FROM UNNEST(ARRAY<STRUCT<x INT64, y STRING>>[(1, "foo"), (3, "bar")]) TABLESAMPLE BERNOULLI (90 PERCENT)
diff --git a/testdata/result/statement/select_unnest_with_offset_and_alias.sql.txt b/testdata/result/statement/select_unnest_with_offset_and_alias.sql.txt
index ecedf6a..3b88c15 100644
--- a/testdata/result/statement/select_unnest_with_offset_and_alias.sql.txt
+++ b/testdata/result/statement/select_unnest_with_offset_and_alias.sql.txt
@@ -50,7 +50,7 @@ FROM
         },
         Hint: (*ast.Hint)(nil),
         As:   &ast.AsAlias{
-          As:    -1,
+          As:    34,
           Alias: &ast.Ident{
             NamePos: 37,
             NameEnd: 42,
@@ -61,7 +61,7 @@ FROM
           With:   43,
           Offset: 48,
           As:     &ast.AsAlias{
-            As:    -1,
+            As:    55,
             Alias: &ast.Ident{
               NamePos: 58,
               NameEnd: 61,
diff --git a/testdata/result/statement/select_unnest_with_offset_and_alias_min.sql.txt b/testdata/result/statement/select_unnest_with_offset_and_alias_min.sql.txt
index b8d5790..684b111 100644
--- a/testdata/result/statement/select_unnest_with_offset_and_alias_min.sql.txt
+++ b/testdata/result/statement/select_unnest_with_offset_and_alias_min.sql.txt
@@ -50,7 +50,7 @@ FROM
         },
         Hint: (*ast.Hint)(nil),
         As:   &ast.AsAlias{
-          As:    34,
+          As:    -1,
           Alias: &ast.Ident{
             NamePos: 34,
             NameEnd: 39,
@@ -61,7 +61,7 @@ FROM
           With:   40,
           Offset: 45,
           As:     &ast.AsAlias{
-            As:    52,
+            As:    -1,
             Alias: &ast.Ident{
               NamePos: 52,
               NameEnd: 55,
@@ -81,4 +81,4 @@ FROM
 }
 
 --- SQL
-SELECT * FROM UNNEST([1, 2, 3]) AS value WITH OFFSET AS num
+SELECT * FROM UNNEST([1, 2, 3]) value WITH OFFSET num
diff --git a/testdata/result/statement/select_with_field_path.sql.txt b/testdata/result/statement/select_with_field_path.sql.txt
index e977ad1..fb07732 100644
--- a/testdata/result/statement/select_with_field_path.sql.txt
+++ b/testdata/result/statement/select_with_field_path.sql.txt
@@ -212,11 +212,9 @@ WHERE A.z.a = 2
                     },
                   },
                   Values: []ast.Expr{
-                    &ast.StructLiteral{
-                      Struct: 0,
+                    &ast.TupleStructLiteral{
                       Lparen: 206,
                       Rparen: 223,
-                      Fields: []*ast.StructField(nil),
                       Values: []ast.Expr{
                         &ast.IntLiteral{
                           ValuePos: 207,
@@ -229,11 +227,9 @@ WHERE A.z.a = 2
                           ValueEnd: 215,
                           Value:    "foo",
                         },
-                        &ast.StructLiteral{
-                          Struct: 0,
+                        &ast.TupleStructLiteral{
                           Lparen: 217,
                           Rparen: 222,
-                          Fields: []*ast.StructField(nil),
                           Values: []ast.Expr{
                             &ast.IntLiteral{
                               ValuePos: 218,
@@ -251,11 +247,9 @@ WHERE A.z.a = 2
                         },
                       },
                     },
-                    &ast.StructLiteral{
-                      Struct: 0,
+                    &ast.TupleStructLiteral{
                       Lparen: 226,
                       Rparen: 243,
-                      Fields: []*ast.StructField(nil),
                       Values: []ast.Expr{
                         &ast.IntLiteral{
                           ValuePos: 227,
@@ -268,11 +262,9 @@ WHERE A.z.a = 2
                           ValueEnd: 235,
                           Value:    "bar",
                         },
-                        &ast.StructLiteral{
-                          Struct: 0,
+                        &ast.TupleStructLiteral{
                           Lparen: 237,
                           Rparen: 242,
-                          Fields: []*ast.StructField(nil),
                           Values: []ast.Expr{
                             &ast.IntLiteral{
                               ValuePos: 238,
@@ -307,7 +299,7 @@ WHERE A.z.a = 2
         },
         Hint: (*ast.Hint)(nil),
         As:   &ast.AsAlias{
-          As:    -1,
+          As:    257,
           Alias: &ast.Ident{
             NamePos: 260,
             NameEnd: 261,
@@ -357,4 +349,4 @@ WHERE A.z.a = 2
 }
 
 --- SQL
-SELECT A.x, A.y, A.z.a, A.z.b FROM UNNEST(ARRAY(SELECT AS STRUCT x, y, z FROM UNNEST(ARRAY<STRUCT<x INT64, y STRING, z STRUCT<a INT64, b INT64>>>[STRUCT(1, "foo", STRUCT(2, 3)), STRUCT(3, "bar", STRUCT(4, 5))]))) AS A WHERE A.z.a = 2
+SELECT A.x, A.y, A.z.a, A.z.b FROM UNNEST(ARRAY(SELECT AS STRUCT x, y, z FROM UNNEST(ARRAY<STRUCT<x INT64, y STRING, z STRUCT<a INT64, b INT64>>>[(1, "foo", (2, 3)), (3, "bar", (4, 5))]))) AS A WHERE A.z.a = 2
diff --git a/testdata/result/statement/select_with_sequence_function.sql.txt b/testdata/result/statement/select_with_sequence_function.sql.txt
index 491d47f..333ec82 100644
--- a/testdata/result/statement/select_with_sequence_function.sql.txt
+++ b/testdata/result/statement/select_with_sequence_function.sql.txt
@@ -34,7 +34,7 @@ SELECT GET_NEXT_SEQUENCE_VALUE(SEQUENCE my_sequence) as next_id
           Having:       nil,
         },
         As: &ast.AsAlias{
-          As:    -1,
+          As:    53,
           Alias: &ast.Ident{
             NamePos: 56,
             NameEnd: 63,
diff --git a/testdata/result/statement/update_as.sql.txt b/testdata/result/statement/update_as.sql.txt
index 6f269af..a647ea8 100644
--- a/testdata/result/statement/update_as.sql.txt
+++ b/testdata/result/statement/update_as.sql.txt
@@ -9,7 +9,7 @@ update foo as F set F.foo = F.bar + 1 where foo = F.bar
     Name:    "foo",
   },
   As: &ast.AsAlias{
-    As:    -1,
+    As:    11,
     Alias: &ast.Ident{
       NamePos: 14,
       NameEnd: 15,