diff --git a/sql/catalyst/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBase.g4 b/sql/catalyst/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBase.g4 index 2bc71476aba02..ee14a01c64777 100644 --- a/sql/catalyst/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBase.g4 +++ b/sql/catalyst/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBase.g4 @@ -61,6 +61,22 @@ grammar SqlBase; * When true, the behavior of keywords follows ANSI SQL standard. */ public boolean SQL_standard_keyword_behavior = false; + + /** + * This method will be called when we see '/*' and try to match it as a bracketed comment. + * If the next character is '+', it should be parsed as hint later, and we cannot match + * it as a bracketed comment. + * + * Returns true if the next character is '+'. + */ + public boolean isHint() { + int nextChar = _input.LA(1); + if (nextChar == '+') { + return true; + } else { + return false; + } + } } singleStatement @@ -1796,12 +1812,8 @@ SIMPLE_COMMENT : '--' ~[\r\n]* '\r'? '\n'? -> channel(HIDDEN) ; -BRACKETED_EMPTY_COMMENT - : '/**/' -> channel(HIDDEN) - ; - BRACKETED_COMMENT - : '/*' ~[+] .*? '*/' -> channel(HIDDEN) + : '/*' {!isHint()}? (BRACKETED_COMMENT|.)*? '*/' -> channel(HIDDEN) ; WS diff --git a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/PlanParserSuite.scala b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/PlanParserSuite.scala index 875096f615241..7382ef64d0fc4 100644 --- a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/PlanParserSuite.scala +++ b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/parser/PlanParserSuite.scala @@ -55,6 +55,104 @@ class PlanParserSuite extends AnalysisTest { With(plan, ctes) } + test("single comment") { + val plan = table("a").select(star()) + assertEqual("-- single comment\nSELECT * FROM a", plan) + } + + test("bracketed comment case one") { + val plan = table("a").select(star()) + assertEqual( + """ + |/* This is an example of SQL which should not execute: + | * select 'multi-line'; + | */ + |SELECT * FROM a + """.stripMargin, plan) + } + + test("bracketed comment case two") { + val plan = table("a").select(star()) + assertEqual( + """ + |/* + |SELECT 'trailing' as x1; -- inside block comment + |*/ + |SELECT * FROM a + """.stripMargin, plan) + } + + test("nested bracketed comment case one") { + val plan = table("a").select(star()) + assertEqual( + """ + |/* This block comment surrounds a query which itself has a block comment... + |SELECT /* embedded single line */ 'embedded' AS x2; + |*/ + |SELECT * FROM a + """.stripMargin, plan) + } + + test("nested bracketed comment case two") { + val plan = table("a").select(star()) + assertEqual( + """ + |SELECT -- continued after the following block comments... + |/* Deeply nested comment. + | This includes a single apostrophe to make sure we aren't decoding this part as a string. + |SELECT 'deep nest' AS n1; + |/* Second level of nesting... + |SELECT 'deeper nest' as n2; + |/* Third level of nesting... + |SELECT 'deepest nest' as n3; + |*/ + |Hoo boy. Still two deep... + |*/ + |Now just one deep... + |*/ + |* FROM a + """.stripMargin, plan) + } + + test("nested bracketed comment case three") { + val plan = table("a").select(star()) + assertEqual( + """ + |/* This block comment surrounds a query which itself has a block comment... + |//* I am a nested bracketed comment. + |*/ + |*/ + |SELECT * FROM a + """.stripMargin, plan) + } + + test("nested bracketed comment case four") { + val plan = table("a").select(star()) + assertEqual( + """ + |/*/**/*/ + |SELECT * FROM a + """.stripMargin, plan) + } + + test("nested bracketed comment case five") { + val plan = table("a").select(star()) + assertEqual( + """ + |/*/*abc*/*/ + |SELECT * FROM a + """.stripMargin, plan) + } + + test("nested bracketed comment case six") { + val plan = table("a").select(star()) + assertEqual( + """ + |/*/*foo*//*bar*/*/ + |SELECT * FROM a + """.stripMargin, plan) + } + test("case insensitive") { val plan = table("a").select(star()) assertEqual("sELEct * FroM a", plan) diff --git a/sql/core/src/test/resources/sql-tests/inputs/comments.sql b/sql/core/src/test/resources/sql-tests/inputs/comments.sql new file mode 100644 index 0000000000000..19f11de22dfd1 --- /dev/null +++ b/sql/core/src/test/resources/sql-tests/inputs/comments.sql @@ -0,0 +1,90 @@ +-- Test comments. + +-- the first case of bracketed comment +--QUERY-DELIMITER-START +/* This is the first example of bracketed comment. +SELECT 'ommented out content' AS first; +*/ +SELECT 'selected content' AS first; +--QUERY-DELIMITER-END + +-- the second case of bracketed comment +--QUERY-DELIMITER-START +/* This is the second example of bracketed comment. +SELECT '/', 'ommented out content' AS second; +*/ +SELECT '/', 'selected content' AS second; +--QUERY-DELIMITER-END + +-- the third case of bracketed comment +--QUERY-DELIMITER-START +/* This is the third example of bracketed comment. + *SELECT '*', 'ommented out content' AS third; + */ +SELECT '*', 'selected content' AS third; +--QUERY-DELIMITER-END + +-- the first case of empty bracketed comment +--QUERY-DELIMITER-START +/**/ +SELECT 'selected content' AS fourth; +--QUERY-DELIMITER-END + +-- the first case of nested bracketed comment +--QUERY-DELIMITER-START +/* This is the first example of nested bracketed comment. +/* I am a nested bracketed comment.*/ +*/ +SELECT 'selected content' AS fifth; +--QUERY-DELIMITER-END + +-- the second case of nested bracketed comment +--QUERY-DELIMITER-START +/* This is the second example of nested bracketed comment. +/* I am a nested bracketed comment. + */ + */ +SELECT 'selected content' AS sixth; +--QUERY-DELIMITER-END + +-- the third case of nested bracketed comment +--QUERY-DELIMITER-START +/* + * This is the third example of nested bracketed comment. + /* + * I am a nested bracketed comment. + */ + */ +SELECT 'selected content' AS seventh; +--QUERY-DELIMITER-END + +-- the fourth case of nested bracketed comment +--QUERY-DELIMITER-START +/* + * This is the fourth example of nested bracketed comment. +SELECT /* I am a nested bracketed comment.*/ * FROM testData; + */ +SELECT 'selected content' AS eighth; +--QUERY-DELIMITER-END + +-- the fifth case of nested bracketed comment +--QUERY-DELIMITER-START +SELECT /* + * This is the fifth example of nested bracketed comment. +/* I am a second level of nested bracketed comment. +/* I am a third level of nested bracketed comment. +Other information of third level. +SELECT 'ommented out content' AS ninth; +*/ +Other information of second level. +*/ +Other information of first level. +*/ +'selected content' AS ninth; +--QUERY-DELIMITER-END + +-- the first case of empty nested bracketed comment +--QUERY-DELIMITER-START +/*/**/*/ +SELECT 'selected content' AS tenth; +--QUERY-DELIMITER-END diff --git a/sql/core/src/test/resources/sql-tests/inputs/postgreSQL/comments.sql b/sql/core/src/test/resources/sql-tests/inputs/postgreSQL/comments.sql index 1a454179ef79f..b4614bf2e4693 100644 --- a/sql/core/src/test/resources/sql-tests/inputs/postgreSQL/comments.sql +++ b/sql/core/src/test/resources/sql-tests/inputs/postgreSQL/comments.sql @@ -12,14 +12,12 @@ SELECT /* both embedded and trailing single line */ 'both' AS third; -- trailing SELECT 'before multi-line' AS fourth; --QUERY-DELIMITER-START --- [SPARK-28880] ANSI SQL: Bracketed comments /* This is an example of SQL which should not execute: * select 'multi-line'; */ SELECT 'after multi-line' AS fifth; --QUERY-DELIMITER-END --- [SPARK-28880] ANSI SQL: Bracketed comments -- -- Nested comments -- @@ -47,4 +45,5 @@ Now just one deep... */ 'deeply nested example' AS sixth; --QUERY-DELIMITER-END -/* and this is the end of the file */ +-- [SPARK-30824] Support submit sql content only contains comments. +-- /* and this is the end of the file */ diff --git a/sql/core/src/test/resources/sql-tests/results/comments.sql.out b/sql/core/src/test/resources/sql-tests/results/comments.sql.out new file mode 100644 index 0000000000000..fd58a33595fe6 --- /dev/null +++ b/sql/core/src/test/resources/sql-tests/results/comments.sql.out @@ -0,0 +1,121 @@ +-- Automatically generated by SQLQueryTestSuite +-- Number of queries: 10 + + +-- !query +/* This is the first example of bracketed comment. +SELECT 'ommented out content' AS first; +*/ +SELECT 'selected content' AS first +-- !query schema +struct +-- !query output +selected content + + +-- !query +/* This is the second example of bracketed comment. +SELECT '/', 'ommented out content' AS second; +*/ +SELECT '/', 'selected content' AS second +-- !query schema +struct +-- !query output +/ selected content + + +-- !query +/* This is the third example of bracketed comment. + *SELECT '*', 'ommented out content' AS third; + */ +SELECT '*', 'selected content' AS third +-- !query schema +struct<*:string,third:string> +-- !query output +* selected content + + +-- !query +/**/ +SELECT 'selected content' AS fourth +-- !query schema +struct +-- !query output +selected content + + +-- !query +/* This is the first example of nested bracketed comment. +/* I am a nested bracketed comment.*/ +*/ +SELECT 'selected content' AS fifth +-- !query schema +struct +-- !query output +selected content + + +-- !query +/* This is the second example of nested bracketed comment. +/* I am a nested bracketed comment. + */ + */ +SELECT 'selected content' AS sixth +-- !query schema +struct +-- !query output +selected content + + +-- !query +/* + * This is the third example of nested bracketed comment. + /* + * I am a nested bracketed comment. + */ + */ +SELECT 'selected content' AS seventh +-- !query schema +struct +-- !query output +selected content + + +-- !query +/* + * This is the fourth example of nested bracketed comment. +SELECT /* I am a nested bracketed comment.*/ * FROM testData; + */ +SELECT 'selected content' AS eighth +-- !query schema +struct +-- !query output +selected content + + +-- !query +SELECT /* + * This is the fifth example of nested bracketed comment. +/* I am a second level of nested bracketed comment. +/* I am a third level of nested bracketed comment. +Other information of third level. +SELECT 'ommented out content' AS ninth; +*/ +Other information of second level. +*/ +Other information of first level. +*/ +'selected content' AS ninth +-- !query schema +struct +-- !query output +selected content + + +-- !query +/*/**/*/ +SELECT 'selected content' AS tenth +-- !query schema +struct +-- !query output +selected content diff --git a/sql/core/src/test/resources/sql-tests/results/postgreSQL/comments.sql.out b/sql/core/src/test/resources/sql-tests/results/postgreSQL/comments.sql.out index 637c5561bd940..d583a4a5d420a 100644 --- a/sql/core/src/test/resources/sql-tests/results/postgreSQL/comments.sql.out +++ b/sql/core/src/test/resources/sql-tests/results/postgreSQL/comments.sql.out @@ -1,5 +1,5 @@ -- Automatically generated by SQLQueryTestSuite --- Number of queries: 7 +-- Number of queries: 6 -- !query @@ -69,47 +69,6 @@ Now just one deep... */ 'deeply nested example' AS sixth -- !query schema -struct<> +struct -- !query output -org.apache.spark.sql.catalyst.parser.ParseException - -mismatched input ''embedded'' expecting {'(', 'ADD', 'ALTER', 'ANALYZE', 'CACHE', 'CLEAR', 'COMMENT', 'COMMIT', 'CREATE', 'DELETE', 'DESC', 'DESCRIBE', 'DFS', 'DROP', 'EXPLAIN', 'EXPORT', 'FROM', 'GRANT', 'IMPORT', 'INSERT', 'LIST', 'LOAD', 'LOCK', 'MAP', 'MERGE', 'MSCK', 'REDUCE', 'REFRESH', 'REPLACE', 'RESET', 'REVOKE', 'ROLLBACK', 'SELECT', 'SET', 'SHOW', 'START', 'TABLE', 'TRUNCATE', 'UNCACHE', 'UNLOCK', 'UPDATE', 'USE', 'VALUES', 'WITH'}(line 6, pos 34) - -== SQL == -/* -SELECT 'trailing' as x1; -- inside block comment -*/ - -/* This block comment surrounds a query which itself has a block comment... -SELECT /* embedded single line */ 'embedded' AS x2; -----------------------------------^^^ -*/ - -SELECT -- continued after the following block comments... -/* Deeply nested comment. - This includes a single apostrophe to make sure we aren't decoding this part as a string. -SELECT 'deep nest' AS n1; -/* Second level of nesting... -SELECT 'deeper nest' as n2; -/* Third level of nesting... -SELECT 'deepest nest' as n3; -*/ -Hoo boy. Still two deep... -*/ -Now just one deep... -*/ -'deeply nested example' AS sixth - - --- !query -/* and this is the end of the file */ --- !query schema -struct<> --- !query output -org.apache.spark.sql.catalyst.parser.ParseException - -mismatched input '' expecting {'(', 'ADD', 'ALTER', 'ANALYZE', 'CACHE', 'CLEAR', 'COMMENT', 'COMMIT', 'CREATE', 'DELETE', 'DESC', 'DESCRIBE', 'DFS', 'DROP', 'EXPLAIN', 'EXPORT', 'FROM', 'GRANT', 'IMPORT', 'INSERT', 'LIST', 'LOAD', 'LOCK', 'MAP', 'MERGE', 'MSCK', 'REDUCE', 'REFRESH', 'REPLACE', 'RESET', 'REVOKE', 'ROLLBACK', 'SELECT', 'SET', 'SHOW', 'START', 'TABLE', 'TRUNCATE', 'UNCACHE', 'UNLOCK', 'UPDATE', 'USE', 'VALUES', 'WITH'}(line 1, pos 37) - -== SQL == -/* and this is the end of the file */ --------------------------------------^^^ +deeply nested example