// during type coerse, we may have to change el/er type. Consider this case: // el+er where el= 2.5 double, er = 10 decimal // their result is decimal as decimal has higher precedence so we have to // convert el to decimal to avoid later operator+ suprise. // public static ColumnType CoerseType(string op, Expr el, Expr er) { ColumnType result = null; ColumnType l = el.type_; ColumnType r = er.type_; if (l.Equals(r)) { return(l); } else { Type coertype = TypeBase.HigherPrecedence(l, r); // these types needs precision etc further handling if (coertype == typeof(NumericType)) { if (l is DoubleType && r is NumericType rnum) { result = new NumericType(rnum.len_, rnum.scale_); el.type_ = result; if (el is ConstExpr ell) { ell.val_ = Convert.ToDecimal(ell.val_); } } else if (r is DoubleType && l is NumericType lnum) { result = new NumericType(lnum.len_, lnum.scale_); er.type_ = result; if (er is ConstExpr erl) { erl.val_ = Convert.ToDecimal(erl.val_); } } else if (l is NumericType || r is NumericType) { // FIXME: this is a rough calculation int prec = 0, scale = 0; if (l is NumericType ln) { prec = ln.len_; scale = ln.scale_; } if (r is NumericType rn) { prec = Math.Max(rn.len_, prec); scale = Math.Max(rn.scale_, scale); } result = new NumericType(prec, scale); } } else if (TypeBase.IsStringType(l) && TypeBase.IsStringType(r)) { result = new VarCharType(Int32.MaxValue); } else { result = (ColumnType)Activator.CreateInstance(coertype); } } Debug.Assert(result != null); return(result); }
public override Expr Normalize() { // all children get normalized first for (int i = 0; i < children_.Count; ++i) { Expr x = children_[i]; children_[i] = x.Normalize(); } Expr l = lchild_(); Expr r = rchild_(); ConstExpr lce = (l is ConstExpr) ? (ConstExpr)l : null; ConstExpr rce = (r is ConstExpr) ? (ConstExpr)r : null; switch (op_) { case "+": case "-": case "*": case "/": case ">": case ">=": case "<": case "<=": case "||": case "=": case "<>": case "!=": case " and ": case " or ": case "is": case "is not": if ((lce != null && lce.val_ is null) || (rce != null && rce.val_ is null)) { if (IsRelOp()) { // needs to be TRUE or FALSE if ((op_ == "is") || (op_ == "is not") || (lce != null && TypeBase.IsNumberType(l.type_)) || (rce != null && TypeBase.IsNumberType(r.type_))) { return(SimplifyRelop()); } } // NULL simplification: if operator is not relational, X op NULL is NULL if (lce != null && lce.val_ is null) { return(lce); } if (rce != null && rce.IsNull()) { return(rce); } } if (lce != null && rce != null) { // Simplify Constants: children are not non null constants, evaluate them. Value val = Exec(null, null); return(ConstExpr.MakeConst(val, type_, outputName_)); } if (lce != null && rce == null && isPlainSwappableConstOp()) { SwapSide(); } if ((lce != null || rce != null) && (IsArithIdentity(lce, rce))) { return(SimplifyArithmetic(lce, rce)); } if (IsLogicalOp()) { return(SimplifyLogic()); } if (IsRelOp()) { return(SimplifyRelop()); } // arithmetic operators? if (l is BinExpr le && le.children_[1].IsConst() && (rce != null) && isCommutativeConstOp() && le.isCommutativeConstOp() && TypeBase.SameArithType(l.type_, r.type_)) { /* * Here root is distributive operator (only * in this context) left is Commutative * operator, +, or * right is constant, furthermore, left's right is a constant * (becuase we swapped cosntant to be on the right). * if be == + and l == +: add left's right value to root's right value, * make left's left as left of root * * if be == * and l == +: create a expr node as left (x + 10), create (5 * 10) * as right, change operator to + * In either case left and right's children must be nulled out * and since we are going bottom up, this doesn't create any problem. * * Here is a pictorial description: * * root + * old left / \ old right new left / \ new right * / \ / \ * + 10 => * 50 * left of old left / \ / \ * / \ ROL LNL / \ RNL (right of New Left) * x 5 x 10 */ /* * Simple case: when current and left are same operators, distributive * opeartion and node creation is uncessary. */ if ((op_ == "+" && le.op_ == "+") || (op_ == "*" && le.op_ == "*")) { /* create new right node as constant. */ Expr tmpexp = Clone(); tmpexp.children_[0] = le.children_[1]; tmpexp.children_[1] = r; tmpexp.type_ = r.type_; Value val; bool wasConst = tmpexp.TryEvalConst(out val); Expr newr = ConstExpr.MakeConst(val, tmpexp.type_, r.outputName_); // new left is old left's left child // of left will be right of the root. children_[0] = l.children_[0]; // new right is the new constant node children_[1] = newr; } else if (op_ == "*" && le.rchild_() is ConstExpr lrc && (le.op_ == "+" || le.op_ == "-")) { /* * case of (a + const1) * const2 => (a * const2) + (const1 * const2)) * make a newe left node to house (a * const2) * * * + * / \ / \ * / \ / \ * + c2 => * c1 * c2 * / \ / \ * / \ / \ * X c1 X c2 * */ /* make a const expr node to evaluate const1 * const 2 */ Expr tmpexp = Clone(); tmpexp.children_[0] = lrc; // right of left is const tmpexp.children_[1] = r; // our right is const tmpexp.type_ = r.type_; Value val; tmpexp.TryEvalConst(out val); // set c2 as the value of right child of our left lrc.val_ = rce.val_; // val is c1 * c2, set it as the value of our right child rce.val_ = val; /* swap the operators */ string op = op_; op_ = le.op_; le.op_ = op; } /* we can't do any thing about + at the top and * as left child. */ } return(this); } return(this); }