private SqlExpr other(SqlExpr node, SqlExpr compare, Class<? extends BinarySqlOperator> type) {
   if (type.isInstance(node)) {
     BinarySqlOperator cast = type.cast(node);
     SqlExpr left = cast.getLeftArg();
     SqlExpr right = cast.getRightArg();
     if (left.equals(compare)) return right;
     if (right.equals(compare)) return left;
   }
   return null;
 }
 @Override
 public void meet(SqlIsNull node) throws RuntimeException {
   super.meet(node);
   SqlExpr arg = node.getArg();
   if (arg instanceof SqlNull) {
     replace(node, new TrueValue());
   } else if (arg instanceof SqlConstant<?>) {
     replace(node, new FalseValue());
   } else if (arg instanceof SqlCase) {
     SqlExpr rep = null;
     SqlExpr prev = null;
     SqlCase scase = (SqlCase) arg;
     for (Entry entry : scase.getEntries()) {
       SqlExpr condition = entry.getCondition();
       if (rep == null) {
         rep = and(condition.clone(), isNull(entry.getResult().clone()));
         prev = not(condition.clone());
       } else {
         rep =
             or(rep, and(and(prev.clone(), condition.clone()), isNull(entry.getResult().clone())));
         prev = and(prev, not(condition.clone()));
       }
     }
     replace(node, or(rep, prev.clone()));
   }
 }
 @Override
 public void meet(SqlAnd node) throws RuntimeException {
   super.meet(node);
   SqlExpr left = node.getLeftArg();
   SqlExpr right = node.getRightArg();
   if (left instanceof FalseValue || right instanceof FalseValue) {
     replace(node, new FalseValue());
   } else if (left instanceof TrueValue && right instanceof TrueValue) {
     replace(node, new TrueValue());
   } else if (left instanceof TrueValue) {
     replace(node, right.clone());
   } else if (right instanceof TrueValue) {
     replace(node, left.clone());
   } else if (right instanceof SqlNull || left instanceof SqlNull) {
     replace(node, new SqlNull());
   } else if (right instanceof SqlNot && ((SqlNot) right).getArg().equals(left)) {
     replace(node, new FalseValue());
   } else if (left instanceof SqlNot && ((SqlNot) left).getArg().equals(right)) {
     replace(node, new FalseValue());
   }
 }
 @Override
 public void meet(SqlOr node) throws RuntimeException {
   super.meet(node);
   SqlExpr left = node.getLeftArg();
   SqlExpr right = node.getRightArg();
   if (left instanceof TrueValue || right instanceof TrueValue) {
     replace(node, new TrueValue());
   } else if (left instanceof FalseValue && right instanceof FalseValue) {
     replace(node, new FalseValue());
   } else if (left instanceof FalseValue) {
     replace(node, right.clone());
   } else if (right instanceof FalseValue) {
     replace(node, left.clone());
   } else if (right instanceof SqlNull && andAllTheWay(node)) {
     replace(node, left.clone());
   } else if (left instanceof SqlNull && andAllTheWay(node)) {
     replace(node, right.clone());
   } else if (right instanceof SqlNull && left instanceof SqlNull) {
     replace(node, new SqlNull());
   } else if (left instanceof SqlNull && right instanceof SqlOr) {
     SqlOr r = (SqlOr) right;
     SqlExpr rleft = r.getLeftArg();
     SqlExpr rright = r.getRightArg();
     if (rleft instanceof SqlNull || rright instanceof SqlNull) {
       replace(node, right.clone());
     }
   } else if (right instanceof SqlNull && left instanceof SqlOr) {
     SqlOr l = (SqlOr) left;
     SqlExpr lleft = l.getLeftArg();
     SqlExpr lright = l.getRightArg();
     if (lleft instanceof SqlNull || lright instanceof SqlNull) {
       replace(node, left.clone());
     }
   } else if (right instanceof SqlNull && left instanceof SqlAnd) {
     // value IS NOT NULL AND value = ? OR NULL
     // -> value = ?
     SqlAnd l = (SqlAnd) left;
     SqlExpr lleft = l.getLeftArg();
     SqlExpr lright = l.getRightArg();
     SqlExpr isNotNull = arg(arg(lleft, SqlNot.class), SqlIsNull.class);
     SqlExpr isNotEq = other(lright, isNotNull, SqlEq.class);
     if (isNotEq instanceof SqlConstant) {
       replace(node, lright);
     }
   }
 }
 private void replace(SqlExpr before, SqlExpr after) {
   before.replaceWith(after);
   after.visit(this);
 }
 public void optimize(SqlExpr sqlExpr) {
   sqlExpr.visit(this);
 }