From 452ffdd42ee5e64ce901947c1b2a05c88341b9fa Mon Sep 17 00:00:00 2001 From: Rich DiCroce Date: Fri, 20 Dec 2024 03:18:09 -0500 Subject: [PATCH] Fix calling stored procs on Postgres, and enable some tests (#2308) --- .../databaseaccess/DatabasePlatform.java | 5 +- .../platform/database/PostgreSQLPlatform.java | 72 +++---------------- .../test/storedproc/TestStoredProcedures.java | 24 +++++-- 3 files changed, 30 insertions(+), 71 deletions(-) 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 b7b0172f7a8..6b0fac3f574 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 @@ -1,6 +1,6 @@ /* - * Copyright (c) 1998, 2022 Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2019, 2022 IBM Corporation. All rights reserved. + * Copyright (c) 1998, 2024 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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 @@ -1443,6 +1443,7 @@ public boolean isInformixOuterJoin() { * * @see PostgreSQLPlatform */ + @Deprecated(forRemoval = true) public boolean isJDBCExecuteCompliant() { return true; } 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 fa5f8074aff..ab00390fe26 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 @@ -305,20 +305,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. */ @@ -522,7 +508,7 @@ public int getMaxFieldNameSize() { */ @Override public String getProcedureBeginString() { - return "AS $$ BEGIN "; + return "$$ BEGIN "; } /** @@ -530,55 +516,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(Helper.getShortClassName(this), 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");