use of org.apache.derby.iapi.services.loader.ClassInspector in project derby by apache.
the class DataTypeDescriptor method getDominantType.
/**
* Get the dominant type (DataTypeDescriptor) of the 2.
* For variable length types, the resulting type will have the
* biggest max length of the 2.
* If either side is nullable, then the result will also be nullable.
*
* If dealing with character string types, then make sure to set the
* collation info on the dominant type. Following algorithm will be used
* for dominant DTD's collation determination. Each of the steps of the
* algorithm have been numbered in the comments below and those same
* numbers are used in the actual algorithm below so it is easier to
* understand and maintain.
*
* Step 1
* If the DTD for "this" node has the same collation derivation as the
* otherDTS, then check if their collation types match too. If the
* collation types match too, then DTD for dominant type will get the same
* collation derivation and type.
*
* Step 2
* If the collation derivation for DTD for "this" node and otherDTS do not
* match, then check if one of them has the collation derivation of NONE.
* If that is the case, then dominant DTD will get the collation type and
* derivation of DTD whose collation derivation is not NONE.
*
* Step 3
* If the collation derivation for DTD for "this" node and otherDTS do not
* match, and none of them have the derivation of NONE then it means that
* we are dealing with collation derivation of IMPLICIT and EXPLICIT and
* hence the dominant DTD should get collation derivation of NONE. This is
* not a possibility in Derby 10.3 because the only 2 possible collation
* derivation supported are IMPLICIT and NONE.
*
* Step 4
* If the collation derivation for DTD for "this" node and otherDTS match,
* then check if the collation types match too. If not, then the dominant
* DTD should get collation derivation of NONE.
*
* @param otherDTS DataTypeDescriptor to compare with.
* @param cf A ClassFactory
*
* @return DataTypeDescriptor DTS for dominant type
*/
public DataTypeDescriptor getDominantType(DataTypeDescriptor otherDTS, ClassFactory cf) {
boolean nullable;
TypeId thisType;
TypeId otherType;
DataTypeDescriptor higherType;
DataTypeDescriptor lowerType = null;
int maximumWidth;
int precision = getPrecision();
int scale = getScale();
thisType = getTypeId();
otherType = otherDTS.getTypeId();
/* The result is nullable if either side is nullable */
nullable = isNullable() || otherDTS.isNullable();
/*
** The result will have the maximum width of both sides
*/
maximumWidth = (getMaximumWidth() > otherDTS.getMaximumWidth()) ? getMaximumWidth() : otherDTS.getMaximumWidth();
/* We need 2 separate methods of determining type dominance - 1 if both
* types are system built-in types and the other if at least 1 is
* a user type. (typePrecedence is meaningless for user types.)
*/
if (!thisType.userType() && !otherType.userType()) {
TypeId higherTypeId;
TypeId lowerTypeId;
if (thisType.typePrecedence() > otherType.typePrecedence()) {
higherType = this;
lowerType = otherDTS;
higherTypeId = thisType;
lowerTypeId = otherType;
} else {
higherType = otherDTS;
lowerType = this;
higherTypeId = otherType;
lowerTypeId = thisType;
}
// then result type should be double
if (higherTypeId.isRealTypeId() && (!lowerTypeId.isRealTypeId()) && lowerTypeId.isNumericTypeId()) {
higherType = DataTypeDescriptor.getBuiltInDataTypeDescriptor(Types.DOUBLE);
higherTypeId = TypeId.getBuiltInTypeId(Types.DOUBLE);
}
/*
** If we have a DECIMAL/NUMERIC we have to do some
** extra work to make sure the resultant type can
** handle the maximum values for the two input
** types. We cannot just take the maximum for
** precision. E.g. we want something like:
**
** DEC(10,10) and DEC(3,0) => DEC(13,10)
**
** (var)char type needs some conversion handled later.
*/
if (higherTypeId.isDecimalTypeId() && (!lowerTypeId.isStringTypeId())) {
precision = higherTypeId.getPrecision(this, otherDTS);
// db2 silently does this and so do we
if (precision > 31)
precision = 31;
scale = higherTypeId.getScale(this, otherDTS);
/* maximumWidth needs to count possible leading '-' and
* decimal point and leading '0' if scale > 0. See also
* sqlgrammar.jj(exactNumericType). Beetle 3875
*/
maximumWidth = (scale > 0) ? precision + 3 : precision + 1;
} else if (thisType.typePrecedence() != otherType.typePrecedence()) {
precision = higherType.getPrecision();
scale = higherType.getScale();
/* GROSS HACKS:
* If we are doing an implicit (var)char->(var)bit conversion
* then the maximum width for the (var)char as a (var)bit
* is really 16 * its width as a (var)char. Adjust
* maximumWidth accordingly.
* If we are doing an implicit (var)char->decimal conversion
* then we need to increment the decimal's precision by
* 2 * the maximum width for the (var)char and the scale
* by the maximum width for the (var)char. The maximumWidth
* becomes the new precision + 3. This is because
* the (var)char could contain any decimal value from XXXXXX
* to 0.XXXXX. (In other words, we don't know which side of the
* decimal point the characters will be on.)
*/
if (lowerTypeId.isStringTypeId()) {
if (higherTypeId.isBitTypeId() && !(higherTypeId.isLongConcatableTypeId())) {
if (lowerTypeId.isLongConcatableTypeId()) {
if (maximumWidth > (Integer.MAX_VALUE / 16))
maximumWidth = Integer.MAX_VALUE;
else
maximumWidth *= 16;
} else {
int charMaxWidth;
int fromWidth = lowerType.getMaximumWidth();
if (fromWidth > (Integer.MAX_VALUE / 16))
charMaxWidth = Integer.MAX_VALUE;
else
charMaxWidth = 16 * fromWidth;
maximumWidth = (maximumWidth >= charMaxWidth) ? maximumWidth : charMaxWidth;
}
}
}
/*
* If we are doing an implicit (var)char->decimal conversion
* then the resulting decimal's precision could be as high as
* 2 * the maximum width (precisely 2mw-1) for the (var)char
* and the scale could be as high as the maximum width
* (precisely mw-1) for the (var)char.
* The maximumWidth becomes the new precision + 3. This is
* because the (var)char could contain any decimal value from
* XXXXXX to 0.XXXXX. (In other words, we don't know which
* side of the decimal point the characters will be on.)
*
* We don't follow this algorithm for long varchar because the
* maximum length of a long varchar is maxint, and we don't
* want to allocate a huge decimal value. So in this case,
* the precision, scale, and maximum width all come from
* the decimal type.
*/
if (lowerTypeId.isStringTypeId() && !(lowerTypeId.isLongConcatableTypeId()) && higherTypeId.isDecimalTypeId()) {
int charMaxWidth = lowerType.getMaximumWidth();
int charPrecision;
/*
** Be careful not to overflow when calculating the
** precision. Remember that we will be adding
** three to the precision to get the maximum width.
*/
if (charMaxWidth > (Integer.MAX_VALUE - 3) / 2)
charPrecision = Integer.MAX_VALUE - 3;
else
charPrecision = charMaxWidth * 2;
if (precision < charPrecision)
precision = charPrecision;
if (scale < charMaxWidth)
scale = charMaxWidth;
maximumWidth = precision + 3;
}
}
} else {
/* At least 1 type is not a system built-in type */
ClassInspector cu = cf.getClassInspector();
TypeId thisCompType = (TypeId) thisType;
TypeId otherCompType = (TypeId) otherType;
if (cu.assignableTo(thisCompType.getCorrespondingJavaTypeName(), otherCompType.getCorrespondingJavaTypeName())) {
higherType = otherDTS;
} else {
if (SanityManager.DEBUG)
SanityManager.ASSERT(cu.assignableTo(otherCompType.getCorrespondingJavaTypeName(), thisCompType.getCorrespondingJavaTypeName()), otherCompType.getCorrespondingJavaTypeName() + " expected to be assignable to " + thisCompType.getCorrespondingJavaTypeName());
higherType = this;
}
precision = higherType.getPrecision();
scale = higherType.getScale();
}
higherType = new DataTypeDescriptor(higherType, precision, scale, nullable, maximumWidth);
// The algorithm used is explained in this method's javadoc
if (higherType.getTypeId().isStringTypeId()) {
if (getCollationDerivation() != otherDTS.getCollationDerivation()) {
if (getCollationDerivation() == StringDataValue.COLLATION_DERIVATION_NONE) {
// Step 2
higherType = higherType.getCollatedType(otherDTS.getCollationType(), otherDTS.getCollationDerivation());
} else if (otherDTS.getCollationDerivation() == StringDataValue.COLLATION_DERIVATION_NONE) {
// Step 2
higherType = higherType.getCollatedType(getCollationType(), getCollationDerivation());
} else {
// Step 3
higherType = higherType.getCollatedType(// ignored
StringDataValue.COLLATION_TYPE_UCS_BASIC, StringDataValue.COLLATION_DERIVATION_NONE);
}
} else if (getCollationType() != otherDTS.getCollationType())
// Step 4
higherType = higherType.getCollatedType(// ignored
StringDataValue.COLLATION_TYPE_UCS_BASIC, StringDataValue.COLLATION_DERIVATION_NONE);
else {
// Step 1
higherType = higherType.getCollatedType(getCollationType(), getCollationDerivation());
}
}
return higherType;
}
use of org.apache.derby.iapi.services.loader.ClassInspector in project derby by apache.
the class MethodCallNode method resolveMethodCall.
protected void resolveMethodCall(String javaClassName, boolean staticMethod) throws StandardException {
// only allow direct method calls through routines and internal SQL.
if (routineInfo == null && !internalCall) {
// See if we are being executed in an internal context
if ((getCompilerContext().getReliability() & CompilerContext.INTERNAL_SQL_ILLEGAL) != 0) {
throw StandardException.newException(SQLState.LANG_SYNTAX_ERROR, javaClassName + (staticMethod ? "::" : ".") + methodName);
}
}
int count = signature.length;
ClassInspector classInspector = getClassFactory().getClassInspector();
String[] parmTypeNames;
String[] primParmTypeNames = null;
boolean[] isParam = getIsParam();
boolean hasDynamicResultSets = hasVarargs() ? false : (routineInfo != null) && (count != 0) && (count != methodParms.length);
/*
** Find the matching method that is public.
*/
int signatureOffset = methodName.indexOf('(');
// support Java signatures by checking if the method name contains a '('
if (signatureOffset != -1) {
parmTypeNames = parseValidateSignature(methodName, signatureOffset, hasDynamicResultSets);
methodName = methodName.substring(0, signatureOffset);
// If the signature is specified then Derby resolves to exactly
// that method. Setting this flag to false disables the method
// resolution from automatically optionally repeating the last
// parameter as needed.
hasDynamicResultSets = false;
} else {
parmTypeNames = getObjectSignature();
}
// the actual type of the trailing Java varargs arg is an array
if (hasVarargs()) {
parmTypeNames[count - 1] = parmTypeNames[count - 1] + "[]";
}
try {
method = classInspector.findPublicMethod(javaClassName, methodName, parmTypeNames, null, isParam, staticMethod, hasDynamicResultSets, hasVarargs());
// Also if the DDL specified a signature, then no alternate resolution
if (signatureOffset == -1 && routineInfo == null) {
/* If no match, then retry with combinations of object and
* primitive types.
*/
if (method == null) {
primParmTypeNames = getPrimitiveSignature(false);
method = classInspector.findPublicMethod(javaClassName, methodName, parmTypeNames, primParmTypeNames, isParam, staticMethod, hasDynamicResultSets, hasVarargs());
}
}
} catch (ClassNotFoundException e) {
/*
** If one of the classes couldn't be found, just act like the
** method couldn't be found. The error lists all the class names,
** which should give the user enough info to diagnose the problem.
*/
method = null;
}
/* Throw exception if no matching signature found */
if (method == null) {
throwNoMethodFound(javaClassName, parmTypeNames, primParmTypeNames);
}
String typeName = classInspector.getType(method);
actualMethodReturnType = typeName;
if (routineInfo == null) {
/* void methods are only okay for CALL Statements */
if (typeName.equals("void")) {
if (!forCallStatement)
throw StandardException.newException(SQLState.LANG_VOID_METHOD_CALL);
}
} else {
String promoteName = null;
TypeDescriptorImpl returnType = (TypeDescriptorImpl) routineInfo.getReturnType();
String requiredType;
if (returnType == null) {
// must have a void method for a procedure call.
requiredType = "void";
} else {
TypeId returnTypeId = TypeId.getBuiltInTypeId(returnType.getJDBCTypeId());
if (returnType.isRowMultiSet() && (routineInfo.getParameterStyle() == RoutineAliasInfo.PS_DERBY_JDBC_RESULT_SET)) {
requiredType = ResultSet.class.getName();
} else if (returnType.getTypeId().userType()) {
requiredType = ((UserDefinedTypeIdImpl) returnType.getTypeId()).getClassName();
} else {
requiredType = returnTypeId.getCorrespondingJavaTypeName();
if (!requiredType.equals(typeName)) {
switch(returnType.getJDBCTypeId()) {
case java.sql.Types.BOOLEAN:
case java.sql.Types.SMALLINT:
case java.sql.Types.INTEGER:
case java.sql.Types.BIGINT:
case java.sql.Types.REAL:
case java.sql.Types.DOUBLE:
TypeCompiler tc = getTypeCompiler(returnTypeId);
requiredType = tc.getCorrespondingPrimitiveTypeName();
if (!routineInfo.calledOnNullInput() && routineInfo.getParameterCount() != 0) {
promoteName = returnTypeId.getCorrespondingJavaTypeName();
}
break;
}
}
}
}
boolean foundCorrectType;
if (ResultSet.class.getName().equals(requiredType)) {
// allow subtypes of ResultSet too
try {
Class<?> actualType = classInspector.getClass(typeName);
foundCorrectType = ResultSet.class.isAssignableFrom(actualType);
} catch (ClassNotFoundException cnfe) {
foundCorrectType = false;
}
} else {
foundCorrectType = requiredType.equals(typeName);
}
if (!foundCorrectType) {
throwNoMethodFound(requiredType + " " + javaClassName, parmTypeNames, primParmTypeNames);
}
// type we need to promote to an object so we can return null.
if (promoteName != null)
typeName = promoteName;
// MethodCallNode DERBY-2972
if (routineInfo.getReturnType() != null)
setCollationType(routineInfo.getReturnType().getCollationType());
}
setJavaTypeName(typeName);
methodParameterTypes = classInspector.getParameterTypes(method);
String methodParameter = null;
for (int i = 0; i < methodParameterTypes.length; i++) {
methodParameter = methodParameterTypes[i];
if (routineInfo != null) {
if (i < routineInfo.getParameterCount()) {
int parameterMode = routineInfo.getParameterModes()[getRoutineArgIdx(i)];
switch(parameterMode) {
case (ParameterMetaData.parameterModeIn):
break;
case (ParameterMetaData.parameterModeInOut):
// we need to see if the type of the array is
// primitive, not the array itself.
methodParameter = stripOneArrayLevel(methodParameter);
break;
case (ParameterMetaData.parameterModeOut):
// value is not obtained *from* parameter.
continue;
}
}
}
//
if (hasVarargs() && (i >= getFirstVarargIdx())) {
methodParameter = stripOneArrayLevel(methodParameter);
}
if (ClassInspector.primitiveType(methodParameter)) {
// corresponding to the vararg
if (i < methodParms.length) {
methodParms[i].castToPrimitive(true);
}
}
}
// casting may be needed on the trailing varargs
if (hasVarargs()) {
int firstVarargIdx = getFirstVarargIdx();
int trailingVarargCount = methodParms.length - firstVarargIdx;
// the first vararg was handled in the preceding loop
for (int i = 1; i < trailingVarargCount; i++) {
if (ClassInspector.primitiveType(methodParameter)) {
methodParms[i + firstVarargIdx].castToPrimitive(true);
}
}
}
/* Set type info for any null parameters */
if (someParametersAreNull()) {
setNullParameterInfo(methodParameterTypes);
}
/* bug 4450 - if the callable statement is ? = call form, generate the metadata
infor for the return parameter. We don't really need that info in order to
execute the callable statement. But with jdbc3.0, this information should be
made available for return parameter through ParameterMetaData class.
Parser sets a flag in compilercontext if ? = call. If the flag is set,
we generate the metadata info for the return parameter and reset the flag
in the compilercontext for future call statements*/
DataTypeDescriptor dts = DataTypeDescriptor.getSQLDataTypeDescriptor(typeName);
if (getCompilerContext().getReturnParameterFlag()) {
getParameterTypes()[0] = dts;
}
}
Aggregations