Repository: cloudspannerecosystem/memefish
Pull request #101 (2024-09-19 03:11:28)

Support Spanner Graph(SQL/PGQ) schema statements
This PR implements Spanner Graph(SQL/PGQ) schema statements:

* `CREATE [OR REPLACE] PROPERTY GRAPH [IF NOT EXISTS]`
* `DROP PROPERTY GRAPH [IF EXISTS]`

~It conflicts with #99.~

#98


Patch:
diff --git a/ast/ast.go b/ast/ast.go
index 5708a22..c425d44 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -71,51 +71,53 @@ type Statement interface {
 // - https://cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language
 // - https://cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax
 
-func (BadStatement) isStatement()       {}
-func (BadDDL) isStatement()             {}
-func (BadDML) isStatement()             {}
-func (QueryStatement) isStatement()     {}
-func (CreateSchema) isStatement()       {}
-func (DropSchema) isStatement()         {}
-func (CreateDatabase) isStatement()     {}
-func (AlterDatabase) isStatement()      {}
-func (CreatePlacement) isStatement()    {}
-func (CreateProtoBundle) isStatement()  {}
-func (AlterProtoBundle) isStatement()   {}
-func (DropProtoBundle) isStatement()    {}
-func (CreateTable) isStatement()        {}
-func (AlterTable) isStatement()         {}
-func (DropTable) isStatement()          {}
-func (RenameTable) isStatement()        {}
-func (CreateIndex) isStatement()        {}
-func (AlterIndex) isStatement()         {}
-func (DropIndex) isStatement()          {}
-func (CreateSearchIndex) isStatement()  {}
-func (DropSearchIndex) isStatement()    {}
-func (AlterSearchIndex) isStatement()   {}
-func (CreateView) isStatement()         {}
-func (DropView) isStatement()           {}
-func (CreateChangeStream) isStatement() {}
-func (AlterChangeStream) isStatement()  {}
-func (DropChangeStream) isStatement()   {}
-func (CreateRole) isStatement()         {}
-func (DropRole) isStatement()           {}
-func (Grant) isStatement()              {}
-func (Revoke) isStatement()             {}
-func (CreateSequence) isStatement()     {}
-func (AlterSequence) isStatement()      {}
-func (DropSequence) isStatement()       {}
-func (AlterStatistics) isStatement()    {}
-func (CreateModel) isStatement()        {}
-func (AlterModel) isStatement()         {}
-func (DropModel) isStatement()          {}
-func (Analyze) isStatement()            {}
-func (CreateVectorIndex) isStatement()  {}
-func (DropVectorIndex) isStatement()    {}
-func (Insert) isStatement()             {}
-func (Delete) isStatement()             {}
-func (Update) isStatement()             {}
-func (Call) isStatement()               {}
+func (BadStatement) isStatement()        {}
+func (BadDDL) isStatement()              {}
+func (BadDML) isStatement()              {}
+func (QueryStatement) isStatement()      {}
+func (CreateSchema) isStatement()        {}
+func (DropSchema) isStatement()          {}
+func (CreateDatabase) isStatement()      {}
+func (AlterDatabase) isStatement()       {}
+func (CreatePlacement) isStatement()     {}
+func (CreateProtoBundle) isStatement()   {}
+func (AlterProtoBundle) isStatement()    {}
+func (DropProtoBundle) isStatement()     {}
+func (CreateTable) isStatement()         {}
+func (AlterTable) isStatement()          {}
+func (DropTable) isStatement()           {}
+func (RenameTable) isStatement()         {}
+func (CreateIndex) isStatement()         {}
+func (AlterIndex) isStatement()          {}
+func (DropIndex) isStatement()           {}
+func (CreateSearchIndex) isStatement()   {}
+func (DropSearchIndex) isStatement()     {}
+func (AlterSearchIndex) isStatement()    {}
+func (CreateView) isStatement()          {}
+func (DropView) isStatement()            {}
+func (CreateChangeStream) isStatement()  {}
+func (AlterChangeStream) isStatement()   {}
+func (DropChangeStream) isStatement()    {}
+func (CreateRole) isStatement()          {}
+func (DropRole) isStatement()            {}
+func (Grant) isStatement()               {}
+func (Revoke) isStatement()              {}
+func (CreateSequence) isStatement()      {}
+func (AlterSequence) isStatement()       {}
+func (DropSequence) isStatement()        {}
+func (AlterStatistics) isStatement()     {}
+func (CreateModel) isStatement()         {}
+func (AlterModel) isStatement()          {}
+func (DropModel) isStatement()           {}
+func (Analyze) isStatement()             {}
+func (CreateVectorIndex) isStatement()   {}
+func (DropVectorIndex) isStatement()     {}
+func (CreatePropertyGraph) isStatement() {}
+func (DropPropertyGraph) isStatement()   {}
+func (Insert) isStatement()              {}
+func (Delete) isStatement()              {}
+func (Update) isStatement()              {}
+func (Call) isStatement()                {}
 
 // QueryExpr represents query expression, which can be body of QueryStatement or subqueries.
 // Select and FromQuery are leaf QueryExpr and others wrap other QueryExpr.
@@ -361,44 +363,46 @@ type DDL interface {
 //
 // - https://cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language
 
-func (BadDDL) isDDL()             {}
-func (CreateSchema) isDDL()       {}
-func (DropSchema) isDDL()         {}
-func (CreateDatabase) isDDL()     {}
-func (AlterDatabase) isDDL()      {}
-func (CreatePlacement) isDDL()    {}
-func (CreateProtoBundle) isDDL()  {}
-func (AlterProtoBundle) isDDL()   {}
-func (DropProtoBundle) isDDL()    {}
-func (CreateTable) isDDL()        {}
-func (AlterTable) isDDL()         {}
-func (DropTable) isDDL()          {}
-func (RenameTable) isDDL()        {}
-func (CreateIndex) isDDL()        {}
-func (AlterIndex) isDDL()         {}
-func (DropIndex) isDDL()          {}
-func (CreateView) isDDL()         {}
-func (CreateSearchIndex) isDDL()  {}
-func (DropSearchIndex) isDDL()    {}
-func (AlterSearchIndex) isDDL()   {}
-func (DropView) isDDL()           {}
-func (CreateChangeStream) isDDL() {}
-func (AlterChangeStream) isDDL()  {}
-func (DropChangeStream) isDDL()   {}
-func (CreateRole) isDDL()         {}
-func (DropRole) isDDL()           {}
-func (Grant) isDDL()              {}
-func (Revoke) isDDL()             {}
-func (CreateSequence) isDDL()     {}
-func (AlterSequence) isDDL()      {}
-func (DropSequence) isDDL()       {}
-func (AlterStatistics) isDDL()    {}
-func (CreateModel) isDDL()        {}
-func (AlterModel) isDDL()         {}
-func (DropModel) isDDL()          {}
-func (Analyze) isDDL()            {}
-func (CreateVectorIndex) isDDL()  {}
-func (DropVectorIndex) isDDL()    {}
+func (BadDDL) isDDL()              {}
+func (CreateSchema) isDDL()        {}
+func (DropSchema) isDDL()          {}
+func (CreateDatabase) isDDL()      {}
+func (AlterDatabase) isDDL()       {}
+func (CreatePlacement) isDDL()     {}
+func (CreateProtoBundle) isDDL()   {}
+func (AlterProtoBundle) isDDL()    {}
+func (DropProtoBundle) isDDL()     {}
+func (CreateTable) isDDL()         {}
+func (AlterTable) isDDL()          {}
+func (DropTable) isDDL()           {}
+func (RenameTable) isDDL()         {}
+func (CreateIndex) isDDL()         {}
+func (AlterIndex) isDDL()          {}
+func (DropIndex) isDDL()           {}
+func (CreateView) isDDL()          {}
+func (CreateSearchIndex) isDDL()   {}
+func (DropSearchIndex) isDDL()     {}
+func (AlterSearchIndex) isDDL()    {}
+func (DropView) isDDL()            {}
+func (CreateChangeStream) isDDL()  {}
+func (AlterChangeStream) isDDL()   {}
+func (DropChangeStream) isDDL()    {}
+func (CreateRole) isDDL()          {}
+func (DropRole) isDDL()            {}
+func (Grant) isDDL()               {}
+func (Revoke) isDDL()              {}
+func (CreateSequence) isDDL()      {}
+func (AlterSequence) isDDL()       {}
+func (DropSequence) isDDL()        {}
+func (AlterStatistics) isDDL()     {}
+func (CreateModel) isDDL()         {}
+func (AlterModel) isDDL()          {}
+func (DropModel) isDDL()           {}
+func (Analyze) isDDL()             {}
+func (CreateVectorIndex) isDDL()   {}
+func (DropVectorIndex) isDDL()     {}
+func (CreatePropertyGraph) isDDL() {}
+func (DropPropertyGraph) isDDL()   {}
 
 // Constraint represents table constraint of CONSTARINT clause.
 type Constraint interface {
@@ -3612,6 +3616,313 @@ type AlterSearchIndex struct {
 	IndexAlteration IndexAlteration
 }
 
+// ================================================================================
+//
+// GQL schema statements
+//
+// ================================================================================
+
+// PropertyGraphLabelsOrProperties represents labels with properties or a single properties of node or edge.
+type PropertyGraphLabelsOrProperties interface {
+	Node
+	isPropertyGraphLabelsOrProperties()
+}
+
+func (PropertyGraphSingleProperties) isPropertyGraphLabelsOrProperties()       {}
+func (PropertyGraphLabelAndPropertiesList) isPropertyGraphLabelsOrProperties() {}
+
+// PropertyGraphElementLabel represents a element label definition.
+type PropertyGraphElementLabel interface {
+	Node
+	isPropertyGraphElementLabel()
+}
+
+func (PropertyGraphElementLabelLabelName) isPropertyGraphElementLabel()    {}
+func (PropertyGraphElementLabelDefaultLabel) isPropertyGraphElementLabel() {}
+
+// PropertyGraphElementKeys represents PropertyGraphNodeElementKey or PropertyGraphEdgeElementKeys.
+type PropertyGraphElementKeys interface {
+	Node
+	isPropertyGraphElementKeys()
+}
+
+func (PropertyGraphNodeElementKey) isPropertyGraphElementKeys()  {}
+func (PropertyGraphEdgeElementKeys) isPropertyGraphElementKeys() {}
+
+// PropertyGraphElementProperties represents a definition of properties.
+// See https://cloud.google.com/spanner/docs/reference/standard-sql/graph-schema-statements#element_table_property_definition.
+type PropertyGraphElementProperties interface {
+	Node
+	isPropertyGraphElementProperties()
+}
+
+func (PropertyGraphNoProperties) isPropertyGraphElementProperties()        {}
+func (PropertyGraphPropertiesAre) isPropertyGraphElementProperties()       {}
+func (PropertyGraphDerivedPropertyList) isPropertyGraphElementProperties() {}
+
+// CreatePropertyGraph is CREATE PROPERTY GRAPH statement node.
+//
+//	CREATE {{if .OrReplace}}OR REPLACE{{end}} PROPERTY GRAPH
+//	{{if .IfNotExists}}IF NOT EXISTS{{end}}
+//	{{.Name | sql}}
+//	{{.Content | sql}}
+type CreatePropertyGraph struct {
+	// pos = Create
+	// end = Content.end
+
+	Create      token.Pos // position of "CREATE" keyword
+	OrReplace   bool
+	IfNotExists bool
+	Name        *Ident
+	Content     *PropertyGraphContent
+}
+
+// PropertyGraphContent represents body of CREATE PROPERTY GRAPH statement.
+//
+//	NODE TABLES {{.NodeTables | sql}} {{.EdgeTables | sqlOpt}}
+type PropertyGraphContent struct {
+	// pos = NodeTables.pos
+	// end = (EdgeTables ?? NodeTables).end
+
+	NodeTables *PropertyGraphNodeTables
+	EdgeTables *PropertyGraphEdgeTables //optional
+}
+
+// PropertyGraphNodeTables is NODE TABLES node in CREATE PROPERTY GRAPH statement.
+//
+//	NODE TABLES {{.Tables | sql}}
+type PropertyGraphNodeTables struct {
+	// pos = Node
+	// end = Tables.end
+
+	Node   token.Pos
+	Tables *PropertyGraphElementList
+}
+
+// PropertyGraphEdgeTables is EDGE TABLES node in CREATE PROPERTY GRAPH statement.
+//
+//	EDGE TABLES {{.Tables | sql}}
+type PropertyGraphEdgeTables struct {
+	// pos = Edge
+	// end = Tables.end
+
+	Edge   token.Pos
+	Tables *PropertyGraphElementList
+}
+
+// PropertyGraphElementList represents element list in NODE TABLES or EDGE TABLES.
+//
+//	({{.Elements | sqlJoin ", "}})
+type PropertyGraphElementList struct {
+	// pos = Lparen
+	// end = Rparen + 1
+
+	Lparen, Rparen token.Pos
+	Elements       []*PropertyGraphElement
+}
+
+// PropertyGraphElement represents a single element in NODE TABLES or EDGE TABLES.
+//
+//	{{.Name | sql}} {{if .Alias | isnil | not)}}AS {{.Alias | sql}}{{end}}
+//	{{.Keys | sqlOpt}} {{.Properties | sqlOpt}}
+type PropertyGraphElement struct {
+	// pos = Name.pos
+	// end = (Properties ?? Keys ?? Alias ?? Name).end
+
+	Name       *Ident
+	Alias      *Ident                          // optional
+	Keys       PropertyGraphElementKeys        // optional
+	Properties PropertyGraphLabelsOrProperties // optional
+}
+
+// PropertyGraphSingleProperties is wrapper node for PropertyGraphElementProperties in PropertyGraphElement.
+// It implements PropertyGraphLabelsOrProperties.
+//
+//	{{.Properties | sql}}
+type PropertyGraphSingleProperties struct {
+	// pos = Properties.pos
+	// end = Properties.end
+
+	Properties PropertyGraphElementProperties
+}
+
+// PropertyGraphLabelAndPropertiesList represents whitespace-separated list of PropertyGraphLabelAndProperties.
+// It implements PropertyGraphLabelsOrProperties.
+//
+//	{{.LabelAndProperties | sqlJoin " "}}
+type PropertyGraphLabelAndPropertiesList struct {
+	// pos = LabelAndProperties[0].pos
+	// end = LabelAndProperties[$].end
+
+	LabelAndProperties []*PropertyGraphLabelAndProperties // len(LabelAndProperties) > 0
+}
+
+// PropertyGraphLabelAndProperties represents label and properties definition for a single label.
+//
+//	{{.Label | sql}} {{.Properties | sqlOpt}}
+type PropertyGraphLabelAndProperties struct {
+	// pos = Label.pos
+	// end = (Properties ?? Label).end
+
+	Label      PropertyGraphElementLabel
+	Properties PropertyGraphElementProperties // optional
+}
+
+// PropertyGraphElementLabelLabelName represents LABEL label_name node.
+//
+//	LABEL {{.Name | sql}}
+type PropertyGraphElementLabelLabelName struct {
+	// pos = Label
+	// end = Name.end
+
+	Label token.Pos
+	Name  *Ident
+}
+
+// PropertyGraphElementLabelDefaultLabel represents DEFAULT LABEL node.
+//
+//	DEFAULT LABEL
+type PropertyGraphElementLabelDefaultLabel struct {
+	// pos = Default
+	// end = Label + 5
+
+	Default token.Pos
+	Label   token.Pos
+}
+
+// PropertyGraphNodeElementKey is a wrapper of PropertyGraphElementKey to implement PropertyGraphElementKeys
+// without deeper AST hierarchy.
+//
+//	{{.Key | sql}}
+type PropertyGraphNodeElementKey struct {
+	// pos = Key.pos
+	// end = Key.end
+
+	Key *PropertyGraphElementKey
+}
+
+// PropertyGraphEdgeElementKeys represents PropertyGraphSourceKey and PropertyGraphDestinationKey with optional PropertyGraphElementKey.
+//
+//	{{.Element | sqlOpt}} {{.Source | sql}} {{.Destination | sql}}
+type PropertyGraphEdgeElementKeys struct {
+	// pos = Element.pos
+	// end = Destination.end
+	Element     *PropertyGraphElementKey // optional
+	Source      *PropertyGraphSourceKey
+	Destination *PropertyGraphDestinationKey
+}
+
+// PropertyGraphElementKey represents the key that identifies the node or edge element.
+//
+//	KEY {{.Keys | sql}}
+type PropertyGraphElementKey struct {
+	// pos = Key
+	// end = Keys.end
+
+	Key  token.Pos
+	Keys *PropertyGraphColumnNameList
+}
+
+// PropertyGraphSourceKey represents the key for the source node of the edge.
+//
+//	SOURCE KEY {{.Keys | sql}}
+//	REFERENCES {{.ElementReference | sql}} {{.ReferenceColumns | sqlOpt}}
+type PropertyGraphSourceKey struct {
+	// pos = Source
+	// end = (ReferenceColumns ?? ElementReference).end
+
+	Source           token.Pos
+	Keys             *PropertyGraphColumnNameList
+	ElementReference *Ident
+	ReferenceColumns *PropertyGraphColumnNameList // optional
+}
+
+// PropertyGraphDestinationKey represents the key for the destination node of the edge.
+//
+//	DESTINATION KEY {{.Keys | sql}}
+//	REFERENCES {{.ElementReference | sql}} {{.ReferenceColumns | sqlOpt}}
+type PropertyGraphDestinationKey struct {
+	// pos = Destination
+	// end = (ReferenceColumns ?? ElementReference).end
+
+	Destination      token.Pos
+	Keys             *PropertyGraphColumnNameList
+	ElementReference *Ident
+	ReferenceColumns *PropertyGraphColumnNameList // optional
+}
+
+// PropertyGraphColumnNameList represents one or more columns to assign to a key.
+//
+//	({{.ColumnNameList | sqlJoin ", "}})
+type PropertyGraphColumnNameList struct {
+	// pos = Lparen
+	// end = Rparen + 1
+
+	Lparen, Rparen token.Pos
+
+	ColumnNameList []*Ident
+}
+
+// PropertyGraphNoProperties represents the element doesn't have properties.
+//
+//	NO PROPERTIES
+type PropertyGraphNoProperties struct {
+	// pos = No
+	// end = Properties + 10
+
+	No, Properties token.Pos // position of "NO" and "PROPERTIES"
+}
+
+// PropertyGraphPropertiesAre defines which columns to include as element properties.
+//
+//	PROPERTIES ARE ALL COLUMNS{{if .ExceptColumns | isnil | not}} EXCEPT {{.ExceptColumns | sql}}{{end}}
+type PropertyGraphPropertiesAre struct {
+	// pos = Properties
+	// end = ExceptColumns.end || Columns + 7
+
+	Properties token.Pos // position of "PROPERTIES"
+	Columns    token.Pos // position of "COLUMNS"
+
+	ExceptColumns *PropertyGraphColumnNameList // optional
+}
+
+// PropertyGraphDerivedPropertyList represents a list of PropertyGraphDerivedProperty.
+// NOTE: In current syntax reference, "(" and ")" are missing.
+//
+//	PROPERTIES ({{.DerivedProperties | sqlJoin ", "}})
+type PropertyGraphDerivedPropertyList struct {
+	// pos = Properties
+	// end = Rparen
+
+	Properties        token.Pos                       // position of "PROPERTIES"
+	Rparen            token.Pos                       // position of ")"
+	DerivedProperties []*PropertyGraphDerivedProperty // len(DerivedProperties) > 0
+}
+
+// PropertyGraphDerivedProperty represents an expression that defines a property and can optionally reference the input table columns.
+//
+//	{{.Expr | sql}} {{if .Alias}}AS {{.Alias | sql}}{{end}}
+type PropertyGraphDerivedProperty struct {
+	// pos = Expr.pos
+	// end = (Alias ?? Expr).end
+
+	Expr  Expr
+	Alias *Ident // optional
+}
+
+// DropPropertyGraph is DROP PROPERTY GRAPH statement node.
+//
+//	DROP PROPERTY GRAPH {{if .IfExists}}IF EXISTS{{end}} {{.Name | sql}}
+type DropPropertyGraph struct {
+	// pos = Drop
+	// end = Name.end
+
+	Drop token.Pos
+
+	IfExists bool
+	Name     *Ident
+}
+
 // ================================================================================
 //
 // DML
@@ -3656,7 +3967,7 @@ type Insert struct {
 
 	InsertOrType InsertOrType
 
-	TableName  *Ident
+	TableName  *Path
 	Columns    []*Ident
 	Input      InsertInput
 	ThenReturn *ThenReturn // optional
@@ -3719,7 +4030,7 @@ type Delete struct {
 
 	Delete token.Pos // position of "DELETE" keyword
 
-	TableName  *Ident
+	TableName  *Path
 	As         *AsAlias // optional
 	Where      *Where
 	ThenReturn *ThenReturn // optional
@@ -3736,7 +4047,7 @@ type Update struct {
 
 	Update token.Pos // position of "UPDATE" keyword
 
-	TableName  *Ident
+	TableName  *Path
 	As         *AsAlias      // optional
 	Updates    []*UpdateItem // len(Updates) > 0
 	Where      *Where
diff --git a/ast/pos.go b/ast/pos.go
index f924446..d370cc4 100644
--- a/ast/pos.go
+++ b/ast/pos.go
@@ -1814,6 +1814,182 @@ func (a *AlterSearchIndex) End() token.Pos {
 	return nodeEnd(wrapNode(a.IndexAlteration))
 }
 
+func (c *CreatePropertyGraph) Pos() token.Pos {
+	return c.Create
+}
+
+func (c *CreatePropertyGraph) End() token.Pos {
+	return nodeEnd(wrapNode(c.Content))
+}
+
+func (p *PropertyGraphContent) Pos() token.Pos {
+	return nodePos(wrapNode(p.NodeTables))
+}
+
+func (p *PropertyGraphContent) End() token.Pos {
+	return nodeEnd(nodeChoice(wrapNode(p.EdgeTables), wrapNode(p.NodeTables)))
+}
+
+func (p *PropertyGraphNodeTables) Pos() token.Pos {
+	return p.Node
+}
+
+func (p *PropertyGraphNodeTables) End() token.Pos {
+	return nodeEnd(wrapNode(p.Tables))
+}
+
+func (p *PropertyGraphEdgeTables) Pos() token.Pos {
+	return p.Edge
+}
+
+func (p *PropertyGraphEdgeTables) End() token.Pos {
+	return nodeEnd(wrapNode(p.Tables))
+}
+
+func (p *PropertyGraphElementList) Pos() token.Pos {
+	return p.Lparen
+}
+
+func (p *PropertyGraphElementList) End() token.Pos {
+	return posAdd(p.Rparen, 1)
+}
+
+func (p *PropertyGraphElement) Pos() token.Pos {
+	return nodePos(wrapNode(p.Name))
+}
+
+func (p *PropertyGraphElement) End() token.Pos {
+	return nodeEnd(nodeChoice(wrapNode(p.Properties), wrapNode(p.Keys), wrapNode(p.Alias), wrapNode(p.Name)))
+}
+
+func (p *PropertyGraphSingleProperties) Pos() token.Pos {
+	return nodePos(wrapNode(p.Properties))
+}
+
+func (p *PropertyGraphSingleProperties) End() token.Pos {
+	return nodeEnd(wrapNode(p.Properties))
+}
+
+func (p *PropertyGraphLabelAndPropertiesList) Pos() token.Pos {
+	return nodePos(nodeSliceIndex(p.LabelAndProperties, 0))
+}
+
+func (p *PropertyGraphLabelAndPropertiesList) End() token.Pos {
+	return nodeEnd(nodeSliceLast(p.LabelAndProperties))
+}
+
+func (p *PropertyGraphLabelAndProperties) Pos() token.Pos {
+	return nodePos(wrapNode(p.Label))
+}
+
+func (p *PropertyGraphLabelAndProperties) End() token.Pos {
+	return nodeEnd(nodeChoice(wrapNode(p.Properties), wrapNode(p.Label)))
+}
+
+func (p *PropertyGraphElementLabelLabelName) Pos() token.Pos {
+	return p.Label
+}
+
+func (p *PropertyGraphElementLabelLabelName) End() token.Pos {
+	return nodeEnd(wrapNode(p.Name))
+}
+
+func (p *PropertyGraphElementLabelDefaultLabel) Pos() token.Pos {
+	return p.Default
+}
+
+func (p *PropertyGraphElementLabelDefaultLabel) End() token.Pos {
+	return posAdd(p.Label, 5)
+}
+
+func (p *PropertyGraphNodeElementKey) Pos() token.Pos {
+	return nodePos(wrapNode(p.Key))
+}
+
+func (p *PropertyGraphNodeElementKey) End() token.Pos {
+	return nodeEnd(wrapNode(p.Key))
+}
+
+func (p *PropertyGraphEdgeElementKeys) Pos() token.Pos {
+	return nodePos(wrapNode(p.Element))
+}
+
+func (p *PropertyGraphEdgeElementKeys) End() token.Pos {
+	return nodeEnd(wrapNode(p.Destination))
+}
+
+func (p *PropertyGraphElementKey) Pos() token.Pos {
+	return p.Key
+}
+
+func (p *PropertyGraphElementKey) End() token.Pos {
+	return nodeEnd(wrapNode(p.Keys))
+}
+
+func (p *PropertyGraphSourceKey) Pos() token.Pos {
+	return p.Source
+}
+
+func (p *PropertyGraphSourceKey) End() token.Pos {
+	return nodeEnd(nodeChoice(wrapNode(p.ReferenceColumns), wrapNode(p.ElementReference)))
+}
+
+func (p *PropertyGraphDestinationKey) Pos() token.Pos {
+	return p.Destination
+}
+
+func (p *PropertyGraphDestinationKey) End() token.Pos {
+	return nodeEnd(nodeChoice(wrapNode(p.ReferenceColumns), wrapNode(p.ElementReference)))
+}
+
+func (p *PropertyGraphColumnNameList) Pos() token.Pos {
+	return p.Lparen
+}
+
+func (p *PropertyGraphColumnNameList) End() token.Pos {
+	return posAdd(p.Rparen, 1)
+}
+
+func (p *PropertyGraphNoProperties) Pos() token.Pos {
+	return p.No
+}
+
+func (p *PropertyGraphNoProperties) End() token.Pos {
+	return posAdd(p.Properties, 10)
+}
+
+func (p *PropertyGraphPropertiesAre) Pos() token.Pos {
+	return p.Properties
+}
+
+func (p *PropertyGraphPropertiesAre) End() token.Pos {
+	return posChoice(nodeEnd(wrapNode(p.ExceptColumns)), posAdd(p.Columns, 7))
+}
+
+func (p *PropertyGraphDerivedPropertyList) Pos() token.Pos {
+	return p.Properties
+}
+
+func (p *PropertyGraphDerivedPropertyList) End() token.Pos {
+	return p.Rparen
+}
+
+func (p *PropertyGraphDerivedProperty) Pos() token.Pos {
+	return nodePos(wrapNode(p.Expr))
+}
+
+func (p *PropertyGraphDerivedProperty) End() token.Pos {
+	return nodeEnd(nodeChoice(wrapNode(p.Alias), wrapNode(p.Expr)))
+}
+
+func (d *DropPropertyGraph) Pos() token.Pos {
+	return d.Drop
+}
+
+func (d *DropPropertyGraph) End() token.Pos {
+	return nodeEnd(wrapNode(d.Name))
+}
+
 func (w *WithAction) Pos() token.Pos {
 	return w.With
 }
diff --git a/ast/sql.go b/ast/sql.go
index e63880d..f110b0c 100644
--- a/ast/sql.go
+++ b/ast/sql.go
@@ -1149,6 +1149,104 @@ func (d *DropModel) SQL() string {
 	return "DROP MODEL " + strOpt(d.IfExists, "IF EXISTS ") + d.Name.SQL()
 }
 
+// ================================================================================
+//
+// GQL schema statements
+//
+// ================================================================================
+
+func (c *CreatePropertyGraph) SQL() string {
+	return "CREATE " +
+		strOpt(c.OrReplace, "OR REPLACE ") +
+		"PROPERTY GRAPH " +
+		strOpt(c.IfNotExists, "IF NOT EXISTS ") +
+		c.Name.SQL() + " " + c.Content.SQL()
+}
+
+func (p *PropertyGraphContent) SQL() string {
+	return p.NodeTables.SQL() + sqlOpt(" ", p.EdgeTables, "")
+}
+
+func (p *PropertyGraphNodeTables) SQL() string {
+	return "NODE TABLES " + p.Tables.SQL()
+}
+
+func (p *PropertyGraphEdgeTables) SQL() string {
+	return "EDGE TABLES " + p.Tables.SQL()
+}
+
+func (p *PropertyGraphElementList) SQL() string {
+	return "(" + sqlJoin(p.Elements, ", ") + ")"
+}
+
+func (p *PropertyGraphElement) SQL() string {
+	return p.Name.SQL() +
+		sqlOpt(" AS ", p.Alias, "") +
+		sqlOpt(" ", p.Keys, "") +
+		sqlOpt(" ", p.Properties, "")
+}
+
+func (p *PropertyGraphSingleProperties) SQL() string { return p.Properties.SQL() }
+
+func (p *PropertyGraphLabelAndPropertiesList) SQL() string {
+	return sqlJoin(p.LabelAndProperties, " ")
+}
+
+func (p *PropertyGraphLabelAndProperties) SQL() string {
+	return p.Label.SQL() + sqlOpt(" ", p.Properties, "")
+}
+
+func (p *PropertyGraphElementLabelLabelName) SQL() string { return "LABEL " + p.Name.SQL() }
+
+func (p *PropertyGraphElementLabelDefaultLabel) SQL() string { return "DEFAULT LABEL" }
+
+func (p *PropertyGraphNodeElementKey) SQL() string { return p.Key.SQL() }
+
+func (p *PropertyGraphEdgeElementKeys) SQL() string {
+	return sqlOpt("", p.Element, " ") +
+		p.Source.SQL() + " " + p.Destination.SQL()
+}
+
+func (p *PropertyGraphElementKey) SQL() string { return "KEY " + p.Keys.SQL() }
+
+func (p *PropertyGraphSourceKey) SQL() string {
+	return "SOURCE KEY " + p.Keys.SQL() +
+		" REFERENCES " + p.ElementReference.SQL() +
+		sqlOpt(" ", p.ReferenceColumns, "")
+}
+
+func (p *PropertyGraphDestinationKey) SQL() string {
+	return "DESTINATION KEY " + p.Keys.SQL() +
+		" REFERENCES " + p.ElementReference.SQL() +
+		sqlOpt(" ", p.ReferenceColumns, "")
+}
+
+func (p *PropertyGraphColumnNameList) SQL() string {
+	return "(" + sqlJoin(p.ColumnNameList, ", ") + ")"
+}
+
+func (p *PropertyGraphNoProperties) SQL() string {
+	return "NO PROPERTIES"
+}
+
+func (p *PropertyGraphPropertiesAre) SQL() string {
+	return "PROPERTIES ARE ALL COLUMNS" + sqlOpt(" EXCEPT ", p.ExceptColumns, "")
+}
+
+func (p *PropertyGraphDerivedPropertyList) SQL() string {
+	return "PROPERTIES (" + sqlJoin(p.DerivedProperties, ", ") + ")"
+}
+
+func (p *PropertyGraphDerivedProperty) SQL() string {
+	return p.Expr.SQL() + sqlOpt(" AS ", p.Alias, "")
+}
+
+func (g *DropPropertyGraph) SQL() string {
+	return "DROP PROPERTY GRAPH " +
+		strOpt(g.IfExists, "IF EXISTS ") +
+		g.Name.SQL()
+}
+
 // ================================================================================
 //
 // Types for Schema
diff --git a/parser.go b/parser.go
index f759615..2d46bda 100644
--- a/parser.go
+++ b/parser.go
@@ -2823,7 +2823,11 @@ func (p *Parser) parseDDL() (ddl ast.DDL) {
 				return p.parseCreateView(pos, true)
 			case p.Token.IsKeywordLike("MODEL"):
 				return p.parseCreateModel(pos, true)
+			case p.Token.IsKeywordLike("PROPERTY"):
+				return p.parseCreatePropertyGraph(pos, true)
 			}
+		case p.Token.IsKeywordLike("PROPERTY"):
+			return p.parseCreatePropertyGraph(pos, false)
 		}
 		p.panicfAtToken(&p.Token, "expected pseudo keyword: DATABASE, TABLE, INDEX, UNIQUE, NULL_FILTERED, ROLE, CHANGE, OR but: %s", p.Token.AsString)
 	case p.Token.IsKeywordLike("ALTER"):
@@ -2874,6 +2878,8 @@ func (p *Parser) parseDDL() (ddl ast.DDL) {
 			return p.parseDropChangeStream(pos)
 		case p.Token.IsKeywordLike("MODEL"):
 			return p.parseDropModel(pos)
+		case p.Token.IsKeywordLike("PROPERTY"):
+			return p.parseDropPropertyGraph(pos)
 		}
 		p.panicfAtToken(&p.Token, "expected pseudo keyword: TABLE, INDEX, ROLE, CHANGE, MODEL, but: %s", p.Token.AsString)
 	case p.Token.IsKeywordLike("RENAME"):
@@ -4462,6 +4468,307 @@ func (p *Parser) tryParseTablePrivilegeColumns() ([]*ast.Ident, token.Pos) {
 	return columns, rparen
 }
 
+// begin CREATE PROPERTY GRAPH
+
+func (p *Parser) parseCreatePropertyGraph(pos token.Pos, orReplace bool) *ast.CreatePropertyGraph {
+	p.expectKeywordLike("PROPERTY")
+	p.expectKeywordLike("GRAPH")
+
+	ifNotExists := p.parseIfNotExists()
+	name := p.parseIdent()
+	content := p.parsePropertyGraphContent()
+
+	return &ast.CreatePropertyGraph{
+		Create:      pos,
+		OrReplace:   orReplace,
+		IfNotExists: ifNotExists,
+		Name:        name,
+		Content:     content,
+	}
+}
+
+func (p *Parser) parsePropertyGraphContent() *ast.PropertyGraphContent {
+	node := p.expectKeywordLike("NODE").Pos
+	p.expectKeywordLike("TABLES")
+
+	nodeTables := &ast.PropertyGraphNodeTables{
+		Node:   node,
+		Tables: p.parsePropertyGraphElementList(),
+	}
+
+	var edgeTables *ast.PropertyGraphEdgeTables
+	if p.Token.IsKeywordLike("EDGE") {
+		edge := p.expectKeywordLike("EDGE").Pos
+		p.expectKeywordLike("TABLES")
+		tables := p.parsePropertyGraphElementList()
+		edgeTables = &ast.PropertyGraphEdgeTables{
+			Edge:   edge,
+			Tables: tables,
+		}
+	}
+
+	return &ast.PropertyGraphContent{
+		NodeTables: nodeTables,
+		EdgeTables: edgeTables,
+	}
+}
+
+func (p *Parser) parsePropertyGraphElementList() *ast.PropertyGraphElementList {
+	lparen := p.expect("(").Pos
+	elements := parseCommaSeparatedList(p, p.parsePropertyGraphElement)
+	rparen := p.expect(")").Pos
+
+	return &ast.PropertyGraphElementList{
+		Lparen:   lparen,
+		Rparen:   rparen,
+		Elements: elements,
+	}
+
+}
+
+func (p *Parser) parsePropertyGraphElement() *ast.PropertyGraphElement {
+	name := p.parseIdent()
+
+	var alias *ast.Ident
+	if p.Token.Kind == "AS" {
+		p.nextToken()
+		alias = p.parseIdent()
+	}
+
+	keys := p.tryParsePropertyGraphElementKeys()
+	properties := p.tryParsePropertyGraphLabelsOrProperties()
+
+	return &ast.PropertyGraphElement{
+		Name:       name,
+		Alias:      alias,
+		Keys:       keys,
+		Properties: properties,
+	}
+}
+
+// parsePropertyGraphLabelAndPropertiesList parses consecutive ast.PropertyGraphLabelAndProperties,
+// and returns *ast.PropertyGraphLabelAndPropertiesList.
+func (p *Parser) parsePropertyGraphLabelAndPropertiesList() *ast.PropertyGraphLabelAndPropertiesList {
+	// list can be empty
+	var list []*ast.PropertyGraphLabelAndProperties
+	for {
+		if p.Token.Kind != "DEFAULT" && !p.Token.IsKeywordLike("LABEL") {
+			break
+		}
+
+		elemLabel := p.parsePropertyGraphElementLabel()
+		properties := p.tryParsePropertyGraphElementProperties()
+
+		list = append(list, &ast.PropertyGraphLabelAndProperties{
+			Label:      elemLabel,
+			Properties: properties,
+		})
+	}
+
+	return &ast.PropertyGraphLabelAndPropertiesList{
+		LabelAndProperties: list,
+	}
+}
+
+func (p *Parser) parsePropertyGraphElementLabel() ast.PropertyGraphElementLabel {
+	if p.Token.Kind == "DEFAULT" {
+		def := p.expect("DEFAULT").Pos
+		label := p.expectKeywordLike("LABEL").Pos
+
+		return &ast.PropertyGraphElementLabelDefaultLabel{
+			Default: def,
+			Label:   label,
+		}
+	}
+
+	label := p.expectKeywordLike("LABEL").Pos
+	name := p.parseIdent()
+
+	return &ast.PropertyGraphElementLabelLabelName{
+		Label: label,
+		Name:  name,
+	}
+}
+
+func (p *Parser) tryParsePropertyGraphElementProperties() ast.PropertyGraphElementProperties {
+	if p.Token.Kind != "NO" && !p.Token.IsKeywordLike("PROPERTIES") {
+		return nil
+	}
+
+	return p.parsePropertyGraphElementProperties()
+}
+
+func (p *Parser) parsePropertyGraphElementProperties() ast.PropertyGraphElementProperties {
+	if p.Token.Kind != "NO" && !p.Token.IsKeywordLike("PROPERTIES") {
+		p.panicfAtToken(&p.Token, `expect "NO" or "PROPERTIES", but %v`, p.Token.Kind)
+	}
+
+	if p.Token.Kind == "NO" {
+		no := p.expect("NO").Pos
+		properties := p.expectKeywordLike("PROPERTIES").Pos
+
+		return &ast.PropertyGraphNoProperties{
+			No:         no,
+			Properties: properties,
+		}
+	}
+
+	properties := p.expectKeywordLike("PROPERTIES")
+	if p.Token.IsKeywordLike("ARE") || p.Token.Kind == "ALL" {
+		// "ARE" is optional
+		if p.Token.IsKeywordLike("ARE") {
+			p.nextToken()
+		}
+
+		p.expect("ALL")
+		columns := p.expectKeywordLike("COLUMNS").Pos
+
+		var exceptColumns *ast.PropertyGraphColumnNameList
+		if p.Token.Kind == "EXCEPT" {
+			p.nextToken()
+			exceptColumns = p.parsePropertyGraphColumnNameList()
+		}
+
+		return &ast.PropertyGraphPropertiesAre{
+			Properties:    properties.Pos,
+			Columns:       columns,
+			ExceptColumns: exceptColumns,
+		}
+	}
+
+	p.expect("(")
+	list := parseCommaSeparatedList(p, p.parsePropertyGraphDerivedProperty)
+	rparen := p.expect(")").Pos
+
+	return &ast.PropertyGraphDerivedPropertyList{
+		Rparen:            rparen,
+		Properties:        properties.Pos,
+		DerivedProperties: list,
+	}
+}
+
+func (p *Parser) parsePropertyGraphDerivedProperty() *ast.PropertyGraphDerivedProperty {
+	expr := p.parseExpr()
+
+	var name *ast.Ident
+	if p.Token.Kind == "AS" {
+		p.nextToken()
+		name = p.parseIdent()
+	}
+
+	return &ast.PropertyGraphDerivedProperty{
+		Expr:  expr,
+		Alias: name,
+	}
+}
+
+func (p *Parser) tryParsePropertyGraphLabelsOrProperties() ast.PropertyGraphLabelsOrProperties {
+	if p.Token.IsKeywordLike("LABEL") || p.Token.Kind == "DEFAULT" {
+		return p.parsePropertyGraphLabelAndPropertiesList()
+	}
+
+	if properties := p.tryParsePropertyGraphElementProperties(); properties != nil {
+		return &ast.PropertyGraphSingleProperties{
+			Properties: properties,
+		}
+	}
+	return nil
+}
+
+func (p *Parser) tryParsePropertyGraphElementKeys() ast.PropertyGraphElementKeys {
+	if !p.Token.IsKeywordLike("KEY") && !p.Token.IsKeywordLike("SOURCE") {
+		return nil
+	}
+
+	// element_key
+	var elementKey *ast.PropertyGraphElementKey
+	if p.Token.IsKeywordLike("KEY") {
+		key := p.expectKeywordLike("KEY").Pos
+		keyColumns := p.parsePropertyGraphColumnNameList()
+
+		elementKey = &ast.PropertyGraphElementKey{
+			Key:  key,
+			Keys: keyColumns,
+		}
+
+		// if SOURCE KEY doesn't follow, it is node_element_key.
+		if !p.Token.IsKeywordLike("SOURCE") {
+			return &ast.PropertyGraphNodeElementKey{
+				Key: elementKey,
+			}
+		}
+
+	}
+
+	// the rest of edge_element_keys
+
+	// source_key
+	source := p.expectKeywordLike("SOURCE").Pos
+	p.expectKeywordLike("KEY")
+	sourceColumns := p.parsePropertyGraphColumnNameList()
+	p.expectKeywordLike("REFERENCES")
+	sourceReference := p.parseIdent()
+	sourceReferenceColumns := p.tryParsePropertyGraphColumnNameList()
+
+	// destination_key
+	destination := p.expectKeywordLike("DESTINATION").Pos
+	p.expectKeywordLike("KEY")
+	destinationColumns := p.parsePropertyGraphColumnNameList()
+	p.expectKeywordLike("REFERENCES")
+	destinationReference := p.parseIdent()
+	destinationReferenceColumns := p.tryParsePropertyGraphColumnNameList()
+
+	return &ast.PropertyGraphEdgeElementKeys{
+		Element: elementKey,
+		Source: &ast.PropertyGraphSourceKey{
+			Source:           source,
+			Keys:             sourceColumns,
+			ElementReference: sourceReference,
+			ReferenceColumns: sourceReferenceColumns,
+		},
+		Destination: &ast.PropertyGraphDestinationKey{
+			Destination:      destination,
+			Keys:             destinationColumns,
+			ElementReference: destinationReference,
+			ReferenceColumns: destinationReferenceColumns,
+		},
+	}
+}
+
+func (p *Parser) parsePropertyGraphColumnNameList() *ast.PropertyGraphColumnNameList {
+	lparen := p.expect("(").Pos
+	list := parseCommaSeparatedList(p, p.parseIdent)
+	rparen := p.expect(")").Pos
+
+	return &ast.PropertyGraphColumnNameList{
+		Lparen:         lparen,
+		Rparen:         rparen,
+		ColumnNameList: list,
+	}
+}
+
+func (p *Parser) tryParsePropertyGraphColumnNameList() *ast.PropertyGraphColumnNameList {
+	if p.Token.Kind != "(" {
+		return nil
+	}
+	return p.parsePropertyGraphColumnNameList()
+}
+
+// end CREATE PROPERTY GRAPH
+
+func (p *Parser) parseDropPropertyGraph(pos token.Pos) *ast.DropPropertyGraph {
+	p.expectKeywordLike("PROPERTY")
+	p.expectKeywordLike("GRAPH")
+	ifExists := p.parseIfExists()
+	name := p.parseIdent()
+
+	return &ast.DropPropertyGraph{
+		Drop:     pos,
+		IfExists: ifExists,
+		Name:     name,
+	}
+}
+
 func (p *Parser) parseSchemaType() ast.SchemaType {
 	switch p.Token.Kind {
 	case token.TokenIdent:
@@ -4754,7 +5061,7 @@ func (p *Parser) parseInsert(pos token.Pos) *ast.Insert {
 		p.nextToken()
 	}
 
-	name := p.parseIdent()
+	name := p.parsePath()
 
 	p.expect("(")
 	var columns []*ast.Ident
@@ -4849,7 +5156,7 @@ func (p *Parser) parseDelete(pos token.Pos) *ast.Delete {
 		p.nextToken()
 	}
 
-	name := p.parseIdent()
+	name := p.parsePath()
 	as := p.tryParseAsAlias(withOptionalAs)
 	where := p.parseWhere()
 	thenReturn := p.tryParseThenReturn()
@@ -4864,7 +5171,7 @@ func (p *Parser) parseDelete(pos token.Pos) *ast.Delete {
 }
 
 func (p *Parser) parseUpdate(pos token.Pos) *ast.Update {
-	name := p.parseIdent()
+	name := p.parsePath()
 	as := p.tryParseAsAlias(withOptionalAs)
 
 	p.expect("SET")


Test patch:
diff --git a/testdata/input/ddl/create_or_replace_property_graph_fingraph_verbose.sql b/testdata/input/ddl/create_or_replace_property_graph_fingraph_verbose.sql
new file mode 100644
index 0000000..2e97e23
--- /dev/null
+++ b/testdata/input/ddl/create_or_replace_property_graph_fingraph_verbose.sql
@@ -0,0 +1,28 @@
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Account AS Account -- element_alias
+      KEY (id) -- element_key in node_element_key in element_keys
+      -- label_and_property_list
+      LABEL DetailedAccount -- LABEL label_name in element_label
+        PROPERTIES (create_time, is_blocked, nick_name AS name) -- derived_property_list
+      DEFAULT LABEL -- DEFAULT LABEL in element_label
+        NO PROPERTIES -- NO PROPERTIES in element_properties
+    ,
+    Person
+      -- no element_keys
+      -- no element_label because of direct element_properties
+      PROPERTIES ARE ALL COLUMNS EXCEPT (city) -- properties_are
+  )
+  EDGE TABLES (
+    PersonOwnAccount AS PersonOwnAccount
+      KEY (id, account_id)
+      SOURCE KEY (id) REFERENCES Person -- source_key without column_name_list
+      DESTINATION KEY (account_id) REFERENCES Account -- destination_key without column_name_list
+      LABEL Owns
+        PROPERTIES ALL COLUMNS,
+    AccountTransferAccount
+      SOURCE KEY (id) REFERENCES Account (id) -- source_key
+      DESTINATION KEY (to_id) REFERENCES Account (id) -- destination_key
+      LABEL Transfers -- LABEL label_name in element_label
+      -- without element_properties
+  )
\ No newline at end of file
diff --git a/testdata/input/ddl/create_property_graph_if_not_exists_fingraph.sql b/testdata/input/ddl/create_property_graph_if_not_exists_fingraph.sql
new file mode 100644
index 0000000..632af15
--- /dev/null
+++ b/testdata/input/ddl/create_property_graph_if_not_exists_fingraph.sql
@@ -0,0 +1,15 @@
+CREATE PROPERTY GRAPH IF NOT EXISTS FinGraph
+  NODE TABLES (
+    Account,
+    Person
+  )
+  EDGE TABLES (
+    PersonOwnAccount
+      SOURCE KEY (id) REFERENCES Person (id)
+      DESTINATION KEY (account_id) REFERENCES Account (id)
+      LABEL Owns,
+    AccountTransferAccount
+      SOURCE KEY (id) REFERENCES Account (id)
+      DESTINATION KEY (to_id) REFERENCES Account (id)
+      LABEL Transfers
+  )
\ No newline at end of file
diff --git a/testdata/input/ddl/drop_property_graph.sql b/testdata/input/ddl/drop_property_graph.sql
new file mode 100644
index 0000000..403d329
--- /dev/null
+++ b/testdata/input/ddl/drop_property_graph.sql
@@ -0,0 +1,1 @@
+DROP PROPERTY GRAPH FinGraph
diff --git a/testdata/input/ddl/drop_property_graph_if_exists.sql b/testdata/input/ddl/drop_property_graph_if_exists.sql
new file mode 100644
index 0000000..ecb5196
--- /dev/null
+++ b/testdata/input/ddl/drop_property_graph_if_exists.sql
@@ -0,0 +1,1 @@
+DROP PROPERTY GRAPH IF EXISTS FinGraph
diff --git a/testdata/input/dml/delete_fqn.sql b/testdata/input/dml/delete_fqn.sql
new file mode 100644
index 0000000..dbf0d93
--- /dev/null
+++ b/testdata/input/dml/delete_fqn.sql
@@ -0,0 +1,1 @@
+delete sch1.foo where foo = 1 and bar = 2
\ No newline at end of file
diff --git a/testdata/input/dml/insert_fqn_values.sql b/testdata/input/dml/insert_fqn_values.sql
new file mode 100644
index 0000000..9ce5040
--- /dev/null
+++ b/testdata/input/dml/insert_fqn_values.sql
@@ -0,0 +1,3 @@
+insert sch1.foo (foo, bar, baz)
+values (1, 2, 3),
+       (4, 5, 6)
\ No newline at end of file
diff --git a/testdata/input/dml/update_fqn.sql b/testdata/input/dml/update_fqn.sql
new file mode 100644
index 0000000..8bf9d13
--- /dev/null
+++ b/testdata/input/dml/update_fqn.sql
@@ -0,0 +1,1 @@
+update sch1.foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1
\ No newline at end of file
diff --git a/testdata/result/ddl/create_or_replace_property_graph_fingraph_verbose.sql.txt b/testdata/result/ddl/create_or_replace_property_graph_fingraph_verbose.sql.txt
new file mode 100644
index 0000000..4094102
--- /dev/null
+++ b/testdata/result/ddl/create_or_replace_property_graph_fingraph_verbose.sql.txt
@@ -0,0 +1,341 @@
+--- create_or_replace_property_graph_fingraph_verbose.sql
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Account AS Account -- element_alias
+      KEY (id) -- element_key in node_element_key in element_keys
+      -- label_and_property_list
+      LABEL DetailedAccount -- LABEL label_name in element_label
+        PROPERTIES (create_time, is_blocked, nick_name AS name) -- derived_property_list
+      DEFAULT LABEL -- DEFAULT LABEL in element_label
+        NO PROPERTIES -- NO PROPERTIES in element_properties
+    ,
+    Person
+      -- no element_keys
+      -- no element_label because of direct element_properties
+      PROPERTIES ARE ALL COLUMNS EXCEPT (city) -- properties_are
+  )
+  EDGE TABLES (
+    PersonOwnAccount AS PersonOwnAccount
+      KEY (id, account_id)
+      SOURCE KEY (id) REFERENCES Person -- source_key without column_name_list
+      DESTINATION KEY (account_id) REFERENCES Account -- destination_key without column_name_list
+      LABEL Owns
+        PROPERTIES ALL COLUMNS,
+    AccountTransferAccount
+      SOURCE KEY (id) REFERENCES Account (id) -- source_key
+      DESTINATION KEY (to_id) REFERENCES Account (id) -- destination_key
+      LABEL Transfers -- LABEL label_name in element_label
+      -- without element_properties
+  )
+--- AST
+&ast.CreatePropertyGraph{
+  OrReplace: true,
+  Name:      &ast.Ident{
+    NamePos: 33,
+    NameEnd: 41,
+    Name:    "FinGraph",
+  },
+  Content: &ast.PropertyGraphContent{
+    NodeTables: &ast.PropertyGraphNodeTables{
+      Node:   44,
+      Tables: &ast.PropertyGraphElementList{
+        Lparen:   56,
+        Rparen:   638,
+        Elements: []*ast.PropertyGraphElement{
+          &ast.PropertyGraphElement{
+            Name: &ast.Ident{
+              NamePos: 62,
+              NameEnd: 69,
+              Name:    "Account",
+            },
+            Alias: &ast.Ident{
+              NamePos: 73,
+              NameEnd: 80,
+              Name:    "Account",
+            },
+            Keys: &ast.PropertyGraphNodeElementKey{
+              Key: &ast.PropertyGraphElementKey{
+                Key:  104,
+                Keys: &ast.PropertyGraphColumnNameList{
+                  Lparen:         108,
+                  Rparen:         111,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 109,
+                      NameEnd: 111,
+                      Name:    "id",
+                    },
+                  },
+                },
+              },
+            },
+            Properties: &ast.PropertyGraphLabelAndPropertiesList{
+              LabelAndProperties: []*ast.PropertyGraphLabelAndProperties{
+                &ast.PropertyGraphLabelAndProperties{
+                  Label: &ast.PropertyGraphElementLabelLabelName{
+                    Label: 203,
+                    Name:  &ast.Ident{
+                      NamePos: 209,
+                      NameEnd: 224,
+                      Name:    "DetailedAccount",
+                    },
+                  },
+                  Properties: &ast.PropertyGraphDerivedPropertyList{
+                    Properties:        270,
+                    Rparen:            324,
+                    DerivedProperties: []*ast.PropertyGraphDerivedProperty{
+                      &ast.PropertyGraphDerivedProperty{
+                        Expr: &ast.Ident{
+                          NamePos: 282,
+                          NameEnd: 293,
+                          Name:    "create_time",
+                        },
+                      },
+                      &ast.PropertyGraphDerivedProperty{
+                        Expr: &ast.Ident{
+                          NamePos: 295,
+                          NameEnd: 305,
+                          Name:    "is_blocked",
+                        },
+                      },
+                      &ast.PropertyGraphDerivedProperty{
+                        Expr: &ast.Ident{
+                          NamePos: 307,
+                          NameEnd: 316,
+                          Name:    "nick_name",
+                        },
+                        Alias: &ast.Ident{
+                          NamePos: 320,
+                          NameEnd: 324,
+                          Name:    "name",
+                        },
+                      },
+                    },
+                  },
+                },
+                &ast.PropertyGraphLabelAndProperties{
+                  Label: &ast.PropertyGraphElementLabelDefaultLabel{
+                    Default: 357,
+                    Label:   365,
+                  },
+                  Properties: &ast.PropertyGraphNoProperties{
+                    No:         413,
+                    Properties: 416,
+                  },
+                },
+              },
+            },
+          },
+          &ast.PropertyGraphElement{
+            Name: &ast.Ident{
+              NamePos: 476,
+              NameEnd: 482,
+              Name:    "Person",
+            },
+            Properties: &ast.PropertyGraphSingleProperties{
+              Properties: &ast.PropertyGraphPropertiesAre{
+                Properties:    577,
+                Columns:       596,
+                ExceptColumns: &ast.PropertyGraphColumnNameList{
+                  Lparen:         611,
+                  Rparen:         616,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 612,
+                      NameEnd: 616,
+                      Name:    "city",
+                    },
+                  },
+                },
+              },
+            },
+          },
+        },
+      },
+    },
+    EdgeTables: &ast.PropertyGraphEdgeTables{
+      Edge:   642,
+      Tables: &ast.PropertyGraphElementList{
+        Lparen:   654,
+        Rparen:   1207,
+        Elements: []*ast.PropertyGraphElement{
+          &ast.PropertyGraphElement{
+            Name: &ast.Ident{
+              NamePos: 660,
+              NameEnd: 676,
+              Name:    "PersonOwnAccount",
+            },
+            Alias: &ast.Ident{
+              NamePos: 680,
+              NameEnd: 696,
+              Name:    "PersonOwnAccount",
+            },
+            Keys: &ast.PropertyGraphEdgeElementKeys{
+              Element: &ast.PropertyGraphElementKey{
+                Key:  703,
+                Keys: &ast.PropertyGraphColumnNameList{
+                  Lparen:         707,
+                  Rparen:         722,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 708,
+                      NameEnd: 710,
+                      Name:    "id",
+                    },
+                    &ast.Ident{
+                      NamePos: 712,
+                      NameEnd: 722,
+                      Name:    "account_id",
+                    },
+                  },
+                },
+              },
+              Source: &ast.PropertyGraphSourceKey{
+                Source: 730,
+                Keys:   &ast.PropertyGraphColumnNameList{
+                  Lparen:         741,
+                  Rparen:         744,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 742,
+                      NameEnd: 744,
+                      Name:    "id",
+                    },
+                  },
+                },
+                ElementReference: &ast.Ident{
+                  NamePos: 757,
+                  NameEnd: 763,
+                  Name:    "Person",
+                },
+              },
+              Destination: &ast.PropertyGraphDestinationKey{
+                Destination: 809,
+                Keys:        &ast.PropertyGraphColumnNameList{
+                  Lparen:         825,
+                  Rparen:         836,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 826,
+                      NameEnd: 836,
+                      Name:    "account_id",
+                    },
+                  },
+                },
+                ElementReference: &ast.Ident{
+                  NamePos: 849,
+                  NameEnd: 856,
+                  Name:    "Account",
+                },
+              },
+            },
+            Properties: &ast.PropertyGraphLabelAndPropertiesList{
+              LabelAndProperties: []*ast.PropertyGraphLabelAndProperties{
+                &ast.PropertyGraphLabelAndProperties{
+                  Label: &ast.PropertyGraphElementLabelLabelName{
+                    Label: 907,
+                    Name:  &ast.Ident{
+                      NamePos: 913,
+                      NameEnd: 917,
+                      Name:    "Owns",
+                    },
+                  },
+                  Properties: &ast.PropertyGraphPropertiesAre{
+                    Properties: 926,
+                    Columns:    941,
+                  },
+                },
+              },
+            },
+          },
+          &ast.PropertyGraphElement{
+            Name: &ast.Ident{
+              NamePos: 954,
+              NameEnd: 976,
+              Name:    "AccountTransferAccount",
+            },
+            Keys: &ast.PropertyGraphEdgeElementKeys{
+              Source: &ast.PropertyGraphSourceKey{
+                Source: 983,
+                Keys:   &ast.PropertyGraphColumnNameList{
+                  Lparen:         994,
+                  Rparen:         997,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 995,
+                      NameEnd: 997,
+                      Name:    "id",
+                    },
+                  },
+                },
+                ElementReference: &ast.Ident{
+                  NamePos: 1010,
+                  NameEnd: 1017,
+                  Name:    "Account",
+                },
+                ReferenceColumns: &ast.PropertyGraphColumnNameList{
+                  Lparen:         1018,
+                  Rparen:         1021,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 1019,
+                      NameEnd: 1021,
+                      Name:    "id",
+                    },
+                  },
+                },
+              },
+              Destination: &ast.PropertyGraphDestinationKey{
+                Destination: 1043,
+                Keys:        &ast.PropertyGraphColumnNameList{
+                  Lparen:         1059,
+                  Rparen:         1065,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 1060,
+                      NameEnd: 1065,
+                      Name:    "to_id",
+                    },
+                  },
+                },
+                ElementReference: &ast.Ident{
+                  NamePos: 1078,
+                  NameEnd: 1085,
+                  Name:    "Account",
+                },
+                ReferenceColumns: &ast.PropertyGraphColumnNameList{
+                  Lparen:         1086,
+                  Rparen:         1089,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 1087,
+                      NameEnd: 1089,
+                      Name:    "id",
+                    },
+                  },
+                },
+              },
+            },
+            Properties: &ast.PropertyGraphLabelAndPropertiesList{
+              LabelAndProperties: []*ast.PropertyGraphLabelAndProperties{
+                &ast.PropertyGraphLabelAndProperties{
+                  Label: &ast.PropertyGraphElementLabelLabelName{
+                    Label: 1116,
+                    Name:  &ast.Ident{
+                      NamePos: 1122,
+                      NameEnd: 1131,
+                      Name:    "Transfers",
+                    },
+                  },
+                },
+              },
+            },
+          },
+        },
+      },
+    },
+  },
+}
+
+--- SQL
+CREATE OR REPLACE PROPERTY GRAPH FinGraph NODE TABLES (Account AS Account KEY (id) LABEL DetailedAccount PROPERTIES (create_time, is_blocked, nick_name AS name) DEFAULT LABEL NO PROPERTIES, Person PROPERTIES ARE ALL COLUMNS EXCEPT (city)) EDGE TABLES (PersonOwnAccount AS PersonOwnAccount KEY (id, account_id) SOURCE KEY (id) REFERENCES Person DESTINATION KEY (account_id) REFERENCES Account LABEL Owns PROPERTIES ARE ALL COLUMNS, AccountTransferAccount SOURCE KEY (id) REFERENCES Account (id) DESTINATION KEY (to_id) REFERENCES Account (id) LABEL Transfers)
diff --git a/testdata/result/ddl/create_property_graph_if_not_exists_fingraph.sql.txt b/testdata/result/ddl/create_property_graph_if_not_exists_fingraph.sql.txt
new file mode 100644
index 0000000..a0869e1
--- /dev/null
+++ b/testdata/result/ddl/create_property_graph_if_not_exists_fingraph.sql.txt
@@ -0,0 +1,228 @@
+--- create_property_graph_if_not_exists_fingraph.sql
+CREATE PROPERTY GRAPH IF NOT EXISTS FinGraph
+  NODE TABLES (
+    Account,
+    Person
+  )
+  EDGE TABLES (
+    PersonOwnAccount
+      SOURCE KEY (id) REFERENCES Person (id)
+      DESTINATION KEY (account_id) REFERENCES Account (id)
+      LABEL Owns,
+    AccountTransferAccount
+      SOURCE KEY (id) REFERENCES Account (id)
+      DESTINATION KEY (to_id) REFERENCES Account (id)
+      LABEL Transfers
+  )
+--- AST
+&ast.CreatePropertyGraph{
+  IfNotExists: true,
+  Name:        &ast.Ident{
+    NamePos: 36,
+    NameEnd: 44,
+    Name:    "FinGraph",
+  },
+  Content: &ast.PropertyGraphContent{
+    NodeTables: &ast.PropertyGraphNodeTables{
+      Node:   47,
+      Tables: &ast.PropertyGraphElementList{
+        Lparen:   59,
+        Rparen:   87,
+        Elements: []*ast.PropertyGraphElement{
+          &ast.PropertyGraphElement{
+            Name: &ast.Ident{
+              NamePos: 65,
+              NameEnd: 72,
+              Name:    "Account",
+            },
+          },
+          &ast.PropertyGraphElement{
+            Name: &ast.Ident{
+              NamePos: 78,
+              NameEnd: 84,
+              Name:    "Person",
+            },
+          },
+        },
+      },
+    },
+    EdgeTables: &ast.PropertyGraphEdgeTables{
+      Edge:   91,
+      Tables: &ast.PropertyGraphElementList{
+        Lparen:   103,
+        Rparen:   399,
+        Elements: []*ast.PropertyGraphElement{
+          &ast.PropertyGraphElement{
+            Name: &ast.Ident{
+              NamePos: 109,
+              NameEnd: 125,
+              Name:    "PersonOwnAccount",
+            },
+            Keys: &ast.PropertyGraphEdgeElementKeys{
+              Source: &ast.PropertyGraphSourceKey{
+                Source: 132,
+                Keys:   &ast.PropertyGraphColumnNameList{
+                  Lparen:         143,
+                  Rparen:         146,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 144,
+                      NameEnd: 146,
+                      Name:    "id",
+                    },
+                  },
+                },
+                ElementReference: &ast.Ident{
+                  NamePos: 159,
+                  NameEnd: 165,
+                  Name:    "Person",
+                },
+                ReferenceColumns: &ast.PropertyGraphColumnNameList{
+                  Lparen:         166,
+                  Rparen:         169,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 167,
+                      NameEnd: 169,
+                      Name:    "id",
+                    },
+                  },
+                },
+              },
+              Destination: &ast.PropertyGraphDestinationKey{
+                Destination: 177,
+                Keys:        &ast.PropertyGraphColumnNameList{
+                  Lparen:         193,
+                  Rparen:         204,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 194,
+                      NameEnd: 204,
+                      Name:    "account_id",
+                    },
+                  },
+                },
+                ElementReference: &ast.Ident{
+                  NamePos: 217,
+                  NameEnd: 224,
+                  Name:    "Account",
+                },
+                ReferenceColumns: &ast.PropertyGraphColumnNameList{
+                  Lparen:         225,
+                  Rparen:         228,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 226,
+                      NameEnd: 228,
+                      Name:    "id",
+                    },
+                  },
+                },
+              },
+            },
+            Properties: &ast.PropertyGraphLabelAndPropertiesList{
+              LabelAndProperties: []*ast.PropertyGraphLabelAndProperties{
+                &ast.PropertyGraphLabelAndProperties{
+                  Label: &ast.PropertyGraphElementLabelLabelName{
+                    Label: 236,
+                    Name:  &ast.Ident{
+                      NamePos: 242,
+                      NameEnd: 246,
+                      Name:    "Owns",
+                    },
+                  },
+                },
+              },
+            },
+          },
+          &ast.PropertyGraphElement{
+            Name: &ast.Ident{
+              NamePos: 252,
+              NameEnd: 274,
+              Name:    "AccountTransferAccount",
+            },
+            Keys: &ast.PropertyGraphEdgeElementKeys{
+              Source: &ast.PropertyGraphSourceKey{
+                Source: 281,
+                Keys:   &ast.PropertyGraphColumnNameList{
+                  Lparen:         292,
+                  Rparen:         295,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 293,
+                      NameEnd: 295,
+                      Name:    "id",
+                    },
+                  },
+                },
+                ElementReference: &ast.Ident{
+                  NamePos: 308,
+                  NameEnd: 315,
+                  Name:    "Account",
+                },
+                ReferenceColumns: &ast.PropertyGraphColumnNameList{
+                  Lparen:         316,
+                  Rparen:         319,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 317,
+                      NameEnd: 319,
+                      Name:    "id",
+                    },
+                  },
+                },
+              },
+              Destination: &ast.PropertyGraphDestinationKey{
+                Destination: 327,
+                Keys:        &ast.PropertyGraphColumnNameList{
+                  Lparen:         343,
+                  Rparen:         349,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 344,
+                      NameEnd: 349,
+                      Name:    "to_id",
+                    },
+                  },
+                },
+                ElementReference: &ast.Ident{
+                  NamePos: 362,
+                  NameEnd: 369,
+                  Name:    "Account",
+                },
+                ReferenceColumns: &ast.PropertyGraphColumnNameList{
+                  Lparen:         370,
+                  Rparen:         373,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 371,
+                      NameEnd: 373,
+                      Name:    "id",
+                    },
+                  },
+                },
+              },
+            },
+            Properties: &ast.PropertyGraphLabelAndPropertiesList{
+              LabelAndProperties: []*ast.PropertyGraphLabelAndProperties{
+                &ast.PropertyGraphLabelAndProperties{
+                  Label: &ast.PropertyGraphElementLabelLabelName{
+                    Label: 381,
+                    Name:  &ast.Ident{
+                      NamePos: 387,
+                      NameEnd: 396,
+                      Name:    "Transfers",
+                    },
+                  },
+                },
+              },
+            },
+          },
+        },
+      },
+    },
+  },
+}
+
+--- SQL
+CREATE PROPERTY GRAPH IF NOT EXISTS FinGraph NODE TABLES (Account, Person) EDGE TABLES (PersonOwnAccount SOURCE KEY (id) REFERENCES Person (id) DESTINATION KEY (account_id) REFERENCES Account (id) LABEL Owns, AccountTransferAccount SOURCE KEY (id) REFERENCES Account (id) DESTINATION KEY (to_id) REFERENCES Account (id) LABEL Transfers)
diff --git a/testdata/result/ddl/drop_property_graph.sql.txt b/testdata/result/ddl/drop_property_graph.sql.txt
new file mode 100644
index 0000000..1a41bcb
--- /dev/null
+++ b/testdata/result/ddl/drop_property_graph.sql.txt
@@ -0,0 +1,14 @@
+--- drop_property_graph.sql
+DROP PROPERTY GRAPH FinGraph
+
+--- AST
+&ast.DropPropertyGraph{
+  Name: &ast.Ident{
+    NamePos: 20,
+    NameEnd: 28,
+    Name:    "FinGraph",
+  },
+}
+
+--- SQL
+DROP PROPERTY GRAPH FinGraph
diff --git a/testdata/result/ddl/drop_property_graph_if_exists.sql.txt b/testdata/result/ddl/drop_property_graph_if_exists.sql.txt
new file mode 100644
index 0000000..3f0efc3
--- /dev/null
+++ b/testdata/result/ddl/drop_property_graph_if_exists.sql.txt
@@ -0,0 +1,15 @@
+--- drop_property_graph_if_exists.sql
+DROP PROPERTY GRAPH IF EXISTS FinGraph
+
+--- AST
+&ast.DropPropertyGraph{
+  IfExists: true,
+  Name:     &ast.Ident{
+    NamePos: 30,
+    NameEnd: 38,
+    Name:    "FinGraph",
+  },
+}
+
+--- SQL
+DROP PROPERTY GRAPH IF EXISTS FinGraph
diff --git a/testdata/result/dml/!bad_insert.sql.txt b/testdata/result/dml/!bad_insert.sql.txt
index ddcb4be..3f0fc9e 100644
--- a/testdata/result/dml/!bad_insert.sql.txt
+++ b/testdata/result/dml/!bad_insert.sql.txt
@@ -10,10 +10,14 @@ syntax error: testdata/input/dml/!bad_insert.sql:2:1: expected beginning of simp
 
 --- AST
 &ast.Insert{
-  TableName: &ast.Ident{
-    NamePos: 7,
-    NameEnd: 10,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 10,
+        Name:    "foo",
+      },
+    },
   },
   Columns: []*ast.Ident{
     &ast.Ident{
diff --git a/testdata/result/dml/delete _from.sql.txt b/testdata/result/dml/delete _from.sql.txt
index ef9d175..05434d9 100644
--- a/testdata/result/dml/delete _from.sql.txt
+++ b/testdata/result/dml/delete _from.sql.txt
@@ -2,10 +2,14 @@
 delete from foo where foo = 1 and bar = 2
 --- AST
 &ast.Delete{
-  TableName: &ast.Ident{
-    NamePos: 12,
-    NameEnd: 15,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 12,
+        NameEnd: 15,
+        Name:    "foo",
+      },
+    },
   },
   Where: &ast.Where{
     Where: 16,
diff --git a/testdata/result/dml/delete.sql.txt b/testdata/result/dml/delete.sql.txt
index 6c7b6ce..c0d4957 100644
--- a/testdata/result/dml/delete.sql.txt
+++ b/testdata/result/dml/delete.sql.txt
@@ -2,10 +2,14 @@
 delete foo where foo = 1 and bar = 2
 --- AST
 &ast.Delete{
-  TableName: &ast.Ident{
-    NamePos: 7,
-    NameEnd: 10,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 10,
+        Name:    "foo",
+      },
+    },
   },
   Where: &ast.Where{
     Where: 11,
diff --git a/testdata/result/dml/delete_as.sql.txt b/testdata/result/dml/delete_as.sql.txt
index f0758fd..b4721a9 100644
--- a/testdata/result/dml/delete_as.sql.txt
+++ b/testdata/result/dml/delete_as.sql.txt
@@ -2,10 +2,14 @@
 delete foo as F where F.foo = 1
 --- AST
 &ast.Delete{
-  TableName: &ast.Ident{
-    NamePos: 7,
-    NameEnd: 10,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 10,
+        Name:    "foo",
+      },
+    },
   },
   As: &ast.AsAlias{
     As:    11,
diff --git a/testdata/result/dml/delete_fqn.sql.txt b/testdata/result/dml/delete_fqn.sql.txt
new file mode 100644
index 0000000..7284c6e
--- /dev/null
+++ b/testdata/result/dml/delete_fqn.sql.txt
@@ -0,0 +1,56 @@
+--- delete_fqn.sql
+delete sch1.foo where foo = 1 and bar = 2
+--- AST
+&ast.Delete{
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 11,
+        Name:    "sch1",
+      },
+      &ast.Ident{
+        NamePos: 12,
+        NameEnd: 15,
+        Name:    "foo",
+      },
+    },
+  },
+  Where: &ast.Where{
+    Where: 16,
+    Expr:  &ast.BinaryExpr{
+      Op:   "AND",
+      Left: &ast.BinaryExpr{
+        Op:   "=",
+        Left: &ast.Ident{
+          NamePos: 22,
+          NameEnd: 25,
+          Name:    "foo",
+        },
+        Right: &ast.IntLiteral{
+          ValuePos: 28,
+          ValueEnd: 29,
+          Base:     10,
+          Value:    "1",
+        },
+      },
+      Right: &ast.BinaryExpr{
+        Op:   "=",
+        Left: &ast.Ident{
+          NamePos: 34,
+          NameEnd: 37,
+          Name:    "bar",
+        },
+        Right: &ast.IntLiteral{
+          ValuePos: 40,
+          ValueEnd: 41,
+          Base:     10,
+          Value:    "2",
+        },
+      },
+    },
+  },
+}
+
+--- SQL
+DELETE FROM sch1.foo WHERE foo = 1 AND bar = 2
diff --git a/testdata/result/dml/delete_then_return.sql.txt b/testdata/result/dml/delete_then_return.sql.txt
index 6c160a1..596a4a4 100644
--- a/testdata/result/dml/delete_then_return.sql.txt
+++ b/testdata/result/dml/delete_then_return.sql.txt
@@ -2,10 +2,14 @@
 delete foo where foo = 1 and bar = 2 then return *
 --- AST
 &ast.Delete{
-  TableName: &ast.Ident{
-    NamePos: 7,
-    NameEnd: 10,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 10,
+        Name:    "foo",
+      },
+    },
   },
   Where: &ast.Where{
     Where: 11,
diff --git a/testdata/result/dml/insert_cte_select.sql.txt b/testdata/result/dml/insert_cte_select.sql.txt
index 13e64ec..b98e6b8 100644
--- a/testdata/result/dml/insert_cte_select.sql.txt
+++ b/testdata/result/dml/insert_cte_select.sql.txt
@@ -4,10 +4,14 @@ with cte AS (select 1 as foo, 2 as bar)
 select *
 --- AST
 &ast.Insert{
-  TableName: &ast.Ident{
-    NamePos: 7,
-    NameEnd: 10,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 10,
+        Name:    "foo",
+      },
+    },
   },
   Columns: []*ast.Ident{
     &ast.Ident{
diff --git a/testdata/result/dml/insert_fqn_values.sql.txt b/testdata/result/dml/insert_fqn_values.sql.txt
new file mode 100644
index 0000000..7cb2f58
--- /dev/null
+++ b/testdata/result/dml/insert_fqn_values.sql.txt
@@ -0,0 +1,112 @@
+--- insert_fqn_values.sql
+insert sch1.foo (foo, bar, baz)
+values (1, 2, 3),
+       (4, 5, 6)
+--- AST
+&ast.Insert{
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 11,
+        Name:    "sch1",
+      },
+      &ast.Ident{
+        NamePos: 12,
+        NameEnd: 15,
+        Name:    "foo",
+      },
+    },
+  },
+  Columns: []*ast.Ident{
+    &ast.Ident{
+      NamePos: 17,
+      NameEnd: 20,
+      Name:    "foo",
+    },
+    &ast.Ident{
+      NamePos: 22,
+      NameEnd: 25,
+      Name:    "bar",
+    },
+    &ast.Ident{
+      NamePos: 27,
+      NameEnd: 30,
+      Name:    "baz",
+    },
+  },
+  Input: &ast.ValuesInput{
+    Values: 32,
+    Rows:   []*ast.ValuesRow{
+      &ast.ValuesRow{
+        Lparen: 39,
+        Rparen: 47,
+        Exprs:  []*ast.DefaultExpr{
+          &ast.DefaultExpr{
+            DefaultPos: -1,
+            Expr:       &ast.IntLiteral{
+              ValuePos: 40,
+              ValueEnd: 41,
+              Base:     10,
+              Value:    "1",
+            },
+          },
+          &ast.DefaultExpr{
+            DefaultPos: -1,
+            Expr:       &ast.IntLiteral{
+              ValuePos: 43,
+              ValueEnd: 44,
+              Base:     10,
+              Value:    "2",
+            },
+          },
+          &ast.DefaultExpr{
+            DefaultPos: -1,
+            Expr:       &ast.IntLiteral{
+              ValuePos: 46,
+              ValueEnd: 47,
+              Base:     10,
+              Value:    "3",
+            },
+          },
+        },
+      },
+      &ast.ValuesRow{
+        Lparen: 57,
+        Rparen: 65,
+        Exprs:  []*ast.DefaultExpr{
+          &ast.DefaultExpr{
+            DefaultPos: -1,
+            Expr:       &ast.IntLiteral{
+              ValuePos: 58,
+              ValueEnd: 59,
+              Base:     10,
+              Value:    "4",
+            },
+          },
+          &ast.DefaultExpr{
+            DefaultPos: -1,
+            Expr:       &ast.IntLiteral{
+              ValuePos: 61,
+              ValueEnd: 62,
+              Base:     10,
+              Value:    "5",
+            },
+          },
+          &ast.DefaultExpr{
+            DefaultPos: -1,
+            Expr:       &ast.IntLiteral{
+              ValuePos: 64,
+              ValueEnd: 65,
+              Base:     10,
+              Value:    "6",
+            },
+          },
+        },
+      },
+    },
+  },
+}
+
+--- SQL
+INSERT INTO sch1.foo (foo, bar, baz) VALUES (1, 2, 3), (4, 5, 6)
diff --git a/testdata/result/dml/insert_into_values.sql.txt b/testdata/result/dml/insert_into_values.sql.txt
index 670a2c4..ed4f06d 100644
--- a/testdata/result/dml/insert_into_values.sql.txt
+++ b/testdata/result/dml/insert_into_values.sql.txt
@@ -4,10 +4,14 @@ values (1, 2, 3),
        (4, 5, 6)
 --- AST
 &ast.Insert{
-  TableName: &ast.Ident{
-    NamePos: 12,
-    NameEnd: 15,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 12,
+        NameEnd: 15,
+        Name:    "foo",
+      },
+    },
   },
   Columns: []*ast.Ident{
     &ast.Ident{
diff --git a/testdata/result/dml/insert_or_ignore.sql.txt b/testdata/result/dml/insert_or_ignore.sql.txt
index 43d4b19..e1a34b3 100644
--- a/testdata/result/dml/insert_or_ignore.sql.txt
+++ b/testdata/result/dml/insert_or_ignore.sql.txt
@@ -4,10 +4,14 @@ INSERT OR IGNORE INTO foo
 --- AST
 &ast.Insert{
   InsertOrType: "IGNORE",
-  TableName:    &ast.Ident{
-    NamePos: 22,
-    NameEnd: 25,
-    Name:    "foo",
+  TableName:    &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 22,
+        NameEnd: 25,
+        Name:    "foo",
+      },
+    },
   },
   Columns: []*ast.Ident{
     &ast.Ident{
diff --git a/testdata/result/dml/insert_or_update.sql.txt b/testdata/result/dml/insert_or_update.sql.txt
index 4ecc3ac..9f8837b 100644
--- a/testdata/result/dml/insert_or_update.sql.txt
+++ b/testdata/result/dml/insert_or_update.sql.txt
@@ -4,10 +4,14 @@ INSERT OR UPDATE INTO foo
 --- AST
 &ast.Insert{
   InsertOrType: "UPDATE",
-  TableName:    &ast.Ident{
-    NamePos: 22,
-    NameEnd: 25,
-    Name:    "foo",
+  TableName:    &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 22,
+        NameEnd: 25,
+        Name:    "foo",
+      },
+    },
   },
   Columns: []*ast.Ident{
     &ast.Ident{
diff --git a/testdata/result/dml/insert_or_update_then_return_with_action_as.sql.txt b/testdata/result/dml/insert_or_update_then_return_with_action_as.sql.txt
index 665e0e2..0c4b430 100644
--- a/testdata/result/dml/insert_or_update_then_return_with_action_as.sql.txt
+++ b/testdata/result/dml/insert_or_update_then_return_with_action_as.sql.txt
@@ -5,10 +5,14 @@ THEN RETURN WITH ACTION AS act *
 --- AST
 &ast.Insert{
   InsertOrType: "UPDATE",
-  TableName:    &ast.Ident{
-    NamePos: 22,
-    NameEnd: 25,
-    Name:    "foo",
+  TableName:    &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 22,
+        NameEnd: 25,
+        Name:    "foo",
+      },
+    },
   },
   Columns: []*ast.Ident{
     &ast.Ident{
diff --git a/testdata/result/dml/insert_select.sql.txt b/testdata/result/dml/insert_select.sql.txt
index 2b2e713..efe0b91 100644
--- a/testdata/result/dml/insert_select.sql.txt
+++ b/testdata/result/dml/insert_select.sql.txt
@@ -3,10 +3,14 @@ insert foo (foo, bar)
 select * from unnest([(1, 2), (3, 4)])
 --- AST
 &ast.Insert{
-  TableName: &ast.Ident{
-    NamePos: 7,
-    NameEnd: 10,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 10,
+        Name:    "foo",
+      },
+    },
   },
   Columns: []*ast.Ident{
     &ast.Ident{
diff --git a/testdata/result/dml/insert_values.sql.txt b/testdata/result/dml/insert_values.sql.txt
index e791c60..c5dc078 100644
--- a/testdata/result/dml/insert_values.sql.txt
+++ b/testdata/result/dml/insert_values.sql.txt
@@ -4,10 +4,14 @@ values (1, 2, 3),
        (4, 5, 6)
 --- AST
 &ast.Insert{
-  TableName: &ast.Ident{
-    NamePos: 7,
-    NameEnd: 10,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 10,
+        Name:    "foo",
+      },
+    },
   },
   Columns: []*ast.Ident{
     &ast.Ident{
diff --git a/testdata/result/dml/insert_values_default.sql.txt b/testdata/result/dml/insert_values_default.sql.txt
index b379cda..1f5b31d 100644
--- a/testdata/result/dml/insert_values_default.sql.txt
+++ b/testdata/result/dml/insert_values_default.sql.txt
@@ -3,10 +3,14 @@ insert foo (foo, bar)
 values (1, default)
 --- AST
 &ast.Insert{
-  TableName: &ast.Ident{
-    NamePos: 7,
-    NameEnd: 10,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 10,
+        Name:    "foo",
+      },
+    },
   },
   Columns: []*ast.Ident{
     &ast.Ident{
diff --git a/testdata/result/dml/insert_with_sequence_function.sql.txt b/testdata/result/dml/insert_with_sequence_function.sql.txt
index dc8fe0d..0e61665 100644
--- a/testdata/result/dml/insert_with_sequence_function.sql.txt
+++ b/testdata/result/dml/insert_with_sequence_function.sql.txt
@@ -3,10 +3,14 @@ INSERT INTO foo(bar) VALUES (GET_NEXT_SEQUENCE_VALUE(SEQUENCE my_sequence))
 
 --- AST
 &ast.Insert{
-  TableName: &ast.Ident{
-    NamePos: 12,
-    NameEnd: 15,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 12,
+        NameEnd: 15,
+        Name:    "foo",
+      },
+    },
   },
   Columns: []*ast.Ident{
     &ast.Ident{
diff --git a/testdata/result/dml/update.sql.txt b/testdata/result/dml/update.sql.txt
index e9e63cb..32a3ca4 100644
--- a/testdata/result/dml/update.sql.txt
+++ b/testdata/result/dml/update.sql.txt
@@ -2,10 +2,14 @@
 update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1
 --- AST
 &ast.Update{
-  TableName: &ast.Ident{
-    NamePos: 7,
-    NameEnd: 10,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 10,
+        Name:    "foo",
+      },
+    },
   },
   Updates: []*ast.UpdateItem{
     &ast.UpdateItem{
diff --git a/testdata/result/dml/update_as.sql.txt b/testdata/result/dml/update_as.sql.txt
index 002e921..b7aad4d 100644
--- a/testdata/result/dml/update_as.sql.txt
+++ b/testdata/result/dml/update_as.sql.txt
@@ -2,10 +2,14 @@
 update foo as F set F.foo = F.bar + 1 where foo = F.bar
 --- AST
 &ast.Update{
-  TableName: &ast.Ident{
-    NamePos: 7,
-    NameEnd: 10,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 10,
+        Name:    "foo",
+      },
+    },
   },
   As: &ast.AsAlias{
     As:    11,
diff --git a/testdata/result/dml/update_fqn.sql.txt b/testdata/result/dml/update_fqn.sql.txt
new file mode 100644
index 0000000..9f33753
--- /dev/null
+++ b/testdata/result/dml/update_fqn.sql.txt
@@ -0,0 +1,88 @@
+--- update_fqn.sql
+update sch1.foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1
+--- AST
+&ast.Update{
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 11,
+        Name:    "sch1",
+      },
+      &ast.Ident{
+        NamePos: 12,
+        NameEnd: 15,
+        Name:    "foo",
+      },
+    },
+  },
+  Updates: []*ast.UpdateItem{
+    &ast.UpdateItem{
+      Path: []*ast.Ident{
+        &ast.Ident{
+          NamePos: 20,
+          NameEnd: 23,
+          Name:    "foo",
+        },
+      },
+      DefaultExpr: &ast.DefaultExpr{
+        DefaultPos: -1,
+        Expr:       &ast.Ident{
+          NamePos: 26,
+          NameEnd: 29,
+          Name:    "bar",
+        },
+      },
+    },
+    &ast.UpdateItem{
+      Path: []*ast.Ident{
+        &ast.Ident{
+          NamePos: 31,
+          NameEnd: 34,
+          Name:    "bar",
+        },
+      },
+      DefaultExpr: &ast.DefaultExpr{
+        DefaultPos: -1,
+        Expr:       &ast.Ident{
+          NamePos: 37,
+          NameEnd: 40,
+          Name:    "foo",
+        },
+      },
+    },
+    &ast.UpdateItem{
+      Path: []*ast.Ident{
+        &ast.Ident{
+          NamePos: 42,
+          NameEnd: 45,
+          Name:    "baz",
+        },
+      },
+      DefaultExpr: &ast.DefaultExpr{
+        DefaultPos: 48,
+        Default:    true,
+      },
+    },
+  },
+  Where: &ast.Where{
+    Where: 56,
+    Expr:  &ast.BinaryExpr{
+      Op:   "=",
+      Left: &ast.Ident{
+        NamePos: 62,
+        NameEnd: 65,
+        Name:    "foo",
+      },
+      Right: &ast.IntLiteral{
+        ValuePos: 68,
+        ValueEnd: 69,
+        Base:     10,
+        Value:    "1",
+      },
+    },
+  },
+}
+
+--- SQL
+UPDATE sch1.foo SET foo = bar, bar = foo, baz = DEFAULT WHERE foo = 1
diff --git a/testdata/result/dml/update_then_return_with_action.sql.txt b/testdata/result/dml/update_then_return_with_action.sql.txt
index bfaba0e..7990f30 100644
--- a/testdata/result/dml/update_then_return_with_action.sql.txt
+++ b/testdata/result/dml/update_then_return_with_action.sql.txt
@@ -2,10 +2,14 @@
 update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 then return with action *
 --- AST
 &ast.Update{
-  TableName: &ast.Ident{
-    NamePos: 7,
-    NameEnd: 10,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 10,
+        Name:    "foo",
+      },
+    },
   },
   Updates: []*ast.UpdateItem{
     &ast.UpdateItem{
diff --git a/testdata/result/dml/update_with_safe_ml_predict.sql.txt b/testdata/result/dml/update_with_safe_ml_predict.sql.txt
index 60bd0d1..0a67f8d 100644
--- a/testdata/result/dml/update_with_safe_ml_predict.sql.txt
+++ b/testdata/result/dml/update_with_safe_ml_predict.sql.txt
@@ -14,10 +14,14 @@ WHERE products.desc_embed IS NULL
 --- AST
 &ast.Update{
   Update:    76,
-  TableName: &ast.Ident{
-    NamePos: 83,
-    NameEnd: 91,
-    Name:    "products",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 83,
+        NameEnd: 91,
+        Name:    "products",
+      },
+    },
   },
   Updates: []*ast.UpdateItem{
     &ast.UpdateItem{
diff --git a/testdata/result/statement/!bad_insert.sql.txt b/testdata/result/statement/!bad_insert.sql.txt
index ddcb4be..3f0fc9e 100644
--- a/testdata/result/statement/!bad_insert.sql.txt
+++ b/testdata/result/statement/!bad_insert.sql.txt
@@ -10,10 +10,14 @@ syntax error: testdata/input/dml/!bad_insert.sql:2:1: expected beginning of simp
 
 --- AST
 &ast.Insert{
-  TableName: &ast.Ident{
-    NamePos: 7,
-    NameEnd: 10,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 10,
+        Name:    "foo",
+      },
+    },
   },
   Columns: []*ast.Ident{
     &ast.Ident{
diff --git a/testdata/result/statement/create_or_replace_property_graph_fingraph_verbose.sql.txt b/testdata/result/statement/create_or_replace_property_graph_fingraph_verbose.sql.txt
new file mode 100644
index 0000000..4094102
--- /dev/null
+++ b/testdata/result/statement/create_or_replace_property_graph_fingraph_verbose.sql.txt
@@ -0,0 +1,341 @@
+--- create_or_replace_property_graph_fingraph_verbose.sql
+CREATE OR REPLACE PROPERTY GRAPH FinGraph
+  NODE TABLES (
+    Account AS Account -- element_alias
+      KEY (id) -- element_key in node_element_key in element_keys
+      -- label_and_property_list
+      LABEL DetailedAccount -- LABEL label_name in element_label
+        PROPERTIES (create_time, is_blocked, nick_name AS name) -- derived_property_list
+      DEFAULT LABEL -- DEFAULT LABEL in element_label
+        NO PROPERTIES -- NO PROPERTIES in element_properties
+    ,
+    Person
+      -- no element_keys
+      -- no element_label because of direct element_properties
+      PROPERTIES ARE ALL COLUMNS EXCEPT (city) -- properties_are
+  )
+  EDGE TABLES (
+    PersonOwnAccount AS PersonOwnAccount
+      KEY (id, account_id)
+      SOURCE KEY (id) REFERENCES Person -- source_key without column_name_list
+      DESTINATION KEY (account_id) REFERENCES Account -- destination_key without column_name_list
+      LABEL Owns
+        PROPERTIES ALL COLUMNS,
+    AccountTransferAccount
+      SOURCE KEY (id) REFERENCES Account (id) -- source_key
+      DESTINATION KEY (to_id) REFERENCES Account (id) -- destination_key
+      LABEL Transfers -- LABEL label_name in element_label
+      -- without element_properties
+  )
+--- AST
+&ast.CreatePropertyGraph{
+  OrReplace: true,
+  Name:      &ast.Ident{
+    NamePos: 33,
+    NameEnd: 41,
+    Name:    "FinGraph",
+  },
+  Content: &ast.PropertyGraphContent{
+    NodeTables: &ast.PropertyGraphNodeTables{
+      Node:   44,
+      Tables: &ast.PropertyGraphElementList{
+        Lparen:   56,
+        Rparen:   638,
+        Elements: []*ast.PropertyGraphElement{
+          &ast.PropertyGraphElement{
+            Name: &ast.Ident{
+              NamePos: 62,
+              NameEnd: 69,
+              Name:    "Account",
+            },
+            Alias: &ast.Ident{
+              NamePos: 73,
+              NameEnd: 80,
+              Name:    "Account",
+            },
+            Keys: &ast.PropertyGraphNodeElementKey{
+              Key: &ast.PropertyGraphElementKey{
+                Key:  104,
+                Keys: &ast.PropertyGraphColumnNameList{
+                  Lparen:         108,
+                  Rparen:         111,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 109,
+                      NameEnd: 111,
+                      Name:    "id",
+                    },
+                  },
+                },
+              },
+            },
+            Properties: &ast.PropertyGraphLabelAndPropertiesList{
+              LabelAndProperties: []*ast.PropertyGraphLabelAndProperties{
+                &ast.PropertyGraphLabelAndProperties{
+                  Label: &ast.PropertyGraphElementLabelLabelName{
+                    Label: 203,
+                    Name:  &ast.Ident{
+                      NamePos: 209,
+                      NameEnd: 224,
+                      Name:    "DetailedAccount",
+                    },
+                  },
+                  Properties: &ast.PropertyGraphDerivedPropertyList{
+                    Properties:        270,
+                    Rparen:            324,
+                    DerivedProperties: []*ast.PropertyGraphDerivedProperty{
+                      &ast.PropertyGraphDerivedProperty{
+                        Expr: &ast.Ident{
+                          NamePos: 282,
+                          NameEnd: 293,
+                          Name:    "create_time",
+                        },
+                      },
+                      &ast.PropertyGraphDerivedProperty{
+                        Expr: &ast.Ident{
+                          NamePos: 295,
+                          NameEnd: 305,
+                          Name:    "is_blocked",
+                        },
+                      },
+                      &ast.PropertyGraphDerivedProperty{
+                        Expr: &ast.Ident{
+                          NamePos: 307,
+                          NameEnd: 316,
+                          Name:    "nick_name",
+                        },
+                        Alias: &ast.Ident{
+                          NamePos: 320,
+                          NameEnd: 324,
+                          Name:    "name",
+                        },
+                      },
+                    },
+                  },
+                },
+                &ast.PropertyGraphLabelAndProperties{
+                  Label: &ast.PropertyGraphElementLabelDefaultLabel{
+                    Default: 357,
+                    Label:   365,
+                  },
+                  Properties: &ast.PropertyGraphNoProperties{
+                    No:         413,
+                    Properties: 416,
+                  },
+                },
+              },
+            },
+          },
+          &ast.PropertyGraphElement{
+            Name: &ast.Ident{
+              NamePos: 476,
+              NameEnd: 482,
+              Name:    "Person",
+            },
+            Properties: &ast.PropertyGraphSingleProperties{
+              Properties: &ast.PropertyGraphPropertiesAre{
+                Properties:    577,
+                Columns:       596,
+                ExceptColumns: &ast.PropertyGraphColumnNameList{
+                  Lparen:         611,
+                  Rparen:         616,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 612,
+                      NameEnd: 616,
+                      Name:    "city",
+                    },
+                  },
+                },
+              },
+            },
+          },
+        },
+      },
+    },
+    EdgeTables: &ast.PropertyGraphEdgeTables{
+      Edge:   642,
+      Tables: &ast.PropertyGraphElementList{
+        Lparen:   654,
+        Rparen:   1207,
+        Elements: []*ast.PropertyGraphElement{
+          &ast.PropertyGraphElement{
+            Name: &ast.Ident{
+              NamePos: 660,
+              NameEnd: 676,
+              Name:    "PersonOwnAccount",
+            },
+            Alias: &ast.Ident{
+              NamePos: 680,
+              NameEnd: 696,
+              Name:    "PersonOwnAccount",
+            },
+            Keys: &ast.PropertyGraphEdgeElementKeys{
+              Element: &ast.PropertyGraphElementKey{
+                Key:  703,
+                Keys: &ast.PropertyGraphColumnNameList{
+                  Lparen:         707,
+                  Rparen:         722,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 708,
+                      NameEnd: 710,
+                      Name:    "id",
+                    },
+                    &ast.Ident{
+                      NamePos: 712,
+                      NameEnd: 722,
+                      Name:    "account_id",
+                    },
+                  },
+                },
+              },
+              Source: &ast.PropertyGraphSourceKey{
+                Source: 730,
+                Keys:   &ast.PropertyGraphColumnNameList{
+                  Lparen:         741,
+                  Rparen:         744,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 742,
+                      NameEnd: 744,
+                      Name:    "id",
+                    },
+                  },
+                },
+                ElementReference: &ast.Ident{
+                  NamePos: 757,
+                  NameEnd: 763,
+                  Name:    "Person",
+                },
+              },
+              Destination: &ast.PropertyGraphDestinationKey{
+                Destination: 809,
+                Keys:        &ast.PropertyGraphColumnNameList{
+                  Lparen:         825,
+                  Rparen:         836,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 826,
+                      NameEnd: 836,
+                      Name:    "account_id",
+                    },
+                  },
+                },
+                ElementReference: &ast.Ident{
+                  NamePos: 849,
+                  NameEnd: 856,
+                  Name:    "Account",
+                },
+              },
+            },
+            Properties: &ast.PropertyGraphLabelAndPropertiesList{
+              LabelAndProperties: []*ast.PropertyGraphLabelAndProperties{
+                &ast.PropertyGraphLabelAndProperties{
+                  Label: &ast.PropertyGraphElementLabelLabelName{
+                    Label: 907,
+                    Name:  &ast.Ident{
+                      NamePos: 913,
+                      NameEnd: 917,
+                      Name:    "Owns",
+                    },
+                  },
+                  Properties: &ast.PropertyGraphPropertiesAre{
+                    Properties: 926,
+                    Columns:    941,
+                  },
+                },
+              },
+            },
+          },
+          &ast.PropertyGraphElement{
+            Name: &ast.Ident{
+              NamePos: 954,
+              NameEnd: 976,
+              Name:    "AccountTransferAccount",
+            },
+            Keys: &ast.PropertyGraphEdgeElementKeys{
+              Source: &ast.PropertyGraphSourceKey{
+                Source: 983,
+                Keys:   &ast.PropertyGraphColumnNameList{
+                  Lparen:         994,
+                  Rparen:         997,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 995,
+                      NameEnd: 997,
+                      Name:    "id",
+                    },
+                  },
+                },
+                ElementReference: &ast.Ident{
+                  NamePos: 1010,
+                  NameEnd: 1017,
+                  Name:    "Account",
+                },
+                ReferenceColumns: &ast.PropertyGraphColumnNameList{
+                  Lparen:         1018,
+                  Rparen:         1021,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 1019,
+                      NameEnd: 1021,
+                      Name:    "id",
+                    },
+                  },
+                },
+              },
+              Destination: &ast.PropertyGraphDestinationKey{
+                Destination: 1043,
+                Keys:        &ast.PropertyGraphColumnNameList{
+                  Lparen:         1059,
+                  Rparen:         1065,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 1060,
+                      NameEnd: 1065,
+                      Name:    "to_id",
+                    },
+                  },
+                },
+                ElementReference: &ast.Ident{
+                  NamePos: 1078,
+                  NameEnd: 1085,
+                  Name:    "Account",
+                },
+                ReferenceColumns: &ast.PropertyGraphColumnNameList{
+                  Lparen:         1086,
+                  Rparen:         1089,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 1087,
+                      NameEnd: 1089,
+                      Name:    "id",
+                    },
+                  },
+                },
+              },
+            },
+            Properties: &ast.PropertyGraphLabelAndPropertiesList{
+              LabelAndProperties: []*ast.PropertyGraphLabelAndProperties{
+                &ast.PropertyGraphLabelAndProperties{
+                  Label: &ast.PropertyGraphElementLabelLabelName{
+                    Label: 1116,
+                    Name:  &ast.Ident{
+                      NamePos: 1122,
+                      NameEnd: 1131,
+                      Name:    "Transfers",
+                    },
+                  },
+                },
+              },
+            },
+          },
+        },
+      },
+    },
+  },
+}
+
+--- SQL
+CREATE OR REPLACE PROPERTY GRAPH FinGraph NODE TABLES (Account AS Account KEY (id) LABEL DetailedAccount PROPERTIES (create_time, is_blocked, nick_name AS name) DEFAULT LABEL NO PROPERTIES, Person PROPERTIES ARE ALL COLUMNS EXCEPT (city)) EDGE TABLES (PersonOwnAccount AS PersonOwnAccount KEY (id, account_id) SOURCE KEY (id) REFERENCES Person DESTINATION KEY (account_id) REFERENCES Account LABEL Owns PROPERTIES ARE ALL COLUMNS, AccountTransferAccount SOURCE KEY (id) REFERENCES Account (id) DESTINATION KEY (to_id) REFERENCES Account (id) LABEL Transfers)
diff --git a/testdata/result/statement/create_property_graph_if_not_exists_fingraph.sql.txt b/testdata/result/statement/create_property_graph_if_not_exists_fingraph.sql.txt
new file mode 100644
index 0000000..a0869e1
--- /dev/null
+++ b/testdata/result/statement/create_property_graph_if_not_exists_fingraph.sql.txt
@@ -0,0 +1,228 @@
+--- create_property_graph_if_not_exists_fingraph.sql
+CREATE PROPERTY GRAPH IF NOT EXISTS FinGraph
+  NODE TABLES (
+    Account,
+    Person
+  )
+  EDGE TABLES (
+    PersonOwnAccount
+      SOURCE KEY (id) REFERENCES Person (id)
+      DESTINATION KEY (account_id) REFERENCES Account (id)
+      LABEL Owns,
+    AccountTransferAccount
+      SOURCE KEY (id) REFERENCES Account (id)
+      DESTINATION KEY (to_id) REFERENCES Account (id)
+      LABEL Transfers
+  )
+--- AST
+&ast.CreatePropertyGraph{
+  IfNotExists: true,
+  Name:        &ast.Ident{
+    NamePos: 36,
+    NameEnd: 44,
+    Name:    "FinGraph",
+  },
+  Content: &ast.PropertyGraphContent{
+    NodeTables: &ast.PropertyGraphNodeTables{
+      Node:   47,
+      Tables: &ast.PropertyGraphElementList{
+        Lparen:   59,
+        Rparen:   87,
+        Elements: []*ast.PropertyGraphElement{
+          &ast.PropertyGraphElement{
+            Name: &ast.Ident{
+              NamePos: 65,
+              NameEnd: 72,
+              Name:    "Account",
+            },
+          },
+          &ast.PropertyGraphElement{
+            Name: &ast.Ident{
+              NamePos: 78,
+              NameEnd: 84,
+              Name:    "Person",
+            },
+          },
+        },
+      },
+    },
+    EdgeTables: &ast.PropertyGraphEdgeTables{
+      Edge:   91,
+      Tables: &ast.PropertyGraphElementList{
+        Lparen:   103,
+        Rparen:   399,
+        Elements: []*ast.PropertyGraphElement{
+          &ast.PropertyGraphElement{
+            Name: &ast.Ident{
+              NamePos: 109,
+              NameEnd: 125,
+              Name:    "PersonOwnAccount",
+            },
+            Keys: &ast.PropertyGraphEdgeElementKeys{
+              Source: &ast.PropertyGraphSourceKey{
+                Source: 132,
+                Keys:   &ast.PropertyGraphColumnNameList{
+                  Lparen:         143,
+                  Rparen:         146,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 144,
+                      NameEnd: 146,
+                      Name:    "id",
+                    },
+                  },
+                },
+                ElementReference: &ast.Ident{
+                  NamePos: 159,
+                  NameEnd: 165,
+                  Name:    "Person",
+                },
+                ReferenceColumns: &ast.PropertyGraphColumnNameList{
+                  Lparen:         166,
+                  Rparen:         169,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 167,
+                      NameEnd: 169,
+                      Name:    "id",
+                    },
+                  },
+                },
+              },
+              Destination: &ast.PropertyGraphDestinationKey{
+                Destination: 177,
+                Keys:        &ast.PropertyGraphColumnNameList{
+                  Lparen:         193,
+                  Rparen:         204,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 194,
+                      NameEnd: 204,
+                      Name:    "account_id",
+                    },
+                  },
+                },
+                ElementReference: &ast.Ident{
+                  NamePos: 217,
+                  NameEnd: 224,
+                  Name:    "Account",
+                },
+                ReferenceColumns: &ast.PropertyGraphColumnNameList{
+                  Lparen:         225,
+                  Rparen:         228,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 226,
+                      NameEnd: 228,
+                      Name:    "id",
+                    },
+                  },
+                },
+              },
+            },
+            Properties: &ast.PropertyGraphLabelAndPropertiesList{
+              LabelAndProperties: []*ast.PropertyGraphLabelAndProperties{
+                &ast.PropertyGraphLabelAndProperties{
+                  Label: &ast.PropertyGraphElementLabelLabelName{
+                    Label: 236,
+                    Name:  &ast.Ident{
+                      NamePos: 242,
+                      NameEnd: 246,
+                      Name:    "Owns",
+                    },
+                  },
+                },
+              },
+            },
+          },
+          &ast.PropertyGraphElement{
+            Name: &ast.Ident{
+              NamePos: 252,
+              NameEnd: 274,
+              Name:    "AccountTransferAccount",
+            },
+            Keys: &ast.PropertyGraphEdgeElementKeys{
+              Source: &ast.PropertyGraphSourceKey{
+                Source: 281,
+                Keys:   &ast.PropertyGraphColumnNameList{
+                  Lparen:         292,
+                  Rparen:         295,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 293,
+                      NameEnd: 295,
+                      Name:    "id",
+                    },
+                  },
+                },
+                ElementReference: &ast.Ident{
+                  NamePos: 308,
+                  NameEnd: 315,
+                  Name:    "Account",
+                },
+                ReferenceColumns: &ast.PropertyGraphColumnNameList{
+                  Lparen:         316,
+                  Rparen:         319,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 317,
+                      NameEnd: 319,
+                      Name:    "id",
+                    },
+                  },
+                },
+              },
+              Destination: &ast.PropertyGraphDestinationKey{
+                Destination: 327,
+                Keys:        &ast.PropertyGraphColumnNameList{
+                  Lparen:         343,
+                  Rparen:         349,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 344,
+                      NameEnd: 349,
+                      Name:    "to_id",
+                    },
+                  },
+                },
+                ElementReference: &ast.Ident{
+                  NamePos: 362,
+                  NameEnd: 369,
+                  Name:    "Account",
+                },
+                ReferenceColumns: &ast.PropertyGraphColumnNameList{
+                  Lparen:         370,
+                  Rparen:         373,
+                  ColumnNameList: []*ast.Ident{
+                    &ast.Ident{
+                      NamePos: 371,
+                      NameEnd: 373,
+                      Name:    "id",
+                    },
+                  },
+                },
+              },
+            },
+            Properties: &ast.PropertyGraphLabelAndPropertiesList{
+              LabelAndProperties: []*ast.PropertyGraphLabelAndProperties{
+                &ast.PropertyGraphLabelAndProperties{
+                  Label: &ast.PropertyGraphElementLabelLabelName{
+                    Label: 381,
+                    Name:  &ast.Ident{
+                      NamePos: 387,
+                      NameEnd: 396,
+                      Name:    "Transfers",
+                    },
+                  },
+                },
+              },
+            },
+          },
+        },
+      },
+    },
+  },
+}
+
+--- SQL
+CREATE PROPERTY GRAPH IF NOT EXISTS FinGraph NODE TABLES (Account, Person) EDGE TABLES (PersonOwnAccount SOURCE KEY (id) REFERENCES Person (id) DESTINATION KEY (account_id) REFERENCES Account (id) LABEL Owns, AccountTransferAccount SOURCE KEY (id) REFERENCES Account (id) DESTINATION KEY (to_id) REFERENCES Account (id) LABEL Transfers)
diff --git a/testdata/result/statement/delete _from.sql.txt b/testdata/result/statement/delete _from.sql.txt
index ef9d175..05434d9 100644
--- a/testdata/result/statement/delete _from.sql.txt
+++ b/testdata/result/statement/delete _from.sql.txt
@@ -2,10 +2,14 @@
 delete from foo where foo = 1 and bar = 2
 --- AST
 &ast.Delete{
-  TableName: &ast.Ident{
-    NamePos: 12,
-    NameEnd: 15,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 12,
+        NameEnd: 15,
+        Name:    "foo",
+      },
+    },
   },
   Where: &ast.Where{
     Where: 16,
diff --git a/testdata/result/statement/delete.sql.txt b/testdata/result/statement/delete.sql.txt
index 6c7b6ce..c0d4957 100644
--- a/testdata/result/statement/delete.sql.txt
+++ b/testdata/result/statement/delete.sql.txt
@@ -2,10 +2,14 @@
 delete foo where foo = 1 and bar = 2
 --- AST
 &ast.Delete{
-  TableName: &ast.Ident{
-    NamePos: 7,
-    NameEnd: 10,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 10,
+        Name:    "foo",
+      },
+    },
   },
   Where: &ast.Where{
     Where: 11,
diff --git a/testdata/result/statement/delete_as.sql.txt b/testdata/result/statement/delete_as.sql.txt
index f0758fd..b4721a9 100644
--- a/testdata/result/statement/delete_as.sql.txt
+++ b/testdata/result/statement/delete_as.sql.txt
@@ -2,10 +2,14 @@
 delete foo as F where F.foo = 1
 --- AST
 &ast.Delete{
-  TableName: &ast.Ident{
-    NamePos: 7,
-    NameEnd: 10,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 10,
+        Name:    "foo",
+      },
+    },
   },
   As: &ast.AsAlias{
     As:    11,
diff --git a/testdata/result/statement/delete_fqn.sql.txt b/testdata/result/statement/delete_fqn.sql.txt
new file mode 100644
index 0000000..7284c6e
--- /dev/null
+++ b/testdata/result/statement/delete_fqn.sql.txt
@@ -0,0 +1,56 @@
+--- delete_fqn.sql
+delete sch1.foo where foo = 1 and bar = 2
+--- AST
+&ast.Delete{
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 11,
+        Name:    "sch1",
+      },
+      &ast.Ident{
+        NamePos: 12,
+        NameEnd: 15,
+        Name:    "foo",
+      },
+    },
+  },
+  Where: &ast.Where{
+    Where: 16,
+    Expr:  &ast.BinaryExpr{
+      Op:   "AND",
+      Left: &ast.BinaryExpr{
+        Op:   "=",
+        Left: &ast.Ident{
+          NamePos: 22,
+          NameEnd: 25,
+          Name:    "foo",
+        },
+        Right: &ast.IntLiteral{
+          ValuePos: 28,
+          ValueEnd: 29,
+          Base:     10,
+          Value:    "1",
+        },
+      },
+      Right: &ast.BinaryExpr{
+        Op:   "=",
+        Left: &ast.Ident{
+          NamePos: 34,
+          NameEnd: 37,
+          Name:    "bar",
+        },
+        Right: &ast.IntLiteral{
+          ValuePos: 40,
+          ValueEnd: 41,
+          Base:     10,
+          Value:    "2",
+        },
+      },
+    },
+  },
+}
+
+--- SQL
+DELETE FROM sch1.foo WHERE foo = 1 AND bar = 2
diff --git a/testdata/result/statement/delete_then_return.sql.txt b/testdata/result/statement/delete_then_return.sql.txt
index 6c160a1..596a4a4 100644
--- a/testdata/result/statement/delete_then_return.sql.txt
+++ b/testdata/result/statement/delete_then_return.sql.txt
@@ -2,10 +2,14 @@
 delete foo where foo = 1 and bar = 2 then return *
 --- AST
 &ast.Delete{
-  TableName: &ast.Ident{
-    NamePos: 7,
-    NameEnd: 10,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 10,
+        Name:    "foo",
+      },
+    },
   },
   Where: &ast.Where{
     Where: 11,
diff --git a/testdata/result/statement/drop_property_graph.sql.txt b/testdata/result/statement/drop_property_graph.sql.txt
new file mode 100644
index 0000000..1a41bcb
--- /dev/null
+++ b/testdata/result/statement/drop_property_graph.sql.txt
@@ -0,0 +1,14 @@
+--- drop_property_graph.sql
+DROP PROPERTY GRAPH FinGraph
+
+--- AST
+&ast.DropPropertyGraph{
+  Name: &ast.Ident{
+    NamePos: 20,
+    NameEnd: 28,
+    Name:    "FinGraph",
+  },
+}
+
+--- SQL
+DROP PROPERTY GRAPH FinGraph
diff --git a/testdata/result/statement/drop_property_graph_if_exists.sql.txt b/testdata/result/statement/drop_property_graph_if_exists.sql.txt
new file mode 100644
index 0000000..3f0efc3
--- /dev/null
+++ b/testdata/result/statement/drop_property_graph_if_exists.sql.txt
@@ -0,0 +1,15 @@
+--- drop_property_graph_if_exists.sql
+DROP PROPERTY GRAPH IF EXISTS FinGraph
+
+--- AST
+&ast.DropPropertyGraph{
+  IfExists: true,
+  Name:     &ast.Ident{
+    NamePos: 30,
+    NameEnd: 38,
+    Name:    "FinGraph",
+  },
+}
+
+--- SQL
+DROP PROPERTY GRAPH IF EXISTS FinGraph
diff --git a/testdata/result/statement/insert_cte_select.sql.txt b/testdata/result/statement/insert_cte_select.sql.txt
index 13e64ec..b98e6b8 100644
--- a/testdata/result/statement/insert_cte_select.sql.txt
+++ b/testdata/result/statement/insert_cte_select.sql.txt
@@ -4,10 +4,14 @@ with cte AS (select 1 as foo, 2 as bar)
 select *
 --- AST
 &ast.Insert{
-  TableName: &ast.Ident{
-    NamePos: 7,
-    NameEnd: 10,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 10,
+        Name:    "foo",
+      },
+    },
   },
   Columns: []*ast.Ident{
     &ast.Ident{
diff --git a/testdata/result/statement/insert_fqn_values.sql.txt b/testdata/result/statement/insert_fqn_values.sql.txt
new file mode 100644
index 0000000..7cb2f58
--- /dev/null
+++ b/testdata/result/statement/insert_fqn_values.sql.txt
@@ -0,0 +1,112 @@
+--- insert_fqn_values.sql
+insert sch1.foo (foo, bar, baz)
+values (1, 2, 3),
+       (4, 5, 6)
+--- AST
+&ast.Insert{
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 11,
+        Name:    "sch1",
+      },
+      &ast.Ident{
+        NamePos: 12,
+        NameEnd: 15,
+        Name:    "foo",
+      },
+    },
+  },
+  Columns: []*ast.Ident{
+    &ast.Ident{
+      NamePos: 17,
+      NameEnd: 20,
+      Name:    "foo",
+    },
+    &ast.Ident{
+      NamePos: 22,
+      NameEnd: 25,
+      Name:    "bar",
+    },
+    &ast.Ident{
+      NamePos: 27,
+      NameEnd: 30,
+      Name:    "baz",
+    },
+  },
+  Input: &ast.ValuesInput{
+    Values: 32,
+    Rows:   []*ast.ValuesRow{
+      &ast.ValuesRow{
+        Lparen: 39,
+        Rparen: 47,
+        Exprs:  []*ast.DefaultExpr{
+          &ast.DefaultExpr{
+            DefaultPos: -1,
+            Expr:       &ast.IntLiteral{
+              ValuePos: 40,
+              ValueEnd: 41,
+              Base:     10,
+              Value:    "1",
+            },
+          },
+          &ast.DefaultExpr{
+            DefaultPos: -1,
+            Expr:       &ast.IntLiteral{
+              ValuePos: 43,
+              ValueEnd: 44,
+              Base:     10,
+              Value:    "2",
+            },
+          },
+          &ast.DefaultExpr{
+            DefaultPos: -1,
+            Expr:       &ast.IntLiteral{
+              ValuePos: 46,
+              ValueEnd: 47,
+              Base:     10,
+              Value:    "3",
+            },
+          },
+        },
+      },
+      &ast.ValuesRow{
+        Lparen: 57,
+        Rparen: 65,
+        Exprs:  []*ast.DefaultExpr{
+          &ast.DefaultExpr{
+            DefaultPos: -1,
+            Expr:       &ast.IntLiteral{
+              ValuePos: 58,
+              ValueEnd: 59,
+              Base:     10,
+              Value:    "4",
+            },
+          },
+          &ast.DefaultExpr{
+            DefaultPos: -1,
+            Expr:       &ast.IntLiteral{
+              ValuePos: 61,
+              ValueEnd: 62,
+              Base:     10,
+              Value:    "5",
+            },
+          },
+          &ast.DefaultExpr{
+            DefaultPos: -1,
+            Expr:       &ast.IntLiteral{
+              ValuePos: 64,
+              ValueEnd: 65,
+              Base:     10,
+              Value:    "6",
+            },
+          },
+        },
+      },
+    },
+  },
+}
+
+--- SQL
+INSERT INTO sch1.foo (foo, bar, baz) VALUES (1, 2, 3), (4, 5, 6)
diff --git a/testdata/result/statement/insert_into_values.sql.txt b/testdata/result/statement/insert_into_values.sql.txt
index 670a2c4..ed4f06d 100644
--- a/testdata/result/statement/insert_into_values.sql.txt
+++ b/testdata/result/statement/insert_into_values.sql.txt
@@ -4,10 +4,14 @@ values (1, 2, 3),
        (4, 5, 6)
 --- AST
 &ast.Insert{
-  TableName: &ast.Ident{
-    NamePos: 12,
-    NameEnd: 15,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 12,
+        NameEnd: 15,
+        Name:    "foo",
+      },
+    },
   },
   Columns: []*ast.Ident{
     &ast.Ident{
diff --git a/testdata/result/statement/insert_or_ignore.sql.txt b/testdata/result/statement/insert_or_ignore.sql.txt
index 43d4b19..e1a34b3 100644
--- a/testdata/result/statement/insert_or_ignore.sql.txt
+++ b/testdata/result/statement/insert_or_ignore.sql.txt
@@ -4,10 +4,14 @@ INSERT OR IGNORE INTO foo
 --- AST
 &ast.Insert{
   InsertOrType: "IGNORE",
-  TableName:    &ast.Ident{
-    NamePos: 22,
-    NameEnd: 25,
-    Name:    "foo",
+  TableName:    &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 22,
+        NameEnd: 25,
+        Name:    "foo",
+      },
+    },
   },
   Columns: []*ast.Ident{
     &ast.Ident{
diff --git a/testdata/result/statement/insert_or_update.sql.txt b/testdata/result/statement/insert_or_update.sql.txt
index 4ecc3ac..9f8837b 100644
--- a/testdata/result/statement/insert_or_update.sql.txt
+++ b/testdata/result/statement/insert_or_update.sql.txt
@@ -4,10 +4,14 @@ INSERT OR UPDATE INTO foo
 --- AST
 &ast.Insert{
   InsertOrType: "UPDATE",
-  TableName:    &ast.Ident{
-    NamePos: 22,
-    NameEnd: 25,
-    Name:    "foo",
+  TableName:    &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 22,
+        NameEnd: 25,
+        Name:    "foo",
+      },
+    },
   },
   Columns: []*ast.Ident{
     &ast.Ident{
diff --git a/testdata/result/statement/insert_or_update_then_return_with_action_as.sql.txt b/testdata/result/statement/insert_or_update_then_return_with_action_as.sql.txt
index 665e0e2..0c4b430 100644
--- a/testdata/result/statement/insert_or_update_then_return_with_action_as.sql.txt
+++ b/testdata/result/statement/insert_or_update_then_return_with_action_as.sql.txt
@@ -5,10 +5,14 @@ THEN RETURN WITH ACTION AS act *
 --- AST
 &ast.Insert{
   InsertOrType: "UPDATE",
-  TableName:    &ast.Ident{
-    NamePos: 22,
-    NameEnd: 25,
-    Name:    "foo",
+  TableName:    &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 22,
+        NameEnd: 25,
+        Name:    "foo",
+      },
+    },
   },
   Columns: []*ast.Ident{
     &ast.Ident{
diff --git a/testdata/result/statement/insert_select.sql.txt b/testdata/result/statement/insert_select.sql.txt
index 2b2e713..efe0b91 100644
--- a/testdata/result/statement/insert_select.sql.txt
+++ b/testdata/result/statement/insert_select.sql.txt
@@ -3,10 +3,14 @@ insert foo (foo, bar)
 select * from unnest([(1, 2), (3, 4)])
 --- AST
 &ast.Insert{
-  TableName: &ast.Ident{
-    NamePos: 7,
-    NameEnd: 10,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 10,
+        Name:    "foo",
+      },
+    },
   },
   Columns: []*ast.Ident{
     &ast.Ident{
diff --git a/testdata/result/statement/insert_values.sql.txt b/testdata/result/statement/insert_values.sql.txt
index e791c60..c5dc078 100644
--- a/testdata/result/statement/insert_values.sql.txt
+++ b/testdata/result/statement/insert_values.sql.txt
@@ -4,10 +4,14 @@ values (1, 2, 3),
        (4, 5, 6)
 --- AST
 &ast.Insert{
-  TableName: &ast.Ident{
-    NamePos: 7,
-    NameEnd: 10,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 10,
+        Name:    "foo",
+      },
+    },
   },
   Columns: []*ast.Ident{
     &ast.Ident{
diff --git a/testdata/result/statement/insert_values_default.sql.txt b/testdata/result/statement/insert_values_default.sql.txt
index b379cda..1f5b31d 100644
--- a/testdata/result/statement/insert_values_default.sql.txt
+++ b/testdata/result/statement/insert_values_default.sql.txt
@@ -3,10 +3,14 @@ insert foo (foo, bar)
 values (1, default)
 --- AST
 &ast.Insert{
-  TableName: &ast.Ident{
-    NamePos: 7,
-    NameEnd: 10,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 10,
+        Name:    "foo",
+      },
+    },
   },
   Columns: []*ast.Ident{
     &ast.Ident{
diff --git a/testdata/result/statement/insert_with_sequence_function.sql.txt b/testdata/result/statement/insert_with_sequence_function.sql.txt
index dc8fe0d..0e61665 100644
--- a/testdata/result/statement/insert_with_sequence_function.sql.txt
+++ b/testdata/result/statement/insert_with_sequence_function.sql.txt
@@ -3,10 +3,14 @@ INSERT INTO foo(bar) VALUES (GET_NEXT_SEQUENCE_VALUE(SEQUENCE my_sequence))
 
 --- AST
 &ast.Insert{
-  TableName: &ast.Ident{
-    NamePos: 12,
-    NameEnd: 15,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 12,
+        NameEnd: 15,
+        Name:    "foo",
+      },
+    },
   },
   Columns: []*ast.Ident{
     &ast.Ident{
diff --git a/testdata/result/statement/update.sql.txt b/testdata/result/statement/update.sql.txt
index e9e63cb..32a3ca4 100644
--- a/testdata/result/statement/update.sql.txt
+++ b/testdata/result/statement/update.sql.txt
@@ -2,10 +2,14 @@
 update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1
 --- AST
 &ast.Update{
-  TableName: &ast.Ident{
-    NamePos: 7,
-    NameEnd: 10,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 10,
+        Name:    "foo",
+      },
+    },
   },
   Updates: []*ast.UpdateItem{
     &ast.UpdateItem{
diff --git a/testdata/result/statement/update_as.sql.txt b/testdata/result/statement/update_as.sql.txt
index 002e921..b7aad4d 100644
--- a/testdata/result/statement/update_as.sql.txt
+++ b/testdata/result/statement/update_as.sql.txt
@@ -2,10 +2,14 @@
 update foo as F set F.foo = F.bar + 1 where foo = F.bar
 --- AST
 &ast.Update{
-  TableName: &ast.Ident{
-    NamePos: 7,
-    NameEnd: 10,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 10,
+        Name:    "foo",
+      },
+    },
   },
   As: &ast.AsAlias{
     As:    11,
diff --git a/testdata/result/statement/update_fqn.sql.txt b/testdata/result/statement/update_fqn.sql.txt
new file mode 100644
index 0000000..9f33753
--- /dev/null
+++ b/testdata/result/statement/update_fqn.sql.txt
@@ -0,0 +1,88 @@
+--- update_fqn.sql
+update sch1.foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1
+--- AST
+&ast.Update{
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 11,
+        Name:    "sch1",
+      },
+      &ast.Ident{
+        NamePos: 12,
+        NameEnd: 15,
+        Name:    "foo",
+      },
+    },
+  },
+  Updates: []*ast.UpdateItem{
+    &ast.UpdateItem{
+      Path: []*ast.Ident{
+        &ast.Ident{
+          NamePos: 20,
+          NameEnd: 23,
+          Name:    "foo",
+        },
+      },
+      DefaultExpr: &ast.DefaultExpr{
+        DefaultPos: -1,
+        Expr:       &ast.Ident{
+          NamePos: 26,
+          NameEnd: 29,
+          Name:    "bar",
+        },
+      },
+    },
+    &ast.UpdateItem{
+      Path: []*ast.Ident{
+        &ast.Ident{
+          NamePos: 31,
+          NameEnd: 34,
+          Name:    "bar",
+        },
+      },
+      DefaultExpr: &ast.DefaultExpr{
+        DefaultPos: -1,
+        Expr:       &ast.Ident{
+          NamePos: 37,
+          NameEnd: 40,
+          Name:    "foo",
+        },
+      },
+    },
+    &ast.UpdateItem{
+      Path: []*ast.Ident{
+        &ast.Ident{
+          NamePos: 42,
+          NameEnd: 45,
+          Name:    "baz",
+        },
+      },
+      DefaultExpr: &ast.DefaultExpr{
+        DefaultPos: 48,
+        Default:    true,
+      },
+    },
+  },
+  Where: &ast.Where{
+    Where: 56,
+    Expr:  &ast.BinaryExpr{
+      Op:   "=",
+      Left: &ast.Ident{
+        NamePos: 62,
+        NameEnd: 65,
+        Name:    "foo",
+      },
+      Right: &ast.IntLiteral{
+        ValuePos: 68,
+        ValueEnd: 69,
+        Base:     10,
+        Value:    "1",
+      },
+    },
+  },
+}
+
+--- SQL
+UPDATE sch1.foo SET foo = bar, bar = foo, baz = DEFAULT WHERE foo = 1
diff --git a/testdata/result/statement/update_then_return_with_action.sql.txt b/testdata/result/statement/update_then_return_with_action.sql.txt
index bfaba0e..7990f30 100644
--- a/testdata/result/statement/update_then_return_with_action.sql.txt
+++ b/testdata/result/statement/update_then_return_with_action.sql.txt
@@ -2,10 +2,14 @@
 update foo set foo = bar, bar = foo, baz = DEFAULT where foo = 1 then return with action *
 --- AST
 &ast.Update{
-  TableName: &ast.Ident{
-    NamePos: 7,
-    NameEnd: 10,
-    Name:    "foo",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 7,
+        NameEnd: 10,
+        Name:    "foo",
+      },
+    },
   },
   Updates: []*ast.UpdateItem{
     &ast.UpdateItem{
diff --git a/testdata/result/statement/update_with_safe_ml_predict.sql.txt b/testdata/result/statement/update_with_safe_ml_predict.sql.txt
index 60bd0d1..0a67f8d 100644
--- a/testdata/result/statement/update_with_safe_ml_predict.sql.txt
+++ b/testdata/result/statement/update_with_safe_ml_predict.sql.txt
@@ -14,10 +14,14 @@ WHERE products.desc_embed IS NULL
 --- AST
 &ast.Update{
   Update:    76,
-  TableName: &ast.Ident{
-    NamePos: 83,
-    NameEnd: 91,
-    Name:    "products",
+  TableName: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 83,
+        NameEnd: 91,
+        Name:    "products",
+      },
+    },
   },
   Updates: []*ast.UpdateItem{
     &ast.UpdateItem{