/******************************************************************************* * Copyright (c) 1998, 2015 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 v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * 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 { public static final String TARGET_SHORT_PREFIX = "T_"; public static final String TARGET_SUFFIX = "TARGET"; public static final String COMPAT_SHORT_PREFIX = "C_"; public static final String COMPAT_SUFFIX = "COMPAT"; public static final int ARGNAME_SIZE_LIMIT = 30 - TARGET_SUFFIX.length(); public boolean isComplexDatabaseType(); public boolean isJDBCType(); public int getSqlCode(); public int getConversionCode(); public String getTypeName(); public int computeInIndex(PLSQLargument inArg, int newIndex, ListIterator<PLSQLargument> i); public int computeOutIndex(PLSQLargument outArg, int newIndex, ListIterator<PLSQLargument> i); public void buildInDeclare(StringBuilder sb, PLSQLargument inArg); public void buildOutDeclare(StringBuilder sb, PLSQLargument outArg); public void buildBeginBlock(StringBuilder sb, PLSQLargument arg, PLSQLStoredProcedureCall call); public void buildOutAssignment(StringBuilder sb, PLSQLargument outArg, PLSQLStoredProcedureCall call); public void translate(PLSQLargument arg, AbstractRecord translationRow, AbstractRecord copyOfTranslationRow, List<DatabaseField> copyOfTranslationFields, List<DatabaseField> translationRowFields, List translationRowValues, StoredProcedureCall call); public void buildOutputRow(PLSQLargument outArg, AbstractRecord outputRow, DatabaseRecord newOutputRow, List<DatabaseField> outputRowFields, List outputRowValues); public void logParameter(StringBuilder sb, Integer direction, PLSQLargument arg, AbstractRecord translationRow, DatabasePlatform platform); public 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(); } protected 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 copyOfTranslationFields, List translationRowFields, List translationRowValues, StoredProcedureCall call) { DatabaseField field = null; for (Iterator i = copyOfTranslationFields.iterator(); i.hasNext(); ) { DatabaseField f = (DatabaseField)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 i = outputRowFields.iterator(); i.hasNext(); ) { DatabaseField f = (DatabaseField)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); } } } }