From 9936f08868ef06272a684c6e3fefb09109e21fe0 Mon Sep 17 00:00:00 2001
From: "Andrey M. Borodin" <x4mmm@night.local>
Date: Thu, 4 Jan 2024 23:01:15 +0500
Subject: [PATCH v8 2/2] Add optional argument unix_ts_ms to gen_uuid_v7()

This would allow to do k-way sorted uuids.
---
 src/backend/utils/adt/pseudotypes.c      | 12 ++++++++++--
 src/backend/utils/adt/uuid.c             | 22 ++++++++++++++++++----
 src/include/catalog/pg_proc.dat          |  6 ++++--
 src/test/regress/expected/opr_sanity.out |  2 +-
 src/test/regress/expected/uuid.out       | 10 ++++++++++
 src/test/regress/sql/uuid.sql            |  6 ++++++
 6 files changed, 49 insertions(+), 9 deletions(-)

diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index a3a991f634..82c2d1309d 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -23,6 +23,7 @@
 #include "postgres.h"
 
 #include "libpq/pqformat.h"
+#include "miscadmin.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/rangetypes.h"
@@ -332,11 +333,18 @@ shell_out(PG_FUNCTION_ARGS)
  *
  * We must disallow input of pg_node_tree values because the SQL functions
  * that operate on the type are not secure against malformed input.
- * We do want to allow output, though.
+ * We do want to allow output, though. Also we need input during bootstrap.
  */
-PSEUDOTYPE_DUMMY_INPUT_FUNC(pg_node_tree);
 PSEUDOTYPE_DUMMY_RECEIVE_FUNC(pg_node_tree);
 
+Datum
+pg_node_tree_in(PG_FUNCTION_ARGS)
+{
+	if (!IsBootstrapProcessingMode())
+		elog(ERROR, "cannot accept a value of type pg_node_tree_in");
+	return textin(fcinfo);
+}
+
 Datum
 pg_node_tree_out(PG_FUNCTION_ARGS)
 {
diff --git a/src/backend/utils/adt/uuid.c b/src/backend/utils/adt/uuid.c
index ce4be60698..9e07987045 100644
--- a/src/backend/utils/adt/uuid.c
+++ b/src/backend/utils/adt/uuid.c
@@ -436,12 +436,26 @@ gen_uuid_v7(PG_FUNCTION_ARGS)
 	pg_uuid_t  *uuid = palloc(UUID_LEN);
 	uint64_t tms;
 	struct timeval tp;
+	bool increment_counter;
 
-	gettimeofday(&tp, NULL);
-
-	tms = ((uint64_t)tp.tv_sec) * 1000 + (tp.tv_usec) / 1000;
+	if (PG_NARGS() == 0 || PG_ARGISNULL(0))
+	{
+		gettimeofday(&tp, NULL);
+		tms = ((uint64_t)tp.tv_sec) * 1000 + (tp.tv_usec) / 1000;
+		/* time from clock is protected from backward leaps */
+		increment_counter = tms <= previous_timestamp;
+	}
+	else
+	{
+		tms = PG_GETARG_INT64(0);
+		/*
+		 * The time can leap backwards when provided by the user, so we use
+		 * counter only when called with exactly same unix_ts_ms argument.
+		 */
+		increment_counter = (tms == previous_timestamp);
+	}
 
-	if (tms <= previous_timestamp)
+	if (increment_counter)
 	{
 		/* Time did not increment from the previous generation, we must increment counter */
 		++sequence_counter;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 21560c0e81..873ea90871 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9174,9 +9174,11 @@
 { oid => '3432', descr => 'generate random UUID',
   proname => 'gen_random_uuid', proleakproof => 't', provolatile => 'v',
   prorettype => 'uuid', proargtypes => '', prosrc => 'gen_random_uuid' },
-{ oid => '9895', descr => 'generate UUID version 7',
+{ oid => '9895', descr => 'generate UUID version 7', proisstrict => 'f',
   proname => 'gen_uuid_v7', proleakproof => 't', provolatile => 'v',
-  prorettype => 'uuid', proargtypes => '', prosrc => 'gen_uuid_v7' },
+  prorettype => 'uuid', proargtypes => 'int8', prosrc => 'gen_uuid_v7',
+  proargnames => '{unix_ts_ms}', pronargdefaults => 1, proargmodes => '{i}',
+  proargdefaults => '({CONST :consttype 20 :consttypmod -1 :constcollid 0 :constlen 8 :constbyval true :constisnull true :location 47 :constvalue <>})' },
 { oid => '9896', descr => 'extract timestamp from UUID version 7',
   proname => 'get_uuid_v7_time', proleakproof => 't', provolatile => 'i',
   prorettype => 'timestamptz', proargtypes => 'uuid', prosrc => 'get_uuid_v7_time' },
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 853d3574a3..3fa90ae59e 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -872,7 +872,7 @@ xid8ge(xid8,xid8)
 xid8eq(xid8,xid8)
 xid8ne(xid8,xid8)
 xid8cmp(xid8,xid8)
-gen_uuid_v7()
+gen_uuid_v7(bigint)
 get_uuid_v7_time(uuid)
 -- restore normal output mode
 \a\t
diff --git a/src/test/regress/expected/uuid.out b/src/test/regress/expected/uuid.out
index dfb446b4cb..cdad0024a6 100644
--- a/src/test/regress/expected/uuid.out
+++ b/src/test/regress/expected/uuid.out
@@ -178,6 +178,16 @@ SELECT count(DISTINCT guid_field) FROM guid1;
      2
 (1 row)
 
+-- generation test for v7 with same unix_ts_ms
+TRUNCATE guid1;
+INSERT INTO guid1 (guid_field) VALUES (gen_uuid_v7(12345));
+INSERT INTO guid1 (guid_field) VALUES (gen_uuid_v7(12345));
+SELECT count(DISTINCT guid_field) FROM guid1;
+ count 
+-------
+     2
+(1 row)
+
 -- check that timestamp is extracted correctly
 WITH uuid_time_extraction AS
 (SELECT get_uuid_v7_time(gen_uuid_v7())-now() d)
diff --git a/src/test/regress/sql/uuid.sql b/src/test/regress/sql/uuid.sql
index dccb8335cd..a2de91feed 100644
--- a/src/test/regress/sql/uuid.sql
+++ b/src/test/regress/sql/uuid.sql
@@ -91,6 +91,12 @@ INSERT INTO guid1 (guid_field) VALUES (gen_uuid_v7());
 INSERT INTO guid1 (guid_field) VALUES (gen_uuid_v7());
 SELECT count(DISTINCT guid_field) FROM guid1;
 
+-- generation test for v7 with same unix_ts_ms
+TRUNCATE guid1;
+INSERT INTO guid1 (guid_field) VALUES (gen_uuid_v7(12345));
+INSERT INTO guid1 (guid_field) VALUES (gen_uuid_v7(12345));
+SELECT count(DISTINCT guid_field) FROM guid1;
+
 -- check that timestamp is extracted correctly
 WITH uuid_time_extraction AS
 (SELECT get_uuid_v7_time(gen_uuid_v7())-now() d)
-- 
2.37.1 (Apple Git-137.1)

