Skip to content

Commit

Permalink
[Blazebit#841] Add json functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Mobe91 committed Jul 22, 2020
1 parent d95f283 commit 56c72e9
Show file tree
Hide file tree
Showing 40 changed files with 1,357 additions and 93 deletions.
28 changes: 14 additions & 14 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ before_script:
source setup-graalvm.sh
fi
- bash -c "if [ '$RDBMS' = 'mysql' ]; then MYSQL_VERSION=5.7 bash travis/before_script_mysql.sh; fi"
- bash -c "if [ '$RDBMS' = 'mysql8' ]; then MYSQL_VERSION=8.0 bash travis/before_script_mysql.sh; fi"
- bash -c "if [ '$RDBMS' = 'mysql8' ]; then MYSQL_VERSION=8.0.21 bash travis/before_script_mysql.sh; fi"
- bash -c "if [ '$RDBMS' = 'postgresql' ]; then psql -c 'create database test;' -U postgres; fi"
- bash -c "if [ '$RDBMS' = 'db2' ]; then bash travis/before_script_db2.sh; fi"
- bash -c "if [ '$RDBMS' = 'firebird' ]; then bash travis/before_script_firebird.sh; fi"
Expand Down Expand Up @@ -62,7 +62,7 @@ matrix:
- env: JPAPROVIDER=hibernate-5.2 RDBMS=postgresql
jdk: oraclejdk8
addons:
postgresql: "9.4"
postgresql: "9.5"
- env: JPAPROVIDER=hibernate-5.2 RDBMS=mssql
jdk: oraclejdk8
sudo: true
Expand All @@ -89,7 +89,7 @@ matrix:
- env: JPAPROVIDER=hibernate-apt RDBMS=postgresql SPRING_DATA=spring-data-2.3.x DELTASPIKE=deltaspike-1.9
jdk: oraclejdk8
addons:
postgresql: "9.4"
postgresql: "9.5"
# - env: JPAPROVIDER=hibernate-5.2 RDBMS=sqlite
# jdk: oraclejdk8
# - env: JPAPROVIDER=hibernate-5.2 RDBMS=firebird
Expand All @@ -107,7 +107,7 @@ matrix:
- env: JPAPROVIDER=datanucleus-5 RDBMS=postgresql
jdk: oraclejdk8
addons:
postgresql: "9.4"
postgresql: "9.5"
# - env: JPAPROVIDER=datanucleus-5 RDBMS=mssql
# jdk: oraclejdk8
# sudo: true
Expand Down Expand Up @@ -138,7 +138,7 @@ matrix:
# sudo: required
# - env: JPAPROVIDER=eclipselink RDBMS=postgresql
# addons:
# postgresql: "9.4"
# postgresql: "9.5"
# - env: JPAPROVIDER=eclipselink RDBMS=mssql
# jdk: oraclejdk8
# sudo: true
Expand Down Expand Up @@ -166,7 +166,7 @@ matrix:
# sudo: required
# - env: JPAPROVIDER=openjpa RDBMS=postgresql
# addons:
# postgresql: "9.4"
# postgresql: "9.5"
# - env: JPAPROVIDER=openjpa RDBMS=mssql
# jdk: oraclejdk8
# sudo: true
Expand Down Expand Up @@ -197,7 +197,7 @@ matrix:
- env: JPAPROVIDER=hibernate-5.3 RDBMS=postgresql
jdk: oraclejdk8
addons:
postgresql: "9.4"
postgresql: "9.5"
- env: JPAPROVIDER=hibernate-5.3 RDBMS=mssql
jdk: oraclejdk8
sudo: true
Expand Down Expand Up @@ -233,7 +233,7 @@ matrix:
- env: JPAPROVIDER=hibernate-5.4 RDBMS=postgresql NATIVE=true
jdk: oraclejdk8
addons:
postgresql: "9.4"
postgresql: "9.5"
script:
- travis_wait 70 ./build.sh
- env: JPAPROVIDER=hibernate-5.4 RDBMS=mssql NATIVE=true
Expand Down Expand Up @@ -270,7 +270,7 @@ matrix:
# - env: JPAPROVIDER=datanucleus-5.1 RDBMS=postgresql
# jdk: oraclejdk8
# addons:
# postgresql: "9.4"
# postgresql: "9.5"
# - env: JPAPROVIDER=datanucleus-5.1 RDBMS=mssql
# jdk: oraclejdk8
# sudo: true
Expand Down Expand Up @@ -303,7 +303,7 @@ matrix:
- env: JPAPROVIDER=hibernate-5.1 RDBMS=postgresql
jdk: oraclejdk8
addons:
postgresql: "9.4"
postgresql: "9.5"
# - env: JPAPROVIDER=hibernate-5.1 RDBMS=mssql
# jdk: oraclejdk8
# sudo: true
Expand Down Expand Up @@ -336,7 +336,7 @@ matrix:
- env: JPAPROVIDER=hibernate-5.0 RDBMS=postgresql
jdk: oraclejdk8
addons:
postgresql: "9.4"
postgresql: "9.5"
# - env: JPAPROVIDER=hibernate-5.0 RDBMS=mssql
# jdk: oraclejdk8
# sudo: true
Expand Down Expand Up @@ -369,7 +369,7 @@ matrix:
- env: JPAPROVIDER=hibernate-4.3 RDBMS=postgresql
jdk: oraclejdk8
addons:
postgresql: "9.4"
postgresql: "9.5"
# - env: JPAPROVIDER=hibernate-4.3 RDBMS=mssql
# jdk: oraclejdk8
# sudo: true
Expand Down Expand Up @@ -401,7 +401,7 @@ matrix:
- env: JPAPROVIDER=hibernate RDBMS=postgresql
jdk: oraclejdk8
addons:
postgresql: "9.4"
postgresql: "9.5"
# - env: JPAPROVIDER=hibernate RDBMS=mssql
# jdk: oraclejdk8
# sudo: true
Expand Down Expand Up @@ -432,7 +432,7 @@ matrix:
# sudo: required
# - env: JPAPROVIDER=datanucleus-4 RDBMS=postgresql
# addons:
# postgresql: "9.4"
# postgresql: "9.5"
# jdk: oraclejdk8
# - env: JPAPROVIDER=datanucleus-4 RDBMS=mssql
# jdk: oraclejdk8
Expand Down
12 changes: 2 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -492,16 +492,8 @@ When setting up Oracle locally, keep in mind that when you connect to it, you ha
Since the JDBC driver derives values from the locale settings of the JVM, you should set the default locale settings to en_US.
In IntelliJ when defining the Oracle database, go to the Advanced tab an specify the JVM options `-Duser.country=us -Duser.language=en`.

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`
For executing tests against the Oracle docker container started via `docker_db.sh oracle` you need to specify the following
system properties `-Djdbc.url=jdbc:oracle:thin:@192.168.99.100:1521/xe -Djdbc.user=SYSTEM -Djdbc.password=oracle -Doracle.jdbc.DateZeroTime=true`.

### Install Oracle locally

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.blazebit.persistence.impl.function.base64.Base64Function;
import com.blazebit.persistence.impl.function.base64.PostgreSQLBase64Function;
import com.blazebit.persistence.impl.function.cast.CastFunction;
import com.blazebit.persistence.impl.function.cast.DB2CastFunction;
import com.blazebit.persistence.impl.function.chr.CharChrFunction;
import com.blazebit.persistence.impl.function.chr.ChrFunction;
import com.blazebit.persistence.impl.function.colldml.CollectionDmlSupportFunction;
Expand Down Expand Up @@ -344,6 +345,18 @@
import com.blazebit.persistence.impl.function.groupconcat.MySQLGroupConcatFunction;
import com.blazebit.persistence.impl.function.groupconcat.OracleListaggGroupConcatFunction;
import com.blazebit.persistence.impl.function.groupconcat.PostgreSQLGroupConcatFunction;
import com.blazebit.persistence.impl.function.jsonget.AbstractJsonGetFunction;
import com.blazebit.persistence.impl.function.jsonget.DB2JsonGetFunction;
import com.blazebit.persistence.impl.function.jsonget.MSSQLJsonGetFunction;
import com.blazebit.persistence.impl.function.jsonget.MySQL8JsonGetFunction;
import com.blazebit.persistence.impl.function.jsonget.OracleJsonGetFunction;
import com.blazebit.persistence.impl.function.jsonget.PostgreSQLJsonGetFunction;
import com.blazebit.persistence.impl.function.jsonset.AbstractJsonSetFunction;
import com.blazebit.persistence.impl.function.jsonset.DB2JsonSetFunction;
import com.blazebit.persistence.impl.function.jsonset.MSSQLJsonSetFunction;
import com.blazebit.persistence.impl.function.jsonset.MySQL8JsonSetFunction;
import com.blazebit.persistence.impl.function.jsonset.OracleJsonSetFunction;
import com.blazebit.persistence.impl.function.jsonset.PostgreSQLJsonSetFunction;
import com.blazebit.persistence.impl.function.least.AbstractLeastFunction;
import com.blazebit.persistence.impl.function.least.DefaultLeastFunction;
import com.blazebit.persistence.impl.function.least.MinLeastFunction;
Expand All @@ -369,9 +382,9 @@
import com.blazebit.persistence.impl.function.set.SetFunction;
import com.blazebit.persistence.impl.function.stringjsonagg.AbstractStringJsonAggFunction;
import com.blazebit.persistence.impl.function.stringjsonagg.GroupConcatBasedStringJsonAggFunction;
import com.blazebit.persistence.impl.function.stringjsonagg.MySQLStringJsonAggFunction;
import com.blazebit.persistence.impl.function.stringjsonagg.OracleStringJsonAggFunction;
import com.blazebit.persistence.impl.function.stringjsonagg.PostgreSQLStringJsonAggFunction;
import com.blazebit.persistence.impl.function.stringjsonagg.MySQLStringJsonAggFunction;
import com.blazebit.persistence.impl.function.stringxmlagg.AbstractStringXmlAggFunction;
import com.blazebit.persistence.impl.function.stringxmlagg.GroupConcatBasedStringXmlAggFunction;
import com.blazebit.persistence.impl.function.stringxmlagg.OracleGroupConcatBasedStringXmlAggFunction;
Expand All @@ -381,9 +394,9 @@
import com.blazebit.persistence.impl.function.tostringjson.AbstractToStringJsonFunction;
import com.blazebit.persistence.impl.function.tostringjson.ForJsonPathToStringJsonFunction;
import com.blazebit.persistence.impl.function.tostringjson.GroupConcatBasedToStringJsonFunction;
import com.blazebit.persistence.impl.function.tostringjson.MySQLToStringJsonFunction;
import com.blazebit.persistence.impl.function.tostringjson.OracleToStringJsonFunction;
import com.blazebit.persistence.impl.function.tostringjson.PostgreSQLToStringJsonFunction;
import com.blazebit.persistence.impl.function.tostringjson.MySQLToStringJsonFunction;
import com.blazebit.persistence.impl.function.tostringxml.AbstractToStringXmlFunction;
import com.blazebit.persistence.impl.function.tostringxml.ForXmlPathToStringXmlFunction;
import com.blazebit.persistence.impl.function.tostringxml.GroupConcatBasedToStringXmlFunction;
Expand Down Expand Up @@ -704,7 +717,13 @@ private void loadFunctions() {

for (Map.Entry<String, DbmsDialect> dbmsDialectEntry : dbmsDialects.entrySet()) {
for (Class<?> type : BasicCastTypes.TYPES) {
functions.get("cast_" + type.getSimpleName().toLowerCase()).add(dbmsDialectEntry.getKey(), new CastFunction(type, dbmsDialectEntry.getValue()));
CastFunction castFunction;
if ("db2".equals(dbmsDialectEntry.getKey())) {
castFunction = new DB2CastFunction(type, dbmsDialectEntry.getValue());
} else {
castFunction = new CastFunction(type, dbmsDialectEntry.getValue());
}
functions.get("cast_" + type.getSimpleName().toLowerCase()).add(dbmsDialectEntry.getKey(), castFunction);
}
}

Expand Down Expand Up @@ -1759,6 +1778,24 @@ private void loadFunctions() {
jpqlFunctionGroup.add(dialectEntry.getKey(), new NthValueFunction(dialectEntry.getValue()));
}
registerFunction(jpqlFunctionGroup);

// json_get
jpqlFunctionGroup = new JpqlFunctionGroup(AbstractJsonGetFunction.FUNCTION_NAME, false);
jpqlFunctionGroup.add("postgresql", new PostgreSQLJsonGetFunction());
jpqlFunctionGroup.add("mysql8", new MySQL8JsonGetFunction());
jpqlFunctionGroup.add("oracle", new OracleJsonGetFunction());
jpqlFunctionGroup.add("db2", new DB2JsonGetFunction());
jpqlFunctionGroup.add("microsoft", new MSSQLJsonGetFunction());
registerFunction(jpqlFunctionGroup);

// json_set
jpqlFunctionGroup = new JpqlFunctionGroup(AbstractJsonSetFunction.FUNCTION_NAME, false);
jpqlFunctionGroup.add("postgresql", new PostgreSQLJsonSetFunction());
jpqlFunctionGroup.add("mysql8", new MySQL8JsonSetFunction());
jpqlFunctionGroup.add("oracle", new OracleJsonSetFunction());
jpqlFunctionGroup.add("db2", new DB2JsonSetFunction());
jpqlFunctionGroup.add("microsoft", new MSSQLJsonSetFunction());
registerFunction(jpqlFunctionGroup);
}

private <T extends JpqlFunction> T findFunction(String name, String dbms) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ protected static Map<Class<?>, String> getSqlTypes() {
Map<Class<?>, String> types = new HashMap<Class<?>, String>();

types.put(String.class, "longtext");
types.put(Integer.class, "signed");
types.put(Boolean.class, "unsigned");

return types;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
*/
public class CastFunction implements JpqlFunction {

private final String functionName;
private final Class<?> castType;
private final String defaultSqlCastType;
protected final String functionName;
protected final Class<?> castType;
protected final String defaultSqlCastType;

public CastFunction(Class<?> castType, DbmsDialect dbmsDialect) {
this.functionName = "CAST_" + castType.getSimpleName().toUpperCase();
Expand Down Expand Up @@ -64,7 +64,7 @@ public void render(FunctionRenderContext context) {
if (context.getArgumentsSize() == 1) {
context.addChunk(defaultSqlCastType);
} else {
context.addChunk(JpqlFunctionUtil.unquote(context.getArgument(1)));
context.addChunk(JpqlFunctionUtil.unquoteSingleQuotes(context.getArgument(1)));
}
context.addChunk(")");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright 2014 - 2020 Blazebit.
*
* 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
*
* http://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 com.blazebit.persistence.impl.function.cast;

import com.blazebit.persistence.impl.util.JpqlFunctionUtil;
import com.blazebit.persistence.spi.DbmsDialect;
import com.blazebit.persistence.spi.FunctionRenderContext;

/**
* @author Christian Beikov
* @since 1.2.0
*/
public class DB2CastFunction extends CastFunction {

private static final String[] CLOB_RETURNING_FUNCTIONS = new String[] {
"json_value(",
"json_query("
};
private static final String[] CLOB_COMPATIBLE_CAST_TARGET_TYPES = new String[] {
"char",
"varchar",
"graphic",
"vargraphic",
"dbclob",
"blob",
"xml"
};

public DB2CastFunction(Class<?> castType, DbmsDialect dbmsDialect) {
super(castType, dbmsDialect);
}

@Override
public void render(FunctionRenderContext context) {
if (context.getArgumentsSize() != 1 && context.getArgumentsSize() != 2) {
throw new RuntimeException("The " + functionName + " function needs one argument <expression> with an optional second argument <sql-type-name>! args=" + context);
}
String effectiveCastTargetType;
if (context.getArgumentsSize() == 1) {
effectiveCastTargetType = defaultSqlCastType;
} else {
effectiveCastTargetType = JpqlFunctionUtil.unquoteSingleQuotes(context.getArgument(1));
}
boolean insertVarcharCast = isClobReturningFunction(context.getArgument(0)) && !isClobCompatibleCastTarget(effectiveCastTargetType);
context.addChunk("cast(");
if (insertVarcharCast) {
context.addChunk("cast(");
}
context.addArgument(0);
if (insertVarcharCast) {
context.addChunk(" as varchar(32000))");
}
context.addChunk(" as ");
context.addChunk(effectiveCastTargetType);
context.addChunk(")");
}

@Override
public String getCastExpression(String argument) {
boolean insertVarcharCast = isClobReturningFunction(argument) && !isClobCompatibleCastTarget(defaultSqlCastType);
if (insertVarcharCast) {
return "cast(cast(" + argument + " as varchar(32000)) as " + defaultSqlCastType + ")";
} else {
return "cast(" + argument + " as " + defaultSqlCastType + ")";
}
}

private static boolean isClobReturningFunction(String castSource) {
for (int i = 0; i < CLOB_RETURNING_FUNCTIONS.length; i++) {
if (castSource.toLowerCase().startsWith(CLOB_RETURNING_FUNCTIONS[i])) {
return true;
}
}
return false;
}

private static boolean isClobCompatibleCastTarget(String castTargetType) {
for (int i = 0; i < CLOB_COMPATIBLE_CAST_TARGET_TYPES.length; i++) {
if (castTargetType.toLowerCase().indexOf(CLOB_COMPATIBLE_CAST_TARGET_TYPES[i]) == 0 &&
(castTargetType.length() == CLOB_COMPATIBLE_CAST_TARGET_TYPES[i].length() ||
castTargetType.charAt(CLOB_COMPATIBLE_CAST_TARGET_TYPES[i].length()) == '(')
) {
return true;
}
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ public void render(FunctionRenderContext functionRenderContext) {
aliasStartIndex--;
}

String entityName = JpqlFunctionUtil.unquote(functionRenderContext.getArgument(1));
String valuesClause = JpqlFunctionUtil.unquote(functionRenderContext.getArgument(2));
String valuesAliases = JpqlFunctionUtil.unquote(functionRenderContext.getArgument(3));
String syntheticPredicate = JpqlFunctionUtil.unquote(functionRenderContext.getArgument(4));
String entityName = JpqlFunctionUtil.unquoteSingleQuotes(functionRenderContext.getArgument(1));
String valuesClause = JpqlFunctionUtil.unquoteSingleQuotes(functionRenderContext.getArgument(2));
String valuesAliases = JpqlFunctionUtil.unquoteSingleQuotes(functionRenderContext.getArgument(3));
String syntheticPredicate = JpqlFunctionUtil.unquoteSingleQuotes(functionRenderContext.getArgument(4));
String valuesTableSqlAlias = subquery.substring(aliasStartIndex, aliasEndIndex);
appendSubqueryPart(sb, subquery, 1, subqueryEndIndex, subquery.length() - 1);

Expand Down
Loading

0 comments on commit 56c72e9

Please sign in to comment.