From 3166a94e71d6e38a4d3492713d2babccc53ed480 Mon Sep 17 00:00:00 2001 From: "AJ Roetker (aider)" Date: Thu, 22 May 2025 13:41:47 -0700 Subject: [PATCH 1/2] test: Add tests for DROP INDEX statements --- rel/parse_sql_test.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/rel/parse_sql_test.go b/rel/parse_sql_test.go index 2eec2bf..0bbfb41 100644 --- a/rel/parse_sql_test.go +++ b/rel/parse_sql_test.go @@ -747,6 +747,28 @@ func TestSqlDrop(t *testing.T) { assert.Equal(t, lex.TokenDrop, ds.Keyword(), "Has keyword DROP") assert.Equal(t, "TABLE", ds.Tok.V, "Wanted TABLE: got %q", ds.Tok.V) assert.Equal(t, "articles", ds.Identity, "has articles: %v", ds.Identity) + + sql = `DROP INDEX idx_user_email;` + req, err = rel.ParseSql(sql) + assert.Equal(t, nil, err) + assert.NotEqual(t, nil, req) + ds, ok = req.(*rel.SqlDrop) + assert.True(t, ok, "wanted SqlDrop got %T", req) + assert.Equal(t, lex.TokenDrop, ds.Keyword(), "Has keyword DROP") + assert.Equal(t, "INDEX", ds.Tok.V, "Wanted INDEX: got %q", ds.Tok.V) + assert.Equal(t, "idx_user_email", ds.Identity, "has idx_user_email: %v", ds.Identity) + + sql = `DROP INDEX IF EXISTS idx_user_name ON users;` + req, err = rel.ParseSql(sql) + assert.Equal(t, nil, err) + assert.NotEqual(t, nil, req) + ds, ok = req.(*rel.SqlDrop) + assert.True(t, ok, "wanted SqlDrop got %T", req) + assert.Equal(t, lex.TokenDrop, ds.Keyword(), "Has keyword DROP") + assert.Equal(t, "INDEX", ds.Tok.V, "Wanted INDEX: got %q", ds.Tok.V) + assert.Equal(t, "idx_user_name", ds.Identity, "has idx_user_name: %v", ds.Identity) + assert.Equal(t, "users", ds.Table, "has table users: %v", ds.Table) + assert.True(t, ds.IfExists, "Expected IfExists to be true") } func TestWithNameValue(t *testing.T) { From 633bccfcb0925c2d46c87a47068550600d09cab0 Mon Sep 17 00:00:00 2001 From: AJ Roetker Date: Thu, 22 May 2025 14:07:03 -0700 Subject: [PATCH 2/2] Support drop index statements --- lex/dialect_sql.go | 16 ++++++++++++++++ rel/parse_sql.go | 27 +++++++++++++++++++++++---- rel/parse_sql_test.go | 16 ++++++++-------- rel/sql.go | 2 ++ 4 files changed, 49 insertions(+), 12 deletions(-) diff --git a/lex/dialect_sql.go b/lex/dialect_sql.go index ca73963..60e93f8 100644 --- a/lex/dialect_sql.go +++ b/lex/dialect_sql.go @@ -461,6 +461,17 @@ func lexAs(l *Lexer) StateFn { } return nil } +func lexOn(l *Lexer) StateFn { + l.SkipWhiteSpaces() + keyWord := strings.ToLower(l.PeekWord()) + switch keyWord { + case "on": + l.ConsumeWord(keyWord) + l.Emit(TokenOn) + return nil + } + return nil +} func lexNotExists(l *Lexer) StateFn { l.SkipWhiteSpaces() keyWord := strings.ToLower(l.PeekWord()) @@ -534,6 +545,9 @@ func LexDrop(l *Lexer) StateFn { case "table": l.ConsumeWord(keyWord) l.Emit(TokenTable) + case "index": + l.ConsumeWord(keyWord) + l.Emit(TokenIndex) case "source": l.ConsumeWord(keyWord) l.Emit(TokenSource) @@ -553,6 +567,8 @@ func LexDrop(l *Lexer) StateFn { return nil } l.Push("LexIdentifier", LexIdentifier) + l.Push("LexOn", lexOn) + l.Push("LexIdentifier", LexIdentifier) return lexNotExists } diff --git a/rel/parse_sql.go b/rel/parse_sql.go index 7a19a98..770ddbd 100644 --- a/rel/parse_sql.go +++ b/rel/parse_sql.go @@ -860,28 +860,47 @@ func (m *Sqlbridge) parseDrop() (*SqlDrop, error) { req.Temp = true } - // DROP (TABLE|VIEW|SOURCE|CONTINUOUSVIEW) + // DROP (TABLE|VIEW|SOURCE|INDEX|CONTINUOUSVIEW) switch m.Cur().T { - case lex.TokenTable, lex.TokenView, lex.TokenSource, lex.TokenContinuousView, + case lex.TokenTable, lex.TokenView, lex.TokenIndex, lex.TokenSource, lex.TokenContinuousView, lex.TokenSchema, lex.TokenDatabase: req.Tok = m.Next() case lex.TokenIdentity: // triggers, indexes req.Tok = m.Next() default: - return nil, m.ErrMsg("Expected view, database,schema, table, source, continuousview for DROP got") + return nil, m.ErrMsg("Expected view, database, index, schema, table, source, continuousview for DROP got") + } + + if m.Cur().T == lex.TokenIf { + m.Next() // Consume IF + if m.Next().T != lex.TokenExists { + return nil, m.ErrMsg("Expected CREATE {TABLE|INDEX|SCHEMA|DATABASE} IF EXISTS ") + } + req.IfExists = true } switch m.Cur().T { case lex.TokenTable, lex.TokenIdentity: req.Identity = m.Next().V default: - return nil, m.ErrMsg("Expected identity after DROP (TABLE|VIEW|SOURCE|SCHEMA|DATABASE) ") + return nil, m.ErrMsg("Expected identity after DROP (TABLE|INDEX|VIEW|SOURCE|SCHEMA|DATABASE) ") } switch req.Tok.T { case lex.TokenTable: // just table + case lex.TokenIndex: + t := m.Next() // consume ON + if t.T != lex.TokenOn { + return nil, m.ErrMsg("Expected ON") + } + t = m.Next() // consume Identity + if t.T != lex.TokenIdentity { + return nil, m.ErrMsg("Expected Identity table name") + } + req.Parent = t.V + discardComments(m) case lex.TokenSource, lex.TokenSchema: // schema case lex.TokenContinuousView, lex.TokenView: diff --git a/rel/parse_sql_test.go b/rel/parse_sql_test.go index 0bbfb41..017380a 100644 --- a/rel/parse_sql_test.go +++ b/rel/parse_sql_test.go @@ -740,18 +740,18 @@ func TestSqlDrop(t *testing.T) { t.Parallel() sql := `DROP TABLE articles;` req, err := rel.ParseSql(sql) - assert.Equal(t, nil, err) - assert.NotEqual(t, nil, req) + assert.NoError(t, err) + assert.NotNil(t, req) ds, ok := req.(*rel.SqlDrop) assert.True(t, ok, "wanted SqlDrop got %T", req) assert.Equal(t, lex.TokenDrop, ds.Keyword(), "Has keyword DROP") assert.Equal(t, "TABLE", ds.Tok.V, "Wanted TABLE: got %q", ds.Tok.V) assert.Equal(t, "articles", ds.Identity, "has articles: %v", ds.Identity) - sql = `DROP INDEX idx_user_email;` + sql = `DROP INDEX idx_user_email ON users;` req, err = rel.ParseSql(sql) - assert.Equal(t, nil, err) - assert.NotEqual(t, nil, req) + assert.NoError(t, err) + assert.NotNil(t, req) ds, ok = req.(*rel.SqlDrop) assert.True(t, ok, "wanted SqlDrop got %T", req) assert.Equal(t, lex.TokenDrop, ds.Keyword(), "Has keyword DROP") @@ -760,14 +760,14 @@ func TestSqlDrop(t *testing.T) { sql = `DROP INDEX IF EXISTS idx_user_name ON users;` req, err = rel.ParseSql(sql) - assert.Equal(t, nil, err) - assert.NotEqual(t, nil, req) + assert.NoError(t, err) + assert.NotNil(t, req) ds, ok = req.(*rel.SqlDrop) assert.True(t, ok, "wanted SqlDrop got %T", req) assert.Equal(t, lex.TokenDrop, ds.Keyword(), "Has keyword DROP") assert.Equal(t, "INDEX", ds.Tok.V, "Wanted INDEX: got %q", ds.Tok.V) assert.Equal(t, "idx_user_name", ds.Identity, "has idx_user_name: %v", ds.Identity) - assert.Equal(t, "users", ds.Table, "has table users: %v", ds.Table) + assert.Equal(t, "users", ds.Parent, "has table users: %v", ds.Parent) assert.True(t, ds.IfExists, "Expected IfExists to be true") } diff --git a/rel/sql.go b/rel/sql.go index 9fab16e..de79825 100644 --- a/rel/sql.go +++ b/rel/sql.go @@ -221,6 +221,8 @@ type ( Temp bool // Temp? Tok lex.Token // DROP [TEMP] [TABLE,VIEW,CONTINUOUSVIEW,TRIGGER] etc With u.JsonHelper + IfExists bool + Parent string } // SqlAlter SQL ALTER statement SqlAlter struct {