org.eclipse.persistence.platform.database.DB2ZPlatform Maven / Gradle / Ivy
/*
* Copyright (c) 2015, 2024 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2024 IBM Corporation. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// 03/19/2015 - Rick Curtis
// - 462586 : Add national character support for z/OS.
package org.eclipse.persistence.platform.database;
import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.expressions.ExpressionOperator;
import org.eclipse.persistence.internal.databaseaccess.BindCallCustomParameter;
import org.eclipse.persistence.internal.databaseaccess.DatasourceCall;
import org.eclipse.persistence.internal.databaseaccess.DatasourceCall.ParameterType;
import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition;
import org.eclipse.persistence.internal.expressions.CollectionExpression;
import org.eclipse.persistence.internal.expressions.ConstantExpression;
import org.eclipse.persistence.internal.expressions.ExpressionJavaPrinter;
import org.eclipse.persistence.internal.expressions.ExpressionSQLPrinter;
import org.eclipse.persistence.internal.expressions.ParameterExpression;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.security.PrivilegedClassForName;
import org.eclipse.persistence.internal.security.PrivilegedGetContextClassLoader;
import org.eclipse.persistence.internal.security.PrivilegedGetMethod;
import org.eclipse.persistence.internal.security.PrivilegedMethodInvoker;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.logging.AbstractSessionLog;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.mappings.structures.ObjectRelationalDatabaseField;
import org.eclipse.persistence.platform.database.converters.StructConverter;
import org.eclipse.persistence.queries.StoredProcedureCall;
import org.eclipse.persistence.queries.ValueReadQuery;
import java.io.ByteArrayInputStream;
import java.io.CharArrayReader;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.sql.CallableStatement;
import java.sql.SQLException;
import java.util.Calendar;
import java.util.Collection;
import java.util.Hashtable;
import java.util.List;
/**
* Purpose: Provides DB2 z/OS specific behavior.
*
* This provides for some additional compatibility in certain DB2 versions.
*
* Responsibilities:
*
* - Support creating tables that handle multibyte characters
*
*/
public class DB2ZPlatform extends DB2Platform {
private static String DB2_CALLABLESTATEMENT_CLASS = "com.ibm.db2.jcc.DB2CallableStatement";
private static String DB2_PREPAREDSTATEMENT_CLASS = "com.ibm.db2.jcc.DB2PreparedStatement";
public DB2ZPlatform() {
super();
this.pingSQL = "SELECT COUNT(*) from SYSIBM.SYSDUMMY1 WHERE 1 = 0";
}
@Override
protected Hashtable, FieldTypeDefinition> buildFieldTypes() {
Hashtable, FieldTypeDefinition> res = super.buildFieldTypes();
if (getUseNationalCharacterVaryingTypeForString()) {
res.put(String.class, new FieldTypeDefinition("VARCHAR", DEFAULT_VARCHAR_SIZE));
}
return res;
}
@Override
public String getTableCreationSuffix() {
// If we're on Z and using unicode support we need to append CCSID
// UNICODE on the table rather than FOR MIXED DATA on each column
if (getUseNationalCharacterVaryingTypeForString()) {
return " CCSID UNICODE";
}
return super.getTableCreationSuffix();
}
@Override
public String getProcedureArgument(String name, Object parameter, ParameterType parameterType,
StoredProcedureCall call, AbstractSession session) {
if (name != null && shouldPrintStoredProcedureArgumentNameInCall()) {
return ":" + name;
}
return "?";
}
/**
* DB2 on Z uses ":" as prefix for procedure arguments.
*/
@Override
public String getProcedureOptionList() {
return " DISABLE DEBUG MODE ";
}
/**
* INTERNAL:
* This method returns the query to select the timestamp from the server for
* DB2.
*/
@Override
public ValueReadQuery getTimestampQuery() {
if (timestampQuery == null) {
if (getUseNationalCharacterVaryingTypeForString()) {
timestampQuery = new ValueReadQuery();
timestampQuery.setSQLString("SELECT CAST (CURRENT TIMESTAMP AS TIMESTAMP CCSID UNICODE) FROM SYSIBM.SYSDUMMY1");
timestampQuery.setAllowNativeSQLQuery(true);
} else {
timestampQuery = super.getTimestampQuery();
}
}
return timestampQuery;
}
/**
* INTERNAL:
* Initialize any platform-specific operators
*/
@Override
protected void initializePlatformOperators() {
super.initializePlatformOperators();
addOperator(avgOperator());
addOperator(sumOperator());
addOperator(absOperator());
addOperator(sqrtOperator());
addOperator(trimOperator());
addOperator(ltrimOperator());
addOperator(rtrimOperator());
addOperator(locateOperator());
addOperator(locate2Operator());
addOperator(equalOperator());
addOperator(notEqualOperator());
addOperator(lessThanOperator());
addOperator(lessThanEqualOperator());
addOperator(greaterThanOperator());
addOperator(greaterThanEqualOperator());
addOperator(isNullOperator());
addOperator(isNotNullOperator());
addOperator(modOperator());
addOperator(betweenOperator());
addOperator(notBetweenOperator());
addOperator(inOperator());
addOperator(likeEscapeOperator());
addOperator(notLikeEscapeOperator());
addOperator(ceilingOperator());
addOperator(floorOperator());
addOperator(expOperator());
addOperator(lnOperator());
addOperator(powerOperator());
addOperator(signOperator());
}
/**
* Disable binding support.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement cannot be executed because a parameter marker has been used
* in an invalid way. DB2 SQL Error: SQLCODE=-418, SQLSTATE=42610
*/
protected ExpressionOperator avgOperator() {
ExpressionOperator operator = disableAllBindingExpression();
ExpressionOperator.average().copyTo(operator);
return operator;
}
/**
* Disable binding support.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement cannot be executed because a parameter marker has been used
* in an invalid way. DB2 SQL Error: SQLCODE=-418, SQLSTATE=42610
*/
protected ExpressionOperator sumOperator() {
ExpressionOperator operator = disableAllBindingExpression();
ExpressionOperator.sum().copyTo(operator);
return operator;
}
/**
* Disable binding support.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement cannot be executed because a parameter marker has been used
* in an invalid way. DB2 SQL Error: SQLCODE=-418, SQLSTATE=42610
*/
protected ExpressionOperator absOperator() {
ExpressionOperator operator = disableAllBindingExpression();
ExpressionOperator.abs().copyTo(operator);
return operator;
}
/**
* DB2 z/OS requires that at least one argument be a known type
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement cannot be executed because a parameter marker has been used
* in an invalid way. DB2 SQL Error: SQLCODE=-418, SQLSTATE=42610
*/
@Override
protected ExpressionOperator concatOperator() {
ExpressionOperator operatorS = super.concatOperator();
ExpressionOperator operator = disableAtLeast1BindingExpression();
operatorS.copyTo(operator);
return operator;
}
/**
* DB2 z/OS requires that at least one argument be a known type
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement string specified as the object of a PREPARE contains a
* predicate or expression where parameter markers have been used as operands of
* the same operator—for example: ? > ?. DB2 SQL Error: SQLCODE=-417, SQLSTATE=42609
*/
protected ExpressionOperator equalOperator() {
ExpressionOperator operator = disableAtLeast1BindingExpression();
ExpressionOperator.equal().copyTo(operator);
return operator;
}
/**
* DB2 z/OS requires that at least one argument be a known type
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement string specified as the object of a PREPARE contains a
* predicate or expression where parameter markers have been used as operands of
* the same operator—for example: ? > ?. DB2 SQL Error: SQLCODE=-417, SQLSTATE=42609
*/
protected ExpressionOperator notEqualOperator() {
ExpressionOperator operator = disableAtLeast1BindingExpression();
ExpressionOperator.notEqual().copyTo(operator);
return operator;
}
/**
* DB2 z/OS requires that at least one argument be a known type
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement string specified as the object of a PREPARE contains a
* predicate or expression where parameter markers have been used as operands of
* the same operator—for example: ? > ?. DB2 SQL Error: SQLCODE=-417, SQLSTATE=42609
*/
protected ExpressionOperator greaterThanOperator() {
ExpressionOperator operator = disableAtLeast1BindingExpression();
ExpressionOperator.greaterThan().copyTo(operator);
return operator;
}
/**
* DB2 z/OS requires that at least one argument be a known type
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement string specified as the object of a PREPARE contains a
* predicate or expression where parameter markers have been used as operands of
* the same operator—for example: ? > ?. DB2 SQL Error: SQLCODE=-417, SQLSTATE=42609
*/
protected ExpressionOperator greaterThanEqualOperator() {
ExpressionOperator operator = disableAtLeast1BindingExpression();
ExpressionOperator.greaterThanEqual().copyTo(operator);
return operator;
}
/**
* Set binding support to PARTIAL.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement string specified as the object of a PREPARE contains a
* predicate or expression where parameter markers have been used as operands of
* the same operator—for example: ? > ?. DB2 SQL Error: SQLCODE=-417, SQLSTATE=42609
*/
protected ExpressionOperator lessThanOperator() {
ExpressionOperator operator = disableAtLeast1BindingExpression();
ExpressionOperator.lessThan().copyTo(operator);
return operator;
}
/**
* DB2 z/OS requires that at least one argument be a known type
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement string specified as the object of a PREPARE contains a
* predicate or expression where parameter markers have been used as operands of
* the same operator—for example: ? > ?. DB2 SQL Error: SQLCODE=-417, SQLSTATE=42609
*/
protected ExpressionOperator lessThanEqualOperator() {
ExpressionOperator operator = disableAtLeast1BindingExpression();
ExpressionOperator.lessThanEqual().copyTo(operator);
return operator;
}
/**
* Disable binding support.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement string specified as the object of a PREPARE contains a
* predicate or expression where parameter markers have been used as operands of
* the same operator—for example: ? > ?. DB2 SQL Error: SQLCODE=-417, SQLSTATE=42609
*/
protected ExpressionOperator isNullOperator() {
ExpressionOperator operator = disableAllBindingExpression();
ExpressionOperator.isNull().copyTo(operator);
return operator;
}
/**
* Disable binding support.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement string specified as the object of a PREPARE contains a
* predicate or expression where parameter markers have been used as operands of
* the same operator—for example: ? > ?. DB2 SQL Error: SQLCODE=-417, SQLSTATE=42609
*/
protected ExpressionOperator isNotNullOperator() {
ExpressionOperator operator = disableAllBindingExpression();
ExpressionOperator.notNull().copyTo(operator);
return operator;
}
/**
* DB2 z/OS requires that at least one argument be a known type
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement string specified as the object of a PREPARE contains a
* predicate or expression where parameter markers have been used as operands of
* the same operator—for example: ? > ?. DB2 SQL Error: SQLCODE=-417, SQLSTATE=42609
*/
protected ExpressionOperator betweenOperator() {
ExpressionOperator operator = disableAtLeast1BindingExpression();
ExpressionOperator.between().copyTo(operator);
return operator;
}
/**
* DB2 z/OS requires that at least one argument be a known type
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement string specified as the object of a PREPARE contains a
* predicate or expression where parameter markers have been used as operands of
* the same operator—for example: ? > ?. DB2 SQL Error: SQLCODE=-417, SQLSTATE=42609
*/
protected ExpressionOperator notBetweenOperator() {
ExpressionOperator operator = disableAtLeast1BindingExpression();
ExpressionOperator.notBetween().copyTo(operator);
return operator;
}
/**
* DB2 z/OS support for binding the LIKE ESCAPE character depends on database configuration (mixed vs DBCS).
* Since we cannot know how the database in configured, we will disable parameter binding for the ESCAPE
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement string specified as the object of a PREPARE contains a
* predicate or expression where parameter markers have been used as operands of
* the same operator for example: ? > ?. DB2 SQL Error: SQLCODE=-417, SQLSTATE=42609
*/
protected ExpressionOperator likeEscapeOperator() {
ExpressionOperator operator = new ExpressionOperator(){
@Override
public void printCollection(List items, ExpressionSQLPrinter printer) {
if(!printer.getPlatform().shouldBindPartialParameters()) {
super.printCollection(items, printer);
return;
}
// Initialize argumentIndices
if (this.argumentIndices == null) {
this.argumentIndices = new int[items.size()];
for (int i = 0; i < this.argumentIndices.length; i++){
this.argumentIndices[i] = i;
}
}
for (int i = 0; i < items.size(); i++) {
// Disable the first item, which should be for this operator
if(i == (items.size() - 1)) {
final int index = this.argumentIndices[i];
Expression item = items.get(index);
if(item.isParameterExpression()) {
((ParameterExpression) item).setCanBind(false);
} else if(item.isConstantExpression()) {
((ConstantExpression) item).setCanBind(false);
}
}
}
super.printCollection(items, printer);
}
@Override
public void printJavaCollection(List items, ExpressionJavaPrinter printer) {
if(!printer.getPlatform().shouldBindPartialParameters()) {
super.printJavaCollection(items, printer);
return;
}
for (int i = 0; i < items.size(); i++) {
// Disable the last item, which should be for this operator
if(i == (items.size() - 1)) {
Expression item = items.get(i);
if(item.isParameterExpression()) {
((ParameterExpression) item).setCanBind(false);
} else if(item.isConstantExpression()) {
((ConstantExpression) item).setCanBind(false);
}
}
}
super.printJavaCollection(items, printer);
}
};
ExpressionOperator.likeEscape().copyTo(operator);
return operator;
}
/**
* DB2 z/OS support for binding the LIKE ESCAPE character depends on database configuration (mixed vs DBCS).
* Since we cannot know how the database in configured, we will disable parameter binding for the ESCAPE
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement string specified as the object of a PREPARE contains a
* predicate or expression where parameter markers have been used as operands of
* the same operator for example: ? > ?. DB2 SQL Error: SQLCODE=-417, SQLSTATE=42609
*/
protected ExpressionOperator notLikeEscapeOperator() {
ExpressionOperator operator = new ExpressionOperator(){
@Override
public void printCollection(List items, ExpressionSQLPrinter printer) {
if(!printer.getPlatform().shouldBindPartialParameters()) {
super.printCollection(items, printer);
return;
}
// Initialize argumentIndices
if (this.argumentIndices == null) {
this.argumentIndices = new int[items.size()];
for (int i = 0; i < this.argumentIndices.length; i++){
this.argumentIndices[i] = i;
}
}
for (int i = 0; i < items.size(); i++) {
// Disable the first item, which should be for this operator
if(i == (items.size() - 1)) {
final int index = this.argumentIndices[i];
Expression item = items.get(index);
if(item.isParameterExpression()) {
((ParameterExpression) item).setCanBind(false);
} else if(item.isConstantExpression()) {
((ConstantExpression) item).setCanBind(false);
}
}
}
super.printCollection(items, printer);
}
@Override
public void printJavaCollection(List items, ExpressionJavaPrinter printer) {
if(!printer.getPlatform().shouldBindPartialParameters()) {
super.printJavaCollection(items, printer);
return;
}
for (int i = 0; i < items.size(); i++) {
// Disable the last item, which should be for this operator
if(i == (items.size() - 1)) {
Expression item = items.get(i);
if(item.isParameterExpression()) {
((ParameterExpression) item).setCanBind(false);
} else if(item.isConstantExpression()) {
((ConstantExpression) item).setCanBind(false);
}
}
}
super.printJavaCollection(items, printer);
}
};
ExpressionOperator.notLikeEscape().copyTo(operator);
return operator;
}
/**
* Disable binding support.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement cannot be executed because a parameter marker has been used
* in an invalid way. DB2 SQL Error: SQLCODE=-418, SQLSTATE=42610
*
* With binding enabled, DB2 z/OS will throw an error:
*
The data type, the length, or the value of an argument of a scalar function
* is incorrect. DB2 SQL Error: SQLCODE=-171, SQLSTATE=42815
*/
protected ExpressionOperator locateOperator() {
ExpressionOperator operator = disableAllBindingExpression();
ExpressionOperator.locate().copyTo(operator);
return operator;
}
/**
* Disable binding support.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement cannot be executed because a parameter marker has been used
* in an invalid way. DB2 SQL Error: SQLCODE=-418, SQLSTATE=42610
*
* With binding enabled, DB2 z/OS will throw an error:
*
The data type, the length, or the value of an argument of a scalar function
* is incorrect. DB2 SQL Error: SQLCODE=-171, SQLSTATE=42815
*/
protected ExpressionOperator locate2Operator() {
ExpressionOperator operator = disableAllBindingExpression();
ExpressionOperator.locate2().copyTo(operator);
return operator;
}
/**
* Disable binding support.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement cannot be executed because a parameter marker has been used
* in an invalid way. DB2 SQL Error: SQLCODE=-418, SQLSTATE=42610
*
* With binding enabled, DB2 z/OS will throw an error:
*
The data type, the length, or the value of an argument of a scalar function
* is incorrect. DB2 SQL Error: SQLCODE=-171, SQLSTATE=42815
*/
protected ExpressionOperator modOperator() {
ExpressionOperator operator = disableAllBindingExpression();
ExpressionOperator.mod().copyTo(operator);
return operator;
}
/**
* Disable binding support.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement cannot be executed because a parameter marker has been used
* in an invalid way. DB2 SQL Error: SQLCODE=-418, SQLSTATE=42610
*/
protected ExpressionOperator sqrtOperator() {
ExpressionOperator operator = disableAllBindingExpression();
ExpressionOperator.sqrt().copyTo(operator);
return operator;
}
/**
* Disable binding support.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement cannot be executed because a parameter marker has been used
* in an invalid way. DB2 SQL Error: SQLCODE=-418, SQLSTATE=42610
*/
protected ExpressionOperator trimOperator() {
ExpressionOperator operator = disableAllBindingExpression();
ExpressionOperator.trim().copyTo(operator);
return operator;
}
/**
* Disable binding support.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The data type, the length, or the value of an argument of a scalar function
* is incorrect. DB2 SQL Error: SQLCODE=-171, SQLSTATE=42815
*/
@Override
protected ExpressionOperator trim2() {
ExpressionOperator operator = disableAllBindingExpression();
ExpressionOperator.trim2().copyTo(operator);
return operator;
}
/**
* Disable binding support.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement cannot be executed because a parameter marker has been used
* in an invalid way. DB2 SQL Error: SQLCODE=-418, SQLSTATE=42610
*/
protected ExpressionOperator ltrimOperator() {
ExpressionOperator operator = disableAllBindingExpression();
ExpressionOperator.leftTrim().copyTo(operator);
return operator;
}
/**
* Disable binding support.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The data type, the length, or the value of an argument of a scalar function
* is incorrect. DB2 SQL Error: SQLCODE=-171, SQLSTATE=42815
*/
@Override
protected ExpressionOperator ltrim2Operator() {
ExpressionOperator operatorS = super.ltrim2Operator();
ExpressionOperator operator = disableAllBindingExpression();
operatorS.copyTo(operator);
return operator;
}
/**
* Disable binding support.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement cannot be executed because a parameter marker has been used
* in an invalid way. DB2 SQL Error: SQLCODE=-418, SQLSTATE=42610
*/
protected ExpressionOperator rtrimOperator() {
ExpressionOperator operator = disableAllBindingExpression();
ExpressionOperator.rightTrim().copyTo(operator);
return operator;
}
/**
* Disable binding support.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The data type, the length, or the value of an argument of a scalar function
* is incorrect. DB2 SQL Error: SQLCODE=-171, SQLSTATE=42815
*/
@Override
protected ExpressionOperator rtrim2Operator() {
ExpressionOperator operatorS = super.rtrim2Operator();
ExpressionOperator operator = disableAllBindingExpression();
operatorS.copyTo(operator);
return operator;
}
/**
* DB2 z/OS requires that at least one argument be a known type
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement string specified as the object of a PREPARE contains a
* predicate or expression where parameter markers have been used as operands of
* the same operator—for example: ? > ?. DB2 SQL Error: SQLCODE=-417, SQLSTATE=42609
*/
protected ExpressionOperator inOperator() {
ExpressionOperator operator = new ExpressionOperator() {
@Override
public void printDuo(Expression first, Expression second, ExpressionSQLPrinter printer) {
if(!printer.getPlatform().shouldBindPartialParameters()) {
super.printDuo(first, second, printer);
return;
}
// If the first argument isn't a Constant/Parameter, this will suffice
if(!first.isValueExpression() || (first.isConstantExpression() && !printer.getPlatform().shouldBindLiterals())) {
super.printDuo(first, second, printer);
return;
}
// Otherwise, we need to inspect the right, collection side of the IN expression
boolean firstBound = true;
if(second instanceof CollectionExpression) {
Object val = ((CollectionExpression) second).getValue();
if (val instanceof Collection values) {
firstBound = false;
for(Object value : values) {
// If the value isn't a Constant/Parameter, this will suffice and the first should bind
if(value instanceof Expression && !((Expression)value).isValueExpression()) {
firstBound = true;
break;
}
// If the value is a Constant and literal binding is disabled, this will suffice and the first should bind
if(value instanceof Expression && ((Expression)value).isConstantExpression() && !printer.getPlatform().shouldBindLiterals()) {
firstBound = true;
break;
}
}
}
}
if(first.isParameterExpression()) {
((ParameterExpression) first).setCanBind(firstBound);
} else if(first.isConstantExpression()) {
((ConstantExpression) first).setCanBind(firstBound);
}
super.printDuo(first, second, printer);
}
@Override
public void printJavaDuo(Expression first, Expression second, ExpressionJavaPrinter printer) {
if(!printer.getPlatform().shouldBindPartialParameters()) {
super.printJavaDuo(first, second, printer);
return;
}
// If the first argument isn't a Constant/Parameter, this will suffice
if(!first.isValueExpression() || (first.isConstantExpression() && !printer.getPlatform().shouldBindLiterals())) {
super.printJavaDuo(first, second, printer);
return;
}
// Otherwise, we need to inspect the right, collection side of the IN expression
boolean firstBound = true;
if(second instanceof CollectionExpression) {
Object val = ((CollectionExpression) second).getValue();
if (val instanceof Collection values) {
firstBound = false;
for(Object value : values) {
// If the value isn't a Constant/Parameter, this will suffice and the first should bind
if(value instanceof Expression && !((Expression)value).isValueExpression()) {
firstBound = true;
break;
}
// If the value is a Constant and literal binding is disabled, this will suffice and the first should bind
if(value instanceof Expression && ((Expression)value).isConstantExpression() && !printer.getPlatform().shouldBindLiterals()) {
firstBound = true;
break;
}
}
}
}
if(first.isParameterExpression()) {
((ParameterExpression) first).setCanBind(firstBound);
} else if(first.isConstantExpression()) {
((ConstantExpression) first).setCanBind(firstBound);
}
super.printJavaDuo(first, second, printer);
}
};
ExpressionOperator.in().copyTo(operator);
return operator;
}
/**
* Disable binding support.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement cannot be executed because a parameter marker has been used
* in an invalid way. DB2 SQL Error: SQLCODE=-418, SQLSTATE=42610
*/
protected ExpressionOperator ceilingOperator() {
ExpressionOperator operator = disableAllBindingExpression();
ExpressionOperator.ceil().copyTo(operator);
return operator;
}
/**
* Disable binding support.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement cannot be executed because a parameter marker has been used
* in an invalid way. DB2 SQL Error: SQLCODE=-418, SQLSTATE=42610
*/
protected ExpressionOperator floorOperator() {
ExpressionOperator operator = disableAllBindingExpression();
ExpressionOperator.floor().copyTo(operator);
return operator;
}
/**
* Disable binding support.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The data type, the length, or the value of an argument of a scalar function
* is incorrect. DB2 SQL Error: SQLCODE=-171, SQLSTATE=42815
*/
@Override
protected ExpressionOperator roundOperator() {
ExpressionOperator operatorS = super.roundOperator();
ExpressionOperator operator = disableAllBindingExpression();
operatorS.copyTo(operator);
return operator;
}
/**
* Disable binding support.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement cannot be executed because a parameter marker has been used
* in an invalid way. DB2 SQL Error: SQLCODE=-418, SQLSTATE=42610
*/
protected ExpressionOperator expOperator() {
ExpressionOperator operator = disableAllBindingExpression();
ExpressionOperator.exp().copyTo(operator);
return operator;
}
/**
* Disable binding support.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement cannot be executed because a parameter marker has been used
* in an invalid way. DB2 SQL Error: SQLCODE=-418, SQLSTATE=42610
*/
protected ExpressionOperator lnOperator() {
ExpressionOperator operator = disableAllBindingExpression();
ExpressionOperator.ln().copyTo(operator);
return operator;
}
/**
* Disable binding support.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement cannot be executed because a parameter marker has been used
* in an invalid way. DB2 SQL Error: SQLCODE=-418, SQLSTATE=42610
*/
protected ExpressionOperator powerOperator() {
ExpressionOperator operator = disableAllBindingExpression();
ExpressionOperator.power().copyTo(operator);
return operator;
}
/**
* Disable binding support.
*
* With binding enabled, DB2 z/OS will throw an error:
*
The statement cannot be executed because a parameter marker has been used
* in an invalid way. DB2 SQL Error: SQLCODE=-418, SQLSTATE=42610
*/
protected ExpressionOperator signOperator() {
ExpressionOperator operator = disableAllBindingExpression();
ExpressionOperator.sign().copyTo(operator);
return operator;
}
@Override
public boolean isDB2Z() {
return true;
}
/**
* INTERNAL: Used for sp calls. PostGreSQL uses a different method for executing StoredProcedures than other platforms.
*/
@Override
public String buildProcedureCallString(StoredProcedureCall call, AbstractSession session, AbstractRecord row) {
StringWriter writer = new StringWriter();
writer.write(call.getCallHeader(this));
writer.write(call.getProcedureName());
if (requiresProcedureCallBrackets()) {
writer.write("(");
} else {
writer.write(" ");
}
int indexFirst = call.getFirstParameterIndexForCallString();
int size = call.getParameters().size();
for (int index = indexFirst; index < size; index++) {
String name = call.getProcedureArgumentNames().get(index);
Object parameter = call.getParameters().get(index);
ParameterType parameterType = call.getParameterTypes().get(index);
// If the argument is optional and null, ignore it.
if (!call.hasOptionalArguments() || !call.getOptionalArguments().contains(parameter) || (row.get(parameter) != null)) {
writer.write(getProcedureArgument(name, parameter, parameterType, call, session));
if (DatasourceCall.isOutputParameterType(parameterType) && requiresProcedureCallOuputToken()) {
writer.write(" ");
writer.write(getOutputProcedureToken());
}
if ((index + 1) < call.getParameters().size()) {
writer.write(", ");
}
}
}
if (requiresProcedureCallBrackets()) {
writer.write(")");
}
writer.write(getProcedureCallTail());
return writer.toString();
}
/**
* This method is used to register output parameter on Callable Statements for Stored Procedures
* as each database seems to have a different method.
*/
@Override
public void registerOutputParameter(CallableStatement statement, String name, int jdbcType) throws SQLException {
try {
Class clazz;
Method method;
String methodName = "registerJccOutParameterAtName";
Class[] methodArgs = new Class[] {String.class, int.class};
Object[] parameters = new Object[] {name, jdbcType};
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
try {
ClassLoader cl = AccessController.doPrivileged(new PrivilegedGetContextClassLoader(Thread.currentThread()));
clazz = AccessController.doPrivileged(new PrivilegedClassForName<>(DB2_CALLABLESTATEMENT_CLASS, true, cl));
method = AccessController.doPrivileged(new PrivilegedGetMethod(clazz, methodName, methodArgs, true));
Object o = statement.unwrap(clazz);
AccessController.doPrivileged(new PrivilegedMethodInvoker<>(method, o, parameters));
} catch (PrivilegedActionException ex) {
if (ex.getCause() instanceof ClassNotFoundException) {
throw (ClassNotFoundException) ex.getCause();
}
throw (RuntimeException) ex.getCause();
}
} else {
ClassLoader cl = PrivilegedAccessHelper.getContextClassLoader(Thread.currentThread());
clazz = PrivilegedAccessHelper.getClassForName(DB2_CALLABLESTATEMENT_CLASS, true, cl);
method = PrivilegedAccessHelper.getMethod(clazz, methodName, methodArgs, true);
Object o = statement.unwrap(clazz);
PrivilegedAccessHelper.invokeMethod(method, o, parameters);
}
} catch (ReflectiveOperationException e) {
AbstractSessionLog.getLog().logThrowable(SessionLog.WARNING, null, e);
//Didn't work, fall back. This most likely still won't work, but the driver exception from there will be helpful.
super.registerOutputParameter(statement, name, jdbcType);
}
}
/**
* This method is used to register output parameter on Callable Statements for Stored Procedures
* as each database seems to have a different method.
*/
@Override
public void registerOutputParameter(CallableStatement statement, String name, int jdbcType, String typeName) throws SQLException {
try {
Class clazz;
Method method;
String methodName = "registerJccOutParameterAtName";
Class[] methodArgs = new Class[] {String.class, int.class, String.class};
Object[] parameters = new Object[] {name, jdbcType, typeName};
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
try {
ClassLoader cl = AccessController.doPrivileged(new PrivilegedGetContextClassLoader(Thread.currentThread()));
clazz = AccessController.doPrivileged(new PrivilegedClassForName<>(DB2_CALLABLESTATEMENT_CLASS, true, cl));
method = AccessController.doPrivileged(new PrivilegedGetMethod(clazz, methodName, methodArgs, true));
Object o = statement.unwrap(clazz);
AccessController.doPrivileged(new PrivilegedMethodInvoker<>(method, o, parameters));
} catch (PrivilegedActionException ex) {
if (ex.getCause() instanceof ClassNotFoundException) {
throw (ClassNotFoundException) ex.getCause();
}
throw (RuntimeException) ex.getCause();
}
} else {
ClassLoader cl = PrivilegedAccessHelper.getContextClassLoader(Thread.currentThread());
clazz = PrivilegedAccessHelper.getClassForName(DB2_CALLABLESTATEMENT_CLASS, true, cl);
method = PrivilegedAccessHelper.getMethod(clazz, methodName, methodArgs, true);
Object o = statement.unwrap(clazz);
PrivilegedAccessHelper.invokeMethod(method, o, parameters);
}
} catch (ReflectiveOperationException e) {
AbstractSessionLog.getLog().logThrowable(SessionLog.WARNING, null, e);
//Didn't work, fall back. This most likely still won't work, but the driver exception from there will be helpful.
super.registerOutputParameter(statement, name, jdbcType, typeName);
}
}
@Override
@SuppressWarnings({"rawtypes"})
public void setParameterValueInDatabaseCall(Object parameter,
CallableStatement statement, String name, AbstractSession session)
throws SQLException {
String methodName = null;
Class[] methodArgs = null;
Object[] parameters = null;
// Process common types first.
if (parameter instanceof String) {
// Check for stream binding of large strings.
if (usesStringBinding() && (((String)parameter).length() > getStringBindingSize())) {
CharArrayReader reader = new CharArrayReader(((String)parameter).toCharArray());
methodName = "setJccCharacterStreamAtName";
methodArgs = new Class[] {String.class, java.io.Reader.class, int.class};
parameters = new Object[] {name, reader, ((String)parameter).length()};
} else {
//TODO find shouldUseGetSetNString() support for DB2/Z
methodName = "setJccStringAtName";
methodArgs = new Class[] {String.class, String.class};
parameters = new Object[] {name, parameter};
}
} else if (parameter instanceof Number number) {
if (number instanceof Integer) {
methodName = "setJccIntAtName";
methodArgs = new Class[] {String.class, int.class};
parameters = new Object[] {name, number.intValue()};
} else if (number instanceof Long) {
methodName = "setJccLongAtName";
methodArgs = new Class[] {String.class, long.class};
parameters = new Object[] {name, number.longValue()};
} else if (number instanceof BigDecimal) {
methodName = "setJccBigDecimalAtName";
methodArgs = new Class[] {String.class, BigDecimal.class};
parameters = new Object[] {name, number};
} else if (number instanceof Double) {
methodName = "setJccDoubleAtName";
methodArgs = new Class[] {String.class, double.class};
parameters = new Object[] {name, number.doubleValue()};
} else if (number instanceof Float) {
methodName = "setJccFloatAtName";
methodArgs = new Class[] {String.class, float.class};
parameters = new Object[] {name, number.floatValue()};
} else if (number instanceof Short) {
methodName = "setJccShortAtName";
methodArgs = new Class[] {String.class, short.class};
parameters = new Object[] {name, number.shortValue()};
} else if (number instanceof Byte) {
methodName = "setJccByteAtName";
methodArgs = new Class[] {String.class, byte.class};
parameters = new Object[] {name, number.byteValue()};
} else if (number instanceof BigInteger) {
// Convert to BigDecimal.
methodName = "setJccBigDecimalAtName";
methodArgs = new Class[] {String.class, BigDecimal.class};
parameters = new Object[] {name, new BigDecimal((BigInteger) number)};
} else {
methodName = "setJccObjectAtName";
methodArgs = new Class[] {String.class, Object.class};
parameters = new Object[] {name, parameter};
}
} else if (parameter instanceof java.sql.Date){
methodName = "setJccDateAtName";
methodArgs = new Class[] {String.class, java.sql.Date.class};
parameters = new Object[] {name, parameter};
} else if (parameter instanceof java.time.LocalDate){
// Convert to java.sql.Date
methodName = "setJccDateAtName";
methodArgs = new Class[] {String.class, java.sql.Date.class};
parameters = new Object[] {name, java.sql.Date.valueOf((java.time.LocalDate) parameter)};
} else if (parameter instanceof java.sql.Timestamp){
methodName = "setJccTimestampAtName";
methodArgs = new Class[] {String.class, java.sql.Timestamp.class};
parameters = new Object[] {name, parameter};
} else if (parameter instanceof java.time.LocalDateTime){
// Convert to java.sql.Timestamp
methodName = "setJccTimestampAtName";
methodArgs = new Class[] {String.class, java.sql.Timestamp.class};
parameters = new Object[] {name, java.sql.Timestamp.valueOf((java.time.LocalDateTime) parameter)};
} else if (parameter instanceof java.time.OffsetDateTime) {
// Convert to java.sql.Timestamp
methodName = "setJccTimestampAtName";
methodArgs = new Class[] {String.class, java.sql.Timestamp.class};
parameters = new Object[] {name, java.sql.Timestamp.from(((java.time.OffsetDateTime) parameter).toInstant())};
} else if (parameter instanceof java.sql.Time){
methodName = "setJccTimeAtName";
methodArgs = new Class[] {String.class, java.sql.Time.class};
parameters = new Object[] {name, parameter};
} else if (parameter instanceof java.time.LocalTime lt){
java.sql.Timestamp ts = java.sql.Timestamp.valueOf(java.time.LocalDateTime.of(java.time.LocalDate.ofEpochDay(0), lt));
methodName = "setJccTimestampAtName";
methodArgs = new Class[] {String.class, java.sql.Timestamp.class};
parameters = new Object[] {name, ts};
} else if (parameter instanceof java.time.OffsetTime ot) {
java.sql.Timestamp ts = java.sql.Timestamp.valueOf(java.time.LocalDateTime.of(java.time.LocalDate.ofEpochDay(0), ot.toLocalTime()));
methodName = "setJccTimestampAtName";
methodArgs = new Class[] {String.class, java.sql.Timestamp.class};
parameters = new Object[] {name, ts};
} else if (parameter instanceof Boolean) {
methodName = "setJccBooleanAtName";
methodArgs = new Class[] {String.class, boolean.class};
parameters = new Object[] {name, parameter};
} else if (parameter == null) {
// Normally null is passed as a DatabaseField so the type is included, but in some case may be passed directly.
methodName = "setJccNullAtName";
methodArgs = new Class[] {String.class, int.class};
parameters = new Object[] {name, getJDBCType((Class)null)};
} else if (parameter instanceof DatabaseField) {
setNullFromDatabaseField((DatabaseField)parameter, statement, name);
} else if (parameter instanceof byte[]) {
if (usesStreamsForBinding()) {
ByteArrayInputStream inputStream = new ByteArrayInputStream((byte[])parameter);
methodName = "setJccBinaryStreamAtName";
methodArgs = new Class[] {String.class, java.io.InputStream.class, int.class};
parameters = new Object[] {name, inputStream, ((byte[])parameter).length};
} else {
methodName = "setJccBytesAtName";
methodArgs = new Class[] {String.class, byte[].class};
parameters = new Object[] {name, parameter};
}
}
// Next process types that need conversion.
else if (parameter instanceof Calendar) {
methodName = "setJccTimestampAtName";
methodArgs = new Class[] {String.class, java.sql.Timestamp.class};
parameters = new Object[] {name, Helper.timestampFromDate(((Calendar)parameter).getTime())};
} else if (parameter.getClass() == ClassConstants.UTILDATE) {
methodName = "setJccTimestampAtName";
methodArgs = new Class[] {String.class, java.sql.Timestamp.class};
parameters = new Object[] {name, Helper.timestampFromDate((java.util.Date) parameter)};
} else if (parameter instanceof Character) {
methodName = "setJccStringAtName";
methodArgs = new Class[] {String.class, String.class};
parameters = new Object[] {name, ((Character)parameter).toString()};
} else if (parameter instanceof char[]) {
methodName = "setJccStringAtName";
methodArgs = new Class[] {String.class, String.class};
parameters = new Object[] {name, new String((char[])parameter)};
} else if (parameter instanceof Character[]) {
methodName = "setJccStringAtName";
methodArgs = new Class[] {String.class, String.class};
parameters = new Object[] {name, convertObject(parameter, ClassConstants.STRING)};
} else if (parameter instanceof Byte[]) {
methodName = "setJccBytesAtName";
methodArgs = new Class[] {String.class, byte[].class};
parameters = new Object[] {name, convertObject(parameter, ClassConstants.APBYTE)};
} else if (parameter instanceof java.sql.SQLXML) {
methodName = "setJccSQLXMLAtName";
methodArgs = new Class[] {String.class, java.sql.SQLXML.class};
parameters = new Object[] {name, parameter};
} else if (parameter instanceof BindCallCustomParameter) {
((BindCallCustomParameter)(parameter)).set(this, statement, name, session);
} else if (typeConverters != null && typeConverters.containsKey(parameter.getClass())){
StructConverter converter = typeConverters.get(parameter.getClass());
java.sql.Struct struct = converter.convertToStruct(parameter, getConnection(session, statement.getConnection()));
methodName = "setJccObjectAtName";
methodArgs = new Class[] {String.class, Object.class};
parameters = new Object[] {name, struct};
} else {
methodName = "setJccObjectAtName";
methodArgs = new Class[] {String.class, Object.class};
parameters = new Object[] {name, parameter};
}
if(methodName != null) {
try {
Class clazz;
Method method;
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
try {
ClassLoader cl = AccessController.doPrivileged(new PrivilegedGetContextClassLoader(Thread.currentThread()));
clazz = AccessController.doPrivileged(new PrivilegedClassForName<>(DB2_PREPAREDSTATEMENT_CLASS, true, cl));
method = AccessController.doPrivileged(new PrivilegedGetMethod(clazz, methodName, methodArgs, true));
Object o = statement.unwrap(clazz);
AccessController.doPrivileged(new PrivilegedMethodInvoker<>(method, o, parameters));
} catch (PrivilegedActionException ex) {
if (ex.getCause() instanceof ClassNotFoundException) {
throw (ClassNotFoundException) ex.getCause();
}
throw (RuntimeException) ex.getCause();
}
} else {
ClassLoader cl = PrivilegedAccessHelper.getContextClassLoader(Thread.currentThread());
clazz = PrivilegedAccessHelper.getClassForName(DB2_PREPAREDSTATEMENT_CLASS, true, cl);
method = PrivilegedAccessHelper.getMethod(clazz, methodName, methodArgs, true);
Object o = statement.unwrap(clazz);
PrivilegedAccessHelper.invokeMethod(method, o, parameters);
}
} catch (ReflectiveOperationException e) {
AbstractSessionLog.getLog().logThrowable(SessionLog.WARNING, null, e);
//Didn't work, fall back. This most likely still won't work, but the driver exception from there will be helpful.
super.setParameterValueInDatabaseCall(parameter, statement, name, session);
}
}
}
@Override
protected void setNullFromDatabaseField(DatabaseField databaseField, CallableStatement statement, String name) throws SQLException {
String methodName;
Class[] methodArgs;
Object[] parameters;
if (databaseField instanceof ObjectRelationalDatabaseField field) {
methodName = "setJccNullAtName";
methodArgs = new Class[] {String.class, int.class, String.class};
parameters = new Object[] {name, field.getSqlType(), field.getSqlTypeName()};
} else {
int jdbcType = getJDBCTypeForSetNull(databaseField);
methodName = "setJccNullAtName";
methodArgs = new Class[] {String.class, int.class};
parameters = new Object[] {name, jdbcType};
}
try {
Class clazz;
Method method;
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
try {
ClassLoader cl = AccessController.doPrivileged(new PrivilegedGetContextClassLoader(Thread.currentThread()));
clazz = AccessController.doPrivileged(new PrivilegedClassForName<>(DB2_PREPAREDSTATEMENT_CLASS, true, cl));
method = AccessController.doPrivileged(new PrivilegedGetMethod(clazz, methodName, methodArgs, true));
Object o = statement.unwrap(clazz);
AccessController.doPrivileged(new PrivilegedMethodInvoker<>(method, o, parameters));
} catch (PrivilegedActionException ex) {
if (ex.getCause() instanceof ClassNotFoundException) {
throw (ClassNotFoundException) ex.getCause();
}
throw (RuntimeException) ex.getCause();
}
} else {
ClassLoader cl = PrivilegedAccessHelper.getContextClassLoader(Thread.currentThread());
clazz = PrivilegedAccessHelper.getClassForName(DB2_PREPAREDSTATEMENT_CLASS, true, cl);
method = PrivilegedAccessHelper.getMethod(clazz, methodName, methodArgs, true);
Object o = statement.unwrap(clazz);
PrivilegedAccessHelper.invokeMethod(method, o, parameters);
}
} catch (ReflectiveOperationException e) {
AbstractSessionLog.getLog().logThrowable(SessionLog.WARNING, null, e);
//Didn't work, fall back. This most likely still won't work, but the driver exception from there will be helpful.
super.setNullFromDatabaseField(databaseField, statement, name);
}
}
@Override
public Object getParameterValueFromDatabaseCall(CallableStatement statement, String name, AbstractSession session)
throws SQLException {
String methodName;
Class[] methodArgs;
Object[] parameters;
methodName = "getJccObjectAtName";
methodArgs = new Class[] {String.class};
parameters = new Object[] {name};
try {
Class clazz;
Method method;
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
try {
ClassLoader cl = AccessController.doPrivileged(new PrivilegedGetContextClassLoader(Thread.currentThread()));
clazz = AccessController.doPrivileged(new PrivilegedClassForName<>(DB2_CALLABLESTATEMENT_CLASS, true, cl));
method = AccessController.doPrivileged(new PrivilegedGetMethod(clazz, methodName, methodArgs, true));
Object o = statement.unwrap(clazz);
return AccessController.doPrivileged(new PrivilegedMethodInvoker<>(method, o, parameters));
} catch (PrivilegedActionException ex) {
if (ex.getCause() instanceof ClassNotFoundException) {
throw (ClassNotFoundException) ex.getCause();
}
throw (RuntimeException) ex.getCause();
}
} else {
ClassLoader cl = PrivilegedAccessHelper.getContextClassLoader(Thread.currentThread());
clazz = PrivilegedAccessHelper.getClassForName(DB2_CALLABLESTATEMENT_CLASS, true, cl);
method = PrivilegedAccessHelper.getMethod(clazz, methodName, methodArgs, true);
Object o = statement.unwrap(clazz);
return PrivilegedAccessHelper.invokeMethod(method, o, parameters);
}
} catch (ReflectiveOperationException e) {
AbstractSessionLog.getLog().logThrowable(SessionLog.WARNING, null, e);
}
//Didn't work, fall back. This most likely still won't work, but the driver exception from there will be helpful.
return super.getParameterValueFromDatabaseCall(statement, name, session);
}
}