diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatabasePlatform.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatabasePlatform.java index e688652ecb0..1e0f3512dc0 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatabasePlatform.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatabasePlatform.java @@ -1442,16 +1442,6 @@ public boolean isInformixOuterJoin() { return false; } - /** - * Returns true if this platform complies with the expected behavior from - * a jdbc execute call. Most platforms do, some have issues: - * - * @see PostgreSQLPlatform - */ - public boolean isJDBCExecuteCompliant() { - return true; - } - /** * Return true is the given exception occurred as a result of a lock * time out exception (WAIT clause). If sub-platform supports this clause, diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/PostgreSQLPlatform.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/PostgreSQLPlatform.java index fb32ff4370e..1e1818646f8 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/PostgreSQLPlatform.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/PostgreSQLPlatform.java @@ -26,8 +26,6 @@ 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.DatasourceCall.ParameterType; import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition; import org.eclipse.persistence.internal.expressions.ExpressionJavaPrinter; import org.eclipse.persistence.internal.expressions.ExpressionSQLPrinter; @@ -36,17 +34,14 @@ import org.eclipse.persistence.internal.helper.ClassConstants; import org.eclipse.persistence.internal.helper.DatabaseField; import org.eclipse.persistence.internal.helper.DatabaseTable; -import org.eclipse.persistence.internal.sessions.AbstractRecord; import org.eclipse.persistence.internal.sessions.AbstractSession; import org.eclipse.persistence.mappings.structures.ObjectRelationalDatabaseField; import org.eclipse.persistence.queries.SQLCall; -import org.eclipse.persistence.queries.StoredProcedureCall; import org.eclipse.persistence.queries.ValueReadQuery; import org.eclipse.persistence.tools.schemaframework.FieldDefinition; import java.io.CharArrayWriter; import java.io.IOException; -import java.io.StringWriter; import java.io.Writer; import java.sql.CallableStatement; import java.sql.PreparedStatement; @@ -303,20 +298,6 @@ public boolean shouldPrintOutputTokenAtStart() { return true; } - /** - * Calling a stored procedure query on PostgreSQL with no output parameters - * always returns true from an execute call regardless if a result set is - * returned or not. This flag will help avoid throwing a JPA mandated - * exception on an executeUpdate call (which calls jdbc execute and checks - * the return value to ensure no results sets are returned (true)) - * - * @see PostgreSQLPlatform - */ - @Override - public boolean isJDBCExecuteCompliant() { - return false; - } - /** * INTERNAL: Answers whether platform is Postgres. */ @@ -520,7 +501,7 @@ public int getMaxFieldNameSize() { */ @Override public String getProcedureBeginString() { - return "AS $$ BEGIN "; + return "$$ BEGIN "; } /** @@ -528,55 +509,17 @@ public String getProcedureBeginString() { */ @Override public String getProcedureEndString() { - return "; END ; $$ LANGUAGE plpgsql;"; - } - - /** - * 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 tailWriter = new StringWriter(); - StringWriter writer = new StringWriter(); - boolean outParameterFound = false; - - tailWriter.write(call.getProcedureName()); - tailWriter.write("("); - - int indexFirst = call.getFirstParameterIndexForCallString(); - int size = call.getParameters().size(); - String nextBindString = "?"; - - 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)) { - if (!DatasourceCall.isOutputParameterType(parameterType)) { - tailWriter.write(nextBindString); - nextBindString = ", ?"; - } else { - if (outParameterFound) { - //multiple outs found - throw ValidationException.multipleOutParamsNotSupported(getClass().getSimpleName(), call.getProcedureName()); - } - outParameterFound = true; //PostGreSQL uses a very different header to execute when there are out params - } - } - } - tailWriter.write(")"); - - if (outParameterFound) { - writer.write("{?= CALL "); - tailWriter.write("}"); - } else { - writer.write("SELECT * FROM "); - } - writer.write(tailWriter.toString()); + return "END; $$ LANGUAGE plpgsql;"; + } - return writer.toString(); + /** + * INTERNAL: Used for sp calls. + */ + @Override + public String getProcedureCallHeader() { + return "CALL "; } + /** * INTERNAL Used for stored function calls. */ diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/storedproc/TestStoredProcedures.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/storedproc/TestStoredProcedures.java index 2afa45992cd..8cfb15995cc 100644 --- a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/storedproc/TestStoredProcedures.java +++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/storedproc/TestStoredProcedures.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024 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 @@ -30,6 +30,7 @@ import org.eclipse.persistence.jpa.test.framework.Property; import org.eclipse.persistence.platform.database.DatabasePlatform; import org.eclipse.persistence.sessions.DatabaseSession; +import org.eclipse.persistence.tools.schemaframework.FieldDefinition; import org.eclipse.persistence.tools.schemaframework.SchemaManager; import org.eclipse.persistence.tools.schemaframework.StoredProcedureDefinition; @@ -121,6 +122,8 @@ public void testStoredProcedure_SetUnordered_IndexParameters() { */ @Test public void testStoredProcedure_SetOrdered_NamedParameters() { + Assume.assumeFalse("pgjdbc does not support named parameters", getPlatform(storedProcedureEmf).isPostgreSQL()); + EntityManager em = storedProcedureEmf.createEntityManager(); try { StoredProcedureQuery storedProcedure = em.createStoredProcedureQuery("simple_order_procedure"); @@ -152,6 +155,8 @@ public void testStoredProcedure_SetOrdered_NamedParameters() { */ @Test public void testStoredProcedure_SetUnordered_NamedParameters() { + Assume.assumeFalse("pgjdbc does not support named parameters", getPlatform(storedProcedureEmf).isPostgreSQL()); + EntityManager em = storedProcedureEmf.createEntityManager(); try { StoredProcedureQuery storedProcedure = em.createStoredProcedureQuery("simple_order_procedure"); @@ -186,22 +191,27 @@ private static boolean createSimpleStoredProcedure(EntityManagerFactory emf) { //Setup a stored procedure EntityManager em = emf.createEntityManager(); try { + DatabaseSession dbs = ((EntityManagerImpl)em).getDatabaseSession(); + SchemaManager manager = new SchemaManager(dbs); + Platform platform = dbs.getDatasourcePlatform(); + StoredProcedureDefinition proc = new StoredProcedureDefinition(); proc.setName("simple_order_procedure"); proc.addArgument("in_param_one", String.class, 10); proc.addArgument("in_param_two", String.class, 10); proc.addArgument("in_param_three", String.class, 10); - proc.addOutputArgument("out_param_one", String.class, 30); - - DatabaseSession dbs = ((EntityManagerImpl)em).getDatabaseSession(); - SchemaManager manager = new SchemaManager(dbs); - Platform platform = dbs.getDatasourcePlatform(); + if (platform.isPostgreSQL()) { + // PG only supports OUT in 14+ + proc.addInOutputArgument(new FieldDefinition("out_param_one", String.class, 30)); + } else { + proc.addOutputArgument("out_param_one", String.class, 30); + } //Add more platform specific diction to support more platforms if(platform.isMySQL()) { proc.addStatement("SET out_param_one = CONCAT('One: ',in_param_one,' Two: ',in_param_two,' Three: ',in_param_three)"); - } else if(platform.isOracle()) { + } else if(platform.isOracle() || platform.isPostgreSQL()) { proc.addStatement("out_param_one := 'One: ' || in_param_one || ' Two: ' || in_param_two || ' Three: ' || in_param_three"); } else if (platform.isDB2() || platform.isDB2Z()) { proc.addStatement("SET out_param_one = 'One: ' || in_param_one || ' Two: ' || in_param_two || ' Three: ' || in_param_three"); diff --git a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/StoredProcedureQueryImpl.java b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/StoredProcedureQueryImpl.java index ffc91cee172..1fe79759906 100644 --- a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/StoredProcedureQueryImpl.java +++ b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/StoredProcedureQueryImpl.java @@ -380,11 +380,7 @@ public int executeUpdate() { // If the return value is true indicating a result set then throw an exception. if (execute()) { - if (getActiveSession().getPlatform().isJDBCExecuteCompliant()) { - throw new IllegalStateException(ExceptionLocalization.buildMessage("incorrect_spq_query_for_execute_update")); - } else { - return getUpdateCount(); - } + throw new IllegalStateException(ExceptionLocalization.buildMessage("incorrect_spq_query_for_execute_update")); } else { return getUpdateCount(); }