blob: 80be3fbc790c44230fc02d2e9a30eadbf376fb67 [file] [log] [blame]
/*
* 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
* 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 API and implementation from Oracle TopLink
package org.eclipse.persistence.internal.helper;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import static java.lang.Integer.MIN_VALUE;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.platform.database.DatabasePlatform;
import org.eclipse.persistence.platform.database.oracle.plsql.PLSQLStoredProcedureCall;
import org.eclipse.persistence.platform.database.oracle.plsql.PLSQLargument;
import org.eclipse.persistence.queries.StoredProcedureCall;
import org.eclipse.persistence.sessions.DatabaseRecord;
import static org.eclipse.persistence.internal.databaseaccess.DatasourceCall.IN;
import static org.eclipse.persistence.internal.databaseaccess.DatasourceCall.OUT;
import static org.eclipse.persistence.internal.helper.Helper.NL;
import static org.eclipse.persistence.internal.helper.Helper.buildHexStringFromBytes;
/**
* <b>PUBLIC</b>: Interface used to categorize arguments to Stored Procedures as either
* 'simple' (use subclass SimpleDatabaseType) or 'complex' (use subclass ComplexDatabaseType)
*
* @author Mike Norman - michael.norman@oracle.com
* @since Oracle TopLink 11.x.x
*/
@SuppressWarnings("unchecked")
public interface DatabaseType {
String TARGET_SHORT_PREFIX = "T_";
String TARGET_SUFFIX = "TARGET";
String COMPAT_SHORT_PREFIX = "C_";
String COMPAT_SUFFIX = "COMPAT";
int ARGNAME_SIZE_LIMIT = 30 - TARGET_SUFFIX.length();
boolean isComplexDatabaseType();
boolean isJDBCType();
int getSqlCode();
int getConversionCode();
String getTypeName();
int computeInIndex(PLSQLargument inArg, int newIndex,
ListIterator<PLSQLargument> i);
int computeOutIndex(PLSQLargument outArg, int newIndex,
ListIterator<PLSQLargument> i);
void buildInDeclare(StringBuilder sb, PLSQLargument inArg);
void buildOutDeclare(StringBuilder sb, PLSQLargument outArg);
void buildBeginBlock(StringBuilder sb, PLSQLargument arg, PLSQLStoredProcedureCall call);
void buildOutAssignment(StringBuilder sb, PLSQLargument outArg, PLSQLStoredProcedureCall call);
void translate(PLSQLargument arg, AbstractRecord translationRow,
AbstractRecord copyOfTranslationRow, List<DatabaseField> copyOfTranslationFields,
List<DatabaseField> translationRowFields, List translationRowValues,
StoredProcedureCall call);
void buildOutputRow(PLSQLargument outArg, AbstractRecord outputRow,
DatabaseRecord newOutputRow, List<DatabaseField> outputRowFields, List outputRowValues);
void logParameter(StringBuilder sb, Integer direction, PLSQLargument arg,
AbstractRecord translationRow, DatabasePlatform platform);
enum DatabaseTypeHelper {
databaseTypeHelper;
static String getTruncatedSHA1Hash(String s) {
StringBuilder sb = new StringBuilder(28);
try {
byte[] longIdentifierBytes = s.getBytes();
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(longIdentifierBytes, 0, longIdentifierBytes.length);
byte[] digest = md.digest(); //produces a 160-bit hash
//truncate to 112 bits, which is about the same java.util.UUID;
//HMAC-SHA1-96 is only 96 bits and that's good enough for IPSEC work
//TL;DR - probability of collision quite small
byte[] truncDigest = new byte[14];
System.arraycopy(digest, 0, truncDigest, 0, 14);
sb.append(buildHexStringFromBytes(truncDigest));
}
catch (NoSuchAlgorithmException e) {
//ignore: should never happen
}
return sb.toString();
}
private String getTruncatedSHA1Name(String argName, String prefix) {
if (argName.length() >= ARGNAME_SIZE_LIMIT) {
StringBuilder sb = new StringBuilder();
//the truncated SHA is great, but a PL/SQL identifier
//can't start with a number, so use prefix
sb.append(prefix);
sb.append(getTruncatedSHA1Hash(argName));
return sb.toString();
}
return argName;
}
public String buildTarget(PLSQLargument arg) {
StringBuilder sb = new StringBuilder();
if (arg.name.length() >= ARGNAME_SIZE_LIMIT) {
sb.append(getTruncatedSHA1Name(arg.name, TARGET_SHORT_PREFIX));
}
else {
sb.append(arg.name);
sb.append(TARGET_SUFFIX);
}
return sb.toString();
}
public String buildCompatible(PLSQLargument arg) {
StringBuilder sb = new StringBuilder();
if (arg.name.length() >= ARGNAME_SIZE_LIMIT) {
sb.append(getTruncatedSHA1Name(arg.name, COMPAT_SHORT_PREFIX));
}
else {
sb.append(arg.name);
sb.append(COMPAT_SUFFIX);
}
return sb.toString();
}
public void declareTarget(StringBuilder sb, PLSQLargument arg,
DatabaseType databaseType) {
sb.append(" ");
sb.append(buildTarget(arg));
sb.append(" ");
sb.append(databaseType.getTypeName());
}
public int computeInIndex(PLSQLargument inArg, int newIndex) {
inArg.inIndex = newIndex;
return ++newIndex;
}
public int computeOutIndex(PLSQLargument outArg, int newIndex) {
outArg.outIndex = newIndex;
return ++newIndex;
}
public void buildOutAssignment(StringBuilder sb, PLSQLargument outArg, PLSQLStoredProcedureCall call) {
sb.append(" :");
sb.append(outArg.outIndex);
sb.append(" := ");
sb.append(buildTarget(outArg));
sb.append(";");
sb.append(NL);
}
public void translate(PLSQLargument arg, AbstractRecord translationRow,
AbstractRecord copyOfTranslationRow, List<DatabaseField> copyOfTranslationFields,
List<DatabaseField> translationRowFields, List translationRowValues,
StoredProcedureCall call) {
DatabaseField field = null;
for (Iterator<DatabaseField> i = copyOfTranslationFields.iterator(); i.hasNext(); ) {
DatabaseField f = i.next();
if (f.getName().equals(arg.name)) {
field = f;
break;
}
}
if (arg.length != MIN_VALUE) {
field.setLength(arg.length);
}
if (arg.precision != MIN_VALUE) {
field.setPrecision(arg.precision);
}
if (arg.scale != MIN_VALUE) {
field.setScale(arg.scale);
}
translationRowFields.set(arg.inIndex - 1, field);
Object value = copyOfTranslationRow.get(field);
translationRowValues.set(arg.inIndex - 1, value);
}
public void buildOutputRow(PLSQLargument outArg, AbstractRecord outputRow,
DatabaseRecord newOutputRow, List<DatabaseField> outputRowFields, List outputRowValues) {
DatabaseField field = null;
for (Iterator<DatabaseField> i = outputRowFields.iterator(); i.hasNext(); ) {
DatabaseField f = i.next();
if (f.getName().equals(outArg.name)) {
field = f;
break;
}
}
Object value = outputRow.get(field);
newOutputRow.add(field, value);
}
public void logParameter(StringBuilder sb, Integer direction, PLSQLargument arg,
AbstractRecord translationRow, DatabasePlatform platform) {
if (direction == IN && arg.inIndex != MIN_VALUE) {
sb.append(":");
sb.append(arg.inIndex);
sb.append(" => ");
sb.append(platform.convertToDatabaseType(translationRow.get(arg.name)));
}
if (direction == OUT && arg.outIndex != MIN_VALUE) {
sb.append(arg.name);
sb.append(" => :");
sb.append(arg.outIndex);
}
}
}
}