diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryRendererTests.java index f78dc6a1f..17188c06f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryRendererTests.java @@ -347,6 +347,38 @@ class EqlQueryRendererTests { assertQuery("SELECT e FROM Employee e WHERE CAST(e.salary NUMERIC(10, 2)) > 0.0"); } + @Test // GH-3136 + void substring() { + + assertQuery("select substring(c.number, 1, 2) " + // + "from Call c"); + + assertQuery("select substring(c.number, 1) " + // + "from Call c"); + } + + @Test // GH-3136 + void currentDateFunctions() { + + assertQuery("select CURRENT_DATE " + // + "from Call c "); + + assertQuery("select CURRENT_TIME " + // + "from Call c "); + + assertQuery("select CURRENT_TIMESTAMP " + // + "from Call c "); + + assertQuery("select LOCAL_DATE " + // + "from Call c "); + + assertQuery("select LOCAL_TIME " + // + "from Call c "); + + assertQuery("select LOCAL_DATETIME " + // + "from Call c "); + } + @Test void pathExpressionsNamedParametersExample() { @@ -458,18 +490,13 @@ class EqlQueryRendererTests { * @see #functionInvocationExampleWithCorrection() */ @Test - @Disabled(SPEC_FAULT + "FUNCTION calls needs a comparator") - void functionInvocationExample_SPEC_BUG() { + void functionInvocationExample() { assertQuery(""" SELECT c FROM Customer c WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) """); - } - - @Test - void functionInvocationExampleWithCorrection() { assertQuery(""" SELECT c diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlSpecificationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlSpecificationTests.java deleted file mode 100644 index df67e51b7..000000000 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlSpecificationTests.java +++ /dev/null @@ -1,937 +0,0 @@ -/* - * Copyright 2023-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.jpa.repository.query; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import org.springframework.data.jpa.repository.query.QueryRenderer.TokenRenderer; - -/** - * Tests built around examples of EQL found in the JPA spec - * https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc
- *
- * IMPORTANT: Purely verifies the parser without any transformations. - * - * @author Greg Turnquist - */ -class EqlSpecificationTests { - - private static final String SPEC_FAULT = "Disabled due to spec fault> "; - - private static String parseWithoutChanges(String query) { - - JpaQueryEnhancer.EqlQueryParser parser = JpaQueryEnhancer.EqlQueryParser.parseQuery(query); - - return TokenRenderer.render(new EqlQueryRenderer().visit(parser.getContext())); - } - - private void assertQuery(String query) { - - String slimmedDownQuery = reduceWhitespace(query); - assertThat(parseWithoutChanges(slimmedDownQuery)).isEqualTo(slimmedDownQuery); - } - - private String reduceWhitespace(String original) { - - return original // - .replaceAll("[ \\t\\n]{1,}", " ") // - .trim(); - } - - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example - */ - @Test - void joinExample1() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order AS o JOIN o.lineItems AS l - WHERE l.shipped = FALSE - """); - } - - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#identification-variables - */ - @Test - void joinExample2() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order o JOIN o.lineItems l JOIN l.product p - WHERE p.productType = 'office_supplies' - """); - } - - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#range-variable-declarations - */ - @Test - void rangeVariableDeclarations() { - - assertQuery(""" - SELECT DISTINCT o1 - FROM Order o1, Order o2 - WHERE o1.quantity > o2.quantity AND - o2.customer.lastname = 'Smith' AND - o2.customer.firstname = 'John' - """); - } - - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions - */ - @Test - void pathExpressionsExample1() { - - assertQuery(""" - SELECT i.name, VALUE(p) - FROM Item i JOIN i.photos p - WHERE KEY(p) LIKE '%egret' - """); - } - - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions - */ - @Test - void pathExpressionsExample2() { - - assertQuery(""" - SELECT i.name, p - FROM Item i JOIN i.photos p - WHERE KEY(p) LIKE '%egret' - """); - } - - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions - */ - @Test - void pathExpressionsExample3() { - - assertQuery(""" - SELECT p.vendor - FROM Employee e JOIN e.contactInfo.phones p - """); - } - - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions - */ - @Test - void pathExpressionsExample4() { - - assertQuery(""" - SELECT p.vendor - FROM Employee e JOIN e.contactInfo c JOIN c.phones p - WHERE e.contactInfo.address.zipcode = '95054' - """); - } - - @Test - void pathExpressionSyntaxExample1() { - - assertQuery(""" - SELECT DISTINCT l.product - FROM Order AS o JOIN o.lineItems l - """); - } - - @Test - void joinsExample1() { - - assertQuery(""" - SELECT c FROM Customer c, Employee e WHERE c.hatsize = e.shoesize - """); - } - - @Test - void joinsExample2() { - - assertQuery(""" - SELECT c FROM Customer c JOIN c.orders o WHERE c.status = 1 - """); - } - - @Test - void joinsInnerExample() { - - assertQuery(""" - SELECT c FROM Customer c INNER JOIN c.orders o WHERE c.status = 1 - """); - } - - @Test - void joinsInExample() { - - assertQuery(""" - SELECT OBJECT(c) FROM Customer c, IN(c.orders) o WHERE c.status = 1 - """); - } - - @Test - void doubleJoinExample() { - - assertQuery(""" - SELECT p.vendor - FROM Employee e JOIN e.contactInfo c JOIN c.phones p - WHERE c.address.zipcode = '95054' - """); - } - - @Test - void leftJoinExample() { - - assertQuery(""" - SELECT s.name, COUNT(p) - FROM Suppliers s LEFT JOIN s.products p - GROUP BY s.name - """); - } - - @Test - void leftJoinOnExample() { - - assertQuery(""" - SELECT s.name, COUNT(p) - FROM Suppliers s LEFT JOIN s.products p - ON p.status = 'inStock' - GROUP BY s.name - """); - } - - @Test - void leftJoinWhereExample() { - - assertQuery(""" - SELECT s.name, COUNT(p) - FROM Suppliers s LEFT JOIN s.products p - WHERE p.status = 'inStock' - GROUP BY s.name - """); - } - - @Test - void leftJoinFetchExample() { - - assertQuery(""" - SELECT d - FROM Department d LEFT JOIN FETCH d.employees - WHERE d.deptno = 1 - """); - } - - @Test - void collectionMemberExample() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order o JOIN o.lineItems l - WHERE l.product.productType = 'office_supplies' - """); - } - - @Test - void collectionMemberInExample() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order o, IN(o.lineItems) l - WHERE l.product.productType = 'office_supplies' - """); - } - - @Test - void fromClauseExample() { - - assertQuery(""" - SELECT o - FROM Order AS o JOIN o.lineItems l JOIN l.product p - """); - } - - @Test - void fromClauseDowncastingExample1() { - - assertQuery(""" - SELECT b.name, b.ISBN - FROM Order o JOIN TREAT(o.product AS Book) b - """); - } - - @Test - void fromClauseDowncastingExample2() { - - assertQuery(""" - SELECT e FROM Employee e JOIN TREAT(e.projects AS LargeProject) lp - WHERE lp.budget > 1000 - """); - } - - /** - * @see #fromClauseDowncastingExample3fixed() - */ - @Test - @Disabled(SPEC_FAULT + "Use double-quotes when it should be using single-quotes for a string literal") - void fromClauseDowncastingExample3_SPEC_BUG() { - - assertQuery(""" - SELECT e FROM Employee e JOIN e.projects p - WHERE TREAT(p AS LargeProject).budget > 1000 - OR TREAT(p AS SmallProject).name LIKE 'Persist%' - OR p.description LIKE "cost overrun" - """); - } - - @Test - void fromClauseDowncastingExample3fixed() { - - assertQuery(""" - SELECT e FROM Employee e JOIN e.projects p - WHERE TREAT(p AS LargeProject).budget > 1000 - OR TREAT(p AS SmallProject).name LIKE 'Persist%' - OR p.description LIKE 'cost overrun' - """); - } - - @Test - void fromClauseDowncastingExample4() { - - assertQuery(""" - SELECT e FROM Employee e - WHERE TREAT(e AS Exempt).vacationDays > 10 - OR TREAT(e AS Contractor).hours > 100 - """); - } - - @Test // GH-3136 - void substring() { - - assertQuery("select substring(c.number, 1, 2) " + // - "from Call c"); - - assertQuery("select substring(c.number, 1) " + // - "from Call c"); - } - - @Test // GH-3136 - void currentDateFunctions() { - - assertQuery("select CURRENT_DATE " + // - "from Call c "); - - assertQuery("select CURRENT_TIME " + // - "from Call c "); - - assertQuery("select CURRENT_TIMESTAMP " + // - "from Call c "); - - assertQuery("select LOCAL_DATE " + // - "from Call c "); - - assertQuery("select LOCAL_TIME " + // - "from Call c "); - - assertQuery("select LOCAL_DATETIME " + // - "from Call c "); - } - - @Test - void pathExpressionsNamedParametersExample() { - - assertQuery(""" - SELECT c - FROM Customer c - WHERE c.status = :stat - """); - } - - @Test - void betweenExpressionsExample() { - - assertQuery(""" - SELECT t - FROM CreditCard c JOIN c.transactionHistory t - WHERE c.holder.name = 'John Doe' AND INDEX(t) BETWEEN 0 AND 9 - """); - } - - @Test - void isEmptyExample() { - - assertQuery(""" - SELECT o - FROM Order o - WHERE o.lineItems IS EMPTY - """); - } - - @Test - void memberOfExample() { - - assertQuery(""" - SELECT p - FROM Person p - WHERE 'Joe' MEMBER OF p.nicknames - """); - } - - @Test - void existsSubSelectExample1() { - - assertQuery(""" - SELECT DISTINCT emp - FROM Employee emp - WHERE EXISTS (SELECT spouseEmp - FROM Employee spouseEmp - WHERE spouseEmp = emp.spouse) - """); - } - - @Test - void allExample() { - - assertQuery(""" - SELECT emp - FROM Employee emp - WHERE emp.salary > ALL (SELECT m.salary - FROM Manager m - WHERE m.department = emp.department) - """); - } - - @Test - void existsSubSelectExample2() { - - assertQuery(""" - SELECT DISTINCT emp - FROM Employee emp - WHERE EXISTS (SELECT spouseEmp - FROM Employee spouseEmp - WHERE spouseEmp = emp.spouse) - """); - } - - @Test - void subselectNumericComparisonExample1() { - - assertQuery(""" - SELECT c - FROM Customer c - WHERE (SELECT AVG(o.price) FROM c.orders o) > 100 - """); - } - - @Test - void subselectNumericComparisonExample2() { - - assertQuery(""" - SELECT goodCustomer - FROM Customer goodCustomer - WHERE goodCustomer.balanceOwed < (SELECT AVG(c.balanceOwed) / 2.0 FROM Customer c) - """); - } - - @Test - void indexExample() { - - assertQuery(""" - SELECT w.name - FROM Course c JOIN c.studentWaitlist w - WHERE c.name = 'Calculus' - AND INDEX(w) = 0 - """); - } - - /** - * @see #functionInvocationExampleWithCorrection() - */ - @Test - @Disabled(SPEC_FAULT + "FUNCTION calls needs a comparator") - void functionInvocationExample_SPEC_BUG() { - - assertQuery(""" - SELECT c - FROM Customer c - WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) - """); - } - - @Test - void functionInvocationExampleWithCorrection() { - - assertQuery(""" - SELECT c - FROM Customer c - WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) = TRUE - """); - } - - @Test - void updateCaseExample1() { - - assertQuery(""" - UPDATE Employee e - SET e.salary = - CASE WHEN e.rating = 1 THEN e.salary * 1.1 - WHEN e.rating = 2 THEN e.salary * 1.05 - ELSE e.salary * 1.01 - END - """); - } - - @Test - void updateCaseExample2() { - - assertQuery(""" - UPDATE Employee e - SET e.salary = - CASE e.rating WHEN 1 THEN e.salary * 1.1 - WHEN 2 THEN e.salary * 1.05 - ELSE e.salary * 1.01 - END - """); - } - - @Test - void selectCaseExample1() { - - assertQuery(""" - SELECT e.name, - CASE TYPE(e) WHEN Exempt THEN 'Exempt' - WHEN Contractor THEN 'Contractor' - WHEN Intern THEN 'Intern' - ELSE 'NonExempt' - END - FROM Employee e - WHERE e.dept.name = 'Engineering' - """); - } - - @Test - void selectCaseExample2() { - - assertQuery(""" - SELECT e.name, - f.name, - CONCAT(CASE WHEN f.annualMiles > 50000 THEN 'Platinum ' - WHEN f.annualMiles > 25000 THEN 'Gold ' - ELSE '' - END, - 'Frequent Flyer') - FROM Employee e JOIN e.frequentFlierPlan f - """); - } - - @Test - void theRest() { - - assertQuery(""" - SELECT e - FROM Employee e - WHERE TYPE(e) IN (Exempt, Contractor) - """); - } - - @Test - void theRest2() { - - assertQuery(""" - SELECT e - FROM Employee e - WHERE TYPE(e) IN (:empType1, :empType2) - """); - } - - @Test - void theRest3() { - - assertQuery(""" - SELECT e - FROM Employee e - WHERE TYPE(e) IN :empTypes - """); - } - - @Test - void theRest4() { - - assertQuery(""" - SELECT TYPE(e) - FROM Employee e - WHERE TYPE(e) <> Exempt - """); - } - - @Test - void theRest5() { - - assertQuery(""" - SELECT c.status, AVG(c.filledOrderCount), COUNT(c) - FROM Customer c - GROUP BY c.status - HAVING c.status IN (1, 2) - """); - } - - @Test - void theRest6() { - - assertQuery(""" - SELECT c.country, COUNT(c) - FROM Customer c - GROUP BY c.country - HAVING COUNT(c) > 30 - """); - } - - @Test - void theRest7() { - - assertQuery(""" - SELECT c, COUNT(o) - FROM Customer c JOIN c.orders o - GROUP BY c - HAVING COUNT(o) >= 5 - """); - } - - @Test - void theRest8() { - - assertQuery(""" - SELECT c.id, c.status - FROM Customer c JOIN c.orders o - WHERE o.count > 100 - """); - } - - @Test - void theRest9() { - - assertQuery(""" - SELECT v.location.street, KEY(i).title, VALUE(i) - FROM VideoStore v JOIN v.videoInventory i - WHERE v.location.zipcode = '94301' AND VALUE(i) > 0 - """); - } - - @Test - void theRest10() { - - assertQuery(""" - SELECT o.lineItems FROM Order AS o - """); - } - - @Test - void theRest11() { - - assertQuery(""" - SELECT c, COUNT(l) AS itemCount - FROM Customer c JOIN c.Orders o JOIN o.lineItems l - WHERE c.address.state = 'CA' - GROUP BY c - ORDER BY itemCount - """); - } - - @Test - void theRest12() { - - assertQuery(""" - SELECT NEW com.acme.example.CustomerDetails(c.id, c.status, o.count) - FROM Customer c JOIN c.orders o - WHERE o.count > 100 - """); - } - - @Test - void theRest13() { - - assertQuery(""" - SELECT e.address AS addr - FROM Employee e - """); - } - - @Test - void theRest14() { - - assertQuery(""" - SELECT AVG(o.quantity) FROM Order o - """); - } - - @Test - void theRest15() { - - assertQuery(""" - SELECT SUM(l.price) - FROM Order o JOIN o.lineItems l JOIN o.customer c - WHERE c.lastname = 'Smith' AND c.firstname = 'John' - """); - } - - @Test - void theRest16() { - - assertQuery(""" - SELECT COUNT(o) FROM Order o - """); - } - - @Test - void theRest17() { - - assertQuery(""" - SELECT COUNT(l.price) - FROM Order o JOIN o.lineItems l JOIN o.customer c - WHERE c.lastname = 'Smith' AND c.firstname = 'John' - """); - } - - @Test - void theRest18() { - - assertQuery(""" - SELECT COUNT(l) - FROM Order o JOIN o.lineItems l JOIN o.customer c - WHERE c.lastname = 'Smith' AND c.firstname = 'John' AND l.price IS NOT NULL - """); - } - - @Test - void theRest19() { - - assertQuery(""" - SELECT o - FROM Customer c JOIN c.orders o JOIN c.address a - WHERE a.state = 'CA' - ORDER BY o.quantity DESC, o.totalcost - """); - } - - @Test - void theRest20() { - - assertQuery(""" - SELECT o.quantity, a.zipcode - FROM Customer c JOIN c.orders o JOIN c.address a - WHERE a.state = 'CA' - ORDER BY o.quantity, a.zipcode - """); - } - - @Test - void theRest21() { - - assertQuery(""" - SELECT o.quantity, o.cost * 1.08 AS taxedCost, a.zipcode - FROM Customer c JOIN c.orders o JOIN c.address a - WHERE a.state = 'CA' AND a.county = 'Santa Clara' - ORDER BY o.quantity, taxedCost, a.zipcode - """); - } - - @Test - void theRest22() { - - assertQuery(""" - SELECT AVG(o.quantity) as q, a.zipcode - FROM Customer c JOIN c.orders o JOIN c.address a - WHERE a.state = 'CA' - GROUP BY a.zipcode - ORDER BY q DESC - """); - } - - @Test - void theRest23() { - - assertQuery(""" - SELECT p.product_name - FROM Order o JOIN o.lineItems l JOIN l.product p JOIN o.customer c - WHERE c.lastname = 'Smith' AND c.firstname = 'John' - ORDER BY p.price - """); - } - - /** - * This query is specifically dubbed illegal in the spec. It may actually be failing for a different reason. - */ - @Test - void theRest24() { - - assertThatExceptionOfType(BadJpqlGrammarException.class).isThrownBy(() -> { - assertQuery(""" - SELECT p.product_name - FROM Order o, IN(o.lineItems) l JOIN o.customer c - WHERE c.lastname = 'Smith' AND c.firstname = 'John' - ORDER BY o.quantity - """); - }); - } - - @Test - void theRest25() { - - assertQuery(""" - DELETE - FROM Customer c - WHERE c.status = 'inactive' - """); - } - - @Test - void theRest26() { - - assertQuery(""" - DELETE - FROM Customer c - WHERE c.status = 'inactive' - AND c.orders IS EMPTY - """); - } - - @Test - void theRest27() { - - assertQuery(""" - UPDATE Customer c - SET c.status = 'outstanding' - WHERE c.balance < 10000 - """); - } - - @Test - void theRest28() { - - assertQuery(""" - UPDATE Employee e - SET e.address.building = 22 - WHERE e.address.building = 14 - AND e.address.city = 'Santa Clara' - AND e.project = 'Jakarta EE' - """); - } - - @Test - void theRest29() { - - assertQuery(""" - SELECT o - FROM Order o - """); - } - - @Test - void theRest30() { - - assertQuery(""" - SELECT o - FROM Order o - WHERE o.shippingAddress.state = 'CA' - """); - } - - @Test - void theRest31() { - - assertQuery(""" - SELECT DISTINCT o.shippingAddress.state - FROM Order o - """); - } - - @Test - void theRest32() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order o JOIN o.lineItems l - """); - } - - @Test - void theRest33() { - - assertQuery(""" - SELECT o - FROM Order o - WHERE o.lineItems IS NOT EMPTY - """); - } - - @Test - void theRest34() { - - assertQuery(""" - SELECT o - FROM Order o - WHERE o.lineItems IS EMPTY - """); - } - - @Test - void theRest35() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order o JOIN o.lineItems l - WHERE l.shipped = FALSE - """); - } - - @Test - void theRest36() { - - assertQuery(""" - SELECT o - FROM Order o - WHERE - NOT (o.shippingAddress.state = o.billingAddress.state AND - o.shippingAddress.city = o.billingAddress.city AND - o.shippingAddress.street = o.billingAddress.street) - """); - } - - @Test - void theRest37() { - - assertQuery(""" - SELECT o - FROM Order o - WHERE o.shippingAddress <> o.billingAddress - """); - } - - @Test - void theRest38() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order o JOIN o.lineItems l - WHERE l.product.name = ?1 - """); - } -} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java index 1f273c639..ea44e5a32 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java @@ -19,6 +19,8 @@ import static org.assertj.core.api.Assertions.*; import java.util.stream.Stream; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -41,12 +43,10 @@ import org.junit.jupiter.params.provider.ValueSource; */ class HqlQueryRendererTests { - private static final String SPEC_FAULT = "Disabled due to spec fault> "; - /** * Parse the query using {@link HqlParser} then run it through the query-preserving {@link HqlQueryRenderer}. */ - private static String parseWithoutChanges(String query) { + static String parseWithoutChanges(String query) { JpaQueryEnhancer.HqlQueryParser parser = JpaQueryEnhancer.HqlQueryParser.parseQuery(query); @@ -71,33 +71,6 @@ class HqlQueryRendererTests { .trim(); } - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example - */ - @Test - void joinExample1() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order AS o JOIN o.lineItems AS l - WHERE l.shipped = FALSE - """); - } - - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#identification-variables - */ - @Test - void joinExample2() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order o JOIN o.lineItems l JOIN l.product p - WHERE p.productType = 'office_supplies' - """); - } - /** * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#range-variable-declarations */ @@ -169,11 +142,11 @@ class HqlQueryRendererTests { assertQuery(""" SELECT DISTINCT l.product - FROM Order AS o JOIN o.lineItems l + FROM Order AS o JOIN o.lineItems l LEFT JOIN l.product p """); } - @Test // GH-3711 + @Test // GH-3711, GH-2970 void entityTypeReference() { assertQuery(""" @@ -185,6 +158,42 @@ class HqlQueryRendererTests { SELECT TYPE(?0) FROM Employee e """); + + assertQuery(""" + SELECT e + FROM Employee e + WHERE TYPE(e) IN (Exempt, Contractor) + """); + + assertQuery(""" + SELECT e + FROM Employee e + WHERE TYPE(e) IN (:empType1, :empType2) + """); + + assertQuery(""" + SELECT e + FROM Employee e + WHERE TYPE(e) IN :empTypes + """); + + assertQuery(""" + SELECT TYPE(e) + FROM Employee e + WHERE TYPE(e) <> Exempt + """); + + assertQuery(""" + SELECT TYPE(e) + FROM Employee e + WHERE TYPE(e) != Exempt + """); + + assertQuery(""" + SELECT TYPE(e) + FROM Employee e + WHERE TYPE(e) ^= Exempt + """); } @Test // GH-3711 @@ -347,6 +356,366 @@ class HqlQueryRendererTests { """); } + @ParameterizedTest // GH-3689 + @ValueSource(strings = { "RESPECT NULLS", "IGNORE NULLS" }) + void generic(String nullHandling) { + + // not in the official documentation but supported in the grammar. + assertQuery(""" + SELECT e FROM Employee e + WHERE FOO(x).bar %s + """.formatted(nullHandling)); + } + + @Test // GH-3689 + void size() { + + assertQuery(""" + SELECT e FROM Employee e + WHERE SIZE(x) > 1 + """); + + assertQuery(""" + SELECT e FROM Employee e + WHERE SIZE(e.skills) > 1 + """); + } + + @Test // GH-3689 + void collectionAggregate() { + + assertQuery(""" + SELECT e FROM Employee e + WHERE MAXELEMENT(foo) > MINELEMENT(bar) + """); + + assertQuery(""" + SELECT e FROM Employee e + WHERE MININDEX(foo) > MAXINDEX(bar) + """); + } + + @Test // GH-3689 + void trunc() { + + assertQuery(""" + SELECT e FROM Employee e + WHERE TRUNC(x) = TRUNCATE(y) + """); + + assertQuery(""" + SELECT e FROM Employee e + WHERE TRUNC(e, 'foo') = TRUNCATE(e, 'bar') + """); + + assertQuery(""" + SELECT e FROM Employee e + WHERE TRUNC(e, 'YEAR') = TRUNCATE(LOCAL DATETIME, 'YEAR') + """); + } + + @ParameterizedTest // GH-3689 + @ValueSource(strings = { "YEAR", "MONTH", "DAY", "WEEK", "QUARTER", "HOUR", "MINUTE", "SECOND", "NANOSECOND", + "NANOSECOND", "EPOCH" }) + void trunc(String truncation) { + + assertQuery(""" + SELECT e FROM Employee e + WHERE TRUNC(e, %1$s) = TRUNCATE(e, %1$s) + """.formatted(truncation)); + } + + @Test // GH-3689 + void format() { + + assertQuery(""" + SELECT e FROM Employee e + WHERE FORMAT(x AS 'yyyy') = FORMAT(e.hiringDate AS 'yyyy') + """); + + assertQuery(""" + SELECT e FROM Employee e + WHERE e.hiringDate = format(LOCAL DATETIME as 'yyyy-MM-dd') + """); + + assertQuery(""" + SELECT e FROM Employee e + WHERE e.hiringDate = format(LOCAL_DATE() as 'yyyy-MM-dd') + """); + } + + @Test // GH-3689 + void collate() { + + assertQuery(""" + SELECT e FROM Employee e + WHERE COLLATE(x AS ucs_basic) = COLLATE(e.name AS ucs_basic) + """); + } + + @Test // GH-3689 + void substring() { + + assertQuery("select substring(c.number, 1, 2) " + // + "from Call c"); + + assertQuery("select substring(c.number, 1) " + // + "from Call c"); + + assertQuery("select substring(c.number, 1, position('/0' in c.number)) " + // + "from Call c"); + + assertQuery("select substring(c.number FROM 1 FOR 2) " + // + "from Call c"); + + assertQuery("select substring(c.number FROM 1) " + // + "from Call c"); + + assertQuery("select substring(c.number FROM 1 FOR position('/0' in c.number)) " + // + "from Call c"); + + assertQuery("select substring(c.number FROM 1) AS shortNumber " + // + "from Call c"); + } + + @Test // GH-3689 + void overlay() { + + assertQuery("select OVERLAY(c.number PLACING 1 FROM 2) " + // + "from Call c "); + + assertQuery("select OVERLAY(p.number PLACING 1 FROM 2 FOR 3) " + // + "from Call c "); + } + + @Test // GH-3689 + void pad() { + + assertQuery("select PAD(c.number WITH 1 LEADING) " + // + "from Call c "); + + assertQuery("select PAD(c.number WITH 1 TRAILING) " + // + "from Call c "); + + assertQuery("select PAD(c.number WITH 1 LEADING '0') " + // + "from Call c "); + + assertQuery("select PAD(c.number WITH 1 TRAILING '0') " + // + "from Call c "); + } + + @Test // GH-3689 + void position() { + + assertQuery("select POSITION(c.number IN 'foo') " + // + "from Call c "); + + assertQuery("select POSITION(c.number IN 'foo') + 1 AS pos " + // + "from Call c "); + } + + @Test // GH-3689 + void currentDateFunctions() { + + assertQuery("select CURRENT DATE, CURRENT_DATE() " + // + "from Call c "); + + assertQuery("select CURRENT TIME, CURRENT_TIME() " + // + "from Call c "); + + assertQuery("select CURRENT TIMESTAMP, CURRENT_TIMESTAMP() " + // + "from Call c "); + + assertQuery("select INSTANT, CURRENT_INSTANT() " + // + "from Call c "); + + assertQuery("select LOCAL DATE, LOCAL_DATE() " + // + "from Call c "); + + assertQuery("select LOCAL TIME, LOCAL_TIME() " + // + "from Call c "); + + assertQuery("select LOCAL DATETIME, LOCAL_DATETIME() " + // + "from Call c "); + + assertQuery("select OFFSET DATETIME, OFFSET_DATETIME() " + // + "from Call c "); + + assertQuery("select OFFSET DATETIME AS offsetDatetime, OFFSET_DATETIME() AS offset_datetime " + // + "from Call c "); + } + + @Test // GH-3689 + void cube() { + + assertQuery("select CUBE(foo), CUBE(foo, bar) " + // + "from Call c "); + + assertQuery("select c.callerId from Call c GROUP BY CUBE(state, province)"); + } + + @Test // GH-3689 + void rollup() { + + assertQuery("select ROLLUP(foo), ROLLUP(foo, bar) " + // + "from Call c "); + + assertQuery("select c.callerId from Call c GROUP BY ROLLUP(state, province)"); + } + + @Test + void pathExpressionsNamedParametersExample() { + + assertQuery(""" + SELECT c + FROM Customer c + WHERE c.status = :stat + """); + } + + @Test + void betweenExpressionsExample() { + + assertQuery(""" + SELECT t + FROM CreditCard c JOIN c.transactionHistory t + WHERE c.holder.name = 'John Doe' AND INDEX(t) BETWEEN 0 AND 9 + """); + } + + @Test + void isEmptyExample() { + + assertQuery(""" + SELECT o + FROM Order o + WHERE o.lineItems IS EMPTY + """); + } + + @Test + void memberOfExample() { + + assertQuery(""" + SELECT p + FROM Person p + WHERE 'Joe' MEMBER OF p.nicknames + """); + } + + @Test + void existsSubSelectExample1() { + + assertQuery(""" + SELECT DISTINCT emp + FROM Employee emp + WHERE EXISTS (SELECT spouseEmp + FROM Employee spouseEmp + WHERE spouseEmp = emp.spouse) + """); + } + + @Test // GH-3689 + void everyAll() { + + assertQuery(""" + SELECT DISTINCT emp + FROM Employee emp + WHERE EVERY (SELECT spouseEmp + FROM Employee spouseEmp) > 1 + """); + + assertQuery(""" + SELECT DISTINCT emp + FROM Employee emp + WHERE ALL (SELECT spouseEmp + FROM Employee spouseEmp) > 1 + """); + + assertQuery(""" + SELECT DISTINCT emp + FROM Employee emp + WHERE ALL (foo > 1) OVER (PARTITION BY bar) + """); + + assertQuery(""" + SELECT DISTINCT emp + FROM Employee emp + WHERE ALL VALUES(foo) > 1 + """); + + assertQuery(""" + SELECT DISTINCT emp + FROM Employee emp + WHERE ALL ELEMENTS(foo) > 1 + """); + } + + @Test // GH-3689 + void anySome() { + + assertQuery(""" + SELECT DISTINCT emp + FROM Employee emp + WHERE ANY (SELECT spouseEmp + FROM Employee spouseEmp) > 1 + """); + + assertQuery(""" + SELECT DISTINCT emp + FROM Employee emp + WHERE SOME (SELECT spouseEmp + FROM Employee spouseEmp) > 1 + """); + + assertQuery(""" + SELECT DISTINCT emp + FROM Employee emp + WHERE ANY (foo > 1) OVER (PARTITION BY bar) + """); + + assertQuery(""" + SELECT DISTINCT emp + FROM Employee emp + WHERE ANY VALUES(foo) > 1 + """); + } + + @Test // GH-3689 + void listAgg() { + + assertQuery("select listagg(p.number, ', ') within group (order by p.type, p.number) " + // + "from Phone p " + // + "group by p.person"); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example + */ + @Test + void joinExample1() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order AS o JOIN o.lineItems AS l + WHERE l.shipped = FALSE + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#identification-variables + */ + @Test + void joinExample2() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l JOIN l.product p + WHERE p.productType = 'office_supplies' + """); + } + @Test void joinsExample1() { @@ -371,7 +740,6 @@ class HqlQueryRendererTests { """); } - @Disabled("Deprecated syntax dating back to EJB-QL prior to EJB 3, required by JPA, never documented in Hibernate") @Test void joinsInExample() { @@ -447,7 +815,7 @@ class HqlQueryRendererTests { assertQuery(""" SELECT DISTINCT o - FROM Order o , IN(o.lineItems) l + FROM Order o, IN(o.lineItems) l WHERE l.product.productType = 'office_supplies' """); } @@ -483,8 +851,7 @@ class HqlQueryRendererTests { * @see #fromClauseDowncastingExample3fixed() */ @Test - @Disabled(SPEC_FAULT + "Use double-quotes when it should be using single-quotes for a string literal") - void fromClauseDowncastingExample3_SPEC_BUG() { + void fromClauseDowncastingExample3() { assertQuery(""" SELECT e FROM Employee e JOIN e.projects p @@ -492,10 +859,6 @@ class HqlQueryRendererTests { OR TREAT(p AS SmallProject).name LIKE 'Persist%' OR p.description LIKE "cost overrun" """); - } - - @Test - void fromClauseDowncastingExample3fixed() { assertQuery(""" SELECT e FROM Employee e JOIN e.projects p @@ -515,58 +878,6 @@ class HqlQueryRendererTests { """); } - @Test - void pathExpressionsNamedParametersExample() { - - assertQuery(""" - SELECT c - FROM Customer c - WHERE c.status = :stat - """); - } - - @Test - void betweenExpressionsExample() { - - assertQuery(""" - SELECT t - FROM CreditCard c JOIN c.transactionHistory t - WHERE c.holder.name = 'John Doe' AND INDEX(t) BETWEEN 0 AND 9 - """); - } - - @Test - void isEmptyExample() { - - assertQuery(""" - SELECT o - FROM Order o - WHERE o.lineItems IS EMPTY - """); - } - - @Test - void memberOfExample() { - - assertQuery(""" - SELECT p - FROM Person p - WHERE 'Joe' MEMBER OF p.nicknames - """); - } - - @Test - void existsSubSelectExample1() { - - assertQuery(""" - SELECT DISTINCT emp - FROM Employee emp - WHERE EXISTS (SELECT spouseEmp - FROM Employee spouseEmp - WHERE spouseEmp = emp.spouse) - """); - } - @Test void allExample() { @@ -626,8 +937,7 @@ class HqlQueryRendererTests { * @see #functionInvocationExampleWithCorrection() */ @Test - @Disabled(SPEC_FAULT + "FUNCTION calls needs a comparator") - void functionInvocationExample_SPEC_BUG() { + void functionInvocationExample() { assertQuery(""" SELECT c @@ -646,6 +956,15 @@ class HqlQueryRendererTests { """); } + @ParameterizedTest // GH-3628 + @ValueSource(strings = { "is true", "is not true", "is false", "is not false" }) + void functionInvocationWithIsBoolean(String booleanComparison) { + + assertQuery(""" + from RoleTmpl where find_in_set(:appId, appIds) %s + """.formatted(booleanComparison)); + } + @Test void updateCaseExample1() { @@ -703,59 +1022,93 @@ class HqlQueryRendererTests { } @Test - void theRest() { + void collectionIsEmpty() { assertQuery(""" - SELECT e - FROM Employee e - WHERE TYPE(e) IN (Exempt, Contractor) + DELETE + FROM Customer c + WHERE c.status = 'inactive' + AND c.orders IS EMPTY + """); + + assertQuery(""" + DELETE + FROM Customer c + WHERE c.status = 'inactive' + AND c.orders IS NOT EMPTY """); } - @Test - void theRest2() { + @Test // GH-3628 + void booleanPredicate() { assertQuery(""" - SELECT e - FROM Employee e - WHERE TYPE(e) IN (:empType1, :empType2) + SELECT c + FROM Customer c + WHERE c.orders IS TRUE + """); + + assertQuery(""" + SELECT c + FROM Customer c + WHERE c.orders IS NOT TRUE + """); + + assertQuery(""" + SELECT c + FROM Customer c + WHERE c.orders IS FALSE + """); + + assertQuery(""" + SELECT c + FROM Customer c + WHERE c.orders IS NOT FALSE + """); + + assertQuery(""" + SELECT c + FROM Customer c + WHERE c.orders IS NULL + """); + + assertQuery(""" + SELECT c + FROM Customer c + WHERE c.orders IS NOT NULL """); } - @Test - void theRest3() { + @ParameterizedTest // GH-3628 + @ValueSource(strings = { "IS DISTINCT FROM", "IS NOT DISTINCT FROM" }) + void distinctFromPredicate(String distinctFrom) { assertQuery(""" - SELECT e - FROM Employee e - WHERE TYPE(e) IN :empTypes - """); - } - - @Test - void theRest4() { + SELECT c + FROM Customer c + WHERE c.orders %s c.payments + """.formatted(distinctFrom)); assertQuery(""" - SELECT TYPE(e) - FROM Employee e - WHERE TYPE(e) <> Exempt - """); - } - - @Test // GH-2970 - void alternateNotEqualsShouldAlsoWork() { + SELECT c + FROM Customer c + WHERE c.orders %s c.payments + """.formatted(distinctFrom)); assertQuery(""" - SELECT TYPE(e) - FROM Employee e - WHERE TYPE(e) != Exempt - """); + SELECT c + FROM Customer c + GROUP BY c.lastname + HAVING c.orders %s c.payments + """.formatted(distinctFrom)); assertQuery(""" - SELECT TYPE(e) - FROM Employee e - WHERE TYPE(e) ^= Exempt - """); + SELECT c + FROM Customer c + WHERE EXISTS (SELECT c2 + FROM Customer c2 + WHERE c2.orders %s c.orders) + """.formatted(distinctFrom)); } @Test @@ -972,7 +1325,7 @@ class HqlQueryRendererTests { assertQuery(""" SELECT p.product_name - FROM Order o , IN(o.lineItems) l JOIN o.customer c + FROM Order o, IN(o.lineItems) l JOIN o.customer c WHERE c.lastname = 'Smith' AND c.firstname = 'John' ORDER BY o.quantity """); @@ -1121,85 +1474,108 @@ class HqlQueryRendererTests { """); } + @Test // GH-3689 + void insertQueries() { + + assertQuery("insert Person (id, name) values (100L, 'Jane Doe')"); + + assertQuery("insert Person (id, name) values " + // + "(101L, 'J A Doe III'), " + // + "(102L, 'J X Doe'), " + // + "(103L, 'John Doe, Jr')"); + + assertQuery("insert into Partner (id, name) " + // + "select p.id, p.name from Person p "); + + assertQuery("INSERT INTO AggregationPrice (range, price, type) " + "VALUES (:range, :price, :priceType) " + + "ON CONFLICT (range) DO UPDATE SET price = :price, type = :priceType"); + + assertQuery("INSERT INTO AggregationPrice (range, price, type) " + "VALUES (:range, :price, :priceType) " + + "ON CONFLICT ON CONSTRAINT foo DO UPDATE SET price = :price, type = :priceType"); + + assertQuery("INSERT INTO AggregationPrice (range, price, type) " + "VALUES (:range, :price, :priceType) " + + "ON CONFLICT ON CONSTRAINT foo DO NOTHING"); + } + @Test void hqlQueries() { - parseWithoutChanges("from Person"); - parseWithoutChanges("select local datetime"); - parseWithoutChanges("from Person p select p.name"); - parseWithoutChanges("update Person set nickName = 'Nacho' " + // + assertQuery("from Person"); + assertQuery("select local datetime"); + assertQuery("from Person p select p.name"); + assertQuery("update Person set nickName = 'Nacho' " + // "where name = 'Ignacio'"); - parseWithoutChanges("update Person p " + // + assertQuery("update Person p " + // "set p.name = :newName " + // "where p.name = :oldName"); - parseWithoutChanges("update Person " + // + assertQuery("update Person " + // "set name = :newName " + // "where name = :oldName"); - parseWithoutChanges("update versioned Person " + // + assertQuery("update versioned Person " + // "set name = :newName " + // "where name = :oldName"); - parseWithoutChanges("insert Person (id, name) " + // + assertQuery("insert Person (id, name) " + // "values (100L, 'Jane Doe')"); - parseWithoutChanges("insert Person (id, name) " + // + assertQuery("insert Person (id, name) " + // "values (101L, 'J A Doe III'), " + // "(102L, 'J X Doe'), " + // "(103L, 'John Doe, Jr')"); - parseWithoutChanges("insert into Partner (id, name) " + // + assertQuery("insert into Partner (id, name) " + // "select p.id, p.name " + // "from Person p "); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where p.name like 'Joe'"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where p.name ilike 'Joe'"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where p.name like 'Joe''s'"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where p.id = 1"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where p.id = 1L"); - parseWithoutChanges("select c " + // + assertQuery("select c " + // "from Call c " + // "where c.duration > 100.5"); - parseWithoutChanges("select c " + // + assertQuery("select c " + // "from Call c " + // "where c.duration > 100.5F"); - parseWithoutChanges("select c " + // + assertQuery("select c " + // "from Call c " + // "where c.duration > 1e+2"); - parseWithoutChanges("select c " + // + assertQuery("select c " + // "from Call c " + // "where c.duration > 1e+2F"); - parseWithoutChanges("from Phone ph " + // + assertQuery("from Phone ph " + // "where ph.type = LAND_LINE"); - parseWithoutChanges("select java.lang.Math.PI"); - parseWithoutChanges("select 'Customer ' || p.name " + // + assertQuery("select java.lang.Math.PI"); + assertQuery("select 'Customer ' || p.name " + // "from Person p " + // "where p.id = 1"); - parseWithoutChanges("select sum(ch.duration) * :multiplier " + // + assertQuery("select sum(ch.duration) * :multiplier " + // "from Person pr " + // "join pr.phones ph " + // "join ph.callHistory ch " + // "where ph.id = 1L "); - parseWithoutChanges("select year(local date) - year(p.createdOn) " + // + assertQuery("select year(local date) - year(p.createdOn) " + // "from Person p " + // "where p.id = 1L"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where year(local date) - year(p.createdOn) > 1"); - parseWithoutChanges("select " + // + assertQuery("select " + // " case p.nickName " + // " when 'NA' " + // " then '' " + // " else p.nickName " + // " end " + // "from Person p"); - parseWithoutChanges("select " + // + assertQuery("select " + // " case " + // " when p.nickName is null " + // " then " + // @@ -1211,336 +1587,336 @@ class HqlQueryRendererTests { " else p.nickName " + // " end " + // "from Person p"); - parseWithoutChanges("select " + // + assertQuery("select " + // " case when p.nickName is null " + // " then p.id * 1000 " + // " else p.id " + // " end " + // "from Person p " + // "order by p.id"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Payment p " + // "where type(p) = CreditCardPayment"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Payment p " + // "where type(p) = :type"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Payment p " + // "where length(treat(p as CreditCardPayment).cardNumber) between 16 and 20"); - parseWithoutChanges("select nullif(p.nickName, p.name) " + // + assertQuery("select nullif(p.nickName, p.name) " + // "from Person p"); - parseWithoutChanges("select " + // + assertQuery("select " + // " case" + // " when p.nickName = p.name" + // " then null" + // " else p.nickName" + // " end " + // "from Person p"); - parseWithoutChanges("select coalesce(p.nickName, '') " + // + assertQuery("select coalesce(p.nickName, '') " + // "from Person p"); - parseWithoutChanges("select coalesce(p.nickName, p.name, '') " + // + assertQuery("select coalesce(p.nickName, p.name, '') " + // "from Person p"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where size(p.phones) >= 2"); - parseWithoutChanges("select concat(p.number, ' : ' , cast(c.duration as string)) " + // + assertQuery("select concat(p.number, ' : ', cast(c.duration as string)) " + // "from Call c " + // "join c.phone p"); - parseWithoutChanges("select substring(p.number, 1, 2) " + // + assertQuery("select substring(p.number, 1, 2) " + // "from Call c " + // "join c.phone p"); - parseWithoutChanges("select upper(p.name) " + // + assertQuery("select upper(p.name) " + // "from Person p "); - parseWithoutChanges("select lower(p.name) " + // + assertQuery("select lower(p.name) " + // "from Person p "); - parseWithoutChanges("select trim(p.name) " + // + assertQuery("select trim(p.name) " + // "from Person p "); - parseWithoutChanges("select trim(leading ' ' from p.name) " + // + assertQuery("select trim(leading ' ' from p.name) " + // "from Person p "); - parseWithoutChanges("select length(p.name) " + // + assertQuery("select length(p.name) " + // "from Person p "); - parseWithoutChanges("select locate('John', p.name) " + // + assertQuery("select locate('John', p.name) " + // "from Person p "); - parseWithoutChanges("select abs(c.duration) " + // + assertQuery("select abs(c.duration) " + // "from Call c "); - parseWithoutChanges("select mod(c.duration, 10) " + // + assertQuery("select mod(c.duration, 10) " + // "from Call c "); - parseWithoutChanges("select sqrt(c.duration) " + // + assertQuery("select sqrt(c.duration) " + // "from Call c "); - parseWithoutChanges("select cast(c.duration as String) " + // + assertQuery("select cast(c.duration as String) " + // "from Call c "); - parseWithoutChanges("select str(c.timestamp) " + // + assertQuery("select str(c.timestamp) " + // "from Call c "); - parseWithoutChanges("select str(cast(duration as float) / 60, 4, 2) " + // + assertQuery("select str(cast(duration as float) / 60, 4, 2) " + // "from Call c "); - parseWithoutChanges("select c " + // + assertQuery("select c " + // "from Call c " + // "where extract(date from c.timestamp) = local date"); - parseWithoutChanges("select extract(year from c.timestamp) " + // + assertQuery("select extract(year from c.timestamp) " + // "from Call c "); - parseWithoutChanges("select year(c.timestamp) " + // + assertQuery("select year(c.timestamp) " + // "from Call c "); - parseWithoutChanges("select var_samp(c.duration) as sampvar, var_pop(c.duration) as popvar " + // + assertQuery("select var_samp(c.duration) as sampvar, var_pop(c.duration) as popvar " + // "from Call c "); - parseWithoutChanges("select bit_length(c.phone.number) " + // + assertQuery("select bit_length(c.phone.number) " + // "from Call c "); - parseWithoutChanges("select c " + // + assertQuery("select c " + // "from Call c " + // "where c.duration < 30 "); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where p.name like 'John%' "); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where p.createdOn > '1950-01-01' "); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Phone p " + // "where p.type = 'MOBILE' "); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Payment p " + // "where p.completed = true "); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Payment p " + // "where type(p) = WireTransferPayment "); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Payment p, Phone ph " + // "where p.person = ph.person "); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "join p.phones ph " + // "where p.id = 1L and index(ph) between 0 and 3"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where p.createdOn between '1999-01-01' and '2001-01-02'"); - parseWithoutChanges("select c " + // + assertQuery("select c " + // "from Call c " + // "where c.duration between 5 and 20"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where p.name between 'H' and 'M'"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where p.nickName is not null"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where p.nickName is null"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where p.name like 'Jo%'"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where p.name not like 'Jo%'"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where p.name like 'Dr|_%' escape '|'"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Payment p " + // "where type(p) in (CreditCardPayment, WireTransferPayment)"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Phone p " + // "where type in ('MOBILE', 'LAND_LINE')"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Phone p " + // "where type in :types"); - parseWithoutChanges("select distinct p " + // + assertQuery("select distinct p " + // "from Phone p " + // - "where p.person.id in (" + // - " select py.person.id " + // + "where p.person.id in " + // + "(select py.person.id " + // " from Payment py" + // - " where py.completed = true and py.amount > 50 " + // + " where py.completed = true and py.amount > 50" + // ")"); - parseWithoutChanges("select distinct p " + // + assertQuery("select distinct p " + // "from Phone p " + // - "where p.person in (" + // - " select py.person " + // + "where p.person in " + // + "(select py.person " + // " from Payment py" + // - " where py.completed = true and py.amount > 50 " + // + " where py.completed = true and py.amount > 50" + // ")"); - parseWithoutChanges("select distinct p " + // + assertQuery("select distinct p " + // "from Payment p " + // "where (p.amount, p.completed) in (" + // - " (50, true)," + // + "(50, true)," + // " (100, true)," + // " (5, false)" + // ")"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where 1 in indices(p.phones)"); - parseWithoutChanges("select distinct p.person " + // + assertQuery("select distinct p.person " + // "from Phone p " + // "join p.calls c " + // - "where 50 > all (" + // - " select duration" + // + "where 50 > all " + // + "(select duration" + // " from Call" + // - " where phone = p " + // + " where phone = p" + // ") "); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Phone p " + // "where local date > all elements(p.repairTimestamps)"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where :phone = some elements(p.phones)"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where :phone member of p.phones"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where exists elements(p.phones)"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where p.phones is empty"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where p.phones is not empty"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where p.phones is not empty"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where 'Home address' member of p.addresses"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where 'Home address' not member of p.addresses"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from org.hibernate.userguide.model.Person p"); - parseWithoutChanges("select distinct pr, ph " + // + assertQuery("select distinct pr, ph " + // "from Person pr, Phone ph " + // "where ph.person = pr and ph is not null"); - parseWithoutChanges("select distinct pr1 " + // + assertQuery("select distinct pr1 " + // "from Person pr1, Person pr2 " + // "where pr1.id <> pr2.id " + // " and pr1.address = pr2.address " + // " and pr1.createdOn < pr2.createdOn"); - parseWithoutChanges("select distinct pr, ph " + // + assertQuery("select distinct pr, ph " + // "from Person pr cross join Phone ph " + // "where ph.person = pr and ph is not null"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Payment p "); - parseWithoutChanges("select d.owner, d.payed " + // - "from (" + // - " select p.person as owner, c.payment is not null as payed " + // + assertQuery("select d.owner, d.payed " + // + "from " + // + "(select p.person as owner, c.payment is not null as payed " + // " from Call c " + // " join c.phone p " + // " where p.number = :phoneNumber) d"); - parseWithoutChanges("select distinct pr " + // + assertQuery("select distinct pr " + // "from Person pr " + // "join Phone ph on ph.person = pr " + // "where ph.type = :phoneType"); - parseWithoutChanges("select distinct pr " + // + assertQuery("select distinct pr " + // "from Person pr " + // "join pr.phones ph " + // "where ph.type = :phoneType"); - parseWithoutChanges("select distinct pr " + // + assertQuery("select distinct pr " + // "from Person pr " + // "inner join pr.phones ph " + // "where ph.type = :phoneType"); - parseWithoutChanges("select distinct pr " + // + assertQuery("select distinct pr " + // "from Person pr " + // "left join pr.phones ph " + // "where ph is null " + // " or ph.type = :phoneType"); - parseWithoutChanges("select distinct pr " + // + assertQuery("select distinct pr " + // "from Person pr " + // "left outer join pr.phones ph " + // "where ph is null " + // " or ph.type = :phoneType"); - parseWithoutChanges("select pr.name, ph.number " + // + assertQuery("select pr.name, ph.number " + // "from Person pr " + // "left join pr.phones ph with ph.type = :phoneType "); - parseWithoutChanges("select pr.name, ph.number " + // + assertQuery("select pr.name, ph.number " + // "from Person pr " + // "left join pr.phones ph on ph.type = :phoneType "); - parseWithoutChanges("select distinct pr " + // + assertQuery("select distinct pr " + // "from Person pr " + // "left join fetch pr.phones "); - parseWithoutChanges("select a, ccp " + // + assertQuery("select a, ccp " + // "from Account a " + // "join treat(a.payments as CreditCardPayment) ccp " + // "where length(ccp.cardNumber) between 16 and 20"); - parseWithoutChanges("select c, ccp " + // + assertQuery("select c, ccp " + // "from Call c " + // "join treat(c.payment as CreditCardPayment) ccp " + // "where length(ccp.cardNumber) between 16 and 20"); - parseWithoutChanges("select longest.duration " + // + assertQuery("select longest.duration " + // "from Phone p " + // - "left join lateral (" + // - " select c.duration as duration " + // + "left join lateral " + // + "(select c.duration as duration " + // " from p.calls c" + // " order by c.duration desc" + // " limit 1 " + // " ) longest " + // "where p.number = :phoneNumber"); - parseWithoutChanges("select ph " + // + assertQuery("select ph " + // "from Phone ph " + // "where ph.person.address = :address "); - parseWithoutChanges("select ph " + // + assertQuery("select ph " + // "from Phone ph " + // "join ph.person pr " + // "where pr.address = :address "); - parseWithoutChanges("select ph " + // + assertQuery("select ph " + // "from Phone ph " + // "where ph.person.address = :address " + // " and ph.person.createdOn > :timestamp"); - parseWithoutChanges("select ph " + // + assertQuery("select ph " + // "from Phone ph " + // "inner join ph.person pr " + // "where pr.address = :address " + // " and pr.createdOn > :timestamp"); - parseWithoutChanges("select ph " + // + assertQuery("select ph " + // "from Person pr " + // "join pr.phones ph " + // "join ph.calls c " + // "where pr.address = :address " + // " and c.duration > :duration"); - parseWithoutChanges("select ch " + // + assertQuery("select ch " + // "from Phone ph " + // "join ph.callHistory ch " + // "where ph.id = :id "); - parseWithoutChanges("select value(ch) " + // + assertQuery("select value(ch) " + // "from Phone ph " + // "join ph.callHistory ch " + // "where ph.id = :id "); - parseWithoutChanges("select key(ch) " + // + assertQuery("select key(ch) " + // "from Phone ph " + // "join ph.callHistory ch " + // "where ph.id = :id "); - parseWithoutChanges("select key(ch) " + // + assertQuery("select key(ch) " + // "from Phone ph " + // "join ph.callHistory ch " + // "where ph.id = :id "); - parseWithoutChanges("select entry(ch) " + // + assertQuery("select entry (ch) " + // "from Phone ph " + // "join ph.callHistory ch " + // "where ph.id = :id "); - parseWithoutChanges("select sum(ch.duration) " + // + assertQuery("select sum(ch.duration) " + // "from Person pr " + // "join pr.phones ph " + // "join ph.callHistory ch " + // "where ph.id = :id " + // " and index(ph) = :phoneIndex"); - parseWithoutChanges("select value(ph.callHistory) " + // + assertQuery("select value(ph.callHistory) " + // "from Phone ph " + // "where ph.id = :id "); - parseWithoutChanges("select key(ph.callHistory) " + // + assertQuery("select key(ph.callHistory) " + // "from Phone ph " + // "where ph.id = :id "); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where p.phones[0].type = LAND_LINE"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where p.addresses['HOME'] = :address"); - parseWithoutChanges("select pr " + // + assertQuery("select pr " + // "from Person pr " + // "where pr.phones[max(indices(pr.phones))].type = 'LAND_LINE'"); - parseWithoutChanges("select p.name, p.nickName " + // + assertQuery("select p.name, p.nickName " + // "from Person p "); - parseWithoutChanges("select p.name as name, p.nickName as nickName " + // + assertQuery("select p.name as name, p.nickName as nickName " + // "from Person p "); - parseWithoutChanges("select new org.hibernate.userguide.hql.CallStatistics(" + // - " count(c), " + // + assertQuery("select new org.hibernate.userguide.hql.CallStatistics" + // + "(count(c), " + // " sum(c.duration), " + // " min(c.duration), " + // " max(c.duration), " + // @@ -1548,100 +1924,99 @@ class HqlQueryRendererTests { " 1" + // ") " + // "from Call c "); - parseWithoutChanges("select new map(" + // - " p.number as phoneNumber , " + // + assertQuery("select new map(" + // + "p.number as phoneNumber, " + // " sum(c.duration) as totalDuration, " + // - " avg(c.duration) as averageDuration " + // + " avg(c.duration) as averageDuration" + // ") " + // "from Call c " + // "join c.phone p " + // "group by p.number "); - parseWithoutChanges("select new list(" + // - " p.number, " + // - " c.duration " + // - ") " + // + assertQuery("select new list(" + // + "p.number, " + // + " c.duration) " + // "from Call c " + // "join c.phone p "); - parseWithoutChanges("select distinct p.lastName " + // + assertQuery("select distinct p.lastName " + // "from Person p"); - parseWithoutChanges("select " + // + assertQuery("select " + // " count(c), " + // " sum(c.duration), " + // " min(c.duration), " + // " max(c.duration), " + // " avg(c.duration) " + // "from Call c "); - parseWithoutChanges("select count(distinct c.phone) " + // + assertQuery("select count(distinct c.phone) " + // "from Call c "); - parseWithoutChanges("select p.number, count(c) " + // + assertQuery("select p.number, count(c) " + // "from Call c " + // "join c.phone p " + // "group by p.number"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Phone p " + // "where max(elements(p.calls)) = :call"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Phone p " + // "where min(elements(p.calls)) = :call"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "where max(indices(p.phones)) = 0"); - parseWithoutChanges("select count(c) filter (where c.duration < 30) " + // + assertQuery("select count(c) filter (where c.duration < 30) " + // "from Call c "); - parseWithoutChanges("select p.number, count(c) filter (where c.duration < 30) " + // + assertQuery("select p.number, count(c) filter (where c.duration < 30) " + // "from Call c " + // "join c.phone p " + // "group by p.number"); - parseWithoutChanges("select listagg(p.number, ', ') within group (order by p.type,p.number) " + // + assertQuery("select listagg(p.number, ', ') within group (order by p.type, p.number) " + // "from Phone p " + // "group by p.person"); - parseWithoutChanges("select sum(c.duration) " + // + assertQuery("select sum(c.duration) " + // "from Call c "); - parseWithoutChanges("select p.name, sum(c.duration) " + // + assertQuery("select p.name, sum(c.duration) " + // "from Call c " + // "join c.phone ph " + // "join ph.person p " + // "group by p.name"); - parseWithoutChanges("select p, sum(c.duration) " + // + assertQuery("select p, sum(c.duration) " + // "from Call c " + // "join c.phone ph " + // "join ph.person p " + // "group by p"); - parseWithoutChanges("select p.name, sum(c.duration) " + // + assertQuery("select p.name, sum(c.duration) " + // "from Call c " + // "join c.phone ph " + // "join ph.person p " + // "group by p.name " + // "having sum(c.duration) > 1000"); - parseWithoutChanges("select p.name from Person p " + // + assertQuery("select p.name from Person p " + // "union " + // "select p.nickName from Person p where p.nickName is not null"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Person p " + // "order by p.name"); - parseWithoutChanges("select p.name, sum(c.duration) as total " + // + assertQuery("select p.name, sum(c.duration) as total " + // "from Call c " + // "join c.phone ph " + // "join ph.person p " + // "group by p.name " + // "order by total"); - parseWithoutChanges("select c " + // + assertQuery("select c " + // "from Call c " + // "join c.phone p " + // "order by p.number " + // "limit 50"); - parseWithoutChanges("select c " + // + assertQuery("select c " + // "from Call c " + // "join c.phone p " + // "order by p.number " + // "fetch first 50 rows only"); - parseWithoutChanges("select c " + // + assertQuery("select c " + // "from Call c " + // "join c.phone p " + // "order by p.number " + // "offset 10 rows " + // "fetch first 50 rows with ties"); - parseWithoutChanges("select p " + // + assertQuery("select p " + // "from Phone p " + // "join fetch p.calls " + // "order by p " + // @@ -1984,7 +2359,7 @@ class HqlQueryRendererTests { } @ParameterizedTest // GH-3136 - @ValueSource(strings = {"LEFT", "RIGHT"}) + @ValueSource(strings = { "LEFT", "RIGHT" }) void leftRightStringFunctions(String keyword) { assertQuery("SELECT %s(e.name, 3) FROM Employee e".formatted(keyword)); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlSpecificationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlSpecificationTests.java deleted file mode 100644 index be05e3fce..000000000 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlSpecificationTests.java +++ /dev/null @@ -1,1788 +0,0 @@ -/* - * Copyright 2022-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.jpa.repository.query; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -import org.springframework.data.jpa.repository.query.QueryRenderer.TokenRenderer; - -/** - * Tests built around examples of HQL found in - * https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc and - * https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#query-language
- *
- * IMPORTANT: Purely verifies the parser without any transformations. - * - * @author Greg Turnquist - * @author Mark Paluch - * @author Christoph Strobl - * @since 3.1 - */ -class HqlSpecificationTests { - - private static final String SPEC_FAULT = "Disabled due to spec fault> "; - - private static String parseWithoutChanges(String query) { - - JpaQueryEnhancer.HqlQueryParser parser = JpaQueryEnhancer.HqlQueryParser.parseQuery(query); - - return TokenRenderer.render(new HqlQueryRenderer().visit(parser.getContext())); - } - - private void assertQuery(String query) { - - String slimmedDownQuery = reduceWhitespace(query); - assertThat(parseWithoutChanges(slimmedDownQuery)).isEqualTo(slimmedDownQuery); - } - - private String reduceWhitespace(String original) { - - return original // - .replaceAll("[ \\t\\n]{1,}", " ") // - .trim(); - } - - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example - */ - @Test - void joinExample1() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order AS o JOIN o.lineItems AS l - WHERE l.shipped = FALSE - """); - } - - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#identification-variables - */ - @Test - void joinExample2() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order o JOIN o.lineItems l JOIN l.product p - WHERE p.productType = 'office_supplies' - """); - } - - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#range-variable-declarations - */ - @Test - void rangeVariableDeclarations() { - - assertQuery(""" - SELECT DISTINCT o1 - FROM Order o1, Order o2 - WHERE o1.quantity > o2.quantity AND - o2.customer.lastname = 'Smith' AND - o2.customer.firstname = 'John' - """); - } - - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions - */ - @Test - void pathExpressionsExample1() { - - assertQuery(""" - SELECT i.name, VALUE(p) - FROM Item i JOIN i.photos p - WHERE KEY(p) LIKE '%egret' - """); - } - - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions - */ - @Test - void pathExpressionsExample2() { - - assertQuery(""" - SELECT i.name, p - FROM Item i JOIN i.photos p - WHERE KEY(p) LIKE '%egret' - """); - } - - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions - */ - @Test - void pathExpressionsExample3() { - - assertQuery(""" - SELECT p.vendor - FROM Employee e JOIN e.contactInfo.phones p - """); - } - - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions - */ - @Test - void pathExpressionsExample4() { - - assertQuery(""" - SELECT p.vendor - FROM Employee e JOIN e.contactInfo c JOIN c.phones p - WHERE e.contactInfo.address.zipcode = '95054' - """); - } - - @Test - void pathExpressionSyntaxExample1() { - - assertQuery(""" - SELECT DISTINCT l.product - FROM Order AS o JOIN o.lineItems l - """); - } - - @Test - void joinsExample1() { - - assertQuery(""" - SELECT c FROM Customer c, Employee e WHERE c.hatsize = e.shoesize - """); - } - - @Test - void joinsExample2() { - - assertQuery(""" - SELECT c FROM Customer c JOIN c.orders o WHERE c.status = 1 - """); - } - - @Test - void joinsInnerExample() { - - assertQuery(""" - SELECT c FROM Customer c INNER JOIN c.orders o WHERE c.status = 1 - """); - } - - @Test - void joinsInExample() { - - assertQuery(""" - SELECT OBJECT(c) FROM Customer c , IN(c.orders) o WHERE c.status = 1 - """); - } - - @Test - void doubleJoinExample() { - - assertQuery(""" - SELECT p.vendor - FROM Employee e JOIN e.contactInfo c JOIN c.phones p - WHERE c.address.zipcode = '95054' - """); - } - - @Test - void leftJoinExample() { - - assertQuery(""" - SELECT s.name, COUNT(p) - FROM Suppliers s LEFT JOIN s.products p - GROUP BY s.name - """); - } - - @Test - void leftJoinOnExample() { - - assertQuery(""" - SELECT s.name, COUNT(p) - FROM Suppliers s LEFT JOIN s.products p - ON p.status = 'inStock' - GROUP BY s.name - """); - } - - @Test - void leftJoinWhereExample() { - - assertQuery(""" - SELECT s.name, COUNT(p) - FROM Suppliers s LEFT JOIN s.products p - WHERE p.status = 'inStock' - GROUP BY s.name - """); - } - - @Test - void leftJoinFetchExample() { - - assertQuery(""" - SELECT d - FROM Department d LEFT JOIN FETCH d.employees - WHERE d.deptno = 1 - """); - } - - @Test - void collectionMemberExample() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order o JOIN o.lineItems l - WHERE l.product.productType = 'office_supplies' - """); - } - - @Test - void collectionMemberInExample() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order o , IN(o.lineItems) l - WHERE l.product.productType = 'office_supplies' - """); - } - - @Test - void fromClauseExample() { - - assertQuery(""" - SELECT o - FROM Order AS o JOIN o.lineItems l JOIN l.product p - """); - } - - @Test - void fromClauseDowncastingExample1() { - - assertQuery(""" - SELECT b.name, b.ISBN - FROM Order o JOIN TREAT(o.product AS Book) b - """); - } - - @Test - void fromClauseDowncastingExample2() { - - assertQuery(""" - SELECT e FROM Employee e JOIN TREAT(e.projects AS LargeProject) lp - WHERE lp.budget > 1000 - """); - } - - /** - * @see #fromClauseDowncastingExample3fixed() - */ - @Test - @Disabled(SPEC_FAULT + "Use double-quotes when it should be using single-quotes for a string literal") - void fromClauseDowncastingExample3_SPEC_BUG() { - - assertQuery(""" - SELECT e FROM Employee e JOIN e.projects p - WHERE TREAT(p AS LargeProject).budget > 1000 - OR TREAT(p AS SmallProject).name LIKE 'Persist%' - OR p.description LIKE "cost overrun" - """); - } - - @Test - void fromClauseDowncastingExample3fixed() { - - assertQuery(""" - SELECT e FROM Employee e JOIN e.projects p - WHERE TREAT(p AS LargeProject).budget > 1000 - OR TREAT(p AS SmallProject).name LIKE 'Persist%' - OR p.description LIKE 'cost overrun' - """); - } - - @Test - void fromClauseDowncastingExample4() { - - assertQuery(""" - SELECT e FROM Employee e - WHERE TREAT(e AS Exempt).vacationDays > 10 - OR TREAT(e AS Contractor).hours > 100 - """); - } - - @ParameterizedTest // GH-3689 - @ValueSource(strings = { "RESPECT NULLS", "IGNORE NULLS" }) - void generic(String nullHandling) { - - // not in the official documentation but supported in the grammar. - assertQuery(""" - SELECT e FROM Employee e - WHERE FOO(x).bar %s - """.formatted(nullHandling)); - } - - @Test // GH-3689 - void size() { - - assertQuery(""" - SELECT e FROM Employee e - WHERE SIZE(x) > 1 - """); - - assertQuery(""" - SELECT e FROM Employee e - WHERE SIZE(e.skills) > 1 - """); - } - - @Test // GH-3689 - void collectionAggregate() { - - assertQuery(""" - SELECT e FROM Employee e - WHERE MAXELEMENT(foo) > MINELEMENT(bar) - """); - - assertQuery(""" - SELECT e FROM Employee e - WHERE MININDEX(foo) > MAXINDEX(bar) - """); - } - - @Test // GH-3689 - void trunc() { - - assertQuery(""" - SELECT e FROM Employee e - WHERE TRUNC(x) = TRUNCATE(y) - """); - - assertQuery(""" - SELECT e FROM Employee e - WHERE TRUNC(e, 'foo') = TRUNCATE(e, 'bar') - """); - - assertQuery(""" - SELECT e FROM Employee e - WHERE TRUNC(e, 'YEAR') = TRUNCATE(LOCAL DATETIME, 'YEAR') - """); - } - - @ParameterizedTest // GH-3689 - @ValueSource(strings = { "YEAR", "MONTH", "DAY", "WEEK", "QUARTER", "HOUR", "MINUTE", "SECOND", "NANOSECOND", - "NANOSECOND", "EPOCH" }) - void trunc(String truncation) { - - assertQuery(""" - SELECT e FROM Employee e - WHERE TRUNC(e, %1$s) = TRUNCATE(e, %1$s) - """.formatted(truncation)); - } - - @Test // GH-3689 - void format() { - - assertQuery(""" - SELECT e FROM Employee e - WHERE FORMAT(x AS 'yyyy') = FORMAT(e.hiringDate AS 'yyyy') - """); - - assertQuery(""" - SELECT e FROM Employee e - WHERE e.hiringDate = format(LOCAL DATETIME as 'yyyy-MM-dd') - """); - - assertQuery(""" - SELECT e FROM Employee e - WHERE e.hiringDate = format(LOCAL_DATE() as 'yyyy-MM-dd') - """); - } - - @Test // GH-3689 - void collate() { - - assertQuery(""" - SELECT e FROM Employee e - WHERE COLLATE(x AS ucs_basic) = COLLATE(e.name AS ucs_basic) - """); - } - - @Test // GH-3689 - void substring() { - - assertQuery("select substring(c.number, 1, 2) " + // - "from Call c"); - - assertQuery("select substring(c.number, 1) " + // - "from Call c"); - - assertQuery("select substring(c.number, 1, position('/0' in c.number)) " + // - "from Call c"); - - assertQuery("select substring(c.number FROM 1 FOR 2) " + // - "from Call c"); - - assertQuery("select substring(c.number FROM 1) " + // - "from Call c"); - - assertQuery("select substring(c.number FROM 1 FOR position('/0' in c.number)) " + // - "from Call c"); - - assertQuery("select substring(c.number FROM 1) AS shortNumber " + // - "from Call c"); - } - - @Test // GH-3689 - void overlay() { - - assertQuery("select OVERLAY(c.number PLACING 1 FROM 2) " + // - "from Call c "); - - assertQuery("select OVERLAY(p.number PLACING 1 FROM 2 FOR 3) " + // - "from Call c "); - } - - @Test // GH-3689 - void pad() { - - assertQuery("select PAD(c.number WITH 1 LEADING) " + // - "from Call c "); - - assertQuery("select PAD(c.number WITH 1 TRAILING) " + // - "from Call c "); - - assertQuery("select PAD(c.number WITH 1 LEADING '0') " + // - "from Call c "); - - assertQuery("select PAD(c.number WITH 1 TRAILING '0') " + // - "from Call c "); - } - - @Test // GH-3689 - void position() { - - assertQuery("select POSITION(c.number IN 'foo') " + // - "from Call c "); - - assertQuery("select POSITION(c.number IN 'foo') + 1 AS pos " + // - "from Call c "); - } - - @Test // GH-3689 - void currentDateFunctions() { - - assertQuery("select CURRENT DATE, CURRENT_DATE() " + // - "from Call c "); - - assertQuery("select CURRENT TIME, CURRENT_TIME() " + // - "from Call c "); - - assertQuery("select CURRENT TIMESTAMP, CURRENT_TIMESTAMP() " + // - "from Call c "); - - assertQuery("select INSTANT, CURRENT_INSTANT() " + // - "from Call c "); - - assertQuery("select LOCAL DATE, LOCAL_DATE() " + // - "from Call c "); - - assertQuery("select LOCAL TIME, LOCAL_TIME() " + // - "from Call c "); - - assertQuery("select LOCAL DATETIME, LOCAL_DATETIME() " + // - "from Call c "); - - assertQuery("select OFFSET DATETIME, OFFSET_DATETIME() " + // - "from Call c "); - - assertQuery("select OFFSET DATETIME AS offsetDatetime, OFFSET_DATETIME() AS offset_datetime " + // - "from Call c "); - } - - @Test // GH-3689 - void cube() { - - assertQuery("select CUBE(foo), CUBE(foo, bar) " + // - "from Call c "); - - assertQuery("select c.callerId from Call c GROUP BY CUBE(state, province)"); - } - - @Test // GH-3689 - void rollup() { - - assertQuery("select ROLLUP(foo), ROLLUP(foo, bar) " + // - "from Call c "); - - assertQuery("select c.callerId from Call c GROUP BY ROLLUP(state, province)"); - } - - @Test - void pathExpressionsNamedParametersExample() { - - assertQuery(""" - SELECT c - FROM Customer c - WHERE c.status = :stat - """); - } - - @Test - void betweenExpressionsExample() { - - assertQuery(""" - SELECT t - FROM CreditCard c JOIN c.transactionHistory t - WHERE c.holder.name = 'John Doe' AND INDEX(t) BETWEEN 0 AND 9 - """); - } - - @Test - void isEmptyExample() { - - assertQuery(""" - SELECT o - FROM Order o - WHERE o.lineItems IS EMPTY - """); - } - - @Test - void memberOfExample() { - - assertQuery(""" - SELECT p - FROM Person p - WHERE 'Joe' MEMBER OF p.nicknames - """); - } - - @Test - void existsSubSelectExample1() { - - assertQuery(""" - SELECT DISTINCT emp - FROM Employee emp - WHERE EXISTS (SELECT spouseEmp - FROM Employee spouseEmp - WHERE spouseEmp = emp.spouse) - """); - } - - @Test // GH-3689 - void everyAll() { - - assertQuery(""" - SELECT DISTINCT emp - FROM Employee emp - WHERE EVERY (SELECT spouseEmp - FROM Employee spouseEmp) > 1 - """); - - assertQuery(""" - SELECT DISTINCT emp - FROM Employee emp - WHERE ALL (SELECT spouseEmp - FROM Employee spouseEmp) > 1 - """); - - assertQuery(""" - SELECT DISTINCT emp - FROM Employee emp - WHERE ALL (foo > 1) OVER (PARTITION BY bar) - """); - - assertQuery(""" - SELECT DISTINCT emp - FROM Employee emp - WHERE ALL VALUES (foo) > 1 - """); - - assertQuery(""" - SELECT DISTINCT emp - FROM Employee emp - WHERE ALL ELEMENTS (foo) > 1 - """); - } - - @Test // GH-3689 - void anySome() { - - assertQuery(""" - SELECT DISTINCT emp - FROM Employee emp - WHERE ANY (SELECT spouseEmp - FROM Employee spouseEmp) > 1 - """); - - assertQuery(""" - SELECT DISTINCT emp - FROM Employee emp - WHERE SOME (SELECT spouseEmp - FROM Employee spouseEmp) > 1 - """); - - assertQuery(""" - SELECT DISTINCT emp - FROM Employee emp - WHERE ANY (foo > 1) OVER (PARTITION BY bar) - """); - - assertQuery(""" - SELECT DISTINCT emp - FROM Employee emp - WHERE ANY VALUES (foo) > 1 - """); - } - - @Test // GH-3689 - void listAgg() { - - assertQuery("select listagg(p.number, ', ') within group (order by p.type, p.number) " + // - "from Phone p " + // - "group by p.person"); - } - - @Test - void allExample() { - - assertQuery(""" - SELECT emp - FROM Employee emp - WHERE emp.salary > ALL (SELECT m.salary - FROM Manager m - WHERE m.department = emp.department) - """); - } - - @Test - void existsSubSelectExample2() { - - assertQuery(""" - SELECT DISTINCT emp - FROM Employee emp - WHERE EXISTS (SELECT spouseEmp - FROM Employee spouseEmp - WHERE spouseEmp = emp.spouse) - """); - } - - @Test - void subselectNumericComparisonExample1() { - - assertQuery(""" - SELECT c - FROM Customer c - WHERE (SELECT AVG(o.price) FROM c.orders o) > 100 - """); - } - - @Test - void subselectNumericComparisonExample2() { - - assertQuery(""" - SELECT goodCustomer - FROM Customer goodCustomer - WHERE goodCustomer.balanceOwed < (SELECT AVG(c.balanceOwed) / 2.0 FROM Customer c) - """); - } - - @Test - void indexExample() { - - assertQuery(""" - SELECT w.name - FROM Course c JOIN c.studentWaitlist w - WHERE c.name = 'Calculus' - AND INDEX(w) = 0 - """); - } - - /** - * @see #functionInvocationExampleWithCorrection() - */ - @Test - void functionInvocationExampleAsBooleanExpression() { - - assertQuery(""" - SELECT c - FROM Customer c - WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) - """); - } - - @Test - void functionInvocationExampleWithCorrection() { - - assertQuery(""" - SELECT c - FROM Customer c - WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) = TRUE - """); - } - - @ParameterizedTest // GH-3628 - @ValueSource(strings = { "is true", "is not true", "is false", "is not false" }) - void functionInvocationWithIsBoolean(String booleanComparison) { - - assertQuery(""" - from RoleTmpl where find_in_set(:appId, appIds) %s - """.formatted(booleanComparison)); - } - - @Test - void updateCaseExample1() { - - assertQuery(""" - UPDATE Employee e - SET e.salary = - CASE WHEN e.rating = 1 THEN e.salary * 1.1 - WHEN e.rating = 2 THEN e.salary * 1.05 - ELSE e.salary * 1.01 - END - """); - } - - @Test - void updateCaseExample2() { - - assertQuery(""" - UPDATE Employee e - SET e.salary = - CASE e.rating WHEN 1 THEN e.salary * 1.1 - WHEN 2 THEN e.salary * 1.05 - ELSE e.salary * 1.01 - END - """); - } - - @Test - void selectCaseExample1() { - - assertQuery(""" - SELECT e.name, - CASE TYPE(e) WHEN Exempt THEN 'Exempt' - WHEN Contractor THEN 'Contractor' - WHEN Intern THEN 'Intern' - ELSE 'NonExempt' - END - FROM Employee e - WHERE e.dept.name = 'Engineering' - """); - } - - @Test - void selectCaseExample2() { - - assertQuery(""" - SELECT e.name, - f.name, - CONCAT(CASE WHEN f.annualMiles > 50000 THEN 'Platinum ' - WHEN f.annualMiles > 25000 THEN 'Gold ' - ELSE '' - END, - 'Frequent Flyer') - FROM Employee e JOIN e.frequentFlierPlan f - """); - } - - @Test - void theRest() { - - assertQuery(""" - SELECT e - FROM Employee e - WHERE TYPE(e) IN (Exempt, Contractor) - """); - } - - @Test - void theRest2() { - - assertQuery(""" - SELECT e - FROM Employee e - WHERE TYPE(e) IN (:empType1, :empType2) - """); - } - - @Test - void theRest3() { - - assertQuery(""" - SELECT e - FROM Employee e - WHERE TYPE(e) IN :empTypes - """); - } - - @Test - void theRest4() { - - assertQuery(""" - SELECT TYPE(e) - FROM Employee e - WHERE TYPE(e) <> Exempt - """); - } - - @Test - void theRest5() { - - assertQuery(""" - SELECT c.status, AVG(c.filledOrderCount), COUNT(c) - FROM Customer c - GROUP BY c.status - HAVING c.status IN (1, 2) - """); - } - - @Test - void theRest6() { - - assertQuery(""" - SELECT c.country, COUNT(c) - FROM Customer c - GROUP BY c.country - HAVING COUNT(c) > 30 - """); - } - - @Test - void theRest7() { - - assertQuery(""" - SELECT c, COUNT(o) - FROM Customer c JOIN c.orders o - GROUP BY c - HAVING COUNT(o) >= 5 - """); - } - - @Test - void theRest8() { - - assertQuery(""" - SELECT c.id, c.status - FROM Customer c JOIN c.orders o - WHERE o.count > 100 - """); - } - - @Test - void theRest9() { - - assertQuery(""" - SELECT v.location.street, KEY(i).title, VALUE(i) - FROM VideoStore v JOIN v.videoInventory i - WHERE v.location.zipcode = '94301' AND VALUE(i) > 0 - """); - } - - @Test - void theRest10() { - - assertQuery(""" - SELECT o.lineItems FROM Order AS o - """); - } - - @Test - void theRest11() { - - assertQuery(""" - SELECT c, COUNT(l) AS itemCount - FROM Customer c JOIN c.Orders o JOIN o.lineItems l - WHERE c.address.state = 'CA' - GROUP BY c - ORDER BY itemCount - """); - } - - @Test - void theRest12() { - - assertQuery(""" - SELECT NEW com.acme.example.CustomerDetails(c.id, c.status, o.count) - FROM Customer c JOIN c.orders o - WHERE o.count > 100 - """); - } - - @Test - void theRest13() { - - assertQuery(""" - SELECT e.address AS addr - FROM Employee e - """); - } - - @Test - void theRest14() { - - assertQuery(""" - SELECT AVG(o.quantity) FROM Order o - """); - } - - @Test - void theRest15() { - - assertQuery(""" - SELECT SUM(l.price) - FROM Order o JOIN o.lineItems l JOIN o.customer c - WHERE c.lastname = 'Smith' AND c.firstname = 'John' - """); - } - - @Test - void theRest16() { - - assertQuery(""" - SELECT COUNT(o) FROM Order o - """); - } - - @Test - void theRest17() { - - assertQuery(""" - SELECT COUNT(l.price) - FROM Order o JOIN o.lineItems l JOIN o.customer c - WHERE c.lastname = 'Smith' AND c.firstname = 'John' - """); - } - - @Test - void theRest18() { - - assertQuery(""" - SELECT COUNT(l) - FROM Order o JOIN o.lineItems l JOIN o.customer c - WHERE c.lastname = 'Smith' AND c.firstname = 'John' AND l.price IS NOT NULL - """); - } - - @Test - void theRest19() { - - assertQuery(""" - SELECT o - FROM Customer c JOIN c.orders o JOIN c.address a - WHERE a.state = 'CA' - ORDER BY o.quantity DESC, o.totalcost - """); - } - - @Test - void theRest20() { - - assertQuery(""" - SELECT o.quantity, a.zipcode - FROM Customer c JOIN c.orders o JOIN c.address a - WHERE a.state = 'CA' - ORDER BY o.quantity, a.zipcode - """); - } - - @Test - void theRest21() { - - assertQuery(""" - SELECT o.quantity, o.cost * 1.08 AS taxedCost, a.zipcode - FROM Customer c JOIN c.orders o JOIN c.address a - WHERE a.state = 'CA' AND a.county = 'Santa Clara' - ORDER BY o.quantity, taxedCost, a.zipcode - """); - } - - @Test - void theRest22() { - - assertQuery(""" - SELECT AVG(o.quantity) as q, a.zipcode - FROM Customer c JOIN c.orders o JOIN c.address a - WHERE a.state = 'CA' - GROUP BY a.zipcode - ORDER BY q DESC - """); - } - - @Test - void theRest23() { - - assertQuery(""" - SELECT p.product_name - FROM Order o JOIN o.lineItems l JOIN l.product p JOIN o.customer c - WHERE c.lastname = 'Smith' AND c.firstname = 'John' - ORDER BY p.price - """); - } - - /** - * This query is specifically dubbed illegal in the spec, but apparently works with Hibernate. - */ - @Test - void theRest24() { - - assertQuery(""" - SELECT p.product_name - FROM Order o , IN(o.lineItems) l JOIN o.customer c - WHERE c.lastname = 'Smith' AND c.firstname = 'John' - ORDER BY o.quantity - """); - } - - @Test - void theRest25() { - - assertQuery(""" - DELETE - FROM Customer c - WHERE c.status = 'inactive' - """); - } - - @Test - void collectionIsEmpty() { - - assertQuery(""" - DELETE - FROM Customer c - WHERE c.status = 'inactive' - AND c.orders IS EMPTY - """); - - assertQuery(""" - DELETE - FROM Customer c - WHERE c.status = 'inactive' - AND c.orders IS NOT EMPTY - """); - } - - @Test // GH-3628 - void booleanPredicate() { - - assertQuery(""" - SELECT c - FROM Customer c - WHERE c.orders IS TRUE - """); - - assertQuery(""" - SELECT c - FROM Customer c - WHERE c.orders IS NOT TRUE - """); - - assertQuery(""" - SELECT c - FROM Customer c - WHERE c.orders IS FALSE - """); - - assertQuery(""" - SELECT c - FROM Customer c - WHERE c.orders IS NOT FALSE - """); - - assertQuery(""" - SELECT c - FROM Customer c - WHERE c.orders IS NULL - """); - - assertQuery(""" - SELECT c - FROM Customer c - WHERE c.orders IS NOT NULL - """); - } - - @ParameterizedTest // GH-3628 - @ValueSource(strings = { "IS DISTINCT FROM", "IS NOT DISTINCT FROM" }) - void distinctFromPredicate(String distinctFrom) { - - assertQuery(""" - SELECT c - FROM Customer c - WHERE c.orders %s c.payments - """.formatted(distinctFrom)); - - assertQuery(""" - SELECT c - FROM Customer c - WHERE c.orders %s c.payments - """.formatted(distinctFrom)); - - assertQuery(""" - SELECT c - FROM Customer c - GROUP BY c.lastname - HAVING c.orders %s c.payments - """.formatted(distinctFrom)); - - assertQuery(""" - SELECT c - FROM Customer c - WHERE EXISTS (SELECT c2 - FROM Customer c2 - WHERE c2.orders %s c.orders) - """.formatted(distinctFrom)); - } - - @Test - void theRest27() { - - assertQuery(""" - UPDATE Customer c - SET c.status = 'outstanding' - WHERE c.balance < 10000 - """); - } - - @Test - void theRest28() { - - assertQuery(""" - UPDATE Employee e - SET e.address.building = 22 - WHERE e.address.building = 14 - AND e.address.city = 'Santa Clara' - AND e.project = 'Jakarta EE' - """); - } - - @Test - void theRest29() { - - assertQuery(""" - SELECT o - FROM Order o - """); - } - - @Test - void theRest30() { - - assertQuery(""" - SELECT o - FROM Order o - WHERE o.shippingAddress.state = 'CA' - """); - } - - @Test - void theRest31() { - - assertQuery(""" - SELECT DISTINCT o.shippingAddress.state - FROM Order o - """); - } - - @Test - void theRest32() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order o JOIN o.lineItems l - """); - } - - @Test - void theRest33() { - - assertQuery(""" - SELECT o - FROM Order o - WHERE o.lineItems IS NOT EMPTY - """); - } - - @Test - void theRest34() { - - assertQuery(""" - SELECT o - FROM Order o - WHERE o.lineItems IS EMPTY - """); - } - - @Test - void theRest35() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order o JOIN o.lineItems l - WHERE l.shipped = FALSE - """); - } - - @Test - void theRest36() { - - assertQuery(""" - SELECT o - FROM Order o - WHERE - NOT (o.shippingAddress.state = o.billingAddress.state AND - o.shippingAddress.city = o.billingAddress.city AND - o.shippingAddress.street = o.billingAddress.street) - """); - } - - @Test - void theRest37() { - - assertQuery(""" - SELECT o - FROM Order o - WHERE o.shippingAddress <> o.billingAddress - """); - } - - @Test - void theRest38() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order o JOIN o.lineItems l - WHERE l.product.name = ?1 - """); - } - - @Test // GH-3689 - void insertQueries() { - - assertQuery("insert Person (id, name) values (100L, 'Jane Doe')"); - - assertQuery("insert Person (id, name) values " + // - "(101L, 'J A Doe III'), " + // - "(102L, 'J X Doe'), " + // - "(103L, 'John Doe, Jr')"); - - assertQuery("insert into Partner (id, name) " + // - "select p.id, p.name from Person p "); - - assertQuery("INSERT INTO AggregationPrice (range, price, type) " + "VALUES (:range, :price, :priceType) " - + "ON CONFLICT (range) DO UPDATE SET price = :price, type = :priceType"); - - assertQuery("INSERT INTO AggregationPrice (range, price, type) " + "VALUES (:range, :price, :priceType) " - + "ON CONFLICT ON CONSTRAINT foo DO UPDATE SET price = :price, type = :priceType"); - - assertQuery("INSERT INTO AggregationPrice (range, price, type) " + "VALUES (:range, :price, :priceType) " - + "ON CONFLICT ON CONSTRAINT foo DO NOTHING"); - } - - @Test - void hqlQueries() { - - assertQuery("from Person"); - assertQuery("select local datetime"); - assertQuery("from Person p select p.name"); - assertQuery("update Person set nickName = 'Nacho' " + // - "where name = 'Ignacio'"); - assertQuery("update Person p " + // - "set p.name = :newName " + // - "where p.name = :oldName"); - assertQuery("update Person " + // - "set name = :newName " + // - "where name = :oldName"); - assertQuery("update versioned Person " + // - "set name = :newName " + // - "where name = :oldName"); - - assertQuery("select p " + // - "from Person p " + // - "where p.name like 'Joe'"); - assertQuery("select p " + // - "from Person p " + // - "where p.name like 'Joe''s'"); - assertQuery("select p " + // - "from Person p " + // - "where p.id = 1"); - assertQuery("select p " + // - "from Person p " + // - "where p.id = 1L"); - assertQuery("select c " + // - "from Call c " + // - "where c.duration > 100.5"); - assertQuery("select c " + // - "from Call c " + // - "where c.duration > 100.5F"); - assertQuery("select c " + // - "from Call c " + // - "where c.duration > 1e+2"); - assertQuery("select c " + // - "from Call c " + // - "where c.duration > 1e+2F"); - assertQuery("from Phone ph " + // - "where ph.type = LAND_LINE"); - assertQuery("select java.lang.Math.PI"); - assertQuery("select 'Customer ' || p.name " + // - "from Person p " + // - "where p.id = 1"); - assertQuery("select sum(ch.duration) * :multiplier " + // - "from Person pr " + // - "join pr.phones ph " + // - "join ph.callHistory ch " + // - "where ph.id = 1L "); - assertQuery("select year(local date) - year(p.createdOn) " + // - "from Person p " + // - "where p.id = 1L"); - assertQuery("select p " + // - "from Person p " + // - "where year(local date) - year(p.createdOn) > 1"); - assertQuery("select " + // - " case p.nickName " + // - " when 'NA' " + // - " then '' " + // - " else p.nickName " + // - " end " + // - "from Person p"); - assertQuery("select " + // - " case " + // - " when p.nickName is null " + // - " then " + // - " case " + // - " when p.name is null " + // - " then '' " + // - " else p.name " + // - " end" + // - " else p.nickName " + // - " end " + // - "from Person p"); - assertQuery("select " + // - " case when p.nickName is null " + // - " then p.id * 1000 " + // - " else p.id " + // - " end " + // - "from Person p " + // - "order by p.id"); - assertQuery("select p " + // - "from Payment p " + // - "where type(p) = CreditCardPayment"); - assertQuery("select p " + // - "from Payment p " + // - "where type(p) = :type"); - assertQuery("select p " + // - "from Payment p " + // - "where length(treat(p as CreditCardPayment).cardNumber) between 16 and 20"); - assertQuery("select nullif(p.nickName, p.name) " + // - "from Person p"); - assertQuery("select " + // - " case" + // - " when p.nickName = p.name" + // - " then null" + // - " else p.nickName" + // - " end " + // - "from Person p"); - assertQuery("select coalesce(p.nickName, '') " + // - "from Person p"); - assertQuery("select coalesce(p.nickName, p.name, '') " + // - "from Person p"); - assertQuery("select p " + // - "from Person p " + // - "where size(p.phones) >= 2"); - assertQuery("select concat(p.number, ' : ', cast(c.duration as string)) " + // - "from Call c " + // - "join c.phone p"); - assertQuery("select upper(p.name) " + // - "from Person p "); - assertQuery("select lower(p.name) " + // - "from Person p "); - assertQuery("select trim(p.name) " + // - "from Person p "); - assertQuery("select trim(leading ' ' from p.name) " + // - "from Person p "); - assertQuery("select length(p.name) " + // - "from Person p "); - assertQuery("select locate('John', p.name) " + // - "from Person p "); - assertQuery("select abs(c.duration) " + // - "from Call c "); - assertQuery("select mod(c.duration, 10) " + // - "from Call c "); - assertQuery("select sqrt(c.duration) " + // - "from Call c "); - assertQuery("select cast(c.duration as String) " + // - "from Call c "); - assertQuery("select str(c.timestamp) " + // - "from Call c "); - assertQuery("select str(cast(duration as float) / 60, 4, 2) " + // - "from Call c "); - assertQuery("select c " + // - "from Call c " + // - "where extract(date from c.timestamp) = local date"); - assertQuery("select extract(year from c.timestamp) " + // - "from Call c "); - assertQuery("select year(c.timestamp) " + // - "from Call c "); - assertQuery("select var_samp(c.duration) as sampvar, var_pop(c.duration) as popvar " + // - "from Call c "); - assertQuery("select bit_length(c.phone.number) " + // - "from Call c "); - assertQuery("select c " + // - "from Call c " + // - "where c.duration < 30 "); - assertQuery("select p " + // - "from Person p " + // - "where p.name like 'John%' "); - assertQuery("select p " + // - "from Person p " + // - "where p.createdOn > '1950-01-01' "); - assertQuery("select p " + // - "from Phone p " + // - "where p.type = 'MOBILE' "); - assertQuery("select p " + // - "from Payment p " + // - "where p.completed = true "); - assertQuery("select p " + // - "from Payment p " + // - "where type(p) = WireTransferPayment "); - assertQuery("select p " + // - "from Payment p, Phone ph " + // - "where p.person = ph.person "); - assertQuery("select p " + // - "from Person p " + // - "join p.phones ph " + // - "where p.id = 1L and index(ph) between 0 and 3"); - assertQuery("select p " + // - "from Person p " + // - "where p.createdOn between '1999-01-01' and '2001-01-02'"); - assertQuery("select c " + // - "from Call c " + // - "where c.duration between 5 and 20"); - assertQuery("select p " + // - "from Person p " + // - "where p.name between 'H' and 'M'"); - assertQuery("select p " + // - "from Person p " + // - "where p.nickName is not null"); - assertQuery("select p " + // - "from Person p " + // - "where p.nickName is null"); - assertQuery("select p " + // - "from Person p " + // - "where p.name like 'Jo%'"); - assertQuery("select p " + // - "from Person p " + // - "where p.name not like 'Jo%'"); - assertQuery("select p " + // - "from Person p " + // - "where p.name like 'Dr|_%' escape '|'"); - assertQuery("select p " + // - "from Payment p " + // - "where type(p) in (CreditCardPayment, WireTransferPayment)"); - assertQuery("select p " + // - "from Phone p " + // - "where type in ('MOBILE', 'LAND_LINE')"); - assertQuery("select p " + // - "from Phone p " + // - "where type in :types"); - assertQuery("select distinct p " + // - "from Phone p " + // - "where p.person.id in (select py.person.id " + // - " from Payment py" + // - " where py.completed = true and py.amount > 50)"); - assertQuery("select distinct p " + // - "from Phone p " + // - "where p.person in (select py.person " + // - " from Payment py" + // - " where py.completed = true and py.amount > 50)"); - assertQuery("select distinct p " + // - "from Payment p " + // - "where (p.amount, p.completed) in ((50, true)," + // - " (100, true)," + // - " (5, false))"); - assertQuery("select p " + // - "from Person p " + // - "where 1 in indices (p.phones)"); - assertQuery("select distinct p.person " + // - "from Phone p " + // - "join p.calls c " + // - "where 50 > all (select duration" + // - " from Call" + // - " where phone = p) "); - assertQuery("select p " + // - "from Phone p " + // - "where local date > all elements (p.repairTimestamps)"); - assertQuery("select p " + // - "from Person p " + // - "where :phone = some elements (p.phones)"); - assertQuery("select p " + // - "from Person p " + // - "where :phone member of p.phones"); - assertQuery("select p " + // - "from Person p " + // - "where exists elements (p.phones)"); - assertQuery("select p " + // - "from Person p " + // - "where p.phones is empty"); - assertQuery("select p " + // - "from Person p " + // - "where p.phones is not empty"); - assertQuery("select p " + // - "from Person p " + // - "where p.phones is not empty"); - assertQuery("select p " + // - "from Person p " + // - "where 'Home address' member of p.addresses"); - assertQuery("select p " + // - "from Person p " + // - "where 'Home address' not member of p.addresses"); - assertQuery("select p " + // - "from Person p"); - assertQuery("select p " + // - "from org.hibernate.userguide.model.Person p"); - assertQuery("select distinct pr, ph " + // - "from Person pr, Phone ph " + // - "where ph.person = pr and ph is not null"); - assertQuery("select distinct pr1 " + // - "from Person pr1, Person pr2 " + // - "where pr1.id <> pr2.id " + // - " and pr1.address = pr2.address " + // - " and pr1.createdOn < pr2.createdOn"); - assertQuery("select distinct pr, ph " + // - "from Person pr cross join Phone ph " + // - "where ph.person = pr and ph is not null"); - assertQuery("select p " + // - "from Payment p "); - assertQuery("select d.owner, d.payed " + // - "from (select p.person as owner, c.payment is not null as payed " + // - " from Call c " + // - " join c.phone p " + // - " where p.number = :phoneNumber) d"); - assertQuery("select distinct pr " + // - "from Person pr " + // - "join Phone ph on ph.person = pr " + // - "where ph.type = :phoneType"); - assertQuery("select distinct pr " + // - "from Person pr " + // - "join pr.phones ph " + // - "where ph.type = :phoneType"); - assertQuery("select distinct pr " + // - "from Person pr " + // - "inner join pr.phones ph " + // - "where ph.type = :phoneType"); - assertQuery("select distinct pr " + // - "from Person pr " + // - "left join pr.phones ph " + // - "where ph is null " + // - " or ph.type = :phoneType"); - assertQuery("select distinct pr " + // - "from Person pr " + // - "left outer join pr.phones ph " + // - "where ph is null " + // - " or ph.type = :phoneType"); - assertQuery("select pr.name, ph.number " + // - "from Person pr " + // - "left join pr.phones ph with ph.type = :phoneType "); - assertQuery("select pr.name, ph.number " + // - "from Person pr " + // - "left join pr.phones ph on ph.type = :phoneType "); - assertQuery("select distinct pr " + // - "from Person pr " + // - "left join fetch pr.phones "); - assertQuery("select a, ccp " + // - "from Account a " + // - "join treat(a.payments as CreditCardPayment) ccp " + // - "where length(ccp.cardNumber) between 16 and 20"); - assertQuery("select c, ccp " + // - "from Call c " + // - "join treat(c.payment as CreditCardPayment) ccp " + // - "where length(ccp.cardNumber) between 16 and 20"); - assertQuery("select longest.duration " + // - "from Phone p " + // - "left join lateral (" + // - "select c.duration as duration " + // - " from p.calls c" + // - " order by c.duration desc" + // - " limit 1 " + // - " ) longest " + // - "where p.number = :phoneNumber"); - assertQuery("select ph " + // - "from Phone ph " + // - "where ph.person.address = :address "); - assertQuery("select ph " + // - "from Phone ph " + // - "join ph.person pr " + // - "where pr.address = :address "); - assertQuery("select ph " + // - "from Phone ph " + // - "where ph.person.address = :address " + // - " and ph.person.createdOn > :timestamp"); - assertQuery("select ph " + // - "from Phone ph " + // - "inner join ph.person pr " + // - "where pr.address = :address " + // - " and pr.createdOn > :timestamp"); - assertQuery("select ph " + // - "from Person pr " + // - "join pr.phones ph " + // - "join ph.calls c " + // - "where pr.address = :address " + // - " and c.duration > :duration"); - assertQuery("select ch " + // - "from Phone ph " + // - "join ph.callHistory ch " + // - "where ph.id = :id "); - assertQuery("select value(ch) " + // - "from Phone ph " + // - "join ph.callHistory ch " + // - "where ph.id = :id "); - assertQuery("select key(ch) " + // - "from Phone ph " + // - "join ph.callHistory ch " + // - "where ph.id = :id "); - assertQuery("select key(ch) " + // - "from Phone ph " + // - "join ph.callHistory ch " + // - "where ph.id = :id "); - assertQuery("select entry (ch) " + // - "from Phone ph " + // - "join ph.callHistory ch " + // - "where ph.id = :id "); - assertQuery("select sum(ch.duration) " + // - "from Person pr " + // - "join pr.phones ph " + // - "join ph.callHistory ch " + // - "where ph.id = :id " + // - " and index(ph) = :phoneIndex"); - assertQuery("select value(ph.callHistory) " + // - "from Phone ph " + // - "where ph.id = :id "); - assertQuery("select key(ph.callHistory) " + // - "from Phone ph " + // - "where ph.id = :id "); - assertQuery("select p " + // - "from Person p " + // - "where p.phones[0].type = LAND_LINE"); - assertQuery("select p " + // - "from Person p " + // - "where p.addresses['HOME'] = :address"); - assertQuery("select pr " + // - "from Person pr " + // - "where pr.phones[max(indices(pr.phones))].type = 'LAND_LINE'"); - assertQuery("select p.name, p.nickName " + // - "from Person p "); - assertQuery("select p.name as name, p.nickName as nickName " + // - "from Person p "); - assertQuery("select new org.hibernate.userguide.hql.CallStatistics(count(c), " + // - " sum(c.duration), " + // - " min(c.duration), " + // - " max(c.duration), " + // - " avg(c.duration)" + // - ") " + // - "from Call c "); - assertQuery("select new map(p.number as phoneNumber, " + // - " sum(c.duration) as totalDuration, " + // - " avg(c.duration) as averageDuration) " + // - "from Call c " + // - "join c.phone p " + // - "group by p.number "); - assertQuery("select new list(p.number," + // - " c.duration) " + // - "from Call c " + // - "join c.phone p "); - assertQuery("select distinct p.lastName " + // - "from Person p"); - assertQuery("select " + // - " count(c), " + // - " sum(c.duration), " + // - " min(c.duration), " + // - " max(c.duration), " + // - " avg(c.duration) " + // - "from Call c "); - assertQuery("select count(distinct c.phone) " + // - "from Call c "); - assertQuery("select p.number, count(c) " + // - "from Call c " + // - "join c.phone p " + // - "group by p.number"); - assertQuery("select p " + // - "from Phone p " + // - "where max(elements(p.calls)) = :call"); - assertQuery("select p " + // - "from Phone p " + // - "where min(elements(p.calls)) = :call"); - assertQuery("select p " + // - "from Person p " + // - "where max(indices(p.phones)) = 0"); - assertQuery("select count(c) filter (where c.duration < 30) " + // - "from Call c "); - assertQuery("select p.number, count(c) filter (where c.duration < 30) " + // - "from Call c " + // - "join c.phone p " + // - "group by p.number"); - assertQuery("select sum(c.duration) " + // - "from Call c "); - assertQuery("select p.name, sum(c.duration) " + // - "from Call c " + // - "join c.phone ph " + // - "join ph.person p " + // - "group by p.name"); - assertQuery("select p, sum(c.duration) " + // - "from Call c " + // - "join c.phone ph " + // - "join ph.person p " + // - "group by p"); - assertQuery("select p.name, sum(c.duration) " + // - "from Call c " + // - "join c.phone ph " + // - "join ph.person p " + // - "group by p.name " + // - "having sum(c.duration) > 1000"); - assertQuery("select p.name from Person p " + // - "union " + // - "select p.nickName from Person p where p.nickName is not null"); - assertQuery("select p " + // - "from Person p " + // - "order by p.name"); - assertQuery("select p.name, sum(c.duration) as total " + // - "from Call c " + // - "join c.phone ph " + // - "join ph.person p " + // - "group by p.name " + // - "order by total"); - assertQuery("select c " + // - "from Call c " + // - "join c.phone p " + // - "order by p.number " + // - "limit 50"); - assertQuery("select c " + // - "from Call c " + // - "join c.phone p " + // - "order by p.number " + // - "fetch first 50 rows only"); - assertQuery("select p " + // - "from Phone p " + // - "join fetch p.calls " + // - "order by p " + // - "limit 50"); - } -} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlComplianceTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlComplianceTests.java deleted file mode 100644 index a346c8c39..000000000 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlComplianceTests.java +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.jpa.repository.query; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -/** - * Test to verify compliance of {@link JpqlParser} with standard SQL. Other than {@link JpqlSpecificationTests} tests in - * this class check that the parser follows a lenient approach and does not error on well known concepts like numeric - * suffix. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -class JpqlComplianceTests { - - private static String parseWithoutChanges(String query) { - - JpaQueryEnhancer.JpqlQueryParser parser = JpaQueryEnhancer.JpqlQueryParser.parseQuery(query); - - return QueryRenderer.render(new JpqlQueryRenderer().visit(parser.getContext())); - } - - private void assertQuery(String query) { - - String slimmedDownQuery = reduceWhitespace(query); - assertThat(parseWithoutChanges(slimmedDownQuery)).isEqualTo(slimmedDownQuery); - } - - private String reduceWhitespace(String original) { - - return original // - .replaceAll("[ \\t\\n]{1,}", " ") // - .trim(); - } - - @Test - void selectQueries() { - - assertQuery("Select e FROM Employee e WHERE e.salary > 100000"); - assertQuery("Select e FROM Employee e WHERE e.id = :id"); - assertQuery("Select MAX(e.salary) FROM Employee e"); - assertQuery("Select e.firstName FROM Employee e"); - assertQuery("Select e.firstName, e.lastName FROM Employee e"); - } - - @Test - void selectClause() { - - assertQuery("SELECT COUNT(e) FROM Employee e"); - assertQuery("SELECT MAX(e.salary) FROM Employee e"); - assertQuery("SELECT NEW com.acme.reports.EmpReport(e.firstName, e.lastName, e.salary) FROM Employee e"); - } - - @Test - void fromClause() { - - assertQuery("SELECT e FROM Employee e"); - assertQuery("SELECT e, a FROM Employee e, MailingAddress a WHERE e.address = a.address"); - assertQuery("SELECT e FROM com.acme.Employee e"); - } - - @Test - void join() { - - assertQuery("SELECT e FROM Employee e JOIN e.address a WHERE a.city = :city"); - assertQuery("SELECT e FROM Employee e JOIN e.projects p JOIN e.projects p2 WHERE p.name = :p1 AND p2.name = :p2"); - } - - @Test - void joinFetch() { - - assertQuery("SELECT e FROM Employee e JOIN FETCH e.address"); - assertQuery("SELECT e FROM Employee e JOIN FETCH e.address a ORDER BY a.city"); - assertQuery("SELECT e FROM Employee e JOIN FETCH e.address AS a ORDER BY a.city"); - } - - @Test - void leftJoin() { - assertQuery("SELECT e FROM Employee e LEFT JOIN e.address a ORDER BY a.city"); - } - - @Test // GH-3277 - void numericLiterals() { - - assertQuery("SELECT e FROM Employee e WHERE e.id = 1234"); - assertQuery("SELECT e FROM Employee e WHERE e.id = 1234L"); - assertQuery("SELECT s FROM Stat s WHERE s.ratio > 3.14"); - assertQuery("SELECT s FROM Stat s WHERE s.ratio > 3.14F"); - assertQuery("SELECT s FROM Stat s WHERE s.ratio > 3.14e32D"); - } - - @Test // GH-3308 - void newWithStrings() { - assertQuery("select new com.example.demo.SampleObject(se.id, se.sampleValue, \"java\") from SampleEntity se"); - } - - @Test - void orderByClause() { - - assertQuery("SELECT e FROM Employee e ORDER BY e.lastName ASC, e.firstName ASC"); // Typo in EQL document - assertQuery("SELECT e FROM Employee e LEFT JOIN e.manager m ORDER BY m.lastName NULLS FIRST"); - assertQuery("SELECT e FROM Employee e ORDER BY e.address"); - } - - @Test - void groupByClause() { - - assertQuery("SELECT AVG(e.salary), e.address.city FROM Employee e GROUP BY e.address.city"); - assertQuery("SELECT e, COUNT(p) FROM Employee e LEFT JOIN e.projects p GROUP BY e"); - } - - @Test - void havingClause() { - assertQuery( - "SELECT AVG(e.salary), e.address.city FROM Employee e GROUP BY e.address.city HAVING AVG(e.salary) > 100000"); - } - - @Test // GH-3136 - void union() { - - assertQuery(""" - SELECT MAX(e.salary) FROM Employee e WHERE e.address.city = :city1 - UNION SELECT MAX(e.salary) FROM Employee e WHERE e.address.city = :city2 - """); - assertQuery(""" - SELECT e FROM Employee e JOIN e.phones p WHERE p.areaCode = :areaCode1 - INTERSECT SELECT e FROM Employee e JOIN e.phones p WHERE p.areaCode = :areaCode2 - """); - assertQuery(""" - SELECT e FROM Employee e - EXCEPT SELECT e FROM Employee e WHERE e.salary > e.manager.salary - """); - } - - @Test - void whereClause() { - // TBD - } - - @Test - void updateQueries() { - assertQuery("UPDATE Employee e SET e.salary = 60000 WHERE e.salary = 50000"); - } - - @Test - void deleteQueries() { - assertQuery("DELETE FROM Employee e WHERE e.department IS NULL"); - } - - @Test - void literals() { - - assertQuery("SELECT e FROM Employee e WHERE e.name = 'Bob'"); - assertQuery("SELECT e FROM Employee e WHERE e.id = 1234"); - assertQuery("SELECT e FROM Employee e WHERE e.id = 1234L"); - assertQuery("SELECT s FROM Stat s WHERE s.ratio > 3.14F"); - assertQuery("SELECT s FROM Stat s WHERE s.ratio > 3.14e32D"); - assertQuery("SELECT e FROM Employee e WHERE e.active = TRUE"); - assertQuery("SELECT e FROM Employee e WHERE e.startDate = {d'2012-01-03'}"); - assertQuery("SELECT e FROM Employee e WHERE e.startTime = {t'09:00:00'}"); - assertQuery("SELECT e FROM Employee e WHERE e.version = {ts'2012-01-03 09:00:00.000000001'}"); - assertQuery("SELECT e FROM Employee e WHERE e.gender = org.acme.Gender.MALE"); - assertQuery("UPDATE Employee e SET e.manager = NULL WHERE e.manager = :manager"); - } - - @Test - void functionsInSelect() { - - assertQuery("SELECT e.salary - 1000 FROM Employee e"); - assertQuery("SELECT e.salary + 1000 FROM Employee e"); - assertQuery("SELECT e.salary * 2 FROM Employee e"); - assertQuery("SELECT e.salary * 2.0 FROM Employee e"); - assertQuery("SELECT e.salary / 2 FROM Employee e"); - assertQuery("SELECT e.salary / 2.0 FROM Employee e"); - assertQuery("SELECT ABS(e.salary - e.manager.salary) FROM Employee e"); - assertQuery( - "select e from Employee e where case e.firstName when 'Bob' then 'Robert' when 'Jill' then 'Gillian' else '' end = 'Robert'"); - assertQuery( - "select case when e.firstName = 'Bob' then 'Robert' when e.firstName = 'Jill' then 'Gillian' else '' end from Employee e where e.firstName = 'Bob' or e.firstName = 'Jill'"); - assertQuery( - "select e from Employee e where case when e.firstName = 'Bob' then 'Robert' when e.firstName = 'Jill' then 'Gillian' else '' end = 'Robert'"); - assertQuery("SELECT COALESCE(e.salary, 0) FROM Employee e"); - assertQuery("SELECT CONCAT(e.firstName, ' ', e.lastName) FROM Employee e"); - assertQuery("SELECT e.name, CURRENT_DATE FROM Employee e"); - assertQuery("SELECT e.name, CURRENT_TIME FROM Employee e"); - assertQuery("SELECT e.name, CURRENT_TIMESTAMP FROM Employee e"); - assertQuery("SELECT LENGTH(e.lastName) FROM Employee e"); - assertQuery("SELECT LOWER(e.lastName) FROM Employee e"); - assertQuery("SELECT MOD(e.hoursWorked, 8) FROM Employee e"); - assertQuery("SELECT NULLIF(e.salary, 0) FROM Employee e"); - assertQuery("SELECT SQRT(o.RESULT) FROM Output o"); - assertQuery("SELECT SUBSTRING(e.lastName, 0, 2) FROM Employee e"); - assertQuery( - "SELECT TRIM(TRAILING FROM e.lastName), TRIM(e.lastName), TRIM(LEADING '-' FROM e.lastName) FROM Employee e"); - assertQuery("SELECT UPPER(e.lastName) FROM Employee e"); - assertQuery("SELECT CAST(e.salary NUMERIC(10, 2)) FROM Employee e"); - assertQuery("SELECT EXTRACT(YEAR FROM e.startDate) FROM Employee e"); - } - - @Test - void functionsInWhere() { - - assertQuery("SELECT e FROM Employee e WHERE e.salary - 1000 > 0"); - assertQuery("SELECT e FROM Employee e WHERE e.salary + 1000 > 0"); - assertQuery("SELECT e FROM Employee e WHERE e.salary * 2 > 0"); - assertQuery("SELECT e FROM Employee e WHERE e.salary * 2.0 > 0.0"); - assertQuery("SELECT e FROM Employee e WHERE e.salary / 2 > 0"); - assertQuery("SELECT e FROM Employee e WHERE e.salary / 2.0 > 0.0"); - assertQuery("SELECT e FROM Employee e WHERE ABS(e.salary - e.manager.salary) > 0"); - assertQuery("SELECT e FROM Employee e WHERE COALESCE(e.salary, 0) > 0"); - assertQuery("SELECT e FROM Employee e WHERE CONCAT(e.firstName, ' ', e.lastName) = 'Bilbo'"); - assertQuery("SELECT e FROM Employee e WHERE CURRENT_DATE > CURRENT_TIME"); - assertQuery("SELECT e FROM Employee e WHERE CURRENT_TIME > CURRENT_TIMESTAMP"); - assertQuery("SELECT e FROM Employee e WHERE LENGTH(e.lastName) > 0"); - assertQuery("SELECT e FROM Employee e WHERE LOWER(e.lastName) = 'bilbo'"); - assertQuery("SELECT e FROM Employee e WHERE MOD(e.hoursWorked, 8) > 0"); - assertQuery("SELECT e FROM Employee e WHERE SQRT(o.RESULT) > 0.0"); - assertQuery("SELECT e FROM Employee e WHERE SUBSTRING(e.lastName, 0, 2) = 'Bilbo'"); - assertQuery("SELECT e FROM Employee e WHERE TRIM(TRAILING FROM e.lastName) = 'Bilbo'"); - assertQuery("SELECT e FROM Employee e WHERE TRIM(e.lastName) = 'Bilbo'"); - assertQuery("SELECT e FROM Employee e WHERE TRIM(LEADING '-' FROM e.lastName) = 'Bilbo'"); - assertQuery("SELECT e FROM Employee e WHERE UPPER(e.lastName) = 'BILBO'"); - assertQuery("SELECT e FROM Employee e WHERE CAST(e.salary NUMERIC(10, 2)) > 0.0"); - assertQuery("SELECT e FROM Employee e WHERE EXTRACT(YEAR FROM e.startDate) = '2023'"); - } - - @Test - void specialOperators() { - - assertQuery("SELECT toDo FROM Employee e JOIN e.toDoList toDo WHERE INDEX(toDo) = 1"); - assertQuery("SELECT p FROM Employee e JOIN e.priorities p WHERE KEY(p) = 'high'"); - assertQuery("SELECT e FROM Employee e WHERE SIZE(e.managedEmployees) < 2"); - assertQuery("SELECT e FROM Employee e WHERE e.managedEmployees IS EMPTY"); - assertQuery("SELECT e FROM Employee e WHERE 'write code' MEMBER OF e.responsibilities"); - assertQuery("SELECT p FROM Project p WHERE TYPE(p) = LargeProject"); - - /** - * NOTE: The following query has been altered to properly align with EclipseLink test code despite NOT matching - * their ref docs. See https://github.com/eclipse-ee4j/eclipselink/issues/1949 for more details. - */ - assertQuery("SELECT e FROM Employee e JOIN TREAT(e.projects AS LargeProject) p WHERE p.budget > 1000000"); - - assertQuery("SELECT p FROM Phone p WHERE FUNCTION('TO_NUMBER', p.areaCode) > 613"); - } - - @Test // GH-3314 - void isNullAndIsNotNull() { - - assertQuery("SELECT e FROM Employee e WHERE (e.active IS null OR e.active = true)"); - assertQuery("SELECT e FROM Employee e WHERE (e.active IS NULL OR e.active = true)"); - assertQuery("SELECT e FROM Employee e WHERE (e.active IS NOT null OR e.active = true)"); - assertQuery("SELECT e FROM Employee e WHERE (e.active IS NOT NULL OR e.active = true)"); - } - - @Test // GH-3496 - void lateralShouldBeAValidParameter() { - - assertQuery("select e from Employee e where e.lateral = :_lateral"); - assertQuery("select te from TestEntity te where te.lateral = :lateral"); - } - - @Test // GH-3136 - void intersect() { - - assertQuery(""" - SELECT e FROM Employee e JOIN e.phones p WHERE p.areaCode = :areaCode1 - INTERSECT SELECT e FROM Employee e JOIN e.phones p WHERE p.areaCode = :areaCode2 - """); - } - - @Test // GH-3136 - void except() { - - assertQuery(""" - SELECT e FROM Employee e - EXCEPT SELECT e FROM Employee e WHERE e.salary > e.manager.salary - """); - } - - @ParameterizedTest // GH-3136 - @ValueSource(strings = { "STRING", "INTEGER", "FLOAT", "DOUBLE" }) - void cast(String targetType) { - assertQuery("SELECT CAST(e.salary AS %s) FROM Employee e".formatted(targetType)); - } - - @ParameterizedTest // GH-3136 - @ValueSource(strings = { "LEFT", "RIGHT" }) - void leftRightStringFunctions(String keyword) { - assertQuery("SELECT %s(e.name, 3) FROM Employee e".formatted(keyword)); - } - - @Test // GH-3136 - void replaceStringFunctions() { - assertQuery("SELECT REPLACE(e.name, 'o', 'a') FROM Employee e"); - assertQuery("SELECT REPLACE(e.name, ' ', '_') FROM Employee e"); - } - - @Test // GH-3136 - void stringConcatWithPipes() { - assertQuery("SELECT e.firstname || e.lastname AS name FROM Employee e"); - } - -} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java index 7cc49745e..3d9b1bf1b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java @@ -70,6 +70,266 @@ class JpqlQueryRendererTests { .trim(); } + @Test + void selectQueries() { + + assertQuery("Select e FROM Employee e WHERE e.salary > 100000"); + assertQuery("Select e FROM Employee e WHERE e.id = :id"); + assertQuery("Select MAX(e.salary) FROM Employee e"); + assertQuery("Select e.firstName FROM Employee e"); + assertQuery("Select e.firstName, e.lastName FROM Employee e"); + } + + @Test + void selectClause() { + + assertQuery("SELECT COUNT(e) FROM Employee e"); + assertQuery("SELECT MAX(e.salary) FROM Employee e"); + assertQuery("SELECT NEW com.acme.reports.EmpReport(e.firstName, e.lastName, e.salary) FROM Employee e"); + } + + @Test + void fromClause() { + + assertQuery("SELECT e FROM Employee e"); + assertQuery("SELECT e, a FROM Employee e, MailingAddress a WHERE e.address = a.address"); + assertQuery("SELECT e FROM com.acme.Employee e"); + } + + @Test + void join() { + + assertQuery("SELECT e FROM Employee e JOIN e.address a WHERE a.city = :city"); + assertQuery("SELECT e FROM Employee e JOIN e.projects p JOIN e.projects p2 WHERE p.name = :p1 AND p2.name = :p2"); + } + + @Test + void joinFetch() { + + assertQuery("SELECT e FROM Employee e JOIN FETCH e.address"); + assertQuery("SELECT e FROM Employee e JOIN FETCH e.address a ORDER BY a.city"); + assertQuery("SELECT e FROM Employee e JOIN FETCH e.address AS a ORDER BY a.city"); + } + + @Test + void leftJoin() { + assertQuery("SELECT e FROM Employee e LEFT JOIN e.address a ORDER BY a.city"); + } + + @Test // GH-3277 + void numericLiterals() { + + assertQuery("SELECT e FROM Employee e WHERE e.id = 1234"); + assertQuery("SELECT e FROM Employee e WHERE e.id = 1234L"); + assertQuery("SELECT s FROM Stat s WHERE s.ratio > 3.14"); + assertQuery("SELECT s FROM Stat s WHERE s.ratio > 3.14F"); + assertQuery("SELECT s FROM Stat s WHERE s.ratio > 3.14e32D"); + } + + @Test // GH-3308 + void newWithStrings() { + assertQuery("select new com.example.demo.SampleObject(se.id, se.sampleValue, \"java\") from SampleEntity se"); + } + + @Test + void orderByClause() { + + assertQuery("SELECT e FROM Employee e ORDER BY e.lastName ASC, e.firstName ASC"); // Typo in EQL document + assertQuery("SELECT e FROM Employee e LEFT JOIN e.manager m ORDER BY m.lastName NULLS FIRST"); + assertQuery("SELECT e FROM Employee e ORDER BY e.address"); + } + + @Test + void groupByClause() { + + assertQuery("SELECT AVG(e.salary), e.address.city FROM Employee e GROUP BY e.address.city"); + assertQuery("SELECT e, COUNT(p) FROM Employee e LEFT JOIN e.projects p GROUP BY e"); + } + + @Test + void havingClause() { + assertQuery( + "SELECT AVG(e.salary), e.address.city FROM Employee e GROUP BY e.address.city HAVING AVG(e.salary) > 100000"); + } + + @Test // GH-3136 + void union() { + + assertQuery(""" + SELECT MAX(e.salary) FROM Employee e WHERE e.address.city = :city1 + UNION SELECT MAX(e.salary) FROM Employee e WHERE e.address.city = :city2 + """); + assertQuery(""" + SELECT e FROM Employee e JOIN e.phones p WHERE p.areaCode = :areaCode1 + INTERSECT SELECT e FROM Employee e JOIN e.phones p WHERE p.areaCode = :areaCode2 + """); + assertQuery(""" + SELECT e FROM Employee e + EXCEPT SELECT e FROM Employee e WHERE e.salary > e.manager.salary + """); + } + + @Test + void whereClause() { + // TBD + } + + @Test + void updateQueries() { + assertQuery("UPDATE Employee e SET e.salary = 60000 WHERE e.salary = 50000"); + } + + @Test + void deleteQueries() { + assertQuery("DELETE FROM Employee e WHERE e.department IS NULL"); + } + + @Test + void literals() { + + assertQuery("SELECT e FROM Employee e WHERE e.name = 'Bob'"); + assertQuery("SELECT e FROM Employee e WHERE e.id = 1234"); + assertQuery("SELECT e FROM Employee e WHERE e.id = 1234L"); + assertQuery("SELECT s FROM Stat s WHERE s.ratio > 3.14F"); + assertQuery("SELECT s FROM Stat s WHERE s.ratio > 3.14e32D"); + assertQuery("SELECT e FROM Employee e WHERE e.active = TRUE"); + assertQuery("SELECT e FROM Employee e WHERE e.startDate = {d'2012-01-03'}"); + assertQuery("SELECT e FROM Employee e WHERE e.startTime = {t'09:00:00'}"); + assertQuery("SELECT e FROM Employee e WHERE e.version = {ts'2012-01-03 09:00:00.000000001'}"); + assertQuery("SELECT e FROM Employee e WHERE e.gender = org.acme.Gender.MALE"); + assertQuery("UPDATE Employee e SET e.manager = NULL WHERE e.manager = :manager"); + } + + @Test + void functionsInSelect() { + + assertQuery("SELECT e.salary - 1000 FROM Employee e"); + assertQuery("SELECT e.salary + 1000 FROM Employee e"); + assertQuery("SELECT e.salary * 2 FROM Employee e"); + assertQuery("SELECT e.salary * 2.0 FROM Employee e"); + assertQuery("SELECT e.salary / 2 FROM Employee e"); + assertQuery("SELECT e.salary / 2.0 FROM Employee e"); + assertQuery("SELECT ABS(e.salary - e.manager.salary) FROM Employee e"); + assertQuery( + "select e from Employee e where case e.firstName when 'Bob' then 'Robert' when 'Jill' then 'Gillian' else '' end = 'Robert'"); + assertQuery( + "select case when e.firstName = 'Bob' then 'Robert' when e.firstName = 'Jill' then 'Gillian' else '' end from Employee e where e.firstName = 'Bob' or e.firstName = 'Jill'"); + assertQuery( + "select e from Employee e where case when e.firstName = 'Bob' then 'Robert' when e.firstName = 'Jill' then 'Gillian' else '' end = 'Robert'"); + assertQuery("SELECT COALESCE(e.salary, 0) FROM Employee e"); + assertQuery("SELECT CONCAT(e.firstName, ' ', e.lastName) FROM Employee e"); + assertQuery("SELECT e.name, CURRENT_DATE FROM Employee e"); + assertQuery("SELECT e.name, CURRENT_TIME FROM Employee e"); + assertQuery("SELECT e.name, CURRENT_TIMESTAMP FROM Employee e"); + assertQuery("SELECT LENGTH(e.lastName) FROM Employee e"); + assertQuery("SELECT LOWER(e.lastName) FROM Employee e"); + assertQuery("SELECT MOD(e.hoursWorked, 8) FROM Employee e"); + assertQuery("SELECT NULLIF(e.salary, 0) FROM Employee e"); + assertQuery("SELECT SQRT(o.RESULT) FROM Output o"); + assertQuery("SELECT SUBSTRING(e.lastName, 0, 2) FROM Employee e"); + assertQuery( + "SELECT TRIM(TRAILING FROM e.lastName), TRIM(e.lastName), TRIM(LEADING '-' FROM e.lastName) FROM Employee e"); + assertQuery("SELECT UPPER(e.lastName) FROM Employee e"); + assertQuery("SELECT CAST(e.salary NUMERIC(10, 2)) FROM Employee e"); + assertQuery("SELECT EXTRACT(YEAR FROM e.startDate) FROM Employee e"); + } + + @Test + void functionsInWhere() { + + assertQuery("SELECT e FROM Employee e WHERE e.salary - 1000 > 0"); + assertQuery("SELECT e FROM Employee e WHERE e.salary + 1000 > 0"); + assertQuery("SELECT e FROM Employee e WHERE e.salary * 2 > 0"); + assertQuery("SELECT e FROM Employee e WHERE e.salary * 2.0 > 0.0"); + assertQuery("SELECT e FROM Employee e WHERE e.salary / 2 > 0"); + assertQuery("SELECT e FROM Employee e WHERE e.salary / 2.0 > 0.0"); + assertQuery("SELECT e FROM Employee e WHERE ABS(e.salary - e.manager.salary) > 0"); + assertQuery("SELECT e FROM Employee e WHERE COALESCE(e.salary, 0) > 0"); + assertQuery("SELECT e FROM Employee e WHERE CONCAT(e.firstName, ' ', e.lastName) = 'Bilbo'"); + assertQuery("SELECT e FROM Employee e WHERE CURRENT_DATE > CURRENT_TIME"); + assertQuery("SELECT e FROM Employee e WHERE CURRENT_TIME > CURRENT_TIMESTAMP"); + assertQuery("SELECT e FROM Employee e WHERE LENGTH(e.lastName) > 0"); + assertQuery("SELECT e FROM Employee e WHERE LOWER(e.lastName) = 'bilbo'"); + assertQuery("SELECT e FROM Employee e WHERE MOD(e.hoursWorked, 8) > 0"); + assertQuery("SELECT e FROM Employee e WHERE SQRT(o.RESULT) > 0.0"); + assertQuery("SELECT e FROM Employee e WHERE SUBSTRING(e.lastName, 0, 2) = 'Bilbo'"); + assertQuery("SELECT e FROM Employee e WHERE TRIM(TRAILING FROM e.lastName) = 'Bilbo'"); + assertQuery("SELECT e FROM Employee e WHERE TRIM(e.lastName) = 'Bilbo'"); + assertQuery("SELECT e FROM Employee e WHERE TRIM(LEADING '-' FROM e.lastName) = 'Bilbo'"); + assertQuery("SELECT e FROM Employee e WHERE UPPER(e.lastName) = 'BILBO'"); + assertQuery("SELECT e FROM Employee e WHERE CAST(e.salary NUMERIC(10, 2)) > 0.0"); + assertQuery("SELECT e FROM Employee e WHERE EXTRACT(YEAR FROM e.startDate) = '2023'"); + } + + @Test + void specialOperators() { + + assertQuery("SELECT toDo FROM Employee e JOIN e.toDoList toDo WHERE INDEX(toDo) = 1"); + assertQuery("SELECT p FROM Employee e JOIN e.priorities p WHERE KEY(p) = 'high'"); + assertQuery("SELECT e FROM Employee e WHERE SIZE(e.managedEmployees) < 2"); + assertQuery("SELECT e FROM Employee e WHERE e.managedEmployees IS EMPTY"); + assertQuery("SELECT e FROM Employee e WHERE 'write code' MEMBER OF e.responsibilities"); + assertQuery("SELECT p FROM Project p WHERE TYPE(p) = LargeProject"); + + /** + * NOTE: The following query has been altered to properly align with EclipseLink test code despite NOT matching + * their ref docs. See https://github.com/eclipse-ee4j/eclipselink/issues/1949 for more details. + */ + assertQuery("SELECT e FROM Employee e JOIN TREAT(e.projects AS LargeProject) p WHERE p.budget > 1000000"); + + assertQuery("SELECT p FROM Phone p WHERE FUNCTION('TO_NUMBER', p.areaCode) > 613"); + } + + @Test // GH-3314 + void isNullAndIsNotNull() { + + assertQuery("SELECT e FROM Employee e WHERE (e.active IS null OR e.active = true)"); + assertQuery("SELECT e FROM Employee e WHERE (e.active IS NULL OR e.active = true)"); + assertQuery("SELECT e FROM Employee e WHERE (e.active IS NOT null OR e.active = true)"); + assertQuery("SELECT e FROM Employee e WHERE (e.active IS NOT NULL OR e.active = true)"); + } + + @Test // GH-3136 + void intersect() { + + assertQuery(""" + SELECT e FROM Employee e JOIN e.phones p WHERE p.areaCode = :areaCode1 + INTERSECT SELECT e FROM Employee e JOIN e.phones p WHERE p.areaCode = :areaCode2 + """); + } + + @Test // GH-3136 + void except() { + + assertQuery(""" + SELECT e FROM Employee e + EXCEPT SELECT e FROM Employee e WHERE e.salary > e.manager.salary + """); + } + + @ParameterizedTest // GH-3136 + @ValueSource(strings = { "STRING", "INTEGER", "FLOAT", "DOUBLE" }) + void cast(String targetType) { + assertQuery("SELECT CAST(e.salary AS %s) FROM Employee e".formatted(targetType)); + } + + @ParameterizedTest // GH-3136 + @ValueSource(strings = { "LEFT", "RIGHT" }) + void leftRightStringFunctions(String keyword) { + assertQuery("SELECT %s(e.name, 3) FROM Employee e".formatted(keyword)); + } + + @Test // GH-3136 + void replaceStringFunctions() { + assertQuery("SELECT REPLACE(e.name, 'o', 'a') FROM Employee e"); + assertQuery("SELECT REPLACE(e.name, ' ', '_') FROM Employee e"); + } + + @Test // GH-3136 + void stringConcatWithPipes() { + assertQuery("SELECT e.firstname || e.lastname AS name FROM Employee e"); + } + /** * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example */ @@ -348,6 +608,38 @@ class JpqlQueryRendererTests { assertQuery("SELECT e FROM Employee e WHERE CAST(e.salary NUMERIC(10, 2)) > 0.0"); } + @Test // GH-3136 + void substring() { + + assertQuery("select substring(c.number, 1, 2) " + // + "from Call c"); + + assertQuery("select substring(c.number, 1) " + // + "from Call c"); + } + + @Test // GH-3136 + void currentDateFunctions() { + + assertQuery("select CURRENT_DATE " + // + "from Call c "); + + assertQuery("select CURRENT_TIME " + // + "from Call c "); + + assertQuery("select CURRENT_TIMESTAMP " + // + "from Call c "); + + assertQuery("select LOCAL_DATE " + // + "from Call c "); + + assertQuery("select LOCAL_TIME " + // + "from Call c "); + + assertQuery("select LOCAL_DATETIME " + // + "from Call c "); + } + @Test void pathExpressionsNamedParametersExample() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlSpecificationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlSpecificationTests.java deleted file mode 100644 index 566bfb880..000000000 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlSpecificationTests.java +++ /dev/null @@ -1,941 +0,0 @@ -/* - * Copyright 2022-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.jpa.repository.query; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import org.springframework.data.jpa.repository.query.QueryRenderer.TokenRenderer; - -/** - * Tests built around examples of JPQL found in the JPA spec - * https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc
- *
- * IMPORTANT: Purely verifies the parser without any transformations. - * - * @author Greg Turnquist - * @since 3.1 - */ -class JpqlSpecificationTests { - - private static final String SPEC_FAULT = "Disabled due to spec fault> "; - - /** - * Parse the query using {@link HqlParser} then run it through the query-preserving {@link HqlQueryRenderer}. - */ - private static String parseWithoutChanges(String query) { - - JpaQueryEnhancer.JpqlQueryParser parser = JpaQueryEnhancer.JpqlQueryParser.parseQuery(query); - - return TokenRenderer.render(new JpqlQueryRenderer().visit(parser.getContext())); - } - - private void assertQuery(String query) { - - String slimmedDownQuery = reduceWhitespace(query); - assertThat(parseWithoutChanges(slimmedDownQuery)).isEqualTo(slimmedDownQuery); - } - - private String reduceWhitespace(String original) { - - return original // - .replaceAll("[ \\t\\n]{1,}", " ") // - .trim(); - } - - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example - */ - @Test - void joinExample1() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order AS o JOIN o.lineItems AS l - WHERE l.shipped = FALSE - """); - } - - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#identification-variables - */ - @Test - void joinExample2() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order o JOIN o.lineItems l JOIN l.product p - WHERE p.productType = 'office_supplies' - """); - } - - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#range-variable-declarations - */ - @Test - void rangeVariableDeclarations() { - - assertQuery(""" - SELECT DISTINCT o1 - FROM Order o1, Order o2 - WHERE o1.quantity > o2.quantity AND - o2.customer.lastname = 'Smith' AND - o2.customer.firstname = 'John' - """); - } - - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions - */ - @Test - void pathExpressionsExample1() { - - assertQuery(""" - SELECT i.name, VALUE(p) - FROM Item i JOIN i.photos p - WHERE KEY(p) LIKE '%egret' - """); - } - - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions - */ - @Test - void pathExpressionsExample2() { - - assertQuery(""" - SELECT i.name, p - FROM Item i JOIN i.photos p - WHERE KEY(p) LIKE '%egret' - """); - } - - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions - */ - @Test - void pathExpressionsExample3() { - - assertQuery(""" - SELECT p.vendor - FROM Employee e JOIN e.contactInfo.phones p - """); - } - - /** - * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions - */ - @Test - void pathExpressionsExample4() { - - assertQuery(""" - SELECT p.vendor - FROM Employee e JOIN e.contactInfo c JOIN c.phones p - WHERE e.contactInfo.address.zipcode = '95054' - """); - } - - @Test - void pathExpressionSyntaxExample1() { - - assertQuery(""" - SELECT DISTINCT l.product - FROM Order AS o JOIN o.lineItems l - """); - } - - @Test - void joinsExample1() { - - assertQuery(""" - SELECT c FROM Customer c, Employee e WHERE c.hatsize = e.shoesize - """); - } - - @Test - void joinsExample2() { - - assertQuery(""" - SELECT c FROM Customer c JOIN c.orders o WHERE c.status = 1 - """); - } - - @Test - void joinsInnerExample() { - - assertQuery(""" - SELECT c FROM Customer c INNER JOIN c.orders o WHERE c.status = 1 - """); - } - - @Test - void joinsInExample() { - - assertQuery(""" - SELECT OBJECT(c) FROM Customer c, IN(c.orders) o WHERE c.status = 1 - """); - } - - @Test - void doubleJoinExample() { - - assertQuery(""" - SELECT p.vendor - FROM Employee e JOIN e.contactInfo c JOIN c.phones p - WHERE c.address.zipcode = '95054' - """); - } - - @Test - void leftJoinExample() { - - assertQuery(""" - SELECT s.name, COUNT(p) - FROM Suppliers s LEFT JOIN s.products p - GROUP BY s.name - """); - } - - @Test - void leftJoinOnExample() { - - assertQuery(""" - SELECT s.name, COUNT(p) - FROM Suppliers s LEFT JOIN s.products p - ON p.status = 'inStock' - GROUP BY s.name - """); - } - - @Test - void leftJoinWhereExample() { - - assertQuery(""" - SELECT s.name, COUNT(p) - FROM Suppliers s LEFT JOIN s.products p - WHERE p.status = 'inStock' - GROUP BY s.name - """); - } - - @Test - void leftJoinFetchExample() { - - assertQuery(""" - SELECT d - FROM Department d LEFT JOIN FETCH d.employees - WHERE d.deptno = 1 - """); - } - - @Test - void collectionMemberExample() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order o JOIN o.lineItems l - WHERE l.product.productType = 'office_supplies' - """); - } - - @Test - void collectionMemberInExample() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order o, IN(o.lineItems) l - WHERE l.product.productType = 'office_supplies' - """); - } - - @Test - void fromClauseExample() { - - assertQuery(""" - SELECT o - FROM Order AS o JOIN o.lineItems l JOIN l.product p - """); - } - - @Test - void fromClauseDowncastingExample1() { - - assertQuery(""" - SELECT b.name, b.ISBN - FROM Order o JOIN TREAT(o.product AS Book) b - """); - } - - @Test - void fromClauseDowncastingExample2() { - - assertQuery(""" - SELECT e FROM Employee e JOIN TREAT(e.projects AS LargeProject) lp - WHERE lp.budget > 1000 - """); - } - - /** - * @see #fromClauseDowncastingExample3fixed() - */ - @Test - @Disabled(SPEC_FAULT + "Use double-quotes when it should be using single-quotes for a string literal") - void fromClauseDowncastingExample3_SPEC_BUG() { - - assertQuery(""" - SELECT e FROM Employee e JOIN e.projects p - WHERE TREAT(p AS LargeProject).budget > 1000 - OR TREAT(p AS SmallProject).name LIKE 'Persist%' - OR p.description LIKE "cost overrun" - """); - } - - @Test - void fromClauseDowncastingExample3fixed() { - - assertQuery(""" - SELECT e FROM Employee e JOIN e.projects p - WHERE TREAT(p AS LargeProject).budget > 1000 - OR TREAT(p AS SmallProject).name LIKE 'Persist%' - OR p.description LIKE 'cost overrun' - """); - } - - @Test - void fromClauseDowncastingExample4() { - - assertQuery(""" - SELECT e FROM Employee e - WHERE TREAT(e AS Exempt).vacationDays > 10 - OR TREAT(e AS Contractor).hours > 100 - """); - } - - @Test // GH-3136 - void substring() { - - assertQuery("select substring(c.number, 1, 2) " + // - "from Call c"); - - assertQuery("select substring(c.number, 1) " + // - "from Call c"); - } - - @Test // GH-3136 - void currentDateFunctions() { - - assertQuery("select CURRENT_DATE " + // - "from Call c "); - - assertQuery("select CURRENT_TIME " + // - "from Call c "); - - assertQuery("select CURRENT_TIMESTAMP " + // - "from Call c "); - - assertQuery("select LOCAL_DATE " + // - "from Call c "); - - assertQuery("select LOCAL_TIME " + // - "from Call c "); - - assertQuery("select LOCAL_DATETIME " + // - "from Call c "); - } - - @Test - void pathExpressionsNamedParametersExample() { - - assertQuery(""" - SELECT c - FROM Customer c - WHERE c.status = :stat - """); - } - - @Test - void betweenExpressionsExample() { - - assertQuery(""" - SELECT t - FROM CreditCard c JOIN c.transactionHistory t - WHERE c.holder.name = 'John Doe' AND INDEX(t) BETWEEN 0 AND 9 - """); - } - - @Test - void isEmptyExample() { - - assertQuery(""" - SELECT o - FROM Order o - WHERE o.lineItems IS EMPTY - """); - } - - @Test - void memberOfExample() { - - assertQuery(""" - SELECT p - FROM Person p - WHERE 'Joe' MEMBER OF p.nicknames - """); - } - - @Test - void existsSubSelectExample1() { - - assertQuery(""" - SELECT DISTINCT emp - FROM Employee emp - WHERE EXISTS (SELECT spouseEmp - FROM Employee spouseEmp - WHERE spouseEmp = emp.spouse) - """); - } - - @Test - void allExample() { - - assertQuery(""" - SELECT emp - FROM Employee emp - WHERE emp.salary > ALL (SELECT m.salary - FROM Manager m - WHERE m.department = emp.department) - """); - } - - @Test - void existsSubSelectExample2() { - - assertQuery(""" - SELECT DISTINCT emp - FROM Employee emp - WHERE EXISTS (SELECT spouseEmp - FROM Employee spouseEmp - WHERE spouseEmp = emp.spouse) - """); - } - - @Test - void subselectNumericComparisonExample1() { - - assertQuery(""" - SELECT c - FROM Customer c - WHERE (SELECT AVG(o.price) FROM c.orders o) > 100 - """); - } - - @Test - void subselectNumericComparisonExample2() { - - assertQuery(""" - SELECT goodCustomer - FROM Customer goodCustomer - WHERE goodCustomer.balanceOwed < (SELECT AVG(c.balanceOwed) / 2.0 FROM Customer c) - """); - } - - @Test - void indexExample() { - - assertQuery(""" - SELECT w.name - FROM Course c JOIN c.studentWaitlist w - WHERE c.name = 'Calculus' - AND INDEX(w) = 0 - """); - } - - /** - * @see #functionInvocationExampleWithCorrection() - */ - @Test - @Disabled(SPEC_FAULT + "FUNCTION calls needs a comparator") - void functionInvocationExample_SPEC_BUG() { - - assertQuery(""" - SELECT c - FROM Customer c - WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) - """); - } - - @Test - void functionInvocationExampleWithCorrection() { - - assertQuery(""" - SELECT c - FROM Customer c - WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) = TRUE - """); - } - - @Test - void updateCaseExample1() { - - assertQuery(""" - UPDATE Employee e - SET e.salary = - CASE WHEN e.rating = 1 THEN e.salary * 1.1 - WHEN e.rating = 2 THEN e.salary * 1.05 - ELSE e.salary * 1.01 - END - """); - } - - @Test - void updateCaseExample2() { - - assertQuery(""" - UPDATE Employee e - SET e.salary = - CASE e.rating WHEN 1 THEN e.salary * 1.1 - WHEN 2 THEN e.salary * 1.05 - ELSE e.salary * 1.01 - END - """); - } - - @Test - void selectCaseExample1() { - - assertQuery(""" - SELECT e.name, - CASE TYPE(e) WHEN Exempt THEN 'Exempt' - WHEN Contractor THEN 'Contractor' - WHEN Intern THEN 'Intern' - ELSE 'NonExempt' - END - FROM Employee e - WHERE e.dept.name = 'Engineering' - """); - } - - @Test - void selectCaseExample2() { - - assertQuery(""" - SELECT e.name, - f.name, - CONCAT(CASE WHEN f.annualMiles > 50000 THEN 'Platinum ' - WHEN f.annualMiles > 25000 THEN 'Gold ' - ELSE '' - END, - 'Frequent Flyer') - FROM Employee e JOIN e.frequentFlierPlan f - """); - } - - @Test - void theRest() { - - assertQuery(""" - SELECT e - FROM Employee e - WHERE TYPE(e) IN (Exempt, Contractor) - """); - } - - @Test - void theRest2() { - - assertQuery(""" - SELECT e - FROM Employee e - WHERE TYPE(e) IN (:empType1, :empType2) - """); - } - - @Test - void theRest3() { - - assertQuery(""" - SELECT e - FROM Employee e - WHERE TYPE(e) IN :empTypes - """); - } - - @Test - void theRest4() { - - assertQuery(""" - SELECT TYPE(e) - FROM Employee e - WHERE TYPE(e) <> Exempt - """); - } - - @Test - void theRest5() { - - assertQuery(""" - SELECT c.status, AVG(c.filledOrderCount), COUNT(c) - FROM Customer c - GROUP BY c.status - HAVING c.status IN (1, 2) - """); - } - - @Test - void theRest6() { - - assertQuery(""" - SELECT c.country, COUNT(c) - FROM Customer c - GROUP BY c.country - HAVING COUNT(c) > 30 - """); - } - - @Test - void theRest7() { - - assertQuery(""" - SELECT c, COUNT(o) - FROM Customer c JOIN c.orders o - GROUP BY c - HAVING COUNT(o) >= 5 - """); - } - - @Test - void theRest8() { - - assertQuery(""" - SELECT c.id, c.status - FROM Customer c JOIN c.orders o - WHERE o.count > 100 - """); - } - - @Test - void theRest9() { - - assertQuery(""" - SELECT v.location.street, KEY(i).title, VALUE(i) - FROM VideoStore v JOIN v.videoInventory i - WHERE v.location.zipcode = '94301' AND VALUE(i) > 0 - """); - } - - @Test - void theRest10() { - - assertQuery(""" - SELECT o.lineItems FROM Order AS o - """); - } - - @Test - void theRest11() { - - assertQuery(""" - SELECT c, COUNT(l) AS itemCount - FROM Customer c JOIN c.Orders o JOIN o.lineItems l - WHERE c.address.state = 'CA' - GROUP BY c - ORDER BY itemCount - """); - } - - @Test - void theRest12() { - - assertQuery(""" - SELECT NEW com.acme.example.CustomerDetails(c.id, c.status, o.count) - FROM Customer c JOIN c.orders o - WHERE o.count > 100 - """); - } - - @Test - void theRest13() { - - assertQuery(""" - SELECT e.address AS addr - FROM Employee e - """); - } - - @Test - void theRest14() { - - assertQuery(""" - SELECT AVG(o.quantity) FROM Order o - """); - } - - @Test - void theRest15() { - - assertQuery(""" - SELECT SUM(l.price) - FROM Order o JOIN o.lineItems l JOIN o.customer c - WHERE c.lastname = 'Smith' AND c.firstname = 'John' - """); - } - - @Test - void theRest16() { - - assertQuery(""" - SELECT COUNT(o) FROM Order o - """); - } - - @Test - void theRest17() { - - assertQuery(""" - SELECT COUNT(l.price) - FROM Order o JOIN o.lineItems l JOIN o.customer c - WHERE c.lastname = 'Smith' AND c.firstname = 'John' - """); - } - - @Test - void theRest18() { - - assertQuery(""" - SELECT COUNT(l) - FROM Order o JOIN o.lineItems l JOIN o.customer c - WHERE c.lastname = 'Smith' AND c.firstname = 'John' AND l.price IS NOT NULL - """); - } - - @Test - void theRest19() { - - assertQuery(""" - SELECT o - FROM Customer c JOIN c.orders o JOIN c.address a - WHERE a.state = 'CA' - ORDER BY o.quantity DESC, o.totalcost - """); - } - - @Test - void theRest20() { - - assertQuery(""" - SELECT o.quantity, a.zipcode - FROM Customer c JOIN c.orders o JOIN c.address a - WHERE a.state = 'CA' - ORDER BY o.quantity, a.zipcode - """); - } - - @Test - void theRest21() { - - assertQuery(""" - SELECT o.quantity, o.cost * 1.08 AS taxedCost, a.zipcode - FROM Customer c JOIN c.orders o JOIN c.address a - WHERE a.state = 'CA' AND a.county = 'Santa Clara' - ORDER BY o.quantity, taxedCost, a.zipcode - """); - } - - @Test - void theRest22() { - - assertQuery(""" - SELECT AVG(o.quantity) as q, a.zipcode - FROM Customer c JOIN c.orders o JOIN c.address a - WHERE a.state = 'CA' - GROUP BY a.zipcode - ORDER BY q DESC - """); - } - - @Test - void theRest23() { - - assertQuery(""" - SELECT p.product_name - FROM Order o JOIN o.lineItems l JOIN l.product p JOIN o.customer c - WHERE c.lastname = 'Smith' AND c.firstname = 'John' - ORDER BY p.price - """); - } - - /** - * This query is specifically dubbed illegal in the spec. It may actually be failing for a different reason. - */ - @Test - void theRest24() { - - assertThatExceptionOfType(BadJpqlGrammarException.class).isThrownBy(() -> { - assertQuery(""" - SELECT p.product_name - FROM Order o, IN(o.lineItems) l JOIN o.customer c - WHERE c.lastname = 'Smith' AND c.firstname = 'John' - ORDER BY o.quantity - """); - }); - } - - @Test - void theRest25() { - - assertQuery(""" - DELETE - FROM Customer c - WHERE c.status = 'inactive' - """); - } - - @Test - void theRest26() { - - assertQuery(""" - DELETE - FROM Customer c - WHERE c.status = 'inactive' - AND c.orders IS EMPTY - """); - } - - @Test - void theRest27() { - - assertQuery(""" - UPDATE Customer c - SET c.status = 'outstanding' - WHERE c.balance < 10000 - """); - } - - @Test - void theRest28() { - - assertQuery(""" - UPDATE Employee e - SET e.address.building = 22 - WHERE e.address.building = 14 - AND e.address.city = 'Santa Clara' - AND e.project = 'Jakarta EE' - """); - } - - @Test - void theRest29() { - - assertQuery(""" - SELECT o - FROM Order o - """); - } - - @Test - void theRest30() { - - assertQuery(""" - SELECT o - FROM Order o - WHERE o.shippingAddress.state = 'CA' - """); - } - - @Test - void theRest31() { - - assertQuery(""" - SELECT DISTINCT o.shippingAddress.state - FROM Order o - """); - } - - @Test - void theRest32() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order o JOIN o.lineItems l - """); - } - - @Test - void theRest33() { - - assertQuery(""" - SELECT o - FROM Order o - WHERE o.lineItems IS NOT EMPTY - """); - } - - @Test - void theRest34() { - - assertQuery(""" - SELECT o - FROM Order o - WHERE o.lineItems IS EMPTY - """); - } - - @Test - void theRest35() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order o JOIN o.lineItems l - WHERE l.shipped = FALSE - """); - } - - @Test - void theRest36() { - - assertQuery(""" - SELECT o - FROM Order o - WHERE - NOT (o.shippingAddress.state = o.billingAddress.state AND - o.shippingAddress.city = o.billingAddress.city AND - o.shippingAddress.street = o.billingAddress.street) - """); - } - - @Test - void theRest37() { - - assertQuery(""" - SELECT o - FROM Order o - WHERE o.shippingAddress <> o.billingAddress - """); - } - - @Test - void theRest38() { - - assertQuery(""" - SELECT DISTINCT o - FROM Order o JOIN o.lineItems l - WHERE l.product.name = ?1 - """); - } -} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java index 3113627c8..163a91dd9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java @@ -80,7 +80,7 @@ class QueryEnhancerUnitTests { @MethodSource("detectsAliasWithUCorrectlySource") void detectsAliasWithUCorrectly(DeclaredQuery query, String alias) { - assumeThat(query.getQueryString()).as("JsqlParser does not support simple JPA syntax.") + assumeThat(query.getQueryString()).as("JsqlParser does not support simple JPA syntax") .doesNotStartWithIgnoringCase("from"); assertThat(getEnhancer(query).detectAlias()).isEqualTo(alias);