Re: SLOPE - Planner optimizations on monotonic expressions.

From: Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>
To: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
Cc: pgsql-hackers(at)lists(dot)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-07-17 21:16:23
Message-ID: CAN4CZFMJZWaZMYqn01D+KVY+fz-rWFOHojwy6c-mXftBOjWEoQ@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

- if (enable_slope && index->rel->reloptkind == RELOPT_BASEREL)
+ if (enable_slope)
{
...
- if (cpathkey)
- {
- /*
...

Isn't that cpathkey if/else needed in the !enable_slope case?

CREATE TABLE t (a int);
INSERT INTO t SELECT g FROM generate_series(1, 10000) g;
CREATE INDEX ON t (a);
ANALYZE t;

CREATE TABLE u (a int);
INSERT INTO u SELECT g FROM generate_series(1, 10000) g;
CREATE INDEX ON u (a);
ANALYZE u;

SET enable_seqscan = off;
SET enable_bitmapscan = off;

SET enable_slope = off;
EXPLAIN (COSTS OFF) SELECT a FROM t ORDER BY a LIMIT 5;

SET enable_hashjoin = off;
SET enable_nestloop = off;
EXPLAIN (COSTS OFF) SELECT * FROM t JOIN u USING (a);

I also realized one more timestamp issue: timestamptz -> timestamp /
date is not monotonic at a DST change:

SET TimeZone = 'America/New_York';
CREATE TABLE tsdst (x timestamptz);
-- in UTC these are ascending, in EDT->ESC descending
INSERT INTO tsdst VALUES ('2023-11-05 05:59:00+00'), ('2023-11-05 06:00:00+00');
CREATE INDEX ON tsdst (x);
ANALYZE tsdst;

SET enable_seqscan = off;
SET enable_bitmapscan = off;

SELECT x, x::timestamp AS local_ts FROM tsdst ORDER BY x::timestamp;
SET enable_slope = off;
SELECT x, x::timestamp AS local_ts FROM tsdst ORDER BY x::timestamp;

> Additionally,
> 0003 lets the planner ignore NULLS FIRST or NULLS LAST in a pathkey
> if the target expression is known to be NOT NULL. This change is is
> independent of SLOPE implementation.

This patch has an issue in the null handling:

CREATE TABLE l (a int); -- one join column can be NULL
CREATE TABLE r (b int NOT NULL); -- other join column NOT NULL
CREATE INDEX ON l (a);
INSERT INTO l VALUES (1), (2), (NULL);
INSERT INTO r VALUES (1), (2);

SELECT l.a FROM l LEFT JOIN r ON l.a = r.b ORDER BY l.a NULLS FIRST;

+ <para>
+ <varname>MONOTONICFUNC_INCREASING</varname>: <literal>x1 > x2</literal>
+ implies <literal>f(x1) >= f(x2)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <varname>MONOTONICFUNC_DECREASING</varname>: <literal>x1 > x2</literal>
+ implies <literal>f(x1) >= f(x2)</literal>
+ </para>

Decreasing should be <= ?

+ <listitem>
+ <para>
+ <varname>MONOTONICFUNC_BOTH</varname>: both
+ <varname>MONOTONICFUNC_DECREASING</varname> and
+ <varname>MONOTONICFUNC_DECREASING</varname>, so
+ <literal>f(x1) = f(x2)</literal> for any valid
+ <literal>x1</literal> and <literal>x1</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <varname>MONOTONICFUNC_BOTH</varname>: the order of
<literal>f(x)</literal>
+ cannot be infeered from the order of <literal>x</literal>
+ </para>
+ </listitem>

DECREASING and BOTH duplicated, both seem to be mistakes.

+CREATE TABLE (created_at timestamp, ...);
+CREATE INDEX ON orders(created_at);

CREATE TABLE orders?

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Andres Freund 2026-07-17 21:18:18 Re: Why clearing the VM doesn't require registering vm buffer in wal record
Previous Message Andres Freund 2026-07-17 21:15:05 Re: Possible Visibility Map corruption in supported branches?