Skip to content

Commit

Permalink
SQL Struct with @lob (BLOB, CLOB) used as stored procedure parameter …
Browse files Browse the repository at this point in the history
…type - fix (#1302)

SQL Struct with @lob (BLOB, CLOB) used as stored procedure parameter type - fix

Bugfix and unit test.


Signed-off-by: Radek Felcman <radek.felcman@oracle.com>
  • Loading branch information
rfelcman committed Sep 21, 2021
1 parent 705e961 commit ebabd20
Show file tree
Hide file tree
Showing 9 changed files with 355 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3387,6 +3387,16 @@ public Struct createStruct(String structTypeName, Object[] attributes, AbstractS
return createStruct(structTypeName,attributes,unwrappedConnection);
}

/**
* INTERNAL:
* This method builds a Struct using the unwrapped connection within the session
* @return Struct
*/
public Struct createStruct(String structTypeName, Object[] attributes, AbstractRecord row, Vector orderedFields, AbstractSession session, Connection connection) throws SQLException {
java.sql.Connection unwrappedConnection = getConnection(session, connection);
return createStruct(structTypeName,attributes,unwrappedConnection);
}

/**
* INTERNAL:
* Platforms that support java.sql.Array may override this method.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2020 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. 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 @@ -189,17 +189,17 @@ public void buildOutDeclare(StringBuilder sb, PLSQLargument outArg) {// Validate

@Override
public void buildBeginBlock(StringBuilder sb, PLSQLargument arg, PLSQLStoredProcedureCall call) {
String sql2PlName = call.getSQL2PlName(this);
if (sql2PlName == null) {
String conversionRoutine = ((this.getTypeName().equals(this.getCompatibleType()))) ? call.getPl2SQLName(this) : call.getSQL2PlName(this);
if (conversionRoutine == null) {
// TODO exception
throw new NullPointerException("no SQL2Pl conversion routine for " + typeName);
throw new NullPointerException("no SQL2Pl or Pl2SQL conversion routine for " + typeName);
}
String target = databaseTypeHelper.buildTarget(arg);
String compat = databaseTypeHelper.buildCompatible(arg);
sb.append(" ");
sb.append(target);
sb.append(" := ");
sb.append(sql2PlName);
sb.append(conversionRoutine);
sb.append("(");
sb.append(compat);
sb.append(");");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2019 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. 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 @@ -339,7 +339,7 @@ public Struct buildStructureFromRow(AbstractRecord row, AbstractSession session,
fields[index] = row.get(field);
}

structure = session.getPlatform().createStruct(getStructureName(), fields, session, connection);
structure = session.getPlatform().createStruct(getStructureName(), fields, row, getOrderedFields(), session, connection);
} catch (java.sql.SQLException exception) {
throw DatabaseException.sqlException(exception, session, false);
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -670,20 +670,26 @@ protected void addNestedFunctionsForArgument(List functions, PLSQLargument argum
if (info == null) {
info = generateNestedFunction(type, isNestedTable);
}
if (argument.direction == IN) {
if (!functions.contains(info.sql2PlConv)) {
functions.add(info.sql2PlConv);
}
} else if (argument.direction == INOUT) {
if (!functions.contains(info.sql2PlConv)) {
functions.add(info.sql2PlConv);
}
if (type.getTypeName().equals(type.getCompatibleType())) {
if (!functions.contains(info.pl2SqlConv)) {
functions.add(info.pl2SqlConv);
}
} else if (argument.direction == OUT) {
if (!functions.contains(info.pl2SqlConv)) {
functions.add(info.pl2SqlConv);
} else {
if (argument.direction == IN) {
if (!functions.contains(info.sql2PlConv)) {
functions.add(info.sql2PlConv);
}
} else if (argument.direction == INOUT) {
if (!functions.contains(info.sql2PlConv)) {
functions.add(info.sql2PlConv);
}
if (!functions.contains(info.pl2SqlConv)) {
functions.add(info.pl2SqlConv);
}
} else if (argument.direction == OUT) {
if (!functions.contains(info.pl2SqlConv)) {
functions.add(info.pl2SqlConv);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2020 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021 Oracle and/or its affiliates. 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 All @@ -18,12 +18,22 @@

import java.io.IOException;
import java.io.Writer;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Struct;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;

import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.ConversionException;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.internal.sessions.EmptyRecord;
import org.eclipse.persistence.logging.SessionLog;
Expand Down Expand Up @@ -151,4 +161,29 @@ public void printFieldIdentityClause(final Writer writer) throws ValidationExcep
}
}

/**
* INTERNAL:
* This method builds a Struct using the unwrapped connection within the session
* @return Struct
*/
@Override
public Struct createStruct(String structTypeName, Object[] attributes, AbstractRecord row, Vector orderedFields, AbstractSession session, Connection connection) throws SQLException {
for (int index = 0; index < orderedFields.size(); index++) {
DatabaseField field = (DatabaseField)orderedFields.elementAt(index);
if (row.getField(field) != null && row.getField(field).getTypeName() != null) {
if (ClassConstants.BLOB.getTypeName().equals(row.getField(field).getTypeName())) {
Blob blob = connection.createBlob();
blob.setBytes(1L, (byte[]) row.get(field));
attributes[index] = blob;
} else if (ClassConstants.CLOB.getTypeName().equals(row.getField(field).getTypeName())) {
Clob clob = connection.createClob();
clob.setString(1L, (String) attributes[index]);
attributes[index] = clob;
}
} else {
attributes[index] = row.get(field);
}
}
return super.createStruct(structTypeName, attributes, session, connection);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright (c) 2021 Oracle and/or its affiliates. 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:
// Oracle - initial implementation
package org.eclipse.persistence.testing.models.jpa.plsql;

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Lob;
import org.eclipse.persistence.annotations.Struct;

import java.util.Arrays;

@Embeddable
@Struct(name="PLSQL_P_PLSQL_INNER_BLOB_REC", fields={"BLOB_ID", "BLOB_CONTENT", "CLOB_CONTENT"})
public class InnerObjBlob {

public InnerObjBlob() {
}

public InnerObjBlob(int blobId, byte[] blobContent, String clobContent) {
this.blobId = blobId;
this.blobContent = blobContent;
this.clobContent = clobContent;
}

@Column(name="BLOB_ID")
private int blobId;

@Column(name="BLOB_CONTENT")
@Lob
private byte[] blobContent;

@Column(name="CLOB_CONTENT")
@Lob
private String clobContent;

public int getBlobId() {
return blobId;
}

public void setBlobId(int blobId) {
this.blobId = blobId;
}

public byte[] getBlobContent() {
return blobContent;
}

public void setBlobContent(byte[] blobContent) {
this.blobContent = blobContent;
}

public String getClobContent() {
return clobContent;
}

public void setClobContent(String clobContent) {
this.clobContent = clobContent;
}

@Override
public String toString() {
return "InnerObjBlob{" +
"blobId=" + blobId +
", blobContent=" + Arrays.toString(blobContent) +
", clobContent='" + clobContent + '\'' +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright (c) 2021 Oracle and/or its affiliates. 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:
// Oracle - initial implementation
package org.eclipse.persistence.testing.models.jpa.plsql;

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import org.eclipse.persistence.annotations.Struct;

@Embeddable
@Struct(name="PLSQL_P_PLSQL_OUTER_STRUCT_REC", fields={"STRUCT_ID", "STRUCT_CONTENT"})
public class OuterObjBlob {

public OuterObjBlob() {
}

public OuterObjBlob(int structId, InnerObjBlob structContent) {
this.structId = structId;
this.structContent = structContent;
}

@Column(name="STRUCT_ID")
private int structId;

@Column(name="STRUCT_CONTENT")
private InnerObjBlob structContent;

public int getStructId() {
return structId;
}

public void setStructId(int structId) {
this.structId = structId;
}

public InnerObjBlob getStructContent() {
return structContent;
}

public void setStructContent(InnerObjBlob structContent) {
this.structContent = structContent;
}

@Override
public String toString() {
return "OuterObjBBlob{" +
"structId=" + structId +
", structContent=" + structContent +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2021 Oracle and/or its affiliates. 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:
// Oracle - initial implementation
package org.eclipse.persistence.testing.models.jpa.plsql;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import org.eclipse.persistence.annotations.Direction;
import org.eclipse.persistence.platform.database.oracle.annotations.*;

@Entity
@NamedPLSQLStoredFunctionQueries(value = {
@NamedPLSQLStoredFunctionQuery(name = "PLSQL_BLOB_REC_IN", functionName = "PLSQL_P.PLSQL_BLOB_REC_IN",
parameters = {@PLSQLParameter(name = "P_BLOB_REC", direction = Direction.IN, databaseType = "PLSQL_P_PLSQL_INNER_BLOB_REC"),
@PLSQLParameter(name = "P_CITY", direction = Direction.IN, databaseType = "VARCHAR_TYPE")},
returnParameter = @PLSQLParameter(name = "RESULT", direction = Direction.OUT, databaseType = "VARCHAR_TYPE")),
@NamedPLSQLStoredFunctionQuery(name = "PLSQL_STRUCT_INSTRUCT_REC_IN", functionName = "PLSQL_P.PLSQL_STRUCT_INSTRUCT_REC_IN",
parameters = {@PLSQLParameter(name = "P_BLOB_REC", databaseType = "PLSQL_P_PLSQL_INNER_BLOB_REC"),
@PLSQLParameter(name = "P_STRUCT_REC", databaseType = "PLSQL_P_PLSQL_OUTER_STRUCT_REC"),
@PLSQLParameter(name = "P_CITY", databaseType = "VARCHAR_TYPE")},
returnParameter = @PLSQLParameter(name = "RESULT", direction = Direction.OUT, databaseType = "VARCHAR_TYPE"))
}
)
@PLSQLRecords(value = {
@PLSQLRecord(name = "PLSQL_P_PLSQL_INNER_BLOB_REC", compatibleType = "PLSQL_P_PLSQL_INNER_BLOB_REC", javaType = InnerObjBlob.class,
fields = {
@PLSQLParameter(name = "BLOB_ID", databaseType = "NUMERIC_TYPE"),
@PLSQLParameter(name = "BLOB_CONTENT", databaseType = "BLOB_TYPE"),
@PLSQLParameter(name = "CLOB_CONTENT", databaseType = "CLOB_TYPE")
}
),
@PLSQLRecord(name = "PLSQL_P_PLSQL_OUTER_STRUCT_REC", compatibleType = "PLSQL_P_PLSQL_OUTER_STRUCT_REC", javaType = OuterObjBlob.class,
fields = {
@PLSQLParameter(name = "STRUCT_ID", databaseType = "NUMERIC_TYPE"),
@PLSQLParameter(name = "STRUCT_CONTENT", databaseType = "PLSQL_P_PLSQL_INNER_BLOB_REC")
}

)
}
)
public class SaveComplexBlob {

@Id
@Column(name = "RESULT")
private String result;

public String getResult() {
return result;
}

public void setResult(String result) {
this.result = result;
}
}

0 comments on commit ebabd20

Please sign in to comment.