SLOPE - Planner optimizations on monotonic expressions.

Lists: pgsql-hackers
From: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
To: pgsql-hackers(at)postgresql(dot)org
Subject: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-02-11 21:02:53
Message-ID: CAE8JnxN8kLUMygXb33LC-UPGyTC0cTcCyeWUdfEoz=OBo3aT5g@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

Motivation:
Consider a table data with an indexed timestamp column ts,
the query `SELECT ts::date, count(1) GROUP BY 1;` requires
ts::date to be sorted, but the planner is not aware of the fact
that ts::date will ordered whenever ts is ordered.

This includes slope information to several builtin functions.
type casting, addition, subtraction, common mathematical functions,
e.g. atan, sinh, log, exp, erf, etc. And some date manipulation function.
This may not be complete, but already covers obvious cases.

e.g. SELECT created_at::date, count(1) FROM tasks GROUP BY 1;

can use an index on created_at, to count tasks per day.

You can find examples of howthis feature can be useful in the test file,
including
- Ordered outputs without a sorting node.
- GroupAggregate used directly on the index scan
- MinMaxAggregate replaced by Index scan + limit.

Attachment Content-Type Size
0002-SLOPE-Builtin-support.patch application/octet-stream 52.7 KB
0003-SLOPE-Tests.patch application/octet-stream 21.8 KB
0001-SLOPE-Analysis-Machinery.patch application/octet-stream 13.6 KB

From: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
To: pgsql-hackers(at)lists(dot)postgresql(dot)org
Cc: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-02-18 21:51:03
Message-ID: 177145146356.628.9602820388536326984.pgcf@coridan.postgresql.org
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

Hi Hackers,

Is there anything else I can do to move this forward?
Anyone could review review? This one should be a quick review!

Regards,
Alexandre


From: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
To: pgsql-hackers(at)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-03-20 06:53:32
Message-ID: CAE8JnxN_XzS1DgmEvEizbg7jXPgUwJ853uorciA5X8p+=9dYvA@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

v2 updates prosupport function OIDs, as the ones I initially used were
taken while this waits for review.

This patch adds paths where 'order by f(x)' is satisfied by an index on x
if f(x) is monotonic.

You can find examples of how this feature can be useful in the test file,
including
- Ordered outputs without a sorting node.
- GroupAggregate used directly on the index scan
- MinMaxAggregate replaced by Index scan + limit.

My favourite use case is

+explain (costs off, verbose)
+select date_trunc('month', ts), count(*)
+from src
+group by 1;
+ QUERY PLAN
+------------------------------------------------------
+ GroupAggregate
+ Output: (date_trunc('month'::text, ts)), count(*)
+ Group Key: date_trunc('month'::text, src.ts)
+ -> Index Only Scan using src_ts_idx on public.src
+ Output: date_trunc('month'::text, ts)
+(5 rows)

Where we can take advantage of an index on timestamp src(ts) to fulfill a
query aggregating monthly. With this feature the same timestamp column can
be used to either sort the rows, or aggregate by different intervals, e.g.
days, weeks, months or years. Great for reports or dashboards on busy
tables, as we only need one index.

On Wed, Feb 11, 2026 at 9:02 PM Alexandre Felipe <
o(dot)alexandre(dot)felipe(at)gmail(dot)com> wrote:

> Motivation:
> Consider a table data with an indexed timestamp column ts,
> the query `SELECT ts::date, count(1) GROUP BY 1;` requires
> ts::date to be sorted, but the planner is not aware of the fact
> that ts::date will ordered whenever ts is ordered.
>
> This includes slope information to several builtin functions.
> type casting, addition, subtraction, common mathematical functions,
> e.g. atan, sinh, log, exp, erf, etc. And some date manipulation function.
> This may not be complete, but already covers obvious cases.
>
> e.g. SELECT created_at::date, count(1) FROM tasks GROUP BY 1;
>
> can use an index on created_at, to count tasks per day.
>
> You can find examples of howthis feature can be useful in the test file,
> including
> - Ordered outputs without a sorting node.
> - GroupAggregate used directly on the index scan
> - MinMaxAggregate replaced by Index scan + limit.
>
>
>

Attachment Content-Type Size
v2-0003-SLOPE-Tests.patch application/octet-stream 21.8 KB
v2-0001-SLOPE-Analysis-Machinery.patch application/octet-stream 13.6 KB
v2-0002-SLOPE-Builtin-support.patch application/octet-stream 52.7 KB

From: Corey Huinker <corey(dot)huinker(at)gmail(dot)com>
To: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
Cc: pgsql-hackers(at)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-03-25 05:18:12
Message-ID: CADkLM=c6-UuLNpN_O6e0Cuv7RWg1jS0bOd5WK_AJuPzR7JAD3Q@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

>
> +explain (costs off, verbose)
> +select date_trunc('month', ts), count(*)
> +from src
> +group by 1;
> + QUERY PLAN
> +------------------------------------------------------
> + GroupAggregate
> + Output: (date_trunc('month'::text, ts)), count(*)
> + Group Key: date_trunc('month'::text, src.ts)
> + -> Index Only Scan using src_ts_idx on public.src
> + Output: date_trunc('month'::text, ts)
> +(5 rows)
>

That's a good one.

+/* Slope type for representing monotonicity */
+typedef int8 Slope;
+#define SLOPE_ANY 0 /* 0b00 - unknown/either (safe default) */
+#define SLOPE_ASC 1 /* 0b01 - ascending (descending blocked) */
+#define SLOPE_DESC 2 /* 0b10 - descending (ascending blocked) */
+#define SLOPE_CONST 3 /* 0b11 - constant (both blocked) */

The MonotonicFunction enum seems like a good pattern to follow here.

Nitpick: it's a slope sign (+/-) rather than a slope itself, which to me
implies a scalar. I can't think of a good singular word for it, either.

+ * If the result is SLOPE_ASC or SLOPE_DESC, *underlying_expr is set to the
+ * by checking the slopes of the function arguments and the expression
+ * passed combined as follows:

"is set to the by" - seems like you left out a word here.

Patches 0002-0003 would have to get committed at the same time, but I see
why you separated them for clarity.

0002 is missing the catversion bump but that's fine at this early stage.

So, how would this work with a function like left() with a positive 2nd
param (assuming specified collation matches the index)? I'm also curious if
NULLS FIRST/LAST will throw this off.


From: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
To: Corey Huinker <corey(dot)huinker(at)gmail(dot)com>
Cc: pgsql-hackers(at)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-03-25 14:47:58
Message-ID: CAE8JnxP=2_eOEMvoq0KHopUHfv=oRbesbL1p48qo-G853v525A@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

Thank you for your review Corey,

On Wed, Mar 25, 2026 at 5:18 AM Corey Huinker <corey(dot)huinker(at)gmail(dot)com>
wrote:

> +explain (costs off, verbose)
>> +select date_trunc('month', ts), count(*)
>> +from src
>> +group by 1;
>> + QUERY PLAN
>> +------------------------------------------------------
>> + GroupAggregate
>> + Output: (date_trunc('month'::text, ts)), count(*)
>> + Group Key: date_trunc('month'::text, src.ts)
>> + -> Index Only Scan using src_ts_idx on public.src
>> + Output: date_trunc('month'::text, ts)
>> +(5 rows)
>>
>
> That's a good one.
>

I am glad you noted, I waited for a while for this moment :)

> +/* Slope type for representing monotonicity */
> +typedef int8 Slope;
> +#define SLOPE_ANY 0 /* 0b00 - unknown/either (safe default) */
> +#define SLOPE_ASC 1 /* 0b01 - ascending (descending blocked) */
> +#define SLOPE_DESC 2 /* 0b10 - descending (ascending blocked) */
> +#define SLOPE_CONST 3 /* 0b11 - constant (both blocked) */
>
> The MonotonicFunction enum seems like a good pattern to follow here.
>

Learning about the existence of that now

typedef enum MonotonicFunction
{
MONOTONICFUNC_NONE = 0,
MONOTONICFUNC_INCREASING = (1 << 0),
MONOTONICFUNC_DECREASING = (1 << 1),
MONOTONICFUNC_BOTH = MONOTONICFUNC_INCREASING | MONOTONICFUNC_DECREASING,
} MonotonicFunction;

So, BOTH means that the function is both increasing and decreasing, thus it
is constant.
Simply replacing the SLOPE_* by the corresponding MONOTONICFUNC_* does the
job.
But now using an enum we will have 4 bytes per argument.

Nitpick: it's a slope sign (+/-) rather than a slope itself, which to me
> implies a scalar. I can't think of a good singular word for it, either.
>

Solved if we use MonotonicFunction I guess. But the word slope isn't always
used to refer to derivatives.
And here we are mostly dealing with functions that are constant or
discontinuous, i.e. they never have a non-zero real derivative.

+ * If the result is SLOPE_ASC or SLOPE_DESC, *underlying_expr is set to the
> + * by checking the slopes of the function arguments and the expression
> + * passed combined as follows:
>
> "is set to the by" - seems like you left out a word here.
>

rewording like this

* The contribution of each argument to the final slope of the function
* determined by the slope of the function with respect to an argument
* and the slope of the underlying expression expression passed to it
* as follows:

> Patches 0002-0003 would have to get committed at the same time, but I see
> why you separated them for clarity.
>

>
0002 is missing the catversion bump but that's fine at this early stage.
>
changed catversion this time

So, how would this work with a function like left() with a positive 2nd
> param (assuming specified collation matches the index)?
>
That would require a separate prosupport function, it would have to do some
extra work, checking if the second argument is a positive constant at
planning time.
I'm happy to do that if there is interest, but I would keep after this.

> I'm also curious if NULLS FIRST/LAST will throw this off.
>

All that this will do is, when considering index scans, check if the
requested order
matches the index order. And whether we have to do a backwards or forwards
scan.

+ if (mono_decreasing)
+ {
+ reverse_sort = !reverse_sort;
+ nulls_first = !nulls_first;
+ }

I think this covers all the cases under consideration.

Added more test cases at the end of 0003.

Is there a specialized nulls first/last sort or does it use a generic sort?

Regards,
Alexandre

Attachment Content-Type Size
v3-0001-SLOPE-Analysis-Machinery.patch application/octet-stream 14.7 KB
v3-0003-SLOPE-Tests.patch application/octet-stream 28.1 KB
v3-0002-SLOPE-Builtin-support.patch application/octet-stream 53.3 KB

From: Corey Huinker <corey(dot)huinker(at)gmail(dot)com>
To: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
Cc: pgsql-hackers(at)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-03-25 17:26:54
Message-ID: CADkLM=fYpdyRjVb3cYiLsG=GSa4sjXCCZ6pGTQFust4Kn10GZg@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

>
> The MonotonicFunction enum seems like a good pattern to follow here.
>>
>
> Learning about the existence of that now
>
> typedef enum MonotonicFunction
> {
> MONOTONICFUNC_NONE = 0,
> MONOTONICFUNC_INCREASING = (1 << 0),
> MONOTONICFUNC_DECREASING = (1 << 1),
> MONOTONICFUNC_BOTH = MONOTONICFUNC_INCREASING | MONOTONICFUNC_DECREASING,
> } MonotonicFunction;
>
> So, BOTH means that the function is both increasing and decreasing, thus
> it is constant.
> Simply replacing the SLOPE_* by the corresponding MONOTONICFUNC_* does the
> job.
> But now using an enum we will have 4 bytes per argument.
>

Sorry, I wasn't suggesting that you use MonotonicFunction outright, only
that you follow it's pattern (make an enum, if you're needing specific bit
patterns do it this way, etc. We often use enums as a way for switch
statements to warn us when we're missing values.

>
> So, how would this work with a function like left() with a positive 2nd
>> param (assuming specified collation matches the index)?
>>
> That would require a separate prosupport function, it would have to do
> some extra work, checking if the second argument is a positive constant at
> planning time.
> I'm happy to do that if there is interest, but I would keep after this.
>
>
>> I'm also curious if NULLS FIRST/LAST will throw this off.
>>
>
> All that this will do is, when considering index scans, check if the
> requested order
> matches the index order. And whether we have to do a backwards or forwards
> scan.
>

What I meant was will we need a custom function to inspect parameters such
that we *do* try the index optimization on substr(foo,x,..) if and only if
x = 1?

It almost seems like what we need is "does f(x) preserve the leading sort
bits of x, and reduce the trailing bits to ties?"

Is there a specialized nulls first/last sort or does it use a generic sort?
>

No, it just fed into my wondering about situations where the ordering of
f(x) would be modified away from the ordering of x.

Oh, one other thing I am curious if any other reviewers like/dislike the

static const Slope pattern[2] = {SLOPE_ASC, SLOPE_DESC};

pattern. I haven't seen that one done before, and I'm wondering if we need
some comments to explain how it works so that people don't start looking
for the pfree that they'll never find.


From: Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>
To: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
Cc: Corey Huinker <corey(dot)huinker(at)gmail(dot)com>, pgsql-hackers(at)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-03-25 20:57:23
Message-ID: CAN4CZFPFhdh+ky-554yMOXo-u8ALgBJKEf_a2vZOtWLuxtKDBw@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

Hello

- proname => 'time_pl_interval', prorettype => 'time',
- proargtypes => 'time interval', prosrc => 'time_pl_interval' },
+ proname => 'time_pl_interval', prosupport => 'addition_slope_support',
+ prorettype => 'time', proargtypes => 'time interval',
+ prosrc => 'time_pl_interval' },

I think this is incorrect, time can wrap around and isn't monotonic.

CREATE TABLE time_wrap_test (t time PRIMARY KEY);
INSERT INTO time_wrap_test VALUES
('20:00'), ('21:00'), ('22:00'), ('23:00'), ('23:30'),
('00:00'), ('01:00'), ('02:00'), ('03:00'), ('04:00');
SELECT t, t + interval '3 hours' AS t_plus_3h
FROM time_wrap_test
ORDER BY t + interval '3 hours';

+ /* Check each index on this relation */
+ foreach(lc, rel->indexlist)
+ {
+ IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);

Isn't a sortopfamily check missing from this?

> Added one line to .gitignore file because I like to keep
> data related to this in the project workarea, e.g.
> ".install", ".dbdata", ".patches", and I don't want those

+
+# ignore hidden files
+.*

This doesn't seem related to the patch.


From: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
To: Corey Huinker <corey(dot)huinker(at)gmail(dot)com>
Cc: pgsql-hackers(at)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-03-25 22:22:03
Message-ID: CAE8JnxPHZMtY-yujHHtDVB_wL7QWhu7epKCnHDqwzZ2JZVYa4g@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

On Wed, Mar 25, 2026 at 5:27 PM Corey Huinker <corey(dot)huinker(at)gmail(dot)com>
wrote:

> The MonotonicFunction enum seems like a good pattern to follow here.
>>>
>>
>> Learning about the existence of that now
>>
>> typedef enum MonotonicFunction
>> {
>> MONOTONICFUNC_NONE = 0,
>> MONOTONICFUNC_INCREASING = (1 << 0),
>> MONOTONICFUNC_DECREASING = (1 << 1),
>> MONOTONICFUNC_BOTH = MONOTONICFUNC_INCREASING | MONOTONICFUNC_DECREASING,
>> } MonotonicFunction;
>>
>> So, BOTH means that the function is both increasing and decreasing, thus
>> it is constant.
>> Simply replacing the SLOPE_* by the corresponding MONOTONICFUNC_* does
>> the job.
>> But now using an enum we will have 4 bytes per argument.
>>
>
> Sorry, I wasn't suggesting that you use MonotonicFunction outright, only
> that you follow it's pattern (make an enum, if you're needing specific bit
> patterns do it this way, etc. We often use enums as a way for switch
> statements to warn us when we're missing values.
>

No worries, that was easy as the values coincided.

>> So, how would this work with a function like left() with a positive 2nd
>>> param (assuming specified collation matches the index)?
>>>
>> That would require a separate prosupport function, it would have to do
>> some extra work, checking if the second argument is a positive constant at
>> planning time.
>> I'm happy to do that if there is interest, but I would keep after this.
>>
>>
>>> I'm also curious if NULLS FIRST/LAST will throw this off.
>>>
>>
>> All that this will do is, when considering index scans, check if the
>> requested order
>> matches the index order. And whether we have to do a backwards or
>> forwards scan.
>>
>
> What I meant was will we need a custom function to inspect parameters such
> that we *do* try the index optimization on substr(foo,x,..) if and only if
> x = 1?
>
What I would do in this case is
if(x is constant and x > 0) {
return {INCREASING, BOTH, NONE}
}else{
return {NONE, BOTH, NONE}
}

It almost seems like what we need is "does f(x) preserve the leading sort
> bits of x, and reduce the trailing bits to ties?"
>

Yes, this is definitely the most common case, the few exceptions are there
for completeness, but I don't believe people will want to order by e.g.
atan(x) very often.

Is there a specialized nulls first/last sort or does it use a generic sort?
>>
>
> No, it just fed into my wondering about situations where the ordering of
> f(x) would be modified away from the ordering of x.
>
There is one caveat, my current approach for nulls first/last is valid only
if f(NULL) is NULL

> Oh, one other thing I am curious if any other reviewers like/dislike the
>
> static const Slope pattern[2] = {SLOPE_ASC, SLOPE_DESC};
>
> pattern. I haven't seen that one done before, and I'm wondering if we need
> some comments to explain how it works so that people don't start looking
> for the pfree that they'll never find.
>

OK, adding this
+ req.expr = (Node *) expr;
+ /* prosupport function sets req.slopes to a static
+ * pointer, no pfree is required.
+ */
+ req.slopes = NULL;


From: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
To: Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>
Cc: Corey Huinker <corey(dot)huinker(at)gmail(dot)com>, pgsql-hackers(at)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-03-25 23:34:40
Message-ID: CAE8JnxMq5mrfPAUfc_WwTozd622+SkbcLY8hHGAtqEUE4OZpkw@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

On Wed, Mar 25, 2026 at 8:57 PM Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>
wrote:

> Hello
>
> - proname => 'time_pl_interval', prorettype => 'time',
> - proargtypes => 'time interval', prosrc => 'time_pl_interval' },
> + proname => 'time_pl_interval', prosupport => 'addition_slope_support',
> + prorettype => 'time', proargtypes => 'time interval',
> + prosrc => 'time_pl_interval' },
>
> I think this is incorrect, time can wrap around and isn't monotonic.
>
> CREATE TABLE time_wrap_test (t time PRIMARY KEY);
> INSERT INTO time_wrap_test VALUES
> ('20:00'), ('21:00'), ('22:00'), ('23:00'), ('23:30'),
> ('00:00'), ('01:00'), ('02:00'), ('03:00'), ('04:00');
> SELECT t, t + interval '3 hours' AS t_plus_3h
> FROM time_wrap_test
> ORDER BY t + interval '3 hours';
>

Good catch,
Can you think of any other type that would wrap,
int2, int4, int8, money, float4, float8, date, timestamp, timestamptz,
interval
all raise some error.
time and timetz wrap around.

@@ -4820,21 +4820,17 @@
prosrc => 'width_bucket_numeric' },

{ oid => '1747',
- proname => 'time_pl_interval', prosupport => 'addition_slope_support',
- prorettype => 'time', proargtypes => 'time interval',
- prosrc => 'time_pl_interval' },
+ proname => 'time_pl_interval', prorettype => 'time',
+ proargtypes => 'time interval', prosrc => 'time_pl_interval' },
{ oid => '1748',
- proname => 'time_mi_interval', prosupport => 'diff_slope_support',
- prorettype => 'time', proargtypes => 'time interval',
- prosrc => 'time_mi_interval' },
+ proname => 'time_mi_interval', prorettype => 'time',
+ proargtypes => 'time interval', prosrc => 'time_mi_interval' },
{ oid => '1749',
- proname => 'timetz_pl_interval', prosupport => 'addition_slope_support',
- prorettype => 'timetz', proargtypes => 'timetz interval',
- prosrc => 'timetz_pl_interval' },
+ proname => 'timetz_pl_interval', prorettype => 'timetz',
+ proargtypes => 'timetz interval', prosrc => 'timetz_pl_interval' },
{ oid => '1750',
- proname => 'timetz_mi_interval', prosupport => 'diff_slope_support',
- prorettype => 'timetz', proargtypes => 'timetz interval',
- prosrc => 'timetz_mi_interval' },
+ proname => 'timetz_mi_interval', prorettype => 'timetz',
+ proargtypes => 'timetz interval', prosrc => 'timetz_mi_interval' },

{ oid => '1764', descr => 'increment by one',
proname => 'numeric_inc', prorettype => 'numeric', proargtypes =>
'numeric',

> + /* Check each index on this relation */
> + foreach(lc, rel->indexlist)
> + {
> + IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);
>
> Isn't a sortopfamily check missing from this?
>

You are probably right but I am confused here.
We could simply skip this analysis entirely if the index has a custom
operator family.
Do you think this would be enough?

> Added one line to .gitignore file because I like to keep
> > data related to this in the project workarea, e.g.
> > ".install", ".dbdata", ".patches", and I don't want those
>
> +
> +# ignore hidden files
> +.*
>
> This doesn't seem related to the patch.
>

Yes, it is not specific for this patch, just annoying that it doesn't
ignore hidden files.

PS.
The v3 patch failed on the CI checks, I was testing without assertions.
The problem was that v2-0001 had a
+ sortkey = mono_var;
modifying the sort key from the query, and this caused an assertion to fail.
v4 keeps the sort key untouched but has checks
ec_member_is_monotonic_in
in both pathkey_is_monotonic_of to determine if the available order is
useful,
and again in build_index_pathkeys that creates a trivial equivalence class
to be used in pathkey_is_monotonic_of with the index key if it doesn't
belong to one already.
Maybe this can be improved later, but it is likely better than a broken
build.

Regards,
Alexandre

Attachment Content-Type Size
v4-0001-SLOPE-Analysis-Machinery.patch application/octet-stream 19.2 KB
v4-0002-SLOPE-Builtin-support.patch application/octet-stream 51.9 KB
v4-0003-SLOPE-Tests.patch application/octet-stream 28.2 KB

From: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
To: Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>
Cc: Corey Huinker <corey(dot)huinker(at)gmail(dot)com>, pgsql-hackers(at)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-03-26 09:51:56
Message-ID: CAE8JnxOiw8B1FXb-fBGO-03xpVtZuZubCbpoW9LytmfFNB35Lw@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

Zsolt,

v5 aims to prevent the elimination of the sort node if the index has a
custom sort operator family.

+ /* Check each index on this relation */
> + foreach(lc, rel->indexlist)
> + {
> + IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);
>
>>
> Isn't a sortopfamily check missing from this?
> You are probably right but I am confused here.
> We could simply skip this analysis entirely if the index has a custom
> operator family.
> Do you think this would be enough?
>

The benchmark table had these 24 indices:
19 single-column: a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p (all int),
ts (timestamp), v_float8 (float8), v_numeric (numeric)
4 multi-column: (a, b), (c, d, e), (ts, a), (v_float8, a)
1 primary key: id (serial)

Evaluating the effect of v5 on planning time (in microseconds)
ORDER BY Master Patched Delta Overhead
(nothing) 21.3 21.5 +0.2 ~1%
a 28.2 29.5 +1.3 ~5%
a + 1 29.2 32.4 +3.2 ~11%
a::float8 30.1 34.6 +4.5 ~15%
date_trunc(ts) 30.0 31.4 +1.4 ~5%

+ /*
+ * The query pathkey's opfamily is the default for
+ * the sort expression's type. If it matches the
+ * index opfamily, the index uses the standard
+ * ordering — no catalog lookup needed. Otherwise
+ * fall back to GetDefaultOpClass to handle the
+ * cross-type case (e.g. int4 cast to float8).
+ */
+ if (qpk->pk_opfamily != index->sortopfamily[i])
+ {
+ default_opclass = GetDefaultOpClass(
+ index->opcintype[i], BTREE_AM_OID);
+ default_opfamily = OidIsValid(default_opclass)
+ ? get_opclass_family(default_opclass)
+ : InvalidOid;
+
+ if (index->sortopfamily[i] != default_opfamily)
+ break;
+ }

Regards,
Alexandre

Attachment Content-Type Size
v5-0002-SLOPE-Builtin-support.patch application/octet-stream 51.9 KB
v5-0001-SLOPE-Analysis-Machinery.patch application/octet-stream 19.7 KB
v5-0003-SLOPE-Tests.patch application/octet-stream 28.2 KB

From: Dean Rasheed <dean(dot)a(dot)rasheed(at)gmail(dot)com>
To: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
Cc: Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>, Corey Huinker <corey(dot)huinker(at)gmail(dot)com>, pgsql-hackers(at)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-03-26 18:17:11
Message-ID: CAEZATCXHYc_NGLNbvn5Uj8JU2XCXME=JTjT_M9amCPwZWGrNvA@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

On Wed, 25 Mar 2026 at 23:35, Alexandre Felipe
<o(dot)alexandre(dot)felipe(at)gmail(dot)com> wrote:
>
> Good catch,
> Can you think of any other type that would wrap,

Arithmetic with intervals isn't monotonic in general because of the
way interval time units vary.

For example, '1 month - 29 days' is "positive" (in the sense that it
compares as greater than zero, and it compares equal to an interval of
'1 day'), but adding it to a date or timestamp may go forwards or
backwards, or not move at all, depending on the number of days in the
month.

Another example, is the interval '361 days', which compares as greater
than the interval '1 year' (because that's taken to be 12 months of 30
days each). But adding '361 days' to a date or timestamp advances it
by less than a year.

So ordering by an interval column can produce different results than
ordering by a fixed date/timestamp plus the interval.

Regards,
Dean


From: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
To: Dean Rasheed <dean(dot)a(dot)rasheed(at)gmail(dot)com>
Cc: Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>, Corey Huinker <corey(dot)huinker(at)gmail(dot)com>, pgsql-hackers(at)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-03-26 20:49:57
Message-ID: CAE8JnxP__hhKjP8uXKqGepg+GAjRy+Jthg=f-e24G3PML7ZdbA@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

I know it is less than a week from the end of the commitfest,
does it make sense to reduce the scope of this to get it in an
acceptable condition?

#1 GROUP BY
What motivated me to get this was queries with
GROUP BY date_trunc(x, timestamp)
GROUP BY timestamp::date
I tried to cover as many functions as possible, as I don't know how
other people might be creatively writing their queries.

#2 JOIN
There might be some joins that can be made a merge join
but that is not a big deal, and makes it much more complicated
happy to drop.

#3 ORDER BY
This case is a bit pointless, if we are ordering by f(x) we could
simply order by x and still use f(x) in the query. But is there
because I use the pathkey to get the grouping correct.

#3 min/max
This is a nice case where min(f(x)) is computed by an index scan
with limit 1, but again, one can simply use f(min(x)) to get the same
plan.

#4 Recursive slope analysis
While the recursive slope analysis is very elegant I know that
a lot of eyebrows will raise with a 15% planning time increase.
So I am happy to restrict this to the queries with GROUP BY
clause.

#5 Alternative explicit monotonicity
If you think that this is better, we could add two functions
increasing(x, f(x)) returns f(x)
decreasing(x, f(x)) returns f(x)
at planning time we accept the index paths, and at
run time we could check that the result does not violate the order
this could be even more general, e.g.

SELECT increasing(x, abs(x)) WHERE x >= 0
GROUP BY 1;

SELECT increasing(x, x * y) WHERE y >= 0
GROUP BY 1;

Do any of these give a good chance to have this in this commitfest
or should I just chill out?

On Thu, Mar 26, 2026 at 6:17 PM Dean Rasheed <dean(dot)a(dot)rasheed(at)gmail(dot)com>
wrote:

> On Wed, 25 Mar 2026 at 23:35, Alexandre Felipe
> <o(dot)alexandre(dot)felipe(at)gmail(dot)com> wrote:
> >
> > Good catch,
> > Can you think of any other type that would wrap,
>
> Arithmetic with intervals isn't monotonic in general because of the
> way interval time units vary.

For example, '1 month - 29 days' is "positive" (in the sense that it
> compares as greater than zero, and it compares equal to an interval of
> '1 day'), but adding it to a date or timestamp may go forwards or
> backwards, or not move at all, depending on the number of days in the
> month.

Now that you say it makes sense.

So ordering by an interval column can produce different results than
> ordering by a fixed date/timestamp plus the interval.
>

Removing interval arithmetic

Regards,
Alexandre


From: Dean Rasheed <dean(dot)a(dot)rasheed(at)gmail(dot)com>
To: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
Cc: Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>, Corey Huinker <corey(dot)huinker(at)gmail(dot)com>, pgsql-hackers(at)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-03-27 08:46:55
Message-ID: CAEZATCXY9sM6izR20tSZFmJ-7484qU=EvekiQmR8X+XkEV6QpA@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

On Thu, 26 Mar 2026 at 20:50, Alexandre Felipe
<o(dot)alexandre(dot)felipe(at)gmail(dot)com> wrote:
>
> I know it is less than a week from the end of the commitfest,
> does it make sense to reduce the scope of this to get it in an
> acceptable condition?
>
> [snip various options]
>
> Do any of these give a good chance to have this in this commitfest
> or should I just chill out?

I don't know. I haven't looked at the patches themselves in any
detail. I was merely replying to a specific question about
monotonicity with respect to a particular data type.

However, my impression from reading this thread is that the patches
haven't had a great deal of in-depth review yet, there are still a
number of design ideas that people might wish to explore in more
detail, it probably needs more careful analysis to verify correctness,
and more testing. That makes me think that it's not feasible to get
anything committable in less than a week, and it would be better to
defer this.

Regards,
Dean


From: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
To: Dean Rasheed <dean(dot)a(dot)rasheed(at)gmail(dot)com>
Cc: Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>, Corey Huinker <corey(dot)huinker(at)gmail(dot)com>, pgsql-hackers(at)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-03-27 09:29:03
Message-ID: CAE8JnxOcnzLFDgPTBZDhKG9bsSemHrUJ6p-jKgM-NJ8bvzCFQg@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

On Fri, Mar 27, 2026 at 8:47 AM Dean Rasheed <dean(dot)a(dot)rasheed(at)gmail(dot)com>
wrote:

> I don't know. I haven't looked at the patches themselves in any
> detail. I was merely replying to a specific question about
> monotonicity with respect to a particular data type.
>
> However, my impression from reading this thread is that the patches
> haven't had a great deal of in-depth review yet, there are still a
> number of design ideas that people might wish to explore in more
> detail, it probably needs more careful analysis to verify correctness,
> and more testing. That makes me think that it's not feasible to get
> anything committable in less than a week, and it would be better to
> defer this.

Thank you for your feedback, either way I am happy that at least
now it received some attention.

I am testing an iterative single variable/expression and to find the
simplest expression x that is the cause of all the variation in the
pathkey expression, saving this in the plan info, and thus, reduce
the per index work.

Regards,
Alexandre


From: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
To: Dean Rasheed <dean(dot)a(dot)rasheed(at)gmail(dot)com>
Cc: Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>, Corey Huinker <corey(dot)huinker(at)gmail(dot)com>, pgsql-hackers(at)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-04-05 21:28:35
Message-ID: CAE8JnxP75xOK9K7F0c74C9j4wnGEz9+xsuvwehExb3Lth3+3-A@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

Hi All,

I hope I am early enough for PG20, so v6 maintains the full scope.

0001 as the name suggests is just a benchmark, to get a baseline.
0002 is just a refactoring to ensure build_index_pathkeys is called once
per index.
master is calling once to produce forward pathkeys and once to produce
backward pathkeys.

Other questions are: Should we maybe do this in a way to support
monotonicity
for user defined functions too? Maybe use some per argument flags that can
retrieved without the call by oid?

v6 splits the pathkey monotonicity analysis in two parts.

If the pathkey expression depends on a single table, the innermost
sub expression that contains all variable terms is extracted during the
index
creation, and stored in slope_info.
When considering indices for pathkeys, check if the index key matches
the slope info source of variation, if it does, check the monotonicity.

Limitations:

Not supporting pathkeys [f(x), x], maybe useful for
queries SELECT OVER (PARTITION f(x) ORDER BY x)
I have an implementation on a 0006 patch but I think it would hurt the
overall
patchset quality.

Not working with joins where I expected a MergeJoin to be used.
Any hints here? Because I am not properly using equivalence classes? or
something else?

I see that `make_canonical_pathkey` does a list search for every index key.
expressions, the complexity is roughly quadratic with the number of indices.
I suspect that pointer chasing having a preallocated array
would already be better, as it would probably improve the memory locality.
Would it be worth investigating other data structures here, like hash or a
tree?
(I guess the answer will be no as that could hurt the very simple plans
with
a handful of indices).

Regards,
Alexandre

On Fri, Mar 27, 2026 at 9:29 AM Alexandre Felipe <
o(dot)alexandre(dot)felipe(at)gmail(dot)com> wrote:

>
>
> On Fri, Mar 27, 2026 at 8:47 AM Dean Rasheed <dean(dot)a(dot)rasheed(at)gmail(dot)com>
> wrote:
>
>> I don't know. I haven't looked at the patches themselves in any
>> detail. I was merely replying to a specific question about
>> monotonicity with respect to a particular data type.
>>
>> However, my impression from reading this thread is that the patches
>> haven't had a great deal of in-depth review yet, there are still a
>> number of design ideas that people might wish to explore in more
>> detail, it probably needs more careful analysis to verify correctness,
>> and more testing. That makes me think that it's not feasible to get
>> anything committable in less than a week, and it would be better to
>> defer this.
>
>
> Thank you for your feedback, either way I am happy that at least
> now it received some attention.
>
> I am testing an iterative single variable/expression and to find the
> simplest expression x that is the cause of all the variation in the
> pathkey expression, saving this in the plan info, and thus, reduce
> the per index work.
>
> Regards,
> Alexandre
>
>
>

Attachment Content-Type Size
v6-0001-benchmark.patch application/x-patch 4.8 KB
v6-0005-SLOPE-Planner-support.patch application/x-patch 35.8 KB
v6-0002-Optimized-reverse-pathkeys.patch application/x-patch 6.2 KB
v6-0004-SLOPE-catalog-changes.patch application/x-patch 19.7 KB
v6-0003-SLOPE-prosupport-definitions.patch application/x-patch 10.2 KB

From: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
To: Dean Rasheed <dean(dot)a(dot)rasheed(at)gmail(dot)com>
Cc: Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>, Corey Huinker <corey(dot)huinker(at)gmail(dot)com>, pgsql-hackers(at)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-04-06 08:29:34
Message-ID: CAE8JnxOgT5io0r-cCRuFqUa=yOLjngrtOw4-VH0wwUaAgfLnoA@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

Hi All,

I hope I am early enough for PG20, so v6 maintains the full scope.

0001 as the name suggests is just a benchmark, to get a baseline.
0002 is just a refactoring to ensure build_index_pathkeys is called once
per index.
master is calling once to produce forward pathkeys and once to produce
backward pathkeys.

Other questions are: Should we maybe do this in a way to support
monotonicity
for user defined functions too? Maybe use some per argument flags that can
retrieved without the call by oid?

v6 splits the pathkey monotonicity analysis in two parts.

If the pathkey expression depends on a single table, the innermost
sub expression that contains all variable terms is extracted during the
index
creation, and stored in slope_info.
When considering indices for pathkeys, check if the index key matches
the slope info source of variation, if it does, check the monotonicity.

Limitations:

Not supporting pathkeys [f(x), x], maybe useful for
queries SELECT OVER (PARTITION f(x) ORDER BY x)
I have an implementation on a 0006 patch but I think it would hurt the
overall
patchset quality.

Not working with joins where I expected a MergeJoin to be used.
Any hints here? Because I am not properly using equivalence classes? or
something else?

I see that `make_canonical_pathkey` does a list search for every index key.
expressions, the complexity is roughly quadratic with the number of indices.
I suspect that pointer chasing having a preallocated array
would already be better, as it would probably improve the memory locality.
Would it be worth investigating other data structures here, like hash or a
tree?
(I guess the answer will be no as that could hurt the very simple plans
with
a handful of indices).

PS. Only rebasing the previous patch set

Regards,
Alexandre

On Sun, Apr 5, 2026 at 10:28 PM Alexandre Felipe <
o(dot)alexandre(dot)felipe(at)gmail(dot)com> wrote:

> Hi All,
>
> I hope I am early enough for PG20, so v6 maintains the full scope.
>
> 0001 as the name suggests is just a benchmark, to get a baseline.
> 0002 is just a refactoring to ensure build_index_pathkeys is called once
> per index.
> master is calling once to produce forward pathkeys and once to produce
> backward pathkeys.
>
> Other questions are: Should we maybe do this in a way to support
> monotonicity
> for user defined functions too? Maybe use some per argument flags that can
> retrieved without the call by oid?
>
> v6 splits the pathkey monotonicity analysis in two parts.
>
> If the pathkey expression depends on a single table, the innermost
> sub expression that contains all variable terms is extracted during the
> index
> creation, and stored in slope_info.
> When considering indices for pathkeys, check if the index key matches
> the slope info source of variation, if it does, check the monotonicity.
>
> Limitations:
>
> Not supporting pathkeys [f(x), x], maybe useful for
> queries SELECT OVER (PARTITION f(x) ORDER BY x)
> I have an implementation on a 0006 patch but I think it would hurt the
> overall
> patchset quality.
>
> Not working with joins where I expected a MergeJoin to be used.
> Any hints here? Because I am not properly using equivalence classes? or
> something else?
>
> I see that `make_canonical_pathkey` does a list search for every index key.
> expressions, the complexity is roughly quadratic with the number of
> indices.
> I suspect that pointer chasing having a preallocated array
> would already be better, as it would probably improve the memory locality.
> Would it be worth investigating other data structures here, like hash or a
> tree?
> (I guess the answer will be no as that could hurt the very simple plans
> with
> a handful of indices).
>
>
> Regards,
> Alexandre
>
>
>
> On Fri, Mar 27, 2026 at 9:29 AM Alexandre Felipe <
> o(dot)alexandre(dot)felipe(at)gmail(dot)com> wrote:
>
>>
>>
>> On Fri, Mar 27, 2026 at 8:47 AM Dean Rasheed <dean(dot)a(dot)rasheed(at)gmail(dot)com>
>> wrote:
>>
>>> I don't know. I haven't looked at the patches themselves in any
>>> detail. I was merely replying to a specific question about
>>> monotonicity with respect to a particular data type.
>>>
>>> However, my impression from reading this thread is that the patches
>>> haven't had a great deal of in-depth review yet, there are still a
>>> number of design ideas that people might wish to explore in more
>>> detail, it probably needs more careful analysis to verify correctness,
>>> and more testing. That makes me think that it's not feasible to get
>>> anything committable in less than a week, and it would be better to
>>> defer this.
>>
>>
>> Thank you for your feedback, either way I am happy that at least
>> now it received some attention.
>>
>> I am testing an iterative single variable/expression and to find the
>> simplest expression x that is the cause of all the variation in the
>> pathkey expression, saving this in the plan info, and thus, reduce
>> the per index work.
>>
>> Regards,
>> Alexandre
>>
>>
>>

Attachment Content-Type Size
v6-0002-Optimized-reverse-pathkeys.patch application/octet-stream 6.2 KB
v6-0005-SLOPE-Planner-support.patch application/octet-stream 35.8 KB
v6-0001-benchmark.patch application/octet-stream 4.8 KB
v6-0003-SLOPE-prosupport-definitions.patch application/octet-stream 10.2 KB
v6-0004-SLOPE-catalog-changes.patch application/octet-stream 19.7 KB

From: Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>
To: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
Cc: Dean Rasheed <dean(dot)a(dot)rasheed(at)gmail(dot)com>, Corey Huinker <corey(dot)huinker(at)gmail(dot)com>, pgsql-hackers(at)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-04-06 23:09:33
Message-ID: CAN4CZFMDq-FMwLQitgAZOWW98R__Ajqy5EzJt3cBLMNtohJQnw@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

I think there's a bug with NULL handling:

CREATE TABLE slope_nulls_test (v int4);
INSERT INTO slope_nulls_test SELECT generate_series(1, 10);
INSERT INTO slope_nulls_test VALUES (NULL), (NULL);
CREATE INDEX slope_nulls_idx ON slope_nulls_test (v ASC NULLS FIRST);
ANALYZE slope_nulls_test;

SET enable_seqscan = off;

SELECT floor(v::float8), v FROM slope_nulls_test ORDER BY 1;

RESET enable_seqscan;
SET enable_indexscan = off;
SELECT floor(v::float8), v FROM slope_nulls_test ORDER BY 1;

{ oid => '2308', descr => 'nearest integer >= value',
- proname => 'ceil', prorettype => 'float8', proargtypes => 'float8',
- prosrc => 'dceil' },
+ proname => 'ceil', prosupport => 'arg0_asc_slope_support',
+ prorettype => 'float8', proargtypes => 'float8', prosrc => 'dceil' },
{ oid => '2320', descr => 'nearest integer >= value',
proname => 'ceiling', prorettype => 'float8', proargtypes => 'float8',
prosrc => 'dceil' },

Shouldn't 2320 also have the same change? Same for 1711/2167

{ oid => '183',
- proname => 'int42mi', prorettype => 'int4', proargtypes => 'int4 int2',
- prosrc => 'int42mi' },
+ proname => 'int42mi', prosupport => 'diff_slope_support',
+ prorettype => 'int4', proargtypes => 'int4 int2', prosrc => 'int42mi' },

Similarly shouldn't int24/int42 mul and div also be included?


From: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
To: Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>
Cc: Dean Rasheed <dean(dot)a(dot)rasheed(at)gmail(dot)com>, Corey Huinker <corey(dot)huinker(at)gmail(dot)com>, pgsql-hackers(at)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-04-07 07:50:23
Message-ID: CAE8JnxM_RsLBWnM9hwN=wyfp16c=q8b-WU8DSjbqBwBc=Jsabw@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

Thank you,
Valuable feedback

On Tue, Apr 7, 2026 at 12:09 AM Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>
wrote:

I think there's a bug with NULL handling

I was genuinely surprised that this was not caught by any test in the
regression.

There are 32 cases, next patch will verify both the plan and the output
FOR r IN
SELECT idx_dir, idx_nf, qry_dir, qry_nf, sign
FROM unnest(ARRAY['+','-']) AS sign,
unnest(ARRAY['ASC','DESC']) AS idx_dir,
unnest(ARRAY['FIRST','LAST']) AS idx_nf,
unnest(ARRAY['ASC','DESC']) AS qry_dir,
unnest(ARRAY['FIRST','LAST']) AS qry_nf

{ oid => '2308', descr => 'nearest integer >= value',
> - proname => 'ceil', prorettype => 'float8', proargtypes => 'float8',
> - prosrc => 'dceil' },
> + proname => 'ceil', prosupport => 'arg0_asc_slope_support',
> + prorettype => 'float8', proargtypes => 'float8', prosrc => 'dceil' },
> { oid => '2320', descr => 'nearest integer >= value',
> proname => 'ceiling', prorettype => 'float8', proargtypes => 'float8',
> prosrc => 'dceil' },
>
> Shouldn't 2320 also have the same change? Same for 1711/2167
>
> { oid => '183',
> - proname => 'int42mi', prorettype => 'int4', proargtypes => 'int4 int2',
> - prosrc => 'int42mi' },
> + proname => 'int42mi', prosupport => 'diff_slope_support',
> + prorettype => 'int4', proargtypes => 'int4 int2', prosrc => 'int42mi' },
>
> Similarly shouldn't int24/int42 mul and div also be included?
>

Here I am genuinely NOT surprised :) I will take another look there before
submitting the next patch.

Regards,
Alexandre


From: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
To: Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>
Cc: Dean Rasheed <dean(dot)a(dot)rasheed(at)gmail(dot)com>, Corey Huinker <corey(dot)huinker(at)gmail(dot)com>, pgsql-hackers(at)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-04-12 15:01:52
Message-ID: CAE8JnxObfX3bq7CS=rdy0U-vwWbNrx_cj5+PT1jwDvvTkkPkVw@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

Now including a 0006 patch supporting redundant pathkeys such as
[f(x), x], or [x, f(x)], motivated by queries like
queries SELECT OVER (PARTITION f(x) ORDER BY x)

On Tue, Apr 7, 2026 at 12:09 AM Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>
wrote:
> I think there's a bug with NULL handling

There were too many possibilities for my brain to enumerate, so I scripted
the
extraction and presentation of that in regress/sql/slope.sql in commit 0005.

sign | index_order | query_order | scan_method | example
------+------------------+------------------+-------------+------------
+ | ASC NULLS FIRST | ASC NULLS FIRST | Forward | NULL,1,2
+ | ASC NULLS LAST | ASC NULLS FIRST | Sort | NULL,1,2
+ | DESC NULLS FIRST | ASC NULLS FIRST | Sort | NULL,1,2
+ | DESC NULLS LAST | ASC NULLS FIRST | Backward | NULL,1,2
- | ASC NULLS FIRST | ASC NULLS FIRST | Sort | NULL,-2,-1
- | ASC NULLS LAST | ASC NULLS FIRST | Backward | NULL,-2,-1
- | DESC NULLS FIRST | ASC NULLS FIRST | Forward | NULL,-2,-1
- | DESC NULLS LAST | ASC NULLS FIRST | Sort | NULL,-2,-1
+ | ASC NULLS FIRST | ASC NULLS LAST | Sort | 1,2,NULL
+ | ASC NULLS LAST | ASC NULLS LAST | Forward | 1,2,NULL
+ | DESC NULLS FIRST | ASC NULLS LAST | Backward | 1,2,NULL
+ | DESC NULLS LAST | ASC NULLS LAST | Sort | 1,2,NULL
- | ASC NULLS FIRST | ASC NULLS LAST | Backward | -2,-1,NULL
- | ASC NULLS LAST | ASC NULLS LAST | Sort | -2,-1,NULL
- | DESC NULLS FIRST | ASC NULLS LAST | Sort | -2,-1,NULL
- | DESC NULLS LAST | ASC NULLS LAST | Forward | -2,-1,NULL
+ | ASC NULLS FIRST | DESC NULLS FIRST | Sort | NULL,2,1
+ | ASC NULLS LAST | DESC NULLS FIRST | Backward | NULL,2,1
+ | DESC NULLS FIRST | DESC NULLS FIRST | Forward | NULL,2,1
+ | DESC NULLS LAST | DESC NULLS FIRST | Sort | NULL,2,1
- | ASC NULLS FIRST | DESC NULLS FIRST | Forward | NULL,-1,-2
- | ASC NULLS LAST | DESC NULLS FIRST | Sort | NULL,-1,-2
- | DESC NULLS FIRST | DESC NULLS FIRST | Sort | NULL,-1,-2
- | DESC NULLS LAST | DESC NULLS FIRST | Backward | NULL,-1,-2
+ | ASC NULLS FIRST | DESC NULLS LAST | Backward | 2,1,NULL
+ | ASC NULLS LAST | DESC NULLS LAST | Sort | 2,1,NULL
+ | DESC NULLS FIRST | DESC NULLS LAST | Sort | 2,1,NULL
+ | DESC NULLS LAST | DESC NULLS LAST | Forward | 2,1,NULL
- | ASC NULLS FIRST | DESC NULLS LAST | Sort | -1,-2,NULL
- | ASC NULLS LAST | DESC NULLS LAST | Forward | -1,-2,NULL
- | DESC NULLS FIRST | DESC NULLS LAST | Backward | -1,-2,NULL
- | DESC NULLS LAST | DESC NULLS LAST | Sort | -1,-2,NULL

I hope this covers everything (I hope nobody minds the lines starting with
" +" and " -")

> Shouldn't 2320 also have the same change? Same for 1711/2167
Fixed that, and probably a bunch of others, included the math functions
that were in v5
and added regress/sql/slope_catalog.sql to format the results, this serves
a double purpose
(a) check that what we wrote had de desired effect (b) facilitate the
review by providing
a more readable format.

There you will find 4 tables
-- Operators with slope prosupport
oid | operator | left_type | right_type | prosupport
...

-- Functions (non-operator) with slope prosupport
oid | function | arguments | returns | prosupport

-- Operators whose name has slope support for some types but not others
e.g.

oid | operator | left_type | right_type

------+----------+-----------------------------+-----------------------------
4394 | * | anymultirange | anymultirange
3900 | * | anyrange | anyrange

-- Functions whose name has slope support for some signatures but not others
oid | function | arguments |
returns
------+--------------+--------------------------------------+-----------------------------
1218 | date_trunc | text, interval | interval
1736 | log | numeric, numeric | numeric
1961 | timestamp | timestamp without time zone, integer | timestamp
without time zone
1967 | timestamptz | timestamp with time zone, integer | timestamp
with time zone
1778 | to_timestamp | text, text | timestamp
with time zone
753 | trunc | macaddr | macaddr
4112 | trunc | macaddr8 | macaddr8

Regards,
Alexandre

Attachment Content-Type Size
v7-0002-Optimized-reverse-pathkeys.patch application/octet-stream 6.2 KB
v7-0004-SLOPE-catalog-changes.patch application/octet-stream 85.1 KB
v7-0005-SLOPE-Planner-support.patch application/octet-stream 39.0 KB
v7-0001-benchmark.patch application/octet-stream 4.8 KB
v7-0003-SLOPE-prosupport-definitions.patch application/octet-stream 10.3 KB
v7-0006-SLOPE-redundancy-checks.patch application/octet-stream 14.1 KB

From: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
To: Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>
Cc: Dean Rasheed <dean(dot)a(dot)rasheed(at)gmail(dot)com>, Corey Huinker <corey(dot)huinker(at)gmail(dot)com>, pgsql-hackers(at)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-04-13 06:22:22
Message-ID: CAE8JnxMPNoLLuQa85aVtT=W4CK4bNW-AS_VZF+oV2cN-bd16zQ@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

Hi,

Fixing Meta ordering issue :), the table reporting orders was ordered in
different ways on
on different systems, specifying collation on the results table.

On Sun, Apr 12, 2026 at 4:01 PM Alexandre Felipe <
o(dot)alexandre(dot)felipe(at)gmail(dot)com> wrote:

Now including a 0006 patch supporting redundant pathkeys such as
[f(x), x], or [x, f(x)], motivated by queries like
queries SELECT OVER (PARTITION f(x) ORDER BY x)

On Tue, Apr 7, 2026 at 12:09 AM Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>
wrote:
> I think there's a bug with NULL handling

There were too many possibilities for my brain to enumerate, so I scripted
the
extraction and presentation of that in regress/sql/slope.sql in commit 0005.

sign | index_order | query_order | scan_method | example
------+------------------+------------------+-------------+------------
+ | ASC NULLS FIRST | ASC NULLS FIRST | Forward | NULL,1,2
+ | ASC NULLS LAST | ASC NULLS FIRST | Sort | NULL,1,2
+ | DESC NULLS FIRST | ASC NULLS FIRST | Sort | NULL,1,2
+ | DESC NULLS LAST | ASC NULLS FIRST | Backward | NULL,1,2
- | ASC NULLS FIRST | ASC NULLS FIRST | Sort | NULL,-2,-1
- | ASC NULLS LAST | ASC NULLS FIRST | Backward | NULL,-2,-1
- | DESC NULLS FIRST | ASC NULLS FIRST | Forward | NULL,-2,-1
- | DESC NULLS LAST | ASC NULLS FIRST | Sort | NULL,-2,-1
+ | ASC NULLS FIRST | ASC NULLS LAST | Sort | 1,2,NULL
+ | ASC NULLS LAST | ASC NULLS LAST | Forward | 1,2,NULL
+ | DESC NULLS FIRST | ASC NULLS LAST | Backward | 1,2,NULL
+ | DESC NULLS LAST | ASC NULLS LAST | Sort | 1,2,NULL
- | ASC NULLS FIRST | ASC NULLS LAST | Backward | -2,-1,NULL
- | ASC NULLS LAST | ASC NULLS LAST | Sort | -2,-1,NULL
- | DESC NULLS FIRST | ASC NULLS LAST | Sort | -2,-1,NULL
- | DESC NULLS LAST | ASC NULLS LAST | Forward | -2,-1,NULL
+ | ASC NULLS FIRST | DESC NULLS FIRST | Sort | NULL,2,1
+ | ASC NULLS LAST | DESC NULLS FIRST | Backward | NULL,2,1
+ | DESC NULLS FIRST | DESC NULLS FIRST | Forward | NULL,2,1
+ | DESC NULLS LAST | DESC NULLS FIRST | Sort | NULL,2,1
- | ASC NULLS FIRST | DESC NULLS FIRST | Forward | NULL,-1,-2
- | ASC NULLS LAST | DESC NULLS FIRST | Sort | NULL,-1,-2
- | DESC NULLS FIRST | DESC NULLS FIRST | Sort | NULL,-1,-2
- | DESC NULLS LAST | DESC NULLS FIRST | Backward | NULL,-1,-2
+ | ASC NULLS FIRST | DESC NULLS LAST | Backward | 2,1,NULL
+ | ASC NULLS LAST | DESC NULLS LAST | Sort | 2,1,NULL
+ | DESC NULLS FIRST | DESC NULLS LAST | Sort | 2,1,NULL
+ | DESC NULLS LAST | DESC NULLS LAST | Forward | 2,1,NULL
- | ASC NULLS FIRST | DESC NULLS LAST | Sort | -1,-2,NULL
- | ASC NULLS LAST | DESC NULLS LAST | Forward | -1,-2,NULL
- | DESC NULLS FIRST | DESC NULLS LAST | Backward | -1,-2,NULL
- | DESC NULLS LAST | DESC NULLS LAST | Sort | -1,-2,NULL

I hope this covers everything (I hope nobody minds the lines starting with
" +" and " -")

> Shouldn't 2320 also have the same change? Same for 1711/2167
Fixed that, and probably a bunch of others, included the math functions
that were in v5
and added regress/sql/slope_catalog.sql to format the results, this serves
a double purpose
(a) check that what we wrote had de desired effect (b) facilitate the
review by providing
a more readable format.

There you will find 4 tables
-- Operators with slope prosupport
oid | operator | left_type | right_type | prosupport
...

-- Functions (non-operator) with slope prosupport
oid | function | arguments | returns | prosupport

-- Operators whose name has slope support for some types but not others
e.g.

oid | operator | left_type | right_type

------+----------+-----------------------------+------------
-----------------
4394 | * | anymultirange | anymultirange
3900 | * | anyrange | anyrange

-- Functions whose name has slope support for some signatures but not others
oid | function | arguments |
returns
------+--------------+--------------------------------------
+-----------------------------
1218 | date_trunc | text, interval | interval
1736 | log | numeric, numeric | numeric
1961 | timestamp | timestamp without time zone, integer | timestamp
without time zone
1967 | timestamptz | timestamp with time zone, integer | timestamp
with time zone
1778 | to_timestamp | text, text | timestamp
with time zone
753 | trunc | macaddr | macaddr
4112 | trunc | macaddr8 | macaddr8

Regards,
Alexandre

Attachment Content-Type Size
v7-0004-SLOPE-catalog-changes.patch application/octet-stream 85.1 KB
v7-0001-benchmark.patch application/octet-stream 4.8 KB
v7-0003-SLOPE-prosupport-definitions.patch application/octet-stream 10.3 KB
v7-0005-SLOPE-Planner-support.patch application/octet-stream 39.0 KB
v7-0002-Optimized-reverse-pathkeys.patch application/octet-stream 6.2 KB
v7-0006-SLOPE-redundancy-checks.patch application/octet-stream 14.1 KB

From: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
To: Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>
Cc: Dean Rasheed <dean(dot)a(dot)rasheed(at)gmail(dot)com>, Corey Huinker <corey(dot)huinker(at)gmail(dot)com>, pgsql-hackers(at)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-04-13 16:38:50
Message-ID: CAE8JnxO_x=jQOOsywc5mREVKFtB9nMsjh_uyrwKBwSiPJbefWQ@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

Fixing Meta ordering issue :), the table reporting orders was ordered in
different ways on
on different systems, specifying collation on the results table.

On Sun, Apr 12, 2026 at 4:01 PM Alexandre Felipe <
o(dot)alexandre(dot)felipe(at)gmail(dot)com> wrote:

Now including a 0006 patch supporting redundant pathkeys such as
[f(x), x], or [x, f(x)], motivated by queries like
queries SELECT OVER (PARTITION f(x) ORDER BY x)

On Tue, Apr 7, 2026 at 12:09 AM Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>
wrote:
> I think there's a bug with NULL handling

There were too many possibilities for my brain to enumerate, so I scripted
the
extraction and presentation of that in regress/sql/slope.sql in commit 0005.

sign | index_order | query_order | scan_method | example
------+------------------+------------------+-------------+------------
+ | ASC NULLS FIRST | ASC NULLS FIRST | Forward | NULL,1,2
+ | ASC NULLS LAST | ASC NULLS FIRST | Sort | NULL,1,2
+ | DESC NULLS FIRST | ASC NULLS FIRST | Sort | NULL,1,2
+ | DESC NULLS LAST | ASC NULLS FIRST | Backward | NULL,1,2
- | ASC NULLS FIRST | ASC NULLS FIRST | Sort | NULL,-2,-1
- | ASC NULLS LAST | ASC NULLS FIRST | Backward | NULL,-2,-1
- | DESC NULLS FIRST | ASC NULLS FIRST | Forward | NULL,-2,-1
- | DESC NULLS LAST | ASC NULLS FIRST | Sort | NULL,-2,-1
+ | ASC NULLS FIRST | ASC NULLS LAST | Sort | 1,2,NULL
+ | ASC NULLS LAST | ASC NULLS LAST | Forward | 1,2,NULL
+ | DESC NULLS FIRST | ASC NULLS LAST | Backward | 1,2,NULL
+ | DESC NULLS LAST | ASC NULLS LAST | Sort | 1,2,NULL
- | ASC NULLS FIRST | ASC NULLS LAST | Backward | -2,-1,NULL
- | ASC NULLS LAST | ASC NULLS LAST | Sort | -2,-1,NULL
- | DESC NULLS FIRST | ASC NULLS LAST | Sort | -2,-1,NULL
- | DESC NULLS LAST | ASC NULLS LAST | Forward | -2,-1,NULL
+ | ASC NULLS FIRST | DESC NULLS FIRST | Sort | NULL,2,1
+ | ASC NULLS LAST | DESC NULLS FIRST | Backward | NULL,2,1
+ | DESC NULLS FIRST | DESC NULLS FIRST | Forward | NULL,2,1
+ | DESC NULLS LAST | DESC NULLS FIRST | Sort | NULL,2,1
- | ASC NULLS FIRST | DESC NULLS FIRST | Forward | NULL,-1,-2
- | ASC NULLS LAST | DESC NULLS FIRST | Sort | NULL,-1,-2
- | DESC NULLS FIRST | DESC NULLS FIRST | Sort | NULL,-1,-2
- | DESC NULLS LAST | DESC NULLS FIRST | Backward | NULL,-1,-2
+ | ASC NULLS FIRST | DESC NULLS LAST | Backward | 2,1,NULL
+ | ASC NULLS LAST | DESC NULLS LAST | Sort | 2,1,NULL
+ | DESC NULLS FIRST | DESC NULLS LAST | Sort | 2,1,NULL
+ | DESC NULLS LAST | DESC NULLS LAST | Forward | 2,1,NULL
- | ASC NULLS FIRST | DESC NULLS LAST | Sort | -1,-2,NULL
- | ASC NULLS LAST | DESC NULLS LAST | Forward | -1,-2,NULL
- | DESC NULLS FIRST | DESC NULLS LAST | Backward | -1,-2,NULL
- | DESC NULLS LAST | DESC NULLS LAST | Sort | -1,-2,NULL

I hope this covers everything (I hope nobody minds the lines starting with
" +" and " -")

> Shouldn't 2320 also have the same change? Same for 1711/2167
Fixed that, and probably a bunch of others, included the math functions
that were in v5
and added regress/sql/slope_catalog.sql to format the results, this serves
a double purpose
(a) check that what we wrote had de desired effect (b) facilitate the
review by providing
a more readable format.

There you will find 4 tables
-- Operators with slope prosupport
oid | operator | left_type | right_type | prosupport
...

-- Functions (non-operator) with slope prosupport
oid | function | arguments | returns | prosupport

-- Operators whose name has slope support for some types but not others
e.g.

oid | operator | left_type | right_type

------+----------+-----------------------------+------------
-----------------
4394 | * | anymultirange | anymultirange
3900 | * | anyrange | anyrange

-- Functions whose name has slope support for some signatures but not others
oid | function | arguments |
returns
------+--------------+--------------------------------------
+-----------------------------
1218 | date_trunc | text, interval | interval
1736 | log | numeric, numeric | numeric
1961 | timestamp | timestamp without time zone, integer | timestamp
without time zone
1967 | timestamptz | timestamp with time zone, integer | timestamp
with time zone
1778 | to_timestamp | text, text | timestamp
with time zone
753 | trunc | macaddr | macaddr
4112 | trunc | macaddr8 | macaddr8

Regards,
Alexandre

Attachment Content-Type Size
v7-0001-benchmark.patch application/octet-stream 4.8 KB
v7-0004-SLOPE-catalog-changes.patch application/octet-stream 85.3 KB
v7-0005-SLOPE-Planner-support.patch application/octet-stream 39.2 KB
v7-0003-SLOPE-prosupport-definitions.patch application/octet-stream 10.3 KB
v7-0002-Optimized-reverse-pathkeys.patch application/octet-stream 6.2 KB
v7-0006-SLOPE-redundancy-checks.patch application/octet-stream 14.1 KB

From: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
To: Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>
Cc: Dean Rasheed <dean(dot)a(dot)rasheed(at)gmail(dot)com>, Corey Huinker <corey(dot)huinker(at)gmail(dot)com>, pgsql-hackers(at)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-05-07 19:12:25
Message-ID: CAE8JnxP=EnLpB3EZ=mZHhq1JQNcpo7SG8J4mw9tV8aNtO8_9CA@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

On Wed, Mar 25, 2026 at 5:18 AM Corey Huinker <corey(dot)huinker(at)gmail(dot)com>
wrote:
> 0002 is missing the catversion bump but that's fine at this early stage.

postponing catversion bump for a time when we get closer to commit,
v8 doesn't include catversion.h changes, hoping to require less frequent
rebases.

Attachment Content-Type Size
v8-0002-Optimized-reverse-pathkeys.patch application/octet-stream 6.2 KB
v8-0004-SLOPE-catalog-changes.patch application/octet-stream 84.9 KB
v8-0003-SLOPE-prosupport-definitions.patch application/octet-stream 10.2 KB
v8-0001-benchmark.patch application/octet-stream 4.8 KB
v8-0005-SLOPE-Planner-support.patch application/octet-stream 39.2 KB
v8-0006-SLOPE-redundancy-checks.patch application/octet-stream 14.0 KB

From: Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>
To: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
Cc: Dean Rasheed <dean(dot)a(dot)rasheed(at)gmail(dot)com>, Corey Huinker <corey(dot)huinker(at)gmail(dot)com>, pgsql-hackers(at)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-05-08 22:18:55
Message-ID: CAN4CZFP+QkhGnt60_Z+QoaPj1nVfNtGFQhLtFxp_bTJ6SbMOMQ@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

> There were too many possibilities for my brain to enumerate, so I scripted
> the
> extraction and presentation of that in regress/sql/slope.sql in commit 0005.

Apologies, I forgot to reply to this earlier, the new NULL handling
looks good. Nice test!

I found one more corner case with infinities (same applies also with
negative infinity):

CREATE TABLE t8 (x float8);
INSERT INTO t8 VALUES (-2), (-1), (0), (1), (2);
CREATE INDEX t8_x_idx ON t8 (x);
ANALYZE t8;

SET enable_seqscan = off;
SELECT x, x * 'Infinity'::float8 AS f FROM t8 ORDER BY x * 'Infinity'::float8;

SET enable_indexscan = off;
SET enable_indexonlyscan = off;
SET enable_seqscan = on;
SELECT x, x * 'Infinity'::float8 AS f FROM t8 ORDER BY x * 'Infinity'::float8;


From: Alexandre Felipe <o(dot)alexandre(dot)felipe(at)gmail(dot)com>
To: Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>
Cc: Dean Rasheed <dean(dot)a(dot)rasheed(at)gmail(dot)com>, Corey Huinker <corey(dot)huinker(at)gmail(dot)com>, pgsql-hackers(at)postgresql(dot)org
Subject: Re: SLOPE - Planner optimizations on monotonic expressions.
Date: 2026-05-10 15:53:49
Message-ID: CAE8JnxNhturjd3qL5O17NHVorqGk9Yr6yPai1AfN4-6T3Gue+Q@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Lists: pgsql-hackers

On Fri, May 8, 2026 at 11:19 PM Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>
wrote:

> I found one more corner case with infinities (same applies also with
> negative infinity):
>

This will restrict a lot of cases.

slope_corner_cases.sql enumerate experssions and orders producing
permutations of (-inf, -1, 0, 1, +inf, nan) mapped to (1,2,3,4,5,6)
to visualize other similar corner cases.

Apparently the violations boil down to two cases
* All basic arithmetic operations with infinity constant (with a lucky
exception x - inf)
* Every decreasing function where the index key.
e.g. `-x desc` would have NaNs first

Should I simply detect and disable the above cases?

sqrt(x < 0) already raise an exception, is it safe to assume that for all
the limited domain functions?

Attachment Content-Type Size
slope_corner_cases.sql application/octet-stream 3.9 KB