Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix calling stored procs on Postgres, and enable some tests #2308

Merged
merged 2 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -1443,6 +1443,7 @@ public boolean isInformixOuterJoin() {
*
* @see PostgreSQLPlatform
*/
@Deprecated(forRemoval = true)
public boolean isJDBCExecuteCompliant() {
rdicroce marked this conversation as resolved.
Show resolved Hide resolved
return true;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (c) 1998, 2023 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2023 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
Expand Down Expand Up @@ -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.
*/
Expand Down Expand Up @@ -520,63 +506,25 @@ public int getMaxFieldNameSize() {
*/
@Override
public String getProcedureBeginString() {
return "AS $$ BEGIN ";
return "$$ BEGIN ";
}

/**
* INTERNAL: Used for sp calls.
*/
@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.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It throws

[ERROR]   TestStoredProcedures.setup:54->createSimpleStoredProcedure:222 » Database 
Internal Exception: org.postgresql.util.PSQLException: ERROR: procedures cannot have OUT arguments
  Hint: INOUT arguments are permitted.
Error Code: 0
Call: CREATE PROCEDURE simple_order_procedure (
	in_param_one VARCHAR(10),
	in_param_two VARCHAR(10),
	in_param_three VARCHAR(10),
	 OUT out_param_one VARCHAR(30)) AS
.....

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently PG only supports OUT params since version 14. I have 17 installed, so it worked for me. I've modified the test to use INOUT instead for PG. It works on 17, and hopefully it works on whatever version you're testing with.

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");
Expand Down
Loading