/* * Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software;Designed and Developed mainly by many Chinese * opensource volunteers. you can redistribute it and/or modify it under the * terms of the GNU General Public License version 2 only, as published by the * Free Software Foundation. * * This code 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 GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Any questions about this component can be directed to it's project Web address * https://code.google.com/p/opencloudb/. * */ package com.akiban.sql.compiler; import com.akiban.sql.parser.*; import com.akiban.sql.StandardException; import com.akiban.sql.types.DataTypeDescriptor; import com.akiban.sql.types.TypeId; import java.sql.Types; /** * This interface defines methods associated with a TypeId that are used * by the compiler. */ public abstract class TypeCompiler { /** * Various fixed numbers related to datatypes. */ // Need to leave space for '-' public static final int LONGINT_MAXWIDTH_AS_CHAR = 20; // Need to leave space for '-' public static final int INT_MAXWIDTH_AS_CHAR = 11; // Need to leave space for '-' public static final int SMALLINT_MAXWIDTH_AS_CHAR = 6; // Need to leave space for '-' public static final int TINYINT_MAXWIDTH_AS_CHAR = 4; // Need to leave space for '-' and decimal point public static final int DOUBLE_MAXWIDTH_AS_CHAR = 54; // Need to leave space for '-' and decimal point public static final int REAL_MAXWIDTH_AS_CHAR = 25; public static final int BOOLEAN_MAXWIDTH_AS_CHAR = 5; public static final String PLUS_OP = "+"; public static final String DIVIDE_OP = "/"; public static final String DIV_OP = "div"; public static final String MINUS_OP = "-"; public static final String TIMES_OP = "*"; public static final String SUM_OP = "sum"; public static final String AVG_OP = "avg"; public static final String MOD_OP = "mod"; private TypeId typeId; protected TypeCompiler(TypeId typeId) { this.typeId = typeId; } public TypeId getTypeId() { return typeId; } /** * Type resolution methods on binary operators * * @param leftType The type of the left parameter * @param rightType The type of the right parameter * @param operator The name of the operator (e.g. "+"). * * @return The type of the result * * @exception StandardException Thrown on error */ public DataTypeDescriptor resolveArithmeticOperation(DataTypeDescriptor leftType, DataTypeDescriptor rightType, String operator) throws StandardException { TypeId leftTypeId = leftType.getTypeId(); TypeId rightTypeId = rightType.getTypeId(); if (leftTypeId.equals(rightTypeId)) return leftType; throw new StandardException("Types not compatible for " + operator + ": " + leftTypeId.getSQLTypeName() + " and " + rightTypeId.getSQLTypeName()); } /** * Determine if this type can be CONVERTed to some other type * * @param otherType The CompilationType of the other type to compare * this type to * * @param forDataTypeFunction true if this is a type function that * requires more liberal behavior (e.g DOUBLE can convert a char but * you cannot cast a CHAR to double. * * @return true if the types can be converted, false if conversion * is not allowed */ public abstract boolean convertible(TypeId otherType, boolean forDataTypeFunction); /** * 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() */ protected boolean numberConvertible(TypeId otherType, boolean forDataTypeFunction) { if (otherType.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; } /** * Determine if this type is compatible to some other type * (e.g. COALESCE(thistype, othertype)). * * @param otherType The CompilationType of the other type to compare * this type to * * @return true if the types are compatible, false if not compatible */ public abstract boolean compatible(TypeId otherType); /** * Get the name of the corresponding Java type. For numerics and booleans * we will get the corresponding Java primitive type. e * Each SQL type has a corresponding Java type. When a SQL value is * passed to a Java method, it is translated to its corresponding Java * type. For example, a SQL Integer will be mapped to a Java int, but * a SQL date will be mapped to a java.sql.Date. * * @return The name of the corresponding Java primitive type. */ public abstract String getCorrespondingPrimitiveTypeName(); /** * Get the method name for getting out the corresponding primitive * Java type from a DataValueDescriptor. * * @return String The method call name for getting the * corresponding primitive Java type. */ public abstract String getPrimitiveMethodName(); /** * Return the maximum width for this data type when cast to a char type. * * @param dtd The associated DataTypeDescriptor for this TypeId. * * @return int The maximum width for this data type when cast to a char type. */ public abstract int getCastToCharWidth(DataTypeDescriptor dtd); /** * Get the format id from the corresponding TypeId. * * @return The format from the corresponding TypeId. * @see TypeId.FormatIds */ protected int getStoredFormatIdFromTypeId() { return getTypeId().getTypeFormatId(); } // These are all the TypeCompilers that are stateless, so we can // use a single instance of each. Initialize all to null, and fault // them in. private static TypeCompiler bitTypeCompiler; private static TypeCompiler booleanTypeCompiler; private static TypeCompiler charTypeCompiler; private static TypeCompiler decimalTypeCompiler; private static TypeCompiler doubleTypeCompiler; private static TypeCompiler intTypeCompiler; private static TypeCompiler longintTypeCompiler; private static TypeCompiler longvarbitTypeCompiler; private static TypeCompiler longvarcharTypeCompiler; private static TypeCompiler realTypeCompiler; private static TypeCompiler smallintTypeCompiler; private static TypeCompiler tinyintTypeCompiler; private static TypeCompiler dateTypeCompiler; private static TypeCompiler timeTypeCompiler; private static TypeCompiler timestampTypeCompiler; private static TypeCompiler varbitTypeCompiler; private static TypeCompiler varcharTypeCompiler; private static TypeCompiler refTypeCompiler; private static TypeCompiler blobTypeCompiler; private static TypeCompiler clobTypeCompiler; private static TypeCompiler xmlTypeCompiler; private static TypeCompiler intervalMonthTypeCompiler, intervalSecondTypeCompiler; /** * Get the TypeCompiler that corresponds to the given TypeId. */ public static TypeCompiler getTypeCompiler(TypeId typeId) { switch (typeId.getJDBCTypeId()) { case Types.BINARY: if (bitTypeCompiler == null) bitTypeCompiler = new BitTypeCompiler(typeId); return bitTypeCompiler; case Types.BIT: case Types.BOOLEAN: if (booleanTypeCompiler == null) booleanTypeCompiler = new BooleanTypeCompiler(typeId); return booleanTypeCompiler; case Types.CHAR: if (charTypeCompiler == null) charTypeCompiler = new CharTypeCompiler(typeId); return charTypeCompiler; case Types.NUMERIC: case Types.DECIMAL: if (decimalTypeCompiler == null) decimalTypeCompiler = new NumericTypeCompiler(typeId); return decimalTypeCompiler; case Types.DOUBLE: if (doubleTypeCompiler == null) doubleTypeCompiler = new NumericTypeCompiler(typeId); return doubleTypeCompiler; case Types.INTEGER: if (intTypeCompiler == null) intTypeCompiler = new NumericTypeCompiler(typeId); return intTypeCompiler; case Types.BIGINT: if (longintTypeCompiler == null) longintTypeCompiler = new NumericTypeCompiler(typeId); return longintTypeCompiler; case Types.BLOB: if (blobTypeCompiler == null) blobTypeCompiler = new LOBTypeCompiler(typeId); return blobTypeCompiler; case Types.LONGVARBINARY: if (longvarbitTypeCompiler == null) longvarbitTypeCompiler = new BitTypeCompiler(typeId); return longvarbitTypeCompiler; case Types.CLOB: if (clobTypeCompiler == null) clobTypeCompiler = new CLOBTypeCompiler(typeId); return clobTypeCompiler; case Types.LONGVARCHAR: if (longvarcharTypeCompiler == null) longvarcharTypeCompiler = new CharTypeCompiler(typeId); return longvarcharTypeCompiler; case Types.REAL: if (realTypeCompiler == null) realTypeCompiler = new NumericTypeCompiler(typeId); return realTypeCompiler; case Types.SMALLINT: if (smallintTypeCompiler == null) smallintTypeCompiler = new NumericTypeCompiler(typeId); return smallintTypeCompiler; case Types.TINYINT: if (tinyintTypeCompiler == null) tinyintTypeCompiler = new NumericTypeCompiler(typeId); return tinyintTypeCompiler; case Types.DATE: if (dateTypeCompiler == null) dateTypeCompiler = new DateTypeCompiler(typeId); return dateTypeCompiler; case Types.TIME: if (timeTypeCompiler == null) timeTypeCompiler = new TimeTypeCompiler(typeId); return timeTypeCompiler; case Types.TIMESTAMP: if (timestampTypeCompiler == null) timestampTypeCompiler = new TimestampTypeCompiler(typeId); return timestampTypeCompiler; case Types.VARBINARY: if (varbitTypeCompiler == null) varbitTypeCompiler = new BitTypeCompiler(typeId); return varbitTypeCompiler; case Types.VARCHAR: if (varcharTypeCompiler == null) varcharTypeCompiler = new CharTypeCompiler(typeId); return varcharTypeCompiler; case Types.JAVA_OBJECT: case Types.OTHER: if (typeId.isRefTypeId()) { if (refTypeCompiler == null) refTypeCompiler = new RefTypeCompiler(typeId); return refTypeCompiler; } else if (typeId.isIntervalTypeId()) { switch (typeId.getTypeFormatId()) { case TypeId.FormatIds.INTERVAL_YEAR_MONTH_ID: if (intervalMonthTypeCompiler == null) intervalMonthTypeCompiler = new IntervalTypeCompiler(typeId); return intervalMonthTypeCompiler; case TypeId.FormatIds.INTERVAL_DAY_SECOND_ID: if (intervalSecondTypeCompiler == null) intervalSecondTypeCompiler = new IntervalTypeCompiler(typeId); return intervalSecondTypeCompiler; default: return null; } } else { // Cannot re-use instances of user-defined type compilers, // because they contain the class name return new UserDefinedTypeCompiler(typeId); } case Types.SQLXML: if (xmlTypeCompiler == null) xmlTypeCompiler = new XMLTypeCompiler(typeId); return xmlTypeCompiler; default: assert false : "Unexpected JDBC type id " + typeId.getJDBCTypeId(); return null; } } }