/*
// Licensed to DynamoBI Corporation (DynamoBI) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. DynamoBI 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.eigenbase.rex;
import java.math.*;
import java.nio.*;
import java.util.*;
import org.eigenbase.rel.*;
import org.eigenbase.reltype.*;
import org.eigenbase.sql.*;
import org.eigenbase.sql.SqlIntervalQualifier.TimeUnit;
import org.eigenbase.sql.fun.*;
import org.eigenbase.sql.type.*;
import org.eigenbase.util.*;
/**
* Factory for row expressions.
*
* <p>Some common literal values (NULL, TRUE, FALSE, 0, 1, '') are cached.</p>
*
* @author jhyde
* @version $Id$
* @since Nov 23, 2003
*/
public class RexBuilder
{
//~ Instance fields --------------------------------------------------------
protected final RelDataTypeFactory typeFactory;
private final RexLiteral booleanTrue;
private final RexLiteral booleanFalse;
private final RexLiteral charEmpty;
private final RexLiteral constantNull;
private final SqlStdOperatorTable opTab = SqlStdOperatorTable.instance();
//~ Constructors -----------------------------------------------------------
/**
* Creates a RexBuilder.
*
* @param typeFactory Type factory
*/
public RexBuilder(RelDataTypeFactory typeFactory)
{
this.typeFactory = typeFactory;
this.booleanTrue =
makeLiteral(
Boolean.TRUE,
typeFactory.createSqlType(SqlTypeName.BOOLEAN),
SqlTypeName.BOOLEAN);
this.booleanFalse =
makeLiteral(
Boolean.FALSE,
typeFactory.createSqlType(SqlTypeName.BOOLEAN),
SqlTypeName.BOOLEAN);
this.charEmpty =
makeLiteral(
new NlsString("", null, null),
typeFactory.createSqlType(SqlTypeName.CHAR, 0),
SqlTypeName.CHAR);
this.constantNull =
makeLiteral(
null,
typeFactory.createSqlType(SqlTypeName.NULL),
SqlTypeName.NULL);
}
//~ Methods ----------------------------------------------------------------
/**
* Returns this RexBuilder's type factory
*
* @return type factory
*/
public RelDataTypeFactory getTypeFactory()
{
return typeFactory;
}
/**
* Returns this RexBuilder's operator table
*
* @return operator table
*/
public SqlStdOperatorTable getOpTab()
{
return opTab;
}
/**
* Creates an expression accessing a given named field from a record.
*
* @param expr Expression yielding a record
* @param fieldName Name of field in record
*
* @return Expression accessing a given named field
*/
public RexNode makeFieldAccess(
RexNode expr,
String fieldName)
{
final RelDataType type = expr.getType();
final RelDataTypeField field = type.getField(fieldName);
if (field == null) {
throw Util.newInternal(
"Type '" + type + "' has no field '"
+ fieldName + "'");
}
return makeFieldAccessInternal(expr, field);
}
/**
* Creates an expression accessing a field with a given ordinal from a
* record.
*
* @param expr Expression yielding a record
* @param i Ordinal of field
*
* @return Expression accessing given field
*/
public RexNode makeFieldAccess(
RexNode expr,
int i)
{
final RelDataType type = expr.getType();
final RelDataTypeField [] fields = type.getFields();
if ((i < 0) || (i >= fields.length)) {
throw Util.newInternal(
"Field ordinal " + i + " is invalid for "
+ " type '" + type + "'");
}
return makeFieldAccessInternal(expr, fields[i]);
}
/**
* Creates an expression accessing a given field from a record.
*
* @param expr Expression yielding a record
* @param field Field
*
* @return Expression accessing given field
*/
private RexNode makeFieldAccessInternal(
RexNode expr,
final RelDataTypeField field)
{
if (expr instanceof RexRangeRef) {
RexRangeRef range = (RexRangeRef) expr;
return new RexInputRef(
range.getOffset() + field.getIndex(),
field.getType());
}
return new RexFieldAccess(expr, field);
}
/**
* Creates a call with an array of arguments and a predetermined type.
*/
public RexNode makeCall(
RelDataType returnType,
SqlOperator op,
RexNode ... exprs)
{
return new RexCall(
returnType,
op,
exprs);
}
/**
* Creates a call with an array of arguments.
*
* <p>This is the fundamental method called by all of the other <code>
* makeCall</code> methods. If you derive a class from {@link RexBuilder},
* this is the only method you need to override.</p>
*/
public RexNode makeCall(
SqlOperator op,
RexNode ... exprs)
{
// TODO jvs 12-Jun-2010: Find a better place for this;
// it surely does not belong here.
if (op == SqlStdOperatorTable.andOperator
&& exprs.length == 2
&& exprs[0].equals(exprs[1]))
{
// Avoid generating 'AND(x, x)'; this can cause plan explosions if a
// relnode is its own child and is merged with itself.
return exprs[0];
}
final RelDataType type = deriveReturnType(op, typeFactory, exprs);
RexNode [] fixExprs = exprs;
// REVIEW: angel 2006-08-27 Commented out the following section
// piece of code, not sure what its purpose is, but it was causing
// errors
/*
if (type instanceof IntervalSqlType) {
//if (op instanceof SqlDatetimeSubtractionOperator) {
// op = SqlStdOperatorTable.minusOperator;
//}
int count = 0;
for (int i = 0; i < exprs.length; i++) {
if ((exprs[i] instanceof RexLiteral)
&& (exprs[i].getType() instanceof IntervalSqlType)
&& (exprs[i].getType().getIntervalQualifier() != null)) {
exprs[i] = null;
continue;
}
count++;
}
fixExprs = new RexNode[count];
for (int i = 0; i < exprs.length; i++) {
if (exprs[i] == null) {
continue;
}
fixExprs[i] = exprs[i];
}
} */
return new RexCall(type, op, fixExprs);
}
/**
* Creates a call with a list of arguments.
*
* <p>Equivalent to <code>makeCall(op, exprList.toArray(new
* RexNode[exprList.size()]))</code>.
*/
public final RexNode makeCall(
SqlOperator op,
List<? extends RexNode> exprList)
{
return makeCall(op, exprList.toArray(new RexNode[exprList.size()]));
}
/**
* Derives the return type of a call to an operator.
*
* @param op the operator being called
* @param typeFactory factory for return type
* @param exprs actual operands
*
* @return derived type
*/
public RelDataType deriveReturnType(
SqlOperator op,
RelDataTypeFactory typeFactory,
RexNode [] exprs)
{
return op.inferReturnType(new RexCallBinding(typeFactory, op, exprs));
}
/**
* Creates a reference to an aggregate call, checking for repeated calls.
*/
public RexNode addAggCall(
AggregateCall aggCall,
int groupCount,
List<AggregateCall> aggCalls,
Map<AggregateCall, RexNode> aggCallMapping)
{
RexNode rex = aggCallMapping.get(aggCall);
if (rex == null) {
int index = aggCalls.size() + groupCount;
aggCalls.add(aggCall);
rex = makeInputRef(aggCall.getType(), index);
aggCallMapping.put(aggCall, rex);
}
return rex;
}
/**
* Creates a call to a windowed agg.
*/
public RexNode makeOver(
RelDataType type,
SqlAggFunction operator,
RexNode [] exprs,
RexNode [] partitionKeys,
RexNode [] orderKeys,
SqlNode lowerBound,
SqlNode upperBound,
boolean physical,
boolean allowPartial,
boolean nullWhenCountZero)
{
assert operator != null;
assert exprs != null;
assert partitionKeys != null;
assert orderKeys != null;
final RexWindow window =
makeWindow(
partitionKeys,
orderKeys,
lowerBound,
upperBound,
physical);
final RexOver over = new RexOver(type, operator, exprs, window);
RexNode result = over;
// This should be correct but need time to go over test results.
// Also want to look at combing with section below.
if (nullWhenCountZero) {
final RelDataType bigintType = getTypeFactory().createSqlType(
SqlTypeName.BIGINT);
result = makeCall(
SqlStdOperatorTable.caseOperator,
makeCall(
SqlStdOperatorTable.greaterThanOperator,
new RexOver(
bigintType,
SqlStdOperatorTable.countOperator,
exprs,
window),
makeLiteral(
new BigDecimal(0),
bigintType,
SqlTypeName.DECIMAL)),
over,
makeCast(over.getType(), constantNull()));
}
if (!allowPartial) {
Util.permAssert(physical, "DISALLOW PARTIAL over RANGE");
final RelDataType bigintType = getTypeFactory().createSqlType(
SqlTypeName.BIGINT);
// todo: read bound
result =
makeCall(
SqlStdOperatorTable.caseOperator,
makeCall(
SqlStdOperatorTable.greaterThanOrEqualOperator,
new RexOver(
bigintType,
SqlStdOperatorTable.countOperator,
RexNode.EMPTY_ARRAY,
window),
makeLiteral(
new BigDecimal(2),
bigintType,
SqlTypeName.DECIMAL)),
result,
constantNull);
}
return result;
}
/**
* Creates a window specification.
*
* @param partitionKeys Partition keys
* @param orderKeys Order keys
* @param lowerBound Lower bound
* @param upperBound Upper bound
* @param physical Whether physical. True if row-based, false if range-based
* @return window specification
*/
public RexWindow makeWindow(
RexNode[] partitionKeys,
RexNode[] orderKeys,
SqlNode lowerBound,
SqlNode upperBound,
boolean physical)
{
return new RexWindow(
partitionKeys,
orderKeys,
lowerBound,
upperBound,
physical);
}
/**
* Creates a constant for the SQL <code>NULL</code> value.
*/
public RexLiteral constantNull()
{
return constantNull;
}
/**
* Creates an expression referencing a correlation variable.
*
* @param type Type of variable
* @param name Name of variable
*
* @return Correlation variable
*/
public RexNode makeCorrel(
RelDataType type,
String name)
{
return new RexCorrelVariable(name, type);
}
/**
* Creates an invocation of the NEW operator.
*
* @param type Type to be instantiated
* @param exprs Arguments to NEW operator
*
* @return Expression invoking NEW operator
*/
public RexNode makeNewInvocation(
RelDataType type,
RexNode [] exprs)
{
return new RexCall(
type,
SqlStdOperatorTable.newOperator,
exprs);
}
/**
* Creates a call to the CAST operator, expanding if possible.
*
* @param type Type to cast to
* @param exp Expression being cast
*
* @return Call to CAST operator
*/
public RexNode makeCast(
RelDataType type,
RexNode exp)
{
if (SqlTypeUtil.isInterval(type)
&& SqlTypeUtil.isExactNumeric(exp.getType()))
{
return makeCastExactToInterval(type, exp);
} else if (SqlTypeUtil.isExactNumeric(type)
&& SqlTypeUtil.isInterval(exp.getType()))
{
return makeCastIntervalToExact(type, exp);
}
return makeAbstractCast(type, exp);
}
private RexNode makeCastIntervalToExact(RelDataType toType, RexNode exp)
{
IntervalSqlType intervalType = (IntervalSqlType) exp.getType();
TimeUnit endUnit = intervalType.getIntervalQualifier().getEndUnit();
if (endUnit == null) {
endUnit = intervalType.getIntervalQualifier().getStartUnit();
}
int scale = 0;
if (endUnit == TimeUnit.SECOND) {
scale = Math.min(
intervalType.getIntervalQualifier()
.getFractionalSecondPrecision(), 3);
}
BigDecimal multiplier = BigDecimal.valueOf(endUnit.multiplier)
.divide(BigDecimal.TEN.pow(scale));
RexNode value = decodeIntervalOrDecimal(exp);
if (multiplier.longValue() != 1) {
value = makeCall(
SqlStdOperatorTable.divideIntegerOperator,
value, makeBigintLiteral(multiplier));
}
if (scale > 0) {
RelDataType decimalType =
getTypeFactory().createSqlType(
SqlTypeName.DECIMAL,
scale + intervalType.getPrecision(),
scale);
value = encodeIntervalOrDecimal(value, decimalType, false);
}
return ensureType(toType, value, false);
}
private RexNode makeCastExactToInterval(RelDataType toType, RexNode exp)
{
IntervalSqlType intervalType = (IntervalSqlType) toType;
TimeUnit endUnit = intervalType.getIntervalQualifier().getEndUnit();
if (endUnit == null) {
endUnit = intervalType.getIntervalQualifier().getStartUnit();
}
int scale = 0;
if (endUnit == TimeUnit.SECOND) {
scale = Math.min(
intervalType.getIntervalQualifier()
.getFractionalSecondPrecision(), 3);
}
BigDecimal multiplier = BigDecimal.valueOf(endUnit.multiplier)
.divide(BigDecimal.TEN.pow(scale));
RelDataType decimalType =
getTypeFactory().createSqlType(
SqlTypeName.DECIMAL,
scale + intervalType.getPrecision(),
scale);
RexNode value = decodeIntervalOrDecimal(
ensureType(decimalType, exp, true));
if (multiplier.longValue() != 1) {
value = makeCall(
SqlStdOperatorTable.multiplyOperator,
value, makeExactLiteral(multiplier));
}
return encodeIntervalOrDecimal(value, toType, false);
}
/**
* Casts a decimal's integer representation to a decimal node. If the
* expression is not the expected integer type, then it is casted first.
*
* <p>An overflow check may be requested to ensure the internal value
* does not exceed the maximum value of the decimal type.
*
* @param value integer representation of decimal
* @param type type integer will be reinterpreted as
* @param checkOverflow indicates whether an overflow check is required
* when reinterpreting this particular value as the decimal type. A
* check usually not required for arithmetic, but is often required for
* rounding and explicit casts.
*
* @return the integer reinterpreted as an opaque decimal type
*/
public RexNode encodeIntervalOrDecimal(
RexNode value,
RelDataType type,
boolean checkOverflow)
{
RelDataType bigintType =
typeFactory.createSqlType(
SqlTypeName.BIGINT);
RexNode cast = ensureType(bigintType, value, true);
return makeReinterpretCast(
type,
cast,
makeLiteral(checkOverflow));
}
/**
* Retrieves an interval or decimal node's integer representation
*
* @param node the interval or decimal value as an opaque type
*
* @return an integer representation of the decimal value
*/
public RexNode decodeIntervalOrDecimal(RexNode node)
{
assert (SqlTypeUtil.isDecimal(node.getType())
|| SqlTypeUtil.isInterval(node.getType()));
RelDataType bigintType =
typeFactory.createSqlType(
SqlTypeName.BIGINT);
return makeReinterpretCast(
matchNullability(bigintType, node),
node,
makeLiteral(false));
}
/**
* Creates a call to the CAST operator.
*
* @param type Type to cast to
* @param exp Expression being cast
*
* @return Call to CAST operator
*/
public RexNode makeAbstractCast(
RelDataType type,
RexNode exp)
{
return new RexCall(
type,
SqlStdOperatorTable.castFunc,
new RexNode[] { exp });
}
/**
* Makes a reinterpret cast.
*
* @param type type returned by the cast
* @param exp expression to be casted
* @param checkOverflow whether an overflow check is required
*
* @return a RexCall with two operands and a special return type
*/
public RexNode makeReinterpretCast(
RelDataType type,
RexNode exp,
RexNode checkOverflow)
{
RexNode [] args;
if ((checkOverflow != null) && checkOverflow.isAlwaysTrue()) {
args = new RexNode[] { exp, checkOverflow };
} else {
args = new RexNode[] { exp };
}
return new RexCall(
type,
SqlStdOperatorTable.reinterpretOperator,
args);
}
/**
* Makes an expression which converts a value of type T to a value of type T
* NOT NULL, or throws if the value is NULL. If the expression is already
* NOT NULL, does nothing.
*/
public RexNode makeNotNullCast(RexNode expr)
{
RelDataType type = expr.getType();
if (!type.isNullable()) {
return expr;
}
RelDataType typeNotNull =
getTypeFactory().createTypeWithNullability(type, false);
return new RexCall(
typeNotNull,
SqlStdOperatorTable.castFunc,
new RexNode[] {
expr
});
}
/**
* Creates a reference to all the fields in the row. That is, the whole row
* as a single record object.
*
* @param rowType Type of the input row.
*/
public RexNode makeRangeReference(RelDataType rowType)
{
return new RexRangeRef(rowType, 0);
}
/**
* Creates a reference to all the fields in the row.
*
* <p>For example, if the input row has type <code>T{f0,f1,f2,f3,f4}</code>
* then <code>makeRangeReference(T{f0,f1,f2,f3,f4}, S{f3,f4}, 3)</code> is
* an expression which yields the last 2 fields.
*
* @param type Type of the resulting range record.
* @param offset Index of first field.
* @param nullable Whether the record is nullable.
*/
public RexNode makeRangeReference(
RelDataType type,
int offset,
boolean nullable)
{
if (nullable && !type.isNullable()) {
type =
typeFactory.createTypeWithNullability(
type,
nullable);
}
return new RexRangeRef(type, offset);
}
/**
* Creates a reference to a given field of the input record.
*
* @param type Type of field
* @param i Ordinal of field
*
* @return Reference to field
*/
public RexNode makeInputRef(
RelDataType type,
int i)
{
type = SqlTypeUtil.addCharsetAndCollation(type, typeFactory);
return new RexInputRef(i, type);
}
/**
* Creates a literal representing a flag.
*
* @param flag Flag value; must be either a {@link
* org.eigenbase.util14.Enum14.Value} or a {@link Enum}, and hence a {@link
* Comparable}.
*/
public RexLiteral makeFlag(
Object flag)
{
assert flag != null;
assert (flag instanceof EnumeratedValues.Value)
|| (flag instanceof Enum);
assert flag instanceof Comparable;
return makeLiteral(
(Comparable) flag,
typeFactory.createSqlType(SqlTypeName.SYMBOL),
SqlTypeName.SYMBOL);
}
/**
* Internal method to create a call to a literal. Code outside this package
* should call one of the type-specific methods such as {@link
* #makeDateLiteral(Calendar)}, {@link #makeLiteral(boolean)}, {@link
* #makeLiteral(String)}.
*
* @param o Value of literal, must be appropriate for the type
* @param type Type of literal
* @param typeName SQL type of literal
*
* @return Literal
*/
protected RexLiteral makeLiteral(
Comparable o,
RelDataType type,
SqlTypeName typeName)
{
// All literals except NULL have NOT NULL types.
type = typeFactory.createTypeWithNullability(type, o == null);
if (typeName == SqlTypeName.CHAR) {
// Character literals must have a charset and collation. Populate
// from the type if necessary.
NlsString nlsString = (NlsString) o;
if ((nlsString.getCollation() == null)
|| (nlsString.getCharset() == null))
{
assert type.getSqlTypeName() == SqlTypeName.CHAR;
assert type.getCharset().name() != null;
assert type.getCollation() != null;
o = new NlsString(
nlsString.getValue(),
type.getCharset().name(),
type.getCollation());
}
}
return new RexLiteral(o, type, typeName);
}
/**
* Creates a boolean literal.
*/
public RexLiteral makeLiteral(boolean b)
{
return b ? booleanTrue : booleanFalse;
}
/**
* Creates a numeric literal.
*/
public RexLiteral makeExactLiteral(BigDecimal bd)
{
RelDataType relType;
int scale = bd.scale();
long l = bd.unscaledValue().longValue();
assert ((scale >= 0) && (scale <= SqlTypeName.MAX_NUMERIC_SCALE));
assert (BigDecimal.valueOf(l, scale).equals(bd));
if (scale == 0) {
if ((l >= Integer.MIN_VALUE) && (l <= Integer.MAX_VALUE)) {
relType = typeFactory.createSqlType(SqlTypeName.INTEGER);
} else {
relType = typeFactory.createSqlType(SqlTypeName.BIGINT);
}
} else {
int precision = bd.unscaledValue().toString().length();
relType =
typeFactory.createSqlType(
SqlTypeName.DECIMAL,
scale,
precision);
}
return makeExactLiteral(bd, relType);
}
/**
* Creates a BIGINT literal.
*/
public RexLiteral makeBigintLiteral(BigDecimal bd)
{
RelDataType bigintType =
typeFactory.createSqlType(
SqlTypeName.BIGINT);
return makeLiteral(bd, bigintType, SqlTypeName.DECIMAL);
}
/**
* Creates a numeric literal.
*/
public RexLiteral makeExactLiteral(BigDecimal bd, RelDataType type)
{
return makeLiteral(
bd,
type,
SqlTypeName.DECIMAL);
}
/**
* Creates a byte array literal.
*/
public RexLiteral makeBinaryLiteral(byte [] byteArray)
{
return makeLiteral(
ByteBuffer.wrap(byteArray),
typeFactory.createSqlType(
SqlTypeName.BINARY,
byteArray.length),
SqlTypeName.BINARY);
}
/**
* Creates a double-precision literal.
*/
public RexLiteral makeApproxLiteral(BigDecimal bd)
{
// Validator should catch if underflow is allowed
// If underflow is allowed, let underflow become zero
if (bd.doubleValue() == 0) {
bd = BigDecimal.ZERO;
}
return makeApproxLiteral(
bd,
typeFactory.createSqlType(SqlTypeName.DOUBLE));
}
/**
* Creates an approximate numeric literal (double or float).
*
* @param bd literal value
* @param type approximate numeric type
*
* @return new literal
*/
public RexLiteral makeApproxLiteral(BigDecimal bd, RelDataType type)
{
assert (SqlTypeFamily.APPROXIMATE_NUMERIC.getTypeNames().contains(
type.getSqlTypeName()));
return makeLiteral(
bd,
type,
SqlTypeName.DOUBLE);
}
/**
* Creates a character string literal.
*
* @pre s != null
*/
public RexLiteral makeLiteral(String s)
{
return makePreciseStringLiteral(s);
}
/**
* Creates a character string literal with type CHAR and default charset and
* collation.
*
* @param s String value
*
* @return Character string literal
*/
protected RexLiteral makePreciseStringLiteral(String s)
{
Util.pre(s != null, "s != null");
if (s.equals("")) {
return charEmpty;
} else {
return makeLiteral(
new NlsString(s, null, null),
typeFactory.createSqlType(
SqlTypeName.CHAR,
s.length()),
SqlTypeName.CHAR);
}
}
/**
* Ensures expression is interpreted as a specified type. The returned
* expression may be wrapped with a cast.
*
* @param type desired type
* @param node expression
* @param matchNullability whether to correct nullability of specified
* type to match the expression; this usually should be true, except for
* explicit casts which can override default nullability
*
* @return a casted expression or the original expression
*/
public RexNode ensureType(
RelDataType type,
RexNode node,
boolean matchNullability)
{
RelDataType targetType = type;
if (matchNullability) {
targetType = matchNullability(type, node);
}
if (node.getType() != targetType) {
return makeCast(targetType, node);
}
return node;
}
/**
* Ensure's type's nullability matches a value's nullability
*/
public RelDataType matchNullability(
RelDataType type,
RexNode value)
{
boolean typeNullability = type.isNullable();
boolean valueNullability = value.getType().isNullable();
if (typeNullability != valueNullability) {
return getTypeFactory().createTypeWithNullability(
type,
valueNullability);
}
return type;
}
/**
* Creates a character string literal from an {@link NlsString}.
*
* <p>If the string's charset and collation are not set, uses the system
* defaults.
*
* @pre str != null
*/
public RexLiteral makeCharLiteral(NlsString str)
{
Util.pre(str != null, "str != null");
RelDataType type = SqlUtil.createNlsStringType(typeFactory, str);
return makeLiteral(str, type, SqlTypeName.CHAR);
}
/**
* Creates a Date literal.
*
* @pre date != null
*/
public RexLiteral makeDateLiteral(Calendar date)
{
Util.pre(date != null, "date != null");
return makeLiteral(
date,
typeFactory.createSqlType(SqlTypeName.DATE),
SqlTypeName.DATE);
}
/**
* Creates a Time literal.
*
* @pre time != null
*/
public RexLiteral makeTimeLiteral(
Calendar time,
int precision)
{
Util.pre(time != null, "time != null");
return makeLiteral(
time,
typeFactory.createSqlType(SqlTypeName.TIME, precision),
SqlTypeName.TIME);
}
/**
* Creates a Timestamp literal.
*
* @pre timestamp != null
*/
public RexLiteral makeTimestampLiteral(
Calendar timestamp,
int precision)
{
Util.pre(timestamp != null, "timestamp != null");
return makeLiteral(
timestamp,
typeFactory.createSqlType(SqlTypeName.TIMESTAMP, precision),
SqlTypeName.TIMESTAMP);
}
/**
* Creates an interval literal.
*/
public RexLiteral makeIntervalLiteral(
SqlIntervalQualifier intervalQualifier)
{
Util.pre(intervalQualifier != null, "intervalQualifier != null");
return makeLiteral(
null,
typeFactory.createSqlIntervalType(intervalQualifier),
intervalQualifier.isYearMonth() ? SqlTypeName.INTERVAL_YEAR_MONTH
: SqlTypeName.INTERVAL_DAY_TIME);
}
/**
* Creates an interval literal.
*/
public RexLiteral makeIntervalLiteral(
long l,
SqlIntervalQualifier intervalQualifier)
{
return makeLiteral(
new BigDecimal(l),
typeFactory.createSqlIntervalType(intervalQualifier),
intervalQualifier.isYearMonth() ? SqlTypeName.INTERVAL_YEAR_MONTH
: SqlTypeName.INTERVAL_DAY_TIME);
}
/**
* Creates a reference to a dynamic parameter
*
* @param type Type of dynamic parameter
* @param index Index of dynamic parameter
*
* @return Expression referencing dynamic parameter
*/
public RexDynamicParam makeDynamicParam(
RelDataType type,
int index)
{
return new RexDynamicParam(type, index);
}
/**
* Creates an expression corresponding to a null literal, cast to a specific
* type and precision
*
* @param typeName name of the type that the null will be cast to
* @param precision precision of the type
*
* @return created expression
*/
public RexNode makeNullLiteral(SqlTypeName typeName, int precision)
{
RelDataType type =
typeFactory.createTypeWithNullability(
typeFactory.createSqlType(typeName, precision),
true);
return makeCast(
type,
constantNull());
}
/**
* Creates a literal whose value is NULL, with a particular type.
*
* <p>The typing is necessary because RexNodes are strictly typed. For
* example, in the Rex world the <code>NULL</code> parameter to <code>
* SUBSTRING(NULL FROM 2 FOR 4)</code> must have a valid VARCHAR type so
* that the result type can be determined.
*
* @param typeName Type to cast NULL to
*
* @return NULL literal of given type
*/
public RexNode makeNullLiteral(SqlTypeName typeName)
{
RelDataType type =
typeFactory.createTypeWithNullability(
typeFactory.createSqlType(typeName),
true);
return makeCast(
type,
constantNull());
}
/**
* Creates a copy of an expression, which may have been created using a
* different RexBuilder and/or {@link RelDataTypeFactory}, using this
* RexBuilder.
*
* @param expr Expression
*
* @return Copy of expression
*
* @see RelDataTypeFactory#copyType(RelDataType)
*/
public RexNode copy(RexNode expr)
{
return expr.accept(new RexCopier(this));
}
}
// End RexBuilder.java