Repository: cloudspannerecosystem/memefish
Pull request #150 (2024-10-19 14:51:21)

Fix some bad `.End` usages in `parser.go`
Related to #149

- This fixes `GeneratedColumnExpr.End()`, and now `Stored` points to the head position of the `STORED` token.
- `ArraySchemeType.Gt` points to the head position of the `>` token.


Patch:
diff --git a/ast/ast.go b/ast/ast.go
index e11ace4..94eed90 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -176,6 +176,24 @@ func (ExprArg) isArg()     {}
 func (IntervalArg) isArg() {}
 func (SequenceArg) isArg() {}
 
+// NullHandlingModifier represents IGNORE/RESPECT NULLS of aggregate function calls
+type NullHandlingModifier interface {
+	Node
+	isNullHandlingModifier()
+}
+
+func (IgnoreNulls) isNullHandlingModifier()  {}
+func (RespectNulls) isNullHandlingModifier() {}
+
+// HavingModifier represents HAVING clause of aggregate function calls.
+type HavingModifier interface {
+	Node
+	isHavingModifier()
+}
+
+func (HavingMax) isHavingModifier() {}
+func (HavingMin) isHavingModifier() {}
+
 // InCondition is right-side value of IN operator.
 type InCondition interface {
 	Node
@@ -989,17 +1007,26 @@ type IndexExpr struct {
 
 // CallExpr is function call expression node.
 //
-//	{{.Func | sql}}({{if .Distinct}}DISTINCT{{end}} {{.Args | sqlJoin ", "}}{{if len(.Args) > 0 && len(.NamedArgs) > 0}}, {{end}}{{.NamedArgs || sqlJoin ", "}})
+//		{{.Func | sql}}(
+//		{{if .Distinct}}DISTINCT{{end}}
+//		{{.Args | sqlJoin ", "}}
+//		{{if len(.Args) > 0 && len(.NamedArgs) > 0}}, {{end}}
+//		{{.NamedArgs | sqlJoin ", "}}
+//	    {{.NullHandling | sqlOpt}}
+//	    {{.Having | sqlOpt}}
+//		)
 type CallExpr struct {
 	// pos = Func.pos
 	// end = Rparen + 1
 
 	Rparen token.Pos // position of ")"
 
-	Func      *Ident
-	Distinct  bool
-	Args      []Arg
-	NamedArgs []*NamedArg
+	Func         *Ident
+	Distinct     bool
+	Args         []Arg
+	NamedArgs    []*NamedArg
+	NullHandling NullHandlingModifier // optional
+	Having       HavingModifier       // optional
 }
 
 // ExprArg is argument of the generic function call.
@@ -1048,6 +1075,50 @@ type NamedArg struct {
 	Value Expr
 }
 
+// IgnoreNulls represents IGNORE NULLS of aggregate function calls.
+//
+//	IGNORE NULLS
+type IgnoreNulls struct {
+	// pos = Ignore
+	// end = Nulls + 5
+
+	Ignore token.Pos
+	Nulls  token.Pos
+}
+
+// RespectNulls represents RESPECT NULLS of aggregate function calls
+//
+//	RESPECT NULLS
+type RespectNulls struct {
+	// pos = Respect
+	// end = Nulls + 5
+
+	Respect token.Pos
+	Nulls   token.Pos
+}
+
+// HavingMax represents HAVING MAX of aggregate function calls.
+//
+//	HAVING MAX {{Expr | sql}}
+type HavingMax struct {
+	// pos = Having
+	// end = Expr.end
+
+	Having token.Pos
+	Expr   Expr
+}
+
+// HavingMin represents HAVING MIN of aggregate function calls.
+//
+//	HAVING MIN {{Expr | sql}}
+type HavingMin struct {
+	// pos = Having
+	// end = Expr.end
+
+	Having token.Pos
+	Expr   Expr
+}
+
 // CountStarExpr is node just for COUNT(*).
 //
 //	COUNT(*)
@@ -1487,6 +1558,30 @@ type CastNumValue struct {
 //
 // ================================================================================
 
+// Options is generic OPTIONS clause node without key and value checking.
+//
+//	OPTIONS ({{.Records | sqlJoin ","}})
+type Options struct {
+	// pos = Options
+	// end = Rparen + 1
+
+	Options token.Pos // position of "OPTIONS" keyword
+	Rparen  token.Pos // position of ")"
+
+	Records []*OptionsDef // len(Records) > 0
+}
+
+// OptionsDef is single option definition for DDL statements.
+//
+//	{{.Name | sql}} = {{.Value | sql}}
+type OptionsDef struct {
+	// pos = Name.pos
+	// end = Value.end
+
+	Name  *Ident
+	Value Expr
+}
+
 // CreateDatabase is CREATE DATABASE statement node.
 //
 //	CREATE DATABASE {{.Name | sql}}
@@ -1530,7 +1625,7 @@ type CreateTable struct {
 
 // CreateSequence is CREATE SEQUENCE statement node.
 //
-//	CREATE SEQUENCE {{if .IfNotExists}}IF NOT EXISTS{{end}} {{.Name | sql}} }} OPTIONS ({{.Options | sqlJoin ","}})
+//	CREATE SEQUENCE {{if .IfNotExists}}IF NOT EXISTS{{end}} {{.Name | sql}} }} {{.Options | sql}}
 type CreateSequence struct {
 	// pos = Create
 	// end = Options.end
@@ -1539,7 +1634,7 @@ type CreateSequence struct {
 
 	Name        *Ident
 	IfNotExists bool
-	Options     *SequenceOptions
+	Options     *Options
 }
 
 // ColumnDef is column definition in CREATE TABLE.
@@ -1560,7 +1655,7 @@ type ColumnDef struct {
 	NotNull       bool
 	DefaultExpr   *ColumnDefaultExpr   // optional
 	GeneratedExpr *GeneratedColumnExpr // optional
-	Options       *ColumnDefOptions    // optional
+	Options       *Options             // optional
 }
 
 // ColumnDefaultExpr is a default value expression for the column.
@@ -1581,7 +1676,7 @@ type ColumnDefaultExpr struct {
 //	AS ({{.Expr | sql}}) STORED
 type GeneratedColumnExpr struct {
 	// pos = As
-	// end = Stored
+	// end = Stored + 6
 
 	As     token.Pos // position of "AS" keyword
 	Stored token.Pos // position of "STORED" keyword
@@ -1589,19 +1684,6 @@ type GeneratedColumnExpr struct {
 	Expr Expr
 }
 
-// ColumnDefOption is options for column definition.
-//
-//	OPTIONS(allow_commit_timestamp = {{if .AllowCommitTimestamp}}true{{else}null{{end}}})
-type ColumnDefOptions struct {
-	// pos = Options
-	// end = Rparen + 1
-
-	Options token.Pos // position of "OPTIONS" keyword
-	Rparen  token.Pos // position of ")"
-
-	AllowCommitTimestamp bool
-}
-
 // TableConstraint is table constraint in CREATE TABLE and ALTER TABLE.
 //
 //	{{if .Name}}CONSTRAINT {{.Name}}{{end}}{{.Constraint | sql}}
@@ -1749,7 +1831,7 @@ type AlterSequence struct {
 	Alter token.Pos // position of "ALTER" keyword
 
 	Name    *Ident
-	Options *SequenceOptions
+	Options *Options
 }
 
 // AlterChangeStream is ALTER CHANGE STREAM statement node.
@@ -1887,7 +1969,7 @@ type AlterColumnSet struct {
 	Alter token.Pos // position of "ALTER" keyword
 
 	Name        *Ident
-	Options     *ColumnDefOptions
+	Options     *Options
 	DefaultExpr *ColumnDefaultExpr
 }
 
@@ -1954,20 +2036,7 @@ type CreateVectorIndex struct {
 	//
 	// Reference: https://cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#vector_index_statements
 	Where   *Where // optional
-	Options *VectorIndexOptions
-}
-
-// VectorIndexOptions is OPTIONS clause node in CREATE VECTOR INDEX.
-//
-//	OPTIONS ({{.Records | sqlJoin ","}})
-type VectorIndexOptions struct {
-	// pos = Options
-	// end = Rparen + 1
-
-	Options token.Pos // position of "OPTIONS" keyword
-	Rparen  token.Pos // position of ")"
-
-	Records []*VectorIndexOption // len(Records) > 0
+	Options *Options
 }
 
 // VectorIndexOption is OPTIONS record node.
@@ -1991,8 +2060,8 @@ type CreateChangeStream struct {
 	Create token.Pos // position of "CREATE" keyword
 
 	Name    *Ident
-	For     ChangeStreamFor      // optional
-	Options *ChangeStreamOptions // optional
+	For     ChangeStreamFor // optional
+	Options *Options        // optional
 }
 
 // ChangeStreamForAll is FOR ALL node in CREATE CHANGE STREAM
@@ -2031,30 +2100,6 @@ type ChangeStreamForTable struct {
 	Columns   []*Ident
 }
 
-// ChangeStreamOptions is OPTIONS clause node in CREATE CHANGE STREAM.
-//
-//	OPTIONS ({{.Records | sqlJoin ","}})
-type ChangeStreamOptions struct {
-	// pos = Options
-	// end = Rparen + 1
-
-	Options token.Pos // position of "OPTIONS" keyword
-	Rparen  token.Pos // position of ")"
-
-	Records []*ChangeStreamOptionsRecord // len(Records) > 0
-}
-
-// ChangeStreamOptionsRecord is OPTIONS record node.
-//
-//	{{.Key | sql}}={{.Expr | sql}}
-type ChangeStreamOptionsRecord struct {
-	// pos = Key.pos
-	// end = Value.end
-
-	Key   *Ident
-	Value Expr
-}
-
 // ChangeStreamSetFor is SET FOR tables node in ALTER CHANGE STREAM
 //
 //	SET FOR {{.For | sql}}
@@ -2087,7 +2132,7 @@ type ChangeStreamSetOptions struct {
 
 	Set token.Pos // position of "SET" keyword
 
-	Options *ChangeStreamOptions
+	Options *Options
 }
 
 // Storing is STORING clause in CREATE INDEX.
@@ -2490,27 +2535,3 @@ type UpdateItem struct {
 	Path []*Ident // len(Path) > 0
 	Expr Expr
 }
-
-// SequenceOption is option for CREATE SEQUENCE.
-//
-//	{{.Name | sql}} = {{.Value | sql}}
-type SequenceOption struct {
-	// pos = Name.pos
-	// end = Value.end
-
-	Name  *Ident
-	Value Expr
-}
-
-// SequenceOptions is OPTIONS clause node in CREATE|ALTER SEQUENCE .
-//
-//	OPTIONS ({{.Records | sqlJoin ","}})
-type SequenceOptions struct {
-	// pos = Options
-	// end = Rparen + 1
-
-	Options token.Pos // position of "OPTIONS" keyword
-	Rparen  token.Pos // position of ")"
-
-	Records []*SequenceOption // len(Records) > 0
-}
diff --git a/ast/options.go b/ast/options.go
new file mode 100644
index 0000000..931c64c
--- /dev/null
+++ b/ast/options.go
@@ -0,0 +1,94 @@
+package ast
+
+import (
+	"errors"
+	"fmt"
+	"strconv"
+)
+
+// Only the `Options` method should be placed in this file.
+
+// Field finds name in Records, and return its value as Expr.
+// The second return value indicates that the name was found in Records.
+// You should use other *Field methods if possible.
+func (o *Options) Field(name string) (expr Expr, found bool) {
+	for _, r := range o.Records {
+		if r.Name.Name != name {
+			continue
+		}
+		return r.Value, true
+	}
+	return nil, false
+}
+
+// FieldNotFound is returned Options.*Field methods.
+// It can be handled as a non-error.
+var FieldNotFound = errors.New("field not found")
+
+// fieldTypeMismatch is only defined for test.
+// It is intentionally unexported.
+var fieldTypeMismatch = errors.New("type mismatched")
+
+// BoolField finds name in records, and return its value as *bool.
+// If Options doesn't have a record with name, it returns FieldNotFound error.
+// If record have NullLiteral value, it returns nil.
+// If record have BoolLiteral value, it returns pointer of bool value.
+// If record have value which is neither NullLiteral nor BoolLiteral, it returns error.
+func (o *Options) BoolField(name string) (*bool, error) {
+	v, ok := o.Field(name)
+	if !ok {
+		return nil, FieldNotFound
+	}
+	switch v := v.(type) {
+	case *BoolLiteral:
+		return &v.Value, nil
+	case *NullLiteral:
+		return nil, nil
+	default:
+		return nil, fmt.Errorf("expect true, false or null, but got unknown type %T: %w", v, fieldTypeMismatch)
+	}
+}
+
+// IntegerField finds name in records, and return its value as *int64.
+// If Options doesn't have a record with name, it returns FieldNotFound error.
+// If record have NullLiteral value, it returns nil.
+// If record have IntegerLiteral value, it returns pointer of int64 value.
+// If record have value which is neither NullLiteral nor BoolLiteral, it returns error.
+func (o *Options) IntegerField(name string) (*int64, error) {
+	v, ok := o.Field(name)
+	if !ok {
+		return nil, FieldNotFound
+	}
+	switch v := v.(type) {
+	case *IntLiteral:
+		n, err := strconv.ParseInt(v.Value, 10, 64)
+		if err != nil {
+			return nil, err
+		}
+		return &n, nil
+	case *NullLiteral:
+		return nil, nil
+	default:
+		return nil, fmt.Errorf("expect integer or null, but got unknown type %T: %w", v, fieldTypeMismatch)
+	}
+}
+
+// StringField finds name in records, and return its value as *string.
+// If Options doesn't have a record with name, it returns FieldNotFound error.
+// If record have NullLiteral value, it returns nil.
+// If record have StringLiteral value, it returns pointer of string value.
+// If record have value which is neither NullLiteral nor StringLiteral, it returns error.
+func (o *Options) StringField(name string) (*string, error) {
+	v, ok := o.Field(name)
+	if !ok {
+		return nil, FieldNotFound
+	}
+	switch v := v.(type) {
+	case *StringLiteral:
+		return &v.Value, nil
+	case *NullLiteral:
+		return nil, nil
+	default:
+		return nil, fmt.Errorf("expect string literal or null, but got unknown type %T: %w", v, fieldTypeMismatch)
+	}
+}
diff --git a/ast/pos.go b/ast/pos.go
index c0d892e..6111f8a 100644
--- a/ast/pos.go
+++ b/ast/pos.go
@@ -367,6 +367,18 @@ func (s *SequenceArg) End() token.Pos { return s.Expr.End() }
 func (n *NamedArg) Pos() token.Pos { return n.Name.Pos() }
 func (n *NamedArg) End() token.Pos { return n.Value.End() }
 
+func (i *IgnoreNulls) Pos() token.Pos { return i.Ignore }
+func (i *IgnoreNulls) End() token.Pos { return i.Nulls + 5 }
+
+func (r *RespectNulls) Pos() token.Pos { return r.Respect }
+func (r *RespectNulls) End() token.Pos { return r.Nulls + 5 }
+
+func (h *HavingMax) Pos() token.Pos { return h.Having }
+func (h *HavingMax) End() token.Pos { return h.Expr.End() }
+
+func (h *HavingMin) Pos() token.Pos { return h.Having }
+func (h *HavingMin) End() token.Pos { return h.Expr.End() }
+
 func (c *CountStarExpr) Pos() token.Pos { return c.Count }
 func (c *CountStarExpr) End() token.Pos { return c.Rparen + 1 }
 
@@ -523,6 +535,12 @@ func (c *CastNumValue) End() token.Pos { return c.Rparen + 1 }
 //
 // ================================================================================
 
+func (g *Options) Pos() token.Pos { return g.Options }
+func (g *Options) End() token.Pos { return g.Rparen + 1 }
+
+func (g *OptionsDef) Pos() token.Pos { return g.Name.Pos() }
+func (g *OptionsDef) End() token.Pos { return g.Value.End() }
+
 func (c *CreateDatabase) Pos() token.Pos { return c.Create }
 func (c *CreateDatabase) End() token.Pos { return c.Name.End() }
 
@@ -580,10 +598,7 @@ func (g *ColumnDefaultExpr) Pos() token.Pos { return g.Default }
 func (g *ColumnDefaultExpr) End() token.Pos { return g.Rparen }
 
 func (g *GeneratedColumnExpr) Pos() token.Pos { return g.As }
-func (g *GeneratedColumnExpr) End() token.Pos { return g.Stored }
-
-func (c *ColumnDefOptions) Pos() token.Pos { return c.Options }
-func (c *ColumnDefOptions) End() token.Pos { return c.Rparen + 1 }
+func (g *GeneratedColumnExpr) End() token.Pos { return g.Stored + 6 }
 
 func (c *TableConstraint) Pos() token.Pos {
 	if c.Name != nil {
@@ -731,9 +746,6 @@ func (c *CreateChangeStream) End() token.Pos {
 	return c.Name.End()
 }
 
-func (c *ChangeStreamOptions) Pos() token.Pos { return c.Options }
-func (c *ChangeStreamOptions) End() token.Pos { return c.Rparen + 1 }
-
 func (c *ChangeStreamForAll) Pos() token.Pos    { return c.For }
 func (c *ChangeStreamForAll) End() token.Pos    { return c.All }
 func (c *ChangeStreamForTables) Pos() token.Pos { return c.For }
@@ -903,9 +915,3 @@ func (u *Update) End() token.Pos { return u.Where.End() }
 
 func (u *UpdateItem) Pos() token.Pos { return u.Path[0].Pos() }
 func (u *UpdateItem) End() token.Pos { return u.Expr.End() }
-
-func (o *SequenceOption) Pos() token.Pos { return o.Name.Pos() }
-func (o *SequenceOption) End() token.Pos { return o.Value.End() }
-
-func (o *SequenceOptions) Pos() token.Pos { return o.Options }
-func (o *SequenceOptions) End() token.Pos { return o.Rparen + 1 }
diff --git a/ast/sql.go b/ast/sql.go
index bf913bb..2bdcb60 100644
--- a/ast/sql.go
+++ b/ast/sql.go
@@ -2,6 +2,7 @@ package ast
 
 import (
 	"github.com/cloudspannerecosystem/memefish/token"
+	"strconv"
 	"strings"
 )
 
@@ -505,26 +506,20 @@ func (c *CallExpr) SQL() string {
 		sqlJoin(c.Args, ", ") +
 		strOpt(len(c.Args) > 0 && len(c.NamedArgs) > 0, ", ") +
 		sqlJoin(c.NamedArgs, ", ") +
+		sqlOpt(" ", c.NullHandling, "") +
+		sqlOpt(" ", c.Having, "") +
 		")"
 }
 
 func (n *NamedArg) SQL() string { return n.Name.SQL() + " => " + n.Value.SQL() }
 
-func (o *SequenceOption) SQL() string {
-	return o.Name.SQL() + " = " + o.Value.SQL()
-}
+func (i *IgnoreNulls) SQL() string { return "IGNORE NULLS" }
 
-func (o *SequenceOptions) SQL() string {
-	sql := "OPTIONS ("
-	for i, o := range o.Records {
-		if i > 0 {
-			sql += ", "
-		}
-		sql += o.SQL()
-	}
-	sql += ")"
-	return sql
-}
+func (r *RespectNulls) SQL() string { return "RESPECT NULLS" }
+
+func (h *HavingMax) SQL() string { return "HAVING MAX " + h.Expr.SQL() }
+
+func (h *HavingMin) SQL() string { return "HAVING MIN " + h.Expr.SQL() }
 
 func (s *ExprArg) SQL() string {
 	return s.Expr.SQL()
@@ -758,6 +753,22 @@ func (c *CastNumValue) SQL() string {
 //
 // ================================================================================
 
+func (g *Options) SQL() string { return "OPTIONS (" + sqlJoin(g.Records, ", ") + ")" }
+
+func (g *OptionsDef) SQL() string {
+	// Lowercase "null", "true", "false" is popular in option values.
+	var valueSql string
+	switch v := g.Value.(type) {
+	case *NullLiteral:
+		valueSql = "null"
+	case *BoolLiteral:
+		valueSql = strconv.FormatBool(v.Value)
+	default:
+		valueSql = g.Value.SQL()
+	}
+	return g.Name.SQL() + " = " + valueSql
+}
+
 func (c *CreateDatabase) SQL() string {
 	return "CREATE DATABASE " + c.Name.SQL()
 }
@@ -879,16 +890,6 @@ func (g *GeneratedColumnExpr) SQL() string {
 	return "AS (" + g.Expr.SQL() + ") STORED"
 }
 
-func (c *ColumnDefOptions) SQL() string {
-	sql := "OPTIONS(allow_commit_timestamp = "
-	if c.AllowCommitTimestamp {
-		sql += "true)"
-	} else {
-		sql += "null)"
-	}
-	return sql
-}
-
 func (i *IndexKey) SQL() string {
 	sql := i.Name.SQL()
 	if i.Dir != "" {
@@ -1024,31 +1025,10 @@ func (c *CreateVectorIndex) SQL() string {
 	return sql
 }
 
-func (v *VectorIndexOptions) SQL() string {
-	sql := "OPTIONS ("
-	for i, o := range v.Records {
-		if i > 0 {
-			sql += ", "
-		}
-		sql += o.SQL()
-	}
-	sql += ")"
-	return sql
-}
-
-func (v *VectorIndexOption) SQL() string {
-	return v.Key.SQL() + "=" + v.Value.SQL()
-}
-
 func (c *CreateChangeStream) SQL() string {
-	sql := "CREATE CHANGE STREAM " + c.Name.SQL()
-	if c.For != nil {
-		sql += " " + c.For.SQL()
-	}
-	if c.Options != nil {
-		sql += c.Options.SQL()
-	}
-	return sql
+	return "CREATE CHANGE STREAM " + c.Name.SQL() +
+		sqlOpt(" ", c.For, "") +
+		sqlOpt(" ", c.Options, "")
 }
 
 func (c *ChangeStreamForAll) SQL() string {
@@ -1066,18 +1046,6 @@ func (c *ChangeStreamForTables) SQL() string {
 	return sql
 }
 
-func (c *ChangeStreamOptions) SQL() string {
-	sql := " OPTIONS ("
-	for i, record := range c.Records {
-		if i > 0 {
-			sql += ", "
-		}
-		sql += record.Key.SQL() + "=" + record.Value.SQL()
-	}
-	sql += ")"
-	return sql
-}
-
 func (a *AlterChangeStream) SQL() string {
 	return "ALTER CHANGE STREAM " + a.Name.SQL() + " " + a.ChangeStreamAlteration.SQL()
 }
diff --git a/parse_helpers.go b/parse_helpers.go
new file mode 100644
index 0000000..6eaafed
--- /dev/null
+++ b/parse_helpers.go
@@ -0,0 +1,68 @@
+package memefish
+
+import (
+	"github.com/cloudspannerecosystem/memefish/ast"
+	"github.com/cloudspannerecosystem/memefish/token"
+)
+
+func newParser(filepath, s string) *Parser {
+	return &Parser{
+		Lexer: &Lexer{
+			File: &token.File{FilePath: filepath, Buffer: s},
+		},
+	}
+}
+
+// ParseStatement parses an input string containing a statement.
+// filepath can be empty, it is only used in error message.
+func ParseStatement(filepath, s string) (ast.Statement, error) {
+	return newParser(filepath, s).ParseStatement()
+}
+
+// ParseStatements parses an input string containing statements.
+// filepath can be empty, it is only used in error message.
+func ParseStatements(filepath, s string) ([]ast.Statement, error) {
+	return newParser(filepath, s).ParseStatements()
+}
+
+// ParseQuery parses an input string containing a query statement.
+// filepath can be empty, it is only used in error message.
+func ParseQuery(filepath, s string) (*ast.QueryStatement, error) {
+	return newParser(filepath, s).ParseQuery()
+}
+
+// ParseExpr parses an input string containing an expression.
+// filepath can be empty, it is only used in error message.
+func ParseExpr(filepath, s string) (ast.Expr, error) {
+	return newParser(filepath, s).ParseExpr()
+}
+
+// ParseType parses an input string containing a type.
+// filepath can be empty, it is only used in error message.
+func ParseType(filepath, s string) (ast.Type, error) {
+	return newParser(filepath, s).ParseType()
+}
+
+// ParseDDL parses an input string containing a DDL statement.
+// filepath can be empty, it is only used in error message.
+func ParseDDL(filepath, s string) (ast.DDL, error) {
+	return newParser(filepath, s).ParseDDL()
+}
+
+// ParseDDLs parses an input string containing DDL statements.
+// filepath can be empty, it is only used in error message.
+func ParseDDLs(filepath, s string) ([]ast.DDL, error) {
+	return newParser(filepath, s).ParseDDLs()
+}
+
+// ParseDML parses an input string containing a DML statement.
+// filepath can be empty, it is only used in error message.
+func ParseDML(filepath, s string) (ast.DML, error) {
+	return newParser(filepath, s).ParseDML()
+}
+
+// ParseDMLs parses an input string containing DML statements.
+// filepath can be empty, it is only used in error message.
+func ParseDMLs(filepath, s string) ([]ast.DML, error) {
+	return newParser(filepath, s).ParseDMLs()
+}
diff --git a/parser.go b/parser.go
index 8242f81..47cab7b 100644
--- a/parser.go
+++ b/parser.go
@@ -1492,15 +1492,21 @@ func (p *Parser) parseCall(id token.Token) ast.Expr {
 		p.nextToken()
 	}
 
+	nullHandling := p.tryParseNullHandlingModifier()
+	having := p.tryParseHavingModifier()
+
 	rparen := p.expect(")").Pos
 	return &ast.CallExpr{
-		Rparen:    rparen,
-		Func:      fn,
-		Distinct:  distinct,
-		Args:      args,
-		NamedArgs: namedArgs,
+		Rparen:       rparen,
+		Func:         fn,
+		Distinct:     distinct,
+		Args:         args,
+		NamedArgs:    namedArgs,
+		NullHandling: nullHandling,
+		Having:       having,
 	}
 }
+
 func (p *Parser) lookaheadNamedArg() bool {
 	lexer := p.Lexer.Clone()
 	defer func() {
@@ -1574,6 +1580,58 @@ func (p *Parser) parseExprArg() *ast.ExprArg {
 	}
 }
 
+func (p *Parser) tryParseHavingModifier() ast.HavingModifier {
+	if p.Token.Kind != "HAVING" {
+		return nil
+	}
+
+	having := p.expect("HAVING").Pos
+
+	switch {
+	case p.Token.IsKeywordLike("MIN"):
+		p.nextToken()
+		expr := p.parseExpr()
+
+		return &ast.HavingMin{
+			Having: having,
+			Expr:   expr,
+		}
+	case p.Token.IsKeywordLike("MAX"):
+		p.nextToken()
+		expr := p.parseExpr()
+
+		return &ast.HavingMax{
+			Having: having,
+			Expr:   expr,
+		}
+	default:
+		panic(p.errorfAtToken(&p.Token, `expect MIN or MAX, but: %v`, p.Token.Kind))
+	}
+}
+
+func (p *Parser) tryParseNullHandlingModifier() ast.NullHandlingModifier {
+	switch p.Token.Kind {
+	case "IGNORE":
+		ignore := p.expect("IGNORE").Pos
+		nulls := p.expect("NULLS").Pos
+
+		return &ast.IgnoreNulls{
+			Ignore: ignore,
+			Nulls:  nulls,
+		}
+	case "RESPECT":
+		respect := p.expect("RESPECT").Pos
+		nulls := p.expect("NULLS").Pos
+
+		return &ast.RespectNulls{
+			Respect: respect,
+			Nulls:   nulls,
+		}
+	default:
+		return nil
+	}
+}
+
 func (p *Parser) parseCaseExpr() *ast.CaseExpr {
 	pos := p.expect("CASE").Pos
 	var expr ast.Expr
@@ -2204,51 +2262,11 @@ func (p *Parser) parseCreateTable(pos token.Pos) *ast.CreateTable {
 	}
 }
 
-func (p *Parser) parseSequenceOptions(pos token.Pos) *ast.SequenceOptions {
-	p.expectKeywordLike("OPTIONS")
-	p.expect("(")
-	var records []*ast.SequenceOption
-	for p.Token.Kind != token.TokenEOF {
-		if p.Token.Kind == ")" {
-			break
-		}
-		optionName := p.parseIdent()
-		p.expect("=")
-		var value ast.Expr
-		switch p.Token.Kind {
-		case token.TokenInt:
-			value = p.parseIntLiteral()
-		case token.TokenString:
-			value = p.parseStringLiteral()
-		default:
-			p.panicfAtToken(&p.Token, "expected token: <int>, <string>, but: %s", p.Token.Kind)
-		}
-		records = append(records, &ast.SequenceOption{
-			Name:  optionName,
-			Value: value,
-		})
-		if p.Token.Kind != "," {
-			break
-		}
-		p.nextToken()
-
-	}
-	rparen := p.expect(")").Pos
-	if len(records) == 0 {
-		p.panicfAtToken(&p.Token, "required at least one option")
-	}
-	return &ast.SequenceOptions{
-		Options: pos,
-		Rparen:  rparen,
-		Records: records,
-	}
-}
-
 func (p *Parser) parseCreateSequence(pos token.Pos) *ast.CreateSequence {
 	p.expectKeywordLike("SEQUENCE")
 	ifNotExists := p.parseIfNotExists()
 	name := p.parseIdent()
-	options := p.parseSequenceOptions(p.Token.Pos)
+	options := p.parseOptions()
 
 	return &ast.CreateSequence{
 		Create:      pos,
@@ -2301,7 +2319,7 @@ func (p *Parser) parseColumnDef() *ast.ColumnDef {
 	t, notNull, null := p.parseTypeNotNull()
 	defaultExpr := p.tryParseColumnDefaultExpr()
 	generated := p.tryParseGeneratedColumnExpr()
-	options := p.tryParseColumnDefOptions()
+	options := p.tryParseOptions()
 
 	return &ast.ColumnDef{
 		Null:          null,
@@ -2406,53 +2424,19 @@ func (p *Parser) tryParseGeneratedColumnExpr() *ast.GeneratedColumnExpr {
 		return nil
 	}
 
-	posAs := p.expect("AS").Pos
+	pos := p.expect("AS").Pos
 	p.expect("(")
 	expr := p.parseExpr()
 	p.expect(")")
-	posEnd := p.expectKeywordLike("STORED").End
+	stored := p.expectKeywordLike("STORED").Pos
 
 	return &ast.GeneratedColumnExpr{
-		As:     posAs,
-		Stored: posEnd,
+		As:     pos,
+		Stored: stored,
 		Expr:   expr,
 	}
 }
 
-func (p *Parser) tryParseColumnDefOptions() *ast.ColumnDefOptions {
-	if !p.Token.IsKeywordLike("OPTIONS") {
-		return nil
-	}
-
-	return p.parseColumnDefOptions()
-}
-
-func (p *Parser) parseColumnDefOptions() *ast.ColumnDefOptions {
-	pos := p.expectKeywordLike("OPTIONS").Pos
-
-	p.expect("(")
-	p.expectIdent("allow_commit_timestamp")
-	p.expect("=")
-
-	var allowCommitTimestamp bool
-	switch p.Token.Kind {
-	case "TRUE":
-		allowCommitTimestamp = true
-	case "NULL":
-		allowCommitTimestamp = false
-	default:
-		p.panicfAtToken(&p.Token, "expected token: TRUE, NULL, but: %s", p.Token.Kind)
-	}
-	p.nextToken()
-
-	rparen := p.expect(")").End
-	return &ast.ColumnDefOptions{
-		Options:              pos,
-		Rparen:               rparen,
-		AllowCommitTimestamp: allowCommitTimestamp,
-	}
-}
-
 func (p *Parser) parseIndexKey() *ast.IndexKey {
 	name := p.parseIdent()
 	dir, dirPos := p.tryParseDirection()
@@ -2550,28 +2534,34 @@ func (p *Parser) parseOnDeleteAction() (onDelete ast.OnDeleteAction, onDeleteEnd
 	return
 }
 
-func (p *Parser) parseVectorIndexOptions() *ast.VectorIndexOptions {
+func (p *Parser) parseOptionsDef() *ast.OptionsDef {
+	key := p.parseIdent()
+	p.expect("=")
+	value := p.parseExpr()
+	return &ast.OptionsDef{
+		Name:  key,
+		Value: value,
+	}
+}
+
+func (p *Parser) tryParseOptions() *ast.Options {
+	if !p.Token.IsKeywordLike("OPTIONS") {
+		return nil
+	}
+	return p.parseOptions()
+}
+
+func (p *Parser) parseOptions() *ast.Options {
 	pos := p.expectKeywordLike("OPTIONS").Pos
 	p.expect("(")
-	options := &ast.VectorIndexOptions{Options: pos}
-	for {
-		key := p.parseIdent()
-		p.expect("=")
-		value := p.parseExpr()
-		options.Records = append(options.Records, &ast.VectorIndexOption{Key: key, Value: value})
-		if p.Token.Kind == "," {
-			p.nextToken()
-			continue
-		}
-		if p.Token.Kind == ")" {
-			options.Rparen = p.Token.Pos
-			p.nextToken()
-			break
-		}
-		p.panicfAtToken(&p.Token, "expected expr or , or ), but: %s", p.Token.AsString)
-	}
+	optionsDefs := parseCommaSeparatedList(p, p.parseOptionsDef)
+	rparen := p.expect(")").Pos
 
-	return options
+	return &ast.Options{
+		Options: pos,
+		Rparen:  rparen,
+		Records: optionsDefs,
+	}
 }
 
 func (p *Parser) parseCreateVectorIndex(pos token.Pos) *ast.CreateVectorIndex {
@@ -2586,7 +2576,7 @@ func (p *Parser) parseCreateVectorIndex(pos token.Pos) *ast.CreateVectorIndex {
 	p.expect(")")
 
 	where := p.tryParseWhere()
-	options := p.parseVectorIndexOptions()
+	options := p.parseOptions()
 
 	return &ast.CreateVectorIndex{
 		Create:      pos,
@@ -2664,7 +2654,7 @@ func (p *Parser) parseCreateChangeStream(pos token.Pos) *ast.CreateChangeStream
 		cs.For = p.parseChangeStreamFor()
 	}
 	if p.Token.IsKeywordLike("OPTIONS") {
-		cs.Options = p.parseChangeStreamOptions()
+		cs.Options = p.parseOptions()
 	}
 	return cs
 
@@ -2690,7 +2680,7 @@ func (p *Parser) parseAlterChangeStream(pos token.Pos) *ast.AlterChangeStream {
 		} else if p.Token.IsKeywordLike("OPTIONS") {
 			cs.ChangeStreamAlteration = &ast.ChangeStreamSetOptions{
 				Set:     setpos,
-				Options: p.parseChangeStreamOptions(),
+				Options: p.parseOptions(),
 			}
 			return cs
 		}
@@ -2757,34 +2747,6 @@ func (p *Parser) parseChangeStreamFor() ast.ChangeStreamFor {
 
 }
 
-// Parse CHANGE STREAM OPTIONS.
-// We parse any expressions in OPTIONS. even if tokens includes unsupported expressions.
-// This is for the key, value which will supported in the future.
-// We don't need to modify this code for them.
-func (p *Parser) parseChangeStreamOptions() *ast.ChangeStreamOptions {
-	pos := p.expectKeywordLike("OPTIONS").Pos
-	p.expect("(")
-	cso := &ast.ChangeStreamOptions{Options: pos}
-	for {
-		key := p.parseIdent()
-		p.expect("=")
-		value := p.parseExpr()
-		cso.Records = append(cso.Records, &ast.ChangeStreamOptionsRecord{Key: key, Value: value})
-		if p.Token.Kind == "," {
-			p.nextToken()
-			continue
-		}
-		if p.Token.Kind == ")" {
-			cso.Rparen = p.Token.Pos
-			p.nextToken()
-			break
-		}
-		p.panicfAtToken(&p.Token, "expected expr or , or ), but: %s", p.Token.AsString)
-	}
-
-	return cso
-}
-
 func (p *Parser) tryParseStoring() *ast.Storing {
 	if !p.Token.IsKeywordLike("STORING") {
 		return nil
@@ -2970,7 +2932,7 @@ func (p *Parser) parseAlterColumn() ast.TableAlteration {
 				DefaultExpr: defaultExpr,
 			}
 		} else {
-			options := p.parseColumnDefOptions()
+			options := p.parseOptions()
 			return &ast.AlterColumnSet{
 				Alter:   pos,
 				Name:    name,
@@ -3017,7 +2979,7 @@ func (p *Parser) parseAlterSequence(pos token.Pos) *ast.AlterSequence {
 	p.expectKeywordLike("SEQUENCE")
 	name := p.parseIdent()
 	p.expect("SET")
-	options := p.parseSequenceOptions(p.Token.Pos)
+	options := p.parseOptions()
 
 	return &ast.AlterSequence{
 		Alter:   pos,
@@ -3275,7 +3237,7 @@ func (p *Parser) parseSchemaType() ast.SchemaType {
 		pos := p.expect("ARRAY").Pos
 		p.expect("<")
 		t := p.parseScalarSchemaType()
-		end := p.expect(">").End
+		end := p.expect(">").Pos
 		return &ast.ArraySchemaType{
 			Array: pos,
 			Gt:    end,


Test patch:
diff --git a/ast/options_test.go b/ast/options_test.go
new file mode 100644
index 0000000..ad952d5
--- /dev/null
+++ b/ast/options_test.go
@@ -0,0 +1,232 @@
+package ast
+
+import (
+	"errors"
+	"github.com/google/go-cmp/cmp"
+	"testing"
+)
+
+func TestOptions_BoolField(t *testing.T) {
+	tcases := []struct {
+		desc    string
+		input   *Options
+		name    string
+		want    *bool
+		wantErr error
+	}{
+		{
+			desc: "true",
+			input: &Options{
+				Records: []*OptionsDef{
+					{&Ident{Name: "bool_option"}, &BoolLiteral{Value: true}},
+				},
+			},
+			name: "bool_option",
+			want: ptr(true),
+		},
+		{
+			desc: "false",
+			input: &Options{
+				Records: []*OptionsDef{
+					{&Ident{Name: "bool_option"}, &BoolLiteral{Value: false}},
+				},
+			},
+			name: "bool_option",
+			want: ptr(false),
+		},
+		{
+			desc: "explicit null",
+			input: &Options{
+				Records: []*OptionsDef{
+					{&Ident{Name: "bool_option"}, &NullLiteral{}},
+				},
+			},
+			name: "bool_option",
+			want: nil,
+		},
+		{
+			desc: "implicit null",
+			input: &Options{
+				Records: []*OptionsDef{
+					{&Ident{Name: "dummy"}, &BoolLiteral{Value: true}},
+				},
+			},
+			name:    "bool_option",
+			want:    nil,
+			wantErr: FieldNotFound,
+		},
+		{
+			desc: "invalid type",
+			input: &Options{
+				Records: []*OptionsDef{
+					{&Ident{Name: "string_option"}, &StringLiteral{Value: "foo"}},
+				},
+			},
+			name:    "string_option",
+			wantErr: fieldTypeMismatch,
+		},
+	}
+
+	for _, tcase := range tcases {
+		t.Run(tcase.desc, func(t *testing.T) {
+			got, err := tcase.input.BoolField(tcase.name)
+			if tcase.wantErr == nil && err != nil {
+				t.Errorf("should not fail, but: %v", err)
+			}
+			if tcase.wantErr != nil && err == nil {
+				t.Errorf("should fail, but success")
+			}
+			if tcase.wantErr != nil && !errors.Is(err, tcase.wantErr) {
+				t.Errorf("error differ, want: %v, got: %v", tcase.wantErr, err)
+			}
+			if diff := cmp.Diff(tcase.want, got); diff != "" {
+				t.Errorf("differ: %v", diff)
+			}
+		})
+	}
+}
+
+func TestOptions_StringField(t *testing.T) {
+	tcases := []struct {
+		desc    string
+		input   *Options
+		name    string
+		want    *string
+		wantErr error
+	}{
+		{
+			desc: "string",
+			input: &Options{
+				Records: []*OptionsDef{
+					{&Ident{Name: "string_option"}, &StringLiteral{Value: "foo"}},
+				},
+			},
+			name: "string_option",
+			want: ptr("foo"),
+		},
+		{
+			desc: "explicit null",
+			input: &Options{
+				Records: []*OptionsDef{
+					{&Ident{Name: "string_option"}, &NullLiteral{}},
+				},
+			},
+			name: "string_option",
+			want: nil,
+		},
+		{
+			desc: "implicit null",
+			input: &Options{
+				Records: []*OptionsDef{
+					{&Ident{Name: "dummy_option"}, &StringLiteral{Value: "foo"}},
+				},
+			},
+			name:    "string_field",
+			want:    nil,
+			wantErr: FieldNotFound,
+		},
+		{
+			desc: "invalid value",
+			input: &Options{
+				Records: []*OptionsDef{
+					{&Ident{Name: "bool_option"}, &BoolLiteral{Value: true}},
+				},
+			},
+			name:    "bool_option",
+			wantErr: fieldTypeMismatch,
+		},
+	}
+
+	for _, tcase := range tcases {
+		t.Run(tcase.desc, func(t *testing.T) {
+			got, err := tcase.input.StringField(tcase.name)
+			if tcase.wantErr == nil && err != nil {
+				t.Errorf("should not fail, but: %v", err)
+			}
+			if tcase.wantErr != nil && err == nil {
+				t.Errorf("should fail, but success")
+			}
+			if tcase.wantErr != nil && !errors.Is(err, tcase.wantErr) {
+				t.Errorf("error differ, want: %v, got: %v", tcase.wantErr, err)
+			}
+			if diff := cmp.Diff(tcase.want, got); diff != "" {
+				t.Errorf("differ: %v", diff)
+			}
+		})
+	}
+}
+
+func TestOptions_IntegerField(t *testing.T) {
+	tcases := []struct {
+		desc    string
+		input   *Options
+		name    string
+		want    *int64
+		wantErr error
+	}{
+		{
+			desc: "integer",
+			input: &Options{
+				Records: []*OptionsDef{
+					{&Ident{Name: "integer_option"}, &IntLiteral{Value: "7"}},
+				},
+			},
+			name: "integer_option",
+			want: ptr(int64(7)),
+		},
+		{
+			desc: "explicit null",
+			input: &Options{
+				Records: []*OptionsDef{
+					{&Ident{Name: "integer_option"}, &NullLiteral{}},
+				},
+			},
+			name: "integer_option",
+			want: nil,
+		},
+		{
+			desc: "implicit null",
+			input: &Options{
+				Records: []*OptionsDef{
+					{&Ident{Name: "string_option"}, &StringLiteral{Value: "foo"}},
+				},
+			},
+			name:    "integer_option",
+			want:    nil,
+			wantErr: FieldNotFound,
+		},
+		{
+			desc: "invalid value",
+			input: &Options{
+				Records: []*OptionsDef{
+					{&Ident{Name: "bool_option"}, &BoolLiteral{Value: true}},
+				},
+			},
+			name:    "bool_option",
+			wantErr: fieldTypeMismatch,
+		},
+	}
+
+	for _, tcase := range tcases {
+		t.Run(tcase.desc, func(t *testing.T) {
+			got, err := tcase.input.IntegerField(tcase.name)
+			if tcase.wantErr == nil && err != nil {
+				t.Errorf("should not fail, but: %v", err)
+			}
+			if tcase.wantErr != nil && err == nil {
+				t.Errorf("should fail, but success")
+			}
+			if tcase.wantErr != nil && !errors.Is(err, tcase.wantErr) {
+				t.Errorf("error differ, want: %v, got: %v", tcase.wantErr, err)
+			}
+			if diff := cmp.Diff(tcase.want, got); diff != "" {
+				t.Errorf("differ: %v", diff)
+			}
+		})
+	}
+
+}
+
+func ptr[T any](v T) *T {
+	return &v
+}
diff --git a/ast/pos_test.go b/ast/pos_test.go
index 48ebb94..f7f54bf 100644
--- a/ast/pos_test.go
+++ b/ast/pos_test.go
@@ -35,7 +35,7 @@ func TestTableAlteration_Position(t *testing.T) {
 		t.Fatalf("Mismatched end postion of the alter column set")
 	}
 	alterColumnSetOptions := AlterColumnSet{
-		Options: &ColumnDefOptions{Rparen: 104},
+		Options: &Options{Rparen: 104},
 	}
 	if alterColumnSetOptions.End() != alterColumnSetOptions.Options.End() {
 		t.Fatalf("Mismatched end postion of the alter column set")
diff --git a/parse_helpers_test.go b/parse_helpers_test.go
new file mode 100644
index 0000000..12291b9
--- /dev/null
+++ b/parse_helpers_test.go
@@ -0,0 +1,151 @@
+package memefish_test
+
+import (
+	"fmt"
+	"github.com/MakeNowJust/heredoc/v2"
+	"github.com/cloudspannerecosystem/memefish"
+)
+
+func ExampleParseStatement() {
+	stmt, err := memefish.ParseStatement("path/to/file.sql", "SELECT * FROM foo")
+	if err != nil {
+		panic(err)
+	}
+
+	fmt.Println(stmt.SQL())
+
+	// Output:
+	// SELECT * FROM foo
+}
+
+func ExampleParseStatements() {
+	stmts, err := memefish.ParseStatements("path/to/file.sql", "SELECT 1; INSERT foo (x, y) VALUES (1, 2)")
+	if err != nil {
+		panic(err)
+	}
+
+	for _, stmt := range stmts {
+		fmt.Printf("%s;\n", stmt.SQL())
+	}
+
+	// Output:
+	// SELECT 1;
+	// INSERT INTO foo (x, y) VALUES (1, 2);
+}
+
+func ExampleParseQuery() {
+	stmt, err := memefish.ParseQuery("path/to/file.sql", "SELECT * FROM foo")
+	if err != nil {
+		panic(err)
+	}
+
+	fmt.Println(stmt.SQL())
+
+	// Output:
+	// SELECT * FROM foo
+}
+
+func ExampleParseExpr() {
+	stmt, err := memefish.ParseExpr("", "a * b")
+	if err != nil {
+		panic(err)
+	}
+
+	fmt.Println(stmt.SQL())
+
+	// Output:
+	// a * b
+}
+
+func ExampleParseType() {
+	stmt, err := memefish.ParseType("", "ARRAY<STRUCT<n INT64, s STRING>>")
+	if err != nil {
+		panic(err)
+	}
+
+	fmt.Println(stmt.SQL())
+
+	// Output:
+	// ARRAY<STRUCT<n INT64, s STRING>>
+}
+func ExampleParseDDL() {
+	sql := heredoc.Doc(`
+		CREATE TABLE foo (
+			x int64,
+			y int64,
+		) PRIMARY KEY (x)
+	`)
+
+	ddl, err := memefish.ParseDDL("path/to/file.sql", sql)
+	if err != nil {
+		panic(err)
+	}
+
+	fmt.Println(ddl.SQL())
+
+	// Output:
+	// CREATE TABLE foo (x INT64, y INT64) PRIMARY KEY (x)
+}
+
+func ExampleParseDDLs() {
+	sql := heredoc.Doc(`
+		CREATE TABLE foo (x int64, y int64) PRIMARY KEY (x);
+
+		CREATE TABLE bar (
+			x int64, z int64,
+		)
+		PRIMARY KEY (x, z),
+		INTERLEAVE IN PARENT foo;
+	`)
+
+	ddls, err := memefish.ParseDDLs("path/to/file.sql", sql)
+	if err != nil {
+		panic(err)
+	}
+
+	for _, ddl := range ddls {
+		fmt.Printf("%s;\n", ddl.SQL())
+	}
+
+	// Output:
+	// CREATE TABLE foo (x INT64, y INT64) PRIMARY KEY (x);
+	// CREATE TABLE bar (x INT64, z INT64) PRIMARY KEY (x, z), INTERLEAVE IN PARENT foo;
+}
+
+func ExampleParseDML() {
+	sql := heredoc.Doc(`
+		INSERT INTO foo (x, y)
+		VALUES (1, 2),
+		       (3, 4)
+	`)
+
+	dml, err := memefish.ParseDML("path/to/file.sql", sql)
+	if err != nil {
+		panic(err)
+	}
+
+	fmt.Println(dml.SQL())
+
+	// Output:
+	// INSERT INTO foo (x, y) VALUES (1, 2), (3, 4)
+}
+
+func ExampleParseDMLs() {
+	sql := heredoc.Doc(`
+		INSERT INTO foo (x, y) VALUES (1, 2), (3, 4);
+		DELETE FROM foo WHERE foo.x = 1 AND foo.y = 2;
+	`)
+
+	dmls, err := memefish.ParseDMLs("path/to/file.sql", sql)
+	if err != nil {
+		panic(err)
+	}
+
+	for _, dml := range dmls {
+		fmt.Printf("%s;\n", dml.SQL())
+	}
+
+	// Output:
+	// INSERT INTO foo (x, y) VALUES (1, 2), (3, 4);
+	// DELETE FROM foo WHERE foo.x = 1 AND foo.y = 2;
+}
diff --git a/parser_test.go b/parser_test.go
index dbe2ed2..1f5ef07 100644
--- a/parser_test.go
+++ b/parser_test.go
@@ -9,7 +9,6 @@ import (
 	"path/filepath"
 	"testing"
 
-	"github.com/MakeNowJust/heredoc/v2"
 	"github.com/cloudspannerecosystem/memefish"
 	"github.com/cloudspannerecosystem/memefish/ast"
 	"github.com/cloudspannerecosystem/memefish/token"
@@ -177,148 +176,3 @@ func TestParseStatement(t *testing.T) {
 		})
 	}
 }
-
-func ExampleParser_ParseStatements() {
-	p := &memefish.Parser{
-		Lexer: &memefish.Lexer{
-			File: &token.File{Buffer: "SELECT 1; INSERT foo (x, y) VALUES (1, 2)"},
-		},
-	}
-
-	stmts, err := p.ParseStatements()
-	if err != nil {
-		panic(err)
-	}
-
-	for _, stmt := range stmts {
-		fmt.Printf("%s;\n", stmt.SQL())
-	}
-
-	// Output:
-	// SELECT 1;
-	// INSERT INTO foo (x, y) VALUES (1, 2);
-}
-
-func ExampleParser_ParseQuery() {
-	p := &memefish.Parser{
-		Lexer: &memefish.Lexer{
-			File: &token.File{Buffer: "SELECT * FROM foo"},
-		},
-	}
-
-	stmt, err := p.ParseQuery()
-	if err != nil {
-		panic(err)
-	}
-
-	fmt.Println(stmt.SQL())
-
-	// Output:
-	// SELECT * FROM foo
-}
-
-func ExampleParser_ParseDDL() {
-	p := &memefish.Parser{
-		Lexer: &memefish.Lexer{
-			File: &token.File{
-				Buffer: heredoc.Doc(`
-					CREATE TABLE foo (
-						x int64,
-						y int64,
-					) PRIMARY KEY (x)
-				`),
-			},
-		},
-	}
-
-	ddl, err := p.ParseDDL()
-	if err != nil {
-		panic(err)
-	}
-
-	fmt.Println(ddl.SQL())
-
-	// Output:
-	// CREATE TABLE foo (x INT64, y INT64) PRIMARY KEY (x)
-}
-
-func ExampleParser_ParseDDLs() {
-	p := &memefish.Parser{
-		Lexer: &memefish.Lexer{
-			File: &token.File{
-				Buffer: heredoc.Doc(`
-					CREATE TABLE foo (x int64, y int64) PRIMARY KEY (x);
-
-					CREATE TABLE bar (
-						x int64, z int64,
-					)
-					PRIMARY KEY (x, z),
-					INTERLEAVE IN PARENT foo;
-				`),
-			},
-		},
-	}
-
-	ddls, err := p.ParseDDLs()
-	if err != nil {
-		panic(err)
-	}
-
-	for _, ddl := range ddls {
-		fmt.Printf("%s;\n", ddl.SQL())
-	}
-
-	// Output:
-	// CREATE TABLE foo (x INT64, y INT64) PRIMARY KEY (x);
-	// CREATE TABLE bar (x INT64, z INT64) PRIMARY KEY (x, z), INTERLEAVE IN PARENT foo;
-}
-
-func ExampleParser_ParseDML() {
-	p := &memefish.Parser{
-		Lexer: &memefish.Lexer{
-			File: &token.File{
-				Buffer: heredoc.Doc(`
-					INSERT INTO foo (x, y)
-					VALUES (1, 2),
-					       (3, 4)
-				`),
-			},
-		},
-	}
-
-	dml, err := p.ParseDML()
-	if err != nil {
-		panic(err)
-	}
-
-	fmt.Println(dml.SQL())
-
-	// Output:
-	// INSERT INTO foo (x, y) VALUES (1, 2), (3, 4)
-}
-
-func ExampleParser_ParseDMLs() {
-	p := &memefish.Parser{
-		Lexer: &memefish.Lexer{
-			File: &token.File{
-				Buffer: heredoc.Doc(`
-					INSERT INTO foo (x, y) VALUES (1, 2), (3, 4);
-					DELETE FROM foo WHERE foo.x = 1 AND foo.y = 2;
-				`),
-			},
-		},
-	}
-
-	dmls, err := p.ParseDMLs()
-	if err != nil {
-		panic(err)
-	}
-
-	for _, dml := range dmls {
-		fmt.Printf("%s;\n", dml.SQL())
-	}
-
-	// Output:
-	// INSERT INTO foo (x, y) VALUES (1, 2), (3, 4);
-	// DELETE FROM foo WHERE foo.x = 1 AND foo.y = 2;
-}
diff --git a/testdata/input/query/aggregate_function_calls.sql b/testdata/input/query/aggregate_function_calls.sql
new file mode 100644
index 0000000..b921739
--- /dev/null
+++ b/testdata/input/query/aggregate_function_calls.sql
@@ -0,0 +1,6 @@
+SELECT
+    ARRAY_AGG(inches HAVING MAX year),
+    ARRAY_AGG(inches HAVING MIN year),
+    ARRAY_AGG(inches IGNORE NULLS),
+    ARRAY_AGG(inches RESPECT NULLS),
+    ARRAY_AGG(inches RESPECT NULLS HAVING MAX year),
diff --git a/testdata/result/ddl/alter_change_stream_options.sql.txt b/testdata/result/ddl/alter_change_stream_options.sql.txt
index 29591d2..0069de7 100644
--- a/testdata/result/ddl/alter_change_stream_options.sql.txt
+++ b/testdata/result/ddl/alter_change_stream_options.sql.txt
@@ -11,12 +11,12 @@ ALTER CHANGE STREAM change_stream_name SET OPTIONS (retention_period = '1d', val
   },
   ChangeStreamAlteration: &ast.ChangeStreamSetOptions{
     Set:     39,
-    Options: &ast.ChangeStreamOptions{
+    Options: &ast.Options{
       Options: 43,
       Rparen:  118,
-      Records: []*ast.ChangeStreamOptionsRecord{
-        &ast.ChangeStreamOptionsRecord{
-          Key: &ast.Ident{
+      Records: []*ast.OptionsDef{
+        &ast.OptionsDef{
+          Name: &ast.Ident{
             NamePos: 52,
             NameEnd: 68,
             Name:    "retention_period",
@@ -27,8 +27,8 @@ ALTER CHANGE STREAM change_stream_name SET OPTIONS (retention_period = '1d', val
             Value:    "1d",
           },
         },
-        &ast.ChangeStreamOptionsRecord{
-          Key: &ast.Ident{
+        &ast.OptionsDef{
+          Name: &ast.Ident{
             NamePos: 77,
             NameEnd: 95,
             Name:    "value_capture_type",
@@ -45,4 +45,4 @@ ALTER CHANGE STREAM change_stream_name SET OPTIONS (retention_period = '1d', val
 }
 
 --- SQL
-ALTER CHANGE STREAM change_stream_name SET  OPTIONS (retention_period="1d", value_capture_type="OLD_AND_NEW_VALUES")
+ALTER CHANGE STREAM change_stream_name SET OPTIONS (retention_period = "1d", value_capture_type = "OLD_AND_NEW_VALUES")
diff --git a/testdata/result/ddl/alter_sequence.sql.txt b/testdata/result/ddl/alter_sequence.sql.txt
index c8e8305..dd88bf9 100644
--- a/testdata/result/ddl/alter_sequence.sql.txt
+++ b/testdata/result/ddl/alter_sequence.sql.txt
@@ -9,11 +9,11 @@ ALTER SEQUENCE my_sequence SET OPTIONS (skip_range_min=1, skip_range_max=1234567
     NameEnd: 26,
     Name:    "my_sequence",
   },
-  Options: &ast.SequenceOptions{
+  Options: &ast.Options{
     Options: 31,
     Rparen:  80,
-    Records: []*ast.SequenceOption{
-      &ast.SequenceOption{
+    Records: []*ast.OptionsDef{
+      &ast.OptionsDef{
         Name: &ast.Ident{
           NamePos: 40,
           NameEnd: 54,
@@ -26,7 +26,7 @@ ALTER SEQUENCE my_sequence SET OPTIONS (skip_range_min=1, skip_range_max=1234567
           Value:    "1",
         },
       },
-      &ast.SequenceOption{
+      &ast.OptionsDef{
         Name: &ast.Ident{
           NamePos: 58,
           NameEnd: 72,
diff --git a/testdata/result/ddl/alter_table_add_column.sql.txt b/testdata/result/ddl/alter_table_add_column.sql.txt
index b3003ef..d64d67b 100644
--- a/testdata/result/ddl/alter_table_add_column.sql.txt
+++ b/testdata/result/ddl/alter_table_add_column.sql.txt
@@ -28,7 +28,7 @@ alter table foo add column baz string(max) not null
       NotNull:       true,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
 }
diff --git a/testdata/result/ddl/alter_table_add_column_if_not_exists.sql.txt b/testdata/result/ddl/alter_table_add_column_if_not_exists.sql.txt
index 3d0c276..143c229 100644
--- a/testdata/result/ddl/alter_table_add_column_if_not_exists.sql.txt
+++ b/testdata/result/ddl/alter_table_add_column_if_not_exists.sql.txt
@@ -29,7 +29,7 @@ alter table foo add column if not exists baz string(max) not null
       NotNull:       true,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
 }
diff --git a/testdata/result/ddl/alter_table_add_column_with_if_expression.sql.txt b/testdata/result/ddl/alter_table_add_column_with_if_expression.sql.txt
index 296ec2a..6624b33 100644
--- a/testdata/result/ddl/alter_table_add_column_with_if_expression.sql.txt
+++ b/testdata/result/ddl/alter_table_add_column_with_if_expression.sql.txt
@@ -26,7 +26,7 @@ ALTER TABLE foo ADD COLUMN expired_at TIMESTAMP AS (IF (status != "OPEN" AND sta
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: &ast.GeneratedColumnExpr{
         As:     48,
-        Stored: 159,
+        Stored: 153,
         Expr:   &ast.CallExpr{
           Rparen: 150,
           Func:   &ast.Ident{
@@ -99,7 +99,9 @@ ALTER TABLE foo ADD COLUMN expired_at TIMESTAMP AS (IF (status != "OPEN" AND sta
                     },
                   },
                 },
-                NamedArgs: []*ast.NamedArg(nil),
+                NamedArgs:    []*ast.NamedArg(nil),
+                NullHandling: nil,
+                Having:       nil,
               },
             },
             &ast.ExprArg{
@@ -108,10 +110,12 @@ ALTER TABLE foo ADD COLUMN expired_at TIMESTAMP AS (IF (status != "OPEN" AND sta
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
       },
-      Options: (*ast.ColumnDefOptions)(nil),
+      Options: (*ast.Options)(nil),
     },
   },
 }
diff --git a/testdata/result/ddl/alter_table_alter_column_set.sql.txt b/testdata/result/ddl/alter_table_alter_column_set.sql.txt
index 9b7245d..421e02d 100644
--- a/testdata/result/ddl/alter_table_alter_column_set.sql.txt
+++ b/testdata/result/ddl/alter_table_alter_column_set.sql.txt
@@ -15,14 +15,26 @@ alter table foo alter column foo set options(allow_commit_timestamp = true)
       NameEnd: 32,
       Name:    "foo",
     },
-    Options: &ast.ColumnDefOptions{
-      Options:              37,
-      Rparen:               75,
-      AllowCommitTimestamp: true,
+    Options: &ast.Options{
+      Options: 37,
+      Rparen:  74,
+      Records: []*ast.OptionsDef{
+        &ast.OptionsDef{
+          Name: &ast.Ident{
+            NamePos: 45,
+            NameEnd: 67,
+            Name:    "allow_commit_timestamp",
+          },
+          Value: &ast.BoolLiteral{
+            ValuePos: 70,
+            Value:    true,
+          },
+        },
+      },
     },
     DefaultExpr: (*ast.ColumnDefaultExpr)(nil),
   },
 }
 
 --- SQL
-ALTER TABLE foo ALTER COLUMN foo SET OPTIONS(allow_commit_timestamp = true)
+ALTER TABLE foo ALTER COLUMN foo SET OPTIONS (allow_commit_timestamp = true)
diff --git a/testdata/result/ddl/alter_table_alter_column_set_default.sql.txt b/testdata/result/ddl/alter_table_alter_column_set_default.sql.txt
index 33148d0..2c582df 100644
--- a/testdata/result/ddl/alter_table_alter_column_set_default.sql.txt
+++ b/testdata/result/ddl/alter_table_alter_column_set_default.sql.txt
@@ -15,7 +15,7 @@ ALTER TABLE actions ALTER COLUMN output SET DEFAULT("")
       NameEnd: 39,
       Name:    "output",
     },
-    Options:     (*ast.ColumnDefOptions)(nil),
+    Options:     (*ast.Options)(nil),
     DefaultExpr: &ast.ColumnDefaultExpr{
       Default: 44,
       Rparen:  54,
diff --git a/testdata/result/ddl/create_change_stream.sql.txt b/testdata/result/ddl/create_change_stream.sql.txt
index 08efb8b..323175f 100644
--- a/testdata/result/ddl/create_change_stream.sql.txt
+++ b/testdata/result/ddl/create_change_stream.sql.txt
@@ -10,7 +10,7 @@ CREATE CHANGE STREAM change_stream_name
     Name:    "change_stream_name",
   },
   For:     nil,
-  Options: (*ast.ChangeStreamOptions)(nil),
+  Options: (*ast.Options)(nil),
 }
 
 --- SQL
diff --git a/testdata/result/ddl/create_change_stream_for_all.sql.txt b/testdata/result/ddl/create_change_stream_for_all.sql.txt
index 3bc320c..ae3ae9d 100644
--- a/testdata/result/ddl/create_change_stream_for_all.sql.txt
+++ b/testdata/result/ddl/create_change_stream_for_all.sql.txt
@@ -13,7 +13,7 @@ CREATE CHANGE STREAM change_stream_name FOR ALL
     For: 40,
     All: 48,
   },
-  Options: (*ast.ChangeStreamOptions)(nil),
+  Options: (*ast.Options)(nil),
 }
 
 --- SQL
diff --git a/testdata/result/ddl/create_change_stream_for_multiple_table_columns.sql.txt b/testdata/result/ddl/create_change_stream_for_multiple_table_columns.sql.txt
index 508c30b..7f223ba 100644
--- a/testdata/result/ddl/create_change_stream_for_multiple_table_columns.sql.txt
+++ b/testdata/result/ddl/create_change_stream_for_multiple_table_columns.sql.txt
@@ -54,7 +54,7 @@ CREATE CHANGE STREAM change_stream_name FOR table_name1(column1, column2), table
       },
     },
   },
-  Options: (*ast.ChangeStreamOptions)(nil),
+  Options: (*ast.Options)(nil),
 }
 
 --- SQL
diff --git a/testdata/result/ddl/create_change_stream_for_table.sql.txt b/testdata/result/ddl/create_change_stream_for_table.sql.txt
index 2b27ab7..e208fda 100644
--- a/testdata/result/ddl/create_change_stream_for_table.sql.txt
+++ b/testdata/result/ddl/create_change_stream_for_table.sql.txt
@@ -23,7 +23,7 @@ CREATE CHANGE STREAM change_stream_name FOR table_name
       },
     },
   },
-  Options: (*ast.ChangeStreamOptions)(nil),
+  Options: (*ast.Options)(nil),
 }
 
 --- SQL
diff --git a/testdata/result/ddl/create_change_stream_for_table_column.sql.txt b/testdata/result/ddl/create_change_stream_for_table_column.sql.txt
index 895d322..8829eac 100644
--- a/testdata/result/ddl/create_change_stream_for_table_column.sql.txt
+++ b/testdata/result/ddl/create_change_stream_for_table_column.sql.txt
@@ -29,7 +29,7 @@ CREATE CHANGE STREAM change_stream_name FOR table_name(column)
       },
     },
   },
-  Options: (*ast.ChangeStreamOptions)(nil),
+  Options: (*ast.Options)(nil),
 }
 
 --- SQL
diff --git a/testdata/result/ddl/create_change_stream_for_table_columns.sql.txt b/testdata/result/ddl/create_change_stream_for_table_columns.sql.txt
index 40f9f7f..320c4dd 100644
--- a/testdata/result/ddl/create_change_stream_for_table_columns.sql.txt
+++ b/testdata/result/ddl/create_change_stream_for_table_columns.sql.txt
@@ -34,7 +34,7 @@ CREATE CHANGE STREAM change_stream_name FOR table_name(column1, column2)
       },
     },
   },
-  Options: (*ast.ChangeStreamOptions)(nil),
+  Options: (*ast.Options)(nil),
 }
 
 --- SQL
diff --git a/testdata/result/ddl/create_change_stream_for_tables.sql.txt b/testdata/result/ddl/create_change_stream_for_tables.sql.txt
index b877bac..d152e46 100644
--- a/testdata/result/ddl/create_change_stream_for_tables.sql.txt
+++ b/testdata/result/ddl/create_change_stream_for_tables.sql.txt
@@ -32,7 +32,7 @@ CREATE CHANGE STREAM change_stream_name FOR table_name1, table_name2
       },
     },
   },
-  Options: (*ast.ChangeStreamOptions)(nil),
+  Options: (*ast.Options)(nil),
 }
 
 --- SQL
diff --git a/testdata/result/ddl/create_change_stream_for_tables_with_option.sql.txt b/testdata/result/ddl/create_change_stream_for_tables_with_option.sql.txt
index f8cb97c..7f1778c 100644
--- a/testdata/result/ddl/create_change_stream_for_tables_with_option.sql.txt
+++ b/testdata/result/ddl/create_change_stream_for_tables_with_option.sql.txt
@@ -55,12 +55,12 @@ OPTIONS(retention_period = '1d')
       },
     },
   },
-  Options: &ast.ChangeStreamOptions{
+  Options: &ast.Options{
     Options: 105,
     Rparen:  136,
-    Records: []*ast.ChangeStreamOptionsRecord{
-      &ast.ChangeStreamOptionsRecord{
-        Key: &ast.Ident{
+    Records: []*ast.OptionsDef{
+      &ast.OptionsDef{
+        Name: &ast.Ident{
           NamePos: 113,
           NameEnd: 129,
           Name:    "retention_period",
@@ -76,4 +76,4 @@ OPTIONS(retention_period = '1d')
 }
 
 --- SQL
-CREATE CHANGE STREAM change_stream_name FOR table_name1(column1, column2), table_name2(column1, column2) OPTIONS (retention_period="1d")
+CREATE CHANGE STREAM change_stream_name FOR table_name1(column1, column2), table_name2(column1, column2) OPTIONS (retention_period = "1d")
diff --git a/testdata/result/ddl/create_change_stream_for_tables_with_option_null.sql.txt b/testdata/result/ddl/create_change_stream_for_tables_with_option_null.sql.txt
index 7bb6e07..1ff7f95 100644
--- a/testdata/result/ddl/create_change_stream_for_tables_with_option_null.sql.txt
+++ b/testdata/result/ddl/create_change_stream_for_tables_with_option_null.sql.txt
@@ -55,12 +55,12 @@ OPTIONS(retention_period = null)
       },
     },
   },
-  Options: &ast.ChangeStreamOptions{
+  Options: &ast.Options{
     Options: 105,
     Rparen:  136,
-    Records: []*ast.ChangeStreamOptionsRecord{
-      &ast.ChangeStreamOptionsRecord{
-        Key: &ast.Ident{
+    Records: []*ast.OptionsDef{
+      &ast.OptionsDef{
+        Name: &ast.Ident{
           NamePos: 113,
           NameEnd: 129,
           Name:    "retention_period",
@@ -74,4 +74,4 @@ OPTIONS(retention_period = null)
 }
 
 --- SQL
-CREATE CHANGE STREAM change_stream_name FOR table_name1(column1, column2), table_name2(column1, column2) OPTIONS (retention_period=NULL)
+CREATE CHANGE STREAM change_stream_name FOR table_name1(column1, column2), table_name2(column1, column2) OPTIONS (retention_period = null)
diff --git a/testdata/result/ddl/create_change_stream_for_tables_with_options.sql.txt b/testdata/result/ddl/create_change_stream_for_tables_with_options.sql.txt
index 94e6612..ecb4350 100644
--- a/testdata/result/ddl/create_change_stream_for_tables_with_options.sql.txt
+++ b/testdata/result/ddl/create_change_stream_for_tables_with_options.sql.txt
@@ -55,12 +55,12 @@ OPTIONS(retention_period = '1d', value_capture_type = 'NEW_ROW')
       },
     },
   },
-  Options: &ast.ChangeStreamOptions{
+  Options: &ast.Options{
     Options: 105,
     Rparen:  168,
-    Records: []*ast.ChangeStreamOptionsRecord{
-      &ast.ChangeStreamOptionsRecord{
-        Key: &ast.Ident{
+    Records: []*ast.OptionsDef{
+      &ast.OptionsDef{
+        Name: &ast.Ident{
           NamePos: 113,
           NameEnd: 129,
           Name:    "retention_period",
@@ -71,8 +71,8 @@ OPTIONS(retention_period = '1d', value_capture_type = 'NEW_ROW')
           Value:    "1d",
         },
       },
-      &ast.ChangeStreamOptionsRecord{
-        Key: &ast.Ident{
+      &ast.OptionsDef{
+        Name: &ast.Ident{
           NamePos: 138,
           NameEnd: 156,
           Name:    "value_capture_type",
@@ -88,4 +88,4 @@ OPTIONS(retention_period = '1d', value_capture_type = 'NEW_ROW')
 }
 
 --- SQL
-CREATE CHANGE STREAM change_stream_name FOR table_name1(column1, column2), table_name2(column1, column2) OPTIONS (retention_period="1d", value_capture_type="NEW_ROW")
+CREATE CHANGE STREAM change_stream_name FOR table_name1(column1, column2), table_name2(column1, column2) OPTIONS (retention_period = "1d", value_capture_type = "NEW_ROW")
diff --git a/testdata/result/ddl/create_sequance.sql.txt b/testdata/result/ddl/create_sequance.sql.txt
index 9d6a557..e0bf76d 100644
--- a/testdata/result/ddl/create_sequance.sql.txt
+++ b/testdata/result/ddl/create_sequance.sql.txt
@@ -14,11 +14,11 @@ CREATE SEQUENCE IF NOT EXISTS MySequence OPTIONS (
     Name:    "MySequence",
   },
   IfNotExists: true,
-  Options:     &ast.SequenceOptions{
+  Options:     &ast.Options{
     Options: 41,
     Rparen:  172,
-    Records: []*ast.SequenceOption{
-      &ast.SequenceOption{
+    Records: []*ast.OptionsDef{
+      &ast.OptionsDef{
         Name: &ast.Ident{
           NamePos: 55,
           NameEnd: 68,
@@ -30,7 +30,7 @@ CREATE SEQUENCE IF NOT EXISTS MySequence OPTIONS (
           Value:    "bit_reversed_positive",
         },
       },
-      &ast.SequenceOption{
+      &ast.OptionsDef{
         Name: &ast.Ident{
           NamePos: 98,
           NameEnd: 112,
@@ -43,7 +43,7 @@ CREATE SEQUENCE IF NOT EXISTS MySequence OPTIONS (
           Value:    "1",
         },
       },
-      &ast.SequenceOption{
+      &ast.OptionsDef{
         Name: &ast.Ident{
           NamePos: 122,
           NameEnd: 136,
@@ -56,7 +56,7 @@ CREATE SEQUENCE IF NOT EXISTS MySequence OPTIONS (
           Value:    "1000",
         },
       },
-      &ast.SequenceOption{
+      &ast.OptionsDef{
         Name: &ast.Ident{
           NamePos: 149,
           NameEnd: 167,
diff --git a/testdata/result/ddl/create_table.sql.txt b/testdata/result/ddl/create_table.sql.txt
index e263d4b..03c0ed7 100644
--- a/testdata/result/ddl/create_table.sql.txt
+++ b/testdata/result/ddl/create_table.sql.txt
@@ -39,7 +39,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: 50,
@@ -55,7 +55,7 @@ create table foo (
       NotNull:       true,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: 78,
@@ -79,10 +79,21 @@ create table foo (
       NotNull:       true,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       &ast.ColumnDefOptions{
-        Options:              83,
-        Rparen:               121,
-        AllowCommitTimestamp: false,
+      Options:       &ast.Options{
+        Options: 83,
+        Rparen:  120,
+        Records: []*ast.OptionsDef{
+          &ast.OptionsDef{
+            Name: &ast.Ident{
+              NamePos: 91,
+              NameEnd: 113,
+              Name:    "allow_commit_timestamp",
+            },
+            Value: &ast.NullLiteral{
+              Null: 116,
+            },
+          },
+        },
       },
     },
     &ast.ColumnDef{
@@ -108,7 +119,7 @@ create table foo (
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: &ast.GeneratedColumnExpr{
         As:     150,
-        Stored: 178,
+        Stored: 172,
         Expr:   &ast.CallExpr{
           Rparen: 169,
           Func:   &ast.Ident{
@@ -133,10 +144,12 @@ create table foo (
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
       },
-      Options: (*ast.ColumnDefOptions)(nil),
+      Options: (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -152,7 +165,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: 515,
@@ -176,13 +189,15 @@ create table foo (
             NameEnd: 546,
             Name:    "current_timestamp",
           },
-          Distinct:  false,
-          Args:      []ast.Arg(nil),
-          NamedArgs: []*ast.NamedArg(nil),
+          Distinct:     false,
+          Args:         []ast.Arg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
       },
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
   TableConstraints: []*ast.TableConstraint{
@@ -390,4 +405,4 @@ create table foo (
 }
 
 --- SQL
-CREATE TABLE foo (foo INT64, bar FLOAT64 NOT NULL, baz STRING(255) NOT NULL OPTIONS(allow_commit_timestamp = null), qux STRING(255) NOT NULL AS (concat(baz, "a")) STORED, quux JSON, corge TIMESTAMP NOT NULL DEFAULT (current_timestamp()), FOREIGN KEY (foo) REFERENCES t2 (t2key1), FOREIGN KEY (bar) REFERENCES t2 (t2key2) ON DELETE CASCADE, FOREIGN KEY (baz) REFERENCES t2 (t2key3) ON DELETE NO ACTION, CONSTRAINT fkname FOREIGN KEY (foo, bar) REFERENCES t2 (t2key1, t2key2), CHECK (foo > 0), CONSTRAINT cname CHECK (bar > 0)) PRIMARY KEY (foo, bar)
+CREATE TABLE foo (foo INT64, bar FLOAT64 NOT NULL, baz STRING(255) NOT NULL OPTIONS (allow_commit_timestamp = null), qux STRING(255) NOT NULL AS (concat(baz, "a")) STORED, quux JSON, corge TIMESTAMP NOT NULL DEFAULT (current_timestamp()), FOREIGN KEY (foo) REFERENCES t2 (t2key1), FOREIGN KEY (bar) REFERENCES t2 (t2key2) ON DELETE CASCADE, FOREIGN KEY (baz) REFERENCES t2 (t2key3) ON DELETE NO ACTION, CONSTRAINT fkname FOREIGN KEY (foo, bar) REFERENCES t2 (t2key1, t2key2), CHECK (foo > 0), CONSTRAINT cname CHECK (bar > 0)) PRIMARY KEY (foo, bar)
diff --git a/testdata/result/ddl/create_table_cluster.sql.txt b/testdata/result/ddl/create_table_cluster.sql.txt
index 8eb83c3..1cf81fb 100644
--- a/testdata/result/ddl/create_table_cluster.sql.txt
+++ b/testdata/result/ddl/create_table_cluster.sql.txt
@@ -30,7 +30,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -46,7 +46,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
   TableConstraints: []*ast.TableConstraint(nil),
diff --git a/testdata/result/ddl/create_table_cluster_and_row_deletion_policy.sql.txt b/testdata/result/ddl/create_table_cluster_and_row_deletion_policy.sql.txt
index 26aa3fb..53a04b5 100644
--- a/testdata/result/ddl/create_table_cluster_and_row_deletion_policy.sql.txt
+++ b/testdata/result/ddl/create_table_cluster_and_row_deletion_policy.sql.txt
@@ -32,7 +32,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -48,7 +48,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -64,7 +64,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
   TableConstraints: []*ast.TableConstraint(nil),
diff --git a/testdata/result/ddl/create_table_cluster_on_delete_no_action.sql.txt b/testdata/result/ddl/create_table_cluster_on_delete_no_action.sql.txt
index c2caa5c..fda3185 100644
--- a/testdata/result/ddl/create_table_cluster_on_delete_no_action.sql.txt
+++ b/testdata/result/ddl/create_table_cluster_on_delete_no_action.sql.txt
@@ -29,7 +29,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
   TableConstraints: []*ast.TableConstraint(nil),
diff --git a/testdata/result/ddl/create_table_cluster_set_on_delete.sql.txt b/testdata/result/ddl/create_table_cluster_set_on_delete.sql.txt
index 4de2bcc..130bd04 100644
--- a/testdata/result/ddl/create_table_cluster_set_on_delete.sql.txt
+++ b/testdata/result/ddl/create_table_cluster_set_on_delete.sql.txt
@@ -29,7 +29,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
   TableConstraints: []*ast.TableConstraint(nil),
diff --git a/testdata/result/ddl/create_table_if_not_exists.sql.txt b/testdata/result/ddl/create_table_if_not_exists.sql.txt
index 1677b5c..18ae6e0 100644
--- a/testdata/result/ddl/create_table_if_not_exists.sql.txt
+++ b/testdata/result/ddl/create_table_if_not_exists.sql.txt
@@ -29,7 +29,7 @@ create table if not exists foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: 64,
@@ -45,7 +45,7 @@ create table if not exists foo (
       NotNull:       true,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
   TableConstraints: []*ast.TableConstraint(nil),
diff --git a/testdata/result/ddl/create_table_row_deletion_policy.sql.txt b/testdata/result/ddl/create_table_row_deletion_policy.sql.txt
index 4a49748..a82a2a4 100644
--- a/testdata/result/ddl/create_table_row_deletion_policy.sql.txt
+++ b/testdata/result/ddl/create_table_row_deletion_policy.sql.txt
@@ -31,7 +31,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -47,7 +47,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -63,7 +63,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
   TableConstraints:  []*ast.TableConstraint(nil),
diff --git a/testdata/result/ddl/create_table_trailing_comma.sql.txt b/testdata/result/ddl/create_table_trailing_comma.sql.txt
index 0ff16b9..9440fd6 100644
--- a/testdata/result/ddl/create_table_trailing_comma.sql.txt
+++ b/testdata/result/ddl/create_table_trailing_comma.sql.txt
@@ -32,7 +32,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -48,7 +48,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
   TableConstraints: []*ast.TableConstraint(nil),
diff --git a/testdata/result/ddl/create_table_types.sql.txt b/testdata/result/ddl/create_table_types.sql.txt
index f7f536d..3afd6e7 100644
--- a/testdata/result/ddl/create_table_types.sql.txt
+++ b/testdata/result/ddl/create_table_types.sql.txt
@@ -40,7 +40,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -56,7 +56,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -72,7 +72,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -88,7 +88,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -104,7 +104,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -120,7 +120,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -144,7 +144,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -163,7 +163,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -187,7 +187,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -206,7 +206,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -217,7 +217,7 @@ create table types (
       },
       Type: &ast.ArraySchemaType{
         Array: 174,
-        Gt:    185,
+        Gt:    184,
         Item:  &ast.ScalarSchemaType{
           NamePos: 180,
           Name:    "BOOL",
@@ -226,7 +226,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -237,7 +237,7 @@ create table types (
       },
       Type: &ast.ArraySchemaType{
         Array: 193,
-        Gt:    210,
+        Gt:    209,
         Item:  &ast.SizedSchemaType{
           NamePos: 199,
           Rparen:  208,
@@ -249,7 +249,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -275,7 +275,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
   TableConstraints: []*ast.TableConstraint(nil),
diff --git a/testdata/result/ddl/create_table_with_sequence_function.sql.txt b/testdata/result/ddl/create_table_with_sequence_function.sql.txt
index d3fac4a..f2b827c 100644
--- a/testdata/result/ddl/create_table_with_sequence_function.sql.txt
+++ b/testdata/result/ddl/create_table_with_sequence_function.sql.txt
@@ -50,11 +50,13 @@ CREATE TABLE foo
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
       },
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -73,7 +75,7 @@ CREATE TABLE foo
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -89,7 +91,7 @@ CREATE TABLE foo
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
   TableConstraints: []*ast.TableConstraint(nil),
diff --git a/testdata/result/ddl/create_vector_index.sql.txt b/testdata/result/ddl/create_vector_index.sql.txt
index 7d5cf1e..ed7d5a6 100644
--- a/testdata/result/ddl/create_vector_index.sql.txt
+++ b/testdata/result/ddl/create_vector_index.sql.txt
@@ -21,12 +21,12 @@ OPTIONS(distance_type = 'COSINE')
     Name:    "embedding",
   },
   Where:   (*ast.Where)(nil),
-  Options: &ast.VectorIndexOptions{
+  Options: &ast.Options{
     Options: 73,
     Rparen:  105,
-    Records: []*ast.VectorIndexOption{
-      &ast.VectorIndexOption{
-        Key: &ast.Ident{
+    Records: []*ast.OptionsDef{
+      &ast.OptionsDef{
+        Name: &ast.Ident{
           NamePos: 81,
           NameEnd: 94,
           Name:    "distance_type",
@@ -42,4 +42,4 @@ OPTIONS(distance_type = 'COSINE')
 }
 
 --- SQL
-CREATE VECTOR INDEX IF NOT EXISTS hello_vector_index ON hello (embedding) OPTIONS (distance_type="COSINE")
+CREATE VECTOR INDEX IF NOT EXISTS hello_vector_index ON hello (embedding) OPTIONS (distance_type = "COSINE")
diff --git a/testdata/result/ddl/create_vector_index_where_column_not_null.sql.txt b/testdata/result/ddl/create_vector_index_where_column_not_null.sql.txt
index da02696..965499a 100644
--- a/testdata/result/ddl/create_vector_index_where_column_not_null.sql.txt
+++ b/testdata/result/ddl/create_vector_index_where_column_not_null.sql.txt
@@ -33,12 +33,12 @@ OPTIONS(distance_type = 'COSINE')
       },
     },
   },
-  Options: &ast.VectorIndexOptions{
+  Options: &ast.Options{
     Options: 87,
     Rparen:  119,
-    Records: []*ast.VectorIndexOption{
-      &ast.VectorIndexOption{
-        Key: &ast.Ident{
+    Records: []*ast.OptionsDef{
+      &ast.OptionsDef{
+        Name: &ast.Ident{
           NamePos: 95,
           NameEnd: 108,
           Name:    "distance_type",
@@ -54,4 +54,4 @@ OPTIONS(distance_type = 'COSINE')
 }
 
 --- SQL
-CREATE VECTOR INDEX hello_vector_index ON hello (embedding) WHERE embedding IS NOT NULL OPTIONS (distance_type="COSINE")
+CREATE VECTOR INDEX hello_vector_index ON hello (embedding) WHERE embedding IS NOT NULL OPTIONS (distance_type = "COSINE")
diff --git a/testdata/result/dml/insert_with_sequence_function.sql.txt b/testdata/result/dml/insert_with_sequence_function.sql.txt
index 2424b7a..7f5a4b8 100644
--- a/testdata/result/dml/insert_with_sequence_function.sql.txt
+++ b/testdata/result/dml/insert_with_sequence_function.sql.txt
@@ -45,7 +45,9 @@ INSERT INTO foo(bar) VALUES (GET_NEXT_SEQUENCE_VALUE(SEQUENCE my_sequence))
                   },
                 },
               },
-              NamedArgs: []*ast.NamedArg(nil),
+              NamedArgs:    []*ast.NamedArg(nil),
+              NullHandling: nil,
+              Having:       nil,
             },
           },
         },
diff --git a/testdata/result/query/aggregate_function_calls.sql.txt b/testdata/result/query/aggregate_function_calls.sql.txt
new file mode 100644
index 0000000..9e2ad6c
--- /dev/null
+++ b/testdata/result/query/aggregate_function_calls.sql.txt
@@ -0,0 +1,174 @@
+--- aggregate_function_calls.sql
+SELECT
+    ARRAY_AGG(inches HAVING MAX year),
+    ARRAY_AGG(inches HAVING MIN year),
+    ARRAY_AGG(inches IGNORE NULLS),
+    ARRAY_AGG(inches RESPECT NULLS),
+    ARRAY_AGG(inches RESPECT NULLS HAVING MAX year),
+
+--- AST
+&ast.QueryStatement{
+  Hint:  (*ast.Hint)(nil),
+  With:  (*ast.With)(nil),
+  Query: &ast.Select{
+    Select:   0,
+    Distinct: false,
+    As:       nil,
+    Results:  []ast.SelectItem{
+      &ast.ExprSelectItem{
+        Expr: &ast.CallExpr{
+          Rparen: 43,
+          Func:   &ast.Ident{
+            NamePos: 11,
+            NameEnd: 20,
+            Name:    "ARRAY_AGG",
+          },
+          Distinct: false,
+          Args:     []ast.Arg{
+            &ast.ExprArg{
+              Expr: &ast.Ident{
+                NamePos: 21,
+                NameEnd: 27,
+                Name:    "inches",
+              },
+            },
+          },
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       &ast.HavingMax{
+            Having: 28,
+            Expr:   &ast.Ident{
+              NamePos: 39,
+              NameEnd: 43,
+              Name:    "year",
+            },
+          },
+        },
+      },
+      &ast.ExprSelectItem{
+        Expr: &ast.CallExpr{
+          Rparen: 82,
+          Func:   &ast.Ident{
+            NamePos: 50,
+            NameEnd: 59,
+            Name:    "ARRAY_AGG",
+          },
+          Distinct: false,
+          Args:     []ast.Arg{
+            &ast.ExprArg{
+              Expr: &ast.Ident{
+                NamePos: 60,
+                NameEnd: 66,
+                Name:    "inches",
+              },
+            },
+          },
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       &ast.HavingMin{
+            Having: 67,
+            Expr:   &ast.Ident{
+              NamePos: 78,
+              NameEnd: 82,
+              Name:    "year",
+            },
+          },
+        },
+      },
+      &ast.ExprSelectItem{
+        Expr: &ast.CallExpr{
+          Rparen: 118,
+          Func:   &ast.Ident{
+            NamePos: 89,
+            NameEnd: 98,
+            Name:    "ARRAY_AGG",
+          },
+          Distinct: false,
+          Args:     []ast.Arg{
+            &ast.ExprArg{
+              Expr: &ast.Ident{
+                NamePos: 99,
+                NameEnd: 105,
+                Name:    "inches",
+              },
+            },
+          },
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: &ast.IgnoreNulls{
+            Ignore: 106,
+            Nulls:  113,
+          },
+          Having: nil,
+        },
+      },
+      &ast.ExprSelectItem{
+        Expr: &ast.CallExpr{
+          Rparen: 155,
+          Func:   &ast.Ident{
+            NamePos: 125,
+            NameEnd: 134,
+            Name:    "ARRAY_AGG",
+          },
+          Distinct: false,
+          Args:     []ast.Arg{
+            &ast.ExprArg{
+              Expr: &ast.Ident{
+                NamePos: 135,
+                NameEnd: 141,
+                Name:    "inches",
+              },
+            },
+          },
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: &ast.RespectNulls{
+            Respect: 142,
+            Nulls:   150,
+          },
+          Having: nil,
+        },
+      },
+      &ast.ExprSelectItem{
+        Expr: &ast.CallExpr{
+          Rparen: 208,
+          Func:   &ast.Ident{
+            NamePos: 162,
+            NameEnd: 171,
+            Name:    "ARRAY_AGG",
+          },
+          Distinct: false,
+          Args:     []ast.Arg{
+            &ast.ExprArg{
+              Expr: &ast.Ident{
+                NamePos: 172,
+                NameEnd: 178,
+                Name:    "inches",
+              },
+            },
+          },
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: &ast.RespectNulls{
+            Respect: 179,
+            Nulls:   187,
+          },
+          Having: &ast.HavingMax{
+            Having: 193,
+            Expr:   &ast.Ident{
+              NamePos: 204,
+              NameEnd: 208,
+              Name:    "year",
+            },
+          },
+        },
+      },
+    },
+    From:    (*ast.From)(nil),
+    Where:   (*ast.Where)(nil),
+    GroupBy: (*ast.GroupBy)(nil),
+    Having:  (*ast.Having)(nil),
+    OrderBy: (*ast.OrderBy)(nil),
+    Limit:   (*ast.Limit)(nil),
+  },
+}
+
+--- SQL
+SELECT ARRAY_AGG(inches HAVING MAX year), ARRAY_AGG(inches HAVING MIN year), ARRAY_AGG(inches IGNORE NULLS), ARRAY_AGG(inches RESPECT NULLS), ARRAY_AGG(inches RESPECT NULLS HAVING MAX year)
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 347e513..78d1d15 100644
--- a/testdata/result/query/select_call_with_named_expr.sql.txt
+++ b/testdata/result/query/select_call_with_named_expr.sql.txt
@@ -137,6 +137,8 @@ WHERE a.SingerId = 1 AND SEARCH(a.DescriptionTokens, 'classic albums', enhance_q
               },
             },
           },
+          NullHandling: nil,
+          Having:       nil,
         },
       },
     },
diff --git a/testdata/result/query/select_count_distinct.sql.txt b/testdata/result/query/select_count_distinct.sql.txt
index 4cb7d98..76755e4 100644
--- a/testdata/result/query/select_count_distinct.sql.txt
+++ b/testdata/result/query/select_count_distinct.sql.txt
@@ -27,7 +27,9 @@ select count(distinct x) from foo
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
       },
     },
diff --git a/testdata/result/query/select_expr.sql.txt b/testdata/result/query/select_expr.sql.txt
index eeb46ce..e6c89c3 100644
--- a/testdata/result/query/select_expr.sql.txt
+++ b/testdata/result/query/select_expr.sql.txt
@@ -617,7 +617,9 @@ select 1 + 2, 1 - 2,
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
       },
       &ast.ExprSelectItem{
@@ -655,7 +657,9 @@ select 1 + 2, 1 - 2,
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
       },
       &ast.ExprSelectItem{
@@ -899,7 +903,9 @@ select 1 + 2, 1 - 2,
                 },
               },
             },
-            NamedArgs: []*ast.NamedArg(nil),
+            NamedArgs:    []*ast.NamedArg(nil),
+            NullHandling: nil,
+            Having:       nil,
           },
           Right: &ast.IntLiteral{
             ValuePos: 790,
diff --git a/testdata/result/query/select_literals_all.sql.txt b/testdata/result/query/select_literals_all.sql.txt
index b8556bb..3e35cc3 100644
--- a/testdata/result/query/select_literals_all.sql.txt
+++ b/testdata/result/query/select_literals_all.sql.txt
@@ -504,7 +504,9 @@ lines''',
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
       },
       &ast.ExprSelectItem{
@@ -555,7 +557,9 @@ lines''',
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
       },
       &ast.ExprSelectItem{
diff --git a/testdata/result/query/select_literals_date.sql.txt b/testdata/result/query/select_literals_date.sql.txt
index 1ccd8fb..fd6b359 100644
--- a/testdata/result/query/select_literals_date.sql.txt
+++ b/testdata/result/query/select_literals_date.sql.txt
@@ -48,7 +48,9 @@ SELECT
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
       },
       &ast.ExprSelectItem{
@@ -109,7 +111,9 @@ SELECT
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
       },
       &ast.ExprSelectItem{
@@ -140,7 +144,9 @@ SELECT
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
         As: &ast.AsAlias{
           As:    -1,
@@ -169,7 +175,9 @@ SELECT
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
         As: &ast.AsAlias{
           As:    -1,
diff --git a/testdata/result/query/select_with_sequence_function.sql.txt b/testdata/result/query/select_with_sequence_function.sql.txt
index 1abdadb..491d47f 100644
--- a/testdata/result/query/select_with_sequence_function.sql.txt
+++ b/testdata/result/query/select_with_sequence_function.sql.txt
@@ -29,7 +29,9 @@ SELECT GET_NEXT_SEQUENCE_VALUE(SEQUENCE my_sequence) as next_id
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
         As: &ast.AsAlias{
           As:    -1,
diff --git a/testdata/result/statement/aggregate_function_calls.sql.txt b/testdata/result/statement/aggregate_function_calls.sql.txt
new file mode 100644
index 0000000..9e2ad6c
--- /dev/null
+++ b/testdata/result/statement/aggregate_function_calls.sql.txt
@@ -0,0 +1,174 @@
+--- aggregate_function_calls.sql
+SELECT
+    ARRAY_AGG(inches HAVING MAX year),
+    ARRAY_AGG(inches HAVING MIN year),
+    ARRAY_AGG(inches IGNORE NULLS),
+    ARRAY_AGG(inches RESPECT NULLS),
+    ARRAY_AGG(inches RESPECT NULLS HAVING MAX year),
+
+--- AST
+&ast.QueryStatement{
+  Hint:  (*ast.Hint)(nil),
+  With:  (*ast.With)(nil),
+  Query: &ast.Select{
+    Select:   0,
+    Distinct: false,
+    As:       nil,
+    Results:  []ast.SelectItem{
+      &ast.ExprSelectItem{
+        Expr: &ast.CallExpr{
+          Rparen: 43,
+          Func:   &ast.Ident{
+            NamePos: 11,
+            NameEnd: 20,
+            Name:    "ARRAY_AGG",
+          },
+          Distinct: false,
+          Args:     []ast.Arg{
+            &ast.ExprArg{
+              Expr: &ast.Ident{
+                NamePos: 21,
+                NameEnd: 27,
+                Name:    "inches",
+              },
+            },
+          },
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       &ast.HavingMax{
+            Having: 28,
+            Expr:   &ast.Ident{
+              NamePos: 39,
+              NameEnd: 43,
+              Name:    "year",
+            },
+          },
+        },
+      },
+      &ast.ExprSelectItem{
+        Expr: &ast.CallExpr{
+          Rparen: 82,
+          Func:   &ast.Ident{
+            NamePos: 50,
+            NameEnd: 59,
+            Name:    "ARRAY_AGG",
+          },
+          Distinct: false,
+          Args:     []ast.Arg{
+            &ast.ExprArg{
+              Expr: &ast.Ident{
+                NamePos: 60,
+                NameEnd: 66,
+                Name:    "inches",
+              },
+            },
+          },
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       &ast.HavingMin{
+            Having: 67,
+            Expr:   &ast.Ident{
+              NamePos: 78,
+              NameEnd: 82,
+              Name:    "year",
+            },
+          },
+        },
+      },
+      &ast.ExprSelectItem{
+        Expr: &ast.CallExpr{
+          Rparen: 118,
+          Func:   &ast.Ident{
+            NamePos: 89,
+            NameEnd: 98,
+            Name:    "ARRAY_AGG",
+          },
+          Distinct: false,
+          Args:     []ast.Arg{
+            &ast.ExprArg{
+              Expr: &ast.Ident{
+                NamePos: 99,
+                NameEnd: 105,
+                Name:    "inches",
+              },
+            },
+          },
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: &ast.IgnoreNulls{
+            Ignore: 106,
+            Nulls:  113,
+          },
+          Having: nil,
+        },
+      },
+      &ast.ExprSelectItem{
+        Expr: &ast.CallExpr{
+          Rparen: 155,
+          Func:   &ast.Ident{
+            NamePos: 125,
+            NameEnd: 134,
+            Name:    "ARRAY_AGG",
+          },
+          Distinct: false,
+          Args:     []ast.Arg{
+            &ast.ExprArg{
+              Expr: &ast.Ident{
+                NamePos: 135,
+                NameEnd: 141,
+                Name:    "inches",
+              },
+            },
+          },
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: &ast.RespectNulls{
+            Respect: 142,
+            Nulls:   150,
+          },
+          Having: nil,
+        },
+      },
+      &ast.ExprSelectItem{
+        Expr: &ast.CallExpr{
+          Rparen: 208,
+          Func:   &ast.Ident{
+            NamePos: 162,
+            NameEnd: 171,
+            Name:    "ARRAY_AGG",
+          },
+          Distinct: false,
+          Args:     []ast.Arg{
+            &ast.ExprArg{
+              Expr: &ast.Ident{
+                NamePos: 172,
+                NameEnd: 178,
+                Name:    "inches",
+              },
+            },
+          },
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: &ast.RespectNulls{
+            Respect: 179,
+            Nulls:   187,
+          },
+          Having: &ast.HavingMax{
+            Having: 193,
+            Expr:   &ast.Ident{
+              NamePos: 204,
+              NameEnd: 208,
+              Name:    "year",
+            },
+          },
+        },
+      },
+    },
+    From:    (*ast.From)(nil),
+    Where:   (*ast.Where)(nil),
+    GroupBy: (*ast.GroupBy)(nil),
+    Having:  (*ast.Having)(nil),
+    OrderBy: (*ast.OrderBy)(nil),
+    Limit:   (*ast.Limit)(nil),
+  },
+}
+
+--- SQL
+SELECT ARRAY_AGG(inches HAVING MAX year), ARRAY_AGG(inches HAVING MIN year), ARRAY_AGG(inches IGNORE NULLS), ARRAY_AGG(inches RESPECT NULLS), ARRAY_AGG(inches RESPECT NULLS HAVING MAX year)
diff --git a/testdata/result/statement/alter_change_stream_options.sql.txt b/testdata/result/statement/alter_change_stream_options.sql.txt
index 29591d2..0069de7 100644
--- a/testdata/result/statement/alter_change_stream_options.sql.txt
+++ b/testdata/result/statement/alter_change_stream_options.sql.txt
@@ -11,12 +11,12 @@ ALTER CHANGE STREAM change_stream_name SET OPTIONS (retention_period = '1d', val
   },
   ChangeStreamAlteration: &ast.ChangeStreamSetOptions{
     Set:     39,
-    Options: &ast.ChangeStreamOptions{
+    Options: &ast.Options{
       Options: 43,
       Rparen:  118,
-      Records: []*ast.ChangeStreamOptionsRecord{
-        &ast.ChangeStreamOptionsRecord{
-          Key: &ast.Ident{
+      Records: []*ast.OptionsDef{
+        &ast.OptionsDef{
+          Name: &ast.Ident{
             NamePos: 52,
             NameEnd: 68,
             Name:    "retention_period",
@@ -27,8 +27,8 @@ ALTER CHANGE STREAM change_stream_name SET OPTIONS (retention_period = '1d', val
             Value:    "1d",
           },
         },
-        &ast.ChangeStreamOptionsRecord{
-          Key: &ast.Ident{
+        &ast.OptionsDef{
+          Name: &ast.Ident{
             NamePos: 77,
             NameEnd: 95,
             Name:    "value_capture_type",
@@ -45,4 +45,4 @@ ALTER CHANGE STREAM change_stream_name SET OPTIONS (retention_period = '1d', val
 }
 
 --- SQL
-ALTER CHANGE STREAM change_stream_name SET  OPTIONS (retention_period="1d", value_capture_type="OLD_AND_NEW_VALUES")
+ALTER CHANGE STREAM change_stream_name SET OPTIONS (retention_period = "1d", value_capture_type = "OLD_AND_NEW_VALUES")
diff --git a/testdata/result/statement/alter_sequence.sql.txt b/testdata/result/statement/alter_sequence.sql.txt
index c8e8305..dd88bf9 100644
--- a/testdata/result/statement/alter_sequence.sql.txt
+++ b/testdata/result/statement/alter_sequence.sql.txt
@@ -9,11 +9,11 @@ ALTER SEQUENCE my_sequence SET OPTIONS (skip_range_min=1, skip_range_max=1234567
     NameEnd: 26,
     Name:    "my_sequence",
   },
-  Options: &ast.SequenceOptions{
+  Options: &ast.Options{
     Options: 31,
     Rparen:  80,
-    Records: []*ast.SequenceOption{
-      &ast.SequenceOption{
+    Records: []*ast.OptionsDef{
+      &ast.OptionsDef{
         Name: &ast.Ident{
           NamePos: 40,
           NameEnd: 54,
@@ -26,7 +26,7 @@ ALTER SEQUENCE my_sequence SET OPTIONS (skip_range_min=1, skip_range_max=1234567
           Value:    "1",
         },
       },
-      &ast.SequenceOption{
+      &ast.OptionsDef{
         Name: &ast.Ident{
           NamePos: 58,
           NameEnd: 72,
diff --git a/testdata/result/statement/alter_table_add_column.sql.txt b/testdata/result/statement/alter_table_add_column.sql.txt
index b3003ef..d64d67b 100644
--- a/testdata/result/statement/alter_table_add_column.sql.txt
+++ b/testdata/result/statement/alter_table_add_column.sql.txt
@@ -28,7 +28,7 @@ alter table foo add column baz string(max) not null
       NotNull:       true,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
 }
diff --git a/testdata/result/statement/alter_table_add_column_if_not_exists.sql.txt b/testdata/result/statement/alter_table_add_column_if_not_exists.sql.txt
index 3d0c276..143c229 100644
--- a/testdata/result/statement/alter_table_add_column_if_not_exists.sql.txt
+++ b/testdata/result/statement/alter_table_add_column_if_not_exists.sql.txt
@@ -29,7 +29,7 @@ alter table foo add column if not exists baz string(max) not null
       NotNull:       true,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
 }
diff --git a/testdata/result/statement/alter_table_add_column_with_if_expression.sql.txt b/testdata/result/statement/alter_table_add_column_with_if_expression.sql.txt
index 296ec2a..6624b33 100644
--- a/testdata/result/statement/alter_table_add_column_with_if_expression.sql.txt
+++ b/testdata/result/statement/alter_table_add_column_with_if_expression.sql.txt
@@ -26,7 +26,7 @@ ALTER TABLE foo ADD COLUMN expired_at TIMESTAMP AS (IF (status != "OPEN" AND sta
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: &ast.GeneratedColumnExpr{
         As:     48,
-        Stored: 159,
+        Stored: 153,
         Expr:   &ast.CallExpr{
           Rparen: 150,
           Func:   &ast.Ident{
@@ -99,7 +99,9 @@ ALTER TABLE foo ADD COLUMN expired_at TIMESTAMP AS (IF (status != "OPEN" AND sta
                     },
                   },
                 },
-                NamedArgs: []*ast.NamedArg(nil),
+                NamedArgs:    []*ast.NamedArg(nil),
+                NullHandling: nil,
+                Having:       nil,
               },
             },
             &ast.ExprArg{
@@ -108,10 +110,12 @@ ALTER TABLE foo ADD COLUMN expired_at TIMESTAMP AS (IF (status != "OPEN" AND sta
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
       },
-      Options: (*ast.ColumnDefOptions)(nil),
+      Options: (*ast.Options)(nil),
     },
   },
 }
diff --git a/testdata/result/statement/alter_table_alter_column_set.sql.txt b/testdata/result/statement/alter_table_alter_column_set.sql.txt
index 9b7245d..421e02d 100644
--- a/testdata/result/statement/alter_table_alter_column_set.sql.txt
+++ b/testdata/result/statement/alter_table_alter_column_set.sql.txt
@@ -15,14 +15,26 @@ alter table foo alter column foo set options(allow_commit_timestamp = true)
       NameEnd: 32,
       Name:    "foo",
     },
-    Options: &ast.ColumnDefOptions{
-      Options:              37,
-      Rparen:               75,
-      AllowCommitTimestamp: true,
+    Options: &ast.Options{
+      Options: 37,
+      Rparen:  74,
+      Records: []*ast.OptionsDef{
+        &ast.OptionsDef{
+          Name: &ast.Ident{
+            NamePos: 45,
+            NameEnd: 67,
+            Name:    "allow_commit_timestamp",
+          },
+          Value: &ast.BoolLiteral{
+            ValuePos: 70,
+            Value:    true,
+          },
+        },
+      },
     },
     DefaultExpr: (*ast.ColumnDefaultExpr)(nil),
   },
 }
 
 --- SQL
-ALTER TABLE foo ALTER COLUMN foo SET OPTIONS(allow_commit_timestamp = true)
+ALTER TABLE foo ALTER COLUMN foo SET OPTIONS (allow_commit_timestamp = true)
diff --git a/testdata/result/statement/alter_table_alter_column_set_default.sql.txt b/testdata/result/statement/alter_table_alter_column_set_default.sql.txt
index 33148d0..2c582df 100644
--- a/testdata/result/statement/alter_table_alter_column_set_default.sql.txt
+++ b/testdata/result/statement/alter_table_alter_column_set_default.sql.txt
@@ -15,7 +15,7 @@ ALTER TABLE actions ALTER COLUMN output SET DEFAULT("")
       NameEnd: 39,
       Name:    "output",
     },
-    Options:     (*ast.ColumnDefOptions)(nil),
+    Options:     (*ast.Options)(nil),
     DefaultExpr: &ast.ColumnDefaultExpr{
       Default: 44,
       Rparen:  54,
diff --git a/testdata/result/statement/create_change_stream.sql.txt b/testdata/result/statement/create_change_stream.sql.txt
index 08efb8b..323175f 100644
--- a/testdata/result/statement/create_change_stream.sql.txt
+++ b/testdata/result/statement/create_change_stream.sql.txt
@@ -10,7 +10,7 @@ CREATE CHANGE STREAM change_stream_name
     Name:    "change_stream_name",
   },
   For:     nil,
-  Options: (*ast.ChangeStreamOptions)(nil),
+  Options: (*ast.Options)(nil),
 }
 
 --- SQL
diff --git a/testdata/result/statement/create_change_stream_for_all.sql.txt b/testdata/result/statement/create_change_stream_for_all.sql.txt
index 3bc320c..ae3ae9d 100644
--- a/testdata/result/statement/create_change_stream_for_all.sql.txt
+++ b/testdata/result/statement/create_change_stream_for_all.sql.txt
@@ -13,7 +13,7 @@ CREATE CHANGE STREAM change_stream_name FOR ALL
     For: 40,
     All: 48,
   },
-  Options: (*ast.ChangeStreamOptions)(nil),
+  Options: (*ast.Options)(nil),
 }
 
 --- SQL
diff --git a/testdata/result/statement/create_change_stream_for_multiple_table_columns.sql.txt b/testdata/result/statement/create_change_stream_for_multiple_table_columns.sql.txt
index 508c30b..7f223ba 100644
--- a/testdata/result/statement/create_change_stream_for_multiple_table_columns.sql.txt
+++ b/testdata/result/statement/create_change_stream_for_multiple_table_columns.sql.txt
@@ -54,7 +54,7 @@ CREATE CHANGE STREAM change_stream_name FOR table_name1(column1, column2), table
       },
     },
   },
-  Options: (*ast.ChangeStreamOptions)(nil),
+  Options: (*ast.Options)(nil),
 }
 
 --- SQL
diff --git a/testdata/result/statement/create_change_stream_for_table.sql.txt b/testdata/result/statement/create_change_stream_for_table.sql.txt
index 2b27ab7..e208fda 100644
--- a/testdata/result/statement/create_change_stream_for_table.sql.txt
+++ b/testdata/result/statement/create_change_stream_for_table.sql.txt
@@ -23,7 +23,7 @@ CREATE CHANGE STREAM change_stream_name FOR table_name
       },
     },
   },
-  Options: (*ast.ChangeStreamOptions)(nil),
+  Options: (*ast.Options)(nil),
 }
 
 --- SQL
diff --git a/testdata/result/statement/create_change_stream_for_table_column.sql.txt b/testdata/result/statement/create_change_stream_for_table_column.sql.txt
index 895d322..8829eac 100644
--- a/testdata/result/statement/create_change_stream_for_table_column.sql.txt
+++ b/testdata/result/statement/create_change_stream_for_table_column.sql.txt
@@ -29,7 +29,7 @@ CREATE CHANGE STREAM change_stream_name FOR table_name(column)
       },
     },
   },
-  Options: (*ast.ChangeStreamOptions)(nil),
+  Options: (*ast.Options)(nil),
 }
 
 --- SQL
diff --git a/testdata/result/statement/create_change_stream_for_table_columns.sql.txt b/testdata/result/statement/create_change_stream_for_table_columns.sql.txt
index 40f9f7f..320c4dd 100644
--- a/testdata/result/statement/create_change_stream_for_table_columns.sql.txt
+++ b/testdata/result/statement/create_change_stream_for_table_columns.sql.txt
@@ -34,7 +34,7 @@ CREATE CHANGE STREAM change_stream_name FOR table_name(column1, column2)
       },
     },
   },
-  Options: (*ast.ChangeStreamOptions)(nil),
+  Options: (*ast.Options)(nil),
 }
 
 --- SQL
diff --git a/testdata/result/statement/create_change_stream_for_tables.sql.txt b/testdata/result/statement/create_change_stream_for_tables.sql.txt
index b877bac..d152e46 100644
--- a/testdata/result/statement/create_change_stream_for_tables.sql.txt
+++ b/testdata/result/statement/create_change_stream_for_tables.sql.txt
@@ -32,7 +32,7 @@ CREATE CHANGE STREAM change_stream_name FOR table_name1, table_name2
       },
     },
   },
-  Options: (*ast.ChangeStreamOptions)(nil),
+  Options: (*ast.Options)(nil),
 }
 
 --- SQL
diff --git a/testdata/result/statement/create_change_stream_for_tables_with_option.sql.txt b/testdata/result/statement/create_change_stream_for_tables_with_option.sql.txt
index f8cb97c..7f1778c 100644
--- a/testdata/result/statement/create_change_stream_for_tables_with_option.sql.txt
+++ b/testdata/result/statement/create_change_stream_for_tables_with_option.sql.txt
@@ -55,12 +55,12 @@ OPTIONS(retention_period = '1d')
       },
     },
   },
-  Options: &ast.ChangeStreamOptions{
+  Options: &ast.Options{
     Options: 105,
     Rparen:  136,
-    Records: []*ast.ChangeStreamOptionsRecord{
-      &ast.ChangeStreamOptionsRecord{
-        Key: &ast.Ident{
+    Records: []*ast.OptionsDef{
+      &ast.OptionsDef{
+        Name: &ast.Ident{
           NamePos: 113,
           NameEnd: 129,
           Name:    "retention_period",
@@ -76,4 +76,4 @@ OPTIONS(retention_period = '1d')
 }
 
 --- SQL
-CREATE CHANGE STREAM change_stream_name FOR table_name1(column1, column2), table_name2(column1, column2) OPTIONS (retention_period="1d")
+CREATE CHANGE STREAM change_stream_name FOR table_name1(column1, column2), table_name2(column1, column2) OPTIONS (retention_period = "1d")
diff --git a/testdata/result/statement/create_change_stream_for_tables_with_option_null.sql.txt b/testdata/result/statement/create_change_stream_for_tables_with_option_null.sql.txt
index 7bb6e07..1ff7f95 100644
--- a/testdata/result/statement/create_change_stream_for_tables_with_option_null.sql.txt
+++ b/testdata/result/statement/create_change_stream_for_tables_with_option_null.sql.txt
@@ -55,12 +55,12 @@ OPTIONS(retention_period = null)
       },
     },
   },
-  Options: &ast.ChangeStreamOptions{
+  Options: &ast.Options{
     Options: 105,
     Rparen:  136,
-    Records: []*ast.ChangeStreamOptionsRecord{
-      &ast.ChangeStreamOptionsRecord{
-        Key: &ast.Ident{
+    Records: []*ast.OptionsDef{
+      &ast.OptionsDef{
+        Name: &ast.Ident{
           NamePos: 113,
           NameEnd: 129,
           Name:    "retention_period",
@@ -74,4 +74,4 @@ OPTIONS(retention_period = null)
 }
 
 --- SQL
-CREATE CHANGE STREAM change_stream_name FOR table_name1(column1, column2), table_name2(column1, column2) OPTIONS (retention_period=NULL)
+CREATE CHANGE STREAM change_stream_name FOR table_name1(column1, column2), table_name2(column1, column2) OPTIONS (retention_period = null)
diff --git a/testdata/result/statement/create_change_stream_for_tables_with_options.sql.txt b/testdata/result/statement/create_change_stream_for_tables_with_options.sql.txt
index 94e6612..ecb4350 100644
--- a/testdata/result/statement/create_change_stream_for_tables_with_options.sql.txt
+++ b/testdata/result/statement/create_change_stream_for_tables_with_options.sql.txt
@@ -55,12 +55,12 @@ OPTIONS(retention_period = '1d', value_capture_type = 'NEW_ROW')
       },
     },
   },
-  Options: &ast.ChangeStreamOptions{
+  Options: &ast.Options{
     Options: 105,
     Rparen:  168,
-    Records: []*ast.ChangeStreamOptionsRecord{
-      &ast.ChangeStreamOptionsRecord{
-        Key: &ast.Ident{
+    Records: []*ast.OptionsDef{
+      &ast.OptionsDef{
+        Name: &ast.Ident{
           NamePos: 113,
           NameEnd: 129,
           Name:    "retention_period",
@@ -71,8 +71,8 @@ OPTIONS(retention_period = '1d', value_capture_type = 'NEW_ROW')
           Value:    "1d",
         },
       },
-      &ast.ChangeStreamOptionsRecord{
-        Key: &ast.Ident{
+      &ast.OptionsDef{
+        Name: &ast.Ident{
           NamePos: 138,
           NameEnd: 156,
           Name:    "value_capture_type",
@@ -88,4 +88,4 @@ OPTIONS(retention_period = '1d', value_capture_type = 'NEW_ROW')
 }
 
 --- SQL
-CREATE CHANGE STREAM change_stream_name FOR table_name1(column1, column2), table_name2(column1, column2) OPTIONS (retention_period="1d", value_capture_type="NEW_ROW")
+CREATE CHANGE STREAM change_stream_name FOR table_name1(column1, column2), table_name2(column1, column2) OPTIONS (retention_period = "1d", value_capture_type = "NEW_ROW")
diff --git a/testdata/result/statement/create_sequance.sql.txt b/testdata/result/statement/create_sequance.sql.txt
index 9d6a557..e0bf76d 100644
--- a/testdata/result/statement/create_sequance.sql.txt
+++ b/testdata/result/statement/create_sequance.sql.txt
@@ -14,11 +14,11 @@ CREATE SEQUENCE IF NOT EXISTS MySequence OPTIONS (
     Name:    "MySequence",
   },
   IfNotExists: true,
-  Options:     &ast.SequenceOptions{
+  Options:     &ast.Options{
     Options: 41,
     Rparen:  172,
-    Records: []*ast.SequenceOption{
-      &ast.SequenceOption{
+    Records: []*ast.OptionsDef{
+      &ast.OptionsDef{
         Name: &ast.Ident{
           NamePos: 55,
           NameEnd: 68,
@@ -30,7 +30,7 @@ CREATE SEQUENCE IF NOT EXISTS MySequence OPTIONS (
           Value:    "bit_reversed_positive",
         },
       },
-      &ast.SequenceOption{
+      &ast.OptionsDef{
         Name: &ast.Ident{
           NamePos: 98,
           NameEnd: 112,
@@ -43,7 +43,7 @@ CREATE SEQUENCE IF NOT EXISTS MySequence OPTIONS (
           Value:    "1",
         },
       },
-      &ast.SequenceOption{
+      &ast.OptionsDef{
         Name: &ast.Ident{
           NamePos: 122,
           NameEnd: 136,
@@ -56,7 +56,7 @@ CREATE SEQUENCE IF NOT EXISTS MySequence OPTIONS (
           Value:    "1000",
         },
       },
-      &ast.SequenceOption{
+      &ast.OptionsDef{
         Name: &ast.Ident{
           NamePos: 149,
           NameEnd: 167,
diff --git a/testdata/result/statement/create_table.sql.txt b/testdata/result/statement/create_table.sql.txt
index e263d4b..03c0ed7 100644
--- a/testdata/result/statement/create_table.sql.txt
+++ b/testdata/result/statement/create_table.sql.txt
@@ -39,7 +39,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: 50,
@@ -55,7 +55,7 @@ create table foo (
       NotNull:       true,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: 78,
@@ -79,10 +79,21 @@ create table foo (
       NotNull:       true,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       &ast.ColumnDefOptions{
-        Options:              83,
-        Rparen:               121,
-        AllowCommitTimestamp: false,
+      Options:       &ast.Options{
+        Options: 83,
+        Rparen:  120,
+        Records: []*ast.OptionsDef{
+          &ast.OptionsDef{
+            Name: &ast.Ident{
+              NamePos: 91,
+              NameEnd: 113,
+              Name:    "allow_commit_timestamp",
+            },
+            Value: &ast.NullLiteral{
+              Null: 116,
+            },
+          },
+        },
       },
     },
     &ast.ColumnDef{
@@ -108,7 +119,7 @@ create table foo (
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: &ast.GeneratedColumnExpr{
         As:     150,
-        Stored: 178,
+        Stored: 172,
         Expr:   &ast.CallExpr{
           Rparen: 169,
           Func:   &ast.Ident{
@@ -133,10 +144,12 @@ create table foo (
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
       },
-      Options: (*ast.ColumnDefOptions)(nil),
+      Options: (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -152,7 +165,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: 515,
@@ -176,13 +189,15 @@ create table foo (
             NameEnd: 546,
             Name:    "current_timestamp",
           },
-          Distinct:  false,
-          Args:      []ast.Arg(nil),
-          NamedArgs: []*ast.NamedArg(nil),
+          Distinct:     false,
+          Args:         []ast.Arg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
       },
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
   TableConstraints: []*ast.TableConstraint{
@@ -390,4 +405,4 @@ create table foo (
 }
 
 --- SQL
-CREATE TABLE foo (foo INT64, bar FLOAT64 NOT NULL, baz STRING(255) NOT NULL OPTIONS(allow_commit_timestamp = null), qux STRING(255) NOT NULL AS (concat(baz, "a")) STORED, quux JSON, corge TIMESTAMP NOT NULL DEFAULT (current_timestamp()), FOREIGN KEY (foo) REFERENCES t2 (t2key1), FOREIGN KEY (bar) REFERENCES t2 (t2key2) ON DELETE CASCADE, FOREIGN KEY (baz) REFERENCES t2 (t2key3) ON DELETE NO ACTION, CONSTRAINT fkname FOREIGN KEY (foo, bar) REFERENCES t2 (t2key1, t2key2), CHECK (foo > 0), CONSTRAINT cname CHECK (bar > 0)) PRIMARY KEY (foo, bar)
+CREATE TABLE foo (foo INT64, bar FLOAT64 NOT NULL, baz STRING(255) NOT NULL OPTIONS (allow_commit_timestamp = null), qux STRING(255) NOT NULL AS (concat(baz, "a")) STORED, quux JSON, corge TIMESTAMP NOT NULL DEFAULT (current_timestamp()), FOREIGN KEY (foo) REFERENCES t2 (t2key1), FOREIGN KEY (bar) REFERENCES t2 (t2key2) ON DELETE CASCADE, FOREIGN KEY (baz) REFERENCES t2 (t2key3) ON DELETE NO ACTION, CONSTRAINT fkname FOREIGN KEY (foo, bar) REFERENCES t2 (t2key1, t2key2), CHECK (foo > 0), CONSTRAINT cname CHECK (bar > 0)) PRIMARY KEY (foo, bar)
diff --git a/testdata/result/statement/create_table_cluster.sql.txt b/testdata/result/statement/create_table_cluster.sql.txt
index 8eb83c3..1cf81fb 100644
--- a/testdata/result/statement/create_table_cluster.sql.txt
+++ b/testdata/result/statement/create_table_cluster.sql.txt
@@ -30,7 +30,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -46,7 +46,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
   TableConstraints: []*ast.TableConstraint(nil),
diff --git a/testdata/result/statement/create_table_cluster_and_row_deletion_policy.sql.txt b/testdata/result/statement/create_table_cluster_and_row_deletion_policy.sql.txt
index 26aa3fb..53a04b5 100644
--- a/testdata/result/statement/create_table_cluster_and_row_deletion_policy.sql.txt
+++ b/testdata/result/statement/create_table_cluster_and_row_deletion_policy.sql.txt
@@ -32,7 +32,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -48,7 +48,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -64,7 +64,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
   TableConstraints: []*ast.TableConstraint(nil),
diff --git a/testdata/result/statement/create_table_cluster_on_delete_no_action.sql.txt b/testdata/result/statement/create_table_cluster_on_delete_no_action.sql.txt
index c2caa5c..fda3185 100644
--- a/testdata/result/statement/create_table_cluster_on_delete_no_action.sql.txt
+++ b/testdata/result/statement/create_table_cluster_on_delete_no_action.sql.txt
@@ -29,7 +29,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
   TableConstraints: []*ast.TableConstraint(nil),
diff --git a/testdata/result/statement/create_table_cluster_set_on_delete.sql.txt b/testdata/result/statement/create_table_cluster_set_on_delete.sql.txt
index 4de2bcc..130bd04 100644
--- a/testdata/result/statement/create_table_cluster_set_on_delete.sql.txt
+++ b/testdata/result/statement/create_table_cluster_set_on_delete.sql.txt
@@ -29,7 +29,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
   TableConstraints: []*ast.TableConstraint(nil),
diff --git a/testdata/result/statement/create_table_if_not_exists.sql.txt b/testdata/result/statement/create_table_if_not_exists.sql.txt
index 1677b5c..18ae6e0 100644
--- a/testdata/result/statement/create_table_if_not_exists.sql.txt
+++ b/testdata/result/statement/create_table_if_not_exists.sql.txt
@@ -29,7 +29,7 @@ create table if not exists foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: 64,
@@ -45,7 +45,7 @@ create table if not exists foo (
       NotNull:       true,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
   TableConstraints: []*ast.TableConstraint(nil),
diff --git a/testdata/result/statement/create_table_row_deletion_policy.sql.txt b/testdata/result/statement/create_table_row_deletion_policy.sql.txt
index 4a49748..a82a2a4 100644
--- a/testdata/result/statement/create_table_row_deletion_policy.sql.txt
+++ b/testdata/result/statement/create_table_row_deletion_policy.sql.txt
@@ -31,7 +31,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -47,7 +47,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -63,7 +63,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
   TableConstraints:  []*ast.TableConstraint(nil),
diff --git a/testdata/result/statement/create_table_trailing_comma.sql.txt b/testdata/result/statement/create_table_trailing_comma.sql.txt
index 0ff16b9..9440fd6 100644
--- a/testdata/result/statement/create_table_trailing_comma.sql.txt
+++ b/testdata/result/statement/create_table_trailing_comma.sql.txt
@@ -32,7 +32,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -48,7 +48,7 @@ create table foo (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
   TableConstraints: []*ast.TableConstraint(nil),
diff --git a/testdata/result/statement/create_table_types.sql.txt b/testdata/result/statement/create_table_types.sql.txt
index f7f536d..3afd6e7 100644
--- a/testdata/result/statement/create_table_types.sql.txt
+++ b/testdata/result/statement/create_table_types.sql.txt
@@ -40,7 +40,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -56,7 +56,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -72,7 +72,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -88,7 +88,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -104,7 +104,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -120,7 +120,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -144,7 +144,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -163,7 +163,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -187,7 +187,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -206,7 +206,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -217,7 +217,7 @@ create table types (
       },
       Type: &ast.ArraySchemaType{
         Array: 174,
-        Gt:    185,
+        Gt:    184,
         Item:  &ast.ScalarSchemaType{
           NamePos: 180,
           Name:    "BOOL",
@@ -226,7 +226,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -237,7 +237,7 @@ create table types (
       },
       Type: &ast.ArraySchemaType{
         Array: 193,
-        Gt:    210,
+        Gt:    209,
         Item:  &ast.SizedSchemaType{
           NamePos: 199,
           Rparen:  208,
@@ -249,7 +249,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -275,7 +275,7 @@ create table types (
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
   TableConstraints: []*ast.TableConstraint(nil),
diff --git a/testdata/result/statement/create_table_with_sequence_function.sql.txt b/testdata/result/statement/create_table_with_sequence_function.sql.txt
index d3fac4a..f2b827c 100644
--- a/testdata/result/statement/create_table_with_sequence_function.sql.txt
+++ b/testdata/result/statement/create_table_with_sequence_function.sql.txt
@@ -50,11 +50,13 @@ CREATE TABLE foo
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
       },
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -73,7 +75,7 @@ CREATE TABLE foo
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
     &ast.ColumnDef{
       Null: -1,
@@ -89,7 +91,7 @@ CREATE TABLE foo
       NotNull:       false,
       DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
       GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
-      Options:       (*ast.ColumnDefOptions)(nil),
+      Options:       (*ast.Options)(nil),
     },
   },
   TableConstraints: []*ast.TableConstraint(nil),
diff --git a/testdata/result/statement/create_vector_index.sql.txt b/testdata/result/statement/create_vector_index.sql.txt
index 7d5cf1e..ed7d5a6 100644
--- a/testdata/result/statement/create_vector_index.sql.txt
+++ b/testdata/result/statement/create_vector_index.sql.txt
@@ -21,12 +21,12 @@ OPTIONS(distance_type = 'COSINE')
     Name:    "embedding",
   },
   Where:   (*ast.Where)(nil),
-  Options: &ast.VectorIndexOptions{
+  Options: &ast.Options{
     Options: 73,
     Rparen:  105,
-    Records: []*ast.VectorIndexOption{
-      &ast.VectorIndexOption{
-        Key: &ast.Ident{
+    Records: []*ast.OptionsDef{
+      &ast.OptionsDef{
+        Name: &ast.Ident{
           NamePos: 81,
           NameEnd: 94,
           Name:    "distance_type",
@@ -42,4 +42,4 @@ OPTIONS(distance_type = 'COSINE')
 }
 
 --- SQL
-CREATE VECTOR INDEX IF NOT EXISTS hello_vector_index ON hello (embedding) OPTIONS (distance_type="COSINE")
+CREATE VECTOR INDEX IF NOT EXISTS hello_vector_index ON hello (embedding) OPTIONS (distance_type = "COSINE")
diff --git a/testdata/result/statement/create_vector_index_where_column_not_null.sql.txt b/testdata/result/statement/create_vector_index_where_column_not_null.sql.txt
index da02696..965499a 100644
--- a/testdata/result/statement/create_vector_index_where_column_not_null.sql.txt
+++ b/testdata/result/statement/create_vector_index_where_column_not_null.sql.txt
@@ -33,12 +33,12 @@ OPTIONS(distance_type = 'COSINE')
       },
     },
   },
-  Options: &ast.VectorIndexOptions{
+  Options: &ast.Options{
     Options: 87,
     Rparen:  119,
-    Records: []*ast.VectorIndexOption{
-      &ast.VectorIndexOption{
-        Key: &ast.Ident{
+    Records: []*ast.OptionsDef{
+      &ast.OptionsDef{
+        Name: &ast.Ident{
           NamePos: 95,
           NameEnd: 108,
           Name:    "distance_type",
@@ -54,4 +54,4 @@ OPTIONS(distance_type = 'COSINE')
 }
 
 --- SQL
-CREATE VECTOR INDEX hello_vector_index ON hello (embedding) WHERE embedding IS NOT NULL OPTIONS (distance_type="COSINE")
+CREATE VECTOR INDEX hello_vector_index ON hello (embedding) WHERE embedding IS NOT NULL OPTIONS (distance_type = "COSINE")
diff --git a/testdata/result/statement/insert_with_sequence_function.sql.txt b/testdata/result/statement/insert_with_sequence_function.sql.txt
index 2424b7a..7f5a4b8 100644
--- a/testdata/result/statement/insert_with_sequence_function.sql.txt
+++ b/testdata/result/statement/insert_with_sequence_function.sql.txt
@@ -45,7 +45,9 @@ INSERT INTO foo(bar) VALUES (GET_NEXT_SEQUENCE_VALUE(SEQUENCE my_sequence))
                   },
                 },
               },
-              NamedArgs: []*ast.NamedArg(nil),
+              NamedArgs:    []*ast.NamedArg(nil),
+              NullHandling: nil,
+              Having:       nil,
             },
           },
         },
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 347e513..78d1d15 100644
--- a/testdata/result/statement/select_call_with_named_expr.sql.txt
+++ b/testdata/result/statement/select_call_with_named_expr.sql.txt
@@ -137,6 +137,8 @@ WHERE a.SingerId = 1 AND SEARCH(a.DescriptionTokens, 'classic albums', enhance_q
               },
             },
           },
+          NullHandling: nil,
+          Having:       nil,
         },
       },
     },
diff --git a/testdata/result/statement/select_count_distinct.sql.txt b/testdata/result/statement/select_count_distinct.sql.txt
index 4cb7d98..76755e4 100644
--- a/testdata/result/statement/select_count_distinct.sql.txt
+++ b/testdata/result/statement/select_count_distinct.sql.txt
@@ -27,7 +27,9 @@ select count(distinct x) from foo
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
       },
     },
diff --git a/testdata/result/statement/select_expr.sql.txt b/testdata/result/statement/select_expr.sql.txt
index eeb46ce..e6c89c3 100644
--- a/testdata/result/statement/select_expr.sql.txt
+++ b/testdata/result/statement/select_expr.sql.txt
@@ -617,7 +617,9 @@ select 1 + 2, 1 - 2,
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
       },
       &ast.ExprSelectItem{
@@ -655,7 +657,9 @@ select 1 + 2, 1 - 2,
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
       },
       &ast.ExprSelectItem{
@@ -899,7 +903,9 @@ select 1 + 2, 1 - 2,
                 },
               },
             },
-            NamedArgs: []*ast.NamedArg(nil),
+            NamedArgs:    []*ast.NamedArg(nil),
+            NullHandling: nil,
+            Having:       nil,
           },
           Right: &ast.IntLiteral{
             ValuePos: 790,
diff --git a/testdata/result/statement/select_literals_all.sql.txt b/testdata/result/statement/select_literals_all.sql.txt
index b8556bb..3e35cc3 100644
--- a/testdata/result/statement/select_literals_all.sql.txt
+++ b/testdata/result/statement/select_literals_all.sql.txt
@@ -504,7 +504,9 @@ lines''',
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
       },
       &ast.ExprSelectItem{
@@ -555,7 +557,9 @@ lines''',
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
       },
       &ast.ExprSelectItem{
diff --git a/testdata/result/statement/select_literals_date.sql.txt b/testdata/result/statement/select_literals_date.sql.txt
index 1ccd8fb..fd6b359 100644
--- a/testdata/result/statement/select_literals_date.sql.txt
+++ b/testdata/result/statement/select_literals_date.sql.txt
@@ -48,7 +48,9 @@ SELECT
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
       },
       &ast.ExprSelectItem{
@@ -109,7 +111,9 @@ SELECT
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
       },
       &ast.ExprSelectItem{
@@ -140,7 +144,9 @@ SELECT
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
         As: &ast.AsAlias{
           As:    -1,
@@ -169,7 +175,9 @@ SELECT
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
         As: &ast.AsAlias{
           As:    -1,
diff --git a/testdata/result/statement/select_with_sequence_function.sql.txt b/testdata/result/statement/select_with_sequence_function.sql.txt
index 1abdadb..491d47f 100644
--- a/testdata/result/statement/select_with_sequence_function.sql.txt
+++ b/testdata/result/statement/select_with_sequence_function.sql.txt
@@ -29,7 +29,9 @@ SELECT GET_NEXT_SEQUENCE_VALUE(SEQUENCE my_sequence) as next_id
               },
             },
           },
-          NamedArgs: []*ast.NamedArg(nil),
+          NamedArgs:    []*ast.NamedArg(nil),
+          NullHandling: nil,
+          Having:       nil,
         },
         As: &ast.AsAlias{
           As:    -1,