diff --git a/README.md b/README.md index a3122f57f5..f8e50cb84e 100644 --- a/README.md +++ b/README.md @@ -494,15 +494,6 @@ In IntelliJ when defining the Oracle database, go to the Advanced tab an specify When using the Oracle docker container via `docker_db.sh oracle` you might want to specify the following properties when executing tests `-Djdbc.url=jdbc:oracle:thin:@192.168.99.100:1521/xe -Djdbc.user=SYSTEM -Djdbc.password=oracle` -### JDBC Driver - -You have to install the JDBC driver manually. If you install Oracle XE locally, you can take it from $ORACLE_HOME/jdbc otherwise download it from http://www.oracle.com/technetwork/database/features/jdbc/index-091264.html -Copy the jar to $M2_HOME/com/oracle/ojdbc14/10.2.0.4.0/ojdbc14-10.2.0.4.0.jar and you should be good to go. - -If you use the docker container, extract the jdbc driver from the container via `docker cp oracle:/u01/app/oracle/product/11.2.0/xe/jdbc/lib/ojdbc6.jar ojdbc.jar` - -`mvn -q install:install-file -Dfile=ojdbc.jar -DgroupId=com.oracle -DartifactId=ojdbc14 -Dversion=10.2.0.4.0 -Dpackaging=jar -DgeneratePom=true` - ### Install Oracle locally Download Oracle XE from http://www.oracle.com/technetwork/database/database-technologies/express-edition/downloads/index.html diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/function/jsonget/PostgreSQLJsonGetFunction.java b/core/impl/src/main/java/com/blazebit/persistence/impl/function/jsonget/PostgreSQLJsonGetFunction.java index 39fec4e81c..53145fa862 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/function/jsonget/PostgreSQLJsonGetFunction.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/function/jsonget/PostgreSQLJsonGetFunction.java @@ -26,8 +26,9 @@ public class PostgreSQLJsonGetFunction extends AbstractJsonGetFunction { @Override protected void render0(FunctionRenderContext context) { + context.addChunk("cast("); context.addArgument(0); - context.addChunk("::json"); + context.addChunk(" as json)"); context.addChunk("#>>'{"); addUnquotedArgument(context, 1); for (int i = 2; i < context.getArgumentsSize(); i++) { diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/function/jsonset/MSSQLJsonSetFunction.java b/core/impl/src/main/java/com/blazebit/persistence/impl/function/jsonset/MSSQLJsonSetFunction.java index 1ee7d924a8..aba897d958 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/function/jsonset/MSSQLJsonSetFunction.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/function/jsonset/MSSQLJsonSetFunction.java @@ -45,9 +45,15 @@ protected void render0(FunctionRenderContext context) { context.addArgument(0); context.addChunk(", '" + jsonPath + "', CONVERT(bit, json_value(concat('{\"val\": ', temp.val, '}'), '$.val'))) "); - context.addChunk("else json_modify("); + context.addChunk("else case when LOWER(temp.val) = 'null' "); + context.addChunk("then "); + context.addChunk("json_modify("); context.addArgument(0); - context.addChunk(", '" + jsonPath + "', json_value(concat('{\"val\": ', temp.val, '}'), '$.val')) end"); + context.addChunk(", 'strict " + jsonPath + "', json_value(concat('{\"val\": ', temp.val, '}'), '$.val')) "); + context.addChunk("else "); + context.addChunk("json_modify("); + context.addArgument(0); + context.addChunk(", '" + jsonPath + "', json_value(concat('{\"val\": ', temp.val, '}'), '$.val')) end end"); context.addChunk(") else json_modify("); context.addArgument(0); diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/function/jsonset/MySQL8JsonSetFunction.java b/core/impl/src/main/java/com/blazebit/persistence/impl/function/jsonset/MySQL8JsonSetFunction.java index bea8ae3922..8d3d833144 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/function/jsonset/MySQL8JsonSetFunction.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/function/jsonset/MySQL8JsonSetFunction.java @@ -31,6 +31,11 @@ protected void render0(FunctionRenderContext context) { List jsonPathElements = AbstractJsonGetFunction.retrieveJsonPathElements(context, 2); context.addChunk("(select "); + context.addChunk("case when lower(temp.val) = 'null' then json_set("); + context.addArgument(0); + context.addChunk(",'"); + context.addChunk(AbstractJsonGetFunction.toJsonPath(jsonPathElements, jsonPathElements.size(), true)); + context.addChunk("', null) else "); context.addChunk("json_merge_patch("); context.addArgument(0); context.addChunk(", concat('"); @@ -44,7 +49,7 @@ protected void render0(FunctionRenderContext context) { for (int i = jsonPathElements.size() - 1; i >= 0; i--) { endJsonPathElement(context, jsonPathElements, i); } - context.addChunk("'))"); + context.addChunk("')) end"); context.addChunk(" from (values row("); context.addArgument(1); diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/function/jsonset/PostgreSQLJsonSetFunction.java b/core/impl/src/main/java/com/blazebit/persistence/impl/function/jsonset/PostgreSQLJsonSetFunction.java index 9bd4d1e5d8..4e192d57fc 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/function/jsonset/PostgreSQLJsonSetFunction.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/function/jsonset/PostgreSQLJsonSetFunction.java @@ -25,9 +25,9 @@ public class PostgreSQLJsonSetFunction extends AbstractJsonSetFunction { @Override protected void render0(FunctionRenderContext context) { - context.addChunk("jsonb_set("); + context.addChunk("jsonb_set(cast("); context.addArgument(0); - context.addChunk("::jsonb,"); + context.addChunk(" as jsonb),"); context.addChunk("'{"); addUnquotedArgument(context, 2); for (int i = 3; i < context.getArgumentsSize(); i++) { diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/util/JpqlFunctionUtil.java b/core/impl/src/main/java/com/blazebit/persistence/impl/util/JpqlFunctionUtil.java index 1d23bddcbc..c219400e8f 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/util/JpqlFunctionUtil.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/util/JpqlFunctionUtil.java @@ -26,20 +26,38 @@ private JpqlFunctionUtil() { } public static String unquoteSingleQuotes(String s) { - if (s.length() >= 2) { - if (s.charAt(0) == '\'' && s.charAt(s.length() - 1) == '\'') { - return s.substring(1, s.length() - 1); - } - } - return s; + return unquote(s, '\''); } public static String unquoteDoubleQuotes(String s) { - if (s.length() >= 2) { - if (s.charAt(0) == '\"' && s.charAt(s.length() - 1) == '\"') { - return s.substring(1, s.length() - 1); + return unquote(s, '\"'); + } + + private static String unquote(String s, char quoteCharacter) { + if (!s.isEmpty() && s.charAt(0) != quoteCharacter) { + return s; + } + StringBuilder sb = new StringBuilder(s.length()); + boolean quote = false; + for (int i = 1; i < s.length() - 1; i++) { + final char c = s.charAt(i); + if (quote) { + quote = false; + if (c != quoteCharacter) { + sb.append(quoteCharacter); + } + sb.append(c); + } else { + if (c == quoteCharacter) { + quote = true; + } else { + sb.append(c); + } } } - return s; + if (quote) { + sb.append(quoteCharacter); + } + return sb.toString(); } } diff --git a/core/impl/src/test/java/com/blazebit/persistence/impl/util/JpqlFunctionUtilTest.java b/core/impl/src/test/java/com/blazebit/persistence/impl/util/JpqlFunctionUtilTest.java index ae4ed6af45..6a2faded56 100644 --- a/core/impl/src/test/java/com/blazebit/persistence/impl/util/JpqlFunctionUtilTest.java +++ b/core/impl/src/test/java/com/blazebit/persistence/impl/util/JpqlFunctionUtilTest.java @@ -26,9 +26,16 @@ public class JpqlFunctionUtilTest { @Test - public void testUnquote() { + public void testUnquoteSingleQuotes() { assertEquals("0", JpqlFunctionUtil.unquoteSingleQuotes("0")); assertEquals("0", JpqlFunctionUtil.unquoteSingleQuotes("'0'")); assertEquals("'0'", JpqlFunctionUtil.unquoteSingleQuotes("''0''")); } + + @Test + public void testUnquoteDoubleQuotes() { + assertEquals("0", JpqlFunctionUtil.unquoteDoubleQuotes("0")); + assertEquals("0", JpqlFunctionUtil.unquoteDoubleQuotes("\"0\"")); + assertEquals("\"0\"", JpqlFunctionUtil.unquoteDoubleQuotes("\"\"0\"\"")); + } } diff --git a/core/testsuite/pom.xml b/core/testsuite/pom.xml index f97e493683..687c14d044 100644 --- a/core/testsuite/pom.xml +++ b/core/testsuite/pom.xml @@ -594,7 +594,6 @@ com.oracle.database.jdbc ojdbc8 - 18.3.0.0 test diff --git a/core/testsuite/src/test/java/com/blazebit/persistence/testsuite/JsonGetAndSetTest.java b/core/testsuite/src/test/java/com/blazebit/persistence/testsuite/JsonGetAndSetTest.java index dd66e1dc2b..1025086ffe 100644 --- a/core/testsuite/src/test/java/com/blazebit/persistence/testsuite/JsonGetAndSetTest.java +++ b/core/testsuite/src/test/java/com/blazebit/persistence/testsuite/JsonGetAndSetTest.java @@ -165,11 +165,11 @@ public void testJsonSet() throws JsonProcessingException { } @Test - @Category({ NoH2.class, NoSQLite.class, NoFirebird.class, NoMySQL.class, NoOracle.class }) + @Category({ NoH2.class, NoSQLite.class, NoFirebird.class, NoMySQLOld.class, NoOracle.class }) public void testJsonSetNull() throws JsonProcessingException { List objectRootResult = cbf.create(em, Tuple.class).from(JsonDocument.class, "d") .select("d.content") - // json_set with value null not supported for Oracle and MySQL + // json_set with value null not supported for Oracle .select("json_set(d.content, 'null', 'K1', 0, 'K2')") .select("json_set(d.content, 'null', 'K1', 0)") .where("id").eq(1L) diff --git a/documentation/src/main/asciidoc/core/manual/en_US/jpql_functions.adoc b/documentation/src/main/asciidoc/core/manual/en_US/jpql_functions.adoc index 8e2befa56d..e986c0e93f 100644 --- a/documentation/src/main/asciidoc/core/manual/en_US/jpql_functions.adoc +++ b/documentation/src/main/asciidoc/core/manual/en_US/jpql_functions.adoc @@ -531,6 +531,24 @@ Syntax: `BASE64 ( bytes )` Returns a Base64 encoded string that represents the passed bytes. +==== JSON_GET + +Sytax: `JSON_GET(jsonDocument, pathSegment1, ..., pathSegmentN)` + +Where `pathSegmentN` is a quoted json key or array index. + +Returns the json node within the `jsonDocument` designated by the path segments. + +==== JSON_SET + +Sytax: `JSON_SET(jsonDocument, newValue, pathSegment1, ..., pathSegmentN)` + +Where `newValue` is a quoted json node and `pathSegmentN` is a quoted json key or array index. + +Returns the modified `jsonDocument` that results from replacing the json node designated by the path segments with `newValue`. + +Setting JSON `null` is not supported for Oracle. + ==== STRING_JSON_AGG function Syntax: `STRING_JSON_AGG ( key1, value1, ..., keyN, valueN )` diff --git a/entity-view/testsuite/pom.xml b/entity-view/testsuite/pom.xml index 94b660a871..c689081f43 100644 --- a/entity-view/testsuite/pom.xml +++ b/entity-view/testsuite/pom.xml @@ -444,10 +444,10 @@ - com.oracle - ojdbc14 - test - + com.oracle.database.jdbc + ojdbc8 + test + diff --git a/integration/deltaspike-data/testsuite/pom.xml b/integration/deltaspike-data/testsuite/pom.xml index b73447a626..952702f787 100644 --- a/integration/deltaspike-data/testsuite/pom.xml +++ b/integration/deltaspike-data/testsuite/pom.xml @@ -393,8 +393,8 @@ - com.oracle - ojdbc14 + com.oracle.database.jdbc + ojdbc8 test diff --git a/integration/querydsl/testsuite/pom.xml b/integration/querydsl/testsuite/pom.xml index 3f8a735bbf..b5d9e3c5e2 100644 --- a/integration/querydsl/testsuite/pom.xml +++ b/integration/querydsl/testsuite/pom.xml @@ -363,8 +363,8 @@ - com.oracle - ojdbc14 + com.oracle.database.jdbc + ojdbc8 test diff --git a/integration/spring-data/testsuite/webflux/pom.xml b/integration/spring-data/testsuite/webflux/pom.xml index f01c720d8d..5802eb7a45 100644 --- a/integration/spring-data/testsuite/webflux/pom.xml +++ b/integration/spring-data/testsuite/webflux/pom.xml @@ -412,8 +412,8 @@ - com.oracle - ojdbc14 + com.oracle.database.jdbc + ojdbc8 test diff --git a/integration/spring-data/testsuite/webmvc/pom.xml b/integration/spring-data/testsuite/webmvc/pom.xml index 578d7304af..4f1fa0ce45 100644 --- a/integration/spring-data/testsuite/webmvc/pom.xml +++ b/integration/spring-data/testsuite/webmvc/pom.xml @@ -422,8 +422,8 @@ - com.oracle - ojdbc14 + com.oracle.database.jdbc + ojdbc8 test diff --git a/jpa-criteria/testsuite/src/test/java/com/blazebit/persistence/criteria/UpdateTest.java b/jpa-criteria/testsuite/src/test/java/com/blazebit/persistence/criteria/UpdateTest.java index f2a19b8af6..30dbafc487 100644 --- a/jpa-criteria/testsuite/src/test/java/com/blazebit/persistence/criteria/UpdateTest.java +++ b/jpa-criteria/testsuite/src/test/java/com/blazebit/persistence/criteria/UpdateTest.java @@ -93,6 +93,6 @@ public void setCorrelatedSubqueryExpression() { UpdateCriteriaBuilder criteriaBuilder = query.createCriteriaBuilder(em); assertEquals("UPDATE Document d SET d.idx = (SELECT " + function("CAST_INTEGER", "COUNT(owner)") + " FROM d.owner owner) + 1", criteriaBuilder.getQueryString()); - criteriaBuilder.getQuery().executeUpdate(); + criteriaBuilder.getQuery(); } } diff --git a/parent/pom.xml b/parent/pom.xml index c2305ef4d5..7785273418 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -568,9 +568,9 @@ test - com.oracle - ojdbc14 - 10.2.0.4.0 + com.oracle.database.jdbc + ojdbc8 + 18.3.0.0 test diff --git a/travis/before_script_oracle.sh b/travis/before_script_oracle.sh index 68ef102179..63227b7007 100644 --- a/travis/before_script_oracle.sh +++ b/travis/before_script_oracle.sh @@ -3,7 +3,4 @@ # Sets up environment for Blaze-Persistence backend MSSQL at travis-ci.com # -docker run --shm-size=1536m --name oracle -d -p 1521:1521 quillbuilduser/oracle-18-xe - -docker cp oracle:/u01/app/oracle/product/11.2.0/xe/jdbc/lib/ojdbc6.jar ojdbc.jar -mvn install:install-file -Dfile=ojdbc.jar -DgroupId=com.oracle -DartifactId=ojdbc14 -Dversion=10.2.0.4.0 -Dpackaging=jar -DgeneratePom=true +docker run --shm-size=1536m --name oracle -d -p 1521:1521 quillbuilduser/oracle-18-xe \ No newline at end of file