Summary
src/operators/>.sql and src/operators/>=.sql declare every CREATE OPERATOR with RESTRICT = scalarltsel, JOIN = scalarltjoinsel. Those are the "less-than" selectivity estimators. PostgreSQL ships symmetric estimators that should be used instead:
| Operator |
Should be |
> |
RESTRICT = scalargtsel, JOIN = scalargtjoinsel |
>= |
RESTRICT = scalargesel, JOIN = scalargejoinsel |
The inner ore_block_u64_8_256 operators on the underlying domain type are declared correctly (src/ore_block_u64_8_256/operators.sql uses scalargtsel / scalargejoinsel), so the inconsistency is only at the eql_v2_encrypted outer layer.
Impact
Wrong selectivity estimates feed the planner inaccurate row counts for > and >= predicates. Symptoms:
- Index-vs-seq-scan flips on the wrong threshold for range queries.
- Join order chosen on bad cardinality assumptions.
ORDER BY + LIMIT plans can pick the wrong access path when the planner thinks WHERE col > $1 is selective when it isn't (or vice versa).
The bug is silent until cardinality matters. With the range-operator inlining in #211, bare-form range queries now structurally match functional ORE indexes, which makes selectivity hints load-bearing for plan choice — so the cost of leaving this wrong is higher than it was pre-2.3.
Scope
Three CREATE OPERATOR blocks in each of src/operators/>.sql and src/operators/>=.sql (six total), plus regenerated release/ artefacts. No behaviour change for individual query results — only planner estimates. No upgrade note needed.
Detected by
CodeRabbit on #211 review. PR scope was operator inlining, so this was deferred.
Verification
Compare EXPLAIN ANALYZE plans on a 100k-row Block ORE column for WHERE col > $1 (high-selectivity vs. low-selectivity bounds) before/after the change; expected row estimates should track actual within a constant factor.
Summary
src/operators/>.sqlandsrc/operators/>=.sqldeclare everyCREATE OPERATORwithRESTRICT = scalarltsel, JOIN = scalarltjoinsel. Those are the "less-than" selectivity estimators. PostgreSQL ships symmetric estimators that should be used instead:>RESTRICT = scalargtsel, JOIN = scalargtjoinsel>=RESTRICT = scalargesel, JOIN = scalargejoinselThe inner
ore_block_u64_8_256operators on the underlying domain type are declared correctly (src/ore_block_u64_8_256/operators.sqlusesscalargtsel/scalargejoinsel), so the inconsistency is only at theeql_v2_encryptedouter layer.Impact
Wrong selectivity estimates feed the planner inaccurate row counts for
>and>=predicates. Symptoms:ORDER BY+LIMITplans can pick the wrong access path when the planner thinksWHERE col > $1is selective when it isn't (or vice versa).The bug is silent until cardinality matters. With the range-operator inlining in #211, bare-form range queries now structurally match functional ORE indexes, which makes selectivity hints load-bearing for plan choice — so the cost of leaving this wrong is higher than it was pre-2.3.
Scope
Three
CREATE OPERATORblocks in each ofsrc/operators/>.sqlandsrc/operators/>=.sql(six total), plus regeneratedrelease/artefacts. No behaviour change for individual query results — only planner estimates. No upgrade note needed.Detected by
CodeRabbit on #211 review. PR scope was operator inlining, so this was deferred.
Verification
Compare
EXPLAIN ANALYZEplans on a 100k-row Block ORE column forWHERE col > $1(high-selectivity vs. low-selectivity bounds) before/after the change; expected row estimates should track actual within a constant factor.