eval method

  1. @override
Object? eval(
  1. Map<String, Object?> row
)
override

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');
}