Skip to content

Commit f0964ab

Browse files
committed
Add support for index expressions
1 parent 82c1a53 commit f0964ab

File tree

7 files changed

+93
-22
lines changed

7 files changed

+93
-22
lines changed

lex/dialect_sql.go

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ var (
159159
{Token: TokenChange, Lexer: LexDdlAlterColumn},
160160
{Token: TokenWith, Lexer: LexJsonOrKeyValue, Optional: true},
161161
}
162-
// SqlCreate CREATE {SCHEMA | DATABASE | SOURCE | TABLE | VIEW | CONTINUOUSVIEW}
162+
// SqlCreate CREATE {SCHEMA | INDEX | DATABASE | SOURCE | TABLE | VIEW | CONTINUOUSVIEW}
163163
SqlCreate = []*Clause{
164164
{Token: TokenCreate, Lexer: LexCreate},
165165
{Token: TokenEngine, Lexer: LexDdlTableStorage, Optional: true},
@@ -397,6 +397,7 @@ func LexCreate(l *Lexer) StateFn {
397397
/*
398398
CREATE TABLE [IF NOT EXISTS] <identity> [WITH]
399399
CREATE SOURCE [IF NOT EXISTS] <identity> [WITH]
400+
CREATE INDEX [IF NOT EXISTS] <identity> [WITH]
400401
CREATE [OR REPLACE] VIEW <identity> AS <select_statement> [WITH]
401402
*/
402403

@@ -413,6 +414,11 @@ func LexCreate(l *Lexer) StateFn {
413414
l.Emit(TokenTable)
414415
l.Push("LexDdlTable", LexDdlTable)
415416
return nil
417+
case "index":
418+
l.ConsumeWord(keyWord)
419+
l.Emit(TokenIndex)
420+
l.Push("LexDdlIndex", LexDdlIndex)
421+
return nil
416422
case "source":
417423
l.ConsumeWord(keyWord)
418424
l.Emit(TokenSource)
@@ -550,6 +556,72 @@ func LexDrop(l *Lexer) StateFn {
550556
return lexNotExists
551557
}
552558

559+
// LexDdlIndex data definition language index
560+
func LexDdlIndex(l *Lexer) StateFn {
561+
562+
/*
563+
CREATE INDEX [IF NOT EXISTS] index_name (column_name,...)
564+
*/
565+
l.SkipWhiteSpaces()
566+
r := l.Next()
567+
568+
// Cover the logic and grouping
569+
switch r {
570+
case '(':
571+
// Start of columns
572+
l.Emit(TokenLeftParenthesis)
573+
l.Push("LexDdlTableColumn", LexDdlIndex)
574+
return LexColumnNames
575+
case ')':
576+
// end of columns
577+
l.Emit(TokenRightParenthesis)
578+
return LexDdlTableStorage
579+
case '-', '/': // comment?
580+
p := l.Peek()
581+
if p == '-' {
582+
l.backup()
583+
l.Push("LexDdlIndex", LexDdlIndex)
584+
return LexInlineComment
585+
}
586+
u.Warnf("unhandled comment non inline ")
587+
case ';':
588+
l.backup()
589+
return nil
590+
}
591+
592+
l.backup()
593+
word := strings.ToLower(l.PeekWord())
594+
switch word {
595+
case "if":
596+
l.Push("LexDdlIndex", LexDdlIndex)
597+
return lexNotExists
598+
case "with":
599+
l.ConsumeWord("with")
600+
l.Emit(TokenWith)
601+
return LexJsonOrKeyValue
602+
case "on":
603+
l.ConsumeWord("on")
604+
l.Emit(TokenOn)
605+
return LexDdlIndex
606+
}
607+
r = l.Peek()
608+
if r == ',' {
609+
l.Emit(TokenComma)
610+
l.Push("LexDdlIndex", l.clauseState())
611+
return LexExpressionOrIdentity
612+
}
613+
if l.isNextKeyword(word) {
614+
return nil
615+
}
616+
// ensure we don't get into a recursive death spiral here?
617+
if len(l.stack) < 10 {
618+
l.Push("LexDdlIndex", LexDdlIndex)
619+
} else {
620+
u.Errorf("Gracefully refusing to add more LexDdlIndex: ")
621+
}
622+
return LexExpressionOrIdentity
623+
}
624+
553625
// LexDdlTable data definition language table
554626
func LexDdlTable(l *Lexer) StateFn {
555627

lex/dialect_sql_parse_test.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,12 @@ package lex_test
33
import (
44
"testing"
55

6-
u "github.com/araddon/gou"
76
"github.com/stretchr/testify/assert"
87

98
"github.com/lytics/qlbridge/rel"
109
)
1110

1211
func parseSqlTest(t *testing.T, sql string) {
13-
u.Debugf("parsing sql: %s", sql)
1412
sqlRequest, err := rel.ParseSql(sql)
1513
assert.Equal(t, nil, err)
1614
assert.NotEqual(t, nil, sqlRequest, "Must parse: %s \n\t%v", sql, err)
@@ -20,7 +18,6 @@ func parseSqlTest(t *testing.T, sql string) {
2018
}
2119
}
2220
func parseSqlError(t *testing.T, sql string) {
23-
u.Debugf("parse looking for error sql: %s", sql)
2421
_, err := rel.ParseSql(sql)
2522
assert.NotEqual(t, nil, err, "Must error on parse: %s", sql)
2623
}
@@ -35,7 +32,7 @@ func TestSqlParser(t *testing.T) {
3532
parseSqlError(t, `SELECT a, tolower(b) AS b INTO newtable FROM FROM WHERE a != "hello";`)
3633
parseSqlTest(t, `
3734
SELECT a.language, a.template, Count(*) AS count
38-
FROM
35+
FROM
3936
(Select Distinct language, template FROM content WHERE language != "en" OFFSET 1) AS a
4037
Left Join users AS b
4138
On b.language = a.language AND b.template = b.template
@@ -47,10 +44,10 @@ func TestSqlParser(t *testing.T) {
4744
// CREATE
4845
parseSqlTest(t, `CREATE CONTINUOUSVIEW viewx AS SELECT a FROM tbl;`)
4946
parseSqlError(t, `CREATE FAKEITEM viewx;`)
50-
parseSqlTest(t, `CREATE OR REPLACE VIEW viewx
51-
AS SELECT a, b FROM mydb.tbl
47+
parseSqlTest(t, `CREATE OR REPLACE VIEW viewx
48+
AS SELECT a, b FROM mydb.tbl
5249
WITH stuff = "hello";`)
53-
parseSqlTest(t, `CREATE TABLE articles
50+
parseSqlTest(t, `CREATE TABLE articles
5451
--comment-here
5552
(
5653
ID int(11) NOT NULL AUTO_INCREMENT,

lex/dialect_sql_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ func TestLexSqlCreate(t *testing.T) {
9696

9797
// CREATE {DATABASE | SCHEMA | SOURCE | VIEW | CONTINUOUSVIEW} [IF NOT EXISTS] db_name
9898
// [create_specification] ...
99-
verifyTokens(t, `CREATE SCHEMA IF NOT EXISTS mysource
99+
verifyTokens(t, `CREATE SCHEMA IF NOT EXISTS mysource
100100
WITH stuff = "hello";
101101
`,
102102
[]Token{
@@ -145,8 +145,8 @@ func TestLexSqlCreate(t *testing.T) {
145145
tv(TokenValue, "hello"),
146146
})
147147

148-
verifyTokens(t, `CREATE OR REPLACE VIEW viewx
149-
AS SELECT a, b FROM mydb.tbl
148+
verifyTokens(t, `CREATE OR REPLACE VIEW viewx
149+
AS SELECT a, b FROM mydb.tbl
150150
WITH stuff = "hello";`,
151151
[]Token{
152152
tv(TokenCreate, "CREATE"),
@@ -167,7 +167,7 @@ func TestLexSqlCreate(t *testing.T) {
167167
tv(TokenValue, "hello"),
168168
})
169169

170-
verifyTokens(t, `CREATE TABLE articles
170+
verifyTokens(t, `CREATE TABLE articles
171171
(
172172
ID int(11) NOT NULL AUTO_INCREMENT,
173173
Email char(150) NOT NULL DEFAULT '',
@@ -240,7 +240,7 @@ func TestLexSqlCreate(t *testing.T) {
240240
verifyTokens(t, `CREATE INDEX IF NOT EXISTS my_index ON my_table (col1, col2) WITH { "key": "value" };`,
241241
[]Token{
242242
tv(TokenCreate, "CREATE"),
243-
tv(TokenIdentity, "INDEX"), // Assuming INDEX is treated as an Identity for now
243+
tv(TokenIndex, "INDEX"), // Assuming INDEX is treated as an Identity for now
244244
tv(TokenIf, "IF"),
245245
tv(TokenNegate, "NOT"),
246246
tv(TokenExists, "EXISTS"),
@@ -253,11 +253,11 @@ func TestLexSqlCreate(t *testing.T) {
253253
tv(TokenIdentity, "col2"),
254254
tv(TokenRightParenthesis, ")"),
255255
tv(TokenWith, "WITH"),
256-
tv(TokenLeftBracket, "{"),
257-
tv(TokenValue, "key"),
256+
tv(TokenLeftBrace, "{"),
257+
tv(TokenIdentity, "key"),
258258
tv(TokenColon, ":"),
259259
tv(TokenValue, "value"),
260-
tv(TokenRightBracket, "}"),
260+
tv(TokenRightBrace, "}"),
261261
})
262262
}
263263
func TestLexSqlDrop(t *testing.T) {

lex/lexer_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,8 @@ func verifyTokens(t *testing.T, sql string, tokens []Token) {
276276
for _, goodToken := range tokens {
277277
tok := l.NextToken()
278278
//u.Debugf("got:%v want:%v", tok, goodToken)
279-
assert.Equal(t, tok.T, goodToken.T, "want='%v' has %v ", goodToken.T, tok)
280-
assert.Equal(t, tok.V, goodToken.V, "want='%v' has %v ", goodToken.V, tok)
279+
assert.Equal(t, tok.T, goodToken.T, "want='%q' has %q ", goodToken.T, tok)
280+
assert.Equal(t, tok.V, goodToken.V, "want='%q' has %q ", goodToken.V, tok)
281281
}
282282
}
283283

lex/token.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ const (
178178
TokenView TokenType = 404 // VIEW
179179
TokenContinuousView TokenType = 405 // CONTINUOUSVIEW
180180
TokenTemp TokenType = 406 // TEMP or TEMPORARY
181+
TokenIndex TokenType = 407 // INDEX
181182

182183
// ddl other
183184
TokenChange TokenType = 410 // change

rel/parse_sql.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -716,9 +716,9 @@ func (m *Sqlbridge) parseCreate() (*SqlCreate, error) {
716716
}
717717
req.OrReplace = true
718718
}
719-
// CREATE {DATABASE|SCHEMA|TABLE|VIEW|SOURCE|CONTINUOUSVIEW} <identity>
719+
// CREATE {INDEX|DATABASE|SCHEMA|TABLE|VIEW|SOURCE|CONTINUOUSVIEW} <identity>
720720
switch m.Cur().T {
721-
case lex.TokenTable, lex.TokenSource, lex.TokenDatabase, lex.TokenSchema:
721+
case lex.TokenTable, lex.TokenSource, lex.TokenIndex, lex.TokenDatabase, lex.TokenSchema:
722722
req.Tok = m.Next()
723723
case lex.TokenView, lex.TokenContinuousView:
724724
req.Tok = m.Next()
@@ -744,7 +744,7 @@ func (m *Sqlbridge) parseCreate() (*SqlCreate, error) {
744744
req.Select = sel
745745
return req, nil
746746
default:
747-
return nil, m.ErrMsg("Expected view, table, source, schema, database, continuousview for CREATE got")
747+
return nil, m.ErrMsg("Expected index, view, table, source, schema, database, continuousview for CREATE got")
748748
}
749749

750750
// [IF NOT EXISTS]
@@ -803,6 +803,7 @@ func (m *Sqlbridge) parseCreate() (*SqlCreate, error) {
803803
// just with
804804
case lex.TokenSchema:
805805
// just with for now
806+
case lex.TokenIndex:
806807
default:
807808
return nil, fmt.Errorf("not implemented %v", req.Tok.V)
808809
}

rel/sql.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ type (
205205
SqlCreate struct {
206206
Raw string // full original raw statement
207207
Identity string // identity of table, view, etc
208-
Tok lex.Token // CREATE [TABLE,VIEW,CONTINUOUSVIEW,TRIGGER] etc
208+
Tok lex.Token // CREATE [INDEX|TABLE,VIEW,CONTINUOUSVIEW,TRIGGER] etc
209209
OrReplace bool // OR REPLACE
210210
IfNotExists bool // IF NOT EXISTS
211211
Cols []*DdlColumn // columns

0 commit comments

Comments
 (0)