public inbox for [email protected]  
help / color / mirror / Atom feed
[PATCH v16 02/10] pg_stat_file and pg_ls_dir_* to use lstat()..
19+ messages / 9 participants
[nested] [flat]

* [PATCH v16 02/10] pg_stat_file and pg_ls_dir_* to use lstat()..
@ 2020-03-30 23:59  Justin Pryzby <[email protected]>
  0 siblings, 0 replies; 19+ messages in thread

From: Justin Pryzby @ 2020-03-30 23:59 UTC (permalink / raw)

pg_ls_dir_* will now skip (no longer show) symbolic links, same as other
non-regular file types, as we advertize we do since 8b6d94cf6.  That seems to
be the intented behavior, since irregular file types are 1) less portable; and,
2) we don't currently show a file's type except for "bool is_dir".

pg_stat_file will now 1) show metadata of links themselves, rather than their
target; and, 2) specifically, show links to directories with "is_dir=false";
and, 3) not error on broken symlinks.
---
 doc/src/sgml/func.sgml          | 2 +-
 src/backend/utils/adt/genfile.c | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 9b885102da..96b08d0500 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -25486,7 +25486,7 @@ SELECT convert_from(pg_read_binary_file('file_in_utf8.txt'), 'UTF8');
     size, last accessed time stamp, last modified time stamp,
     last file status change time stamp (Unix platforms only),
     file creation time stamp (Windows only), and a <type>boolean</type>
-    indicating if it is a directory (or a symbolic link to a directory).
+    indicating if it is a directory.
     Typical usages include:
 <programlisting>
 SELECT * FROM pg_stat_file('filename');
diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c
index ceaa6180da..219ac160f8 100644
--- a/src/backend/utils/adt/genfile.c
+++ b/src/backend/utils/adt/genfile.c
@@ -370,7 +370,7 @@ pg_stat_file(PG_FUNCTION_ARGS)
 
 	filename = convert_and_check_filename(filename_t);
 
-	if (stat(filename, &fst) < 0)
+	if (lstat(filename, &fst) < 0)
 	{
 		if (missing_ok && errno == ENOENT)
 			PG_RETURN_NULL();
@@ -596,7 +596,7 @@ pg_ls_dir_files(FunctionCallInfo fcinfo, const char *dir, bool missing_ok)
 
 		/* Get the file info */
 		snprintf(path, sizeof(path), "%s/%s", dir, de->d_name);
-		if (stat(path, &attrib) < 0)
+		if (lstat(path, &attrib) < 0)
 		{
 			/* Ignore concurrently-deleted files, else complain */
 			if (errno == ENOENT)
-- 
2.17.0


--2FkSFaIQeDFoAt0B
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="v16-0003-Add-tests-on-pg_ls_dir-before-changing-it.patch"



^ permalink  raw  reply  [nested|flat] 19+ messages in thread

* Re: real/float example for testlibpq3
@ 2022-02-28 15:19  Tom Lane <[email protected]>
  0 siblings, 1 reply; 19+ messages in thread

From: Tom Lane @ 2022-02-28 15:19 UTC (permalink / raw)
  To: Ashutosh Bapat <[email protected]>; +Cc: Mark Wong <[email protected]>; PostgreSQL Hackers <[email protected]>

Ashutosh Bapat <[email protected]> writes:
> I am wondering whether we should provide PQgetfloatvalue() which does
> what that example shows but transparently. It will return a float
> value of the given field instead of char *.

I'm against that, precisely because ...

> That will standardize the
> way to fetch real typed values in libpq. That leads to the next
> question. Do we need to introduce different PQget*value() for standard
> C/SQL data types.

... I do not want to go here.  Where would you stop?  How would you
deal with cross-machine inconsistencies in integer widths?

			regards, tom lane






^ permalink  raw  reply  [nested|flat] 19+ messages in thread

* Re: real/float example for testlibpq3
@ 2022-02-28 22:30  Chapman Flack <[email protected]>
  parent: Tom Lane <[email protected]>
  0 siblings, 1 reply; 19+ messages in thread

From: Chapman Flack @ 2022-02-28 22:30 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; Ashutosh Bapat <[email protected]>; +Cc: Mark Wong <[email protected]>; PostgreSQL Hackers <[email protected]>

On 02/28/22 10:19, Tom Lane wrote:
>> That will standardize the
>> way to fetch real typed values in libpq. That leads to the next
>> question. Do we need to introduce different PQget*value() for standard
>> C/SQL data types.
> 
> ... I do not want to go here.  Where would you stop?  How would you
> deal with cross-machine inconsistencies in integer widths?

This stimulates a question for me.

Not just libpq, but all drivers implemented in whatever language, if they
wish to support binary protocol, depend on knowing what the committed
send/recv wire formats are for whichever types they mean to support.

In the current state of affairs, what's considered the ur-source of that
information?

I have often seen those formats documented in code comments, usually above
the recv function in the .c file for a given adt.

Have we got any more, well, machine-readable collection of that knowledge?

Regards,
-Chap






^ permalink  raw  reply  [nested|flat] 19+ messages in thread

* Re: real/float example for testlibpq3
@ 2022-02-28 22:48  Tom Lane <[email protected]>
  parent: Chapman Flack <[email protected]>
  0 siblings, 1 reply; 19+ messages in thread

From: Tom Lane @ 2022-02-28 22:48 UTC (permalink / raw)
  To: Chapman Flack <[email protected]>; +Cc: Ashutosh Bapat <[email protected]>; Mark Wong <[email protected]>; PostgreSQL Hackers <[email protected]>

Chapman Flack <[email protected]> writes:
> This stimulates a question for me.

> Not just libpq, but all drivers implemented in whatever language, if they
> wish to support binary protocol, depend on knowing what the committed
> send/recv wire formats are for whichever types they mean to support.

> In the current state of affairs, what's considered the ur-source of that
> information?

The source code for the type's send/receive functions :-(.  One could
wish for something better, but no one has stepped up to produce such
documentation.

			regards, tom lane






^ permalink  raw  reply  [nested|flat] 19+ messages in thread

* Re: real/float example for testlibpq3
@ 2022-03-30 17:16  Greg Stark <[email protected]>
  parent: Tom Lane <[email protected]>
  0 siblings, 1 reply; 19+ messages in thread

From: Greg Stark @ 2022-03-30 17:16 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Chapman Flack <[email protected]>; Ashutosh Bapat <[email protected]>; Mark Wong <[email protected]>; PostgreSQL Hackers <[email protected]>

On Mon, 28 Feb 2022 at 17:50, Tom Lane <[email protected]> wrote:
>
> Chapman Flack <[email protected]> writes:
> > In the current state of affairs, what's considered the ur-source of that
> > information?
>
> The source code for the type's send/receive functions :-(.  One could
> wish for something better, but no one has stepped up to produce such
> documentation.

Fwiw the client library I heard of attempting to have good binary mode
support was the Crystal language client
https://github.com/will/crystal-pg. I think he was aiming for full
coverage of the built-in data types. That might make a good reference
implementation to write up documentation from. He probably uncovered
some corner cases in development that one might not find from just
inspection of the server code.

-- 
greg





^ permalink  raw  reply  [nested|flat] 19+ messages in thread

* Re: real/float example for testlibpq3
@ 2022-06-14 17:40  Mark Wong <[email protected]>
  parent: Greg Stark <[email protected]>
  0 siblings, 1 reply; 19+ messages in thread

From: Mark Wong @ 2022-06-14 17:40 UTC (permalink / raw)
  To: Greg Stark <[email protected]>; +Cc: Tom Lane <[email protected]>; Chapman Flack <[email protected]>; Ashutosh Bapat <[email protected]>; PostgreSQL Hackers <[email protected]>

On Wed, Mar 30, 2022 at 01:16:37PM -0400, Greg Stark wrote:
> On Mon, 28 Feb 2022 at 17:50, Tom Lane <[email protected]> wrote:
> >
> > Chapman Flack <[email protected]> writes:
> > > In the current state of affairs, what's considered the ur-source of that
> > > information?
> >
> > The source code for the type's send/receive functions :-(.  One could
> > wish for something better, but no one has stepped up to produce such
> > documentation.
> 
> Fwiw the client library I heard of attempting to have good binary mode
> support was the Crystal language client
> https://github.com/will/crystal-pg. I think he was aiming for full
> coverage of the built-in data types. That might make a good reference
> implementation to write up documentation from. He probably uncovered
> some corner cases in development that one might not find from just
> inspection of the server code.

Checking in for quick feedback to see if this refactor makes sense.

I've created a function for each data type with the idea that an example
for handling a specific data type can be more easily reviewed by looking
in a single place.

I've added examples for REAL, TIMESTAMP WITHOUT TIME ZONE, and BOOLEAN
to try to illustrate how testlibpq3.sql and testlibpq3.c will grow if
this is a good way to go.

Regards,
Mark

diff --git a/src/test/examples/testlibpq3.c b/src/test/examples/testlibpq3.c
index 4f7b791388..68972f17f3 100644
--- a/src/test/examples/testlibpq3.c
+++ b/src/test/examples/testlibpq3.c
@@ -11,21 +11,56 @@
  * CREATE SCHEMA testlibpq3;
  * SET search_path = testlibpq3;
  * SET standard_conforming_strings = ON;
- * CREATE TABLE test1 (i int4, t text, b bytea);
- * INSERT INTO test1 values (1, 'joe''s place', '\000\001\002\003\004');
- * INSERT INTO test1 values (2, 'ho there', '\004\003\002\001\000');
+ * CREATE SCHEMA testlibpq3;
+ * SET search_path = testlibpq3;
+ * SET standard_conforming_strings = ON;
+ * CREATE TABLE test1 (
+ *     i int4
+ *   , r real
+ *   , bo boolean
+ *   , ts timestamp
+ *   , t text
+ *   , b bytea
+ * );
+ * INSERT INTO test1
+ * VALUES (
+ *     1
+ *   , 3.141593
+ *   , true
+ *   , '2000-01-01 00:00:02.414213'
+ *   , 'joe''s place'
+ *   , '\000\001\002\003\004'
+ * );
+ * INSERT INTO test1
+ * VALUES (
+ *     2
+ *   , 1.618033
+ *   , false
+ *   , '2000-01-01 00:00:01.465571'
+ *   , 'ho there'
+ *   , '\004\003\002\001\000'
+ * );
  *
  * The expected output is:
  *
  * tuple 0: got
  *	i = (4 bytes) 1
+ *	r = (4 bytes) 3.141593
+ *	bo = (1 bytes) 1
  *	t = (11 bytes) 'joe's place'
  *	b = (5 bytes) \000\001\002\003\004
  *
  * tuple 0: got
  *	i = (4 bytes) 2
+ *	r = (4 bytes) 1.618033
+ *	bo = (1 bytes) 0
  *	t = (8 bytes) 'ho there'
  *	b = (5 bytes) \004\003\002\001\000
+ *
+ * General notes about this example:
+ *
+ * Use PQfnumber to avoid assumptions about field order in result but when
+ * getting the field values we ignore possibility they are null!
  */
 
 #ifdef WIN32
@@ -36,6 +71,7 @@
 #include <stdlib.h>
 #include <stdint.h>
 #include <string.h>
+#include <time.h>
 #include <sys/types.h>
 #include "libpq-fe.h"
 
@@ -44,6 +80,13 @@
 #include <arpa/inet.h>
 
 
+/* These macros hopefully make reading calculations for timestamps easier. */
+#define POSTGRES_EPOCH_JDATE 2451545 /* == date2j(2000, 1, 1) */
+#define UNIX_EPOCH_JDATE 2440588 /* == date2j(1970, 1, 1) */
+#define SECS_PER_DAY 86400
+
+uint64_t ntohll(uint64_t);
+
 static void
 exit_nicely(PGconn *conn)
 {
@@ -51,6 +94,142 @@ exit_nicely(PGconn *conn)
 	exit(1);
 }
 
+static void
+handle_boolean(PGresult *res, int i)
+{
+	int			fnum;
+	char	   *ptr;
+	int			val;
+
+	fnum = PQfnumber(res, "bo");
+	ptr = PQgetvalue(res, i, fnum);
+	val = (int) *ptr;
+	printf(" bo = (%d bytes) %d\n", PQgetlength(res, i, fnum), val);
+}
+
+static void
+handle_bytea(PGresult *res, int i)
+{
+	int			j;
+	int			fnum;
+	char	   *ptr;
+	int			len;
+
+	fnum = PQfnumber(res, "b");
+	ptr = PQgetvalue(res, i, fnum);
+
+	/*
+	 * The binary representation of BYTEA is a bunch of bytes, which could
+	 * include embedded nulls so we have to pay attention to field length.
+	 */
+	len = PQgetlength(res, i, fnum);
+	printf(" b = (%d bytes) ", len);
+	for (j = 0; j < len; j++) printf("\\%03o", ptr[j]);
+}
+
+static void
+handle_integer(PGresult *res, int i)
+{
+	int			fnum;
+	char	   *ptr;
+	int			val;
+
+	fnum = PQfnumber(res, "i");
+	ptr = PQgetvalue(res, i, fnum);
+
+	/*
+	 * The binary representation of INT4 is in network byte order, which
+	 * we'd better coerce to the local byte order.
+	 */
+	val = ntohl(*((uint32_t *) ptr));
+
+	printf(" i = (%d bytes) %d\n", PQgetlength(res, i, fnum), val);
+}
+
+static void
+handle_real(PGresult *res, int i)
+{
+	int			fnum;
+	char	   *ptr;
+	union {
+		int		i;
+		float	f;
+	}			val;
+
+	fnum = PQfnumber(res, "r");
+	ptr = PQgetvalue(res, i, fnum);
+
+	/*
+	 * The binary representation of INT4 is in network byte order, which
+	 * we'd better coerce to the local byte order.
+	 */
+	val.i = ntohl(*((uint32_t *) ptr));
+
+	printf(" r = (%d bytes) %f\n", PQgetlength(res, i, fnum), val.f);
+}
+
+static void
+handle_text(PGresult *res, int i)
+{
+	int			fnum;
+	char	   *ptr;
+
+	fnum = PQfnumber(res, "t");
+	ptr = PQgetvalue(res, i, fnum);
+
+	/*
+	 * The binary representation of TEXT is, well, text, and since libpq was
+	 * nice enough to append a zero byte to it, it'll work just fine as a C
+	 * string.
+	 */
+	printf(" t = (%d bytes) '%s'\n", PQgetlength(res, i, fnum), ptr);
+}
+
+static void
+handle_timestamp(PGresult *res, int i)
+{
+	int			fnum;
+	char	   *ptr;
+	uint64_t	val;
+
+	struct tm  *tm;
+	time_t		timep;
+	uint32_t	mantissa;
+
+	fnum = PQfnumber(res, "ts");
+	ptr = PQgetvalue(res, i, fnum);
+	val = ntohll(*((uint64_t *) ptr));
+
+	/*
+	 * The binary representation of a timestamp is in microseconds
+	 * from 2000-01-01.
+	 */
+	timep = val / (uint64_t) 1000000 +
+			(uint64_t) (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) *
+			(uint64_t) SECS_PER_DAY;
+	mantissa = val - (uint64_t) (timep -
+			(POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY) *
+			(uint64_t) 1000000;
+
+	/* For ease of testing, assume and print timestamps in GMT. */
+	tm = gmtime(&timep);
+
+	printf(" ts = (%d bytes) %04d-%02d-%02d %02d:%02d:%02d.%06d\n",
+			PQgetlength(res, i, fnum), tm->tm_year + 1900, tm->tm_mon + 1,
+			tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, mantissa);
+}
+
+/* This is a uint64_t version of ntohl from arpa/inet.h. */
+uint64_t
+ntohll(uint64_t netlonglong)
+{
+	if (ntohl(1) == 1)
+		return netlonglong;
+	else
+		return (uint64_t) (ntohl((int) ((netlonglong << 32) >> 32))) << 32 |
+				(uint64_t) ntohl(((int) (netlonglong >> 32)));
+}
+
 /*
  * This function prints a query result that is a binary-format fetch from
  * a table defined as in the comment above.  We split it out because the
@@ -59,54 +238,17 @@ exit_nicely(PGconn *conn)
 static void
 show_binary_results(PGresult *res)
 {
-	int			i,
-				j;
-	int			i_fnum,
-				t_fnum,
-				b_fnum;
-
-	/* Use PQfnumber to avoid assumptions about field order in result */
-	i_fnum = PQfnumber(res, "i");
-	t_fnum = PQfnumber(res, "t");
-	b_fnum = PQfnumber(res, "b");
+	int			i;
 
 	for (i = 0; i < PQntuples(res); i++)
 	{
-		char	   *iptr;
-		char	   *tptr;
-		char	   *bptr;
-		int			blen;
-		int			ival;
-
-		/* Get the field values (we ignore possibility they are null!) */
-		iptr = PQgetvalue(res, i, i_fnum);
-		tptr = PQgetvalue(res, i, t_fnum);
-		bptr = PQgetvalue(res, i, b_fnum);
-
-		/*
-		 * The binary representation of INT4 is in network byte order, which
-		 * we'd better coerce to the local byte order.
-		 */
-		ival = ntohl(*((uint32_t *) iptr));
-
-		/*
-		 * The binary representation of TEXT is, well, text, and since libpq
-		 * was nice enough to append a zero byte to it, it'll work just fine
-		 * as a C string.
-		 *
-		 * The binary representation of BYTEA is a bunch of bytes, which could
-		 * include embedded nulls so we have to pay attention to field length.
-		 */
-		blen = PQgetlength(res, i, b_fnum);
-
 		printf("tuple %d: got\n", i);
-		printf(" i = (%d bytes) %d\n",
-			   PQgetlength(res, i, i_fnum), ival);
-		printf(" t = (%d bytes) '%s'\n",
-			   PQgetlength(res, i, t_fnum), tptr);
-		printf(" b = (%d bytes) ", blen);
-		for (j = 0; j < blen; j++)
-			printf("\\%03o", bptr[j]);
+		handle_integer(res, i);
+		handle_real(res, i);
+		handle_boolean(res, i);
+		handle_timestamp(res, i);
+		handle_text(res, i);
+		handle_bytea(res, i);
 		printf("\n\n");
 	}
 }
diff --git a/src/test/examples/testlibpq3.sql b/src/test/examples/testlibpq3.sql
index 35a95ca347..94cb2b97b0 100644
--- a/src/test/examples/testlibpq3.sql
+++ b/src/test/examples/testlibpq3.sql
@@ -1,6 +1,29 @@
 CREATE SCHEMA testlibpq3;
 SET search_path = testlibpq3;
 SET standard_conforming_strings = ON;
-CREATE TABLE test1 (i int4, t text, b bytea);
-INSERT INTO test1 values (1, 'joe''s place', '\000\001\002\003\004');
-INSERT INTO test1 values (2, 'ho there', '\004\003\002\001\000');
+CREATE TABLE test1 (
+    i int4
+  , r real
+  , bo boolean
+  , ts timestamp
+  , t text
+  , b bytea
+);
+INSERT INTO test1
+VALUES (
+    1
+  , 3.141593
+  , true
+  , '2000-01-01 00:00:02.414213'
+  , 'joe''s place'
+  , '\000\001\002\003\004'
+);
+INSERT INTO test1
+VALUES (
+    2
+  , 1.618033
+  , false
+  , '2000-01-01 00:00:01.465571'
+  , 'ho there'
+  , '\004\003\002\001\000'
+);


Attachments:

  [text/plain] testlibpq3-v2.diff (7.9K, ../../YqjH9qGaIixfZXQo@workstation-mark-wong/2-testlibpq3-v2.diff)
  download | inline diff:
diff --git a/src/test/examples/testlibpq3.c b/src/test/examples/testlibpq3.c
index 4f7b791388..68972f17f3 100644
--- a/src/test/examples/testlibpq3.c
+++ b/src/test/examples/testlibpq3.c
@@ -11,21 +11,56 @@
  * CREATE SCHEMA testlibpq3;
  * SET search_path = testlibpq3;
  * SET standard_conforming_strings = ON;
- * CREATE TABLE test1 (i int4, t text, b bytea);
- * INSERT INTO test1 values (1, 'joe''s place', '\000\001\002\003\004');
- * INSERT INTO test1 values (2, 'ho there', '\004\003\002\001\000');
+ * CREATE SCHEMA testlibpq3;
+ * SET search_path = testlibpq3;
+ * SET standard_conforming_strings = ON;
+ * CREATE TABLE test1 (
+ *     i int4
+ *   , r real
+ *   , bo boolean
+ *   , ts timestamp
+ *   , t text
+ *   , b bytea
+ * );
+ * INSERT INTO test1
+ * VALUES (
+ *     1
+ *   , 3.141593
+ *   , true
+ *   , '2000-01-01 00:00:02.414213'
+ *   , 'joe''s place'
+ *   , '\000\001\002\003\004'
+ * );
+ * INSERT INTO test1
+ * VALUES (
+ *     2
+ *   , 1.618033
+ *   , false
+ *   , '2000-01-01 00:00:01.465571'
+ *   , 'ho there'
+ *   , '\004\003\002\001\000'
+ * );
  *
  * The expected output is:
  *
  * tuple 0: got
  *	i = (4 bytes) 1
+ *	r = (4 bytes) 3.141593
+ *	bo = (1 bytes) 1
  *	t = (11 bytes) 'joe's place'
  *	b = (5 bytes) \000\001\002\003\004
  *
  * tuple 0: got
  *	i = (4 bytes) 2
+ *	r = (4 bytes) 1.618033
+ *	bo = (1 bytes) 0
  *	t = (8 bytes) 'ho there'
  *	b = (5 bytes) \004\003\002\001\000
+ *
+ * General notes about this example:
+ *
+ * Use PQfnumber to avoid assumptions about field order in result but when
+ * getting the field values we ignore possibility they are null!
  */
 
 #ifdef WIN32
@@ -36,6 +71,7 @@
 #include <stdlib.h>
 #include <stdint.h>
 #include <string.h>
+#include <time.h>
 #include <sys/types.h>
 #include "libpq-fe.h"
 
@@ -44,6 +80,13 @@
 #include <arpa/inet.h>
 
 
+/* These macros hopefully make reading calculations for timestamps easier. */
+#define POSTGRES_EPOCH_JDATE 2451545 /* == date2j(2000, 1, 1) */
+#define UNIX_EPOCH_JDATE 2440588 /* == date2j(1970, 1, 1) */
+#define SECS_PER_DAY 86400
+
+uint64_t ntohll(uint64_t);
+
 static void
 exit_nicely(PGconn *conn)
 {
@@ -51,6 +94,142 @@ exit_nicely(PGconn *conn)
 	exit(1);
 }
 
+static void
+handle_boolean(PGresult *res, int i)
+{
+	int			fnum;
+	char	   *ptr;
+	int			val;
+
+	fnum = PQfnumber(res, "bo");
+	ptr = PQgetvalue(res, i, fnum);
+	val = (int) *ptr;
+	printf(" bo = (%d bytes) %d\n", PQgetlength(res, i, fnum), val);
+}
+
+static void
+handle_bytea(PGresult *res, int i)
+{
+	int			j;
+	int			fnum;
+	char	   *ptr;
+	int			len;
+
+	fnum = PQfnumber(res, "b");
+	ptr = PQgetvalue(res, i, fnum);
+
+	/*
+	 * The binary representation of BYTEA is a bunch of bytes, which could
+	 * include embedded nulls so we have to pay attention to field length.
+	 */
+	len = PQgetlength(res, i, fnum);
+	printf(" b = (%d bytes) ", len);
+	for (j = 0; j < len; j++) printf("\\%03o", ptr[j]);
+}
+
+static void
+handle_integer(PGresult *res, int i)
+{
+	int			fnum;
+	char	   *ptr;
+	int			val;
+
+	fnum = PQfnumber(res, "i");
+	ptr = PQgetvalue(res, i, fnum);
+
+	/*
+	 * The binary representation of INT4 is in network byte order, which
+	 * we'd better coerce to the local byte order.
+	 */
+	val = ntohl(*((uint32_t *) ptr));
+
+	printf(" i = (%d bytes) %d\n", PQgetlength(res, i, fnum), val);
+}
+
+static void
+handle_real(PGresult *res, int i)
+{
+	int			fnum;
+	char	   *ptr;
+	union {
+		int		i;
+		float	f;
+	}			val;
+
+	fnum = PQfnumber(res, "r");
+	ptr = PQgetvalue(res, i, fnum);
+
+	/*
+	 * The binary representation of INT4 is in network byte order, which
+	 * we'd better coerce to the local byte order.
+	 */
+	val.i = ntohl(*((uint32_t *) ptr));
+
+	printf(" r = (%d bytes) %f\n", PQgetlength(res, i, fnum), val.f);
+}
+
+static void
+handle_text(PGresult *res, int i)
+{
+	int			fnum;
+	char	   *ptr;
+
+	fnum = PQfnumber(res, "t");
+	ptr = PQgetvalue(res, i, fnum);
+
+	/*
+	 * The binary representation of TEXT is, well, text, and since libpq was
+	 * nice enough to append a zero byte to it, it'll work just fine as a C
+	 * string.
+	 */
+	printf(" t = (%d bytes) '%s'\n", PQgetlength(res, i, fnum), ptr);
+}
+
+static void
+handle_timestamp(PGresult *res, int i)
+{
+	int			fnum;
+	char	   *ptr;
+	uint64_t	val;
+
+	struct tm  *tm;
+	time_t		timep;
+	uint32_t	mantissa;
+
+	fnum = PQfnumber(res, "ts");
+	ptr = PQgetvalue(res, i, fnum);
+	val = ntohll(*((uint64_t *) ptr));
+
+	/*
+	 * The binary representation of a timestamp is in microseconds
+	 * from 2000-01-01.
+	 */
+	timep = val / (uint64_t) 1000000 +
+			(uint64_t) (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) *
+			(uint64_t) SECS_PER_DAY;
+	mantissa = val - (uint64_t) (timep -
+			(POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY) *
+			(uint64_t) 1000000;
+
+	/* For ease of testing, assume and print timestamps in GMT. */
+	tm = gmtime(&timep);
+
+	printf(" ts = (%d bytes) %04d-%02d-%02d %02d:%02d:%02d.%06d\n",
+			PQgetlength(res, i, fnum), tm->tm_year + 1900, tm->tm_mon + 1,
+			tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, mantissa);
+}
+
+/* This is a uint64_t version of ntohl from arpa/inet.h. */
+uint64_t
+ntohll(uint64_t netlonglong)
+{
+	if (ntohl(1) == 1)
+		return netlonglong;
+	else
+		return (uint64_t) (ntohl((int) ((netlonglong << 32) >> 32))) << 32 |
+				(uint64_t) ntohl(((int) (netlonglong >> 32)));
+}
+
 /*
  * This function prints a query result that is a binary-format fetch from
  * a table defined as in the comment above.  We split it out because the
@@ -59,54 +238,17 @@ exit_nicely(PGconn *conn)
 static void
 show_binary_results(PGresult *res)
 {
-	int			i,
-				j;
-	int			i_fnum,
-				t_fnum,
-				b_fnum;
-
-	/* Use PQfnumber to avoid assumptions about field order in result */
-	i_fnum = PQfnumber(res, "i");
-	t_fnum = PQfnumber(res, "t");
-	b_fnum = PQfnumber(res, "b");
+	int			i;
 
 	for (i = 0; i < PQntuples(res); i++)
 	{
-		char	   *iptr;
-		char	   *tptr;
-		char	   *bptr;
-		int			blen;
-		int			ival;
-
-		/* Get the field values (we ignore possibility they are null!) */
-		iptr = PQgetvalue(res, i, i_fnum);
-		tptr = PQgetvalue(res, i, t_fnum);
-		bptr = PQgetvalue(res, i, b_fnum);
-
-		/*
-		 * The binary representation of INT4 is in network byte order, which
-		 * we'd better coerce to the local byte order.
-		 */
-		ival = ntohl(*((uint32_t *) iptr));
-
-		/*
-		 * The binary representation of TEXT is, well, text, and since libpq
-		 * was nice enough to append a zero byte to it, it'll work just fine
-		 * as a C string.
-		 *
-		 * The binary representation of BYTEA is a bunch of bytes, which could
-		 * include embedded nulls so we have to pay attention to field length.
-		 */
-		blen = PQgetlength(res, i, b_fnum);
-
 		printf("tuple %d: got\n", i);
-		printf(" i = (%d bytes) %d\n",
-			   PQgetlength(res, i, i_fnum), ival);
-		printf(" t = (%d bytes) '%s'\n",
-			   PQgetlength(res, i, t_fnum), tptr);
-		printf(" b = (%d bytes) ", blen);
-		for (j = 0; j < blen; j++)
-			printf("\\%03o", bptr[j]);
+		handle_integer(res, i);
+		handle_real(res, i);
+		handle_boolean(res, i);
+		handle_timestamp(res, i);
+		handle_text(res, i);
+		handle_bytea(res, i);
 		printf("\n\n");
 	}
 }
diff --git a/src/test/examples/testlibpq3.sql b/src/test/examples/testlibpq3.sql
index 35a95ca347..94cb2b97b0 100644
--- a/src/test/examples/testlibpq3.sql
+++ b/src/test/examples/testlibpq3.sql
@@ -1,6 +1,29 @@
 CREATE SCHEMA testlibpq3;
 SET search_path = testlibpq3;
 SET standard_conforming_strings = ON;
-CREATE TABLE test1 (i int4, t text, b bytea);
-INSERT INTO test1 values (1, 'joe''s place', '\000\001\002\003\004');
-INSERT INTO test1 values (2, 'ho there', '\004\003\002\001\000');
+CREATE TABLE test1 (
+    i int4
+  , r real
+  , bo boolean
+  , ts timestamp
+  , t text
+  , b bytea
+);
+INSERT INTO test1
+VALUES (
+    1
+  , 3.141593
+  , true
+  , '2000-01-01 00:00:02.414213'
+  , 'joe''s place'
+  , '\000\001\002\003\004'
+);
+INSERT INTO test1
+VALUES (
+    2
+  , 1.618033
+  , false
+  , '2000-01-01 00:00:01.465571'
+  , 'ho there'
+  , '\004\003\002\001\000'
+);


^ permalink  raw  reply  [nested|flat] 19+ messages in thread

* Re: real/float example for testlibpq3
@ 2022-06-16 19:06  Robert Haas <[email protected]>
  parent: Mark Wong <[email protected]>
  0 siblings, 1 reply; 19+ messages in thread

From: Robert Haas @ 2022-06-16 19:06 UTC (permalink / raw)
  To: Mark Wong <[email protected]>; +Cc: Greg Stark <[email protected]>; Tom Lane <[email protected]>; Chapman Flack <[email protected]>; Ashutosh Bapat <[email protected]>; PostgreSQL Hackers <[email protected]>

On Tue, Jun 14, 2022 at 1:40 PM Mark Wong <[email protected]> wrote:
> Checking in for quick feedback to see if this refactor makes sense.
>
> I've created a function for each data type with the idea that an example
> for handling a specific data type can be more easily reviewed by looking
> in a single place.
>
> I've added examples for REAL, TIMESTAMP WITHOUT TIME ZONE, and BOOLEAN
> to try to illustrate how testlibpq3.sql and testlibpq3.c will grow if
> this is a good way to go.

I think this could be a reasonable kind of thing to do, but I think
you left the "ts" output out of the example output in the comments,
and also your code's apparently not portable, because my compiler is
OK with testlibpq3 right now, but with your patch it emits lengthy
unhappy moaning, starting with:

testlibpq3.c:88:10: error: expected ')'
uint64_t ntohll(uint64_t);
         ^
/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/include/sys/_endian.h:140:25:
note: expanded from macro 'ntohll'
#define ntohll(x)       __DARWIN_OSSwapInt64(x)
                        ^

Apparently macOS defines ntohll as a macro, and it's not amused by
your attempt to include a function prototype for it.

I'm not sure that we want to let these test programs grow very large.
In particular, I don't think we should let ourselves get sucked into
adding an example for every data type under the sun -- if that's
wanted, the solution is perhaps to add documentation for the binary
formats, not hide impromptu documentation inside a test program.  But
doing this much seems OK to me.

-- 
Robert Haas
EDB: http://www.enterprisedb.com





^ permalink  raw  reply  [nested|flat] 19+ messages in thread

* Re: real/float example for testlibpq3
@ 2022-06-16 19:41  Tom Lane <[email protected]>
  parent: Robert Haas <[email protected]>
  0 siblings, 2 replies; 19+ messages in thread

From: Tom Lane @ 2022-06-16 19:41 UTC (permalink / raw)
  To: Robert Haas <[email protected]>; +Cc: Mark Wong <[email protected]>; Greg Stark <[email protected]>; Chapman Flack <[email protected]>; Ashutosh Bapat <[email protected]>; PostgreSQL Hackers <[email protected]>

Robert Haas <[email protected]> writes:
> On Tue, Jun 14, 2022 at 1:40 PM Mark Wong <[email protected]> wrote:
>> I've created a function for each data type with the idea that an example
>> for handling a specific data type can be more easily reviewed by looking
>> in a single place.
>> I've added examples for REAL, TIMESTAMP WITHOUT TIME ZONE, and BOOLEAN
>> to try to illustrate how testlibpq3.sql and testlibpq3.c will grow if
>> this is a good way to go.

> I'm not sure that we want to let these test programs grow very large.
> In particular, I don't think we should let ourselves get sucked into
> adding an example for every data type under the sun -- if that's
> wanted, the solution is perhaps to add documentation for the binary
> formats, not hide impromptu documentation inside a test program.  But
> doing this much seems OK to me.

Yeah, "hiding impromptu documentation inside a test program" is what
this looks like, and I'm not sure that's a reasonable way to go.

(1) Who's going to think to look in src/test/examples/testlibpq3.c for
documentation of binary formats?

(2) The useful details are likely to get buried in notational and
portability concerns, as I think your build failure illustrates.

(3) I bet few if any packagers install these files, so that the new
info would be unavailable to many people.

I think some new appendix in the main SGML docs would be the appropriate
place if we want to provide real documentation.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 19+ messages in thread

* Re: real/float example for testlibpq3
@ 2022-06-16 22:56  Mark Wong <[email protected]>
  parent: Tom Lane <[email protected]>
  1 sibling, 0 replies; 19+ messages in thread

From: Mark Wong @ 2022-06-16 22:56 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Robert Haas <[email protected]>; Greg Stark <[email protected]>; Chapman Flack <[email protected]>; Ashutosh Bapat <[email protected]>; PostgreSQL Hackers <[email protected]>

On Thu, Jun 16, 2022 at 03:41:50PM -0400, Tom Lane wrote:
> Robert Haas <[email protected]> writes:
> > On Tue, Jun 14, 2022 at 1:40 PM Mark Wong <[email protected]> wrote:
> >> I've created a function for each data type with the idea that an example
> >> for handling a specific data type can be more easily reviewed by looking
> >> in a single place.
> >> I've added examples for REAL, TIMESTAMP WITHOUT TIME ZONE, and BOOLEAN
> >> to try to illustrate how testlibpq3.sql and testlibpq3.c will grow if
> >> this is a good way to go.
> 
> > I'm not sure that we want to let these test programs grow very large.
> > In particular, I don't think we should let ourselves get sucked into
> > adding an example for every data type under the sun -- if that's
> > wanted, the solution is perhaps to add documentation for the binary
> > formats, not hide impromptu documentation inside a test program.  But
> > doing this much seems OK to me.
> 
> Yeah, "hiding impromptu documentation inside a test program" is what
> this looks like, and I'm not sure that's a reasonable way to go.
> 
> (1) Who's going to think to look in src/test/examples/testlibpq3.c for
> documentation of binary formats?
> 
> (2) The useful details are likely to get buried in notational and
> portability concerns, as I think your build failure illustrates.
> 
> (3) I bet few if any packagers install these files, so that the new
> info would be unavailable to many people.
> 
> I think some new appendix in the main SGML docs would be the appropriate
> place if we want to provide real documentation.

Ok, I'll leave the testlibpq3.c as it was then.  If it's worth keeping
any of those changes, then I can remove the timestamp example because of
the ntohll() portability since that is trivial.

I'll start a new appendix and share again when I have something to show.

Regards,
Mark

--
Mark Wong
EDB: http://www.enterprisedb.com





^ permalink  raw  reply  [nested|flat] 19+ messages in thread

* Re: real/float example for testlibpq3
@ 2022-07-01 17:18  Mark Wong <[email protected]>
  parent: Tom Lane <[email protected]>
  1 sibling, 1 reply; 19+ messages in thread

From: Mark Wong @ 2022-07-01 17:18 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Robert Haas <[email protected]>; Greg Stark <[email protected]>; Chapman Flack <[email protected]>; Ashutosh Bapat <[email protected]>; PostgreSQL Hackers <[email protected]>

On Thu, Jun 16, 2022 at 03:41:50PM -0400, Tom Lane wrote:
> Robert Haas <[email protected]> writes:
> > On Tue, Jun 14, 2022 at 1:40 PM Mark Wong <[email protected]> wrote:
> >> I've created a function for each data type with the idea that an example
> >> for handling a specific data type can be more easily reviewed by looking
> >> in a single place.
> >> I've added examples for REAL, TIMESTAMP WITHOUT TIME ZONE, and BOOLEAN
> >> to try to illustrate how testlibpq3.sql and testlibpq3.c will grow if
> >> this is a good way to go.
> 
> > I'm not sure that we want to let these test programs grow very large.
> > In particular, I don't think we should let ourselves get sucked into
> > adding an example for every data type under the sun -- if that's
> > wanted, the solution is perhaps to add documentation for the binary
> > formats, not hide impromptu documentation inside a test program.  But
> > doing this much seems OK to me.
> 
> Yeah, "hiding impromptu documentation inside a test program" is what
> this looks like, and I'm not sure that's a reasonable way to go.
> 
> (1) Who's going to think to look in src/test/examples/testlibpq3.c for
> documentation of binary formats?
> 
> (2) The useful details are likely to get buried in notational and
> portability concerns, as I think your build failure illustrates.
> 
> (3) I bet few if any packagers install these files, so that the new
> info would be unavailable to many people.
> 
> I think some new appendix in the main SGML docs would be the appropriate
> place if we want to provide real documentation.

Just wanted to do another quick check in before I go too far.  How do
does this start look?  Extending the libpq section with a code snippet
and description per data type?

Regards,
Mark

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 37ec3cb4e5..44c60223ee 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -8874,6 +8874,109 @@ int PQisthreadsafe();
   </para>
  </sect1>
 
+ <sect1 id="libpq-binary-format">
+  <title>Binary Format</title>
+  <para>
+    Various <application>libpq</application> functions support sending or
+    receiving data in binary format.  Data in binary format requires the
+    application to be handle any differences between the system and the network
+    byte order.
+  </para>
+
+  <para>
+    <xref linkend="libpq-example-3"/> demonstrates how to write a complete
+    program that uses <application>libpq</application> functions that request
+    data in binary foramt.  This example shows how to handle only a few data
+    types.  This section will detail the handling of as many of the data types
+    as possible.  See <xref linkend="datatype"/> for descriptions of each of the
+    built-in data types.
+  </para>
+
+  <sect2 id="libpq-binary-boolean">
+   <title><type>boolean</type></title>
+    <para>
+       A <type>boolean</type> is transmitted as single byte that, when cast to
+       an <literal>int</literal>, will be <literal>0</literal> for
+       <literal>false</literal> and <literal>1</literal> for
+       <literal>true</literal>.
+    </para>
+<programlisting>
+<![CDATA[
+int         value;
+
+ptr = PQgetvalue(res, row_number, column_number);
+value = (int) *ptr;
+printf("%d\n", value);
+]]>
+</programlisting>
+  </sect2>
+
+  <sect2 id="libpq-binary-real">
+   <title><type>real</type></title>
+    <para>
+      A <type>real</type> is composed of 4 bytes and needs to be handled
+      correctly for byte order.
+    </para>
+<programlisting>
+<![CDATA[
+union {
+    int     i;
+    float   f;
+}           value;
+
+ptr = PQgetvalue(res, row_number, column_number);
+val.i = ntohl(*((uint32_t *) ptr));
+printf("%f\n", value.f);
+]]>
+</programlisting>
+  </sect2>
+
+  <sect2 id="libpq-binary-timestamp-without-time-zone">
+   <title><type>timestamp without time zone</type></title>
+    <para>
+      A <type>timestamp without time zone</type> is a 64-bit data type
+      representing the number of microseconds since January 1, 2000.  It can be
+      converted into a broken-down time representation by converting the time
+      into seconds and saving the microseconds elsewhere.
+    </para>
+    <para>
+      Note that in C time is counted from January 1, 1970 so this discrepency
+      needs to be accounted for in addition to handling the network byte order.
+    </para>
+<programlisting>
+<![CDATA[
+#define POSTGRES_EPOCH_JDATE 2451545 /* == date2j(2000, 1, 1) */
+#define UNIX_EPOCH_JDATE 2440588 /* == date2j(1970, 1, 1) */
+#define SECS_PER_DAY 86400
+
+uint64_t    value;
+
+struct tm  *tm;
+time_t      timep;
+uint32_t    mantissa;
+
+ptr = PQgetvalue(res, column_number, row_number);
+/* Note ntohll() is not implemented on all platforms. */
+val = ntohll(*((uint64_t *) ptr));
+
+timep = val / (uint64_t) 1000000 +
+        (uint64_t) (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) *
+        (uint64_t) SECS_PER_DAY;
+mantissa = val - (uint64_t) (timep -
+        (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY) *
+        (uint64_t) 1000000;
+
+/* Assume and print timestamps in GMT for simplicity. */
+tm = gmtime(&timep);
+
+printf("%04d-%02d-%02d %02d:%02d:%02d.%06d\n",
+        tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
+        tm->tm_min, tm->tm_sec, mantissa);
+]]>
+</programlisting>
+  </sect2>
+
+ </sect1>
 
  <sect1 id="libpq-build">
   <title>Building <application>libpq</application> Programs</title>


Attachments:

  [text/plain] libpq-binary-format-doc-v0.patch (3.6K, ../../Yr8sXL9J%2Fnel7BEM@workstation-mark-wong/2-libpq-binary-format-doc-v0.patch)
  download | inline diff:
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 37ec3cb4e5..44c60223ee 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -8874,6 +8874,109 @@ int PQisthreadsafe();
   </para>
  </sect1>
 
+ <sect1 id="libpq-binary-format">
+  <title>Binary Format</title>
+  <para>
+    Various <application>libpq</application> functions support sending or
+    receiving data in binary format.  Data in binary format requires the
+    application to be handle any differences between the system and the network
+    byte order.
+  </para>
+
+  <para>
+    <xref linkend="libpq-example-3"/> demonstrates how to write a complete
+    program that uses <application>libpq</application> functions that request
+    data in binary foramt.  This example shows how to handle only a few data
+    types.  This section will detail the handling of as many of the data types
+    as possible.  See <xref linkend="datatype"/> for descriptions of each of the
+    built-in data types.
+  </para>
+
+  <sect2 id="libpq-binary-boolean">
+   <title><type>boolean</type></title>
+    <para>
+       A <type>boolean</type> is transmitted as single byte that, when cast to
+       an <literal>int</literal>, will be <literal>0</literal> for
+       <literal>false</literal> and <literal>1</literal> for
+       <literal>true</literal>.
+    </para>
+<programlisting>
+<![CDATA[
+int         value;
+
+ptr = PQgetvalue(res, row_number, column_number);
+value = (int) *ptr;
+printf("%d\n", value);
+]]>
+</programlisting>
+  </sect2>
+
+  <sect2 id="libpq-binary-real">
+   <title><type>real</type></title>
+    <para>
+      A <type>real</type> is composed of 4 bytes and needs to be handled
+      correctly for byte order.
+    </para>
+<programlisting>
+<![CDATA[
+union {
+    int     i;
+    float   f;
+}           value;
+
+ptr = PQgetvalue(res, row_number, column_number);
+val.i = ntohl(*((uint32_t *) ptr));
+printf("%f\n", value.f);
+]]>
+</programlisting>
+  </sect2>
+
+  <sect2 id="libpq-binary-timestamp-without-time-zone">
+   <title><type>timestamp without time zone</type></title>
+    <para>
+      A <type>timestamp without time zone</type> is a 64-bit data type
+      representing the number of microseconds since January 1, 2000.  It can be
+      converted into a broken-down time representation by converting the time
+      into seconds and saving the microseconds elsewhere.
+    </para>
+    <para>
+      Note that in C time is counted from January 1, 1970 so this discrepency
+      needs to be accounted for in addition to handling the network byte order.
+    </para>
+<programlisting>
+<![CDATA[
+#define POSTGRES_EPOCH_JDATE 2451545 /* == date2j(2000, 1, 1) */
+#define UNIX_EPOCH_JDATE 2440588 /* == date2j(1970, 1, 1) */
+#define SECS_PER_DAY 86400
+
+uint64_t    value;
+
+struct tm  *tm;
+time_t      timep;
+uint32_t    mantissa;
+
+ptr = PQgetvalue(res, column_number, row_number);
+/* Note ntohll() is not implemented on all platforms. */
+val = ntohll(*((uint64_t *) ptr));
+
+timep = val / (uint64_t) 1000000 +
+        (uint64_t) (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) *
+        (uint64_t) SECS_PER_DAY;
+mantissa = val - (uint64_t) (timep -
+        (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY) *
+        (uint64_t) 1000000;
+
+/* Assume and print timestamps in GMT for simplicity. */
+tm = gmtime(&timep);
+
+printf("%04d-%02d-%02d %02d:%02d:%02d.%06d\n",
+        tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
+        tm->tm_min, tm->tm_sec, mantissa);
+]]>
+</programlisting>
+  </sect2>
+
+ </sect1>
 
  <sect1 id="libpq-build">
   <title>Building <application>libpq</application> Programs</title>


^ permalink  raw  reply  [nested|flat] 19+ messages in thread

* [PATCH] have gram.y resolve partition strategy names
@ 2022-10-20 10:29  Alvaro Herrera <[email protected]>
  0 siblings, 0 replies; 19+ messages in thread

From: Alvaro Herrera @ 2022-10-20 10:29 UTC (permalink / raw)

---
 src/backend/commands/tablecmds.c      | 35 +++++++++------------------
 src/backend/parser/gram.y             | 22 ++++++++++++++++-
 src/backend/partitioning/partbounds.c | 26 ++++----------------
 src/backend/utils/cache/partcache.c   |  6 +++++
 src/include/nodes/parsenodes.h        | 15 ++++++------
 src/include/partitioning/partbounds.h |  2 +-
 src/include/utils/partcache.h         |  2 +-
 7 files changed, 53 insertions(+), 55 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 20135ef1b0..e07fd747f7 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -605,9 +605,10 @@ static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
 											Oid oldRelOid, void *arg);
 static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
 											 Oid oldrelid, void *arg);
-static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy);
+static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
 static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
-								  List **partexprs, Oid *partopclass, Oid *partcollation, char strategy);
+								  List **partexprs, Oid *partopclass, Oid *partcollation,
+								  PartitionStrategy strategy);
 static void CreateInheritance(Relation child_rel, Relation parent_rel);
 static void RemoveInheritance(Relation child_rel, Relation parent_rel,
 							  bool expect_detached);
@@ -1122,7 +1123,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	if (partitioned)
 	{
 		ParseState *pstate;
-		char		strategy;
 		int			partnatts;
 		AttrNumber	partattrs[PARTITION_MAX_KEYS];
 		Oid			partopclass[PARTITION_MAX_KEYS];
@@ -1147,14 +1147,14 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 		 * and CHECK constraints, we could not have done the transformation
 		 * earlier.
 		 */
-		stmt->partspec = transformPartitionSpec(rel, stmt->partspec,
-												&strategy);
+		stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
 
 		ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
 							  partattrs, &partexprs, partopclass,
-							  partcollation, strategy);
+							  partcollation, stmt->partspec->strategy);
 
-		StorePartitionKey(rel, strategy, partnatts, partattrs, partexprs,
+		StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
+						  partexprs,
 						  partopclass, partcollation);
 
 		/* make it all visible */
@@ -17132,10 +17132,10 @@ RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
 /*
  * Transform any expressions present in the partition key
  *
- * Returns a transformed PartitionSpec, as well as the strategy code
+ * Returns a transformed PartitionSpec.
  */
 static PartitionSpec *
-transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
+transformPartitionSpec(Relation rel, PartitionSpec *partspec)
 {
 	PartitionSpec *newspec;
 	ParseState *pstate;
@@ -17148,21 +17148,8 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
 	newspec->partParams = NIL;
 	newspec->location = partspec->location;
 
-	/* Parse partitioning strategy name */
-	if (pg_strcasecmp(partspec->strategy, "hash") == 0)
-		*strategy = PARTITION_STRATEGY_HASH;
-	else if (pg_strcasecmp(partspec->strategy, "list") == 0)
-		*strategy = PARTITION_STRATEGY_LIST;
-	else if (pg_strcasecmp(partspec->strategy, "range") == 0)
-		*strategy = PARTITION_STRATEGY_RANGE;
-	else
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("unrecognized partitioning strategy \"%s\"",
-						partspec->strategy)));
-
 	/* Check valid number of columns for strategy */
-	if (*strategy == PARTITION_STRATEGY_LIST &&
+	if (partspec->strategy == PARTITION_STRATEGY_LIST &&
 		list_length(partspec->partParams) != 1)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -17208,7 +17195,7 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
 static void
 ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
 					  List **partexprs, Oid *partopclass, Oid *partcollation,
-					  char strategy)
+					  PartitionStrategy strategy)
 {
 	int			attn;
 	ListCell   *lc;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 737bd2d06d..51cff3cd8a 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -213,6 +213,7 @@ static void SplitColQualList(List *qualList,
 static void processCASbits(int cas_bits, int location, const char *constrType,
 			   bool *deferrable, bool *initdeferred, bool *not_valid,
 			   bool *no_inherit, core_yyscan_t yyscanner);
+static PartitionStrategy parsePartitionStrategy(char *strategy);
 static void preprocess_pubobj_list(List *pubobjspec_list,
 								   core_yyscan_t yyscanner);
 static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
@@ -4357,7 +4358,7 @@ PartitionSpec: PARTITION BY ColId '(' part_params ')'
 				{
 					PartitionSpec *n = makeNode(PartitionSpec);
 
-					n->strategy = $3;
+					n->strategy = parsePartitionStrategy($3);
 					n->partParams = $5;
 					n->location = @1;
 
@@ -18414,6 +18415,25 @@ processCASbits(int cas_bits, int location, const char *constrType,
 	}
 }
 
+/*
+ * Parse a user-supplied partition strategy string into parse node
+ * PartitionStrategy representation, or die trying.
+ */
+static PartitionStrategy
+parsePartitionStrategy(char *strategy)
+{
+	if (pg_strcasecmp(strategy, "range") == 0)
+		return PARTITION_STRATEGY_RANGE;
+	else if (pg_strcasecmp(strategy, "hash") == 0)
+		return PARTITION_STRATEGY_HASH;
+	else if (pg_strcasecmp(strategy, "range") == 0)
+		return PARTITION_STRATEGY_RANGE;
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			 errmsg("unrecognized partitioning strategy \"%s\"",
+					strategy)));
+}
+
 /*
  * Process pubobjspec_list to check for errors in any of the objects and
  * convert PUBLICATIONOBJ_CONTINUATION into appropriate PublicationObjSpecType.
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 0823fa7b1d..7b5cd55e80 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -270,10 +270,6 @@ get_qual_from_partbound(Relation parent, PartitionBoundSpec *spec)
 			Assert(spec->strategy == PARTITION_STRATEGY_RANGE);
 			my_qual = get_qual_for_range(parent, spec, false);
 			break;
-
-		default:
-			elog(ERROR, "unexpected partition strategy: %d",
-				 (int) key->strategy);
 	}
 
 	return my_qual;
@@ -338,11 +334,6 @@ partition_bounds_create(PartitionBoundSpec **boundspecs, int nparts,
 
 		case PARTITION_STRATEGY_RANGE:
 			return create_range_bounds(boundspecs, nparts, key, mapping);
-
-		default:
-			elog(ERROR, "unexpected partition strategy: %d",
-				 (int) key->strategy);
-			break;
 	}
 
 	Assert(false);
@@ -1181,11 +1172,9 @@ partition_bounds_merge(int partnatts,
 									  jointype,
 									  outer_parts,
 									  inner_parts);
-
 		default:
-			elog(ERROR, "unexpected partition strategy: %d",
-				 (int) outer_rel->boundinfo->strategy);
-			return NULL;		/* keep compiler quiet */
+			Assert(false);
+			return NULL;
 	}
 }
 
@@ -2892,8 +2881,7 @@ partitions_are_ordered(PartitionBoundInfo boundinfo, Bitmapset *live_parts)
 				return true;
 
 			break;
-		default:
-			/* HASH, or some other strategy */
+		case PARTITION_STRATEGY_HASH:
 			break;
 	}
 
@@ -3241,10 +3229,6 @@ check_new_partition_bound(char *relname, Relation parent,
 
 				break;
 			}
-
-		default:
-			elog(ERROR, "unexpected partition strategy: %d",
-				 (int) key->strategy);
 	}
 
 	if (overlap)
@@ -3980,8 +3964,8 @@ make_partition_op_expr(PartitionKey key, int keynum,
 								   key->partcollation[keynum]);
 			break;
 
-		default:
-			elog(ERROR, "invalid partitioning strategy");
+		case PARTITION_STRATEGY_HASH:
+			Assert(false);
 			break;
 	}
 
diff --git a/src/backend/utils/cache/partcache.c b/src/backend/utils/cache/partcache.c
index afa99c5d03..8f3e411476 100644
--- a/src/backend/utils/cache/partcache.c
+++ b/src/backend/utils/cache/partcache.c
@@ -115,6 +115,12 @@ RelationBuildPartitionKey(Relation relation)
 	key->strategy = form->partstrat;
 	key->partnatts = form->partnatts;
 
+	/* Validate partition strategy code */
+	if (key->strategy != PARTITION_STRATEGY_HASH &&
+		key->strategy != PARTITION_STRATEGY_LIST &&
+		key->strategy != PARTITION_STRATEGY_RANGE)
+		elog(ERROR, "invalid partition strategy \"%c\"", key->strategy);
+
 	/*
 	 * We can rely on the first variable-length attribute being mapped to the
 	 * relevant field of the catalog's C struct, because all previous
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 633e7671b3..99cba300f3 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -827,6 +827,13 @@ typedef struct PartitionElem
 	int			location;		/* token location, or -1 if unknown */
 } PartitionElem;
 
+typedef enum PartitionStrategy
+{
+   PARTITION_STRATEGY_LIST = 'l',
+   PARTITION_STRATEGY_RANGE = 'r',
+   PARTITION_STRATEGY_HASH = 'h'
+} PartitionStrategy;
+
 /*
  * PartitionSpec - parse-time representation of a partition key specification
  *
@@ -835,17 +842,11 @@ typedef struct PartitionElem
 typedef struct PartitionSpec
 {
 	NodeTag		type;
-	char	   *strategy;		/* partitioning strategy ('hash', 'list' or
-								 * 'range') */
+	PartitionStrategy strategy;
 	List	   *partParams;		/* List of PartitionElems */
 	int			location;		/* token location, or -1 if unknown */
 } PartitionSpec;
 
-/* Internal codes for partitioning strategies */
-#define PARTITION_STRATEGY_HASH		'h'
-#define PARTITION_STRATEGY_LIST		'l'
-#define PARTITION_STRATEGY_RANGE	'r'
-
 /*
  * PartitionBoundSpec - a partition bound specification
  *
diff --git a/src/include/partitioning/partbounds.h b/src/include/partitioning/partbounds.h
index 1f5b706d83..9af4b1b5c7 100644
--- a/src/include/partitioning/partbounds.h
+++ b/src/include/partitioning/partbounds.h
@@ -78,7 +78,7 @@ struct RelOptInfo;				/* avoid including pathnodes.h here */
  */
 typedef struct PartitionBoundInfoData
 {
-	char		strategy;		/* hash, list or range? */
+	PartitionStrategy	strategy;		/* hash, list or range? */
 	int			ndatums;		/* Length of the datums[] array */
 	Datum	  **datums;
 	PartitionRangeDatumKind **kind; /* The kind of each range bound datum;
diff --git a/src/include/utils/partcache.h b/src/include/utils/partcache.h
index 3394e1fcdb..c01ce48241 100644
--- a/src/include/utils/partcache.h
+++ b/src/include/utils/partcache.h
@@ -23,7 +23,7 @@
  */
 typedef struct PartitionKeyData
 {
-	char		strategy;		/* partitioning strategy */
+	PartitionStrategy strategy;		/* partitioning strategy */
 	int16		partnatts;		/* number of columns in the partition key */
 	AttrNumber *partattrs;		/* attribute numbers of columns in the
 								 * partition key or 0 if it's an expr */
-- 
2.30.2


--xrws3pp5u7pwsoei--





^ permalink  raw  reply  [nested|flat] 19+ messages in thread

* [PATCH v2] have gram.y resolve partition strategy names
@ 2022-10-20 10:29  Alvaro Herrera <[email protected]>
  0 siblings, 0 replies; 19+ messages in thread

From: Alvaro Herrera @ 2022-10-20 10:29 UTC (permalink / raw)

---
 src/backend/commands/tablecmds.c      | 35 +++++++++------------------
 src/backend/parser/gram.y             | 22 ++++++++++++++++-
 src/backend/partitioning/partbounds.c | 26 ++++----------------
 src/backend/utils/cache/partcache.c   |  6 +++++
 src/include/nodes/parsenodes.h        | 15 ++++++------
 src/include/partitioning/partbounds.h |  2 +-
 src/include/utils/partcache.h         |  2 +-
 7 files changed, 53 insertions(+), 55 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 20135ef1b0..e07fd747f7 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -605,9 +605,10 @@ static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
 											Oid oldRelOid, void *arg);
 static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
 											 Oid oldrelid, void *arg);
-static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy);
+static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
 static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
-								  List **partexprs, Oid *partopclass, Oid *partcollation, char strategy);
+								  List **partexprs, Oid *partopclass, Oid *partcollation,
+								  PartitionStrategy strategy);
 static void CreateInheritance(Relation child_rel, Relation parent_rel);
 static void RemoveInheritance(Relation child_rel, Relation parent_rel,
 							  bool expect_detached);
@@ -1122,7 +1123,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	if (partitioned)
 	{
 		ParseState *pstate;
-		char		strategy;
 		int			partnatts;
 		AttrNumber	partattrs[PARTITION_MAX_KEYS];
 		Oid			partopclass[PARTITION_MAX_KEYS];
@@ -1147,14 +1147,14 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 		 * and CHECK constraints, we could not have done the transformation
 		 * earlier.
 		 */
-		stmt->partspec = transformPartitionSpec(rel, stmt->partspec,
-												&strategy);
+		stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
 
 		ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
 							  partattrs, &partexprs, partopclass,
-							  partcollation, strategy);
+							  partcollation, stmt->partspec->strategy);
 
-		StorePartitionKey(rel, strategy, partnatts, partattrs, partexprs,
+		StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
+						  partexprs,
 						  partopclass, partcollation);
 
 		/* make it all visible */
@@ -17132,10 +17132,10 @@ RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
 /*
  * Transform any expressions present in the partition key
  *
- * Returns a transformed PartitionSpec, as well as the strategy code
+ * Returns a transformed PartitionSpec.
  */
 static PartitionSpec *
-transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
+transformPartitionSpec(Relation rel, PartitionSpec *partspec)
 {
 	PartitionSpec *newspec;
 	ParseState *pstate;
@@ -17148,21 +17148,8 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
 	newspec->partParams = NIL;
 	newspec->location = partspec->location;
 
-	/* Parse partitioning strategy name */
-	if (pg_strcasecmp(partspec->strategy, "hash") == 0)
-		*strategy = PARTITION_STRATEGY_HASH;
-	else if (pg_strcasecmp(partspec->strategy, "list") == 0)
-		*strategy = PARTITION_STRATEGY_LIST;
-	else if (pg_strcasecmp(partspec->strategy, "range") == 0)
-		*strategy = PARTITION_STRATEGY_RANGE;
-	else
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("unrecognized partitioning strategy \"%s\"",
-						partspec->strategy)));
-
 	/* Check valid number of columns for strategy */
-	if (*strategy == PARTITION_STRATEGY_LIST &&
+	if (partspec->strategy == PARTITION_STRATEGY_LIST &&
 		list_length(partspec->partParams) != 1)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -17208,7 +17195,7 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
 static void
 ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
 					  List **partexprs, Oid *partopclass, Oid *partcollation,
-					  char strategy)
+					  PartitionStrategy strategy)
 {
 	int			attn;
 	ListCell   *lc;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 737bd2d06d..6ca23f88c4 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -213,6 +213,7 @@ static void SplitColQualList(List *qualList,
 static void processCASbits(int cas_bits, int location, const char *constrType,
 			   bool *deferrable, bool *initdeferred, bool *not_valid,
 			   bool *no_inherit, core_yyscan_t yyscanner);
+static PartitionStrategy parsePartitionStrategy(char *strategy);
 static void preprocess_pubobj_list(List *pubobjspec_list,
 								   core_yyscan_t yyscanner);
 static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
@@ -4357,7 +4358,7 @@ PartitionSpec: PARTITION BY ColId '(' part_params ')'
 				{
 					PartitionSpec *n = makeNode(PartitionSpec);
 
-					n->strategy = $3;
+					n->strategy = parsePartitionStrategy($3);
 					n->partParams = $5;
 					n->location = @1;
 
@@ -18414,6 +18415,25 @@ processCASbits(int cas_bits, int location, const char *constrType,
 	}
 }
 
+/*
+ * Parse a user-supplied partition strategy string into parse node
+ * PartitionStrategy representation, or die trying.
+ */
+static PartitionStrategy
+parsePartitionStrategy(char *strategy)
+{
+	if (pg_strcasecmp(strategy, "list") == 0)
+		return PARTITION_STRATEGY_LIST;
+	else if (pg_strcasecmp(strategy, "range") == 0)
+		return PARTITION_STRATEGY_RANGE;
+	else if (pg_strcasecmp(strategy, "hash") == 0)
+		return PARTITION_STRATEGY_HASH;
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			 errmsg("unrecognized partitioning strategy \"%s\"",
+					strategy)));
+}
+
 /*
  * Process pubobjspec_list to check for errors in any of the objects and
  * convert PUBLICATIONOBJ_CONTINUATION into appropriate PublicationObjSpecType.
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 0823fa7b1d..7b5cd55e80 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -270,10 +270,6 @@ get_qual_from_partbound(Relation parent, PartitionBoundSpec *spec)
 			Assert(spec->strategy == PARTITION_STRATEGY_RANGE);
 			my_qual = get_qual_for_range(parent, spec, false);
 			break;
-
-		default:
-			elog(ERROR, "unexpected partition strategy: %d",
-				 (int) key->strategy);
 	}
 
 	return my_qual;
@@ -338,11 +334,6 @@ partition_bounds_create(PartitionBoundSpec **boundspecs, int nparts,
 
 		case PARTITION_STRATEGY_RANGE:
 			return create_range_bounds(boundspecs, nparts, key, mapping);
-
-		default:
-			elog(ERROR, "unexpected partition strategy: %d",
-				 (int) key->strategy);
-			break;
 	}
 
 	Assert(false);
@@ -1181,11 +1172,9 @@ partition_bounds_merge(int partnatts,
 									  jointype,
 									  outer_parts,
 									  inner_parts);
-
 		default:
-			elog(ERROR, "unexpected partition strategy: %d",
-				 (int) outer_rel->boundinfo->strategy);
-			return NULL;		/* keep compiler quiet */
+			Assert(false);
+			return NULL;
 	}
 }
 
@@ -2892,8 +2881,7 @@ partitions_are_ordered(PartitionBoundInfo boundinfo, Bitmapset *live_parts)
 				return true;
 
 			break;
-		default:
-			/* HASH, or some other strategy */
+		case PARTITION_STRATEGY_HASH:
 			break;
 	}
 
@@ -3241,10 +3229,6 @@ check_new_partition_bound(char *relname, Relation parent,
 
 				break;
 			}
-
-		default:
-			elog(ERROR, "unexpected partition strategy: %d",
-				 (int) key->strategy);
 	}
 
 	if (overlap)
@@ -3980,8 +3964,8 @@ make_partition_op_expr(PartitionKey key, int keynum,
 								   key->partcollation[keynum]);
 			break;
 
-		default:
-			elog(ERROR, "invalid partitioning strategy");
+		case PARTITION_STRATEGY_HASH:
+			Assert(false);
 			break;
 	}
 
diff --git a/src/backend/utils/cache/partcache.c b/src/backend/utils/cache/partcache.c
index afa99c5d03..8f3e411476 100644
--- a/src/backend/utils/cache/partcache.c
+++ b/src/backend/utils/cache/partcache.c
@@ -115,6 +115,12 @@ RelationBuildPartitionKey(Relation relation)
 	key->strategy = form->partstrat;
 	key->partnatts = form->partnatts;
 
+	/* Validate partition strategy code */
+	if (key->strategy != PARTITION_STRATEGY_HASH &&
+		key->strategy != PARTITION_STRATEGY_LIST &&
+		key->strategy != PARTITION_STRATEGY_RANGE)
+		elog(ERROR, "invalid partition strategy \"%c\"", key->strategy);
+
 	/*
 	 * We can rely on the first variable-length attribute being mapped to the
 	 * relevant field of the catalog's C struct, because all previous
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 633e7671b3..99cba300f3 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -827,6 +827,13 @@ typedef struct PartitionElem
 	int			location;		/* token location, or -1 if unknown */
 } PartitionElem;
 
+typedef enum PartitionStrategy
+{
+   PARTITION_STRATEGY_LIST = 'l',
+   PARTITION_STRATEGY_RANGE = 'r',
+   PARTITION_STRATEGY_HASH = 'h'
+} PartitionStrategy;
+
 /*
  * PartitionSpec - parse-time representation of a partition key specification
  *
@@ -835,17 +842,11 @@ typedef struct PartitionElem
 typedef struct PartitionSpec
 {
 	NodeTag		type;
-	char	   *strategy;		/* partitioning strategy ('hash', 'list' or
-								 * 'range') */
+	PartitionStrategy strategy;
 	List	   *partParams;		/* List of PartitionElems */
 	int			location;		/* token location, or -1 if unknown */
 } PartitionSpec;
 
-/* Internal codes for partitioning strategies */
-#define PARTITION_STRATEGY_HASH		'h'
-#define PARTITION_STRATEGY_LIST		'l'
-#define PARTITION_STRATEGY_RANGE	'r'
-
 /*
  * PartitionBoundSpec - a partition bound specification
  *
diff --git a/src/include/partitioning/partbounds.h b/src/include/partitioning/partbounds.h
index 1f5b706d83..9af4b1b5c7 100644
--- a/src/include/partitioning/partbounds.h
+++ b/src/include/partitioning/partbounds.h
@@ -78,7 +78,7 @@ struct RelOptInfo;				/* avoid including pathnodes.h here */
  */
 typedef struct PartitionBoundInfoData
 {
-	char		strategy;		/* hash, list or range? */
+	PartitionStrategy	strategy;		/* hash, list or range? */
 	int			ndatums;		/* Length of the datums[] array */
 	Datum	  **datums;
 	PartitionRangeDatumKind **kind; /* The kind of each range bound datum;
diff --git a/src/include/utils/partcache.h b/src/include/utils/partcache.h
index 3394e1fcdb..c01ce48241 100644
--- a/src/include/utils/partcache.h
+++ b/src/include/utils/partcache.h
@@ -23,7 +23,7 @@
  */
 typedef struct PartitionKeyData
 {
-	char		strategy;		/* partitioning strategy */
+	PartitionStrategy strategy;		/* partitioning strategy */
 	int16		partnatts;		/* number of columns in the partition key */
 	AttrNumber *partattrs;		/* attribute numbers of columns in the
 								 * partition key or 0 if it's an expr */
-- 
2.30.2


--dfvyceejmswyjvsx--





^ permalink  raw  reply  [nested|flat] 19+ messages in thread

* [PATCH v3] have gram.y resolve partition strategy names
@ 2022-10-20 10:29  Alvaro Herrera <[email protected]>
  0 siblings, 0 replies; 19+ messages in thread

From: Alvaro Herrera @ 2022-10-20 10:29 UTC (permalink / raw)

---
 src/backend/commands/tablecmds.c      | 35 +++++++++------------------
 src/backend/parser/gram.y             | 22 ++++++++++++++++-
 src/backend/partitioning/partbounds.c | 27 ++++-----------------
 src/backend/utils/cache/partcache.c   |  6 +++++
 src/include/nodes/parsenodes.h        | 15 ++++++------
 src/include/partitioning/partbounds.h |  2 +-
 src/include/utils/partcache.h         |  3 ++-
 7 files changed, 54 insertions(+), 56 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 20135ef1b0..e07fd747f7 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -605,9 +605,10 @@ static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
 											Oid oldRelOid, void *arg);
 static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
 											 Oid oldrelid, void *arg);
-static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy);
+static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
 static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
-								  List **partexprs, Oid *partopclass, Oid *partcollation, char strategy);
+								  List **partexprs, Oid *partopclass, Oid *partcollation,
+								  PartitionStrategy strategy);
 static void CreateInheritance(Relation child_rel, Relation parent_rel);
 static void RemoveInheritance(Relation child_rel, Relation parent_rel,
 							  bool expect_detached);
@@ -1122,7 +1123,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	if (partitioned)
 	{
 		ParseState *pstate;
-		char		strategy;
 		int			partnatts;
 		AttrNumber	partattrs[PARTITION_MAX_KEYS];
 		Oid			partopclass[PARTITION_MAX_KEYS];
@@ -1147,14 +1147,14 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 		 * and CHECK constraints, we could not have done the transformation
 		 * earlier.
 		 */
-		stmt->partspec = transformPartitionSpec(rel, stmt->partspec,
-												&strategy);
+		stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
 
 		ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
 							  partattrs, &partexprs, partopclass,
-							  partcollation, strategy);
+							  partcollation, stmt->partspec->strategy);
 
-		StorePartitionKey(rel, strategy, partnatts, partattrs, partexprs,
+		StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
+						  partexprs,
 						  partopclass, partcollation);
 
 		/* make it all visible */
@@ -17132,10 +17132,10 @@ RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
 /*
  * Transform any expressions present in the partition key
  *
- * Returns a transformed PartitionSpec, as well as the strategy code
+ * Returns a transformed PartitionSpec.
  */
 static PartitionSpec *
-transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
+transformPartitionSpec(Relation rel, PartitionSpec *partspec)
 {
 	PartitionSpec *newspec;
 	ParseState *pstate;
@@ -17148,21 +17148,8 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
 	newspec->partParams = NIL;
 	newspec->location = partspec->location;
 
-	/* Parse partitioning strategy name */
-	if (pg_strcasecmp(partspec->strategy, "hash") == 0)
-		*strategy = PARTITION_STRATEGY_HASH;
-	else if (pg_strcasecmp(partspec->strategy, "list") == 0)
-		*strategy = PARTITION_STRATEGY_LIST;
-	else if (pg_strcasecmp(partspec->strategy, "range") == 0)
-		*strategy = PARTITION_STRATEGY_RANGE;
-	else
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("unrecognized partitioning strategy \"%s\"",
-						partspec->strategy)));
-
 	/* Check valid number of columns for strategy */
-	if (*strategy == PARTITION_STRATEGY_LIST &&
+	if (partspec->strategy == PARTITION_STRATEGY_LIST &&
 		list_length(partspec->partParams) != 1)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -17208,7 +17195,7 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
 static void
 ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
 					  List **partexprs, Oid *partopclass, Oid *partcollation,
-					  char strategy)
+					  PartitionStrategy strategy)
 {
 	int			attn;
 	ListCell   *lc;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 737bd2d06d..6ca23f88c4 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -213,6 +213,7 @@ static void SplitColQualList(List *qualList,
 static void processCASbits(int cas_bits, int location, const char *constrType,
 			   bool *deferrable, bool *initdeferred, bool *not_valid,
 			   bool *no_inherit, core_yyscan_t yyscanner);
+static PartitionStrategy parsePartitionStrategy(char *strategy);
 static void preprocess_pubobj_list(List *pubobjspec_list,
 								   core_yyscan_t yyscanner);
 static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
@@ -4357,7 +4358,7 @@ PartitionSpec: PARTITION BY ColId '(' part_params ')'
 				{
 					PartitionSpec *n = makeNode(PartitionSpec);
 
-					n->strategy = $3;
+					n->strategy = parsePartitionStrategy($3);
 					n->partParams = $5;
 					n->location = @1;
 
@@ -18414,6 +18415,25 @@ processCASbits(int cas_bits, int location, const char *constrType,
 	}
 }
 
+/*
+ * Parse a user-supplied partition strategy string into parse node
+ * PartitionStrategy representation, or die trying.
+ */
+static PartitionStrategy
+parsePartitionStrategy(char *strategy)
+{
+	if (pg_strcasecmp(strategy, "list") == 0)
+		return PARTITION_STRATEGY_LIST;
+	else if (pg_strcasecmp(strategy, "range") == 0)
+		return PARTITION_STRATEGY_RANGE;
+	else if (pg_strcasecmp(strategy, "hash") == 0)
+		return PARTITION_STRATEGY_HASH;
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			 errmsg("unrecognized partitioning strategy \"%s\"",
+					strategy)));
+}
+
 /*
  * Process pubobjspec_list to check for errors in any of the objects and
  * convert PUBLICATIONOBJ_CONTINUATION into appropriate PublicationObjSpecType.
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 0823fa7b1d..29643fb4ab 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -270,10 +270,6 @@ get_qual_from_partbound(Relation parent, PartitionBoundSpec *spec)
 			Assert(spec->strategy == PARTITION_STRATEGY_RANGE);
 			my_qual = get_qual_for_range(parent, spec, false);
 			break;
-
-		default:
-			elog(ERROR, "unexpected partition strategy: %d",
-				 (int) key->strategy);
 	}
 
 	return my_qual;
@@ -338,11 +334,6 @@ partition_bounds_create(PartitionBoundSpec **boundspecs, int nparts,
 
 		case PARTITION_STRATEGY_RANGE:
 			return create_range_bounds(boundspecs, nparts, key, mapping);
-
-		default:
-			elog(ERROR, "unexpected partition strategy: %d",
-				 (int) key->strategy);
-			break;
 	}
 
 	Assert(false);
@@ -1181,12 +1172,9 @@ partition_bounds_merge(int partnatts,
 									  jointype,
 									  outer_parts,
 									  inner_parts);
-
-		default:
-			elog(ERROR, "unexpected partition strategy: %d",
-				 (int) outer_rel->boundinfo->strategy);
-			return NULL;		/* keep compiler quiet */
 	}
+
+	return NULL;
 }
 
 /*
@@ -2892,8 +2880,7 @@ partitions_are_ordered(PartitionBoundInfo boundinfo, Bitmapset *live_parts)
 				return true;
 
 			break;
-		default:
-			/* HASH, or some other strategy */
+		case PARTITION_STRATEGY_HASH:
 			break;
 	}
 
@@ -3241,10 +3228,6 @@ check_new_partition_bound(char *relname, Relation parent,
 
 				break;
 			}
-
-		default:
-			elog(ERROR, "unexpected partition strategy: %d",
-				 (int) key->strategy);
 	}
 
 	if (overlap)
@@ -3980,8 +3963,8 @@ make_partition_op_expr(PartitionKey key, int keynum,
 								   key->partcollation[keynum]);
 			break;
 
-		default:
-			elog(ERROR, "invalid partitioning strategy");
+		case PARTITION_STRATEGY_HASH:
+			Assert(false);
 			break;
 	}
 
diff --git a/src/backend/utils/cache/partcache.c b/src/backend/utils/cache/partcache.c
index afa99c5d03..8f3e411476 100644
--- a/src/backend/utils/cache/partcache.c
+++ b/src/backend/utils/cache/partcache.c
@@ -115,6 +115,12 @@ RelationBuildPartitionKey(Relation relation)
 	key->strategy = form->partstrat;
 	key->partnatts = form->partnatts;
 
+	/* Validate partition strategy code */
+	if (key->strategy != PARTITION_STRATEGY_HASH &&
+		key->strategy != PARTITION_STRATEGY_LIST &&
+		key->strategy != PARTITION_STRATEGY_RANGE)
+		elog(ERROR, "invalid partition strategy \"%c\"", key->strategy);
+
 	/*
 	 * We can rely on the first variable-length attribute being mapped to the
 	 * relevant field of the catalog's C struct, because all previous
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 633e7671b3..99cba300f3 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -827,6 +827,13 @@ typedef struct PartitionElem
 	int			location;		/* token location, or -1 if unknown */
 } PartitionElem;
 
+typedef enum PartitionStrategy
+{
+   PARTITION_STRATEGY_LIST = 'l',
+   PARTITION_STRATEGY_RANGE = 'r',
+   PARTITION_STRATEGY_HASH = 'h'
+} PartitionStrategy;
+
 /*
  * PartitionSpec - parse-time representation of a partition key specification
  *
@@ -835,17 +842,11 @@ typedef struct PartitionElem
 typedef struct PartitionSpec
 {
 	NodeTag		type;
-	char	   *strategy;		/* partitioning strategy ('hash', 'list' or
-								 * 'range') */
+	PartitionStrategy strategy;
 	List	   *partParams;		/* List of PartitionElems */
 	int			location;		/* token location, or -1 if unknown */
 } PartitionSpec;
 
-/* Internal codes for partitioning strategies */
-#define PARTITION_STRATEGY_HASH		'h'
-#define PARTITION_STRATEGY_LIST		'l'
-#define PARTITION_STRATEGY_RANGE	'r'
-
 /*
  * PartitionBoundSpec - a partition bound specification
  *
diff --git a/src/include/partitioning/partbounds.h b/src/include/partitioning/partbounds.h
index 1f5b706d83..9af4b1b5c7 100644
--- a/src/include/partitioning/partbounds.h
+++ b/src/include/partitioning/partbounds.h
@@ -78,7 +78,7 @@ struct RelOptInfo;				/* avoid including pathnodes.h here */
  */
 typedef struct PartitionBoundInfoData
 {
-	char		strategy;		/* hash, list or range? */
+	PartitionStrategy	strategy;		/* hash, list or range? */
 	int			ndatums;		/* Length of the datums[] array */
 	Datum	  **datums;
 	PartitionRangeDatumKind **kind; /* The kind of each range bound datum;
diff --git a/src/include/utils/partcache.h b/src/include/utils/partcache.h
index 3394e1fcdb..88b5bd7b2b 100644
--- a/src/include/utils/partcache.h
+++ b/src/include/utils/partcache.h
@@ -13,6 +13,7 @@
 
 #include "access/attnum.h"
 #include "fmgr.h"
+#include "nodes/parsenodes.h"
 #include "nodes/pg_list.h"
 #include "nodes/primnodes.h"
 #include "partitioning/partdefs.h"
@@ -23,7 +24,7 @@
  */
 typedef struct PartitionKeyData
 {
-	char		strategy;		/* partitioning strategy */
+	PartitionStrategy strategy;		/* partitioning strategy */
 	int16		partnatts;		/* number of columns in the partition key */
 	AttrNumber *partattrs;		/* attribute numbers of columns in the
 								 * partition key or 0 if it's an expr */
-- 
2.30.2


--d4ylx4ich2eovwuz--





^ permalink  raw  reply  [nested|flat] 19+ messages in thread

* Re: real/float example for testlibpq3
@ 2022-11-01 10:39  Peter Eisentraut <[email protected]>
  parent: Mark Wong <[email protected]>
  0 siblings, 1 reply; 19+ messages in thread

From: Peter Eisentraut @ 2022-11-01 10:39 UTC (permalink / raw)
  To: Mark Wong <[email protected]>; Tom Lane <[email protected]>; +Cc: Robert Haas <[email protected]>; Greg Stark <[email protected]>; Chapman Flack <[email protected]>; Ashutosh Bapat <[email protected]>; PostgreSQL Hackers <[email protected]>

On 01.07.22 19:18, Mark Wong wrote:
> Just wanted to do another quick check in before I go too far.  How do
> does this start look?  Extending the libpq section with a code snippet
> and description per data type?

If we want to start documenting the binary format for all data types, it 
should go into the "Data Types" chapter.  There, we already document the 
text format in great detail, so putting the binary format in there as 
well would make sense.

The libpq chapter could then contain some examples of applying that 
information in the context of libpq.  But the libpq documentation 
shouldn't be the primary place where the formats are documented.






^ permalink  raw  reply  [nested|flat] 19+ messages in thread

* Re: real/float example for testlibpq3
@ 2022-11-01 13:15  Tom Lane <[email protected]>
  parent: Peter Eisentraut <[email protected]>
  0 siblings, 1 reply; 19+ messages in thread

From: Tom Lane @ 2022-11-01 13:15 UTC (permalink / raw)
  To: Peter Eisentraut <[email protected]>; +Cc: Mark Wong <[email protected]>; Robert Haas <[email protected]>; Greg Stark <[email protected]>; Chapman Flack <[email protected]>; Ashutosh Bapat <[email protected]>; PostgreSQL Hackers <[email protected]>

Peter Eisentraut <[email protected]> writes:
> If we want to start documenting the binary format for all data types, it 
> should go into the "Data Types" chapter.  There, we already document the 
> text format in great detail, so putting the binary format in there as 
> well would make sense.

Agreed that the libpq manual is not the place for this, but I feel
like it will also be clutter in "Data Types".  Perhaps we should
invent a new appendix or the like?  Somewhere near the wire protocol
docs seems sensible.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 19+ messages in thread

* Re: real/float example for testlibpq3
@ 2022-11-03 13:23  Peter Eisentraut <[email protected]>
  parent: Tom Lane <[email protected]>
  0 siblings, 1 reply; 19+ messages in thread

From: Peter Eisentraut @ 2022-11-03 13:23 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Mark Wong <[email protected]>; Robert Haas <[email protected]>; Greg Stark <[email protected]>; Chapman Flack <[email protected]>; Ashutosh Bapat <[email protected]>; PostgreSQL Hackers <[email protected]>

On 01.11.22 09:15, Tom Lane wrote:
> Peter Eisentraut <[email protected]> writes:
>> If we want to start documenting the binary format for all data types, it
>> should go into the "Data Types" chapter.  There, we already document the
>> text format in great detail, so putting the binary format in there as
>> well would make sense.
> 
> Agreed that the libpq manual is not the place for this, but I feel
> like it will also be clutter in "Data Types".  Perhaps we should
> invent a new appendix or the like?  Somewhere near the wire protocol
> docs seems sensible.

Would that clutter the protocol docs? ;-)

I suppose figuring out exactly where to put it and how to mark it up, 
etc., in a repeatable fashion is part of the job here.






^ permalink  raw  reply  [nested|flat] 19+ messages in thread

* Re: real/float example for testlibpq3
@ 2022-11-03 13:55  Tom Lane <[email protected]>
  parent: Peter Eisentraut <[email protected]>
  0 siblings, 1 reply; 19+ messages in thread

From: Tom Lane @ 2022-11-03 13:55 UTC (permalink / raw)
  To: Peter Eisentraut <[email protected]>; +Cc: Mark Wong <[email protected]>; Robert Haas <[email protected]>; Greg Stark <[email protected]>; Chapman Flack <[email protected]>; Ashutosh Bapat <[email protected]>; PostgreSQL Hackers <[email protected]>

Peter Eisentraut <[email protected]> writes:
> On 01.11.22 09:15, Tom Lane wrote:
>> Agreed that the libpq manual is not the place for this, but I feel
>> like it will also be clutter in "Data Types".  Perhaps we should
>> invent a new appendix or the like?  Somewhere near the wire protocol
>> docs seems sensible.

> Would that clutter the protocol docs? ;-)

I said "near", not "in".  At the time I was thinking "new appendix",
but I now recall that the wire protocol docs are not an appendix
but a chapter in the Internals division.  So that doesn't seem like
quite the right place anyway.

Perhaps a new chapter under "IV. Client Interfaces" is the right
place?

If we wanted to get aggressive, we could move most of the nitpicky details
about datatype text formatting (e.g., the array quoting rules) there too.
I'm not set on that, but it'd make datatype.sgml smaller which could
hardly be a bad thing.

> I suppose figuring out exactly where to put it and how to mark it up, 
> etc., in a repeatable fashion is part of the job here.

Yup.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 19+ messages in thread

* Re: real/float example for testlibpq3
@ 2022-11-09 20:47  Mark Wong <[email protected]>
  parent: Tom Lane <[email protected]>
  0 siblings, 1 reply; 19+ messages in thread

From: Mark Wong @ 2022-11-09 20:47 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Peter Eisentraut <[email protected]>; Robert Haas <[email protected]>; Greg Stark <[email protected]>; Chapman Flack <[email protected]>; Ashutosh Bapat <[email protected]>; PostgreSQL Hackers <[email protected]>

On Thu, Nov 03, 2022 at 09:55:22AM -0400, Tom Lane wrote:
> Peter Eisentraut <[email protected]> writes:
> > On 01.11.22 09:15, Tom Lane wrote:
> >> Agreed that the libpq manual is not the place for this, but I feel
> >> like it will also be clutter in "Data Types".  Perhaps we should
> >> invent a new appendix or the like?  Somewhere near the wire protocol
> >> docs seems sensible.
> 
> > Would that clutter the protocol docs? ;-)
> 
> I said "near", not "in".  At the time I was thinking "new appendix",
> but I now recall that the wire protocol docs are not an appendix
> but a chapter in the Internals division.  So that doesn't seem like
> quite the right place anyway.
> 
> Perhaps a new chapter under "IV. Client Interfaces" is the right
> place?
> 
> If we wanted to get aggressive, we could move most of the nitpicky details
> about datatype text formatting (e.g., the array quoting rules) there too.
> I'm not set on that, but it'd make datatype.sgml smaller which could
> hardly be a bad thing.
> 
> > I suppose figuring out exactly where to put it and how to mark it up, 
> > etc., in a repeatable fashion is part of the job here.
> 
> Yup.

I'll take a stab at adding a new chapter and share how that looks.

Regards,
Mark

--
Mark Wong
EDB https://enterprisedb.com





^ permalink  raw  reply  [nested|flat] 19+ messages in thread

* Re: real/float example for testlibpq3
@ 2022-11-30 06:59  Michael Paquier <[email protected]>
  parent: Mark Wong <[email protected]>
  0 siblings, 0 replies; 19+ messages in thread

From: Michael Paquier @ 2022-11-30 06:59 UTC (permalink / raw)
  To: Mark Wong <[email protected]>; +Cc: Tom Lane <[email protected]>; Peter Eisentraut <[email protected]>; Robert Haas <[email protected]>; Greg Stark <[email protected]>; Chapman Flack <[email protected]>; Ashutosh Bapat <[email protected]>; PostgreSQL Hackers <[email protected]>

On Wed, Nov 09, 2022 at 12:47:23PM -0800, Mark Wong wrote:
> I'll take a stab at adding a new chapter and share how that looks.

This thread has stalled for three weeks, so I have marked it as
returned with feedback.  Once you have a new patch, please feel free
to submit it.
--
Michael


Attachments:

  [application/pgp-signature] signature.asc (833B, ../../Y4b%[email protected]/2-signature.asc)
  download

^ permalink  raw  reply  [nested|flat] 19+ messages in thread


end of thread, other threads:[~2022-11-30 06:59 UTC | newest]

Thread overview: 19+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2020-03-30 23:59 [PATCH v16 02/10] pg_stat_file and pg_ls_dir_* to use lstat().. Justin Pryzby <[email protected]>
2022-02-28 15:19 Re: real/float example for testlibpq3 Tom Lane <[email protected]>
2022-02-28 22:30 ` Re: real/float example for testlibpq3 Chapman Flack <[email protected]>
2022-02-28 22:48   ` Re: real/float example for testlibpq3 Tom Lane <[email protected]>
2022-03-30 17:16     ` Re: real/float example for testlibpq3 Greg Stark <[email protected]>
2022-06-14 17:40       ` Re: real/float example for testlibpq3 Mark Wong <[email protected]>
2022-06-16 19:06         ` Re: real/float example for testlibpq3 Robert Haas <[email protected]>
2022-06-16 19:41           ` Re: real/float example for testlibpq3 Tom Lane <[email protected]>
2022-06-16 22:56             ` Re: real/float example for testlibpq3 Mark Wong <[email protected]>
2022-07-01 17:18             ` Re: real/float example for testlibpq3 Mark Wong <[email protected]>
2022-11-01 10:39               ` Re: real/float example for testlibpq3 Peter Eisentraut <[email protected]>
2022-11-01 13:15                 ` Re: real/float example for testlibpq3 Tom Lane <[email protected]>
2022-11-03 13:23                   ` Re: real/float example for testlibpq3 Peter Eisentraut <[email protected]>
2022-11-03 13:55                     ` Re: real/float example for testlibpq3 Tom Lane <[email protected]>
2022-11-09 20:47                       ` Re: real/float example for testlibpq3 Mark Wong <[email protected]>
2022-11-30 06:59                         ` Re: real/float example for testlibpq3 Michael Paquier <[email protected]>
2022-10-20 10:29 [PATCH] have gram.y resolve partition strategy names Alvaro Herrera <[email protected]>
2022-10-20 10:29 [PATCH v2] have gram.y resolve partition strategy names Alvaro Herrera <[email protected]>
2022-10-20 10:29 [PATCH v3] have gram.y resolve partition strategy names Alvaro Herrera <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox