@@ -126,6 +126,8 @@ func (r *Reader) Read(ctx context.Context, s []Row) (n int, err error) {
126126 count , err := r .inner .ReadColumns (ctx , r .dl .PrimaryColumns (), s [:readSize ])
127127 if err != nil && ! errors .Is (err , io .EOF ) {
128128 return n , err
129+ } else if count == 0 && errors .Is (err , io .EOF ) {
130+ return 0 , io .EOF
129131 }
130132
131133 var passCount int // passCount tracks how many rows pass the predicate.
@@ -196,6 +198,12 @@ func checkPredicate(p Predicate, lookup map[Column]int, row Row) bool {
196198 case OrPredicate :
197199 return checkPredicate (p .Left , lookup , row ) || checkPredicate (p .Right , lookup , row )
198200
201+ case NotPredicate :
202+ return ! checkPredicate (p .Inner , lookup , row )
203+
204+ case FalsePredicate :
205+ return false
206+
199207 case EqualPredicate :
200208 columnIndex , ok := lookup [p .Column ]
201209 if ! ok {
@@ -350,7 +358,7 @@ func (r *Reader) validatePredicate() error {
350358 err = process (p .Column )
351359 case FuncPredicate :
352360 err = process (p .Column )
353- case AndPredicate , OrPredicate , nil :
361+ case AndPredicate , OrPredicate , NotPredicate , FalsePredicate , nil :
354362 // No columns to process.
355363 default :
356364 panic (fmt .Sprintf ("dataset.Reader.validatePredicate: unsupported predicate type %T" , p ))
@@ -422,7 +430,7 @@ func (r *Reader) fillPrimaryMask(mask *bitmask.Mask) {
422430 process (p .Column )
423431 case FuncPredicate :
424432 process (p .Column )
425- case AndPredicate , OrPredicate , nil :
433+ case AndPredicate , OrPredicate , NotPredicate , FalsePredicate , nil :
426434 // No columns to process.
427435 default :
428436 panic (fmt .Sprintf ("dataset.Reader.fillPrimaryMask: unsupported predicate type %T" , p ))
@@ -463,6 +471,25 @@ func (r *Reader) buildPredicateRanges(ctx context.Context, p Predicate) (rowRang
463471 }
464472 return unionRanges (nil , left , right ), nil
465473
474+ case NotPredicate :
475+ // De Morgan's laws must be applied to reduce the NotPredicate to a set of
476+ // predicates that can be applied to pages.
477+ //
478+ // See comment on [simplifyNotPredicate] for more information.
479+ simplified , err := simplifyNotPredicate (p )
480+ if err != nil {
481+ // Predicate can't be simplfied, so we permit the full range.
482+ var rowsCount uint64
483+ for _ , column := range r .dl .AllColumns () {
484+ rowsCount = max (rowsCount , uint64 (column .ColumnInfo ().RowsCount ))
485+ }
486+ return rowRanges {{Start : 0 , End : rowsCount - 1 }}, nil
487+ }
488+ return r .buildPredicateRanges (ctx , simplified )
489+
490+ case FalsePredicate :
491+ return nil , nil // No valid ranges.
492+
466493 case EqualPredicate :
467494 return r .buildColumnPredicateRanges (ctx , p .Column , p )
468495
@@ -489,6 +516,67 @@ func (r *Reader) buildPredicateRanges(ctx context.Context, p Predicate) (rowRang
489516 }
490517}
491518
519+ // simplifyNotPredicate applies De Morgan's laws to a NotPredicate to permit
520+ // page filtering.
521+ //
522+ // While during evaluation, a NotPredicate inverts the result of the inner
523+ // predicate, the same can't be done for page filtering. For example, imagine
524+ // that a page is included from a rule "a > 10." If we inverted that inclusion,
525+ // we may be incorrectly filtering out that page, as that page may also have
526+ // values less than 10.
527+ //
528+ // To correctly apply page filtering to a NotPredicate, we reduce the
529+ // NotPredicate to a set of predicates that can be applied to pages. This may
530+ // result in other NotPredicates that also need to be simplified.
531+ //
532+ // If the NotPredicate can't be simplified, simplifyNotPredicate returns an
533+ // error.
534+ func simplifyNotPredicate (p NotPredicate ) (Predicate , error ) {
535+ switch inner := p .Inner .(type ) {
536+ case AndPredicate : // De Morgan's law: !(A && B) == !A || !B
537+ return OrPredicate {
538+ Left : NotPredicate {Inner : inner .Left },
539+ Right : NotPredicate {Inner : inner .Right },
540+ }, nil
541+
542+ case OrPredicate : // De Morgan's law: !(A || B) == !A && !B
543+ return AndPredicate {
544+ Left : NotPredicate {Inner : inner .Left },
545+ Right : NotPredicate {Inner : inner .Right },
546+ }, nil
547+
548+ case NotPredicate : // De Morgan's law: !!A == A
549+ return inner .Inner , nil
550+
551+ case FalsePredicate :
552+ return nil , fmt .Errorf ("can't simplify FalsePredicate" )
553+
554+ case EqualPredicate : // De Morgan's law: !(A == B) == A != B == A < B || A > B
555+ return OrPredicate {
556+ Left : LessThanPredicate (inner ),
557+ Right : GreaterThanPredicate (inner ),
558+ }, nil
559+
560+ case GreaterThanPredicate : // De Morgan's law: !(A > B) == A <= B
561+ return OrPredicate {
562+ Left : EqualPredicate (inner ),
563+ Right : LessThanPredicate (inner ),
564+ }, nil
565+
566+ case LessThanPredicate : // De Morgan's law: !(A < B) == A >= B
567+ return OrPredicate {
568+ Left : EqualPredicate (inner ),
569+ Right : GreaterThanPredicate (inner ),
570+ }, nil
571+
572+ case FuncPredicate :
573+ return nil , fmt .Errorf ("can't simplify FuncPredicate" )
574+
575+ default :
576+ panic (fmt .Sprintf ("unsupported predicate type %T" , inner ))
577+ }
578+ }
579+
492580// buildColumnPredicateRanges returns a set of rowRanges that are valid based
493581// on whether EqualPredicate, GreaterThanPredicate, or LessThanPredicate may be
494582// true for each page in a column.
@@ -536,10 +624,10 @@ func (r *Reader) buildColumnPredicateRanges(ctx context.Context, c Column, p Pre
536624 switch p := p .(type ) {
537625 case EqualPredicate : // EqualPredicate may be true if p.Value is inside the range of the page.
538626 include = CompareValues (p .Value , minValue ) >= 0 && CompareValues (p .Value , maxValue ) <= 0
539- case GreaterThanPredicate : // GreaterThanPredicate may be true if p.Value is greater than the min value.
540- include = CompareValues (p .Value , minValue ) > 0
541- case LessThanPredicate : // LessThanPredicate may be true if p.Value is less than the max value.
542- include = CompareValues (p .Value , maxValue ) < 0
627+ case GreaterThanPredicate : // GreaterThanPredicate may be true if maxValue of a page is greater than p.Value
628+ include = CompareValues (maxValue , p .Value ) > 0
629+ case LessThanPredicate : // LessThanPredicate may be true if minValue of a page is less than p.Value
630+ include = CompareValues (minValue , p .Value ) < 0
543631 default :
544632 panic (fmt .Sprintf ("unsupported predicate type %T" , p ))
545633 }
0 commit comments