Repository: tomarrell/lbadd
Pull request #75 (2020-03-09 16:32:42)

Implements ANALYZE stmt
This PR completes the implementation of ANALYZE stmt (https://sqlite.org/lang_analyze.html) and is a part of #66 


Patch:
diff --git a/internal/parser/simple_parser_rules.go b/internal/parser/simple_parser_rules.go
index 3beea56..a5e91f7 100644
--- a/internal/parser/simple_parser_rules.go
+++ b/internal/parser/simple_parser_rules.go
@@ -48,6 +48,8 @@ func (p *simpleParser) parseSQLStatement(r reporter) (stmt *ast.SQLStmt) {
 		stmt.DetachStmt = p.parseDetachDatabaseStmt(r)
 	case token.KeywordVacuum:
 		stmt.VacuumStmt = p.parseVacuumStmt(r)
+	case token.KeywordAnalyze:
+		stmt.AnalyzeStmt = p.parseAnalyzeStmt(r)
 	case token.StatementSeparator:
 		r.incompleteStatement()
 		p.consumeToken()
@@ -764,3 +766,46 @@ func (p *simpleParser) parseVacuumStmt(r reporter) (stmt *ast.VacuumStmt) {
 	}
 	return
 }
+
+// parseAnalyzeStmt parses the statments as defined in the spec:
+// https://sqlite.org/lang_analyze.html
+func (p *simpleParser) parseAnalyzeStmt(r reporter) (stmt *ast.AnalyzeStmt) {
+	stmt = &ast.AnalyzeStmt{}
+	p.searchNext(r, token.KeywordAnalyze)
+	next, ok := p.lookahead(r)
+	if !ok {
+		return
+	}
+	stmt.Analyze = next
+	p.consumeToken()
+
+	// optionalLookahead is used, because ANALYZE alone is a valid statement
+	next, ok = p.optionalLookahead(r)
+	if next.Type() == token.EOF {
+		return
+	}
+
+	if !ok || next.Type() == token.Literal {
+		stmt.SchemaName = next
+		stmt.TableOrIndexName = next
+		p.consumeToken()
+	} else {
+		r.unexpectedToken(token.Literal)
+		return
+	}
+
+	period, ok := p.optionalLookahead(r)
+	if !ok || period.Type() == token.EOF {
+		return
+	}
+	stmt.Period = period
+	p.consumeToken()
+
+	next, ok = p.optionalLookahead(r)
+	if !ok || next.Type() == token.EOF {
+		return
+	}
+	stmt.TableOrIndexName = next
+	p.consumeToken()
+	return
+}


Test patch:
diff --git a/internal/parser/parser_test.go b/internal/parser/parser_test.go
index 09cd040..ac3dfb9 100644
--- a/internal/parser/parser_test.go
+++ b/internal/parser/parser_test.go
@@ -233,6 +233,38 @@ func TestSingleStatementParse(t *testing.T) {
 				},
 			},
 		},
+		{
+			"analyze",
+			"ANALYZE",
+			&ast.SQLStmt{
+				AnalyzeStmt: &ast.AnalyzeStmt{
+					Analyze: token.New(1, 1, 0, 7, token.KeywordAnalyze, "ANALYZE"),
+				},
+			},
+		},
+		{
+			"ANALYZE with schema-name/table-or-index-name",
+			"ANALYZE mySchemaOrTableOrIndex",
+			&ast.SQLStmt{
+				AnalyzeStmt: &ast.AnalyzeStmt{
+					Analyze:          token.New(1, 1, 0, 7, token.KeywordAnalyze, "ANALYZE"),
+					SchemaName:       token.New(1, 9, 8, 22, token.Literal, "mySchemaOrTableOrIndex"),
+					TableOrIndexName: token.New(1, 9, 8, 22, token.Literal, "mySchemaOrTableOrIndex"),
+				},
+			},
+		},
+		{
+			"ANALYZE with schema-name/table-or-index-name elaborated",
+			"ANALYZE mySchemaOrTableOrIndex.specificAttr",
+			&ast.SQLStmt{
+				AnalyzeStmt: &ast.AnalyzeStmt{
+					Analyze:          token.New(1, 1, 0, 7, token.KeywordAnalyze, "ANALYZE"),
+					SchemaName:       token.New(1, 9, 8, 22, token.Literal, "mySchemaOrTableOrIndex"),
+					Period:           token.New(1, 31, 30, 1, token.Literal, "."),
+					TableOrIndexName: token.New(1, 32, 31, 12, token.Literal, "specificAttr"),
+				},
+			},
+		},
 	}
 	for _, input := range inputs {
 		t.Run(input.Name, func(t *testing.T) {