From 08ad74d03ec2dde81790f0c871bdb812f7e5a251 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 9 Aug 2024 15:50:05 +0100 Subject: [PATCH 1/9] chore: create jdql query Signed-off-by: Otavio Santana --- .../eclipse/jnosql/query/grammar/data/JDQL.g4 | 160 ++++++++++++++++++ .../jnosql/query/grammar/method/Method.g4 | 40 +++++ 2 files changed, 200 insertions(+) create mode 100644 jnosql-communication/jnosql-communication-query/antlr4/org/eclipse/jnosql/query/grammar/data/JDQL.g4 create mode 100644 jnosql-communication/jnosql-communication-query/antlr4/org/eclipse/jnosql/query/grammar/method/Method.g4 diff --git a/jnosql-communication/jnosql-communication-query/antlr4/org/eclipse/jnosql/query/grammar/data/JDQL.g4 b/jnosql-communication/jnosql-communication-query/antlr4/org/eclipse/jnosql/query/grammar/data/JDQL.g4 new file mode 100644 index 000000000..67722070f --- /dev/null +++ b/jnosql-communication/jnosql-communication-query/antlr4/org/eclipse/jnosql/query/grammar/data/JDQL.g4 @@ -0,0 +1,160 @@ +grammar JDQL; + +statement : select_statement | update_statement | delete_statement; + +select_statement : select_clause? from_clause? where_clause? orderby_clause?; +update_statement : UPDATE entity_name set_clause where_clause?; +delete_statement : DELETE from_clause where_clause?; + +from_clause : FROM entity_name; + +where_clause : WHERE conditional_expression; + +set_clause : SET update_item (COMMA update_item)*; +update_item : state_field_path_expression EQ (scalar_expression | NULL); + +select_clause : SELECT select_list; +select_list + : state_field_path_expression (COMMA state_field_path_expression)* + | aggregate_expression + ; +aggregate_expression : COUNT '(' THIS ')'; + +orderby_clause : ORDER BY orderby_item (COMMA orderby_item)*; +orderby_item : state_field_path_expression (ASC | DESC)?; + +conditional_expression + // highest to lowest precedence + : LPAREN conditional_expression RPAREN + | null_comparison_expression + | in_expression + | between_expression + | like_expression + | comparison_expression + | NOT conditional_expression + | conditional_expression AND conditional_expression + | conditional_expression OR conditional_expression + ; + +comparison_expression : scalar_expression comparison_operator scalar_expression; +comparison_operator : EQ | GT | GTEQ | LT | LTEQ | NEQ; + +between_expression : scalar_expression NOT? BETWEEN scalar_expression AND scalar_expression; +like_expression : scalar_expression NOT? LIKE STRING; + +in_expression : state_field_path_expression NOT? IN '(' in_item (',' in_item)* ')'; +in_item : literal | enum_literal | input_parameter; // could simplify to just literal + +null_comparison_expression : state_field_path_expression IS NOT? NULL; + +scalar_expression + // highest to lowest precedence + : LPAREN scalar_expression RPAREN + | primary_expression + | scalar_expression MUL scalar_expression + | scalar_expression DIV scalar_expression + | scalar_expression PLUS scalar_expression + | scalar_expression MINUS scalar_expression + | scalar_expression CONCAT scalar_expression + ; + +primary_expression + : function_expression + | special_expression + | state_field_path_expression + | enum_literal + | input_parameter + | literal + ; + +function_expression + : ('abs(' | 'ABS(') scalar_expression ')' + | ('length(' | 'LENGTH(') scalar_expression ')' + | ('lower(' | 'LOWER(') scalar_expression ')' + | ('upper(' | 'UPPER(') scalar_expression ')' + | ('left(' | 'LEFT(') scalar_expression ',' scalar_expression ')' + | ('right(' | 'RIGHT(') scalar_expression ',' scalar_expression ')' + ; + +special_expression + : LOCAL_DATE + | LOCAL_DATETIME + | LOCAL_TIME + | TRUE + | FALSE + ; + +state_field_path_expression : IDENTIFIER (DOT IDENTIFIER)* | FULLY_QUALIFIED_IDENTIFIER; + +entity_name : IDENTIFIER; // no ambiguity + +enum_literal : IDENTIFIER (DOT IDENTIFIER)* | FULLY_QUALIFIED_IDENTIFIER; // ambiguity with state_field_path_expression resolvable semantically + +input_parameter : COLON IDENTIFIER | QUESTION INTEGER; + +literal : STRING | INTEGER | DOUBLE; + +// Tokens defined to be case-insensitive using character classes +SELECT : [sS][eE][lL][eE][cC][tT]; +UPDATE : [uU][pP][dD][aA][tT][eE]; +DELETE : [dD][eE][lL][eE][tT][eE]; +FROM : [fF][rR][oO][mM]; +WHERE : [wW][hH][eE][rR][eE]; +SET : [sS][eE][tT]; +ORDER : [oO][rR][dD][eE][rR]; +BY : [bB][yY]; +NOT : [nN][oO][tT]; +IN : [iI][nN]; +IS : [iI][sS]; +NULL : [nN][uU][lL][lL]; +COUNT : [cC][oO][uU][nN][tT]; +TRUE : [tT][rR][uU][eE]; +FALSE : [fF][aA][lL][sS][eE]; +ASC : [aA][sS][cC]; +DESC : [dD][eE][sS][cC]; +AND : [aA][nN][dD]; +OR : [oO][rR]; +LOCAL_DATE : [lL][oO][cC][aA][lL] [dD][aA][tT][eE]; +LOCAL_DATETIME : [lL][oO][cC][aA][lL] [dD][aA][tT][eE][tT][iI][mM][eE]; +LOCAL_TIME : [lL][oO][cC][aA][lL] [tT][iI][mM][eE]; +BETWEEN : [bB][eE][tT][wW][eE][eE][nN]; +LIKE : [lL][iI][kK][eE]; +THIS : [tT][hH][iI][sS]; +LOCAL : [lL][oO][cC][aA][lL]; +DATE : [dD][aA][tT][eE]; +DATETIME : [dD][aA][tT][eE][tT][iI][mM][eE]; +TIME : [tT][iI][mM][eE]; + +// Operators +EQ : '='; +GT : '>'; +LT : '<'; +NEQ : '<>'; +GTEQ : '>='; +LTEQ : '<='; +PLUS : '+'; +MINUS : '-'; +MUL : '*'; +DIV : '/'; +CONCAT : '||'; + +// Special Characters +COMMA : ','; +DOT : '.'; +LPAREN : '('; +RPAREN : ')'; +COLON : ':'; +QUESTION : '?'; + +// Identifier and literals +FULLY_QUALIFIED_IDENTIFIER : [a-zA-Z_][a-zA-Z0-9_]* (DOT [a-zA-Z_][a-zA-Z0-9_]*)+; +IDENTIFIER : [a-zA-Z_][a-zA-Z0-9_]*; +STRING : '\'' ( ~('\'' | '\\') | '\\' . | '\'\'' )* '\'' // single quoted strings with embedded single quotes handled + | '"' ( ~["\\] | '\\' . )* '"' ; // double quoted strings +INTEGER : '-'?[0-9]+; +DOUBLE : '-'?[0-9]+'.'[0-9]* | '-'?'.'[0-9]+; + +// Whitespace and Comments +WS : [ \t\r\n]+ -> skip ; +LINE_COMMENT : '//' ~[\r\n]* -> skip; +BLOCK_COMMENT : '/*' .*? '*/' -> skip; diff --git a/jnosql-communication/jnosql-communication-query/antlr4/org/eclipse/jnosql/query/grammar/method/Method.g4 b/jnosql-communication/jnosql-communication-query/antlr4/org/eclipse/jnosql/query/grammar/method/Method.g4 new file mode 100644 index 000000000..21f5c25a7 --- /dev/null +++ b/jnosql-communication/jnosql-communication-query/antlr4/org/eclipse/jnosql/query/grammar/method/Method.g4 @@ -0,0 +1,40 @@ +grammar Method; +select: selectStart where? order? EOF; +deleteBy: 'deleteBy' where? EOF; + +selectStart: 'find' limit 'By' | 'findBy' | 'countAll' | 'countBy' | 'existsBy'; +where: condition (and condition| or condition)* ; +condition: eq | gt | gte | lt | lte | between | in | like | truth | untruth | nullable | contains | endsWith | startsWith; +order: 'OrderBy' orderName (orderName)*; +orderName: variable | variable asc | variable desc; +limit: firstLimit | firstOne; +firstLimit : 'First' limitNumber; +firstOne: 'First'; +and: 'And'; +or: 'Or'; +asc: 'Asc'; +desc: 'Desc'; +truth: variable 'True'; +untruth: variable 'False'; +eq: variable | variable ignoreCase? not? 'Equals'?; +gt: variable ignoreCase? not? 'GreaterThan'; +gte: variable ignoreCase? not? 'GreaterThanEqual'; +lt: variable ignoreCase? not? 'LessThan'; +lte: variable ignoreCase? not? 'LessThanEqual'; +between: variable ignoreCase? not? 'Between'; +in: variable ignoreCase? not? 'In'; +like: variable ignoreCase? not? 'Like'; +contains: variable ignoreCase? not? 'Contains'; +endsWith: variable ignoreCase? not? 'EndsWith'; +startsWith: variable ignoreCase? not? 'StartsWith'; +nullable: variable ignoreCase? not? 'Null'; +ignoreCase: 'IgnoreCase'; +not: 'Not'; +variable: ANY_NAME; +limitNumber: INT; +ANY_NAME: [a-zA-Z_.][a-zA-Z_.0-9-]*; +WS: [ \t\r\n]+ -> skip ; +INT: [0-9]+; +fragment ESC : '\\' (["\\/bfnrt] | UNICODE) ; +fragment UNICODE : 'u' HEX HEX HEX HEX ; +fragment HEX : [0-9a-fA-F] ; \ No newline at end of file From a3aa330bcae5992f8fa76bb42677f6b1a06d17d7 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 9 Aug 2024 15:50:27 +0100 Subject: [PATCH 2/9] chore: update antlr logic Signed-off-by: Otavio Santana --- jnosql-communication/jnosql-communication-query/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jnosql-communication/jnosql-communication-query/pom.xml b/jnosql-communication/jnosql-communication-query/pom.xml index 84819245d..49a03aff0 100644 --- a/jnosql-communication/jnosql-communication-query/pom.xml +++ b/jnosql-communication/jnosql-communication-query/pom.xml @@ -48,7 +48,7 @@ antlr4-maven-plugin ${antlr4.version} - ../../antlr4 + antlr4 From 1dac17e50ceb4c2a01c50400c1669d6f2520f81f Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 9 Aug 2024 15:56:45 +0100 Subject: [PATCH 3/9] test: create scenario to like Signed-off-by: Otavio Santana --- .../SelectJakartaDataQueryProviderTest.java | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/jnosql-communication/jnosql-communication-query/src/test/java/org/eclipse/jnosql/communication/query/data/SelectJakartaDataQueryProviderTest.java b/jnosql-communication/jnosql-communication-query/src/test/java/org/eclipse/jnosql/communication/query/data/SelectJakartaDataQueryProviderTest.java index e88b18b87..efe0f59b5 100644 --- a/jnosql-communication/jnosql-communication-query/src/test/java/org/eclipse/jnosql/communication/query/data/SelectJakartaDataQueryProviderTest.java +++ b/jnosql-communication/jnosql-communication-query/src/test/java/org/eclipse/jnosql/communication/query/data/SelectJakartaDataQueryProviderTest.java @@ -538,4 +538,89 @@ void shouldReturnErrorWhenUseParenthesis(String query) { selectProvider.apply(query, "entity"); }); } + + @ParameterizedTest(name = "Should parser the query {0}") + @ValueSource(strings = "where employeeName LIKE ?1") + void shouldUseLike(String query) { + var selectQuery = selectProvider.apply(query, "entity"); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(selectQuery.fields()).isEmpty(); + soft.assertThat(selectQuery.entity()).isEqualTo("entity"); + soft.assertThat(selectQuery.orderBy()).isEmpty(); + soft.assertThat(selectQuery.where()).isNotEmpty(); + var where = selectQuery.where().orElseThrow(); + var condition = where.condition(); + soft.assertThat(condition.condition()).isEqualTo(Condition.AND); + + var conditions = (ConditionQueryValue) condition.value(); + var negation = (ConditionQueryValue)conditions.get().get(0).value(); + + var queryCondition = negation.get().get(0); + soft.assertThat(queryCondition.name()).isEqualTo("hexadecimal"); + soft.assertThat(queryCondition.value()).isEqualTo(StringQueryValue.of(" ORDER BY isn''t a keyword when inside a literal")); + var in = conditions.get().get(1); + soft.assertThat(in.condition()).isEqualTo(Condition.IN); + var value = (DataArrayQueryValue) in.value(); + soft.assertThat(value.get()).contains(StringQueryValue.of("4a"), StringQueryValue.of("4b"), StringQueryValue.of("4c")); + soft.assertThat(selectQuery.isCount()).isFalse(); + }); + } + + @ParameterizedTest(name = "Should parser the query {0}") + @ValueSource(strings = "where employeeName LIKE :employeeName") + void shouldUseLike2(String query) { + var selectQuery = selectProvider.apply(query, "entity"); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(selectQuery.fields()).isEmpty(); + soft.assertThat(selectQuery.entity()).isEqualTo("entity"); + soft.assertThat(selectQuery.orderBy()).isEmpty(); + soft.assertThat(selectQuery.where()).isNotEmpty(); + var where = selectQuery.where().orElseThrow(); + var condition = where.condition(); + soft.assertThat(condition.condition()).isEqualTo(Condition.AND); + + var conditions = (ConditionQueryValue) condition.value(); + var negation = (ConditionQueryValue)conditions.get().get(0).value(); + + var queryCondition = negation.get().get(0); + soft.assertThat(queryCondition.name()).isEqualTo("hexadecimal"); + soft.assertThat(queryCondition.value()).isEqualTo(StringQueryValue.of(" ORDER BY isn''t a keyword when inside a literal")); + var in = conditions.get().get(1); + soft.assertThat(in.condition()).isEqualTo(Condition.IN); + var value = (DataArrayQueryValue) in.value(); + soft.assertThat(value.get()).contains(StringQueryValue.of("4a"), StringQueryValue.of("4b"), StringQueryValue.of("4c")); + soft.assertThat(selectQuery.isCount()).isFalse(); + }); + } + + + @ParameterizedTest(name = "Should parser the query {0}") + @ValueSource(strings = "where employeeName LIKE = 'employeeName'") + void shouldUseLike3(String query) { + var selectQuery = selectProvider.apply(query, "entity"); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(selectQuery.fields()).isEmpty(); + soft.assertThat(selectQuery.entity()).isEqualTo("entity"); + soft.assertThat(selectQuery.orderBy()).isEmpty(); + soft.assertThat(selectQuery.where()).isNotEmpty(); + var where = selectQuery.where().orElseThrow(); + var condition = where.condition(); + soft.assertThat(condition.condition()).isEqualTo(Condition.AND); + + var conditions = (ConditionQueryValue) condition.value(); + var negation = (ConditionQueryValue)conditions.get().get(0).value(); + + var queryCondition = negation.get().get(0); + soft.assertThat(queryCondition.name()).isEqualTo("hexadecimal"); + soft.assertThat(queryCondition.value()).isEqualTo(StringQueryValue.of(" ORDER BY isn''t a keyword when inside a literal")); + var in = conditions.get().get(1); + soft.assertThat(in.condition()).isEqualTo(Condition.IN); + var value = (DataArrayQueryValue) in.value(); + soft.assertThat(value.get()).contains(StringQueryValue.of("4a"), StringQueryValue.of("4b"), StringQueryValue.of("4c")); + soft.assertThat(selectQuery.isCount()).isFalse(); + }); + } } From 36878ccc71abadd3b67605c981c11c7c9af4c82d Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 9 Aug 2024 15:57:03 +0100 Subject: [PATCH 4/9] chore: update jdql query Signed-off-by: Otavio Santana --- .../antlr4/org/eclipse/jnosql/query/grammar/data/JDQL.g4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jnosql-communication/jnosql-communication-query/antlr4/org/eclipse/jnosql/query/grammar/data/JDQL.g4 b/jnosql-communication/jnosql-communication-query/antlr4/org/eclipse/jnosql/query/grammar/data/JDQL.g4 index 67722070f..2cdb92cb8 100644 --- a/jnosql-communication/jnosql-communication-query/antlr4/org/eclipse/jnosql/query/grammar/data/JDQL.g4 +++ b/jnosql-communication/jnosql-communication-query/antlr4/org/eclipse/jnosql/query/grammar/data/JDQL.g4 @@ -40,7 +40,7 @@ comparison_expression : scalar_expression comparison_operator scalar_expression; comparison_operator : EQ | GT | GTEQ | LT | LTEQ | NEQ; between_expression : scalar_expression NOT? BETWEEN scalar_expression AND scalar_expression; -like_expression : scalar_expression NOT? LIKE STRING; +like_expression : scalar_expression NOT? LIKE (STRING | input_parameter); in_expression : state_field_path_expression NOT? IN '(' in_item (',' in_item)* ')'; in_item : literal | enum_literal | input_parameter; // could simplify to just literal From d0302c581da4e1d4828e6af60e2db4408641684d Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 9 Aug 2024 16:18:21 +0100 Subject: [PATCH 5/9] test: create converty scenario to like Signed-off-by: Otavio Santana --- .../SelectJakartaDataQueryProviderTest.java | 52 ++++--------------- 1 file changed, 11 insertions(+), 41 deletions(-) diff --git a/jnosql-communication/jnosql-communication-query/src/test/java/org/eclipse/jnosql/communication/query/data/SelectJakartaDataQueryProviderTest.java b/jnosql-communication/jnosql-communication-query/src/test/java/org/eclipse/jnosql/communication/query/data/SelectJakartaDataQueryProviderTest.java index efe0f59b5..7b33c6dcc 100644 --- a/jnosql-communication/jnosql-communication-query/src/test/java/org/eclipse/jnosql/communication/query/data/SelectJakartaDataQueryProviderTest.java +++ b/jnosql-communication/jnosql-communication-query/src/test/java/org/eclipse/jnosql/communication/query/data/SelectJakartaDataQueryProviderTest.java @@ -540,7 +540,7 @@ void shouldReturnErrorWhenUseParenthesis(String query) { } @ParameterizedTest(name = "Should parser the query {0}") - @ValueSource(strings = "where employeeName LIKE ?1") + @ValueSource(strings = "where employeeName LIKE ?1") void shouldUseLike(String query) { var selectQuery = selectProvider.apply(query, "entity"); @@ -551,19 +551,10 @@ void shouldUseLike(String query) { soft.assertThat(selectQuery.where()).isNotEmpty(); var where = selectQuery.where().orElseThrow(); var condition = where.condition(); - soft.assertThat(condition.condition()).isEqualTo(Condition.AND); - - var conditions = (ConditionQueryValue) condition.value(); - var negation = (ConditionQueryValue)conditions.get().get(0).value(); + soft.assertThat(condition.condition()).isEqualTo(Condition.LIKE); + soft.assertThat(condition.name()).isEqualTo("employeeName"); + soft.assertThat(condition.value()).isEqualTo(DefaultQueryValue.of("?1")); - var queryCondition = negation.get().get(0); - soft.assertThat(queryCondition.name()).isEqualTo("hexadecimal"); - soft.assertThat(queryCondition.value()).isEqualTo(StringQueryValue.of(" ORDER BY isn''t a keyword when inside a literal")); - var in = conditions.get().get(1); - soft.assertThat(in.condition()).isEqualTo(Condition.IN); - var value = (DataArrayQueryValue) in.value(); - soft.assertThat(value.get()).contains(StringQueryValue.of("4a"), StringQueryValue.of("4b"), StringQueryValue.of("4c")); - soft.assertThat(selectQuery.isCount()).isFalse(); }); } @@ -579,25 +570,14 @@ void shouldUseLike2(String query) { soft.assertThat(selectQuery.where()).isNotEmpty(); var where = selectQuery.where().orElseThrow(); var condition = where.condition(); - soft.assertThat(condition.condition()).isEqualTo(Condition.AND); - - var conditions = (ConditionQueryValue) condition.value(); - var negation = (ConditionQueryValue)conditions.get().get(0).value(); - - var queryCondition = negation.get().get(0); - soft.assertThat(queryCondition.name()).isEqualTo("hexadecimal"); - soft.assertThat(queryCondition.value()).isEqualTo(StringQueryValue.of(" ORDER BY isn''t a keyword when inside a literal")); - var in = conditions.get().get(1); - soft.assertThat(in.condition()).isEqualTo(Condition.IN); - var value = (DataArrayQueryValue) in.value(); - soft.assertThat(value.get()).contains(StringQueryValue.of("4a"), StringQueryValue.of("4b"), StringQueryValue.of("4c")); - soft.assertThat(selectQuery.isCount()).isFalse(); + soft.assertThat(condition.condition()).isEqualTo(Condition.LIKE); + soft.assertThat(condition.name()).isEqualTo("employeeName"); + soft.assertThat(condition.value()).isEqualTo(DefaultQueryValue.of(":employeeName")); }); } - @ParameterizedTest(name = "Should parser the query {0}") - @ValueSource(strings = "where employeeName LIKE = 'employeeName'") + @ValueSource(strings = "where employeeName LIKE 'employeeName'") void shouldUseLike3(String query) { var selectQuery = selectProvider.apply(query, "entity"); @@ -608,19 +588,9 @@ void shouldUseLike3(String query) { soft.assertThat(selectQuery.where()).isNotEmpty(); var where = selectQuery.where().orElseThrow(); var condition = where.condition(); - soft.assertThat(condition.condition()).isEqualTo(Condition.AND); - - var conditions = (ConditionQueryValue) condition.value(); - var negation = (ConditionQueryValue)conditions.get().get(0).value(); - - var queryCondition = negation.get().get(0); - soft.assertThat(queryCondition.name()).isEqualTo("hexadecimal"); - soft.assertThat(queryCondition.value()).isEqualTo(StringQueryValue.of(" ORDER BY isn''t a keyword when inside a literal")); - var in = conditions.get().get(1); - soft.assertThat(in.condition()).isEqualTo(Condition.IN); - var value = (DataArrayQueryValue) in.value(); - soft.assertThat(value.get()).contains(StringQueryValue.of("4a"), StringQueryValue.of("4b"), StringQueryValue.of("4c")); - soft.assertThat(selectQuery.isCount()).isFalse(); + soft.assertThat(condition.condition()).isEqualTo(Condition.LIKE); + soft.assertThat(condition.name()).isEqualTo("employeeName"); + soft.assertThat(condition.value()).isEqualTo(StringQueryValue.of("employeeName")); }); } } From 40a74d208210271d17d86e25471856f7c047fe27 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 9 Aug 2024 16:18:37 +0100 Subject: [PATCH 6/9] feat: create and fix where condition at like Signed-off-by: Otavio Santana --- .../communication/query/data/AbstractWhere.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/jnosql-communication/jnosql-communication-query/src/main/java/org/eclipse/jnosql/communication/query/data/AbstractWhere.java b/jnosql-communication/jnosql-communication-query/src/main/java/org/eclipse/jnosql/communication/query/data/AbstractWhere.java index c8305522c..1b79a3398 100644 --- a/jnosql-communication/jnosql-communication-query/src/main/java/org/eclipse/jnosql/communication/query/data/AbstractWhere.java +++ b/jnosql-communication/jnosql-communication-query/src/main/java/org/eclipse/jnosql/communication/query/data/AbstractWhere.java @@ -122,13 +122,12 @@ public void exitLike_expression(JDQLParser.Like_expressionContext ctx) { var contexts = ctx.scalar_expression(); var name = contexts.getText(); var contextCondition = Condition.LIKE; - var likeValueIndex = ctx.getChildCount() - 1; - var likeValue = contexts.getParent().getChild(likeValueIndex).getText(); - var literal = StringQueryValue.of(likeValue.substring(1, likeValue.length() - 1)); + QueryValue value = likeQueryValue(ctx, contexts); + if (this.condition != null && this.condition.value() instanceof ConditionQueryValue) { and = andCondition; } - checkCondition(new DefaultQueryCondition(name, contextCondition, literal), hasNot); + checkCondition(new DefaultQueryCondition(name, contextCondition, value), hasNot); and = andCondition; } @@ -274,4 +273,14 @@ private QueryCondition checkNotCondition(QueryCondition condition, boolean hasNo return condition; } } + + private static QueryValue likeQueryValue(JDQLParser.Like_expressionContext ctx, JDQLParser.Scalar_expressionContext contexts) { + if(ctx.input_parameter() != null){ + return DefaultQueryValue.of(ctx.input_parameter().getText()); + } else { + var likeValueIndex = ctx.getChildCount() - 1; + var likeValue = contexts.getParent().getChild(likeValueIndex).getText(); + return StringQueryValue.of(likeValue.substring(1, likeValue.length() - 1)); + } + } } From bddde24bd0139a9730730e59aa0296c737738477 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 9 Aug 2024 16:25:38 +0100 Subject: [PATCH 7/9] test: create scenarios to like at the query Signed-off-by: Otavio Santana --- .../SelectJakartaDataQueryProviderTest.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/jnosql-communication/jnosql-communication-query/src/test/java/org/eclipse/jnosql/communication/query/data/SelectJakartaDataQueryProviderTest.java b/jnosql-communication/jnosql-communication-query/src/test/java/org/eclipse/jnosql/communication/query/data/SelectJakartaDataQueryProviderTest.java index 7b33c6dcc..72498adeb 100644 --- a/jnosql-communication/jnosql-communication-query/src/test/java/org/eclipse/jnosql/communication/query/data/SelectJakartaDataQueryProviderTest.java +++ b/jnosql-communication/jnosql-communication-query/src/test/java/org/eclipse/jnosql/communication/query/data/SelectJakartaDataQueryProviderTest.java @@ -593,4 +593,67 @@ void shouldUseLike3(String query) { soft.assertThat(condition.value()).isEqualTo(StringQueryValue.of("employeeName")); }); } + + @ParameterizedTest(name = "Should parser the query {0}") + @ValueSource(strings = "where employeeName NOT LIKE 'employeeName'") + void shouldUseNotLike(String query) { + var selectQuery = selectProvider.apply(query, "entity"); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(selectQuery.fields()).isEmpty(); + soft.assertThat(selectQuery.entity()).isEqualTo("entity"); + soft.assertThat(selectQuery.orderBy()).isEmpty(); + soft.assertThat(selectQuery.where()).isNotEmpty(); + var where = selectQuery.where().orElseThrow(); + var condition = where.condition(); + soft.assertThat(condition.condition()).isEqualTo(Condition.NOT); + var notCondition = (ConditionQueryValue) condition.value(); + QueryCondition queryCondition = notCondition.get().get(0); + soft.assertThat(queryCondition.name()).isEqualTo("employeeName"); + soft.assertThat(queryCondition.value()).isEqualTo(StringQueryValue.of("employeeName")); + }); + } + + @ParameterizedTest(name = "Should parser the query {0}") + @ValueSource(strings = "where employeeName NOT LIKE ?1") + void shouldUseNotLike2(String query) { + var selectQuery = selectProvider.apply(query, "entity"); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(selectQuery.fields()).isEmpty(); + soft.assertThat(selectQuery.entity()).isEqualTo("entity"); + soft.assertThat(selectQuery.orderBy()).isEmpty(); + soft.assertThat(selectQuery.where()).isNotEmpty(); + var where = selectQuery.where().orElseThrow(); + var condition = where.condition(); + soft.assertThat(condition.condition()).isEqualTo(Condition.NOT); + var notCondition = (ConditionQueryValue) condition.value(); + QueryCondition queryCondition = notCondition.get().get(0); + soft.assertThat(queryCondition.condition()).isEqualTo(Condition.LIKE); + soft.assertThat(queryCondition.name()).isEqualTo("employeeName"); + soft.assertThat(queryCondition.value()).isEqualTo(DefaultQueryValue.of("?1")); + + }); + } + + @ParameterizedTest(name = "Should parser the query {0}") + @ValueSource(strings = "where employeeName NOT LIKE :employeeName") + void shouldUseNotLike3(String query) { + var selectQuery = selectProvider.apply(query, "entity"); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(selectQuery.fields()).isEmpty(); + soft.assertThat(selectQuery.entity()).isEqualTo("entity"); + soft.assertThat(selectQuery.orderBy()).isEmpty(); + soft.assertThat(selectQuery.where()).isNotEmpty(); + var where = selectQuery.where().orElseThrow(); + var condition = where.condition(); + soft.assertThat(condition.condition()).isEqualTo(Condition.NOT); + var notCondition = (ConditionQueryValue) condition.value(); + QueryCondition queryCondition = notCondition.get().get(0); + soft.assertThat(queryCondition.condition()).isEqualTo(Condition.LIKE); + soft.assertThat(queryCondition.name()).isEqualTo("employeeName"); + soft.assertThat(queryCondition.value()).isEqualTo(DefaultQueryValue.of(":employeeName")); + }); + } } From bbd76cdbda2e7e26affa4c484ea260fe324d8c29 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 9 Aug 2024 16:26:10 +0100 Subject: [PATCH 8/9] chore: remove data query jdql Signed-off-by: Otavio Santana --- .../eclipse/jnosql/query/grammar/data/JDQL.g4 | 160 ------------------ .../jnosql/query/grammar/method/Method.g4 | 40 ----- 2 files changed, 200 deletions(-) delete mode 100644 antlr4/org/eclipse/jnosql/query/grammar/data/JDQL.g4 delete mode 100644 antlr4/org/eclipse/jnosql/query/grammar/method/Method.g4 diff --git a/antlr4/org/eclipse/jnosql/query/grammar/data/JDQL.g4 b/antlr4/org/eclipse/jnosql/query/grammar/data/JDQL.g4 deleted file mode 100644 index 67722070f..000000000 --- a/antlr4/org/eclipse/jnosql/query/grammar/data/JDQL.g4 +++ /dev/null @@ -1,160 +0,0 @@ -grammar JDQL; - -statement : select_statement | update_statement | delete_statement; - -select_statement : select_clause? from_clause? where_clause? orderby_clause?; -update_statement : UPDATE entity_name set_clause where_clause?; -delete_statement : DELETE from_clause where_clause?; - -from_clause : FROM entity_name; - -where_clause : WHERE conditional_expression; - -set_clause : SET update_item (COMMA update_item)*; -update_item : state_field_path_expression EQ (scalar_expression | NULL); - -select_clause : SELECT select_list; -select_list - : state_field_path_expression (COMMA state_field_path_expression)* - | aggregate_expression - ; -aggregate_expression : COUNT '(' THIS ')'; - -orderby_clause : ORDER BY orderby_item (COMMA orderby_item)*; -orderby_item : state_field_path_expression (ASC | DESC)?; - -conditional_expression - // highest to lowest precedence - : LPAREN conditional_expression RPAREN - | null_comparison_expression - | in_expression - | between_expression - | like_expression - | comparison_expression - | NOT conditional_expression - | conditional_expression AND conditional_expression - | conditional_expression OR conditional_expression - ; - -comparison_expression : scalar_expression comparison_operator scalar_expression; -comparison_operator : EQ | GT | GTEQ | LT | LTEQ | NEQ; - -between_expression : scalar_expression NOT? BETWEEN scalar_expression AND scalar_expression; -like_expression : scalar_expression NOT? LIKE STRING; - -in_expression : state_field_path_expression NOT? IN '(' in_item (',' in_item)* ')'; -in_item : literal | enum_literal | input_parameter; // could simplify to just literal - -null_comparison_expression : state_field_path_expression IS NOT? NULL; - -scalar_expression - // highest to lowest precedence - : LPAREN scalar_expression RPAREN - | primary_expression - | scalar_expression MUL scalar_expression - | scalar_expression DIV scalar_expression - | scalar_expression PLUS scalar_expression - | scalar_expression MINUS scalar_expression - | scalar_expression CONCAT scalar_expression - ; - -primary_expression - : function_expression - | special_expression - | state_field_path_expression - | enum_literal - | input_parameter - | literal - ; - -function_expression - : ('abs(' | 'ABS(') scalar_expression ')' - | ('length(' | 'LENGTH(') scalar_expression ')' - | ('lower(' | 'LOWER(') scalar_expression ')' - | ('upper(' | 'UPPER(') scalar_expression ')' - | ('left(' | 'LEFT(') scalar_expression ',' scalar_expression ')' - | ('right(' | 'RIGHT(') scalar_expression ',' scalar_expression ')' - ; - -special_expression - : LOCAL_DATE - | LOCAL_DATETIME - | LOCAL_TIME - | TRUE - | FALSE - ; - -state_field_path_expression : IDENTIFIER (DOT IDENTIFIER)* | FULLY_QUALIFIED_IDENTIFIER; - -entity_name : IDENTIFIER; // no ambiguity - -enum_literal : IDENTIFIER (DOT IDENTIFIER)* | FULLY_QUALIFIED_IDENTIFIER; // ambiguity with state_field_path_expression resolvable semantically - -input_parameter : COLON IDENTIFIER | QUESTION INTEGER; - -literal : STRING | INTEGER | DOUBLE; - -// Tokens defined to be case-insensitive using character classes -SELECT : [sS][eE][lL][eE][cC][tT]; -UPDATE : [uU][pP][dD][aA][tT][eE]; -DELETE : [dD][eE][lL][eE][tT][eE]; -FROM : [fF][rR][oO][mM]; -WHERE : [wW][hH][eE][rR][eE]; -SET : [sS][eE][tT]; -ORDER : [oO][rR][dD][eE][rR]; -BY : [bB][yY]; -NOT : [nN][oO][tT]; -IN : [iI][nN]; -IS : [iI][sS]; -NULL : [nN][uU][lL][lL]; -COUNT : [cC][oO][uU][nN][tT]; -TRUE : [tT][rR][uU][eE]; -FALSE : [fF][aA][lL][sS][eE]; -ASC : [aA][sS][cC]; -DESC : [dD][eE][sS][cC]; -AND : [aA][nN][dD]; -OR : [oO][rR]; -LOCAL_DATE : [lL][oO][cC][aA][lL] [dD][aA][tT][eE]; -LOCAL_DATETIME : [lL][oO][cC][aA][lL] [dD][aA][tT][eE][tT][iI][mM][eE]; -LOCAL_TIME : [lL][oO][cC][aA][lL] [tT][iI][mM][eE]; -BETWEEN : [bB][eE][tT][wW][eE][eE][nN]; -LIKE : [lL][iI][kK][eE]; -THIS : [tT][hH][iI][sS]; -LOCAL : [lL][oO][cC][aA][lL]; -DATE : [dD][aA][tT][eE]; -DATETIME : [dD][aA][tT][eE][tT][iI][mM][eE]; -TIME : [tT][iI][mM][eE]; - -// Operators -EQ : '='; -GT : '>'; -LT : '<'; -NEQ : '<>'; -GTEQ : '>='; -LTEQ : '<='; -PLUS : '+'; -MINUS : '-'; -MUL : '*'; -DIV : '/'; -CONCAT : '||'; - -// Special Characters -COMMA : ','; -DOT : '.'; -LPAREN : '('; -RPAREN : ')'; -COLON : ':'; -QUESTION : '?'; - -// Identifier and literals -FULLY_QUALIFIED_IDENTIFIER : [a-zA-Z_][a-zA-Z0-9_]* (DOT [a-zA-Z_][a-zA-Z0-9_]*)+; -IDENTIFIER : [a-zA-Z_][a-zA-Z0-9_]*; -STRING : '\'' ( ~('\'' | '\\') | '\\' . | '\'\'' )* '\'' // single quoted strings with embedded single quotes handled - | '"' ( ~["\\] | '\\' . )* '"' ; // double quoted strings -INTEGER : '-'?[0-9]+; -DOUBLE : '-'?[0-9]+'.'[0-9]* | '-'?'.'[0-9]+; - -// Whitespace and Comments -WS : [ \t\r\n]+ -> skip ; -LINE_COMMENT : '//' ~[\r\n]* -> skip; -BLOCK_COMMENT : '/*' .*? '*/' -> skip; diff --git a/antlr4/org/eclipse/jnosql/query/grammar/method/Method.g4 b/antlr4/org/eclipse/jnosql/query/grammar/method/Method.g4 deleted file mode 100644 index 21f5c25a7..000000000 --- a/antlr4/org/eclipse/jnosql/query/grammar/method/Method.g4 +++ /dev/null @@ -1,40 +0,0 @@ -grammar Method; -select: selectStart where? order? EOF; -deleteBy: 'deleteBy' where? EOF; - -selectStart: 'find' limit 'By' | 'findBy' | 'countAll' | 'countBy' | 'existsBy'; -where: condition (and condition| or condition)* ; -condition: eq | gt | gte | lt | lte | between | in | like | truth | untruth | nullable | contains | endsWith | startsWith; -order: 'OrderBy' orderName (orderName)*; -orderName: variable | variable asc | variable desc; -limit: firstLimit | firstOne; -firstLimit : 'First' limitNumber; -firstOne: 'First'; -and: 'And'; -or: 'Or'; -asc: 'Asc'; -desc: 'Desc'; -truth: variable 'True'; -untruth: variable 'False'; -eq: variable | variable ignoreCase? not? 'Equals'?; -gt: variable ignoreCase? not? 'GreaterThan'; -gte: variable ignoreCase? not? 'GreaterThanEqual'; -lt: variable ignoreCase? not? 'LessThan'; -lte: variable ignoreCase? not? 'LessThanEqual'; -between: variable ignoreCase? not? 'Between'; -in: variable ignoreCase? not? 'In'; -like: variable ignoreCase? not? 'Like'; -contains: variable ignoreCase? not? 'Contains'; -endsWith: variable ignoreCase? not? 'EndsWith'; -startsWith: variable ignoreCase? not? 'StartsWith'; -nullable: variable ignoreCase? not? 'Null'; -ignoreCase: 'IgnoreCase'; -not: 'Not'; -variable: ANY_NAME; -limitNumber: INT; -ANY_NAME: [a-zA-Z_.][a-zA-Z_.0-9-]*; -WS: [ \t\r\n]+ -> skip ; -INT: [0-9]+; -fragment ESC : '\\' (["\\/bfnrt] | UNICODE) ; -fragment UNICODE : 'u' HEX HEX HEX HEX ; -fragment HEX : [0-9a-fA-F] ; \ No newline at end of file From 714132cb12cab010c25ef38acbb56510e1a0a0ec Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 9 Aug 2024 16:34:42 +0100 Subject: [PATCH 9/9] chore: include instruction to JDQL Signed-off-by: Otavio Santana --- CHANGELOG.adoc | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index c60830a1a..1bd801924 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -22,6 +22,7 @@ and this project adheres to https://semver.org/spec/v2.0.0.html[Semantic Version - Make the JDQL return the correct type when the select is by field - Invalid deserialization of maps with generic values - Make sure at the serialization to the field, the API does not return any communication layer, but standard Java types +- Fix the like query at the JDQL === Removed