in exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillOptiq.java [551:783]
private LogicalExpression getDrillFunctionFromOptiqCall(RexCall call) {
List<LogicalExpression> args = new ArrayList<>();
for(RexNode n : call.getOperands()){
args.add(n.accept(this));
}
int argsSize = args.size();
String functionName = call.getOperator().getName().toLowerCase();
// TODO: once we have more function rewrites and a patter emerges from different rewrites, factor this out in a better fashion
/* Rewrite extract functions in the following manner
* extract(year, date '2008-2-23') ---> extractYear(date '2008-2-23')
*/
switch (functionName) {
case "extract": {
// Assert that the first argument to extract is a QuotedString
assert args.get(0) instanceof ValueExpressions.QuotedString;
// Get the unit of time to be extracted
String timeUnitStr = ((ValueExpressions.QuotedString) args.get(0)).value;
TimeUnit timeUnit;
// Clean up day of XXX
if (timeUnitStr.contentEquals("DAYOFWEEK")) {
timeUnit = TimeUnit.DOW;
} else if (timeUnitStr.contentEquals("DAYOFYEAR")) {
timeUnit = TimeUnit.DOY;
} else {
timeUnit = TimeUnit.valueOf(timeUnitStr);
}
switch (timeUnit) {
case YEAR:
case QUARTER:
case MONTH:
case WEEK:
case DAY:
case DOW:
case DOY:
case EPOCH:
case HOUR:
case MINUTE:
case SECOND:
String functionPostfix = StringUtils.capitalize(timeUnitStr.toLowerCase());
functionName += functionPostfix;
return FunctionCallFactory.createExpression(functionName, args.subList(1, 2));
default:
throw new UnsupportedOperationException("extract function supports the following " +
"time units: YEAR, QUARTER, MONTH, WEEK, DAY, DAYOFWEEK, DAYOFYEAR, EPOCH, HOUR, " +
"MINUTE, SECOND");
}
}
case "timestampdiff": {
// Assert that the first argument to extract is a QuotedString
Preconditions.checkArgument(args.get(0) instanceof ValueExpressions.QuotedString,
"The first argument of TIMESTAMPDIFF function should be QuotedString");
String timeUnitStr = ((ValueExpressions.QuotedString) args.get(0)).value;
TimeUnit timeUnit = TimeUnit.valueOf(timeUnitStr);
switch (timeUnit) {
case YEAR:
case MONTH:
case DAY:
case HOUR:
case MINUTE:
case SECOND:
case MILLISECOND:
case QUARTER:
case WEEK:
case MICROSECOND:
case NANOSECOND:
String functionPostfix = StringUtils.capitalize(timeUnitStr.toLowerCase());
functionName += functionPostfix;
return FunctionCallFactory.createExpression(functionName, args.subList(1, 3));
default:
throw new UnsupportedOperationException("TIMESTAMPDIFF function supports the following time units: " +
"YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, QUARTER, WEEK, MICROSECOND, NANOSECOND");
}
}
case "trim": {
String trimFunc;
List<LogicalExpression> trimArgs = new ArrayList<>();
assert args.get(0) instanceof ValueExpressions.QuotedString;
switch (((ValueExpressions.QuotedString) args.get(0)).value.toUpperCase()) {
case "LEADING":
trimFunc = "ltrim";
break;
case "TRAILING":
trimFunc = "rtrim";
break;
case "BOTH":
trimFunc = "btrim";
break;
default:
throw new UnsupportedOperationException("Invalid argument for TRIM function. " +
"Expected one of the following: LEADING, TRAILING, BOTH");
}
trimArgs.add(args.get(2));
trimArgs.add(args.get(1));
return FunctionCallFactory.createExpression(trimFunc, trimArgs);
}
case "date_part": {
// Rewrite DATE_PART functions as extract functions
// assert that the function has exactly two arguments
assert argsSize == 2;
/* Based on the first input to the date_part function we rewrite the function as the
* appropriate extract function. For example
* date_part('year', date '2008-2-23') ------> extractYear(date '2008-2-23')
*/
assert args.get(0) instanceof QuotedString;
QuotedString extractString = (QuotedString) args.get(0);
String functionPostfix = StringUtils.capitalize(extractString.value.toLowerCase());
return FunctionCallFactory.createExpression("extract" + functionPostfix, args.subList(1, 2));
}
case "concat": {
if (argsSize == 1) {
/*
* We treat concat with one argument as a special case. Since we don't have a function
* implementation of concat that accepts one argument. We simply add another dummy argument
* (empty string literal) to the list of arguments.
*/
List<LogicalExpression> concatArgs = new LinkedList<>(args);
concatArgs.add(QuotedString.EMPTY_STRING);
return FunctionCallFactory.createExpression(functionName, concatArgs);
} else if (argsSize > 2) {
List<LogicalExpression> concatArgs = new ArrayList<>();
/* stack concat functions on top of each other if we have more than two arguments
* Eg: concat(col1, col2, col3) => concat(concat(col1, col2), col3)
*/
concatArgs.add(args.get(0));
concatArgs.add(args.get(1));
LogicalExpression first = FunctionCallFactory.createExpression(functionName, concatArgs);
for (int i = 2; i < argsSize; i++) {
concatArgs = new ArrayList<>();
concatArgs.add(first);
concatArgs.add(args.get(i));
first = FunctionCallFactory.createExpression(functionName, concatArgs);
}
return first;
}
break;
}
case "length": {
if (argsSize == 2) {
// Second argument should always be a literal specifying the encoding format
assert args.get(1) instanceof ValueExpressions.QuotedString;
String encodingType = ((ValueExpressions.QuotedString) args.get(1)).value;
functionName += StringUtils.capitalize(encodingType.toLowerCase());
return FunctionCallFactory.createExpression(functionName, args.subList(0, 1));
}
break;
}
case "convert_from":
case "convert_to": {
if (args.get(1) instanceof QuotedString) {
return FunctionCallFactory.createConvert(functionName, ((QuotedString) args.get(1)).value, args.get(0), ExpressionPosition.UNKNOWN);
}
break;
}
case "date_trunc": {
return handleDateTruncFunction(args);
}
case "httprequest":
case "http_request": {
// This code resolves aliases in the http_request function.
String completeRawPluginName = ((QuotedString) args.get(0)).value;
String username = context.getPlannerSettings().getQueryUser();
AliasRegistryProvider aliasRegistryProvider = context.getPlannerSettings().getAliasRegistryProvider();
AliasRegistry storageAliasRegistry = aliasRegistryProvider.getStorageAliasesRegistry();
AliasRegistry tableAliasRegistry = aliasRegistryProvider.getTableAliasesRegistry();
// Split into plugin and endpoint
SchemaPath schemaPath = SchemaPath.parseFromString(completeRawPluginName);
String rawPluginName = SchemaPath.getSimplePath(schemaPath.rootName()).toExpr();
String rawEndpoint = SchemaPath.getSimplePath(schemaPath.getLastSegment().getNameSegment().getPath()).toExpr();
// Now resolve plugin name
String actualPluginName = storageAliasRegistry.getUserAliases(username).get(rawPluginName);
if (StringUtils.isEmpty(actualPluginName)) {
// If it is empty, assign it the original name,
actualPluginName = rawPluginName;
}
// Finally remove backticks
actualPluginName = SchemaPath.parseFromString(actualPluginName).getRootSegmentPath();
// Now do the same for the endpoint name
String actualEndpointName = tableAliasRegistry.getUserAliases(username).get(rawEndpoint);
if (StringUtils.isEmpty(actualEndpointName)) {
// If it is empty, assign it the original name,
actualEndpointName = rawEndpoint;
}
// Now remove backticks
actualEndpointName = SchemaPath.parseFromString(actualEndpointName).getRootSegmentPath();
String finalPluginName = SchemaPath
.getCompoundPath(actualPluginName, actualEndpointName)
.getAsUnescapedPath();
QuotedString q = new QuotedString(finalPluginName, finalPluginName.length(), ExpressionPosition.UNKNOWN);
// Add args to new arg lists
List<LogicalExpression> requestArgs = new ArrayList<>();
requestArgs.add(q);
requestArgs.addAll(args.subList(1, args.size()));
return FunctionCallFactory.createExpression(functionName, requestArgs);
}
}
return FunctionCallFactory.createExpression(functionName, args);
}