org.apache.hive.hplsql.Expression Maven / Gradle / Ivy
/**
* 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.hive.hplsql;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.Calendar;
import org.antlr.v4.runtime.ParserRuleContext;
import org.apache.hive.hplsql.Var.Type;
/**
* Expressions
*/
public class Expression {
Exec exec;
boolean trace = false;
Expression(Exec e) {
exec = e;
trace = exec.getTrace();
}
/**
* Evaluate an expression
*/
public void exec(HplsqlParser.ExprContext ctx) {
try {
if (ctx.T_ADD() != null) {
operatorAdd(ctx);
}
else if (ctx.T_SUB() != null) {
operatorSub(ctx);
}
else if (ctx.T_MUL() != null) {
operatorMultiply(ctx);
}
else if (ctx.T_DIV() != null) {
operatorDiv(ctx);
}
else if (ctx.interval_item() != null) {
createInterval(ctx);
}
else {
visitChildren(ctx);
}
}
catch (Exception e) {
exec.signal(e);
}
}
/**
* Evaluate an expression in executable SQL statement
*/
public void execSql(HplsqlParser.ExprContext ctx) {
StringBuilder sql = new StringBuilder();
if (ctx.T_OPEN_P() != null) {
sql.append("(");
if (ctx.select_stmt() != null) {
exec.append(sql, evalPop(ctx.select_stmt()).toString(), ctx.T_OPEN_P().getSymbol(), ctx.select_stmt().getStart());
exec.append(sql, ctx.T_CLOSE_P().getText(), ctx.select_stmt().stop, ctx.T_CLOSE_P().getSymbol());
}
else {
sql.append(evalPop(ctx.expr(0)).toString());
sql.append(")");
}
}
else if (ctx.T_MUL() != null) {
sql.append(evalPop(ctx.expr(0)).toString());
sql.append(" * ");
sql.append(evalPop(ctx.expr(1)).toString());
}
else if (ctx.T_DIV() != null) {
sql.append(evalPop(ctx.expr(0)).toString());
sql.append(" / ");
sql.append(evalPop(ctx.expr(1)).toString());
}
else if (ctx.T_ADD() != null) {
sql.append(evalPop(ctx.expr(0)).toString());
sql.append(" + ");
sql.append(evalPop(ctx.expr(1)).toString());
}
else if (ctx.T_SUB() != null) {
sql.append(evalPop(ctx.expr(0)).toString());
sql.append(" - ");
sql.append(evalPop(ctx.expr(1)).toString());
}
else if (ctx.interval_item() != null) {
sql.append(exec.getFormattedText(ctx));
}
else {
visitChildren(ctx);
sql.append(exec.stackPop().toString());
}
exec.stackPush(sql);
}
/**
* Evaluate a boolean expression
*/
public void execBool(HplsqlParser.Bool_exprContext ctx) {
if (ctx.bool_expr_atom() != null) {
eval(ctx.bool_expr_atom());
return;
}
Var result = evalPop(ctx.bool_expr(0));
if (ctx.T_OPEN_P() != null) {
if (ctx.T_NOT() != null) {
result.negate();
}
}
else if (ctx.bool_expr_logical_operator() != null) {
if (ctx.bool_expr_logical_operator().T_AND() != null) {
if (result.isTrue()) {
result = evalPop(ctx.bool_expr(1));
}
}
else if (ctx.bool_expr_logical_operator().T_OR() != null) {
if (!result.isTrue()) {
result = evalPop(ctx.bool_expr(1));
}
}
}
exec.stackPush(result);
}
/**
* Evaluate a boolean expression in executable SQL statement
*/
public void execBoolSql(HplsqlParser.Bool_exprContext ctx) {
StringBuilder sql = new StringBuilder();
if (ctx.T_OPEN_P() != null) {
sql.append("(");
sql.append(evalPop(ctx.bool_expr(0)).toString());
sql.append(")");
}
else if (ctx.bool_expr_atom() != null) {
sql.append(evalPop(ctx.bool_expr_atom()).toString());
}
else if (ctx.bool_expr_logical_operator() != null) {
sql.append(evalPop(ctx.bool_expr(0)).toString());
sql.append(" " + ctx.bool_expr_logical_operator().getText() + " ");
sql.append(evalPop(ctx.bool_expr(1)).toString());
}
exec.stackPush(sql);
}
/**
* Binary boolean expression
*/
public Integer execBoolBinary(HplsqlParser.Bool_expr_binaryContext ctx) {
HplsqlParser.Bool_expr_binary_operatorContext op = ctx.bool_expr_binary_operator();
if (op.T_EQUAL() != null || op.T_EQUAL2() != null) {
operatorEqual(ctx, true);
}
else if (op.T_NOTEQUAL() != null || op.T_NOTEQUAL2() != null) {
operatorEqual(ctx, false);
}
else if (op.T_GREATER() != null || op.T_LESS() != null || op.T_GREATEREQUAL() != null || op.T_LESSEQUAL() != null) {
operatorCompare(ctx, op);
}
else {
exec.stackPush(false);
}
return 0;
}
/**
* Binary boolean expression in executable SQL statement
*/
public Integer execBoolBinarySql(HplsqlParser.Bool_expr_binaryContext ctx) {
StringBuilder sql = new StringBuilder();
sql.append(evalPop(ctx.expr(0)).toString());
sql.append(" " + exec.getFormattedText(ctx.bool_expr_binary_operator()) + " ");
sql.append(evalPop(ctx.expr(1)).toString());
exec.stackPush(sql);
return 0;
}
/**
* Unary boolean expression
*/
public Integer execBoolUnary(HplsqlParser.Bool_expr_unaryContext ctx) {
boolean val = false;
if (ctx.T_IS() != null) {
val = evalPop(ctx.expr(0)).isNull();
if (ctx.T_NOT() != null) {
val = !val;
}
}
else if (ctx.T_BETWEEN() != null) {
Var v = evalPop(ctx.expr(0));
Var v1 = evalPop(ctx.expr(1));
int cmp = v.compareTo(v1);
if (cmp >= 0) {
Var v2 = evalPop(ctx.expr(2));
cmp = v.compareTo(v2);
if (cmp <= 0) {
val = true;
}
}
}
exec.stackPush(val);
return 0;
}
/**
* Unary boolean expression in executable SQL statement
*/
public Integer execBoolUnarySql(HplsqlParser.Bool_expr_unaryContext ctx) {
StringBuilder sql = new StringBuilder();
if (ctx.T_IS() != null) {
sql.append(evalPop(ctx.expr(0)).toString());
sql.append(" " + exec.getText(ctx, ctx.T_IS().getSymbol(), ctx.T_NULL().getSymbol()));
}
else if (ctx.T_BETWEEN() != null) {
sql.append(evalPop(ctx.expr(0)).toString());
sql.append(" " + ctx.T_BETWEEN().getText() + " ");
sql.append(evalPop(ctx.expr(1)).toString());
sql.append(" " + ctx.T_AND().getText() + " ");
sql.append(evalPop(ctx.expr(2)).toString());
}
else if (ctx.T_EXISTS() != null) {
exec.append(sql, exec.nvl(ctx.T_NOT(), ctx.T_EXISTS()), ctx.T_OPEN_P());
exec.append(sql, evalPop(ctx.select_stmt()).toString(), ctx.T_OPEN_P().getSymbol(), ctx.select_stmt().getStart());
exec.append(sql, ctx.T_CLOSE_P().getText(), ctx.select_stmt().stop, ctx.T_CLOSE_P().getSymbol());
}
else if (ctx.bool_expr_single_in() != null) {
singleInClauseSql(ctx.bool_expr_single_in(), sql);
}
else if (ctx.bool_expr_multi_in() != null) {
multiInClauseSql(ctx.bool_expr_multi_in(), sql);
}
exec.stackPush(sql);
return 0;
}
/**
* Single value IN clause in executable SQL statement
*/
public void singleInClauseSql(HplsqlParser.Bool_expr_single_inContext ctx, StringBuilder sql) {
sql.append(evalPop(ctx.expr(0)).toString() + " ");
exec.append(sql, exec.nvl(ctx.T_NOT(), ctx.T_IN()), ctx.T_OPEN_P());
if (ctx.select_stmt() != null) {
exec.append(sql, evalPop(ctx.select_stmt()).toString(), ctx.T_OPEN_P().getSymbol(), ctx.select_stmt().getStart());
exec.append(sql, ctx.T_CLOSE_P().getText(), ctx.select_stmt().stop, ctx.T_CLOSE_P().getSymbol());
}
else {
int cnt = ctx.expr().size();
for (int i = 1; i < cnt; i++) {
sql.append(evalPop(ctx.expr(i)).toString());
if (i + 1 < cnt) {
sql.append(", ");
}
}
sql.append(")");
}
}
/**
* Multi-value IN clause in executable SQL statement
*/
public void multiInClauseSql(HplsqlParser.Bool_expr_multi_inContext ctx, StringBuilder sql) {
int cnt = ctx.expr().size();
sql.append("(");
for (int i = 0; i < cnt; i++) {
sql.append(evalPop(ctx.expr(i)).toString());
if (i + 1 < cnt) {
sql.append(", ");
}
}
sql.append(")");
if (ctx.T_NOT() != null) {
sql.append(" " + ctx.T_NOT().getText());
}
sql.append(" " + ctx.T_IN().getText() + " (");
if (ctx.select_stmt() != null) {
sql.append(evalPop(ctx.select_stmt()));
}
sql.append(")");
}
/**
* Cursor attribute %ISOPEN, %FOUND and %NOTFOUND
*/
public void execCursorAttribute(HplsqlParser.Expr_cursor_attributeContext ctx) {
String name = ctx.ident().getText();
Var val = new Var(Var.Type.BOOL);
Var cursor = exec.findCursor(name);
if (cursor != null) {
Query query = (Query)cursor.value;
if (query != null) {
if (ctx.T_ISOPEN() != null) {
val.setValue(query.isOpen());
}
else if (ctx.T_FOUND() != null) {
val.setValue(query.isFound());
}
else if (ctx.T_NOTFOUND() != null) {
val.setValue(query.isNotFound());
}
}
exec.stackPush(val);
}
else {
trace(ctx, "Cursor not found: " + name);
exec.signal(Signal.Type.SQLEXCEPTION);
}
}
/**
* Addition operator
*/
public void operatorAdd(HplsqlParser.ExprContext ctx) {
Var v1 = evalPop(ctx.expr(0));
Var v2 = evalPop(ctx.expr(1));
if (v1.value == null || v2.value == null) {
evalNull();
}
else if (v1.type == Type.BIGINT && v2.type == Type.BIGINT) {
exec.stackPush(new Var((Long)v1.value + (Long)v2.value));
}
else if (v1.type == Type.BIGINT && v2.type == Type.DATE) {
exec.stackPush(changeDateByInt((Date)v2.value, (Long)v1.value, true /*add*/));
}
else if (v1.type == Type.DATE && v2.type == Type.BIGINT) {
exec.stackPush(changeDateByInt((Date)v1.value, (Long)v2.value, true /*add*/));
}
else if (v1.type == Type.STRING && v2.type == Type.STRING) {
exec.stackPush(((String)v1.value) + ((String)v2.value));
}
else if (v1.type == Type.DATE && v2.type == Type.INTERVAL) {
exec.stackPush(new Var(((Interval)v2.value).dateChange((Date)v1.value, true /*add*/)));
}
else if (v1.type == Type.TIMESTAMP && v2.type == Type.INTERVAL) {
exec.stackPush(new Var(((Interval)v2.value).timestampChange((Timestamp)v1.value, true /*add*/), v1.scale));
}
else {
evalNull();
}
}
/**
* Subtraction operator
*/
public void operatorSub(HplsqlParser.ExprContext ctx) {
Var v1 = evalPop(ctx.expr(0));
Var v2 = evalPop(ctx.expr(1));
if (v1.value == null || v2.value == null) {
evalNull();
}
else if (v1.type == Type.BIGINT && v2.type == Type.BIGINT) {
exec.stackPush(new Var((Long)v1.value - (Long)v2.value));
}
else if (v1.type == Type.DATE && v2.type == Type.BIGINT) {
exec.stackPush(changeDateByInt((Date)v1.value, (Long)v2.value, false /*subtract*/));
}
else if (v1.type == Type.DATE && v2.type == Type.INTERVAL) {
exec.stackPush(new Var(((Interval)v2.value).dateChange((Date)v1.value, false /*subtract*/)));
}
else if (v1.type == Type.TIMESTAMP && v2.type == Type.INTERVAL) {
exec.stackPush(new Var(((Interval)v2.value).timestampChange((Timestamp)v1.value, false /*subtract*/), v1.scale));
}
else {
evalNull();
}
}
/**
* Multiplication operator
*/
public void operatorMultiply(HplsqlParser.ExprContext ctx) {
Var v1 = evalPop(ctx.expr(0));
Var v2 = evalPop(ctx.expr(1));
if (v1.value == null || v2.value == null) {
evalNull();
}
else if (v1.type == Type.BIGINT && v2.type == Type.BIGINT) {
exec.stackPush(new Var((Long)v1.value * (Long)v2.value));
}
else {
exec.signal(Signal.Type.UNSUPPORTED_OPERATION, "Unsupported data types in multiplication operator");
}
}
/**
* Division operator
*/
public void operatorDiv(HplsqlParser.ExprContext ctx) {
Var v1 = evalPop(ctx.expr(0));
Var v2 = evalPop(ctx.expr(1));
if (v1.value == null || v2.value == null) {
evalNull();
}
else if (v1.type == Type.BIGINT && v2.type == Type.BIGINT) {
exec.stackPush(new Var((Long)v1.value / (Long)v2.value));
}
else {
exec.signal(Signal.Type.UNSUPPORTED_OPERATION, "Unsupported data types in division operator");
}
}
/**
* Add or subtract the specified number of days from DATE
*/
public Var changeDateByInt(Date d, Long i, boolean add) {
Calendar c = Calendar.getInstance();
c.setTimeInMillis(d.getTime());
int days = i.intValue();
if(!add) {
days *= -1;
}
c.add(Calendar.DAY_OF_MONTH, days);
return new Var(new Date(c.getTimeInMillis()));
}
/**
* Equality operator
*/
public void operatorEqual(HplsqlParser.Bool_expr_binaryContext ctx, boolean equal) {
Var v1 = evalPop(ctx.expr(0));
Var v2 = evalPop(ctx.expr(1));
boolean eq = v1.equals(v2);
if (!equal) {
eq = !eq;
}
exec.stackPush(eq);
}
/**
* Comparison operator
*/
public void operatorCompare(HplsqlParser.Bool_expr_binaryContext ctx, HplsqlParser.Bool_expr_binary_operatorContext op) {
Var v1 = evalPop(ctx.expr(0));
Var v2 = evalPop(ctx.expr(1));
int cmp = v1.compareTo(v2);
boolean bool = false;
if (op.T_GREATER() != null) {
if (cmp > 0) {
bool = true;
}
}
else if (op.T_GREATEREQUAL() != null) {
if (cmp >= 0) {
bool = true;
}
}
if (op.T_LESS() != null) {
if (cmp < 0) {
bool = true;
}
}
else if (op.T_LESSEQUAL() != null) {
if (cmp <= 0) {
bool = true;
}
}
exec.stackPush(bool);
}
/**
* String concatenation operator
*/
public void operatorConcat(HplsqlParser.Expr_concatContext ctx) {
StringBuilder val = new StringBuilder();
int cnt = ctx.expr_concat_item().size();
boolean nulls = true;
for (int i = 0; i < cnt; i++) {
Var c = evalPop(ctx.expr_concat_item(i));
if (!c.isNull()) {
val.append(c.toString());
nulls = false;
}
}
if (nulls) {
evalNull();
}
else {
evalString(val);
}
}
/**
* String concatenation operator in executable SQL statement
*/
public void operatorConcatSql(HplsqlParser.Expr_concatContext ctx) {
StringBuilder sql = new StringBuilder();
sql.append("CONCAT(");
int cnt = ctx.expr_concat_item().size();
for (int i = 0; i < cnt; i++) {
sql.append(evalPop(ctx.expr_concat_item(i)).toString());
if (i + 1 < cnt) {
sql.append(", ");
}
}
sql.append(")");
exec.stackPush(sql);
}
/**
* Simple CASE expression
*/
public void execSimpleCase(HplsqlParser.Expr_case_simpleContext ctx) {
int i = 1;
int cnt = ctx.expr().size();
boolean found = false;
Var val = evalPop(ctx.expr(0));
while(i < cnt) {
Var when = evalPop(ctx.expr(i));
if(val.compareTo(when) == 0) {
visit(ctx.expr(i + 1));
found = true;
break;
}
i += 2;
}
if(!found) {
if(ctx.T_ELSE() != null) {
visit(ctx.expr(cnt - 1));
}
else {
evalNull();
}
}
}
/**
* Simple CASE expression in executable SQL statement
*/
public void execSimpleCaseSql(HplsqlParser.Expr_case_simpleContext ctx) {
StringBuilder sql = new StringBuilder();
sql.append("CASE ");
sql.append(evalPop(ctx.expr(0)).toString());
int cnt = ctx.T_WHEN().size();
for (int i = 0; i < cnt; i++) {
sql.append(" WHEN ");
sql.append(evalPop(ctx.expr(i * 2 + 1)).toString());
sql.append(" THEN ");
sql.append(evalPop(ctx.expr(i * 2 + 2)).toString());
}
if (ctx.T_ELSE() != null) {
sql.append(" ELSE ");
sql.append(evalPop(ctx.expr(cnt * 2 + 1)).toString());
}
sql.append(" END");
exec.stackPush(sql);
}
/**
* Searched CASE expression
*/
public void execSearchedCase(HplsqlParser.Expr_case_searchedContext ctx) {
int cnt = ctx.bool_expr().size();
boolean found = false;
for(int i = 0; i < cnt; i++) {
if(evalPop(ctx.bool_expr(i)).isTrue()) {
visit(ctx.expr(i));
found = true;
break;
}
}
if(!found) {
if(ctx.T_ELSE() != null) {
visit(ctx.expr(cnt));
}
else {
evalNull();
}
}
}
/**
* Searched CASE expression in executable SQL statement
*/
public void execSearchedCaseSql(HplsqlParser.Expr_case_searchedContext ctx) {
StringBuilder sql = new StringBuilder();
sql.append("CASE");
int cnt = ctx.T_WHEN().size();
for (int i = 0; i < cnt; i++) {
sql.append(" WHEN ");
sql.append(evalPop(ctx.bool_expr(i)).toString());
sql.append(" THEN ");
sql.append(evalPop(ctx.expr(i)).toString());
}
if (ctx.T_ELSE() != null) {
sql.append(" ELSE ");
sql.append(evalPop(ctx.expr(cnt)).toString());
}
sql.append(" END");
exec.stackPush(sql);
}
/**
* Create an interval variable
*/
public void createInterval(HplsqlParser.ExprContext ctx) {
int num = evalPop(ctx.expr(0)).intValue();
Interval interval = new Interval().set(num, ctx.interval_item().getText());
exec.stackPush(new Var(interval));
}
/**
* Evaluate the expression and push the value to the stack
*/
void eval(ParserRuleContext ctx) {
visit(ctx);
}
/**
* Evaluate the expression and pop value from the stack
*/
Var evalPop(ParserRuleContext ctx) {
visit(ctx);
if (!exec.stack.isEmpty()) {
return exec.stackPop();
}
return Var.Empty;
}
/**
* Evaluate the expression to specified String value
*/
void evalString(String string) {
exec.stackPush(new Var(string));
}
void evalString(StringBuilder string) {
evalString(string.toString());
}
/**
* Evaluate the expression to NULL
*/
void evalNull() {
exec.stackPush(Var.Null);
}
/**
* Execute rules
*/
Integer visit(ParserRuleContext ctx) {
return exec.visit(ctx);
}
/**
* Execute children rules
*/
Integer visitChildren(ParserRuleContext ctx) {
return exec.visitChildren(ctx);
}
/**
* Trace information
*/
public void trace(ParserRuleContext ctx, String message) {
exec.trace(ctx, message);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy