///|
fn eval_predicate(
ctx : QueryContext,
cond : Cond,
evaluate : (Expr) -> QueryValue,
) -> QueryValue {
match cond {
CTrue => QBool(true)
CTruthy(e) => {
let v = evaluate(e)
if v is QInventory(_) {
query_error(
ctx, "query.oracle_type_error: NotImplementedError inventory truth",
)
return QNull
}
if v is QNull {
QNull
} else {
QBool(truth(v))
}
}
CNot(c) => QBool(!truth(eval_predicate(ctx, c, evaluate)))
CAnd(a, b) => {
let x = eval_predicate(ctx, a, evaluate)
if !truth(x) {
x
} else {
eval_predicate(ctx, b, evaluate)
}
}
COr(a, b) => {
let x = eval_predicate(ctx, a, evaluate)
if truth(x) {
x
} else {
eval_predicate(ctx, b, evaluate)
}
}
CIsNull(e, negated) => QBool((evaluate(e) is QNull) != negated)
CRegex(e, _, regex) =>
match evaluate(e) {
QNull => QNull
QText(s) => QBool(regex.execute(s) is Some(_))
_ => {
query_error(
ctx, "query.oracle_type_error: TypeError regex requires string",
)
QNull
}
}
CCmp(a, op, b) => query_comparison(ctx, evaluate(a), op, evaluate(b))
}
}
///|
fn projection_condition_types(
cond : Cond,
source : QuerySource,
errors : Array[String],
) -> Unit {
match cond {
CTrue => ()
CNot(c) => projection_condition_types(c, source, errors)
CAnd(a, b) | COr(a, b) => {
projection_condition_types(a, source, errors)
projection_condition_types(b, source, errors)
}
CIsNull(e, _) | CTruthy(e) => expression_type(e, source, errors) |> ignore
CRegex(e, _, _) => {
let t = expression_type(e, source, errors)
if t != "str" && t != "object" {
errors.push("compilation query.invalid_signature: regex " + t)
}
}
CCmp(a, _, b) => {
let ta = expression_type(a, source, errors)
let tb = expression_type(b, source, errors)
let primitive = fn(t) { ["int", "decimal", "str", "date"].contains(t) }
if !((ta == tb && primitive(ta)) ||
(["int", "decimal"].contains(ta) && ["int", "decimal"].contains(tb)) ||
(ta == "object" && primitive(tb)) ||
(tb == "object" && primitive(ta))) {
errors.push(
"compilation query.invalid_signature: comparison " + ta + " " + tb,
)
}
}
}
}
///|
fn condition_label(cond : Cond) -> String {
match cond {
CTrue => "true"
CTruthy(e) => oracle_expr_label(e)
CNot(c) => "NOT " + condition_label(c)
CAnd(a, b) => condition_label(a) + " AND " + condition_label(b)
COr(a, b) => condition_label(a) + " OR " + condition_label(b)
CCmp(a, op, b) => oracle_expr_label(a) + op + oracle_expr_label(b)
CIsNull(e, n) =>
oracle_expr_label(e) + (if n { " IS NOT NULL" } else { " IS NULL" })
CRegex(e, p, _) => oracle_expr_label(e) + "~'" + p + "'"
}
}
///|
fn same_condition(a : Cond, b : Cond) -> Bool {
match (a, b) {
(CTrue, CTrue) => true
(CTruthy(a), CTruthy(b)) => same_expr(a, b)
(CNot(a), CNot(b)) => same_condition(a, b)
(CAnd(a, b), CAnd(c, d)) | (COr(a, b), COr(c, d)) =>
same_condition(a, c) && same_condition(b, d)
(CCmp(a, op, b), CCmp(c, p, d)) =>
op == p && same_expr(a, c) && same_expr(b, d)
(CIsNull(a, n), CIsNull(b, m)) => n == m && same_expr(a, b)
(CRegex(a, p, _), CRegex(b, q, _)) => p == q && same_expr(a, b)
_ => false
}
}