From 8a232d422c15b1d15e757aed1282edc4754c1c9e Mon Sep 17 00:00:00 2001 From: Will Dazey Date: Thu, 10 Oct 2019 02:20:55 -0500 Subject: [PATCH] Test Fix: testCoalesceJPQLQueryWithNullParameterValue, StoredProcedureTest_Inout_Out_In bind, testEmpRecordInOut (#581) Signed-off-by: Will Dazey --- .../databaseaccess/DatabasePlatform.java | 24 ++--- .../jpa/jpql/ExpressionBuilderVisitor.java | 16 +++- .../platform/database/DB2ZPlatform.java | 14 +-- .../platform/database/DBasePlatform.java | 18 +++- .../platform/database/HANAPlatform.java | 10 +- .../platform/database/OraclePlatform.java | 19 ++-- .../platform/database/SQLServerPlatform.java | 19 +++- .../platform/database/SybasePlatform.java | 5 + .../plsql/PLSQLStoredProcedureCall.java | 94 +++++++++++++++---- .../queries/StoredProcedureCall.java | 22 +++++ .../database/oracle/Oracle9Platform.java | 22 +++++ 11 files changed, 202 insertions(+), 61 deletions(-) diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/databaseaccess/DatabasePlatform.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/databaseaccess/DatabasePlatform.java index 7ca7bf0e196..e5b3acc36a3 100644 --- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/databaseaccess/DatabasePlatform.java +++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/databaseaccess/DatabasePlatform.java @@ -839,12 +839,9 @@ public String buildProcedureCallString(StoredProcedureCall call, AbstractSession Integer 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)) { - if (name != null && shouldPrintStoredProcedureArgumentNameInCall()) { - writer.write(getProcedureArgumentString()); - writer.write(name); - writer.write(getProcedureArgumentSetter()); - } - writer.write("?"); + + writer.write(getProcedureArgument(name, parameter, parameterType, call, session)); + if (DatasourceCall.isOutputParameterType(parameterType)) { if (requiresProcedureCallOuputToken()) { writer.write(" "); @@ -1502,17 +1499,20 @@ public String getPingSQL(){ } /** - * Used for sp calls. + * Used for sp defs. */ - public String getProcedureArgumentSetter() { - return " = "; + public String getProcedureArgumentString() { + return ""; } /** - * Used for sp defs. + * Obtain the platform specific argument string */ - public String getProcedureArgumentString() { - return ""; + public String getProcedureArgument(String name, Object parameter, Integer parameterType, StoredProcedureCall call, AbstractSession session) { + if (name != null && shouldPrintStoredProcedureArgumentNameInCall()) { + return getProcedureArgumentString() + name + " = " + "?"; + } + return "?"; } /** diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/jpa/jpql/ExpressionBuilderVisitor.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/jpa/jpql/ExpressionBuilderVisitor.java index 430560cdce8..1d6598f2dff 100644 --- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/jpa/jpql/ExpressionBuilderVisitor.java +++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/jpa/jpql/ExpressionBuilderVisitor.java @@ -669,20 +669,28 @@ public void visit(CoalesceExpression expression) { List expressions = new ArrayList(); List> types = new LinkedList>(); + //cache the type of the expression so untyped children have a default type + Class coalesceType = type[0]; + // Create the Expression for each scalar expression for (org.eclipse.persistence.jpa.jpql.parser.Expression child : expression.getExpression().children()) { child.accept(this); expressions.add(queryExpression); - types.add(type[0]); - // Set the type on an untyped ParameterExpression, so that - // valid types can be passed for null parameter values in JDBC + //get the expression type parsed from the child expression + Class childType = type[0]; + + // Default the type on an untyped ParameterExpression to the cached expression type. + // This is to help provide a valid type for null parameter when binding JDBC parameter types if (queryExpression.isParameterExpression()) { ParameterExpression paramExpression = (ParameterExpression) queryExpression; if (paramExpression.getType() == null || paramExpression.getType().equals(Object.class)) { - paramExpression.setType(type[0]); + paramExpression.setType(coalesceType); + childType = coalesceType; } } + + types.add(childType); } // Create the COALESCE expression diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/DB2ZPlatform.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/DB2ZPlatform.java index f40f1d0fb01..a7b8013384d 100644 --- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/DB2ZPlatform.java +++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/DB2ZPlatform.java @@ -88,7 +88,11 @@ public String getTableCreationSuffix() { } @Override - public String getProcedureArgumentSetter() { + public String getProcedureArgument(String name, Object parameter, Integer parameterType, + StoredProcedureCall call, AbstractSession session) { + if (name != null && shouldPrintStoredProcedureArgumentNameInCall()) { + return ":" + name; + } return ""; } @@ -127,11 +131,9 @@ public String buildProcedureCallString(StoredProcedureCall call, AbstractSession Integer 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)) { - if (name != null && shouldPrintStoredProcedureArgumentNameInCall()) { - writer.write(":"); - writer.write(name); - writer.write(getProcedureArgumentSetter()); - } + + writer.write(getProcedureArgument(name, parameter, parameterType, call, session)); + if (DatasourceCall.isOutputParameterType(parameterType)) { if (requiresProcedureCallOuputToken()) { writer.write(" "); diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/DBasePlatform.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/DBasePlatform.java index 41441d102c4..82182c3a180 100644 --- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/DBasePlatform.java +++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/DBasePlatform.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 1998, 2018 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019 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 @@ -15,6 +16,7 @@ package org.eclipse.persistence.platform.database; import java.io.*; +import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.*; @@ -89,6 +91,20 @@ public void setParameterValueInDatabaseCall(Object parameter, super.setParameterValueInDatabaseCall(databaseValue, statement, index, session); } + /** + * INTERNAL: + * DBase does not support Time/Timestamp so we must map to strings. + */ + @Override + public void setParameterValueInDatabaseCall(Object parameter, + CallableStatement statement, String name, AbstractSession session) throws SQLException { + Object databaseValue = super.convertToDatabaseType(parameter); + if ((databaseValue instanceof java.sql.Time) || (databaseValue instanceof java.sql.Timestamp)) { + databaseValue = databaseValue.toString(); + } + super.setParameterValueInDatabaseCall(databaseValue, statement, name, session); + } + /** * INTERNAL: * returns the maximum number of characters that can be used in a field diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/HANAPlatform.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/HANAPlatform.java index 67ec37c40b3..6af047892f6 100644 --- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/HANAPlatform.java +++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/HANAPlatform.java @@ -1,6 +1,7 @@ /* - * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2018 SAP. All rights reserved. + * Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019 IBM Corporation. All rights reserved. + * Copyright (c) 2012, 2019 SAP. 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 @@ -496,11 +497,6 @@ public boolean shouldPrintStoredProcedureArgumentNameInCall() { return false; } - @Override - public boolean requiresProcedureCallOuputToken() { - return false; - } - @Override public boolean supportsStoredFunctions() { return false; diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/OraclePlatform.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/OraclePlatform.java index 9f4b6f7fe3e..4be818ba3ec 100644 --- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/OraclePlatform.java +++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/OraclePlatform.java @@ -49,6 +49,7 @@ import org.eclipse.persistence.expressions.Expression; import org.eclipse.persistence.expressions.ExpressionOperator; import org.eclipse.persistence.internal.databaseaccess.DatabaseCall; +import org.eclipse.persistence.internal.databaseaccess.DatasourceCall; import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition; import org.eclipse.persistence.internal.expressions.ExpressionSQLPrinter; import org.eclipse.persistence.internal.expressions.FunctionExpression; @@ -68,6 +69,7 @@ import org.eclipse.persistence.queries.ObjectBuildingQuery; import org.eclipse.persistence.queries.ReadQuery; import org.eclipse.persistence.queries.SQLCall; +import org.eclipse.persistence.queries.StoredProcedureCall; import org.eclipse.persistence.queries.ValueReadQuery; import org.eclipse.persistence.tools.schemaframework.TableDefinition; @@ -434,14 +436,6 @@ public Vector getNativeTableInfo(String table, String creator, AbstractSession s return session.executeSelectingCall(new SQLCall(query)); } - /** - * Used for sp calls. - */ - @Override - public String getProcedureArgumentSetter() { - return "=>"; - } - /** * Used for sp calls. */ @@ -816,6 +810,15 @@ public boolean shouldPrintStoredProcedureArgumentNameInCall() { return false; } + @Override + public String getProcedureArgument(String name, Object parameter, Integer parameterType, + StoredProcedureCall call, AbstractSession session) { + if(name != null && DatasourceCall.IN.equals(parameterType) && !call.usesBinding(session)) { + return name + "=>" + "?"; + } + return "?"; + } + /** * JDBC defines and outer join syntax, many drivers do not support this. So we normally avoid it. */ diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/SQLServerPlatform.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/SQLServerPlatform.java index 823b6b7ac7f..fed894fdcaa 100644 --- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/SQLServerPlatform.java +++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/SQLServerPlatform.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 1998, 2018 Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 1998, 2018 IBM Corporation. All rights reserved. + * Copyright (c) 1998, 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2019 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 @@ -22,6 +22,7 @@ package org.eclipse.persistence.platform.database; import java.io.*; +import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; @@ -821,7 +822,19 @@ public void setParameterValueInDatabaseCall(Object parameter, PreparedStatement statement.setObject(index, parameter); return; } - + super.setParameterValueInDatabaseCall(parameter, statement, index, session); } + + @Override + public void setParameterValueInDatabaseCall(Object parameter, CallableStatement statement, String name, + AbstractSession session) throws SQLException { + if (driverSupportsOffsetDateTime && parameter instanceof OffsetDateTime) { + // avoid default logic, which loses offset when converting to java.sql.Timestamp + statement.setObject(name, parameter); + return; + } + + super.setParameterValueInDatabaseCall(parameter, statement, name, session); + } } diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/SybasePlatform.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/SybasePlatform.java index 73c62094e11..6df37ec1f79 100644 --- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/SybasePlatform.java +++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/SybasePlatform.java @@ -665,6 +665,11 @@ public void registerOutputParameter(CallableStatement statement, int index, int statement.registerOutParameter(index, jdbcType, getTypeStrings().get(jdbcType)); } + @Override + public void registerOutputParameter(CallableStatement statement, int index, int jdbcType, String typeName) throws SQLException { + statement.registerOutParameter(index, jdbcType, getTypeStrings().get(jdbcType)); + } + /** * USed for sp calls. */ diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/oracle/plsql/PLSQLStoredProcedureCall.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/oracle/plsql/PLSQLStoredProcedureCall.java index 9d51a945e84..bfec9af70fd 100644 --- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/oracle/plsql/PLSQLStoredProcedureCall.java +++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/oracle/plsql/PLSQLStoredProcedureCall.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 1998, 2018 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019 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 @@ -28,7 +29,9 @@ import java.io.Serializable; import java.sql.CallableStatement; +import java.sql.PreparedStatement; import java.sql.SQLException; +import java.sql.Statement; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -40,6 +43,7 @@ import java.util.Map; import java.util.Set; import java.util.Vector; +import java.util.concurrent.TimeUnit; // EclipseLink imports import org.eclipse.persistence.exceptions.QueryException; @@ -505,27 +509,28 @@ protected void assignIndices() { } for (PLSQLargument inArg : inArguments) { DatabaseType type = inArg.databaseType; + String inArgName = inArg.name; if (!type.isComplexDatabaseType()) { // for XMLType, we need to set type name parameter (will be "XMLTYPE") if (type == XMLType) { - super.addNamedArgument(inArg.name, inArg.name, type.getConversionCode(), type.getTypeName()); + super.addNamedArgument(inArgName, inArgName, type.getConversionCode(), type.getTypeName()); } else { - super.addNamedArgument(inArg.name, inArg.name, type.getConversionCode()); + super.addNamedArgument(inArgName, inArgName, type.getConversionCode()); } } else { ComplexDatabaseType complexType = (ComplexDatabaseType) type; if (inArg.inIndex != MIN_VALUE) { if (complexType.isStruct()) { - super.addNamedArgument(inArg.name, inArg.name, complexType.getSqlCode(), complexType.getTypeName()); + super.addNamedArgument(inArgName, inArgName, complexType.getSqlCode(), complexType.getTypeName()); } else if (complexType.isArray()) { DatabaseType nestedType = ((OracleArrayType) complexType).getNestedType(); if (nestedType != null) { ObjectRelationalDatabaseField field = new ObjectRelationalDatabaseField(""); field.setSqlType(nestedType.getSqlCode()); field.setSqlTypeName(nestedType.getTypeName()); - super.addNamedArgument(inArg.name, inArg.name, complexType.getSqlCode(), complexType.getTypeName(), field); + super.addNamedArgument(inArgName, inArgName, complexType.getSqlCode(), complexType.getTypeName(), field); } else { - super.addNamedArgument(inArg.name, inArg.name, complexType.getSqlCode(), complexType.getTypeName()); + super.addNamedArgument(inArgName, inArgName, complexType.getSqlCode(), complexType.getTypeName()); } } else if (complexType.isCollection()) { DatabaseType nestedType = ((PLSQLCollection) complexType).getNestedType(); @@ -535,12 +540,12 @@ protected void assignIndices() { if (nestedType.isComplexDatabaseType()) { field.setSqlTypeName(((ComplexDatabaseType) nestedType).getCompatibleType()); } - super.addNamedArgument(inArg.name, inArg.name, type.getConversionCode(), complexType.getCompatibleType(), field); + super.addNamedArgument(inArgName, inArgName, type.getConversionCode(), complexType.getCompatibleType(), field); } else { - super.addNamedArgument(inArg.name, inArg.name, type.getConversionCode(), complexType.getCompatibleType()); + super.addNamedArgument(inArgName, inArgName, type.getConversionCode(), complexType.getCompatibleType()); } } else { - super.addNamedArgument(inArg.name, inArg.name, type.getConversionCode(), complexType.getCompatibleType()); + super.addNamedArgument(inArgName, inArgName, type.getConversionCode(), complexType.getCompatibleType()); } } } @@ -552,22 +557,23 @@ protected void assignIndices() { newIndex = outArg.databaseType.computeOutIndex(outArg, newIndex, outArgsIter); } for (PLSQLargument outArg : outArguments) { + String outArgName = outArg.name; if (outArg.cursorOutput) { - super.useNamedCursorOutputAsResultSet(outArg.name); + super.useNamedCursorOutputAsResultSet(outArgName); } else { DatabaseType type = outArg.databaseType; if (!type.isComplexDatabaseType()) { // for XMLType, we need to set type name parameter (will be "XMLTYPE") if (type == XMLType) { - super.addNamedOutputArgument(outArg.name, outArg.name, type.getConversionCode(), type.getTypeName()); + super.addNamedOutputArgument(outArgName, outArgName, type.getConversionCode(), type.getTypeName()); } else { - super.addNamedOutputArgument(outArg.name, outArg.name, type.getConversionCode()); + super.addNamedOutputArgument(outArgName, outArgName, type.getConversionCode()); } } else { ComplexDatabaseType complexType = (ComplexDatabaseType) type; if (outArg.outIndex != MIN_VALUE) { if (complexType.isStruct()) { - super.addNamedOutputArgument(outArg.name, outArg.name, complexType.getSqlCode(), complexType.getTypeName(), complexType.getJavaType()); + super.addNamedOutputArgument(outArgName, outArgName, complexType.getSqlCode(), complexType.getTypeName(), complexType.getJavaType()); } else if (complexType.isArray()) { DatabaseType nestedType = ((OracleArrayType) complexType).getNestedType(); if (nestedType != null) { @@ -578,30 +584,30 @@ protected void assignIndices() { nestedField.setType(complexNestedType.getJavaType()); nestedField.setSqlTypeName(complexNestedType.getCompatibleType()); } - super.addNamedOutputArgument(outArg.name, outArg.name, type.getSqlCode(), complexType.getTypeName(), complexType.getJavaType(), nestedField); + super.addNamedOutputArgument(outArgName, outArgName, type.getSqlCode(), complexType.getTypeName(), complexType.getJavaType(), nestedField); } else { - super.addNamedOutputArgument(outArg.name, outArg.name, type.getSqlCode(), complexType.getTypeName(), complexType.getJavaType()); + super.addNamedOutputArgument(outArgName, outArgName, type.getSqlCode(), complexType.getTypeName(), complexType.getJavaType()); } } else if (complexType.isCollection()) { DatabaseType nestedType = ((PLSQLCollection) complexType).getNestedType(); if (nestedType != null) { - ObjectRelationalDatabaseField nestedField = new ObjectRelationalDatabaseField(outArg.name); + ObjectRelationalDatabaseField nestedField = new ObjectRelationalDatabaseField(outArgName); nestedField.setSqlType(nestedType.getSqlCode()); if (nestedType.isComplexDatabaseType()) { ComplexDatabaseType complexNestedType = (ComplexDatabaseType) nestedType; nestedField.setType(complexNestedType.getJavaType()); nestedField.setSqlTypeName(complexNestedType.getCompatibleType()); } - super.addNamedOutputArgument(outArg.name, outArg.name, type.getSqlCode(), complexType.getCompatibleType(), complexType.getJavaType(), nestedField); + super.addNamedOutputArgument(outArgName, outArgName, type.getSqlCode(), complexType.getCompatibleType(), complexType.getJavaType(), nestedField); } else { - super.addNamedOutputArgument(outArg.name, outArg.name, type.getSqlCode(), complexType.getCompatibleType()); + super.addNamedOutputArgument(outArgName, outArgName, type.getSqlCode(), complexType.getCompatibleType()); } } else if (complexType.hasCompatibleType()) { - super.addNamedOutputArgument(outArg.name, outArg.name, type.getSqlCode(), complexType.getCompatibleType(), complexType.getJavaType()); + super.addNamedOutputArgument(outArgName, outArgName, type.getSqlCode(), complexType.getCompatibleType(), complexType.getJavaType()); } else { // If there is no STRUCT type set, then the output is // expanded, so one output for each field. - super.addNamedOutputArgument(outArg.name, outArg.name, type.getSqlCode()); + super.addNamedOutputArgument(outArgName, outArgName, type.getSqlCode()); } } } @@ -1062,6 +1068,49 @@ protected void prepareInternal(AbstractSession session) { super.prepareInternalParameters(session); } + /** + * INTERNAL: + * Prepare the JDBC statement, this may be parameterize or a call statement. + * If caching statements this must check for the pre-prepared statement and re-bind to it. + */ + @Override + public Statement prepareStatement(DatabaseAccessor accessor, AbstractRecord translationRow, AbstractSession session) throws SQLException { + //#Bug5200836 pass shouldUnwrapConnection flag to indicate whether or not using unwrapped connection. + Statement statement = accessor.prepareStatement(this, session); + + // Setup the max rows returned and query timeout limit. + if (this.queryTimeout > 0 && this.queryTimeoutUnit != null) { + long timeout = TimeUnit.SECONDS.convert(this.queryTimeout, this.queryTimeoutUnit); + + if(timeout > Integer.MAX_VALUE){ + timeout = Integer.MAX_VALUE; + } + + //Round up the timeout if SECONDS are larger than the given units + if(TimeUnit.SECONDS.compareTo(this.queryTimeoutUnit) > 0 && this.queryTimeout % 1000 > 0){ + timeout += 1; + } + statement.setQueryTimeout((int)timeout); + } + if (!this.ignoreMaxResultsSetting && this.maxRows > 0) { + statement.setMaxRows(this.maxRows); + } + if (this.resultSetFetchSize > 0) { + statement.setFetchSize(this.resultSetFetchSize); + } + + if (this.parameters == null) { + return statement; + } + List parameters = getParameters(); + int size = parameters.size(); + for (int index = 0; index < size; index++) { + session.getPlatform().setParameterValueInDatabaseCall(parameters.get(index), (PreparedStatement)statement, index+1, session); + } + + return statement; + } + /** * Translate the PLSQL procedure translation row, into the row * expected by the SQL procedure. @@ -1252,6 +1301,11 @@ public String getPl2SQLName(ComplexDatabaseType type) { return info.pl2SqlName; } + @Override + protected Object getObject(CallableStatement statement, int index) throws SQLException { + return statement.getObject(index + 1); + } + /** * Return the PLSQL arguments. */ diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/queries/StoredProcedureCall.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/queries/StoredProcedureCall.java index f10aea73b85..c59e6e5ce3d 100644 --- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/queries/StoredProcedureCall.java +++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/queries/StoredProcedureCall.java @@ -17,13 +17,18 @@ // - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls package org.eclipse.persistence.queries; +import java.io.IOException; +import java.io.Writer; import java.sql.CallableStatement; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.concurrent.TimeUnit; +import org.eclipse.persistence.exceptions.QueryException; +import org.eclipse.persistence.exceptions.ValidationException; import org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor; import org.eclipse.persistence.internal.databaseaccess.DatabaseCall; import org.eclipse.persistence.internal.databaseaccess.DatabasePlatform; @@ -1054,4 +1059,21 @@ protected Object getObject(CallableStatement statement, int index) throws SQLExc String name = procedureArgs.get(index); return statement.getObject(name); } + + /** + * Bind the parameter. Binding is determined by the call and second the platform. + */ + @Override + public void bindParameter(Writer writer, Object parameter) { + if (parameter instanceof Collection) { + throw QueryException.inCannotBeParameterized(getQuery()); + } + + try { + writer.write("?"); + } catch (IOException exception) { + throw ValidationException.fileError(exception); + } + getParameters().add(parameter); + } } diff --git a/foundation/org.eclipse.persistence.oracle/src/org/eclipse/persistence/platform/database/oracle/Oracle9Platform.java b/foundation/org.eclipse.persistence.oracle/src/org/eclipse/persistence/platform/database/oracle/Oracle9Platform.java index 1497a7c4835..e17301a2cf5 100644 --- a/foundation/org.eclipse.persistence.oracle/src/org/eclipse/persistence/platform/database/oracle/Oracle9Platform.java +++ b/foundation/org.eclipse.persistence.oracle/src/org/eclipse/persistence/platform/database/oracle/Oracle9Platform.java @@ -20,6 +20,7 @@ import java.io.Writer; import java.lang.reflect.Constructor; import java.security.AccessController; +import java.sql.CallableStatement; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -562,6 +563,27 @@ public void setParameterValueInDatabaseCall(Object parameter, PreparedStatement } } + /** + * INTERNAL: + * Note that index (not index+1) is used in statement.setObject(index, parameter) + * Binding starts with a 1 not 0, so make sure that index > 0. + * Treat Calendar separately. Bind Calendar as TIMESTAMPTZ. + */ + @Override + public void setParameterValueInDatabaseCall(Object parameter, CallableStatement statement, String name, AbstractSession session) throws SQLException { + if (parameter instanceof Calendar) { + Calendar calendar = (Calendar)parameter; + Connection conn = getConnection(session, statement.getConnection()); + TIMESTAMPTZ tsTZ = TIMESTAMPHelper.buildTIMESTAMPTZ(calendar, conn, this.shouldPrintCalendar); + statement.setObject(name, tsTZ); + } else if (this.shouldTruncateDate && parameter instanceof java.sql.Date) { + // hours, minutes, seconds all set to zero + statement.setDate(name, Helper.truncateDateIgnoreMilliseconds((java.sql.Date)parameter)); + } else { + super.setParameterValueInDatabaseCall(parameter, statement, name, session); + } + } + /** * INTERNAL: * Answer the timestamp from the server. Convert TIMESTAMPTZ to Timestamp