use syn::{
BinOp,
Expr,
ExprLit,
Lit,
};
use ast::{Expression, Limit, Query};
use ast::Limit::{EndRange, Index, LimitOffset, Range, StartRange};
use plugin::number_literal;
fn all_integer_literal(expression: &Expression) -> bool {
match *expression {
Expr::Lit(ExprLit { lit: Lit::Int(_), .. }) => true,
Expr::Binary(ref bin) => all_integer_literal(&bin.left) && all_integer_literal(&bin.right),
_ => false,
}
}
fn evaluate(expression: &Expression) -> i64 {
match *expression {
Expr::Lit(ref lit) => {
if let Lit::Int(ref int_literal) = lit.lit {
int_literal.value() as i64
}
else {
0
}
},
Expr::Binary(ref bin) =>
match bin.op {
BinOp::Add(_) => evaluate(&bin.left) + evaluate(&bin.right),
BinOp::Sub(_) => evaluate(&bin.left) - evaluate(&bin.right),
_ => 0,
},
_ => 0,
}
}
pub fn optimize(query: &mut Query) {
match *query {
Query::Aggregate { .. } => (), Query::CreateTable { .. } => (), Query::Delete { .. } => (), Query::Drop { .. } => (), Query::Insert { .. } => (), Query::Select { ref mut limit, .. } => {
*limit = optimize_limit(limit);
},
Query::Update { .. } => (), }
}
fn optimize_limit(limit: &Limit) -> Limit {
match *limit {
EndRange(ref expression) => {
EndRange(try_simplify(expression))
},
Index(ref expression) => {
Index(try_simplify(expression))
},
Range(ref expression1, ref expression2) => {
if all_integer_literal(expression1) && all_integer_literal(expression2) {
let offset = evaluate(expression1);
let expr2 = evaluate(expression2);
let limit = expr2 - offset;
LimitOffset(number_literal(limit), number_literal(offset))
}
else {
Range(expression1.clone(), expression2.clone())
}
},
StartRange(ref expression) => {
StartRange(try_simplify(expression))
},
ref limit => limit.clone(),
}
}
fn try_simplify(expression: &Expression) -> Expression {
if all_integer_literal(expression) {
number_literal(evaluate(expression))
}
else {
expression.clone()
}
}