Introduce MIN/MAX aggregate functions to pg_lsn
Hi all,
Before we introduce pg_lsn datatype the LSN was expressed as a TEXT type,
so a simple query using MIN/MAX functions works as expected. Query like:
SELECT min(restart_lsn) FROM pg_replication_slots;
SELECT min(sent_lsn) FROM pg_stat_replication ;
So attached patch aims to introduce MIN/MAX aggregate functions to pg_lsn
datatype.
Regards,
--
Fabrízio de Royes Mello Timbira - http://www.timbira.com.br/
PostgreSQL: Consultoria, Desenvolvimento, Suporte 24x7 e Treinamento
Attachments:
add_min_max_aggregates_for_pg_lsn_type_v1.patchtext/x-patch; charset=US-ASCII; name=add_min_max_aggregates_for_pg_lsn_type_v1.patchDownload
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 1a01473..490f3a8 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14663,7 +14663,7 @@ NULL baz</literallayout>(3 rows)</entry>
</indexterm>
<function>max(<replaceable class="parameter">expression</replaceable>)</function>
</entry>
- <entry>any numeric, string, date/time, network, or enum type,
+ <entry>any numeric, string, date/time, network, lsn, or enum type,
or arrays of these types</entry>
<entry>same as argument type</entry>
<entry>Yes</entry>
@@ -14681,7 +14681,7 @@ NULL baz</literallayout>(3 rows)</entry>
</indexterm>
<function>min(<replaceable class="parameter">expression</replaceable>)</function>
</entry>
- <entry>any numeric, string, date/time, network, or enum type,
+ <entry>any numeric, string, date/time, network, lsn, or enum type,
or arrays of these types</entry>
<entry>same as argument type</entry>
<entry>Yes</entry>
diff --git a/src/backend/utils/adt/pg_lsn.c b/src/backend/utils/adt/pg_lsn.c
index 7242d3c..ab393bc 100644
--- a/src/backend/utils/adt/pg_lsn.c
+++ b/src/backend/utils/adt/pg_lsn.c
@@ -155,6 +155,30 @@ pg_lsn_ge(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(lsn1 >= lsn2);
}
+Datum
+pg_lsn_larger(PG_FUNCTION_ARGS)
+{
+ XLogRecPtr lsn1 = PG_GETARG_LSN(0);
+ XLogRecPtr lsn2 = PG_GETARG_LSN(1);
+ XLogRecPtr result;
+
+ result = ((lsn1 > lsn2) ? lsn1 : lsn2);
+
+ PG_RETURN_LSN(result);
+}
+
+Datum
+pg_lsn_smaller(PG_FUNCTION_ARGS)
+{
+ XLogRecPtr lsn1 = PG_GETARG_LSN(0);
+ XLogRecPtr lsn2 = PG_GETARG_LSN(1);
+ XLogRecPtr result;
+
+ result = ((lsn1 < lsn2) ? lsn1 : lsn2);
+
+ PG_RETURN_LSN(result);
+}
+
/* btree index opclass support */
Datum
pg_lsn_cmp(PG_FUNCTION_ARGS)
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index 044695a..242d843 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -146,6 +146,9 @@
{ aggfnoid => 'max(inet)', aggtransfn => 'network_larger',
aggcombinefn => 'network_larger', aggsortop => '>(inet,inet)',
aggtranstype => 'inet' },
+{ aggfnoid => 'max(pg_lsn)', aggtransfn => 'pg_lsn_larger',
+ aggcombinefn => 'pg_lsn_larger', aggsortop => '>(pg_lsn,pg_lsn)',
+ aggtranstype => 'pg_lsn' },
# min
{ aggfnoid => 'min(int8)', aggtransfn => 'int8smaller',
@@ -208,6 +211,9 @@
{ aggfnoid => 'min(inet)', aggtransfn => 'network_smaller',
aggcombinefn => 'network_smaller', aggsortop => '<(inet,inet)',
aggtranstype => 'inet' },
+{ aggfnoid => 'min(pg_lsn)', aggtransfn => 'pg_lsn_smaller',
+ aggcombinefn => 'pg_lsn_smaller', aggsortop => '<(pg_lsn,pg_lsn)',
+ aggtranstype => 'pg_lsn' },
# count
{ aggfnoid => 'count(any)', aggtransfn => 'int8inc_any',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index acf1131..cfc9b86 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6189,6 +6189,9 @@
{ oid => '3564', descr => 'maximum value of all inet input values',
proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'inet',
proargtypes => 'inet', prosrc => 'aggregate_dummy' },
+{ oid => '8125', descr => 'maximum value of all pg_lsn input values',
+ proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'pg_lsn',
+ proargtypes => 'pg_lsn', prosrc => 'aggregate_dummy' },
{ oid => '2131', descr => 'minimum value of all bigint input values',
proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'int8',
@@ -6253,6 +6256,9 @@
{ oid => '3565', descr => 'minimum value of all inet input values',
proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'inet',
proargtypes => 'inet', prosrc => 'aggregate_dummy' },
+{ oid => '8126', descr => 'minimum value of all pg_lsn input values',
+ proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'pg_lsn',
+ proargtypes => 'pg_lsn', prosrc => 'aggregate_dummy' },
# count has two forms: count(any) and count(*)
{ oid => '2147',
@@ -8355,6 +8361,12 @@
{ oid => '3413', descr => 'hash',
proname => 'pg_lsn_hash_extended', prorettype => 'int8',
proargtypes => 'pg_lsn int8', prosrc => 'pg_lsn_hash_extended' },
+{ oid => '8123', descr => 'larger of two',
+ proname => 'pg_lsn_larger', prorettype => 'pg_lsn',
+ proargtypes => 'pg_lsn pg_lsn', prosrc => 'pg_lsn_larger' },
+{ oid => '8124', descr => 'smaller of two',
+ proname => 'pg_lsn_smaller', prorettype => 'pg_lsn',
+ proargtypes => 'pg_lsn pg_lsn', prosrc => 'pg_lsn_smaller' },
# enum related procs
{ oid => '3504', descr => 'I/O',
diff --git a/src/test/regress/expected/pg_lsn.out b/src/test/regress/expected/pg_lsn.out
index 2854cfd..64d41df 100644
--- a/src/test/regress/expected/pg_lsn.out
+++ b/src/test/regress/expected/pg_lsn.out
@@ -26,6 +26,13 @@ INSERT INTO PG_LSN_TBL VALUES ('/ABCD');
ERROR: invalid input syntax for type pg_lsn: "/ABCD"
LINE 1: INSERT INTO PG_LSN_TBL VALUES ('/ABCD');
^
+-- Min/Max aggregation
+SELECT MIN(f1), MAX(f1) FROM PG_LSN_TBL;
+ min | max
+-----+-------------------
+ 0/0 | FFFFFFFF/FFFFFFFF
+(1 row)
+
DROP TABLE PG_LSN_TBL;
-- Operators
SELECT '0/16AE7F8' = '0/16AE7F8'::pg_lsn;
diff --git a/src/test/regress/sql/pg_lsn.sql b/src/test/regress/sql/pg_lsn.sql
index 746f720..2047166 100644
--- a/src/test/regress/sql/pg_lsn.sql
+++ b/src/test/regress/sql/pg_lsn.sql
@@ -14,6 +14,9 @@ INSERT INTO PG_LSN_TBL VALUES ('-1/0');
INSERT INTO PG_LSN_TBL VALUES (' 0/12345678');
INSERT INTO PG_LSN_TBL VALUES ('ABCD/');
INSERT INTO PG_LSN_TBL VALUES ('/ABCD');
+
+-- Min/Max aggregation
+SELECT MIN(f1), MAX(f1) FROM PG_LSN_TBL;
DROP TABLE PG_LSN_TBL;
-- Operators
On Fri, Mar 22, 2019 at 04:49:57PM -0300, Fabrízio de Royes Mello wrote:
So attached patch aims to introduce MIN/MAX aggregate functions to pg_lsn
Fine by me. This looks helpful for monitoring.
Please make sure to register it to the next commit fest:
https://commitfest.postgresql.org/23/
It is too late for Postgres 12 unfortunately.
--
Michael
On Fri, Mar 22, 2019 at 10:27 PM Michael Paquier <michael@paquier.xyz>
wrote:
On Fri, Mar 22, 2019 at 04:49:57PM -0300, Fabrízio de Royes Mello wrote:
So attached patch aims to introduce MIN/MAX aggregate functions to
pg_lsn
Fine by me. This looks helpful for monitoring.
Please make sure to register it to the next commit fest:
https://commitfest.postgresql.org/23/
It is too late for Postgres 12 unfortunately.
Sure, added:
https://commitfest.postgresql.org/23/2070/
Regards,
--
Fabrízio de Royes Mello Timbira - http://www.timbira.com.br/
PostgreSQL: Consultoria, Desenvolvimento, Suporte 24x7 e Treinamento
Hi,
Here are same review comment
- <entry>any numeric, string, date/time, network, or enum type,
+ <entry>any numeric, string, date/time, network, lsn, or enum type,
or arrays of these types</entry>
<entry>same as argument type</entry>
In the documentation it refereed as pg_lsn type rather than lsn alone
+Datum
+pg_lsn_larger(PG_FUNCTION_ARGS)
+{
+ XLogRecPtr lsn1 = PG_GETARG_LSN(0);
+ XLogRecPtr lsn2 = PG_GETARG_LSN(1);
+ XLogRecPtr result;
+
+ result = ((lsn1 > lsn2) ? lsn1 : lsn2);
+
+ PG_RETURN_LSN(result);
+}
rather than using additional variable its more readable and effective to
return the argument
itself like we do in date data type and other place
regards
Surafel
On Tue, Jul 2, 2019 at 7:22 AM Surafel Temesgen <surafel3000@gmail.com>
wrote:
Hi,
Here are same review comment
Thanks for your review.
- <entry>any numeric, string, date/time, network, or enum type, + <entry>any numeric, string, date/time, network, lsn, or enum type, or arrays of these types</entry> <entry>same as argument type</entry> In the documentation it refereed as pg_lsn type rather than lsn alone
Fixed.
+Datum +pg_lsn_larger(PG_FUNCTION_ARGS) +{ + XLogRecPtr lsn1 = PG_GETARG_LSN(0); + XLogRecPtr lsn2 = PG_GETARG_LSN(1); + XLogRecPtr result; + + result = ((lsn1 > lsn2) ? lsn1 : lsn2); + + PG_RETURN_LSN(result); +}rather than using additional variable its more readable and effective to
return the argument
itself like we do in date data type and other place
Fixed.
New version attached.
Regards,
--
Fabrízio de Royes Mello Timbira - http://www.timbira.com.br/
PostgreSQL: Consultoria, Desenvolvimento, Suporte 24x7 e Treinamento
Attachments:
add_min_max_aggregates_for_pg_lsn_type_v2.patchtext/x-patch; charset=US-ASCII; name=add_min_max_aggregates_for_pg_lsn_type_v2.patchDownload
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 3a8581d..b7f746b 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14804,7 +14804,7 @@ NULL baz</literallayout>(3 rows)</entry>
</indexterm>
<function>max(<replaceable class="parameter">expression</replaceable>)</function>
</entry>
- <entry>any numeric, string, date/time, network, or enum type,
+ <entry>any numeric, string, date/time, network, pg_lsn, or enum type,
or arrays of these types</entry>
<entry>same as argument type</entry>
<entry>Yes</entry>
@@ -14822,7 +14822,7 @@ NULL baz</literallayout>(3 rows)</entry>
</indexterm>
<function>min(<replaceable class="parameter">expression</replaceable>)</function>
</entry>
- <entry>any numeric, string, date/time, network, or enum type,
+ <entry>any numeric, string, date/time, network, pg_lsn, or enum type,
or arrays of these types</entry>
<entry>same as argument type</entry>
<entry>Yes</entry>
diff --git a/src/backend/utils/adt/pg_lsn.c b/src/backend/utils/adt/pg_lsn.c
index 4c329a8..b4c6c23 100644
--- a/src/backend/utils/adt/pg_lsn.c
+++ b/src/backend/utils/adt/pg_lsn.c
@@ -171,6 +171,24 @@ pg_lsn_ge(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(lsn1 >= lsn2);
}
+Datum
+pg_lsn_larger(PG_FUNCTION_ARGS)
+{
+ XLogRecPtr lsn1 = PG_GETARG_LSN(0);
+ XLogRecPtr lsn2 = PG_GETARG_LSN(1);
+
+ PG_RETURN_LSN((lsn1 > lsn2) ? lsn1 : lsn2);
+}
+
+Datum
+pg_lsn_smaller(PG_FUNCTION_ARGS)
+{
+ XLogRecPtr lsn1 = PG_GETARG_LSN(0);
+ XLogRecPtr lsn2 = PG_GETARG_LSN(1);
+
+ PG_RETURN_LSN((lsn1 < lsn2) ? lsn1 : lsn2);
+}
+
/* btree index opclass support */
Datum
pg_lsn_cmp(PG_FUNCTION_ARGS)
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index 044695a..242d843 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -146,6 +146,9 @@
{ aggfnoid => 'max(inet)', aggtransfn => 'network_larger',
aggcombinefn => 'network_larger', aggsortop => '>(inet,inet)',
aggtranstype => 'inet' },
+{ aggfnoid => 'max(pg_lsn)', aggtransfn => 'pg_lsn_larger',
+ aggcombinefn => 'pg_lsn_larger', aggsortop => '>(pg_lsn,pg_lsn)',
+ aggtranstype => 'pg_lsn' },
# min
{ aggfnoid => 'min(int8)', aggtransfn => 'int8smaller',
@@ -208,6 +211,9 @@
{ aggfnoid => 'min(inet)', aggtransfn => 'network_smaller',
aggcombinefn => 'network_smaller', aggsortop => '<(inet,inet)',
aggtranstype => 'inet' },
+{ aggfnoid => 'min(pg_lsn)', aggtransfn => 'pg_lsn_smaller',
+ aggcombinefn => 'pg_lsn_smaller', aggsortop => '<(pg_lsn,pg_lsn)',
+ aggtranstype => 'pg_lsn' },
# count
{ aggfnoid => 'count(any)', aggtransfn => 'int8inc_any',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 8733524..aa8674c 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6219,6 +6219,9 @@
{ oid => '3564', descr => 'maximum value of all inet input values',
proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'inet',
proargtypes => 'inet', prosrc => 'aggregate_dummy' },
+{ oid => '8125', descr => 'maximum value of all pg_lsn input values',
+ proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'pg_lsn',
+ proargtypes => 'pg_lsn', prosrc => 'aggregate_dummy' },
{ oid => '2131', descr => 'minimum value of all bigint input values',
proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'int8',
@@ -6283,6 +6286,9 @@
{ oid => '3565', descr => 'minimum value of all inet input values',
proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'inet',
proargtypes => 'inet', prosrc => 'aggregate_dummy' },
+{ oid => '8126', descr => 'minimum value of all pg_lsn input values',
+ proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'pg_lsn',
+ proargtypes => 'pg_lsn', prosrc => 'aggregate_dummy' },
# count has two forms: count(any) and count(*)
{ oid => '2147',
@@ -8385,6 +8391,12 @@
{ oid => '3413', descr => 'hash',
proname => 'pg_lsn_hash_extended', prorettype => 'int8',
proargtypes => 'pg_lsn int8', prosrc => 'pg_lsn_hash_extended' },
+{ oid => '8123', descr => 'larger of two',
+ proname => 'pg_lsn_larger', prorettype => 'pg_lsn',
+ proargtypes => 'pg_lsn pg_lsn', prosrc => 'pg_lsn_larger' },
+{ oid => '8124', descr => 'smaller of two',
+ proname => 'pg_lsn_smaller', prorettype => 'pg_lsn',
+ proargtypes => 'pg_lsn pg_lsn', prosrc => 'pg_lsn_smaller' },
# enum related procs
{ oid => '3504', descr => 'I/O',
diff --git a/src/test/regress/expected/pg_lsn.out b/src/test/regress/expected/pg_lsn.out
index 2854cfd..64d41df 100644
--- a/src/test/regress/expected/pg_lsn.out
+++ b/src/test/regress/expected/pg_lsn.out
@@ -26,6 +26,13 @@ INSERT INTO PG_LSN_TBL VALUES ('/ABCD');
ERROR: invalid input syntax for type pg_lsn: "/ABCD"
LINE 1: INSERT INTO PG_LSN_TBL VALUES ('/ABCD');
^
+-- Min/Max aggregation
+SELECT MIN(f1), MAX(f1) FROM PG_LSN_TBL;
+ min | max
+-----+-------------------
+ 0/0 | FFFFFFFF/FFFFFFFF
+(1 row)
+
DROP TABLE PG_LSN_TBL;
-- Operators
SELECT '0/16AE7F8' = '0/16AE7F8'::pg_lsn;
diff --git a/src/test/regress/sql/pg_lsn.sql b/src/test/regress/sql/pg_lsn.sql
index 746f720..2047166 100644
--- a/src/test/regress/sql/pg_lsn.sql
+++ b/src/test/regress/sql/pg_lsn.sql
@@ -14,6 +14,9 @@ INSERT INTO PG_LSN_TBL VALUES ('-1/0');
INSERT INTO PG_LSN_TBL VALUES (' 0/12345678');
INSERT INTO PG_LSN_TBL VALUES ('ABCD/');
INSERT INTO PG_LSN_TBL VALUES ('/ABCD');
+
+-- Min/Max aggregation
+SELECT MIN(f1), MAX(f1) FROM PG_LSN_TBL;
DROP TABLE PG_LSN_TBL;
-- Operators
On Tue, Jul 02, 2019 at 11:31:49AM -0300, Fabrízio de Royes Mello wrote:
New version attached.
This looks in pretty good shape to me, and no objections from me to
get those functions as the min() flavor is useful for monitoring WAL
retention for complex deployments.
Do you have a particular use-case in mind for max() one? I can think
of at least one case: monitoring the flush LSNs of a set of standbys
to find out how much has been replayed at most.
--
Michael
On Thu, Jul 4, 2019 at 4:17 AM Michael Paquier <michael@paquier.xyz> wrote:
Do you have a particular use-case in mind for max() one? I can think
of at least one case: monitoring the flush LSNs of a set of standbys
to find out how much has been replayed at most.
It would be pretty silly to have one and not the other, regardless of
whether we can think of an immediate use case.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Thu, Jul 4, 2019 at 5:17 AM Michael Paquier <michael@paquier.xyz> wrote:
On Tue, Jul 02, 2019 at 11:31:49AM -0300, Fabrízio de Royes Mello wrote:
New version attached.
This looks in pretty good shape to me, and no objections from me to
get those functions as the min() flavor is useful for monitoring WAL
retention for complex deployments.Do you have a particular use-case in mind for max() one? I can think
of at least one case: monitoring the flush LSNs of a set of standbys
to find out how much has been replayed at most.
I use min/max to measure the amount of generated WAL (diff) during some
periods based on wal position stored in some monitoring system.
Regards,
--
Fabrízio de Royes Mello Timbira - http://www.timbira.com.br/
PostgreSQL: Consultoria, Desenvolvimento, Suporte 24x7 e Treinamento
On Thu, Jul 4, 2019 at 10:57 AM Robert Haas <robertmhaas@gmail.com> wrote:
On Thu, Jul 4, 2019 at 4:17 AM Michael Paquier <michael@paquier.xyz>
wrote:
Do you have a particular use-case in mind for max() one? I can think
of at least one case: monitoring the flush LSNs of a set of standbys
to find out how much has been replayed at most.It would be pretty silly to have one and not the other, regardless of
whether we can think of an immediate use case.
+1
--
Fabrízio de Royes Mello Timbira - http://www.timbira.com.br/
PostgreSQL: Consultoria, Desenvolvimento, Suporte 24x7 e Treinamento
On Thu, Jul 04, 2019 at 01:48:24PM -0300, Fabrízio de Royes Mello wrote:
On Thu, Jul 4, 2019 at 10:57 AM Robert Haas <robertmhaas@gmail.com> wrote:
It would be pretty silly to have one and not the other, regardless of
whether we can think of an immediate use case.+1
OK, applied with a catalog version bump. This is cool to have.
--
Michael
On Fri, Jul 5, 2019 at 12:22 AM Michael Paquier <michael@paquier.xyz> wrote:
On Thu, Jul 04, 2019 at 01:48:24PM -0300, Fabrízio de Royes Mello wrote:
On Thu, Jul 4, 2019 at 10:57 AM Robert Haas <robertmhaas@gmail.com>
wrote:
It would be pretty silly to have one and not the other, regardless of
whether we can think of an immediate use case.+1
OK, applied with a catalog version bump. This is cool to have.
Awesome... thanks.
Att,
--
Fabrízio de Royes Mello Timbira - http://www.timbira.com.br/
PostgreSQL: Consultoria, Desenvolvimento, Suporte 24x7 e Treinamento