Repository: cloudspannerecosystem/memefish
Pull request #71 (2023-08-30 13:54:39)

add support for missing IF (NOT) EXISTS clauses
This PR adds support for following DDL syntax.

- [CREATE INDEX IF NOT EXISTS](https://cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#create-index)
- [ALTER TABLE ADD COLUMN IF NOT EXISTS](https://cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#alter_table)
- [DROP INDEX IF EXISTS](https://cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#drop-index)


Patch:
diff --git a/ast/ast.go b/ast/ast.go
index c6259e9..9597580 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -1600,14 +1600,15 @@ type AlterTable struct {
 
 // AddColumn is ADD COLUMN clause in ALTER TABLE.
 //
-//	ADD COLUMN {{.Column | sql}}
+//	ADD COLUMN {{.IfNotExists}}IF NOT EXISTS{{end}} {{.Column | sql}}
 type AddColumn struct {
 	// pos = Add
 	// end = Column.end
 
 	Add token.Pos // position of "ADD" keyword
 
-	Column *ColumnDef
+	IfNotExists bool
+	Column      *ColumnDef
 }
 
 // AddTableConstraint is ADD table_constraint clause in ALTER TABLE.
@@ -1741,7 +1742,7 @@ type DropTable struct {
 //	CREATE
 //	  {{if .Unique}}UNIQUE{{end}}
 //	  {{if .NullFiltered}}NULL_FILTERED{{end}}
-//	INDEX {{.Name | sql}} ON {{.TableName | sql}} (
+//	INDEX {{if .IfExists}}IF NOT EXISTS{{end}} {{.Name | sql}} ON {{.TableName | sql}} (
 //	  {{.Keys | sqlJoin ","}}
 //	)
 //	{{.Storing | sqlOpt}}
@@ -1755,6 +1756,7 @@ type CreateIndex struct {
 
 	Unique       bool
 	NullFiltered bool
+	IfNotExists  bool
 	Name         *Ident
 	TableName    *Ident
 	Keys         []*IndexKey
@@ -1789,14 +1791,15 @@ type InterleaveIn struct {
 
 // DropIndex is DROP INDEX statement node.
 //
-//	DROP INDEX {{.Name | sql}}
+//	DROP INDEX {{.IfExists}}IF EXISTS{{end}} {{.Name | sql}}
 type DropIndex struct {
 	// pos = Drop
 	// end = Name.end
 
 	Drop token.Pos // position of "DROP" keyword
 
-	Name *Ident
+	IfExists bool
+	Name     *Ident
 }
 
 // CreateRole is CREATE ROLE statement node.
diff --git a/ast/sql.go b/ast/sql.go
index 5300efe..a2ad597 100644
--- a/ast/sql.go
+++ b/ast/sql.go
@@ -873,7 +873,11 @@ func (a *AlterTable) SQL() string {
 }
 
 func (a *AddColumn) SQL() string {
-	return "ADD COLUMN " + a.Column.SQL()
+	sql := "ADD COLUMN "
+	if a.IfNotExists {
+		sql += "IF NOT EXISTS "
+	}
+	return sql + a.Column.SQL()
 }
 
 func (a *AddTableConstraint) SQL() string {
@@ -940,7 +944,11 @@ func (c *CreateIndex) SQL() string {
 	if c.NullFiltered {
 		sql += "NULL_FILTERED "
 	}
-	sql += "INDEX " + c.Name.SQL() + " ON " + c.TableName.SQL() + " ("
+	sql += "INDEX "
+	if c.IfNotExists {
+		sql += "IF NOT EXISTS "
+	}
+	sql += c.Name.SQL() + " ON " + c.TableName.SQL() + " ("
 	for i, k := range c.Keys {
 		if i != 0 {
 			sql += ", "
@@ -974,7 +982,11 @@ func (i *InterleaveIn) SQL() string {
 }
 
 func (d *DropIndex) SQL() string {
-	return "DROP INDEX " + d.Name.SQL()
+	sql := "DROP INDEX "
+	if d.IfExists {
+		sql += "IF EXISTS "
+	}
+	return sql + d.Name.SQL()
 }
 
 func (c *CreateRole) SQL() string {
diff --git a/parser.go b/parser.go
index 66517d7..c889328 100644
--- a/parser.go
+++ b/parser.go
@@ -2030,13 +2030,7 @@ func (p *Parser) parseCreateDatabase(pos token.Pos) *ast.CreateDatabase {
 
 func (p *Parser) parseCreateTable(pos token.Pos) *ast.CreateTable {
 	p.expectKeywordLike("TABLE")
-	ifNotExists := false
-	if p.Token.IsKeywordLike("IF") {
-		p.nextToken()
-		p.expect("NOT")
-		p.expect("EXISTS")
-		ifNotExists = true
-	}
+	ifNotExists := p.parseIfNotExists()
 	name := p.parseIdent()
 
 	// This loop allows parsing trailing comma intentionally.
@@ -2102,13 +2096,7 @@ func (p *Parser) parseCreateTable(pos token.Pos) *ast.CreateTable {
 
 func (p *Parser) parseCreateSequence(pos token.Pos) *ast.CreateSequence {
 	p.expectKeywordLike("SEQUENCE")
-	ifNotExists := false
-	if p.Token.IsKeywordLike("IF") {
-		p.nextToken()
-		p.expect("NOT")
-		p.expect("EXISTS")
-		ifNotExists = true
-	}
+	ifNotExists := p.parseIfNotExists()
 	name := p.parseIdent()
 
 	p.expectKeywordLike("OPTIONS")
@@ -2451,6 +2439,9 @@ func (p *Parser) parseCreateIndex(pos token.Pos) *ast.CreateIndex {
 	}
 
 	p.expectKeywordLike("INDEX")
+
+	ifNotExists := p.parseIfNotExists()
+
 	name := p.parseIdent()
 
 	p.expect("ON")
@@ -2478,6 +2469,7 @@ func (p *Parser) parseCreateIndex(pos token.Pos) *ast.CreateIndex {
 		Rparen:       rparen,
 		Unique:       unique,
 		NullFiltered: nullFiltered,
+		IfNotExists:  ifNotExists,
 		Name:         name,
 		TableName:    tableName,
 		Keys:         keys,
@@ -2561,10 +2553,12 @@ func (p *Parser) parseAlterTableAdd() ast.TableAlternation {
 	switch {
 	case p.Token.IsKeywordLike("COLUMN"):
 		p.expectKeywordLike("COLUMN")
+		ifNotExists := p.parseIfNotExists()
 		column := p.parseColumnDef()
 		alternation = &ast.AddColumn{
-			Add:    pos,
-			Column: column,
+			Add:         pos,
+			IfNotExists: ifNotExists,
+			Column:      column,
 		}
 	case p.Token.IsKeywordLike("CONSTRAINT"):
 		alternation = &ast.AddTableConstraint{
@@ -2696,12 +2690,7 @@ func (p *Parser) parseAlterColumn() ast.TableAlternation {
 
 func (p *Parser) parseDropTable(pos token.Pos) *ast.DropTable {
 	p.expectKeywordLike("TABLE")
-	ifExists := false
-	if p.Token.IsKeywordLike("IF") {
-		p.nextToken()
-		p.expect("EXISTS")
-		ifExists = true
-	}
+	ifExists := p.parseIfExists()
 	name := p.parseIdent()
 	return &ast.DropTable{
 		Drop:     pos,
@@ -2712,10 +2701,12 @@ func (p *Parser) parseDropTable(pos token.Pos) *ast.DropTable {
 
 func (p *Parser) parseDropIndex(pos token.Pos) *ast.DropIndex {
 	p.expectKeywordLike("INDEX")
+	ifExists := p.parseIfExists()
 	name := p.parseIdent()
 	return &ast.DropIndex{
-		Drop: pos,
-		Name: name,
+		Drop:     pos,
+		IfExists: ifExists,
+		Name:     name,
 	}
 }
 
@@ -2991,6 +2982,25 @@ func (p *Parser) parseScalarSchemaType() ast.SchemaType {
 	panic(p.errorfAtToken(id, "expect ident: %s, %s, but: %s", strings.Join(scalarSchemaTypes, ","), strings.Join(sizedSchemaTypes, ","), id.AsString))
 }
 
+func (p *Parser) parseIfNotExists() bool {
+	if p.Token.IsKeywordLike("IF") {
+		p.nextToken()
+		p.expect("NOT")
+		p.expect("EXISTS")
+		return true
+	}
+	return false
+}
+
+func (p *Parser) parseIfExists() bool {
+	if p.Token.IsKeywordLike("IF") {
+		p.nextToken()
+		p.expect("EXISTS")
+		return true
+	}
+	return false
+}
+
 // ================================================================================
 //
 // DML


Test patch:
diff --git a/testdata/input/ddl/alter_table_add_column_if_not_exists.sql b/testdata/input/ddl/alter_table_add_column_if_not_exists.sql
new file mode 100644
index 0000000..e2a4fd9
--- /dev/null
+++ b/testdata/input/ddl/alter_table_add_column_if_not_exists.sql
@@ -0,0 +1,1 @@
+alter table foo add column if not exists baz string(max) not null
diff --git a/testdata/input/ddl/create_index_if_not_exists.sql b/testdata/input/ddl/create_index_if_not_exists.sql
new file mode 100644
index 0000000..668cbc9
--- /dev/null
+++ b/testdata/input/ddl/create_index_if_not_exists.sql
@@ -0,0 +1,1 @@
+create index if not exists foo_bar on foo (bar)
diff --git a/testdata/input/ddl/drop_index_if_exists.sql b/testdata/input/ddl/drop_index_if_exists.sql
new file mode 100644
index 0000000..1a91a6f
--- /dev/null
+++ b/testdata/input/ddl/drop_index_if_exists.sql
@@ -0,0 +1,1 @@
+drop index if exists foo_bar
diff --git a/testdata/result/ddl/alter_table_add_column.sql.txt b/testdata/result/ddl/alter_table_add_column.sql.txt
index 14319ef..bdcaed8 100644
--- a/testdata/result/ddl/alter_table_add_column.sql.txt
+++ b/testdata/result/ddl/alter_table_add_column.sql.txt
@@ -9,8 +9,9 @@ alter table foo add column baz string(max) not null
     Name:    "foo",
   },
   TableAlternation: &ast.AddColumn{
-    Add:    16,
-    Column: &ast.ColumnDef{
+    Add:         16,
+    IfNotExists: false,
+    Column:      &ast.ColumnDef{
       Null: 47,
       Name: &ast.Ident{
         NamePos: 27,
diff --git a/testdata/result/ddl/alter_table_add_column_if_not_exists.sql.txt b/testdata/result/ddl/alter_table_add_column_if_not_exists.sql.txt
new file mode 100644
index 0000000..4f9cfad
--- /dev/null
+++ b/testdata/result/ddl/alter_table_add_column_if_not_exists.sql.txt
@@ -0,0 +1,38 @@
+--- alter_table_add_column_if_not_exists.sql
+alter table foo add column if not exists baz string(max) not null
+
+--- AST
+&ast.AlterTable{
+  Alter: 0,
+  Name:  &ast.Ident{
+    NamePos: 12,
+    NameEnd: 15,
+    Name:    "foo",
+  },
+  TableAlternation: &ast.AddColumn{
+    Add:         16,
+    IfNotExists: true,
+    Column:      &ast.ColumnDef{
+      Null: 61,
+      Name: &ast.Ident{
+        NamePos: 41,
+        NameEnd: 44,
+        Name:    "baz",
+      },
+      Type: &ast.SizedSchemaType{
+        NamePos: 45,
+        Rparen:  55,
+        Name:    "STRING",
+        Max:     true,
+        Size:    nil,
+      },
+      NotNull:       true,
+      DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
+      GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
+      Options:       (*ast.ColumnDefOptions)(nil),
+    },
+  },
+}
+
+--- SQL
+ALTER TABLE foo ADD COLUMN IF NOT EXISTS baz STRING(MAX) NOT NULL
diff --git a/testdata/result/ddl/alter_table_add_column_with_if_expression.sql.txt b/testdata/result/ddl/alter_table_add_column_with_if_expression.sql.txt
index be02cb2..bba7372 100644
--- a/testdata/result/ddl/alter_table_add_column_with_if_expression.sql.txt
+++ b/testdata/result/ddl/alter_table_add_column_with_if_expression.sql.txt
@@ -9,8 +9,9 @@ ALTER TABLE foo ADD COLUMN expired_at TIMESTAMP AS (IF (status != "OPEN" AND sta
     Name:    "foo",
   },
   TableAlternation: &ast.AddColumn{
-    Add:    16,
-    Column: &ast.ColumnDef{
+    Add:         16,
+    IfNotExists: false,
+    Column:      &ast.ColumnDef{
       Null: -1,
       Name: &ast.Ident{
         NamePos: 27,
diff --git a/testdata/result/ddl/create_index.sql.txt b/testdata/result/ddl/create_index.sql.txt
index 1abdc65..aa14baa 100644
--- a/testdata/result/ddl/create_index.sql.txt
+++ b/testdata/result/ddl/create_index.sql.txt
@@ -10,6 +10,7 @@ create index foo_bar on foo (
   Rparen:       53,
   Unique:       false,
   NullFiltered: false,
+  IfNotExists:  false,
   Name:         &ast.Ident{
     NamePos: 13,
     NameEnd: 20,
diff --git a/testdata/result/ddl/create_index_if_not_exists.sql.txt b/testdata/result/ddl/create_index_if_not_exists.sql.txt
new file mode 100644
index 0000000..d188cb1
--- /dev/null
+++ b/testdata/result/ddl/create_index_if_not_exists.sql.txt
@@ -0,0 +1,37 @@
+--- create_index_if_not_exists.sql
+create index if not exists foo_bar on foo (bar)
+
+--- AST
+&ast.CreateIndex{
+  Create:       0,
+  Rparen:       46,
+  Unique:       false,
+  NullFiltered: false,
+  IfNotExists:  true,
+  Name:         &ast.Ident{
+    NamePos: 27,
+    NameEnd: 34,
+    Name:    "foo_bar",
+  },
+  TableName: &ast.Ident{
+    NamePos: 38,
+    NameEnd: 41,
+    Name:    "foo",
+  },
+  Keys: []*ast.IndexKey{
+    &ast.IndexKey{
+      DirPos: -1,
+      Name:   &ast.Ident{
+        NamePos: 43,
+        NameEnd: 46,
+        Name:    "bar",
+      },
+      Dir: "",
+    },
+  },
+  Storing:      (*ast.Storing)(nil),
+  InterleaveIn: (*ast.InterleaveIn)(nil),
+}
+
+--- SQL
+CREATE INDEX IF NOT EXISTS foo_bar ON foo (bar)
diff --git a/testdata/result/ddl/create_index_interleave.sql.txt b/testdata/result/ddl/create_index_interleave.sql.txt
index 7c7549d..7084414 100644
--- a/testdata/result/ddl/create_index_interleave.sql.txt
+++ b/testdata/result/ddl/create_index_interleave.sql.txt
@@ -10,6 +10,7 @@ create index foo_bar on foo (
   Rparen:       41,
   Unique:       false,
   NullFiltered: false,
+  IfNotExists:  false,
   Name:         &ast.Ident{
     NamePos: 13,
     NameEnd: 20,
diff --git a/testdata/result/ddl/create_index_storing.sql.txt b/testdata/result/ddl/create_index_storing.sql.txt
index 35fb090..bc0784e 100644
--- a/testdata/result/ddl/create_index_storing.sql.txt
+++ b/testdata/result/ddl/create_index_storing.sql.txt
@@ -9,6 +9,7 @@ create index foo_bar on foo (
   Rparen:       40,
   Unique:       false,
   NullFiltered: false,
+  IfNotExists:  false,
   Name:         &ast.Ident{
     NamePos: 13,
     NameEnd: 20,
diff --git a/testdata/result/ddl/create_uniq_null_filtered_index.sql.txt b/testdata/result/ddl/create_uniq_null_filtered_index.sql.txt
index 43624cf..6c944be 100644
--- a/testdata/result/ddl/create_uniq_null_filtered_index.sql.txt
+++ b/testdata/result/ddl/create_uniq_null_filtered_index.sql.txt
@@ -7,6 +7,7 @@ create unique null_filtered index foo_bar on foo (foo)
   Rparen:       53,
   Unique:       true,
   NullFiltered: true,
+  IfNotExists:  false,
   Name:         &ast.Ident{
     NamePos: 34,
     NameEnd: 41,
diff --git a/testdata/result/ddl/drop_index.sql.txt b/testdata/result/ddl/drop_index.sql.txt
index 692797a..517bda8 100644
--- a/testdata/result/ddl/drop_index.sql.txt
+++ b/testdata/result/ddl/drop_index.sql.txt
@@ -3,8 +3,9 @@ drop index foo_bar
 
 --- AST
 &ast.DropIndex{
-  Drop: 0,
-  Name: &ast.Ident{
+  Drop:     0,
+  IfExists: false,
+  Name:     &ast.Ident{
     NamePos: 11,
     NameEnd: 18,
     Name:    "foo_bar",
diff --git a/testdata/result/ddl/drop_index_if_exists.sql.txt b/testdata/result/ddl/drop_index_if_exists.sql.txt
new file mode 100644
index 0000000..5549fd1
--- /dev/null
+++ b/testdata/result/ddl/drop_index_if_exists.sql.txt
@@ -0,0 +1,16 @@
+--- drop_index_if_exists.sql
+drop index if exists foo_bar
+
+--- AST
+&ast.DropIndex{
+  Drop:     0,
+  IfExists: true,
+  Name:     &ast.Ident{
+    NamePos: 21,
+    NameEnd: 28,
+    Name:    "foo_bar",
+  },
+}
+
+--- SQL
+DROP INDEX IF EXISTS foo_bar
diff --git a/testdata/result/statement/alter_table_add_column.sql.txt b/testdata/result/statement/alter_table_add_column.sql.txt
index 14319ef..bdcaed8 100644
--- a/testdata/result/statement/alter_table_add_column.sql.txt
+++ b/testdata/result/statement/alter_table_add_column.sql.txt
@@ -9,8 +9,9 @@ alter table foo add column baz string(max) not null
     Name:    "foo",
   },
   TableAlternation: &ast.AddColumn{
-    Add:    16,
-    Column: &ast.ColumnDef{
+    Add:         16,
+    IfNotExists: false,
+    Column:      &ast.ColumnDef{
       Null: 47,
       Name: &ast.Ident{
         NamePos: 27,
diff --git a/testdata/result/statement/alter_table_add_column_if_not_exists.sql.txt b/testdata/result/statement/alter_table_add_column_if_not_exists.sql.txt
new file mode 100644
index 0000000..4f9cfad
--- /dev/null
+++ b/testdata/result/statement/alter_table_add_column_if_not_exists.sql.txt
@@ -0,0 +1,38 @@
+--- alter_table_add_column_if_not_exists.sql
+alter table foo add column if not exists baz string(max) not null
+
+--- AST
+&ast.AlterTable{
+  Alter: 0,
+  Name:  &ast.Ident{
+    NamePos: 12,
+    NameEnd: 15,
+    Name:    "foo",
+  },
+  TableAlternation: &ast.AddColumn{
+    Add:         16,
+    IfNotExists: true,
+    Column:      &ast.ColumnDef{
+      Null: 61,
+      Name: &ast.Ident{
+        NamePos: 41,
+        NameEnd: 44,
+        Name:    "baz",
+      },
+      Type: &ast.SizedSchemaType{
+        NamePos: 45,
+        Rparen:  55,
+        Name:    "STRING",
+        Max:     true,
+        Size:    nil,
+      },
+      NotNull:       true,
+      DefaultExpr:   (*ast.ColumnDefaultExpr)(nil),
+      GeneratedExpr: (*ast.GeneratedColumnExpr)(nil),
+      Options:       (*ast.ColumnDefOptions)(nil),
+    },
+  },
+}
+
+--- SQL
+ALTER TABLE foo ADD COLUMN IF NOT EXISTS baz STRING(MAX) NOT NULL
diff --git a/testdata/result/statement/alter_table_add_column_with_if_expression.sql.txt b/testdata/result/statement/alter_table_add_column_with_if_expression.sql.txt
index be02cb2..bba7372 100644
--- a/testdata/result/statement/alter_table_add_column_with_if_expression.sql.txt
+++ b/testdata/result/statement/alter_table_add_column_with_if_expression.sql.txt
@@ -9,8 +9,9 @@ ALTER TABLE foo ADD COLUMN expired_at TIMESTAMP AS (IF (status != "OPEN" AND sta
     Name:    "foo",
   },
   TableAlternation: &ast.AddColumn{
-    Add:    16,
-    Column: &ast.ColumnDef{
+    Add:         16,
+    IfNotExists: false,
+    Column:      &ast.ColumnDef{
       Null: -1,
       Name: &ast.Ident{
         NamePos: 27,
diff --git a/testdata/result/statement/create_index.sql.txt b/testdata/result/statement/create_index.sql.txt
index 1abdc65..aa14baa 100644
--- a/testdata/result/statement/create_index.sql.txt
+++ b/testdata/result/statement/create_index.sql.txt
@@ -10,6 +10,7 @@ create index foo_bar on foo (
   Rparen:       53,
   Unique:       false,
   NullFiltered: false,
+  IfNotExists:  false,
   Name:         &ast.Ident{
     NamePos: 13,
     NameEnd: 20,
diff --git a/testdata/result/statement/create_index_if_not_exists.sql.txt b/testdata/result/statement/create_index_if_not_exists.sql.txt
new file mode 100644
index 0000000..d188cb1
--- /dev/null
+++ b/testdata/result/statement/create_index_if_not_exists.sql.txt
@@ -0,0 +1,37 @@
+--- create_index_if_not_exists.sql
+create index if not exists foo_bar on foo (bar)
+
+--- AST
+&ast.CreateIndex{
+  Create:       0,
+  Rparen:       46,
+  Unique:       false,
+  NullFiltered: false,
+  IfNotExists:  true,
+  Name:         &ast.Ident{
+    NamePos: 27,
+    NameEnd: 34,
+    Name:    "foo_bar",
+  },
+  TableName: &ast.Ident{
+    NamePos: 38,
+    NameEnd: 41,
+    Name:    "foo",
+  },
+  Keys: []*ast.IndexKey{
+    &ast.IndexKey{
+      DirPos: -1,
+      Name:   &ast.Ident{
+        NamePos: 43,
+        NameEnd: 46,
+        Name:    "bar",
+      },
+      Dir: "",
+    },
+  },
+  Storing:      (*ast.Storing)(nil),
+  InterleaveIn: (*ast.InterleaveIn)(nil),
+}
+
+--- SQL
+CREATE INDEX IF NOT EXISTS foo_bar ON foo (bar)
diff --git a/testdata/result/statement/create_index_interleave.sql.txt b/testdata/result/statement/create_index_interleave.sql.txt
index 7c7549d..7084414 100644
--- a/testdata/result/statement/create_index_interleave.sql.txt
+++ b/testdata/result/statement/create_index_interleave.sql.txt
@@ -10,6 +10,7 @@ create index foo_bar on foo (
   Rparen:       41,
   Unique:       false,
   NullFiltered: false,
+  IfNotExists:  false,
   Name:         &ast.Ident{
     NamePos: 13,
     NameEnd: 20,
diff --git a/testdata/result/statement/create_index_storing.sql.txt b/testdata/result/statement/create_index_storing.sql.txt
index 35fb090..bc0784e 100644
--- a/testdata/result/statement/create_index_storing.sql.txt
+++ b/testdata/result/statement/create_index_storing.sql.txt
@@ -9,6 +9,7 @@ create index foo_bar on foo (
   Rparen:       40,
   Unique:       false,
   NullFiltered: false,
+  IfNotExists:  false,
   Name:         &ast.Ident{
     NamePos: 13,
     NameEnd: 20,
diff --git a/testdata/result/statement/create_uniq_null_filtered_index.sql.txt b/testdata/result/statement/create_uniq_null_filtered_index.sql.txt
index 43624cf..6c944be 100644
--- a/testdata/result/statement/create_uniq_null_filtered_index.sql.txt
+++ b/testdata/result/statement/create_uniq_null_filtered_index.sql.txt
@@ -7,6 +7,7 @@ create unique null_filtered index foo_bar on foo (foo)
   Rparen:       53,
   Unique:       true,
   NullFiltered: true,
+  IfNotExists:  false,
   Name:         &ast.Ident{
     NamePos: 34,
     NameEnd: 41,
diff --git a/testdata/result/statement/drop_index.sql.txt b/testdata/result/statement/drop_index.sql.txt
index 692797a..517bda8 100644
--- a/testdata/result/statement/drop_index.sql.txt
+++ b/testdata/result/statement/drop_index.sql.txt
@@ -3,8 +3,9 @@ drop index foo_bar
 
 --- AST
 &ast.DropIndex{
-  Drop: 0,
-  Name: &ast.Ident{
+  Drop:     0,
+  IfExists: false,
+  Name:     &ast.Ident{
     NamePos: 11,
     NameEnd: 18,
     Name:    "foo_bar",
diff --git a/testdata/result/statement/drop_index_if_exists.sql.txt b/testdata/result/statement/drop_index_if_exists.sql.txt
new file mode 100644
index 0000000..5549fd1
--- /dev/null
+++ b/testdata/result/statement/drop_index_if_exists.sql.txt
@@ -0,0 +1,16 @@
+--- drop_index_if_exists.sql
+drop index if exists foo_bar
+
+--- AST
+&ast.DropIndex{
+  Drop:     0,
+  IfExists: true,
+  Name:     &ast.Ident{
+    NamePos: 21,
+    NameEnd: 28,
+    Name:    "foo_bar",
+  },
+}
+
+--- SQL
+DROP INDEX IF EXISTS foo_bar