Repository: tomarrell/lbadd
Pull request #101 (2020-04-09 17:38:21)

Patches errors for handling stand alone SELECT statements
`SELECT * FROM users` and statements of the sort weren't being parsed due to `lookahead`s being used instead of `optionalLookahead`s.
This PR fixes the errors and parses statements of the sort given above.


Patch:
diff --git a/internal/parser/simple_parser_rules.go b/internal/parser/simple_parser_rules.go
index 8bc2d78..3baabaf 100644
--- a/internal/parser/simple_parser_rules.go
+++ b/internal/parser/simple_parser_rules.go
@@ -30,7 +30,7 @@ func (p *simpleParser) parseSQLStatement(r reporter) (stmt *ast.SQLStmt) {
 	}
 
 	// according to the grammar, these are the tokens that initiate a statement
-	p.searchNext(r, token.StatementSeparator, token.EOF, token.KeywordAlter, token.KeywordAnalyze, token.KeywordAttach, token.KeywordBegin, token.KeywordCommit, token.KeywordCreate, token.KeywordDelete, token.KeywordDetach, token.KeywordDrop, token.KeywordEnd, token.KeywordInsert, token.KeywordPragma, token.KeywordReindex, token.KeywordRelease, token.KeywordRollback, token.KeywordSavepoint, token.KeywordSelect, token.KeywordUpdate, token.KeywordWith, token.KeywordVacuum)
+	p.searchNext(r, token.StatementSeparator, token.EOF, token.KeywordAlter, token.KeywordAnalyze, token.KeywordAttach, token.KeywordBegin, token.KeywordCommit, token.KeywordCreate, token.KeywordDelete, token.KeywordDetach, token.KeywordDrop, token.KeywordEnd, token.KeywordInsert, token.KeywordPragma, token.KeywordReindex, token.KeywordRelease, token.KeywordRollback, token.KeywordSavepoint, token.KeywordSelect, token.KeywordUpdate, token.KeywordVacuum, token.KeywordValues, token.KeywordWith)
 
 	next, ok := p.unsafeLowLevelLookahead()
 	if !ok {
@@ -60,8 +60,12 @@ func (p *simpleParser) parseSQLStatement(r reporter) (stmt *ast.SQLStmt) {
 		stmt.CommitStmt = p.parseCommitStmt(r)
 	case token.KeywordRollback:
 		stmt.RollbackStmt = p.parseRollbackStmt(r)
+	case token.KeywordSelect:
+		stmt.SelectStmt = p.parseSelectStmt(r)
 	case token.KeywordVacuum:
 		stmt.VacuumStmt = p.parseVacuumStmt(r)
+	case token.KeywordValues:
+		stmt.SelectStmt = p.parseSelectStmt(r)
 	case token.KeywordWith:
 		stmt.DeleteStmt = p.parseDeleteStmt(r)
 	case token.StatementSeparator:
@@ -1720,15 +1724,15 @@ func (p *simpleParser) parseWithClause(r reporter) (withClause *ast.WithClause)
 		p.consumeToken()
 	}
 	for {
-		next, ok = p.lookahead(r)
-		if !ok {
+		next, ok = p.optionalLookahead(r)
+		if !ok || next.Type() == token.EOF || next.Type() == token.StatementSeparator {
 			return
 		}
 		if next.Type() == token.Literal {
 			withClause.RecursiveCte = append(withClause.RecursiveCte, p.parseRecursiveCte(r))
 		}
-		next, ok = p.lookahead(r)
-		if !ok {
+		next, ok = p.optionalLookahead(r)
+		if !ok || next.Type() == token.EOF || next.Type() == token.StatementSeparator {
 			return
 		}
 		if next.Value() == "," {
@@ -2174,8 +2178,8 @@ func (p *simpleParser) parseSelectCore(r reporter) (stmt *ast.SelectCore) {
 			}
 		}
 
-		next, ok = p.lookahead(r)
-		if !ok {
+		next, ok = p.optionalLookahead(r)
+		if !ok || next.Type() == token.EOF || next.Type() == token.StatementSeparator {
 			return
 		}
 		if next.Type() == token.KeywordFrom {
@@ -2198,8 +2202,8 @@ func (p *simpleParser) parseSelectCore(r reporter) (stmt *ast.SelectCore) {
 			}
 		}
 
-		next, ok = p.lookahead(r)
-		if !ok {
+		next, ok = p.optionalLookahead(r)
+		if !ok || next.Type() == token.EOF || next.Type() == token.StatementSeparator {
 			return
 		}
 		if next.Type() == token.KeywordWhere {
@@ -2208,8 +2212,8 @@ func (p *simpleParser) parseSelectCore(r reporter) (stmt *ast.SelectCore) {
 			stmt.Expr1 = p.parseExpression(r)
 		}
 
-		next, ok = p.lookahead(r)
-		if !ok {
+		next, ok = p.optionalLookahead(r)
+		if !ok || next.Type() == token.EOF || next.Type() == token.StatementSeparator {
 			return
 		}
 		if next.Type() == token.KeywordGroup {
@@ -2247,8 +2251,8 @@ func (p *simpleParser) parseSelectCore(r reporter) (stmt *ast.SelectCore) {
 			}
 		}
 
-		next, ok = p.lookahead(r)
-		if !ok {
+		next, ok = p.optionalLookahead(r)
+		if !ok || next.Type() == token.EOF || next.Type() == token.StatementSeparator {
 			return
 		}
 		if next.Type() == token.KeywordWindow {
@@ -2279,9 +2283,9 @@ func (p *simpleParser) parseSelectCore(r reporter) (stmt *ast.SelectCore) {
 		}
 		if next.Value() == "(" {
 			for {
-				stmt.ParenthesizedExpressions = append(stmt.ParenthesizedExpressions, p.parseParenthesizeExpression(r))
-				next, ok = p.lookahead(r)
-				if !ok {
+				stmt.ParenthesizedExpressions = append(stmt.ParenthesizedExpressions, p.parseParenthesizedExpression(r))
+				next, ok = p.optionalLookahead(r)
+				if !ok || next.Type() == token.EOF || next.Type() == token.StatementSeparator {
 					return
 				}
 				if next.Value() == "," {
@@ -2773,7 +2777,7 @@ func (p *simpleParser) parseFrameSpec(r reporter) (stmt *ast.FrameSpec) {
 	return
 }
 
-func (p *simpleParser) parseParenthesizeExpression(r reporter) (stmt *ast.ParenthesizedExpressions) {
+func (p *simpleParser) parseParenthesizedExpression(r reporter) (stmt *ast.ParenthesizedExpressions) {
 	stmt = &ast.ParenthesizedExpressions{}
 	next, ok := p.lookahead(r)
 	if !ok {


Test patch:
diff --git a/internal/parser/parser_test.go b/internal/parser/parser_test.go
index 3c57222..e12861d 100644
--- a/internal/parser/parser_test.go
+++ b/internal/parser/parser_test.go
@@ -5942,6 +5942,54 @@ func TestSingleStatementParse(t *testing.T) {
 				},
 			},
 		},
+		{
+			`SELECT standalone`,
+			"SELECT * FROM users",
+			&ast.SQLStmt{
+				SelectStmt: &ast.SelectStmt{
+					SelectCore: []*ast.SelectCore{
+						&ast.SelectCore{
+							Select: token.New(1, 1, 0, 6, token.KeywordSelect, "SELECT"),
+							ResultColumn: []*ast.ResultColumn{
+								&ast.ResultColumn{
+									Asterisk: token.New(1, 8, 7, 1, token.BinaryOperator, "*"),
+								},
+							},
+							From: token.New(1, 10, 9, 4, token.KeywordFrom, "FROM"),
+							JoinClause: &ast.JoinClause{
+								TableOrSubquery: &ast.TableOrSubquery{
+									TableName: token.New(1, 15, 14, 5, token.Literal, "users"),
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+		{
+			`SELECT standalone with VALUES`,
+			"VALUES (expr)",
+			&ast.SQLStmt{
+				SelectStmt: &ast.SelectStmt{
+					SelectCore: []*ast.SelectCore{
+						&ast.SelectCore{
+							Values: token.New(1, 1, 0, 6, token.KeywordValues, "VALUES"),
+							ParenthesizedExpressions: []*ast.ParenthesizedExpressions{
+								&ast.ParenthesizedExpressions{
+									LeftParen: token.New(1, 8, 7, 1, token.Delimiter, "("),
+									Exprs: []*ast.Expr{
+										&ast.Expr{
+											LiteralValue: token.New(1, 9, 8, 4, token.Literal, "expr"),
+										},
+									},
+									RightParen: token.New(1, 13, 12, 1, token.Delimiter, ")"),
+								},
+							},
+						},
+					},
+				},
+			},
+		},
 	}
 	for _, input := range inputs {
 		t.Run(input.Name, func(t *testing.T) {