From a518777175d5ea2c9dbc8ec2b8fa62ab61d9633f Mon Sep 17 00:00:00 2001 From: Felipe Zorzo Date: Mon, 10 Jun 2024 21:42:07 -0300 Subject: [PATCH] feat(grammar): Support JSON_ARRAYAGG (#182) --- .../oracle-database_19/ParsingErrorCheck.json | 5 +- .../api/AggregateSqlFunctionsGrammar.kt | 14 +++- .../plugins/plsqlopen/api/PlSqlKeyword.kt | 1 + .../api/SingleRowSqlFunctionsGrammar.kt | 2 +- .../api/expressions/JsonArrayAggTest.kt | 75 +++++++++++++++++++ 5 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 zpa-core/src/test/kotlin/org/sonar/plugins/plsqlopen/api/expressions/JsonArrayAggTest.kt diff --git a/zpa-checks/src/integrationTest/resources/expected/oracle-database_19/ParsingErrorCheck.json b/zpa-checks/src/integrationTest/resources/expected/oracle-database_19/ParsingErrorCheck.json index a0d222e6..bffc0d1d 100644 --- a/zpa-checks/src/integrationTest/resources/expected/oracle-database_19/ParsingErrorCheck.json +++ b/zpa-checks/src/integrationTest/resources/expected/oracle-database_19/ParsingErrorCheck.json @@ -2156,9 +2156,6 @@ "JSON_ARRAY-0.sql": [ 3 ], - "JSON_ARRAYAGG-0.sql": [ - 2 - ], "JSON_OBJECTAGG-0.sql": [ 2 ], @@ -2924,4 +2921,4 @@ "storage_clause-0.sql": [ 6 ] -} \ No newline at end of file +} diff --git a/zpa-core/src/main/kotlin/org/sonar/plugins/plsqlopen/api/AggregateSqlFunctionsGrammar.kt b/zpa-core/src/main/kotlin/org/sonar/plugins/plsqlopen/api/AggregateSqlFunctionsGrammar.kt index d4182a9b..6ccda840 100644 --- a/zpa-core/src/main/kotlin/org/sonar/plugins/plsqlopen/api/AggregateSqlFunctionsGrammar.kt +++ b/zpa-core/src/main/kotlin/org/sonar/plugins/plsqlopen/api/AggregateSqlFunctionsGrammar.kt @@ -31,6 +31,7 @@ enum class AggregateSqlFunctionsGrammar : GrammarRuleKey { LISTAGG_EXPRESSION, XMLAGG_EXPRESSION, COLLECT_EXPRESSION, + JSON_ARRAYAGG_EXPRESSION, AGGREGATE_SQL_FUNCTION; companion object { @@ -58,11 +59,22 @@ enum class AggregateSqlFunctionsGrammar : GrammarRuleKey { RPARENTHESIS ) + b.rule(JSON_ARRAYAGG_EXPRESSION).define( + JSON_ARRAYAGG, LPARENTHESIS, + EXPRESSION, b.optional(FORMAT, JSON), + b.optional(ORDER_BY_CLAUSE), + b.optional(SingleRowSqlFunctionsGrammar.JSON_ON_NULL_CLAUSE), + b.optional(SingleRowSqlFunctionsGrammar.JSON_RETURNING_CLAUSE), + b.optional(STRICT), + RPARENTHESIS + ) + b.rule(AGGREGATE_SQL_FUNCTION).define( b.firstOf( LISTAGG_EXPRESSION, XMLAGG_EXPRESSION, - COLLECT_EXPRESSION + COLLECT_EXPRESSION, + JSON_ARRAYAGG_EXPRESSION ) ) } diff --git a/zpa-core/src/main/kotlin/org/sonar/plugins/plsqlopen/api/PlSqlKeyword.kt b/zpa-core/src/main/kotlin/org/sonar/plugins/plsqlopen/api/PlSqlKeyword.kt index da3c26d5..cdad5f32 100644 --- a/zpa-core/src/main/kotlin/org/sonar/plugins/plsqlopen/api/PlSqlKeyword.kt +++ b/zpa-core/src/main/kotlin/org/sonar/plugins/plsqlopen/api/PlSqlKeyword.kt @@ -277,6 +277,7 @@ enum class PlSqlKeyword(override val value: String, val isReserved: Boolean = fa JOIN("join"), JSON("json"), JSON_ARRAY("json_array"), + JSON_ARRAYAGG("json_arrayagg"), JSON_QUERY("json_query"), KEEP("keep"), KEY("key"), diff --git a/zpa-core/src/main/kotlin/org/sonar/plugins/plsqlopen/api/SingleRowSqlFunctionsGrammar.kt b/zpa-core/src/main/kotlin/org/sonar/plugins/plsqlopen/api/SingleRowSqlFunctionsGrammar.kt index 8d317ff5..f28300ca 100644 --- a/zpa-core/src/main/kotlin/org/sonar/plugins/plsqlopen/api/SingleRowSqlFunctionsGrammar.kt +++ b/zpa-core/src/main/kotlin/org/sonar/plugins/plsqlopen/api/SingleRowSqlFunctionsGrammar.kt @@ -294,7 +294,7 @@ enum class SingleRowSqlFunctionsGrammar : GrammarRuleKey { b.firstOf( b.sequence( VARCHAR2, - b.optional(LPARENTHESIS, PlSqlTokenType.INTEGER_LITERAL, b.firstOf(CHAR, BYTE), RPARENTHESIS), + b.optional(LPARENTHESIS, PlSqlTokenType.INTEGER_LITERAL, b.optional(b.firstOf(CHAR, BYTE)), RPARENTHESIS), b.optional(WITH, TYPENAME) ), b.sequence( diff --git a/zpa-core/src/test/kotlin/org/sonar/plugins/plsqlopen/api/expressions/JsonArrayAggTest.kt b/zpa-core/src/test/kotlin/org/sonar/plugins/plsqlopen/api/expressions/JsonArrayAggTest.kt new file mode 100644 index 00000000..a106f854 --- /dev/null +++ b/zpa-core/src/test/kotlin/org/sonar/plugins/plsqlopen/api/expressions/JsonArrayAggTest.kt @@ -0,0 +1,75 @@ +/** + * Z PL/SQL Analyzer + * Copyright (C) 2015-2024 Felipe Zorzo + * mailto:felipe AT felipezorzo DOT com DOT br + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.plugins.plsqlopen.api.expressions + +import com.felipebz.flr.tests.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.sonar.plugins.plsqlopen.api.PlSqlGrammar +import org.sonar.plugins.plsqlopen.api.RuleTest + +class JsonArrayAggTest : RuleTest() { + + @BeforeEach + fun init() { + setRootRule(PlSqlGrammar.EXPRESSION) + } + + @Test + fun matchesJsonArrayAgg() { + assertThat(p).matches("json_arrayagg(1)") + } + + @Test + fun matchesJsonArrayAggWithFormat() { + assertThat(p).matches("json_arrayagg('{}' format json)") + } + + @Test + fun matchesJsonArrayAggWithOrderBy() { + assertThat(p).matches("json_arrayagg(foo order by col)") + } + + @Test + fun matchesJsonArrayAggWithNullOnNull() { + assertThat(p).matches("json_arrayagg(foo null on null)") + } + + @Test + fun matchesJsonArrayAggWithAbsentOnNull() { + assertThat(p).matches("json_arrayagg(foo absent on null)") + } + + @Test + fun matchesJsonArrayAggWithReturning() { + assertThat(p).matches("json_arrayagg(foo returning json)") + } + + @Test + fun matchesJsonArrayAggWithStrict() { + assertThat(p).matches("json_arrayagg(foo strict)") + } + + @Test + fun matchesLongJsonArrayAgg() { + assertThat(p).matches("json_arrayagg(foo order by col null on null returning json strict)") + } + +}