blob: a5ae738db07d484eb017a3ec4976b410dca0cffb [file] [log] [blame]
/*
Derby - Class org.apache.derby.impl.sql.compile.BaseTypeCompiler
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to you under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package org.apache.derby.impl.sql.compile;
import org.apache.derby.iapi.error.StandardException;
import org.apache.derby.iapi.reference.SQLState;
import org.apache.derby.iapi.services.classfile.VMOpcode;
import org.apache.derby.iapi.services.compiler.LocalField;
import org.apache.derby.iapi.services.compiler.MethodBuilder;
import org.apache.derby.iapi.services.loader.ClassFactory;
import org.apache.derby.shared.common.sanity.SanityManager;
import org.apache.derby.iapi.sql.compile.TypeCompiler;
import org.apache.derby.iapi.types.DataTypeDescriptor;
import org.apache.derby.iapi.types.TypeId;
/**
* This is the base implementation of TypeCompiler
*
*/
abstract class BaseTypeCompiler implements TypeCompiler
{
private TypeId correspondingTypeId;
/**
* Get the method name for getting out the corresponding primitive
* Java type.
*
* @return String The method call name for getting the
* corresponding primitive Java type.
*/
public String getPrimitiveMethodName()
{
if (SanityManager.DEBUG)
{
SanityManager.THROWASSERT("getPrimitiveMethodName not applicable for " +
getClass().toString());
}
return null;
}
/**
* @see TypeCompiler#resolveArithmeticOperation
*
* @exception StandardException Thrown on error
*/
public DataTypeDescriptor
resolveArithmeticOperation(DataTypeDescriptor leftType,
DataTypeDescriptor rightType,
String operator)
throws StandardException
{
throw StandardException.newException(SQLState.LANG_BINARY_OPERATOR_NOT_SUPPORTED,
operator,
leftType.getTypeId().getSQLTypeName(),
rightType.getTypeId().getSQLTypeName()
);
}
/**
* The caller will have pushed a DataValueFactory and a null or a value
* of the correct type (interfaceName()). Thus upon entry the
* stack looks like on of:
* ...,dvf,ref
* ...,dvf,null
*
* This method then sets up to call the required method
* on DataValueFactory using the nullMethodName().
* The value left on the stack will be a DataValueDescriptor
* of the correct type:
*
* ...,dvd
*
* @see TypeCompiler#generateNull(MethodBuilder, int)
*/
public void generateNull(MethodBuilder mb, int collationType)
{
int argCount;
if (pushCollationForDataValue(collationType))
{
mb.push(collationType);
argCount = 2;
}
else
argCount = 1;
mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
nullMethodName(),
interfaceName(),
argCount);
}
/**
* The caller will have pushed a DataValueFactory and value
* of that can be converted to the correct type, e.g. int
* for a SQL INTEGER.
*
* Thus upon entry the
* stack looks like:
* ...,dvf,value
*
* If field is not null then it is used as the holder
* of the generated DataValueDescriptor to avoid object
* creations on multiple passes through this code.
* The field may contain null or a valid value.
*
* This method then sets up to call the required method
* on DataValueFactory using the dataValueMethodName().
* The value left on the stack will be a DataValueDescriptor
* of the correct type:
*
* If the field contained a valid value then generated
* code will return that value rather than a newly created
* object. If field was not-null then the generated code
* will set the value of field to be the return from
* the DataValueFactory method call. Thus if the field
* was empty (set to null) when this code is executed it
* will contain the newly generated value, otherwise it
* will be reset to the same value.
*
* ...,dvd
*
* @see TypeCompiler#generateDataValue(MethodBuilder, int, LocalField)
*/
public void generateDataValue(MethodBuilder mb, int collationType,
LocalField field)
{
String interfaceName = interfaceName();
// push the second argument
/* If fieldName is null, then there is no
* reusable wrapper (null), else we
* reuse the field.
*/
if (field == null)
{
mb.pushNull(interfaceName);
}
else
{
mb.getField(field);
}
int argCount;
if (pushCollationForDataValue(collationType))
{
mb.push(collationType);
argCount = 3;
}
else
argCount = 2;
mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
dataValueMethodName(),
interfaceName,
argCount);
if (field != null)
{
/* Store the result of the method call in the field,
* so we can re-use the wrapper.
*/
mb.putField(field);
}
}
/**
Return the method name to get a Derby DataValueDescriptor
object of the correct type set to SQL NULL. The method named will
be called with one argument: a holder object if pushCollationForDataValue()
returns false, otherwise two arguments, the second being the
collationType.
*/
abstract String nullMethodName();
/**
Return the method name to get a Derby DataValueDescriptor
object of the correct type and set it to a specific value.
The method named will be called with two arguments, a value to set the
returned value to and a holder object if pushCollationForDataValue()
returns false. Otherwise three arguments, the third being the
collationType.
This implementation returns "getDataValue" to map
to the overloaded methods
DataValueFactory.getDataValue(type, dvd type)
*/
String dataValueMethodName()
{
return "getDataValue";
}
/**
* Return true if the collationType is to be passed
* to the methods generated by generateNull and
* generateDataValue.
*
* @param collationType Collation type of character values.
* @return true collationType will be pushed, false collationType will be ignored.
*/
boolean pushCollationForDataValue(int collationType)
{
return false;
}
/**
* Determine whether thisType is storable in otherType due to otherType
* being a user type.
*
* @param thisType The TypeId of the value to be stored
* @param otherType The TypeId of the value to be stored in
*
* @return true if thisType is storable in otherType
*/
protected boolean userTypeStorable(TypeId thisType,
TypeId otherType,
ClassFactory cf)
{
/*
** If the other type is user-defined, use the java types to determine
** assignability.
*/
if (otherType.userType())
{
return cf.getClassInspector().assignableTo(
thisType.getCorrespondingJavaTypeName(),
otherType.getCorrespondingJavaTypeName());
}
return false;
}
/**
* Tell whether this numeric type can be converted to the given type.
*
* @param otherType The TypeId of the other type.
* @param forDataTypeFunction was this called from a scalarFunction like
* CHAR() or DOUBLE()
*/
boolean numberConvertible(TypeId otherType,
boolean forDataTypeFunction)
{
if ( otherType.getBaseTypeId().isAnsiUDT() ) { return false; }
// Can't convert numbers to long types
if (otherType.isLongConcatableTypeId())
return false;
// Numbers can only be converted to other numbers,
// and CHAR, (not VARCHARS or LONGVARCHAR).
// Only with the CHAR() or VARCHAR()function can they be converted.
boolean retval =((otherType.isNumericTypeId()) ||
(otherType.userType()));
// For CHAR Conversions, function can convert
// Floating types
if (forDataTypeFunction)
retval = retval ||
(otherType.isFixedStringTypeId() &&
(getTypeId().isFloatingPointTypeId()));
retval = retval ||
(otherType.isFixedStringTypeId() &&
(!getTypeId().isFloatingPointTypeId()));
return retval;
}
/**
* Tell whether this numeric type can be stored into from the given type.
*
* @param thisType The TypeId of this type
* @param otherType The TypeId of the other type.
* @param cf A ClassFactory
*/
boolean numberStorable(TypeId thisType,
TypeId otherType,
ClassFactory cf)
{
if ( otherType.getBaseTypeId().isAnsiUDT() ) { return false; }
/*
** Numbers can be stored into from other number types.
** Also, user types with compatible classes can be stored into numbers.
*/
if (otherType.isNumericTypeId()) { return true; }
/*
** If the other type is user-defined, use the java types to determine
** assignability.
*/
return userTypeStorable(thisType, otherType, cf);
}
/**
* Get the TypeId that corresponds to this TypeCompiler.
*/
protected TypeId getTypeId()
{
return correspondingTypeId;
}
/**
* Get the TypeCompiler that corresponds to the given TypeId.
*/
protected TypeCompiler getTypeCompiler(TypeId typeId)
{
return TypeCompilerFactoryImpl.staticGetTypeCompiler(typeId);
}
/**
* Set the TypeCompiler that corresponds to the given TypeId.
*/
void setTypeId(TypeId typeId)
{
correspondingTypeId = typeId;
}
/**
* Get the StoredFormatId from the corresponding
* TypeId.
*
* @return The StoredFormatId from the corresponding
* TypeId.
*/
protected int getStoredFormatIdFromTypeId()
{
return getTypeId().getTypeFormatId();
}
}