/* Copyright (c) 2001-2009, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hsqldb_voltpatches;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Map;
import org.hsqldb_voltpatches.types.Type;
/**
* Implementation of calls to VoltDB functions that may have no SQL standard equivalent.<p>
*
* @author Fred Toussi (fredt@users dot sourceforge.net)
* @version 1.9.0
* @since 1.9.0
*/
public class FunctionForVoltDB extends FunctionSQL {
static class FunctionId {
final private String m_name;
final private int m_id;
final private Type m_type;
final private int m_typeParameter;
final private Type[] m_paramTypes;
final private short[] m_paramParseList;
final private short[] m_paramParseListAlt;
public String getName() {
return m_name;
}
public int getId() {
return m_id;
}
public Type getDataType() {
return m_type;
}
public Type[] getParamTypes() {
return m_paramTypes;
}
public short[] getParamParseList() {
return m_paramParseList;
}
public short[] getParamParseListAlt() {
return m_paramParseListAlt;
}
private FunctionId(String name, Type type, int id, int typeParameter, Type[] paramTypes, short[] paramParseList) {
this(name, type, id, typeParameter, paramTypes, paramParseList, null);
}
private FunctionId(String name, Type type, int id, int typeParameter, Type[] paramTypes, short[] paramParseList, short[] paramParseListAlt) {
m_name = name;
m_type = type;
m_id = id;
m_typeParameter = typeParameter;
m_paramTypes = paramTypes;
m_paramParseList = paramParseList;
m_paramParseListAlt = paramParseListAlt;
}
// These ID numbers need to be unique values for FunctionSQL.functType.
// Assume that 1-19999 are reserved for existing HSQL functions.
// That leaves new VoltDB-specific functions free to use values in the 20000s.
static final int FUNC_CONCAT = 124;
private static final int FUNC_VOLT_SQL_ERROR = 20000;
private static final int FUNC_VOLT_DECODE = 20001;
private static final int FUNC_VOLT_FIELD = 20002;
private static final int FUNC_VOLT_ARRAY_ELEMENT = 20003;
private static final int FUNC_VOLT_ARRAY_LENGTH = 20004;
static final int FUNC_VOLT_SINCE_EPOCH = 20005;
static final int FUNC_VOLT_SINCE_EPOCH_SECOND = 20006;
static final int FUNC_VOLT_SINCE_EPOCH_MILLISECOND = 20007;
static final int FUNC_VOLT_SINCE_EPOCH_MICROSECOND = 20008;
static final int FUNC_VOLT_TO_TIMESTAMP = 20009;
static final int FUNC_VOLT_TO_TIMESTAMP_SECOND = 20010;
static final int FUNC_VOLT_TO_TIMESTAMP_MILLISECOND = 20011;
static final int FUNC_VOLT_TO_TIMESTAMP_MICROSECOND = 20012;
static final int FUNC_VOLT_TRUNCATE_TIMESTAMP = 20013;
static final int FUNC_VOLT_TRUNCATE_YEAR = 20014;
static final int FUNC_VOLT_TRUNCATE_QUARTER = 20015;
static final int FUNC_VOLT_TRUNCATE_MONTH = 20016;
static final int FUNC_VOLT_TRUNCATE_DAY = 20017;
static final int FUNC_VOLT_TRUNCATE_HOUR = 20018;
static final int FUNC_VOLT_TRUNCATE_MINUTE = 20019;
static final int FUNC_VOLT_TRUNCATE_SECOND = 20020;
static final int FUNC_VOLT_TRUNCATE_MILLISECOND = 20021;
static final int FUNC_VOLT_TRUNCATE_MICROSECOND = 20022;
static final int FUNC_VOLT_FROM_UNIXTIME = 20023;
static final int FUNC_VOLT_SET_FIELD = 20024;
static final int FUNC_VOLT_FORMAT_CURRENCY = 20025;
static final int FUNC_VOLT_BITNOT = 20026;
static final int FUNC_VOLT_BIT_SHIFT_LEFT = 20027;
static final int FUNC_VOLT_BIT_SHIFT_RIGHT = 20028;
static final int FUNC_VOLT_HEX = 20029;
static final int FUNC_VOLT_BIN = 20030;
static final int FUNC_VOLT_DATEADD = 20031;
static final int FUNC_VOLT_DATEADD_YEAR = 20032;
static final int FUNC_VOLT_DATEADD_QUARTER = 20033;
static final int FUNC_VOLT_DATEADD_MONTH = 20034;
static final int FUNC_VOLT_DATEADD_DAY = 20035;
static final int FUNC_VOLT_DATEADD_HOUR = 20036;
static final int FUNC_VOLT_DATEADD_MINUTE = 20037;
static final int FUNC_VOLT_DATEADD_SECOND = 20038;
static final int FUNC_VOLT_DATEADD_MILLISECOND = 20039;
static final int FUNC_VOLT_DATEADD_MICROSECOND = 20040;
static final int FUNC_VOLT_REGEXP_POSITION = 20041;
static final int FUNC_VOLT_ROUND = 20042;
static final int FUNC_VOLT_STR = 20043;
// our local functions for networking
static final int FUNC_INET_NTOA = 20044;
static final int FUNC_INET_ATON = 20045;
static final int FUNC_INET6_NTOA = 20046;
static final int FUNC_INET6_ATON = 20047;
// Geospatial functions
static final int FUNC_VOLT_POINTFROMTEXT = 21000;
static final int FUNC_VOLT_POLYGONFROMTEXT = 21001;
static final int FUNC_VOLT_CONTAINS = 21002;
static final int FUNC_VOLT_POLYGON_NUM_INTERIOR_RINGS = 21003;
static final int FUNC_VOLT_POLYGON_NUM_POINTS = 21004;
static final int FUNC_VOLT_POINT_LATITUDE = 21005;
static final int FUNC_VOLT_POINT_LONGITUDE = 21006;
static final int FUNC_VOLT_POLYGON_CENTROID = 21007;
static final int FUNC_VOLT_POLYGON_AREA = 21008;
static final int FUNC_VOLT_DISTANCE = 21009; // wrapper id for distance between all geo types
static final int FUNC_VOLT_DISTANCE_POINT_POINT = 21010; // distance between point and point
static final int FUNC_VOLT_DISTANCE_POLYGON_POINT = 21011; // distance between polygon and point
static final int FUNC_VOLT_ASTEXT = 21012; // wrapper for asText function for all geo types
static final int FUNC_VOLT_ASTEXT_GEOGRAPHY_POINT = 21013; // point to text
static final int FUNC_VOLT_ASTEXT_GEOGRAPHY = 21014; // polygon to text
static final int FUNC_VOLT_VALIDATE_POLYGON = 21015; // Polygon validation.
static final int FUNC_VOLT_POLYGON_INVALID_REASON = 21016; // Reason a polygon may be invalid.
static final int FUNC_VOLT_DWITHIN = 21017; // wrapper id for function that evaluates if two geo objects are within
// certain distance of each other
static final int FUNC_VOLT_DWITHIN_POINT_POINT = 21018; // if two points are within certain distance of each other
static final int FUNC_VOLT_DWITHIN_POLYGON_POINT = 21019; // if a polygon and a point are within certain distance of each other
static final int FUNC_VOLT_VALIDPOLYGONFROMTEXT = 21020; // list polygonFromText, but validates after construction
static final int FUNC_VOLT_MIN_VALID_TIMESTAMP = 21021; // Minimum valid timestamp.
static final int FUNC_VOLT_MAX_VALID_TIMESTAMP = 21022; // Maximum valid timestamp.
static final int FUNC_VOLT_IS_VALID_TIMESTAMP = 21023; // Is a timestamp value in range?
/*
* Note: The name must be all lower case.
*/
private static final FunctionId[] instances = {
new FunctionId("sql_error", null, FUNC_VOLT_SQL_ERROR, 0,
new Type[] { null, Type.SQL_VARCHAR },
new short[] { Tokens.OPENBRACKET, Tokens.QUESTION,
Tokens.X_OPTION, 2, Tokens.COMMA, Tokens.QUESTION, Tokens.CLOSEBRACKET }),
new FunctionId("bit_shift_left", Type.SQL_BIGINT, FUNC_VOLT_BIT_SHIFT_LEFT, -1,
new Type[] { Type.SQL_BIGINT, Type.SQL_BIGINT },
new short[] { Tokens.OPENBRACKET, Tokens.QUESTION,
Tokens.COMMA, Tokens.QUESTION, Tokens.CLOSEBRACKET }),
new FunctionId("bit_shift_right", Type.SQL_BIGINT, FUNC_VOLT_BIT_SHIFT_RIGHT, -1,
new Type[] { Type.SQL_BIGINT, Type.SQL_BIGINT },
new short[] { Tokens.OPENBRACKET, Tokens.QUESTION,
Tokens.COMMA, Tokens.QUESTION, Tokens.CLOSEBRACKET }),
new FunctionId("decode", null, FUNC_VOLT_DECODE, 2,
new Type[] { null, null },
new short[] { Tokens.OPENBRACKET, Tokens.QUESTION, Tokens.COMMA, Tokens.QUESTION,
Tokens.X_REPEAT, 2, Tokens.COMMA, Tokens.QUESTION,
Tokens.CLOSEBRACKET }),
new FunctionId("field", Type.SQL_VARCHAR, FUNC_VOLT_FIELD, -1,
new Type[] { Type.SQL_VARCHAR, Type.SQL_VARCHAR },
new short[] { Tokens.OPENBRACKET, Tokens.QUESTION,
Tokens.COMMA, Tokens.QUESTION,
Tokens.CLOSEBRACKET}),
new FunctionId("set_field", Type.SQL_VARCHAR, FUNC_VOLT_SET_FIELD, -1,
new Type[] { Type.SQL_VARCHAR, Type.SQL_VARCHAR, Type.SQL_VARCHAR },
new short[] { Tokens.OPENBRACKET, Tokens.QUESTION,
Tokens.COMMA, Tokens.QUESTION,
Tokens.COMMA, Tokens.QUESTION,
Tokens.CLOSEBRACKET }),
new FunctionId("array_element", Type.SQL_VARCHAR, FUNC_VOLT_ARRAY_ELEMENT, -1,
new Type[] { Type.SQL_VARCHAR, Type.SQL_INTEGER },
new short[] { Tokens.OPENBRACKET, Tokens.QUESTION,
Tokens.COMMA, Tokens.QUESTION,
Tokens.CLOSEBRACKET}),
new FunctionId("array_length", Type.SQL_INTEGER, FUNC_VOLT_ARRAY_LENGTH, -1,
new Type[] { Type.SQL_VARCHAR },
new short[] { Tokens.OPENBRACKET, Tokens.QUESTION, Tokens.CLOSEBRACKET}),
new FunctionId("since_epoch", Type.SQL_BIGINT, FUNC_VOLT_SINCE_EPOCH, -1,
new Type[] { Type.SQL_VARCHAR, Type.SQL_TIMESTAMP },
new short[] { Tokens.OPENBRACKET, Tokens.X_KEYSET, 5,
Tokens.SECOND, Tokens.MILLIS, Tokens.MICROS,
Tokens.MILLISECOND, Tokens.MICROSECOND,
Tokens.COMMA, Tokens.QUESTION, Tokens.CLOSEBRACKET }),
new FunctionId("to_timestamp", Type.SQL_TIMESTAMP, FUNC_VOLT_TO_TIMESTAMP, -1,
new Type[] { Type.SQL_VARCHAR, Type.SQL_BIGINT },
new short[] { Tokens.OPENBRACKET, Tokens.X_KEYSET, 5,
Tokens.SECOND, Tokens.MILLIS, Tokens.MICROS,
Tokens.MILLISECOND, Tokens.MICROSECOND,
Tokens.COMMA, Tokens.QUESTION, Tokens.CLOSEBRACKET }),
new FunctionId("truncate", Type.SQL_TIMESTAMP, FUNC_VOLT_TRUNCATE_TIMESTAMP, -1,
new Type[] { Type.SQL_VARCHAR, Type.SQL_TIMESTAMP },
new short[] { Tokens.OPENBRACKET, Tokens.X_KEYSET, 11,
Tokens.YEAR, Tokens.QUARTER, Tokens.MONTH, Tokens.DAY, Tokens.HOUR,
Tokens.MINUTE, Tokens.SECOND, Tokens.MILLIS, Tokens.MILLISECOND,
Tokens.MICROS, Tokens.MICROSECOND,
Tokens.COMMA, Tokens.QUESTION, Tokens.CLOSEBRACKET }),
new FunctionId("from_unixtime", Type.SQL_TIMESTAMP, FUNC_VOLT_FROM_UNIXTIME, -1,
new Type[] { Type.SQL_BIGINT },
singleParamList),
new FunctionId("format_currency", Type.SQL_VARCHAR, FUNC_VOLT_FORMAT_CURRENCY, -1,
new Type[] { Type.SQL_DECIMAL, Type.SQL_INTEGER},
doubleParamList),
new FunctionId("round", Type.SQL_DECIMAL, FUNC_VOLT_ROUND, -1,
new Type[] { Type.SQL_DECIMAL, Type.SQL_INTEGER},
doubleParamList),
new FunctionId("str", Type.SQL_VARCHAR, FUNC_VOLT_STR, -1,
new Type[] { Type.SQL_DECIMAL, Type.SQL_INTEGER, Type.SQL_INTEGER},
new short[] { Tokens.OPENBRACKET, Tokens.QUESTION, Tokens.X_OPTION, 6, Tokens.COMMA,
Tokens.QUESTION, Tokens.X_OPTION, 2, Tokens.COMMA, Tokens.QUESTION, Tokens.CLOSEBRACKET }),
new FunctionId("bitnot", Type.SQL_BIGINT, FUNC_VOLT_BITNOT, -1,
new Type[] { Type.SQL_BIGINT },
singleParamList),
new FunctionId("concat", Type.SQL_VARCHAR, FUNC_CONCAT, -1,
new Type[] { Type.SQL_VARCHAR, Type.SQL_VARCHAR },
new short[] { Tokens.OPENBRACKET, Tokens.QUESTION, Tokens.COMMA, Tokens.QUESTION,
Tokens.X_REPEAT, 2, Tokens.COMMA, Tokens.QUESTION,
Tokens.CLOSEBRACKET }),
new FunctionId("hex", Type.SQL_VARCHAR, FUNC_VOLT_HEX, -1,
new Type[] { Type.SQL_BIGINT },
singleParamList),
new FunctionId("bin", Type.SQL_VARCHAR, FUNC_VOLT_BIN, -1,
new Type[] { Type.SQL_BIGINT },
singleParamList),
new FunctionId("dateadd", Type.SQL_TIMESTAMP, FUNC_VOLT_DATEADD, -1,
new Type[] { Type.SQL_VARCHAR, Type.SQL_BIGINT, Type.SQL_TIMESTAMP },
new short[] { Tokens.OPENBRACKET, Tokens.X_KEYSET, 11, Tokens.YEAR,
Tokens.QUARTER, Tokens.MONTH, Tokens.DAY, Tokens.HOUR, Tokens.MINUTE, Tokens.SECOND,
Tokens.MILLIS, Tokens.MILLISECOND, Tokens.MICROS, Tokens.MICROSECOND, Tokens.COMMA,
Tokens.QUESTION, Tokens.COMMA, Tokens.QUESTION, Tokens.CLOSEBRACKET }),
new FunctionId("regexp_position", Type.SQL_BIGINT, FUNC_VOLT_REGEXP_POSITION, -1,
new Type[] { Type.SQL_VARCHAR, Type.SQL_VARCHAR, Type.SQL_VARCHAR },
new short[] { Tokens.OPENBRACKET, Tokens.QUESTION, Tokens.COMMA, Tokens.QUESTION,
Tokens.X_OPTION, 2, Tokens.COMMA, Tokens.QUESTION, Tokens.CLOSEBRACKET}),
new FunctionId("pointfromtext", Type.VOLT_GEOGRAPHY_POINT, FUNC_VOLT_POINTFROMTEXT, -1,
new Type[] { Type.SQL_VARCHAR },
singleParamList),
new FunctionId("polygonfromtext", Type.VOLT_GEOGRAPHY, FUNC_VOLT_POLYGONFROMTEXT, -1,
new Type[] { Type.SQL_VARCHAR },
singleParamList),
new FunctionId("contains", Type.SQL_BOOLEAN, FUNC_VOLT_CONTAINS, -1,
new Type[] { Type.VOLT_GEOGRAPHY, Type.VOLT_GEOGRAPHY_POINT },
doubleParamList),
new FunctionId("numinteriorring", Type.SQL_INTEGER, FUNC_VOLT_POLYGON_NUM_INTERIOR_RINGS, -1,
new Type[] { Type.VOLT_GEOGRAPHY },
singleParamList),
// numinteriorrings is alias of numinteriorring
new FunctionId("numinteriorrings", Type.SQL_INTEGER, FUNC_VOLT_POLYGON_NUM_INTERIOR_RINGS, -1,
new Type[] { Type.VOLT_GEOGRAPHY },
singleParamList),
new FunctionId("numpoints", Type.SQL_INTEGER, FUNC_VOLT_POLYGON_NUM_POINTS, -1,
new Type[] { Type.VOLT_GEOGRAPHY },
singleParamList),
new FunctionId("latitude", Type.SQL_DOUBLE, FUNC_VOLT_POINT_LATITUDE, -1,
new Type[] { Type.VOLT_GEOGRAPHY_POINT },
singleParamList),
new FunctionId("longitude", Type.SQL_DOUBLE, FUNC_VOLT_POINT_LONGITUDE, -1,
new Type[] { Type.VOLT_GEOGRAPHY_POINT },
singleParamList),
new FunctionId("centroid", Type.VOLT_GEOGRAPHY_POINT, FUNC_VOLT_POLYGON_CENTROID, -1,
new Type[] { Type.VOLT_GEOGRAPHY },
singleParamList),
new FunctionId("area", Type.SQL_DOUBLE, FUNC_VOLT_POLYGON_AREA, -1,
new Type[] { Type.VOLT_GEOGRAPHY },
singleParamList),
new FunctionId("distance", Type.SQL_DOUBLE, FUNC_VOLT_DISTANCE, -1,
new Type[] { Type.SQL_ALL_TYPES, Type.SQL_ALL_TYPES },
doubleParamList),
new FunctionId("astext", Type.SQL_VARCHAR, FUNC_VOLT_ASTEXT, -1,
new Type[] { Type.SQL_ALL_TYPES },
singleParamList),
new FunctionId("isvalid", Type.SQL_BOOLEAN, FUNC_VOLT_VALIDATE_POLYGON, -1,
new Type[] { Type.VOLT_GEOGRAPHY },
singleParamList),
new FunctionId("isinvalidreason", Type.SQL_VARCHAR, FUNC_VOLT_POLYGON_INVALID_REASON, -1,
new Type[] { Type.VOLT_GEOGRAPHY },
singleParamList),
new FunctionId("dwithin", Type.SQL_BOOLEAN, FUNC_VOLT_DWITHIN, -1,
new Type[] { Type.SQL_ALL_TYPES, Type.SQL_ALL_TYPES, Type.SQL_DOUBLE },
new short[] { Tokens.OPENBRACKET,
Tokens.QUESTION, Tokens.COMMA,
Tokens.QUESTION, Tokens.COMMA,
Tokens.QUESTION,
Tokens.CLOSEBRACKET }),
new FunctionId("validpolygonfromtext", Type.VOLT_GEOGRAPHY, FUNC_VOLT_VALIDPOLYGONFROMTEXT, -1,
new Type[] { Type.SQL_VARCHAR },
singleParamList),
new FunctionId("min_valid_timestamp", Type.SQL_TIMESTAMP, FUNC_VOLT_MIN_VALID_TIMESTAMP, -1,
new Type[] {},
emptyParamList,
noParamList),
new FunctionId("max_valid_timestamp", Type.SQL_TIMESTAMP, FUNC_VOLT_MAX_VALID_TIMESTAMP, -1,
new Type[] {},
emptyParamList,
noParamList),
new FunctionId("is_valid_timestamp", Type.SQL_BOOLEAN, FUNC_VOLT_IS_VALID_TIMESTAMP, -1,
new Type[] { Type.SQL_TIMESTAMP },
singleParamList),
new FunctionId("inet_ntoa", Type.SQL_VARCHAR, FUNC_INET_NTOA, -1,
new Type[] { Type.SQL_BIGINT },
singleParamList),
new FunctionId("inet_aton", Type.SQL_BIGINT, FUNC_INET_ATON, -1,
new Type[] { Type.SQL_VARCHAR },
singleParamList),
new FunctionId("inet6_aton", Type.SQL_VARBINARY, FUNC_INET6_ATON, -1,
new Type[] { Type.SQL_VARCHAR },
singleParamList),
new FunctionId("inet6_ntoa", Type.SQL_VARCHAR, FUNC_INET6_NTOA, -1,
new Type[] { Type.SQL_VARBINARY },
singleParamList),
};
private static Map<String, FunctionId> by_LC_name = new HashMap<>();
static {
for (FunctionId fn : instances) {
by_LC_name.put(fn.m_name, fn);
}
}
static FunctionId fn_by_name(String anyCase) {
String upCase = anyCase.toLowerCase();
return by_LC_name.get(upCase);
}
public int getTypeParameter() {
return m_typeParameter;
}
}
public static final int FUNC_VOLT_ID_FOR_CONTAINS = FunctionId.FUNC_VOLT_CONTAINS;
private final FunctionId m_def;
public static FunctionSQL newVoltDBFunction(String token, int tokenType) {
FunctionId def = FunctionId.fn_by_name(token);
if (def == null) {
return null;
}
FunctionSQL function = new FunctionForVoltDB(def);
return function;
}
public FunctionForVoltDB(FunctionId fn) {
super();
m_def = fn;
funcType = m_def.getId();
name = m_def.getName();
parseList = m_def.getParamParseList();
parseListAlt = m_def.getParamParseListAlt();
parameterArg = m_def.getTypeParameter();
}
@Override
public void setArguments(Expression[] nodes) {
//TODO; Here's where we might re-order arguments or insert implied values for functions that were implemented as aliases for other functions.
/*
switch (m_def.getId()) {
default :
break;
}
*/
super.setArguments(nodes);
}
@Override
public Expression getFunctionExpression() {
//TODO; Here's where we might substitute wholesale some other HSQL Expression for a function expression that is really just an alias.
/*
switch (m_def.getId()) {
default :
break;
}
*/
return super.getFunctionExpression();
}
@Override
Object getValue(Session session, Object[] data) {
//TODO; Here's where we implement the function for HSQL backends so it can be used for regression testing -- GOOD LUCK!
/*
switch (m_def.getId()) {
default :
break;
}
*/
throw Error.runtimeError(ErrorCode.U_S0500, "This FunctionForVoltDB is not implemented in HSQL backends -- or in HSQL constant-folding.");
}
@Override
public void resolveTypes(Session session, Expression parent) {
Type[] paramTypes = m_def.getParamTypes();
for (int i = 0; i < nodes.length; i++) {
if (nodes[i] != null) {
nodes[i].resolveTypes(session, this);
}
}
switch(m_def.getId()) {
case FunctionId.FUNC_CONCAT:
for (int ii = 0; ii < nodes.length; ii++) {
if (nodes[ii].dataType == null && nodes[ii].isParam) {
nodes[ii].dataType = Type.SQL_VARCHAR;
}
}
break;
/*
* The types to the FIELD functions parameters are VARCHAR
*/
case FunctionId.FUNC_VOLT_FIELD:
if (nodes[0].dataType == null && nodes[0].isParam) {
nodes[0].dataType = Type.SQL_VARCHAR;
}
if (nodes[1].dataType == null && nodes[1].isParam) {
nodes[1].dataType = Type.SQL_VARCHAR;
}
break;
/*
* Infer parameter types to make the types of the 1st, 2nd, and (if not the last) 4th, 6th, etc.
* arguments to DECODE as consistent as possible,
* and the types of the 3rd, 5th, 7th, etc. and LAST arguments as consistent as possible.
* Punt to inferring VARCHAR if the other arguments give no clue or are inconsistent
* -- the VoltDB EE complains about NULL-typed parameters but is somewhat forgiving about
* mixed argument types.
*/
case FunctionId.FUNC_VOLT_DECODE:
// Track whether parameter type hinting is needed for either key or value arguments.
// For simplicity(?), parameters are not tracked explicitly (by position)
// or even by category (key vs. value). So, if any parameter hinting is required at all,
// all arguments get re-checked.
boolean needParamType = false;
Type inputTypeInferred = null;
Type resultTypeInferred = null;
for (int ii = 0; ii < nodes.length; ii++) {
Type argType = nodes[ii].dataType;
if (argType == null) {
// A param here means work to do, below.
if (nodes[ii].isParam || nodes[ii].valueData == null) {
needParamType = true;
}
continue;
}
// Except for the first and the optional last/"default" argument,
// the arguments alternate between candidate inputs and candidate results.
if ((((ii % 2) == 0) || ii == nodes.length-1) && (ii != 0)) {
// These arguments represent candidate result values
// that may hint at the result type or require hinting from the other result values.
if (resultTypeInferred == null) {
resultTypeInferred = argType; // Take the first result type hint.
} else if (resultTypeInferred.typeComparisonGroup != argType.typeComparisonGroup) {
resultTypeInferred = Type.SQL_VARCHAR; // Discard contradictory hints.
}
} else {
// These arguments represent candidate input keys
// that may hint at the input type or may require hinting from the other input keys.
if (inputTypeInferred == null) {
inputTypeInferred = argType; // Take the first input type hint.
} else if (inputTypeInferred.typeComparisonGroup != argType.typeComparisonGroup) {
inputTypeInferred = Type.SQL_VARCHAR; // Discard contradictory hints, falling back to string type.
}
}
}
// With any luck, there are no parameter "?" arguments to worry about.
if ( ! needParamType) {
break;
}
// No luck, try to infer the parameters' types.
// Punt to guessing VARCHAR for lack of better information.
if (inputTypeInferred == null) {
inputTypeInferred = Type.SQL_VARCHAR;
}
if (resultTypeInferred == null) {
resultTypeInferred = Type.SQL_VARCHAR;
}
for (int ii = 0; ii < nodes.length; ii++) {
Type argType = nodes[ii].dataType;
if ((argType != null) || ! (nodes[ii].isParam || nodes[ii].valueData == null)) {
continue;
}
// This is the same test as above for determining that the argument
// is a candidate result vs. a candidate input.
if ((((ii % 2) == 0) || ii == nodes.length-1) && (ii != 0)) {
nodes[ii].dataType = resultTypeInferred;
} else {
nodes[ii].dataType = inputTypeInferred;
}
}
break;
case FunctionId.FUNC_VOLT_BITNOT:
voltResolveToBigintTypesForBitwise();
break;
case FunctionId.FUNC_VOLT_BIT_SHIFT_LEFT:
case FunctionId.FUNC_VOLT_BIT_SHIFT_RIGHT:
// the first parameter has to be BigInteger
voltResolveToBigintType(0);
voltResolveToBigintCompatibleType(1);
dataType = Type.SQL_BIGINT;
break;
case FunctionId.FUNC_VOLT_HEX:
case FunctionId.FUNC_VOLT_BIN:
voltResolveToBigintType(0);
dataType = Type.SQL_VARCHAR;
break;
case FunctionId.FUNC_VOLT_DISTANCE:
// validate the types of argument is valid
if (nodes[0].dataType == null || nodes[1].dataType == null) {
// "data type cast needed for parameter or null literal"
throw Error.error(ErrorCode.X_42567,
"input type to DISTANCE function is ambiguous");
}
else if ((!nodes[0].dataType.isGeographyType() && !nodes[0].dataType.isGeographyPointType()) ||
(!nodes[1].dataType.isGeographyType() && !nodes[1].dataType.isGeographyPointType())) {
// either of the nodes is not a valid type
throw Error.error(ErrorCode.X_42565,
"The DISTANCE function computes distances between POINT-to-POINT, POINT-to-POLYGON " +
"and POLYGON-to-POINT only.");
}
else if (nodes[0].dataType.isGeographyType() && nodes[1].dataType.isGeographyType()) {
// distance between two polygons is not supported, flag as an error
throw Error.error(ErrorCode.X_42565, "DISTANCE between two POLYGONS not supported");
}
else if (nodes[0].dataType.isGeographyPointType() && nodes[1].dataType.isGeographyType()) {
// distance between polygon-to-point and point-to-polygon is symmetric.
// So, update the the expression for distance between point and polygon to
// distance between polygon and point. This simplifies the logic and have to
// handle only one case: polygon-to-point instead of two
Expression tempNode = nodes[0];
nodes[0] = nodes[1];
nodes[1] = tempNode;
}
break;
case FunctionId.FUNC_VOLT_DWITHIN:
if (nodes[0].dataType == null || nodes[1].dataType == null) {
// "data type cast needed for parameter or null literal"
throw Error.error(ErrorCode.X_42567,
"input type to DWITHIN function is ambiguous");
}
else if ((!nodes[0].dataType.isGeographyType() && !nodes[0].dataType.isGeographyPointType()) ||
(!nodes[1].dataType.isGeographyType() && !nodes[1].dataType.isGeographyPointType())) {
// first and second argument should be geography type
throw Error.error(ErrorCode.X_42565,
"DWITHIN function evaulates if geographies are within specified distance of one-another for "
+ "POINT-to-POINT, POINT-to-POLYGON and POLYGON-to-POINT geographies only.");
}
else if (nodes[0].dataType.isGeographyType() && nodes[1].dataType.isGeographyType()) {
// "incompatible data type in operation"
// distance between two polygons is not supported, flag as an error
throw Error.error(ErrorCode.X_42565, "DWITHIN between two POLYGONS not supported");
}
else if (nodes[0].dataType.isGeographyPointType() && nodes[1].dataType.isGeographyType()) {
// Distance between polygon-to-point and point-to-polygon is symmetric. Update the
// expression for DWITHIN between point and polygon to distance between polygon
// and point. This consolidates logic to one case: polygon-to-point
Expression tempNode = nodes[0];
nodes[0] = nodes[1];
nodes[1] = tempNode;
}
if ((nodes[2].dataType != null) &&
(!nodes[2].dataType.isNumberType())) {
// "incompatible data type in operation"
throw Error.error(ErrorCode.X_42565,
"input type DISTANCE to DWITHIN function must be non-negative numeric value");
}
break;
case FunctionId.FUNC_VOLT_ASTEXT:
if (nodes[0].dataType == null) {
// "data type cast needed for parameter or null literal"
throw Error.error(ErrorCode.X_42567,
"input type to ASTEXT function is ambiguous");
}
if (! (nodes[0].dataType.isGeographyPointType() || nodes[0].dataType.isGeographyType())) {
// "incompatible data type in operation"
throw Error.error(ErrorCode.X_42565,
"The asText function accepts only GEOGRAPHY and GEOGRAPHY_POINT types.");
}
break;
// our networking specified functions
case FunctionId.FUNC_INET_NTOA:
if (nodes[0].dataType != null &&
!nodes[0].dataType.isNumberType()) {
throw Error.error(ErrorCode.X_42561);
}
dataType = Type.SQL_VARCHAR;
break;
case FunctionId.FUNC_INET_ATON:
if (nodes[0].dataType != null &&
!nodes[0].dataType.isCharacterType()) {
throw Error.error(ErrorCode.X_42561);
}
dataType = Type.SQL_BIGINT;
break;
case FunctionId.FUNC_INET6_ATON:
if (nodes[0].dataType != null &&
!nodes[0].dataType.isCharacterType()) {
throw Error.error(ErrorCode.X_42561);
}
dataType = Type.SQL_VARBINARY;
break;
case FunctionId.FUNC_INET6_NTOA:
if (nodes[0].dataType != null &&
!nodes[0].dataType.isBinaryType()) {
throw Error.error(ErrorCode.X_42561);
}
dataType = Type.SQL_VARCHAR;
break;
default:
break;
}
for (int i = 0; i < nodes.length; i++) {
if (nodes[i] != null) {
if (i >= paramTypes.length) {
// TODO support type checking for variadic functions
break;
}
if (paramTypes[i] == null) {
continue; // accept all argument types
}
if (nodes[i].dataType == null) {
// assert that the ambiguous argument (e.g. '?' parameter) has the required type
nodes[i].dataType = paramTypes[i];
continue;
}
else if (paramTypes[i].canConvertFrom(nodes[i].dataType)) {
// Add support to pass in a JDBC time string constant
if (paramTypes[i].isDateTimeType() && nodes[i].dataType.isCharacterType()) {
String datetimestring = (String) nodes[i].valueData;
if (datetimestring != null) {
datetimestring = datetimestring.trim();
try {
Timestamp.valueOf(datetimestring);
}
catch (Exception e) {
throw Error.error(ErrorCode.X_42561);
}
nodes[i].valueData = paramTypes[i].castToType(session, nodes[i].valueData, nodes[i].dataType);
nodes[i].dataType = paramTypes[i];
}
} else if (paramTypes[i].isNumberType() && !nodes[i].dataType.isNumberType()) {
throw Error.error(ErrorCode.X_42565);
}
continue; // accept compatible argument types
}
throw Error.error(ErrorCode.X_42565); // incompatible data type
}
}
dataType = m_def.getDataType();
if (dataType == null && nodes.length > 0) {
if (parameterArg < 0 || parameterArg >= nodes.length) {
throw Error.error(ErrorCode.X_42565); // incompatible data type (so says the error -- we're missing one, actually)
}
Expression like_child = nodes[parameterArg];
if (like_child != null) {
dataType = like_child.dataType;
}
}
}
@Override
public String getSQL() {
StringBuffer sb = new StringBuffer();
sb.append(m_def.getName()).append(Tokens.T_OPENBRACKET);
switch (m_def.getId()) {
case FunctionId.FUNC_VOLT_SINCE_EPOCH:
case FunctionId.FUNC_VOLT_TO_TIMESTAMP:
case FunctionId.FUNC_VOLT_TRUNCATE_TIMESTAMP: {
int timeUnit = ((Number) nodes[0].valueData).intValue();
sb.append(Tokens.getKeyword(timeUnit));
break;
}
default:
// If this is a nullary function, we don't want to
// crash here.
if (0 < nodes.length) {
sb.append(nodes[0].getSQL());
}
break;
}
for (int ii = 1; ii < nodes.length; ii++) {
if (nodes[ii] != null) {
sb.append(Tokens.T_COMMA).append(nodes[ii].getSQL());
}
else {
// Some functions, like regexp_position, have optional parameters.
// The omitted optional parameters appear as a null in the function's
// node list. This null-preserving behavior seems to be intentional in
// ParserSQL.readExpression, for reasons that are unclear to me.
// --cwolff, December 2015.
}
}
sb.append(Tokens.T_CLOSEBRACKET);
return sb.toString();
}
}