Skip to content

Commit

Permalink
#893 Parse explain statement (#951)
Browse files Browse the repository at this point in the history
* Add ANSI + MySQL 'explain'

* Add Snowflake explain 馃ザ

* Add the _more complicated_ postgres explain 馃И

* Update CHANGELOG 鉁忥笍

* Mypy 馃Ч
  • Loading branch information
Daniel Mateus Pires committed Apr 12, 2021
1 parent 668f235 commit f32e63f
Show file tree
Hide file tree
Showing 11 changed files with 348 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fix bug in L048 which flagged adjoining commas as failures.
- Fix bug in L019 with inline comments.
- Fix bug in L036 with multiple newlines.
- Parse explain statement ([#893](https://github.com/sqlfluff/sqlfluff/issues/893)).

## [0.5.1] - 2021-04-09
### Changed
Expand Down
3 changes: 3 additions & 0 deletions src/sqlfluff/dialects/ansi_keywords.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@
ISNULL
ISOLATION
ITERATE
JSON
K
KEY
KEY_MEMBER
Expand Down Expand Up @@ -860,7 +861,9 @@
WRITE
WRITETEXT
X509
XML
XOR
YAML
YEAR
YEAR_MONTH
ZEROFILL
Expand Down
25 changes: 25 additions & 0 deletions src/sqlfluff/dialects/dialect_ansi.py
Original file line number Diff line number Diff line change
Expand Up @@ -2550,6 +2550,7 @@ class StatementSegment(BaseSegment):
Ref("CreateModelStatementSegment"),
Ref("DropModelStatementSegment"),
Ref("DescribeStatementSegment"),
Ref("ExplainStatementSegment"),
)

def get_table_references(self):
Expand Down Expand Up @@ -2597,3 +2598,27 @@ class DescribeStatementSegment(BaseSegment):
Ref("NakedIdentifierSegment"),
Ref("ObjectReferenceSegment"),
)


@ansi_dialect.segment()
class ExplainStatementSegment(BaseSegment):
"""An `Explain` statement.
EXPLAIN explainable_stmt
"""

type = "explain_statement"

explainable_stmt = OneOf(
Ref("SelectableGrammar"),
Ref("InsertStatementSegment"),
Ref("UpdateStatementSegment"),
Ref("DeleteStatementSegment"),
)

match_grammar = StartsWith("EXPLAIN")

parse_grammar = Sequence(
"EXPLAIN",
explainable_stmt,
)
66 changes: 66 additions & 0 deletions src/sqlfluff/dialects/dialect_postgres.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@
postgres_dialect.sets("unreserved_keywords").remove("SPACE")
# Reserve WITHIN (required for the WithinGroupClauseSegment)
postgres_dialect.sets("unreserved_keywords").remove("WITHIN")
postgres_dialect.sets("unreserved_keywords").update(
[
"WITHIN",
"ANALYZE",
"VERBOSE",
"COSTS",
"BUFFERS",
"FORMAT",
"XML",
]
)
postgres_dialect.sets("reserved_keywords").add("WITHIN")
# Add the EPOCH datetime unit
postgres_dialect.sets("datetime_units").update(["EPOCH"])
Expand Down Expand Up @@ -112,3 +123,58 @@ class WithinGroupClauseSegment(BaseSegment):
"GROUP",
Bracketed(Ref("OrderByClauseSegment", optional=True)),
)


@postgres_dialect.segment(replace=True)
class ExplainStatementSegment(ansi_dialect.get_segment("ExplainStatementSegment")): # type: ignore
"""An `Explain` statement.
EXPLAIN [ ( option [, ...] ) ] statement
EXPLAIN [ ANALYZE ] [ VERBOSE ] statement
https://www.postgresql.org/docs/9.1/sql-explain.html
"""

parse_grammar = Sequence(
"EXPLAIN",
OneOf(
Sequence(
Ref.keyword("ANALYZE", optional=True),
Ref.keyword("VERBOSE", optional=True),
),
Bracketed(
Delimited(Ref("ExplainOptionSegment"), delimiter=Ref("CommaSegment"))
),
optional=True,
),
ansi_dialect.get_segment("ExplainStatementSegment").explainable_stmt,
)


@postgres_dialect.segment()
class ExplainOptionSegment(BaseSegment):
"""An `Explain` statement option.
ANALYZE [ boolean ]
VERBOSE [ boolean ]
COSTS [ boolean ]
BUFFERS [ boolean ]
FORMAT { TEXT | XML | JSON | YAML }
https://www.postgresql.org/docs/9.1/sql-explain.html
"""

type = "explain_option"

flag_segment = Sequence(
OneOf("ANALYZE", "VERBOSE", "COSTS", "BUFFERS"),
OneOf(Ref("TrueSegment"), Ref("FalseSegment"), optional=True),
)

match_grammar = OneOf(
flag_segment,
Sequence(
"FORMAT",
OneOf("TEXT", "XML", "JSON", "YAML"),
),
)
21 changes: 21 additions & 0 deletions src/sqlfluff/dialects/dialect_snowflake.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"SEED",
"TERSE",
"UNSET",
"TABULAR",
]
)

Expand Down Expand Up @@ -671,3 +672,23 @@ class AlterUserSegment(BaseSegment):
Sequence("UNSET", Delimited(Ref("ParameterNameSegment"))),
),
)


@snowflake_dialect.segment(replace=True)
class ExplainStatementSegment(ansi_dialect.get_segment("ExplainStatementSegment")): # type: ignore
"""An `Explain` statement.
EXPLAIN [ USING { TABULAR | JSON | TEXT } ] <statement>
https://docs.snowflake.com/en/sql-reference/sql/explain.html
"""

parse_grammar = Sequence(
"EXPLAIN",
Sequence(
"USING",
OneOf("TABULAR", "JSON", "TEXT"),
optional=True,
),
ansi_dialect.get_segment("ExplainStatementSegment").explainable_stmt,
)
7 changes: 7 additions & 0 deletions test/fixtures/parser/mysql/explain.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
explain select 1;

explain update tbl set foo = 1 where bar = 2;

explain delete from tbl where foo = 1;

explain insert into tbl (col1) values (123);
70 changes: 70 additions & 0 deletions test/fixtures/parser/mysql/explain.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
file:
- statement:
explain_statement:
keyword: explain
select_statement:
select_clause:
keyword: select
select_clause_element:
literal: '1'
- statement_terminator: ;
- statement:
explain_statement:
keyword: explain
update_statement:
keyword: update
table_reference:
identifier: tbl
set_clause_list:
keyword: set
set_clause:
column_reference:
identifier: foo
comparison_operator: '='
literal: '1'
where_clause:
keyword: where
expression:
column_reference:
identifier: bar
comparison_operator: '='
literal: '2'
- statement_terminator: ;
- statement:
explain_statement:
keyword: explain
delete_statement:
keyword: delete
from_clause:
keyword: from
from_expression:
from_expression_element:
table_expression:
table_reference:
identifier: tbl
where_clause:
keyword: where
expression:
column_reference:
identifier: foo
comparison_operator: '='
literal: '1'
- statement_terminator: ;
- statement:
explain_statement:
keyword: explain
insert_statement:
- keyword: insert
- keyword: into
- table_reference:
identifier: tbl
- start_bracket: (
- column_reference:
identifier: col1
- end_bracket: )
- values_clause:
keyword: values
start_bracket: (
literal: '123'
end_bracket: )
- statement_terminator: ;
11 changes: 11 additions & 0 deletions test/fixtures/parser/postgres/postgres_explain.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
explain (analyze true, costs false, verbose true, buffers true, format xml) select 1;

explain analyze verbose select 1;

explain analyze select 1;

explain (format text) select 1;

explain (format json) select 1;

explain (format yaml) select 1;
94 changes: 94 additions & 0 deletions test/fixtures/parser/postgres/postgres_explain.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
file:
- statement:
explain_statement:
- keyword: explain
- start_bracket: (
- explain_option:
keyword: analyze
literal: 'true'
- comma: ','
- explain_option:
keyword: costs
literal: 'false'
- comma: ','
- explain_option:
keyword: verbose
literal: 'true'
- comma: ','
- explain_option:
keyword: buffers
literal: 'true'
- comma: ','
- explain_option:
- keyword: format
- keyword: xml
- end_bracket: )
- select_statement:
select_clause:
keyword: select
select_clause_element:
literal: '1'
- statement_terminator: ;
- statement:
explain_statement:
- keyword: explain
- keyword: analyze
- keyword: verbose
- select_statement:
select_clause:
keyword: select
select_clause_element:
literal: '1'
- statement_terminator: ;
- statement:
explain_statement:
- keyword: explain
- keyword: analyze
- select_statement:
select_clause:
keyword: select
select_clause_element:
literal: '1'
- statement_terminator: ;
- statement:
explain_statement:
keyword: explain
start_bracket: (
explain_option:
- keyword: format
- keyword: text
end_bracket: )
select_statement:
select_clause:
keyword: select
select_clause_element:
literal: '1'
- statement_terminator: ;
- statement:
explain_statement:
keyword: explain
start_bracket: (
explain_option:
- keyword: format
- keyword: json
end_bracket: )
select_statement:
select_clause:
keyword: select
select_clause_element:
literal: '1'
- statement_terminator: ;
- statement:
explain_statement:
keyword: explain
start_bracket: (
explain_option:
- keyword: format
- keyword: yaml
end_bracket: )
select_statement:
select_clause:
keyword: select
select_clause_element:
literal: '1'
- statement_terminator: ;
7 changes: 7 additions & 0 deletions test/fixtures/parser/snowflake/snowflake_explain.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
explain using tabular select 1;

explain using json select 1;

explain using text select 1;

explain select 1;

0 comments on commit f32e63f

Please sign in to comment.