eval method
Evaluate this expression in the context of row (column name -> value).
Implementation
@override
Object? eval(Map<String, Object?> row) {
// Short-circuit logical ops
if (op == 'AND' || op == 'OR') {
final l = left.eval(row);
if (op == 'AND' && l == false) return false;
if (op == 'OR' && l == true) return true;
final r = right.eval(row);
if (l == null || r == null) return null;
return op == 'AND'
? (l as bool) && (r as bool)
: (l as bool) || (r as bool);
}
// NULL-safe equality (mirrors SQLite's IS / IS NOT and the standard
// IS DISTINCT FROM / IS NOT DISTINCT FROM). These must run before the
// generic null-propagation below.
if (op == 'IS' ||
op == 'IS NOT' ||
op == 'IS DISTINCT FROM' ||
op == 'IS NOT DISTINCT FROM') {
final l = left.eval(row);
final r = right.eval(row);
final same =
(l == null && r == null) || (l != null && r != null && _eq(l, r));
switch (op) {
case 'IS':
case 'IS NOT DISTINCT FROM':
return same;
case 'IS NOT':
case 'IS DISTINCT FROM':
return !same;
}
}
final l = left.eval(row);
final r = right.eval(row);
if (l == null || r == null) {
// SQL three-valued logic: comparisons with NULL yield NULL (treated as false).
return null;
}
switch (op) {
case '->':
return _jsonOp(l, r, asText: false);
case '->>':
return _jsonOp(l, r, asText: true);
case '=':
return _eq(l, r);
case '!=':
case '<>':
return !_eq(l, r);
case '<':
return _cmp(l, r) < 0;
case '<=':
return _cmp(l, r) <= 0;
case '>':
return _cmp(l, r) > 0;
case '>=':
return _cmp(l, r) >= 0;
case '+':
return (l as num) + (r as num);
case '-':
return (l as num) - (r as num);
case '*':
return (l as num) * (r as num);
case '/':
return (l as num) / (r as num);
case '%':
// SQLite uses C-style truncated modulo (sign follows the dividend),
// not Dart's Euclidean `%` which is always non-negative.
return (l as num).remainder(r as num);
case '&':
return (l as num).toInt() & (r as num).toInt();
case '|':
return (l as num).toInt() | (r as num).toInt();
case '<<':
return (l as num).toInt() << (r as num).toInt();
case '>>':
return (l as num).toInt() >> (r as num).toInt();
case '||':
return _stringify(l) + _stringify(r);
case 'LIKE':
return _like(l.toString(), r.toString());
case 'GLOB':
return _glob(l.toString(), r.toString());
case 'MATCH':
return _match(l.toString(), r.toString());
case 'REGEXP':
return _regexp(l.toString(), r.toString());
}
throw StateError('Unknown binary op: $op');
}