io.odysz.transact.sql.parts.condition.ExprPart Maven / Gradle / Ivy
package io.odysz.transact.sql.parts.condition;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import io.odysz.semantics.ISemantext;
import io.odysz.transact.sql.parts.AbsPart;
import io.odysz.transact.sql.parts.Logic.op;
import io.odysz.transact.sql.parts.Sql;
import io.odysz.transact.x.TransException;
/**Expression Node.
* For the Antlr4 grammar, see
* Antlr4 grammars-v4/tsql/TSqlParser.g4.
* For referencing grammar and how this is parsed, see {@link io.odysz.transact.sql.parts.antlr.ExprsVisitor}
* and {@link io.odysz.transact.sql.parts.antlr.ExprsVisitor#visitExpression(gen.antlr.sql.exprs.SearchExprs.ExpressionContext) ExprsVisitor.visitExpression()};
* Expression Grammar:
// Expression.
// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/expressions-transact-sql
// Operator precendence: https://docs.microsoft.com/en-us/sql/t-sql/language-elements/operator-precedence-transact-sql
expression
: primitive_expression
| function_call
| expression COLLATE id
| case_expression
| full_column_name
| bracket_expression
| unary_operator_expression
| expression op=('*' | '/' | '%') expression
| expression op=('+' | '-' | '&' | '^' | '|' | '||') expression
| expression comparison_operator expression
| expression assignment_operator expression
| over_clause
;
primitive_expression
: DEFAULT | NULL | LOCAL_ID | constant
;
// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/case-transact-sql
case_expression
: CASE caseExpr=expression switch_section+ (ELSE elseExpr=expression)? END
| CASE switch_search_condition_section+ (ELSE elseExpr=expression)? END
;
unary_operator_expression
: '~' expression
| op=('+' | '-') expression
;
bracket_expression
: '(' expression ')' | '(' subquery ')'
;
constant_expression
: NULL
| constant
// system functions: https://msdn.microsoft.com/en-us/library/ms187786.aspx
| function_call
| LOCAL_ID // TODO: remove.
| '(' constant_expression ')'
;
subquery
: select_statement
;
// https://msdn.microsoft.com/en-us/library/ms175972.aspx
with_expression
: WITH (XMLNAMESPACES ',')? common_table_expression (',' common_table_expression)*
| WITH BLOCKING_HIERARCHY ('(' full_column_name_list ')')? AS '(' select_statement ')'
;
common_table_expression
: expression_name=id ('(' column_name_list ')')? AS '(' select_statement ')'
;
update_elem
: (full_column_name | LOCAL_ID) ('=' | assignment_operator) expression
| udt_column_name=id '.' method_name=id '(' expression_list ')'
//| full_column_name '.' WRITE (expression, )
;
// https://msdn.microsoft.com/en-us/library/ms173545.aspx
search_condition_list
: search_condition (',' search_condition)*
;
search_condition
: search_condition_and (OR search_condition_and)*
;
search_condition_and
: search_condition_not (AND search_condition_not)*
;
search_condition_not
: NOT? predicate
;
predicate
: EXISTS '(' subquery ')'
| expression comparison_operator expression
| expression comparison_operator (ALL | SOME | ANY) '(' subquery ')'
| expression NOT? BETWEEN expression AND expression
| expression NOT? IN '(' (subquery | expression_list) ')'
| expression NOT? LIKE expression (ESCAPE expression)?
| expression IS null_notnull
| '(' search_condition ')'
;
// Changed union rule to sql_union to avoid union construct with C++ target. Issue reported by person who generates into C++. This individual reports change causes generated code to work
query_expression
: (query_specification | '(' query_expression ')') sql_union*
;
// https://msdn.microsoft.com/en-us/library/ms179899.aspx
constant
: STRING // string, datetime or uniqueidentifier
| BINARY
| sign? DECIMAL
| sign? (REAL | FLOAT) // float or decimal
| sign? dollar='$' (DECIMAL | FLOAT) // money
;
sign
: '+'
| '-'
;
// https://msdn.microsoft.com/en-us/library/ms175874.aspx
id
: simple_id
| DOUBLE_QUOTE_ID
| SQUARE_BRACKET_ID
;
simple_id
: ID
| ABSOLUTE
| ACCENT_SENSITIVITY
| ...
* @author [email protected]
*/
public class ExprPart extends AbsPart {
private boolean isNull;
private op logic;
private Object lexp;
private Object rexp;
/**Create an expression.
* @param op operator, not necessarily a logical one, can also a mathematical one.
* @param lexp
* @param rexp
*/
public ExprPart(op op, String lexp, String rexp) {
this.logic =op;
this.lexp = lexp;
this.rexp = rexp;
this.isNull = false;
}
/**2019.10.12: operand and also be an expression.
* Create an expression.
* @param op
* @param lexp
* @param rexp
*/
public ExprPart(op op, ExprPart lexp, ExprPart rexp) {
this.logic =op;
this.lexp = lexp;
this.rexp = rexp;
this.isNull = false;
}
public ExprPart(String constv) {
this.logic = null;
lexp = constv;
this.isNull = false;
}
/** @since 1.4.40 */
public ExprPart(long n) {
this(String.valueOf(n));
}
// TAG: v1.3.0
public ExprPart(String[] constvs) {
this.logic = null;
lexp = constvs;
this.isNull = false;
}
public ExprPart() {
this.isNull = true;
}
/**
* @since 1.4.40
* @param n
*/
public ExprPart(Number n) {
this(String.valueOf(n));
}
/**
* @deprecated since 1.4.36 is renamed as {@link #constr(String)}
* @param v
* @return constant expr
*/
public static ExprPart constStr(String v) {
return constr(v);
}
/**
* Use this to add "' '" over a string value. Don't confused with {@link #constVal(String)}.
* @param v
* @return expr instance
* @since 1.4.36
*/
public static ExprPart constr(String v) {
if (v != null)
return new ExprPart("'" + v + "'");
else return new ExprPart();
}
/**Use this to add value, without "' '". Don't confused with {@link #constStr(String)}.
* @param v
* @return expr instance
*/
public static ExprPart constVal(String v) {
if (v != null)
return new ExprPart(v);
else return new ExprPart();
}
public static AbsPart constVal(int v) {
return new ExprPart(String.valueOf(v));
}
public static AbsPart constVal(Object v) {
return new ExprPart(String.valueOf(v));
}
boolean escape = true;
/**Stop escape the string value (replace ' with '')
* @param esc
* @return this
*/
public ExprPart escape(boolean esc) {
escape = esc;
return this;
}
/**Mainly used for get string raw value.
* FIXME performance problem base64 string, add a class for binary value?
*/
@Override
public String toString() {
// This function shouldn't been called frequently and only for test,
// or inner referencing, like fullpath, ectc.
// So this is not a performance problem?
String v;
try {
v = sql(null);
return v == null ? null : v.replaceAll("^'|'$", "");
} catch (TransException e) {
e.printStackTrace();
return e.getMessage();
}
}
@Override
public String sql(ISemantext ctx) throws TransException {
if (isNull)
return "null";
if (logic == null)
return expString(lexp, ctx);
else {
return lexp == null ?
logic.sql(ctx, logic, expString(rexp, ctx)) :
String.format("%s %s",
expString(lexp, ctx),
logic.sql(ctx, logic, expString(rexp, ctx)));
}
}
private String expString(Object exp, ISemantext ctx) throws TransException {
if (exp == null)
return null;
else if (escape && exp instanceof String)
return Sql.filterVal((String) exp);
else if (exp instanceof ExprPart)
return ((ExprPart)exp).sql(ctx);
// Tag v1.3.0
else if (exp instanceof String[])
return Stream.of((String[])exp).collect(Collectors.joining("', '", "'", "'"));
else return exp.toString();
}
public boolean isNull() {
return isNull;
}
}