org.firebirdsql.jdbc.FBProcedureCall Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jaybird Show documentation
Show all versions of jaybird Show documentation
JDBC Driver for the Firebird RDBMS
/*
* Firebird Open Source JDBC Driver
*
* Distributable under LGPL license.
* You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* LGPL License for more details.
*
* This file was created by members of the firebird development team.
* All individual contributions remain the Copyright (C) of those
* individuals. Contributors to this file are either listed here or
* can be obtained from a source control history command.
*
* All rights reserved.
*/
package org.firebirdsql.jdbc;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Vector;
/**
* Represents procedure call.
*/
public class FBProcedureCall implements Cloneable {
// TODO Replace with a copy constructor
public Object clone() {
try {
FBProcedureCall newProcedureCall = (FBProcedureCall) super.clone();
//Copy each input and output parameter.
newProcedureCall.inputParams = cloneParameters(inputParams);
newProcedureCall.outputParams = cloneParameters(outputParams);
return newProcedureCall;
} catch (CloneNotSupportedException e) {
return null;
}
}
private static Vector cloneParameters(final Vector parameters) {
final Vector clonedParameters = new Vector<>(parameters.size());
for (FBProcedureParam param : parameters) {
clonedParameters.add(param != null ? (FBProcedureParam) param.clone() : null);
}
return clonedParameters;
}
/**
* true
if the old callable statement compatibility mode should
* be used, otherwise - false
. Current value - true
.
*/
public static final boolean OLD_CALLABLE_STATEMENT_COMPATIBILITY = true;
private String name;
// TODO Replace Vector with a List
private Vector inputParams = new Vector<>();
private Vector outputParams = new Vector<>();
/**
* Get the name of the procedure to be called.
*
* @return The procedure name
*/
public String getName() {
return name;
}
/**
* Set the name of the procedure to be called.
*
* @param name The name of the procedure
*/
public void setName(String name) {
this.name = name;
}
/**
* Get input parameter by the specified index.
*
* @param index index for which parameter has to be returned, first index is 1
*
* @return instance of {@link FBProcedureParam}.
*/
public FBProcedureParam getInputParam(int index) {
FBProcedureParam result = getParam(inputParams, index);
if (result == null || result == NullParam.NULL_PARAM) {
result = getParam(outputParams, index);
// ensure that vector has right size
// note, index starts with 1
if (inputParams.size() < index) {
inputParams.setSize(index);
}
inputParams.set(index - 1, result);
}
return result;
}
/**
* Get the output parameter at the specified index.
*
* @param index The index of the parameter, first index is 1
* @return The parameter at the given index
*/
public FBProcedureParam getOutputParam(int index) {
return getParam(outputParams, index);
}
/**
* Get parameter with the specified index from the specified collection.
*
* @param params collection containing parameters.
* @param index index for which parameter has to be found.
*
* @return instance of {@link FBProcedureParam}.
*/
private FBProcedureParam getParam(Collection params, int index) {
for (FBProcedureParam param : params) {
if (param != null && param.getIndex() == index) {
return param;
}
}
return NullParam.NULL_PARAM;
}
/**
* Map output parameter index to a column number of corresponding result
* set.
*
* @param index index to map.
*
* @return mapped column number or index
if no output parameter
* with the specified index found (assuming that {@link #OLD_CALLABLE_STATEMENT_COMPATIBILITY}
* constant is set to true
, otherwise throws exception).
*
* @throws SQLException if compatibility mode is switched off and no
* parameter was found (see {@link #OLD_CALLABLE_STATEMENT_COMPATIBILITY}
* constant).
*/
public int mapOutParamIndexToPosition(int index) throws SQLException {
return mapOutParamIndexToPosition(index, OLD_CALLABLE_STATEMENT_COMPATIBILITY);
}
/**
* Map output parameter index to a column number of corresponding result
* set.
*
* @param index index to map.
* @param compatibilityMode true
if we should run in old compatibility mode.
*
* @return mapped column number or index
if no output parameter
* with the specified index found and compatibilityMode
is set.
*
* @throws SQLException if compatibility mode is switched off and no
* parameter was found.
*/
public int mapOutParamIndexToPosition(int index, boolean compatibilityMode) throws SQLException {
int position = -1;
for (FBProcedureParam param : outputParams) {
if (param != null && param.isParam()) {
position++;
if (param.getIndex() == index) {
return position + 1;
}
}
}
// hack: if we did not find the right parameter we return
// an index that was asked if we run in compatibility mode
//
// we should switch it off as soon as people convert applications
if (compatibilityMode) {
return index;
} else {
throw new FBSQLException("Specified parameter does not exist.", SQLStateConstants.SQL_STATE_INVALID_COLUMN);
}
}
/**
* Get the list of input parameters for this procedure call.
*
* @return A list of all input parameters
*/
public List getInputParams() {
return inputParams;
}
/**
* Get a list of output parameters for this procedure call.
*
* @return A list of all output parameters
*/
public List getOutputParams() {
return outputParams;
}
/**
* Add an input parameter to this procedure call.
*
* @param param The parameter to be added
*/
public void addInputParam(FBProcedureParam param) {
if (inputParams.size() < param.getPosition() + 1) {
inputParams.setSize(param.getPosition() + 1);
}
inputParams.set(param.getPosition(), param);
}
/**
* Add an output parameter to this procedure call.
*
* @param param
* The parameter to be added
*/
public void addOutputParam(FBProcedureParam param) {
if (outputParams.size() < param.getPosition() + 1) {
outputParams.setSize(param.getPosition() + 1);
}
outputParams.set(param.getPosition(), param);
}
/**
* Add call parameter. This method adds new parameter to the procedure call
* and tries to automatically place the parameter into the right collection
* if it contains a hint whether it is input or output parameter.
*
* @param position position of the parameter in the procedure call.
* @param param contents of the parameter.
*
* @return instance of the {@link FBProcedureParam} that was created to
* represent this parameter.
*/
public FBProcedureParam addParam(int position, String param) {
param = param.trim();
boolean isInputParam = true;
if (param.length() > 4) {
String possibleOutIndicator = param.substring(0, 3);
if ("OUT".equalsIgnoreCase(possibleOutIndicator) && Character.isSpaceChar(param.charAt(3))) {
isInputParam = false;
param = param.substring(4).trim();
}
}
if (isInputParam && param.length() > 4) {
String possibleInIndicator = param.substring(0, 2);
if ("IN".equalsIgnoreCase(possibleInIndicator) && Character.isSpaceChar(param.charAt(2))) {
param = param.substring(3).trim();
}
}
FBProcedureParam callParam = new FBProcedureParam(position, param);
if (isInputParam) {
addInputParam(callParam);
} else {
addOutputParam(callParam);
}
return callParam;
}
/**
* Register output parameter. This method marks parameter with the specified
* index as output. Parameters marked as output cannot be used as input
* parameters.
*
* @param index index of the parameter to mark as output.
* @param type SQL type of the parameter.
*
* @throws SQLException if something went wrong.
*/
public void registerOutParam(int index, int type) throws SQLException {
FBProcedureParam param = getInputParam(index);
if (param == null || param == NullParam.NULL_PARAM) {
param = getOutputParam(index);
} else {
addOutputParam(param);
if (!param.isValueSet()) {
inputParams.set(param.getPosition(), null);
}
}
if (param == null || param == NullParam.NULL_PARAM) {
throw new SQLException("Cannot find parameter with the specified position.",
SQLStateConstants.SQL_STATE_INVALID_COLUMN);
}
param.setType(type);
}
/**
* Get native SQL for the specified procedure call.
*
* @return native SQL that can be executed by the database server.
*/
public String getSQL(boolean select) throws SQLException {
StringBuilder sb = new StringBuilder(select
? FBCallableStatement.NATIVE_SELECT_COMMAND
: FBCallableStatement.NATIVE_CALL_COMMAND);
sb.append(name);
boolean firstParam = true;
sb.append('(');
for (FBProcedureParam param : inputParams) {
if (param == null) {
continue;
}
if (!firstParam) {
sb.append(',');
} else {
firstParam = false;
}
sb.append(param.getParamValue());
}
if (firstParam) {
sb.setLength(sb.length() - 1);
} else {
sb.append(')');
}
return sb.toString();
}
/**
* Checks if all parameters have been set.
*
* @throws SQLException When some parameters don't have values, and are not registered as an out parameter.
*/
public void checkParameters() throws SQLException {
for (FBProcedureParam param : inputParams) {
if (param == null) {
continue;
}
// if parameter does not have set value, and is not registered
// as output parameter, throw an exception, otherwise, continue
// to the next one.
if (!param.isValueSet()) {
if (param.isParam()
&& outputParams.size() > 0
&& outputParams.get(param.getPosition()) == null)
throw new FBSQLException("Value of parameter " + param.getIndex() + " not set and "
+ "it was not registered as output parameter.",
SQLStateConstants.SQL_STATE_WRONG_PARAM_NUM);
}
}
}
/**
* Check if obj
is equal to this instance.
*
* @return true
iff obj
is instance of this class
* representing the same procedure with the same parameters.
*/
public boolean equals(Object obj) {
if (obj == this) return true;
if (!(obj instanceof FBProcedureCall)) return false;
FBProcedureCall that = (FBProcedureCall) obj;
return Objects.equals(name, that.name)
&& inputParams.equals(that.inputParams)
&& outputParams.equals(that.outputParams);
}
public int hashCode() {
int hashCode = 547;
hashCode = 37 * hashCode + (name != null ? name.hashCode() : 0);
hashCode = 37 * hashCode + inputParams.hashCode();
hashCode = 37 * hashCode + outputParams.hashCode();
return hashCode;
}
/**
* This class defines a procedure parameter that does not have any value
* and value of which cannot be set. It is created in order to avoid NPE
* when {@link FBProcedureCall#getInputParam(int)} does not find correct
* parameter.
*/
private static final class NullParam extends FBProcedureParam {
private static final NullParam NULL_PARAM = new NullParam();
private NullParam() {
super(-1, "NULL");
}
@Override
public void setValue(Object value) throws SQLException {
throw new FBSQLException("You cannot set value of a non-existing parameter.",
SQLStateConstants.SQL_STATE_INVALID_ARG_VALUE);
}
@Override
public void setIndex(int index) {
throw new UnsupportedOperationException("You cannot set index of a non-existing parameter.");
}
@Override
public void setType(int type) {
throw new UnsupportedOperationException("You cannot set type of a non-existing parameter.");
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy