Fdw batch insert error out when set batch_size > 65535
Hi,
When reading the code, I noticed some possible issue about fdw batch insert.
When I set batch_size > 65535 and insert more than 65535 rows into foreign table,
It will throw an error:
For example:
------------------
CREATE FOREIGN TABLE vzdalena_tabulka2(a int, b varchar)
SERVER omega_db
OPTIONS (table_name 'tabulka', batch_size '65536');
INSERT INTO vzdalena_tabulka2 SELECT i, 'AHOJ' || i FROM generate_series(1,65536) g(i);
ERROR: number of parameters must be between 0 and 65535
CONTEXT: remote SQL command: INSERT INTO public.tabulka(a, b) VALUES ($1, $2), ($3, $4)...
------------------
Actually, I think if the (number of columns) * (number of rows) > 65535, then we can
get this error. But, I think almost no one will set such a large value, so I think adjust the
batch_size automatically when doing INSERT seems an acceptable solution.
In the postgresGetForeignModifyBatchSize(), if we found the (num of param) * batch_size
Is greater than the limit(65535), then we set it to 65535 / (num of param).
Thoughts ?
Best regards,
houzj
Attachments:
0001-limit-the-fdw-batch-size.patchapplication/octet-stream; name=0001-limit-the-fdw-batch-size.patchDownload
From 6f8752f0940d48e8a4d3aacaaf04efbc514d17e9 Mon Sep 17 00:00:00 2001
From: "houzj.fnst" <houzj.fnst@cn.fujitsu.com>
Date: Fri, 21 May 2021 10:11:28 +0800
Subject: [PATCH] limit the fdw batch size
---
contrib/postgres_fdw/postgres_fdw.c | 10 ++++++++++
src/interfaces/libpq/fe-exec.c | 6 +++---
src/interfaces/libpq/libpq-fe.h | 1 +
3 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index c48a421e88..68e9028ec1 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -1994,6 +1994,16 @@ postgresGetForeignModifyBatchSize(ResultRelInfo *resultRelInfo)
resultRelInfo->ri_TrigDesc->trig_insert_after_row))
return 1;
+ /*
+ * The maximum number of parameters supported by the FE/BE protocol is
+ * 65535, so set the batch_size to not exceed limit in a batch insert.
+ */
+ Assert(fmstate->p_nums > 0);
+ if (batch_size * fmstate->p_nums > PQ_MAX_PARAM_NUMBER)
+ {
+ batch_size = PQ_MAX_PARAM_NUMBER / fmstate->p_nums;
+ }
+
/* Otherwise use the batch size specified for server/table. */
return batch_size;
}
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 03592bdce9..90bccdeb6d 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -1403,7 +1403,7 @@ PQsendQueryParams(PGconn *conn,
libpq_gettext("command string is a null pointer\n"));
return 0;
}
- if (nParams < 0 || nParams > 65535)
+ if (nParams < 0 || nParams > PQ_MAX_PARAM_NUMBER)
{
appendPQExpBufferStr(&conn->errorMessage,
libpq_gettext("number of parameters must be between 0 and 65535\n"));
@@ -1451,7 +1451,7 @@ PQsendPrepare(PGconn *conn,
libpq_gettext("command string is a null pointer\n"));
return 0;
}
- if (nParams < 0 || nParams > 65535)
+ if (nParams < 0 || nParams > PQ_MAX_PARAM_NUMBER)
{
appendPQExpBufferStr(&conn->errorMessage,
libpq_gettext("number of parameters must be between 0 and 65535\n"));
@@ -1548,7 +1548,7 @@ PQsendQueryPrepared(PGconn *conn,
libpq_gettext("statement name is a null pointer\n"));
return 0;
}
- if (nParams < 0 || nParams > 65535)
+ if (nParams < 0 || nParams > PQ_MAX_PARAM_NUMBER)
{
appendPQExpBufferStr(&conn->errorMessage,
libpq_gettext("number of parameters must be between 0 and 65535\n"));
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 227adde5a5..ed3b95e365 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -429,6 +429,7 @@ extern PGresult *PQexecPrepared(PGconn *conn,
int resultFormat);
/* Interface for multiple-result or asynchronous queries */
+#define PQ_MAX_PARAM_NUMBER 65535
extern int PQsendQuery(PGconn *conn, const char *query);
extern int PQsendQueryParams(PGconn *conn,
const char *command,
--
2.18.4
0002-doc-note.patchapplication/octet-stream; name=0002-doc-note.patchDownload
From 4800030f022c355a7a2de234780e0fbf09b12549 Mon Sep 17 00:00:00 2001
From: houzj <houzj.fnst@fujitsu.com>
Date: Fri, 21 May 2021 10:42:12 +0800
Subject: [PATCH] doc note
---
doc/src/sgml/postgres-fdw.sgml | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index fb87372..b7bcfb0 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -369,7 +369,9 @@ OPTIONS (ADD password_required 'false');
This option specifies the number of rows <filename>postgres_fdw</filename>
should insert in each insert operation. It can be specified for a
foreign table or a foreign server. The option specified on a table
- overrides an option specified for the server.
+ overrides an option specified for the server. Note if the batch size
+ exceed the protocol limit (number of columns * batch_size > 65535),
+ then the actual batch size will be less than the specified batch_size.
The default is <literal>1</literal>.
</para>
</listitem>
--
2.7.2.windows.1
On Fri, May 21, 2021 at 8:18 AM houzj.fnst@fujitsu.com
<houzj.fnst@fujitsu.com> wrote:
Hi,
When reading the code, I noticed some possible issue about fdw batch insert.
When I set batch_size > 65535 and insert more than 65535 rows into foreign table,
It will throw an error:For example:
------------------
CREATE FOREIGN TABLE vzdalena_tabulka2(a int, b varchar)
SERVER omega_db
OPTIONS (table_name 'tabulka', batch_size '65536');INSERT INTO vzdalena_tabulka2 SELECT i, 'AHOJ' || i FROM generate_series(1,65536) g(i);
ERROR: number of parameters must be between 0 and 65535
CONTEXT: remote SQL command: INSERT INTO public.tabulka(a, b) VALUES ($1, $2), ($3, $4)...
------------------Actually, I think if the (number of columns) * (number of rows) > 65535, then we can
get this error. But, I think almost no one will set such a large value, so I think adjust the
batch_size automatically when doing INSERT seems an acceptable solution.In the postgresGetForeignModifyBatchSize(), if we found the (num of param) * batch_size
Is greater than the limit(65535), then we set it to 65535 / (num of param).Thoughts ?
+1 to limit batch_size for postgres_fdw and it's a good idea to have a
macro for the max params.
Few comments:
1) How about using macro in the error message, something like below?
appendPQExpBuffer(errorMessage,
libpq_gettext("number of parameters must be
between 0 and %d\n"),
PQ_MAX_PARAM_NUMBER);
2) I think we can choose not mention the 65535 because it's hard to
maintain if that's changed in libpq protocol sometime in future. How
about
"The final number of rows postgres_fdw inserts in a batch actually
depends on the number of columns and the provided batch_size value.
This is because of the limit the libpq protocol (which postgres_fdw
uses to connect to a remote server) has on the number of query
parameters that can be specified per query. For instance, if the
number of columns * batch_size is more than the limit, then the libpq
emits an error. But postgres_fdw adjusts the batch_size to avoid this
error."
instead of
+ overrides an option specified for the server. Note if the batch size
+ exceed the protocol limit (number of columns * batch_size > 65535),
+ then the actual batch size will be less than the specified batch_size.
3) I think "postgres_fdw should insert in each insert operation"
doesn't hold after this patch. We can reword it to "postgres_fdw
inserts in each insert operation".
This option specifies the number of rows
<filename>postgres_fdw</filename>
should insert in each insert operation. It can be specified for a
4) How about naming the macro as PQ_QUERY_PARAM_MAX_LIMIT?
5) We can use macro in code comments as well.
+ * 65535, so set the batch_size to not exceed limit in a batch insert.
6) I think both code and docs patches can be combined into a single patch.
With Regards,
Bharath Rupireddy.
EnterpriseDB: http://www.enterprisedb.com
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Sent: Friday, May 21, 2021 1:42 PM
On Fri, May 21, 2021 at 8:18 AM houzj.fnst@fujitsu.com
<houzj.fnst@fujitsu.com> wrote:Actually, I think if the (number of columns) * (number of rows) >
65535, then we can get this error. But, I think almost no one will set
such a large value, so I think adjust the batch_size automatically when doingINSERT seems an acceptable solution.
In the postgresGetForeignModifyBatchSize(), if we found the (num of
param) * batch_size Is greater than the limit(65535), then we set it to 65535 /(num of param).
Thoughts ?
+1 to limit batch_size for postgres_fdw and it's a good idea to have a
macro for the max params.Few comments:
Thanks for the comments.
1) How about using macro in the error message, something like below?
appendPQExpBuffer(errorMessage,
libpq_gettext("number of parameters must be
between 0 and %d\n"),
PQ_MAX_PARAM_NUMBER);
Changed.
2) I think we can choose not mention the 65535 because it's hard to maintain if that's changed in libpq protocol sometime in future. How about "The final number of rows postgres_fdw inserts in a batch actually depends on the number of columns and the provided batch_size value. This is because of the limit the libpq protocol (which postgres_fdw uses to connect to a remote server) has on the number of query parameters that can be specified per query. For instance, if the number of columns * batch_size is more than the limit, then the libpq emits an error. But postgres_fdw adjusts the batch_size to avoid this error." instead of + overrides an option specified for the server. Note if the batch size + exceed the protocol limit (number of columns * batch_size > 65535), + then the actual batch size will be less than the specified batch_size.
Thanks, your description looks better. Changed.
3) I think "postgres_fdw should insert in each insert operation"
doesn't hold after this patch. We can reword it to "postgres_fdw inserts in
each insert operation".
This option specifies the number of rows
<filename>postgres_fdw</filename>
should insert in each insert operation. It can be specified for a
Changed.
4) How about naming the macro as PQ_QUERY_PARAM_MAX_LIMIT?
Changed.
5) We can use macro in code comments as well.
Thanks, I reorganized the code comments.
+ * 65535, so set the batch_size to not exceed limit in a batch insert.
6) I think both code and docs patches can be combined into a single patch.
Combined.
Attaching V2 patch. Please consider it for further review.
Best regards,
houzj
Attachments:
v2-0001-limit-the-fdw-batch-size.patchapplication/octet-stream; name=v2-0001-limit-the-fdw-batch-size.patchDownload
From bf4fcb14c19d222a32053e7098f53e8aed7229ef Mon Sep 17 00:00:00 2001
From: "houzj.fnst" <houzj.fnst@fujitsu.com>
Date: Fri, 21 May 2021 15:39:07 +0800
Subject: [PATCH] limit-the-fdw-batch-size
---
contrib/postgres_fdw/postgres_fdw.c | 12 +++++++++++-
doc/src/sgml/postgres-fdw.sgml | 11 +++++++++--
src/interfaces/libpq/fe-exec.c | 21 ++++++++++++---------
src/interfaces/libpq/libpq-fe.h | 1 +
4 files changed, 33 insertions(+), 12 deletions(-)
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index c48a421e88..9215c10bf3 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -1994,7 +1994,17 @@ postgresGetForeignModifyBatchSize(ResultRelInfo *resultRelInfo)
resultRelInfo->ri_TrigDesc->trig_insert_after_row))
return 1;
- /* Otherwise use the batch size specified for server/table. */
+ /*
+ * Adjust the batch_size to make sure the number of parameters in a batch
+ * does not exceed maximum number of parameters supported by the FE/BE
+ * protocol.
+ */
+ Assert(fmstate->p_nums > 0);
+ if (batch_size * fmstate->p_nums > PQ_QUERY_PARAM_MAX_LIMIT)
+ {
+ batch_size = PQ_QUERY_PARAM_MAX_LIMIT / fmstate->p_nums;
+ }
+
return batch_size;
}
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index fb87372bde..d77cf1c6d7 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -367,9 +367,16 @@ OPTIONS (ADD password_required 'false');
<listitem>
<para>
This option specifies the number of rows <filename>postgres_fdw</filename>
- should insert in each insert operation. It can be specified for a
+ inserts in each insert operation. It can be specified for a
foreign table or a foreign server. The option specified on a table
- overrides an option specified for the server.
+ overrides an option specified for the server. Note the final number
+ of rows postgres_fdw inserts in a batch actually depends on the
+ number of columns and the provided batch_size value. This is because
+ of the limit the libpq protocol (which postgres_fdw uses to connect
+ to a remote server) has on the number of query parameters that can
+ be specified per query. For instance, if the number of columns * batch_size
+ is more than the limit, then the libpq emits an error. But postgres_fdw
+ adjusts the batch_size to avoid this error.
The default is <literal>1</literal>.
</para>
</listitem>
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 03592bdce9..3876093250 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -1403,10 +1403,11 @@ PQsendQueryParams(PGconn *conn,
libpq_gettext("command string is a null pointer\n"));
return 0;
}
- if (nParams < 0 || nParams > 65535)
+ if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
{
- appendPQExpBufferStr(&conn->errorMessage,
- libpq_gettext("number of parameters must be between 0 and 65535\n"));
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("number of parameters must be between 0 and %d\n"),
+ PQ_QUERY_PARAM_MAX_LIMIT);
return 0;
}
@@ -1451,10 +1452,11 @@ PQsendPrepare(PGconn *conn,
libpq_gettext("command string is a null pointer\n"));
return 0;
}
- if (nParams < 0 || nParams > 65535)
+ if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
{
- appendPQExpBufferStr(&conn->errorMessage,
- libpq_gettext("number of parameters must be between 0 and 65535\n"));
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("number of parameters must be between 0 and %d\n"),
+ PQ_QUERY_PARAM_MAX_LIMIT);
return 0;
}
@@ -1548,10 +1550,11 @@ PQsendQueryPrepared(PGconn *conn,
libpq_gettext("statement name is a null pointer\n"));
return 0;
}
- if (nParams < 0 || nParams > 65535)
+ if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
{
- appendPQExpBufferStr(&conn->errorMessage,
- libpq_gettext("number of parameters must be between 0 and 65535\n"));
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("number of parameters must be between 0 and %d\n"),
+ PQ_QUERY_PARAM_MAX_LIMIT);
return 0;
}
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 227adde5a5..13cf9a9e26 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -429,6 +429,7 @@ extern PGresult *PQexecPrepared(PGconn *conn,
int resultFormat);
/* Interface for multiple-result or asynchronous queries */
+#define PQ_QUERY_PARAM_MAX_LIMIT 65535
extern int PQsendQuery(PGconn *conn, const char *query);
extern int PQsendQueryParams(PGconn *conn,
const char *command,
--
2.18.4
On Fri, May 21, 2021 at 1:19 PM houzj.fnst@fujitsu.com
<houzj.fnst@fujitsu.com> wrote:
Attaching V2 patch. Please consider it for further review.
Thanks for the patch. Some more comments:
1) Can fmstate->p_nums ever be 0 in postgresGetForeignModifyBatchSize?
By any chance, if it can, I think instead of an assert, we can have
something like below, since we are using it in the division.
if (fmstate->p_nums > 0 &&
(batch_size * fmstate->p_nums > PQ_QUERY_PARAM_MAX_LIMIT))
{
batch_size = PQ_QUERY_PARAM_MAX_LIMIT / fmstate->p_nums;
}
Also, please remove the { and } for the above if condition, because
for 1 line statements we don't normally use { and }
2) An empty line after the macro definition will be good.
+#define PQ_QUERY_PARAM_MAX_LIMIT 65535
extern int PQsendQuery(PGconn *conn, const char *query);
3) I think we use:
<filename>postgres_fdw</filename> not postgres_fdw
<literal>batch_size</literal> not batch_size
the number of columns * <literal>batch_size</literal> not the number
of columns * batch_size
+ overrides an option specified for the server. Note the final number
+ of rows postgres_fdw inserts in a batch actually depends on the
+ number of columns and the provided batch_size value. This is because
+ of the limit the libpq protocol (which postgres_fdw uses to connect
+ to a remote server) has on the number of query parameters that can
+ be specified per query. For instance, if the number of columns
* batch_size
+ is more than the limit, then the libpq emits an error. But postgres_fdw
+ adjusts the batch_size to avoid this error.
With Regards,
Bharath Rupireddy.
EnterpriseDB: http://www.enterprisedb.com
On 5/21/21 5:03 AM, Bharath Rupireddy wrote:
On Fri, May 21, 2021 at 1:19 PM houzj.fnst@fujitsu.com
<houzj.fnst@fujitsu.com> wrote:Attaching V2 patch. Please consider it for further review.
Thanks for the patch. Some more comments:
1) Can fmstate->p_nums ever be 0 in postgresGetForeignModifyBatchSize?
By any chance, if it can, I think instead of an assert, we can have
something like below, since we are using it in the division.
if (fmstate->p_nums > 0 &&
(batch_size * fmstate->p_nums > PQ_QUERY_PARAM_MAX_LIMIT))
{
batch_size = PQ_QUERY_PARAM_MAX_LIMIT / fmstate->p_nums;
}
Also, please remove the { and } for the above if condition, because
for 1 line statements we don't normally use { and }
We used to filter that out in pgindent IIRC but we don't any more.
IMNSHO there are cases when it makes the code more readable, especially
if (as here) the condition spans more than one line. I also personally
dislike having one branch of an "if" statement with braces and another
without - it looks far better to my eyes to have all or none with
braces. But I realize that's a matter of taste, and there are plenty of
examples in the code running counter to my taste here.
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Sent: Friday, May 21, 2021 5:03 PM
On Fri, May 21, 2021 at 1:19 PM houzj.fnst@fujitsu.com
<houzj.fnst@fujitsu.com> wrote:Attaching V2 patch. Please consider it for further review.
Thanks for the patch. Some more comments:
1) Can fmstate->p_nums ever be 0 in postgresGetForeignModifyBatchSize? By any chance, if it can, I think instead of an assert, we can have something like below, since we are using it in the division. if (fmstate->p_nums > 0 && (batch_size * fmstate->p_nums > PQ_QUERY_PARAM_MAX_LIMIT)) { batch_size = PQ_QUERY_PARAM_MAX_LIMIT / fmstate->p_nums; } Also, please remove the { and } for the above if condition, because for 1 line statements we don't normally use { and } 2) An empty line after the macro definition will be good. +#define PQ_QUERY_PARAM_MAX_LIMIT 65535 extern int PQsendQuery(PGconn *conn, const char *query); 3) I think we use: <filename>postgres_fdw</filename> not postgres_fdw <literal>batch_size</literal> not batch_size the number of columns * <literal>batch_size</literal> not the number of columns * batch_size + overrides an option specified for the server. Note the final number + of rows postgres_fdw inserts in a batch actually depends on the + number of columns and the provided batch_size value. This is because + of the limit the libpq protocol (which postgres_fdw uses to connect + to a remote server) has on the number of query parameters that can + be specified per query. For instance, if the number of columns * batch_size + is more than the limit, then the libpq emits an error. But postgres_fdw + adjusts the batch_size to avoid this error.
Thanks for the comments. I have addressed all comments to the v3 patch.
BTW, Is the batch_size issue here an Open Item of PG14 ?
Best regards,
houzj
Attachments:
v3-0001-limit-the-fdw-batch-size.patchapplication/octet-stream; name=v3-0001-limit-the-fdw-batch-size.patchDownload
From 711bebfcc0f59d9905492d3d8680c02ca38c23f2 Mon Sep 17 00:00:00 2001
From: houzj <houzj.fnst@cn.fujitsu.com>
Date: Tue, 25 May 2021 15:30:37 +0800
Subject: [PATCH] limit-the-fdw-batch-size
---
contrib/postgres_fdw/postgres_fdw.c | 10 +++++++++-
doc/src/sgml/postgres-fdw.sgml | 13 +++++++++++--
src/interfaces/libpq/fe-exec.c | 21 ++++++++++++---------
src/interfaces/libpq/libpq-fe.h | 2 ++
4 files changed, 34 insertions(+), 12 deletions(-)
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index c48a421..11b50de 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -1994,7 +1994,15 @@ postgresGetForeignModifyBatchSize(ResultRelInfo *resultRelInfo)
resultRelInfo->ri_TrigDesc->trig_insert_after_row))
return 1;
- /* Otherwise use the batch size specified for server/table. */
+ /*
+ * Adjust the batch_size to make sure the number of parameters in a batch
+ * does not exceed maximum number of parameters supported by the FE/BE
+ * protocol.
+ */
+ if (fmstate->p_nums > 0 &&
+ batch_size * fmstate->p_nums > PQ_QUERY_PARAM_MAX_LIMIT)
+ batch_size = PQ_QUERY_PARAM_MAX_LIMIT / fmstate->p_nums;
+
return batch_size;
}
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index fb87372..0be2e69 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -367,9 +367,18 @@ OPTIONS (ADD password_required 'false');
<listitem>
<para>
This option specifies the number of rows <filename>postgres_fdw</filename>
- should insert in each insert operation. It can be specified for a
+ inserts in each insert operation. It can be specified for a
foreign table or a foreign server. The option specified on a table
- overrides an option specified for the server.
+ overrides an option specified for the server. Note the final number
+ of rows <filename>postgres_fdw</filename> inserts in a batch actually
+ depends on the number of columns and the provided <literal>batch_size</literal>
+ value. This is because of the limit the libpq protocol (which
+ <filename>postgres_fdw</filename> uses to connect to a remote server)
+ has on the number of query parameters that can be specified per query.
+ For instance, if the number of columns * <literal>batch_size</literal>
+ is more than the limit, then the libpq emits an error. But
+ <filename>postgres_fdw</filename> adjusts the <literal>batch_size</literal>
+ to avoid this error.
The default is <literal>1</literal>.
</para>
</listitem>
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 03592bd..832d61c 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -1403,10 +1403,11 @@ PQsendQueryParams(PGconn *conn,
libpq_gettext("command string is a null pointer\n"));
return 0;
}
- if (nParams < 0 || nParams > 65535)
+ if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
{
- appendPQExpBufferStr(&conn->errorMessage,
- libpq_gettext("number of parameters must be between 0 and 65535\n"));
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("number of parameters must be between 0 and %d\n"),
+ PQ_QUERY_PARAM_MAX_LIMIT);
return 0;
}
@@ -1451,10 +1452,11 @@ PQsendPrepare(PGconn *conn,
libpq_gettext("command string is a null pointer\n"));
return 0;
}
- if (nParams < 0 || nParams > 65535)
+ if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
{
- appendPQExpBufferStr(&conn->errorMessage,
- libpq_gettext("number of parameters must be between 0 and 65535\n"));
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("number of parameters must be between 0 and %d\n"),
+ PQ_QUERY_PARAM_MAX_LIMIT);
return 0;
}
@@ -1548,10 +1550,11 @@ PQsendQueryPrepared(PGconn *conn,
libpq_gettext("statement name is a null pointer\n"));
return 0;
}
- if (nParams < 0 || nParams > 65535)
+ if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
{
- appendPQExpBufferStr(&conn->errorMessage,
- libpq_gettext("number of parameters must be between 0 and 65535\n"));
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("number of parameters must be between 0 and %d\n"),
+ PQ_QUERY_PARAM_MAX_LIMIT);
return 0;
}
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 227adde..113ab52 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -429,6 +429,8 @@ extern PGresult *PQexecPrepared(PGconn *conn,
int resultFormat);
/* Interface for multiple-result or asynchronous queries */
+#define PQ_QUERY_PARAM_MAX_LIMIT 65535
+
extern int PQsendQuery(PGconn *conn, const char *query);
extern int PQsendQueryParams(PGconn *conn,
const char *command,
--
2.7.2.windows.1
On Tue, May 25, 2021 at 1:08 PM houzj.fnst@fujitsu.com
<houzj.fnst@fujitsu.com> wrote:
Thanks for the comments. I have addressed all comments to the v3 patch.
Thanks! The patch basically looks good to me except that it is missing
a commit message. I think it can be added now.
BTW, Is the batch_size issue here an Open Item of PG14 ?
IMO, the issue you found when setting batch_size to a too high value
is an extreme case testing of the feature added by commit b663a4136
that introduced the batch_size parameter. So, it's a bug to me. I
think you can add it as a bug in the commitfest and let the committers
take the call.
With Regards,
Bharath Rupireddy.
EnterpriseDB: http://www.enterprisedb.com
On Tue, May 25, 2021 at 2:47 PM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:
On Tue, May 25, 2021 at 1:08 PM houzj.fnst@fujitsu.com
<houzj.fnst@fujitsu.com> wrote:Thanks for the comments. I have addressed all comments to the v3 patch.
Thanks! The patch basically looks good to me except that it is missing
a commit message. I think it can be added now.
With v3 patch, I observed failure in postgres_fdw test cases with
insert query in prepared statements. Root cause is that in
postgresGetForeignModifyBatchSize, fmstate can be null (see the
existing code which handles for fmstate null cases). I fixed this, and
added a commit message. PSA v4 patch.
With Regards,
Bharath Rupireddy.
EnterpriseDB: http://www.enterprisedb.com
Attachments:
v4-0001-Adjust-batch_size-to-not-exceed-max-param-limit-o.patchapplication/octet-stream; name=v4-0001-Adjust-batch_size-to-not-exceed-max-param-limit-o.patchDownload
From 419bb78070ffa7306bdb19f55df0128cefe226a1 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddy@enterprisedb.com>
Date: Wed, 26 May 2021 12:18:27 +0530
Subject: [PATCH v4] Adjust batch_size to not exceed max param limit of libpq
In postgres_fdw, when batch_size is set too high (> 65535) and more
than 65535 rows are inserted into the foreign table, then the
insert query fails. This is because of the libpq protocol maximum
limit (65535) on number of query parameters.
Instead of failing in this scenario, it is better to adjust the
batch_size so that the number of parameters in a batch
(number of columns * batch_size) does not exceed maximum number
of parameters (65535) supported by the FE/BE protocol.
While on this, change the maximum number of parameters limit of
libpq to a macro instead of hard coded value.
Author: Hou Zhijie
Reviewed-by: Bharath Rupireddy
---
contrib/postgres_fdw/postgres_fdw.c | 10 +++++++++-
doc/src/sgml/postgres-fdw.sgml | 13 +++++++++++--
src/interfaces/libpq/fe-exec.c | 21 ++++++++++++---------
src/interfaces/libpq/libpq-fe.h | 2 ++
4 files changed, 34 insertions(+), 12 deletions(-)
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index c48a421e88..59fab04ac0 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -1994,7 +1994,15 @@ postgresGetForeignModifyBatchSize(ResultRelInfo *resultRelInfo)
resultRelInfo->ri_TrigDesc->trig_insert_after_row))
return 1;
- /* Otherwise use the batch size specified for server/table. */
+ /*
+ * Adjust the batch_size to make sure the number of parameters in a batch
+ * does not exceed maximum number of parameters supported by the FE/BE
+ * protocol.
+ */
+ if (fmstate && fmstate->p_nums > 0 &&
+ batch_size * fmstate->p_nums > PQ_QUERY_PARAM_MAX_LIMIT)
+ batch_size = PQ_QUERY_PARAM_MAX_LIMIT / fmstate->p_nums;
+
return batch_size;
}
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index fb87372bde..0be2e693a8 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -367,9 +367,18 @@ OPTIONS (ADD password_required 'false');
<listitem>
<para>
This option specifies the number of rows <filename>postgres_fdw</filename>
- should insert in each insert operation. It can be specified for a
+ inserts in each insert operation. It can be specified for a
foreign table or a foreign server. The option specified on a table
- overrides an option specified for the server.
+ overrides an option specified for the server. Note the final number
+ of rows <filename>postgres_fdw</filename> inserts in a batch actually
+ depends on the number of columns and the provided <literal>batch_size</literal>
+ value. This is because of the limit the libpq protocol (which
+ <filename>postgres_fdw</filename> uses to connect to a remote server)
+ has on the number of query parameters that can be specified per query.
+ For instance, if the number of columns * <literal>batch_size</literal>
+ is more than the limit, then the libpq emits an error. But
+ <filename>postgres_fdw</filename> adjusts the <literal>batch_size</literal>
+ to avoid this error.
The default is <literal>1</literal>.
</para>
</listitem>
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 03592bdce9..832d61c544 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -1403,10 +1403,11 @@ PQsendQueryParams(PGconn *conn,
libpq_gettext("command string is a null pointer\n"));
return 0;
}
- if (nParams < 0 || nParams > 65535)
+ if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
{
- appendPQExpBufferStr(&conn->errorMessage,
- libpq_gettext("number of parameters must be between 0 and 65535\n"));
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("number of parameters must be between 0 and %d\n"),
+ PQ_QUERY_PARAM_MAX_LIMIT);
return 0;
}
@@ -1451,10 +1452,11 @@ PQsendPrepare(PGconn *conn,
libpq_gettext("command string is a null pointer\n"));
return 0;
}
- if (nParams < 0 || nParams > 65535)
+ if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
{
- appendPQExpBufferStr(&conn->errorMessage,
- libpq_gettext("number of parameters must be between 0 and 65535\n"));
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("number of parameters must be between 0 and %d\n"),
+ PQ_QUERY_PARAM_MAX_LIMIT);
return 0;
}
@@ -1548,10 +1550,11 @@ PQsendQueryPrepared(PGconn *conn,
libpq_gettext("statement name is a null pointer\n"));
return 0;
}
- if (nParams < 0 || nParams > 65535)
+ if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
{
- appendPQExpBufferStr(&conn->errorMessage,
- libpq_gettext("number of parameters must be between 0 and 65535\n"));
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("number of parameters must be between 0 and %d\n"),
+ PQ_QUERY_PARAM_MAX_LIMIT);
return 0;
}
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 227adde5a5..113ab52ada 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -429,6 +429,8 @@ extern PGresult *PQexecPrepared(PGconn *conn,
int resultFormat);
/* Interface for multiple-result or asynchronous queries */
+#define PQ_QUERY_PARAM_MAX_LIMIT 65535
+
extern int PQsendQuery(PGconn *conn, const char *query);
extern int PQsendQueryParams(PGconn *conn,
const char *command,
--
2.25.1
On 5/26/21 8:57 AM, Bharath Rupireddy wrote:
On Tue, May 25, 2021 at 2:47 PM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:On Tue, May 25, 2021 at 1:08 PM houzj.fnst@fujitsu.com
<houzj.fnst@fujitsu.com> wrote:Thanks for the comments. I have addressed all comments to the v3 patch.
Thanks! The patch basically looks good to me except that it is missing
a commit message. I think it can be added now.With v3 patch, I observed failure in postgres_fdw test cases with
insert query in prepared statements. Root cause is that in
postgresGetForeignModifyBatchSize, fmstate can be null (see the
existing code which handles for fmstate null cases). I fixed this, and
added a commit message. PSA v4 patch.
Thanks. In what situation is the fmstate NULL? If it is NULL, the
current code simply skips the line adjusting it. Doesn't that mean we
may not actually fix the bug in that case?
Also, I think it'd be keep the existing comment, probably as the first
line of the new comment block.
regards
--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Wed, May 26, 2021 at 6:36 PM Tomas Vondra
<tomas.vondra@enterprisedb.com> wrote:
On 5/26/21 8:57 AM, Bharath Rupireddy wrote:
On Tue, May 25, 2021 at 2:47 PM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:On Tue, May 25, 2021 at 1:08 PM houzj.fnst@fujitsu.com
<houzj.fnst@fujitsu.com> wrote:Thanks for the comments. I have addressed all comments to the v3 patch.
Thanks! The patch basically looks good to me except that it is missing
a commit message. I think it can be added now.With v3 patch, I observed failure in postgres_fdw test cases with
insert query in prepared statements. Root cause is that in
postgresGetForeignModifyBatchSize, fmstate can be null (see the
existing code which handles for fmstate null cases). I fixed this, and
added a commit message. PSA v4 patch.Thanks. In what situation is the fmstate NULL? If it is NULL, the
current code simply skips the line adjusting it. Doesn't that mean we
may not actually fix the bug in that case?
fmstate i.e. resultRelInfo->ri_FdwState is NULL for EXPLAIN without
ANALYZE cases, below comment says it and we can't get the bug because
we don't actually execute the insert statement. The bug occurs on the
remote server when the insert query with those many query parameters
is submitted to the remote server. I'm not sure if there are any other
cases where it can be NULL.
/*
* In EXPLAIN without ANALYZE, ri_fdwstate is NULL, so we have to lookup
* the option directly in server/table options. Otherwise just use the
* value we determined earlier.
*/
if (fmstate)
batch_size = fmstate->batch_size;
else
batch_size = get_batch_size_option(resultRelInfo->ri_RelationDesc);
Also, I think it'd be keep the existing comment, probably as the first
line of the new comment block.
Do you mean to say we need to retain "/* Otherwise use the batch size
specified for server/table. */"? If so, PSA v5.
With Regards,
Bharath Rupireddy.
EnterpriseDB: http://www.enterprisedb.com
Attachments:
v5-0001-Adjust-batch_size-to-not-exceed-max-param-limit-o.patchapplication/octet-stream; name=v5-0001-Adjust-batch_size-to-not-exceed-max-param-limit-o.patchDownload
From 9be99b0a18ce34b7538bf53179057b2717686b34 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddy@enterprisedb.com>
Date: Wed, 26 May 2021 19:24:35 +0530
Subject: [PATCH v5] Adjust batch_size to not exceed max param limit of libpq
In postgres_fdw, when batch_size is set too high (> 65535) and more
than 65535 rows are inserted into the foreign table, then the
insert query fails. This is because of the libpq protocol maximum
limit (65535) on number of query parameters.
Instead of failing in this scenario, it is better to adjust the
batch_size so that the number of parameters in a batch
(number of columns * batch_size) does not exceed maximum number
of parameters (65535) supported by the FE/BE protocol.
While on this, change the maximum number of parameters limit of
libpq to a macro instead of hard coded value.
---
contrib/postgres_fdw/postgres_fdw.c | 12 ++++++++++--
doc/src/sgml/postgres-fdw.sgml | 13 +++++++++++--
src/interfaces/libpq/fe-exec.c | 21 ++++++++++++---------
src/interfaces/libpq/libpq-fe.h | 2 ++
4 files changed, 35 insertions(+), 13 deletions(-)
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index c48a421e88..fb82a0070a 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -1979,7 +1979,7 @@ postgresGetForeignModifyBatchSize(ResultRelInfo *resultRelInfo)
Assert(fmstate == NULL || fmstate->aux_fmstate == NULL);
/*
- * In EXPLAIN without ANALYZE, ri_fdwstate is NULL, so we have to lookup
+ * In EXPLAIN without ANALYZE, ri_FdwState is NULL, so we have to lookup
* the option directly in server/table options. Otherwise just use the
* value we determined earlier.
*/
@@ -1994,7 +1994,15 @@ postgresGetForeignModifyBatchSize(ResultRelInfo *resultRelInfo)
resultRelInfo->ri_TrigDesc->trig_insert_after_row))
return 1;
- /* Otherwise use the batch size specified for server/table. */
+ /*
+ * Otherwise use the batch size specified for server/table. If the number
+ * of parameters in a batch exceeds maximum number of parameters supported
+ * by the FE/BE protocol, then adjust the specified batch size accordingly.
+ */
+ if (fmstate && fmstate->p_nums > 0 &&
+ batch_size * fmstate->p_nums > PQ_QUERY_PARAM_MAX_LIMIT)
+ batch_size = PQ_QUERY_PARAM_MAX_LIMIT / fmstate->p_nums;
+
return batch_size;
}
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index fb87372bde..0be2e693a8 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -367,9 +367,18 @@ OPTIONS (ADD password_required 'false');
<listitem>
<para>
This option specifies the number of rows <filename>postgres_fdw</filename>
- should insert in each insert operation. It can be specified for a
+ inserts in each insert operation. It can be specified for a
foreign table or a foreign server. The option specified on a table
- overrides an option specified for the server.
+ overrides an option specified for the server. Note the final number
+ of rows <filename>postgres_fdw</filename> inserts in a batch actually
+ depends on the number of columns and the provided <literal>batch_size</literal>
+ value. This is because of the limit the libpq protocol (which
+ <filename>postgres_fdw</filename> uses to connect to a remote server)
+ has on the number of query parameters that can be specified per query.
+ For instance, if the number of columns * <literal>batch_size</literal>
+ is more than the limit, then the libpq emits an error. But
+ <filename>postgres_fdw</filename> adjusts the <literal>batch_size</literal>
+ to avoid this error.
The default is <literal>1</literal>.
</para>
</listitem>
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 03592bdce9..832d61c544 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -1403,10 +1403,11 @@ PQsendQueryParams(PGconn *conn,
libpq_gettext("command string is a null pointer\n"));
return 0;
}
- if (nParams < 0 || nParams > 65535)
+ if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
{
- appendPQExpBufferStr(&conn->errorMessage,
- libpq_gettext("number of parameters must be between 0 and 65535\n"));
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("number of parameters must be between 0 and %d\n"),
+ PQ_QUERY_PARAM_MAX_LIMIT);
return 0;
}
@@ -1451,10 +1452,11 @@ PQsendPrepare(PGconn *conn,
libpq_gettext("command string is a null pointer\n"));
return 0;
}
- if (nParams < 0 || nParams > 65535)
+ if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
{
- appendPQExpBufferStr(&conn->errorMessage,
- libpq_gettext("number of parameters must be between 0 and 65535\n"));
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("number of parameters must be between 0 and %d\n"),
+ PQ_QUERY_PARAM_MAX_LIMIT);
return 0;
}
@@ -1548,10 +1550,11 @@ PQsendQueryPrepared(PGconn *conn,
libpq_gettext("statement name is a null pointer\n"));
return 0;
}
- if (nParams < 0 || nParams > 65535)
+ if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
{
- appendPQExpBufferStr(&conn->errorMessage,
- libpq_gettext("number of parameters must be between 0 and 65535\n"));
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("number of parameters must be between 0 and %d\n"),
+ PQ_QUERY_PARAM_MAX_LIMIT);
return 0;
}
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 227adde5a5..113ab52ada 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -429,6 +429,8 @@ extern PGresult *PQexecPrepared(PGconn *conn,
int resultFormat);
/* Interface for multiple-result or asynchronous queries */
+#define PQ_QUERY_PARAM_MAX_LIMIT 65535
+
extern int PQsendQuery(PGconn *conn, const char *query);
extern int PQsendQueryParams(PGconn *conn,
const char *command,
--
2.25.1
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Sent: Wednesday, May 26, 2021 9:56 PM
On Wed, May 26, 2021 at 6:36 PM Tomas Vondra
<tomas.vondra@enterprisedb.com> wrote:On 5/26/21 8:57 AM, Bharath Rupireddy wrote:
On Tue, May 25, 2021 at 2:47 PM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:On Tue, May 25, 2021 at 1:08 PM houzj.fnst@fujitsu.com
<houzj.fnst@fujitsu.com> wrote:Thanks for the comments. I have addressed all comments to the v3
patch.
Thanks! The patch basically looks good to me except that it is
missing a commit message. I think it can be added now.With v3 patch, I observed failure in postgres_fdw test cases with
insert query in prepared statements. Root cause is that in
postgresGetForeignModifyBatchSize, fmstate can be null (see the
existing code which handles for fmstate null cases). I fixed this,
and added a commit message. PSA v4 patch.Thanks. In what situation is the fmstate NULL? If it is NULL, the
current code simply skips the line adjusting it. Doesn't that mean we
may not actually fix the bug in that case?fmstate i.e. resultRelInfo->ri_FdwState is NULL for EXPLAIN without ANALYZE
cases, below comment says it and we can't get the bug because we don't
actually execute the insert statement. The bug occurs on the remote server
when the insert query with those many query parameters is submitted to the
remote server.
Agreed.
The "ri_FdwState" is initialized in postgresBeginForeignInsert or postgresBeginForeignModify.
I think the above functions are always invoked before getting the batch_size.
Only in EXPLAIN mode, it will not initialize the ri_FdwState.
/*
* Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState
* stays NULL.
*/
if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
return;
Best regards,
houzj
Hi,
I took at this patch today. I did some minor changes, mostly:
1) change the code limiting batch_size from
if (fmstate->p_nums > 0 &&
(batch_size * fmstate->p_nums > PQ_QUERY_PARAM_MAX_LIMIT))
{
batch_size = PQ_QUERY_PARAM_MAX_LIMIT / fmstate->p_nums;
}
to
if (fmstate && fmstate->p_nums > 0)
batch_size = Min(batch_size,
PQ_QUERY_PARAM_MAX_LIMIT / fmstate->p_nums);
which I think is somewhat clearer / more common patter.
2) I've reworded the docs a bit, splitting the single para into two. I
think this makes it clearer.
Attached is a patch doing this. Please check the commit message etc.
Barring objections I'll get it committed in a couple days.
regards
--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Attachments:
0001-Make-sure-postgres_fdw-batching-does-use-too-many-pa.patchtext/x-patch; charset=UTF-8; name=0001-Make-sure-postgres_fdw-batching-does-use-too-many-pa.patchDownload
From 222806c4eef0fb3a021da3c1d4cc9e1e41bd9ddf Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@2ndquadrant.com>
Date: Sun, 30 May 2021 20:58:38 +0200
Subject: [PATCH] Make sure postgres_fdw batching does use too many parameters
The FE/BE protocol identifies parameters with an Int16 index, which
limits the maximum number of parameters per query to 65535. With
batching added to postges_fdw this limit is much easier to hit, as
the whole batch is essentially a single query, making this error much
easier to hit.
The failures are a bit unpredictable, because it also depends on the
number of columns in the query. So instead of just failing, this patch
tweaks the batch_size to not exceed the maximum number of parameters.
Reported-by: Hou Zhijie <houzj.fnst@cn.fujitsu.com>
Reviewed-by: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Discussion: https://postgr.es/m/OS0PR01MB571603973C0AC2874AD6BF2594299%40OS0PR01MB5716.jpnprd01.prod.outlook.com
---
contrib/postgres_fdw/postgres_fdw.c | 11 +++++++++--
doc/src/sgml/postgres-fdw.sgml | 11 +++++++++++
src/interfaces/libpq/fe-exec.c | 21 ++++++++++++---------
src/interfaces/libpq/libpq-fe.h | 2 ++
4 files changed, 34 insertions(+), 11 deletions(-)
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index c48a421e88..ac86b06b8f 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -1979,7 +1979,7 @@ postgresGetForeignModifyBatchSize(ResultRelInfo *resultRelInfo)
Assert(fmstate == NULL || fmstate->aux_fmstate == NULL);
/*
- * In EXPLAIN without ANALYZE, ri_fdwstate is NULL, so we have to lookup
+ * In EXPLAIN without ANALYZE, ri_FdwState is NULL, so we have to lookup
* the option directly in server/table options. Otherwise just use the
* value we determined earlier.
*/
@@ -1994,7 +1994,14 @@ postgresGetForeignModifyBatchSize(ResultRelInfo *resultRelInfo)
resultRelInfo->ri_TrigDesc->trig_insert_after_row))
return 1;
- /* Otherwise use the batch size specified for server/table. */
+ /*
+ * Otherwise use the batch size specified for server/table. The number of
+ * parameters in a batch is limited to 64k (uint16), so make sure we don't
+ * exceed this limit by using the maximum batch_size possible.
+ */
+ if (fmstate && fmstate->p_nums > 0)
+ batch_size = Min(batch_size, PQ_QUERY_PARAM_MAX_LIMIT / fmstate->p_nums);
+
return batch_size;
}
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index fb87372bde..564651dfaa 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -372,6 +372,17 @@ OPTIONS (ADD password_required 'false');
overrides an option specified for the server.
The default is <literal>1</literal>.
</para>
+
+ <para>
+ Note the actual number of rows <filename>postgres_fdw</filename> inserts at
+ once depends on the number of columns and the provided
+ <literal>batch_size</literal> value. The batch is executed as a single
+ query, and the libpq protocol (which <filename>postgres_fdw</filename>
+ uses to connect to a remote server) limits the number of parameters in a
+ single query to 64k. When the number of columns * <literal>batch_size</literal>
+ exceeds the limit, the <literal>batch_size</literal> will be adjusted to
+ avoid an error.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 03592bdce9..832d61c544 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -1403,10 +1403,11 @@ PQsendQueryParams(PGconn *conn,
libpq_gettext("command string is a null pointer\n"));
return 0;
}
- if (nParams < 0 || nParams > 65535)
+ if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
{
- appendPQExpBufferStr(&conn->errorMessage,
- libpq_gettext("number of parameters must be between 0 and 65535\n"));
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("number of parameters must be between 0 and %d\n"),
+ PQ_QUERY_PARAM_MAX_LIMIT);
return 0;
}
@@ -1451,10 +1452,11 @@ PQsendPrepare(PGconn *conn,
libpq_gettext("command string is a null pointer\n"));
return 0;
}
- if (nParams < 0 || nParams > 65535)
+ if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
{
- appendPQExpBufferStr(&conn->errorMessage,
- libpq_gettext("number of parameters must be between 0 and 65535\n"));
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("number of parameters must be between 0 and %d\n"),
+ PQ_QUERY_PARAM_MAX_LIMIT);
return 0;
}
@@ -1548,10 +1550,11 @@ PQsendQueryPrepared(PGconn *conn,
libpq_gettext("statement name is a null pointer\n"));
return 0;
}
- if (nParams < 0 || nParams > 65535)
+ if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
{
- appendPQExpBufferStr(&conn->errorMessage,
- libpq_gettext("number of parameters must be between 0 and 65535\n"));
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("number of parameters must be between 0 and %d\n"),
+ PQ_QUERY_PARAM_MAX_LIMIT);
return 0;
}
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 227adde5a5..113ab52ada 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -429,6 +429,8 @@ extern PGresult *PQexecPrepared(PGconn *conn,
int resultFormat);
/* Interface for multiple-result or asynchronous queries */
+#define PQ_QUERY_PARAM_MAX_LIMIT 65535
+
extern int PQsendQuery(PGconn *conn, const char *query);
extern int PQsendQueryParams(PGconn *conn,
const char *command,
--
2.31.1
On Mon, May 31, 2021 at 1:21 AM Tomas Vondra
<tomas.vondra@enterprisedb.com> wrote:
Hi,
I took at this patch today. I did some minor changes, mostly:
1) change the code limiting batch_size from
if (fmstate->p_nums > 0 &&
(batch_size * fmstate->p_nums > PQ_QUERY_PARAM_MAX_LIMIT))
{
batch_size = PQ_QUERY_PARAM_MAX_LIMIT / fmstate->p_nums;
}to
if (fmstate && fmstate->p_nums > 0)
batch_size = Min(batch_size,
PQ_QUERY_PARAM_MAX_LIMIT / fmstate->p_nums);which I think is somewhat clearer / more common patter.
Agree, that looks pretty good.
2) I've reworded the docs a bit, splitting the single para into two. I
think this makes it clearer.
LGTM, except one thing that the batch_size description says "should
insert in", but it's not that the value entered for batch_size is
always honoured right? Because this patch might it.
This option specifies the number of rows
<filename>postgres_fdw</filename>
should insert in each insert operation. It can be specified for a
So, I suggest to remove "should" and change it to:
This option specifies the number of rows
<filename>postgres_fdw</filename>
inserts in each insert operation. It can be specified for a
Attached is a patch doing this. Please check the commit message etc.
Barring objections I'll get it committed in a couple days.
One minor comment:
In the commit message, Int16 is used
The FE/BE protocol identifies parameters with an Int16 index, which
limits the maximum number of parameters per query to 65535. With
and in the code comments uint16 is used.
+ * parameters in a batch is limited to 64k (uint16), so make sure we don't
Isn't it uint16 in the commit message too? Also, can we use 64k in the
commit message instead of 65535?
With Regards,
Bharath Rupireddy.
On 5/31/21 6:01 AM, Bharath Rupireddy wrote:
On Mon, May 31, 2021 at 1:21 AM Tomas Vondra
<tomas.vondra@enterprisedb.com> wrote:Hi,
I took at this patch today. I did some minor changes, mostly:
1) change the code limiting batch_size from
if (fmstate->p_nums > 0 &&
(batch_size * fmstate->p_nums > PQ_QUERY_PARAM_MAX_LIMIT))
{
batch_size = PQ_QUERY_PARAM_MAX_LIMIT / fmstate->p_nums;
}to
if (fmstate && fmstate->p_nums > 0)
batch_size = Min(batch_size,
PQ_QUERY_PARAM_MAX_LIMIT / fmstate->p_nums);which I think is somewhat clearer / more common patter.
Agree, that looks pretty good.
2) I've reworded the docs a bit, splitting the single para into two. I
think this makes it clearer.LGTM, except one thing that the batch_size description says "should
insert in", but it's not that the value entered for batch_size is
always honoured right? Because this patch might it.This option specifies the number of rows
<filename>postgres_fdw</filename>
should insert in each insert operation. It can be specified for aSo, I suggest to remove "should" and change it to:
This option specifies the number of rows
<filename>postgres_fdw</filename>
inserts in each insert operation. It can be specified for a
I think the "should" indicates exactly that postgres_fdw may adjust the
batch size. Without it it sounds much more definitive, so I kept it.
Attached is a patch doing this. Please check the commit message etc.
Barring objections I'll get it committed in a couple days.One minor comment:
In the commit message, Int16 is used
The FE/BE protocol identifies parameters with an Int16 index, which
limits the maximum number of parameters per query to 65535. Withand in the code comments uint16 is used.
+ * parameters in a batch is limited to 64k (uint16), so make sure we don'tIsn't it uint16 in the commit message too? Also, can we use 64k in the
commit message instead of 65535?
No, the "Int16" refers to the FE/BE documentation, where we use Int16.
But in the C code we interpret it as uint16.
I've added a simple regression test to postgres_fdw, testing that batch
sizes > 65535 work fine, and pushed the fix.
I've considered checking the value in postgres_fdw_validator and just
rejecting anything over 65535, but I've decided against that. We'd still
need to adjust depending on number of columns anyway.
regards
--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Wed, Jun 9, 2021 at 12:04 AM Tomas Vondra
<tomas.vondra@enterprisedb.com> wrote:
No, the "Int16" refers to the FE/BE documentation, where we use Int16.
But in the C code we interpret it as uint16.
Hm. I see that in protocol.sgml Int16 is being used.
I've added a simple regression test to postgres_fdw, testing that batch
sizes > 65535 work fine, and pushed the fix.
I was earlier thinking of adding one, but stopped because it might
increase the regression test execution time. It looks like that's true
- with and without the test case it takes 17 sec and 4 sec
respectively on my dev system which is 4X slower. I'm not sure if this
is okay.
With Regards,
Bharath Rupireddy.
Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> writes:
I've added a simple regression test to postgres_fdw, testing that batch
sizes > 65535 work fine, and pushed the fix.
I was earlier thinking of adding one, but stopped because it might
increase the regression test execution time. It looks like that's true
- with and without the test case it takes 17 sec and 4 sec
respectively on my dev system which is 4X slower. I'm not sure if this
is okay.
The cost, versus the odds of ever detecting a problem, doesn't
seem like a good tradeoff.
regards, tom lane
I wrote:
Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> writes:
I've added a simple regression test to postgres_fdw, testing that batch
sizes > 65535 work fine, and pushed the fix.
I was earlier thinking of adding one, but stopped because it might
increase the regression test execution time. It looks like that's true
- with and without the test case it takes 17 sec and 4 sec
respectively on my dev system which is 4X slower. I'm not sure if this
is okay.
The cost, versus the odds of ever detecting a problem, doesn't
seem like a good tradeoff.
I took a quick look and noted that on buildfarm member longfin
(to take a random example that's sitting a few feet from me),
the time for contrib-install-check went from 34 seconds before
this patch to 40 seconds after. I find that completely
unacceptable compared to the likely value of this test case.
regards, tom lane
On 6/9/21 8:28 AM, Tom Lane wrote:
I wrote:
Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> writes:
I've added a simple regression test to postgres_fdw, testing that batch
sizes > 65535 work fine, and pushed the fix.I was earlier thinking of adding one, but stopped because it might
increase the regression test execution time. It looks like that's true
- with and without the test case it takes 17 sec and 4 sec
respectively on my dev system which is 4X slower. I'm not sure if this
is okay.The cost, versus the odds of ever detecting a problem, doesn't
seem like a good tradeoff.I took a quick look and noted that on buildfarm member longfin
(to take a random example that's sitting a few feet from me),
the time for contrib-install-check went from 34 seconds before
this patch to 40 seconds after. I find that completely
unacceptable compared to the likely value of this test case.
Note that the problem here is [1] - we're creating a lot of slots
referencing the same tuple descriptor, which inflates the duration.
There's a fix in the other thread, which eliminates ~99% of the
overhead. I plan to push that fix soon (a day or two).
regards
--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On 6/9/21 12:22 PM, Tomas Vondra wrote:
On 6/9/21 8:28 AM, Tom Lane wrote:
I wrote:
Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> writes:
I've added a simple regression test to postgres_fdw, testing that
batch
sizes > 65535 work fine, and pushed the fix.I was earlier thinking of adding one, but stopped because it might
increase the regression test execution time. It looks like that's true
- with and without the test case it takes 17 sec and 4 sec
respectively on my dev system which is 4X slower. I'm not sure if this
is okay.The cost, versus the odds of ever detecting a problem, doesn't
seem like a good tradeoff.I took a quick look and noted that on buildfarm member longfin
(to take a random example that's sitting a few feet from me),
the time for contrib-install-check went from 34 seconds before
this patch to 40 seconds after. I find that completely
unacceptable compared to the likely value of this test case.Note that the problem here is [1] - we're creating a lot of slots
referencing the same tuple descriptor, which inflates the duration.
There's a fix in the other thread, which eliminates ~99% of the
overhead. I plan to push that fix soon (a day or two).
Forgot to add the link:
[1]: /messages/by-id/ebbbcc7d-4286-8c28-0272-61b4753af761@enterprisedb.com
/messages/by-id/ebbbcc7d-4286-8c28-0272-61b4753af761@enterprisedb.com
regards
--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Tomas Vondra <tomas.vondra@enterprisedb.com> writes:
Note that the problem here is [1] - we're creating a lot of slots
referencing the same tuple descriptor, which inflates the duration.
There's a fix in the other thread, which eliminates ~99% of the
overhead. I plan to push that fix soon (a day or two).
Oh, okay, as long as there's a plan to bring the time back down.
regards, tom lane
On 6/9/21 3:28 PM, Tom Lane wrote:
Tomas Vondra <tomas.vondra@enterprisedb.com> writes:
Note that the problem here is [1] - we're creating a lot of slots
referencing the same tuple descriptor, which inflates the duration.
There's a fix in the other thread, which eliminates ~99% of the
overhead. I plan to push that fix soon (a day or two).Oh, okay, as long as there's a plan to bring the time back down.
Yeah. Sorry for not mentioning this in the original message about the
new regression test.
regards
--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On 6/9/21 4:05 PM, Tomas Vondra wrote:
On 6/9/21 3:28 PM, Tom Lane wrote:
Tomas Vondra <tomas.vondra@enterprisedb.com> writes:
Note that the problem here is [1] - we're creating a lot of slots
referencing the same tuple descriptor, which inflates the duration.
There's a fix in the other thread, which eliminates ~99% of the
overhead. I plan to push that fix soon (a day or two).Oh, okay, as long as there's a plan to bring the time back down.
Yeah. Sorry for not mentioning this in the original message about the
new regression test.
I've pushed a fix addressing the performance issue.
There's one caveat, though - for regular builds the slowdown is pretty
much eliminated. But with valgrind it's still considerably slower. For
postgres_fdw the "make check" used to take ~5 minutes for me, now it
takes >1h. And yes, this is entirely due to the new test case which is
generating / inserting 70k rows. So maybe the test case is not worth it
after all, and we should get rid of it.
regards
--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Tomas Vondra <tomas.vondra@enterprisedb.com> writes:
There's one caveat, though - for regular builds the slowdown is pretty
much eliminated. But with valgrind it's still considerably slower. For
postgres_fdw the "make check" used to take ~5 minutes for me, now it
takes >1h. And yes, this is entirely due to the new test case which is
generating / inserting 70k rows. So maybe the test case is not worth it
after all, and we should get rid of it.
I bet the CLOBBER_CACHE animals won't like it much either.
I suggest what we do is leave it in place for long enough to get
a round of reports from those slow animals, and then (assuming
those reports are positive) drop the test.
regards, tom lane
On 2021-Jun-12, Tomas Vondra wrote:
There's one caveat, though - for regular builds the slowdown is pretty
much eliminated. But with valgrind it's still considerably slower. For
postgres_fdw the "make check" used to take ~5 minutes for me, now it
takes >1h. And yes, this is entirely due to the new test case which is
generating / inserting 70k rows. So maybe the test case is not worth it
after all, and we should get rid of it.
Hmm, what if the table is made 1600 columns wide -- would inserting 41
rows be sufficient to trigger the problem case? If it does, maybe it
would reduce the runtime for valgrind/cache-clobber animals enough that
it's no longer a concern.
--
�lvaro Herrera Valdivia, Chile
"At least to kernel hackers, who really are human, despite occasional
rumors to the contrary" (LWN.net)
On 6/13/21 2:40 AM, Alvaro Herrera wrote:
On 2021-Jun-12, Tomas Vondra wrote:
There's one caveat, though - for regular builds the slowdown is pretty
much eliminated. But with valgrind it's still considerably slower. For
postgres_fdw the "make check" used to take ~5 minutes for me, now it
takes >1h. And yes, this is entirely due to the new test case which is
generating / inserting 70k rows. So maybe the test case is not worth it
after all, and we should get rid of it.Hmm, what if the table is made 1600 columns wide -- would inserting 41
rows be sufficient to trigger the problem case? If it does, maybe it
would reduce the runtime for valgrind/cache-clobber animals enough that
it's no longer a concern.
Good idea. I gave that a try, creating a table with 1500 columns and
inserting 50 rows (so 75k parameters). See the attached patch.
While this cuts the runtime about in half (to ~30 minutes on my laptop),
that's probably not enough - it's still about ~6x longer than it used to
take. All these timings are with valgrind.
regards
--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Attachments:
postgres_fdw_batch_test.patchtext/x-patch; charset=UTF-8; name=postgres_fdw_batch_test.patchDownload
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 8cb2148f1f..0c6bc240b7 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -3026,13 +3026,6 @@ SELECT COUNT(*) FROM ftable;
TRUNCATE batch_table;
DROP FOREIGN TABLE ftable;
--- try if large batches exceed max number of bind parameters
-CREATE FOREIGN TABLE ftable ( x int ) SERVER loopback OPTIONS ( table_name 'batch_table', batch_size '100000' );
-INSERT INTO ftable SELECT * FROM generate_series(1, 70000) i;
-SELECT COUNT(*) FROM ftable;
-TRUNCATE batch_table;
-DROP FOREIGN TABLE ftable;
-
-- Disable batch insert
CREATE FOREIGN TABLE ftable ( x int ) SERVER loopback OPTIONS ( table_name 'batch_table', batch_size '1' );
EXPLAIN (VERBOSE, COSTS OFF) INSERT INTO ftable VALUES (1), (2);
@@ -3041,6 +3034,46 @@ SELECT COUNT(*) FROM ftable;
DROP FOREIGN TABLE ftable;
DROP TABLE batch_table;
+
+CREATE OR REPLACE FUNCTION create_batch_tables(p_cnt int) RETURNS void AS $$
+DECLARE
+ v_sql_local text := '';
+ v_sql_foreign text := '';
+ v_i int;
+BEGIN
+
+ v_sql_local := 'CREATE TABLE batch_table (';
+ v_sql_foreign := 'CREATE FOREIGN TABLE ftable (';
+
+ FOR v_i IN 1 .. p_cnt LOOP
+
+ IF v_i > 1 THEN
+ v_sql_local := v_sql_local || ', col_' || v_i || ' int';
+ v_sql_foreign := v_sql_foreign || ', col_' || v_i || ' int';
+ ELSE
+ v_sql_local := v_sql_local || 'col_' || v_i || ' int';
+ v_sql_foreign := v_sql_foreign || 'col_' || v_i || ' int';
+ END IF;
+
+ END LOOP;
+
+ v_sql_local := v_sql_local || ')';
+ v_sql_foreign := v_sql_foreign || ') SERVER loopback OPTIONS (table_name ''batch_table'', batch_size ''100000'' )';
+
+ EXECUTE v_sql_local;
+ EXECUTE v_sql_foreign;
+
+END;
+$$ LANGUAGE plpgsql;
+
+-- try if large batches exceed max number of bind parameters
+SELECT create_batch_tables(1500);
+INSERT INTO ftable SELECT * FROM generate_series(1, 50) i;
+SELECT COUNT(*) FROM ftable;
+DROP TABLE batch_table;
+DROP FOREIGN TABLE ftable;
+DROP FUNCTION create_batch_tables(int);
+
-- Use partitioning
CREATE TABLE batch_table ( x int ) PARTITION BY HASH (x);
On Sun, Jun 13, 2021 at 6:10 AM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:
On 2021-Jun-12, Tomas Vondra wrote:
There's one caveat, though - for regular builds the slowdown is pretty
much eliminated. But with valgrind it's still considerably slower. For
postgres_fdw the "make check" used to take ~5 minutes for me, now it
takes >1h. And yes, this is entirely due to the new test case which is
generating / inserting 70k rows. So maybe the test case is not worth it
after all, and we should get rid of it.Hmm, what if the table is made 1600 columns wide -- would inserting 41
rows be sufficient to trigger the problem case? If it does, maybe it
would reduce the runtime for valgrind/cache-clobber animals enough that
it's no longer a concern.
Yeah, that's a good idea. PSA patch that creates the table of 1600
columns and inserts 41 rows into the foreign table. If the batch_size
adjustment fix isn't there, we will hit the error. On my dev system,
postgres_fdw contrib regression tests execution time: with and without
the attached patch 4.5 sec and 5.7 sec respectively.
On Sun, Jun 13, 2021 at 7:25 PM Tomas Vondra
<tomas.vondra@enterprisedb.com> wrote:
Good idea. I gave that a try, creating a table with 1500 columns and
inserting 50 rows (so 75k parameters). See the attached patch.
Thanks for the patch. I also prepared a patch, just sharing. I'm okay
if it's ignored.
With Regards,
Bharath Rupireddy.
Attachments:
v1-0001-enhance-batch-insert-test-case.patchapplication/x-patch; name=v1-0001-enhance-batch-insert-test-case.patchDownload
From c97cc8385a8e0ee5e5b0f3b4532a7107b1a183f6 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Sun, 13 Jun 2021 02:07:20 -0700
Subject: [PATCH v1] enhance batch insert test case
---
.../postgres_fdw/expected/postgres_fdw.out | 23 ++++++++++++++-----
contrib/postgres_fdw/sql/postgres_fdw.sql | 21 +++++++++++++----
2 files changed, 33 insertions(+), 11 deletions(-)
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 1fb26639fc..cb8a8c8d5c 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -9683,16 +9683,27 @@ SELECT COUNT(*) FROM ftable;
TRUNCATE batch_table;
DROP FOREIGN TABLE ftable;
-- try if large batches exceed max number of bind parameters
-CREATE FOREIGN TABLE ftable ( x int ) SERVER loopback OPTIONS ( table_name 'batch_table', batch_size '100000' );
-INSERT INTO ftable SELECT * FROM generate_series(1, 70000) i;
-SELECT COUNT(*) FROM ftable;
+DO LANGUAGE plpgsql
+$$
+DECLARE
+ l_tbl text := 'CREATE TABLE local_tbl_b('|| string_agg('c' || i || ' int', ',') || ');'
+ FROM generate_series(1,1600) As i;
+ f_tbl text := 'CREATE FOREIGN TABLE foreign_tbl_b(' || string_agg('c' || i || ' int', ',') || E') SERVER loopback OPTIONS (table_name \'local_tbl_b\', batch_size \'100000\');'
+ FROM generate_series(1,1600) As i;
+BEGIN
+ EXECUTE l_tbl;
+ EXECUTE f_tbl;
+END;
+$$;
+INSERT INTO foreign_tbl_b SELECT * FROM generate_series(1, 41) i;
+SELECT COUNT(*) FROM foreign_tbl_b;
count
-------
- 70000
+ 41
(1 row)
-TRUNCATE batch_table;
-DROP FOREIGN TABLE ftable;
+DROP FOREIGN TABLE foreign_tbl_b;
+DROP TABLE local_tbl_b;
-- Disable batch insert
CREATE FOREIGN TABLE ftable ( x int ) SERVER loopback OPTIONS ( table_name 'batch_table', batch_size '1' );
EXPLAIN (VERBOSE, COSTS OFF) INSERT INTO ftable VALUES (1), (2);
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 8cb2148f1f..1be80e07b9 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -3027,11 +3027,22 @@ TRUNCATE batch_table;
DROP FOREIGN TABLE ftable;
-- try if large batches exceed max number of bind parameters
-CREATE FOREIGN TABLE ftable ( x int ) SERVER loopback OPTIONS ( table_name 'batch_table', batch_size '100000' );
-INSERT INTO ftable SELECT * FROM generate_series(1, 70000) i;
-SELECT COUNT(*) FROM ftable;
-TRUNCATE batch_table;
-DROP FOREIGN TABLE ftable;
+DO LANGUAGE plpgsql
+$$
+DECLARE
+ l_tbl text := 'CREATE TABLE local_tbl_b('|| string_agg('c' || i || ' int', ',') || ');'
+ FROM generate_series(1,1600) As i;
+ f_tbl text := 'CREATE FOREIGN TABLE foreign_tbl_b(' || string_agg('c' || i || ' int', ',') || E') SERVER loopback OPTIONS (table_name \'local_tbl_b\', batch_size \'100000\');'
+ FROM generate_series(1,1600) As i;
+BEGIN
+ EXECUTE l_tbl;
+ EXECUTE f_tbl;
+END;
+$$;
+INSERT INTO foreign_tbl_b SELECT * FROM generate_series(1, 41) i;
+SELECT COUNT(*) FROM foreign_tbl_b;
+DROP FOREIGN TABLE foreign_tbl_b;
+DROP TABLE local_tbl_b;
-- Disable batch insert
CREATE FOREIGN TABLE ftable ( x int ) SERVER loopback OPTIONS ( table_name 'batch_table', batch_size '1' );
--
2.25.1
On 6/13/21 5:25 PM, Bharath Rupireddy wrote:
On Sun, Jun 13, 2021 at 6:10 AM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:
On 2021-Jun-12, Tomas Vondra wrote:
There's one caveat, though - for regular builds the slowdown is pretty
much eliminated. But with valgrind it's still considerably slower. For
postgres_fdw the "make check" used to take ~5 minutes for me, now it
takes >1h. And yes, this is entirely due to the new test case which is
generating / inserting 70k rows. So maybe the test case is not worth it
after all, and we should get rid of it.Hmm, what if the table is made 1600 columns wide -- would inserting 41
rows be sufficient to trigger the problem case? If it does, maybe it
would reduce the runtime for valgrind/cache-clobber animals enough that
it's no longer a concern.Yeah, that's a good idea. PSA patch that creates the table of 1600
columns and inserts 41 rows into the foreign table. If the batch_size
adjustment fix isn't there, we will hit the error. On my dev system,
postgres_fdw contrib regression tests execution time: with and without
the attached patch 4.5 sec and 5.7 sec respectively.
But we're discussing cases with valgrind and/or CLOBBER_CACHE_ALWAYS.
regards
--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Sun, Jun 13, 2021 at 9:28 PM Tomas Vondra
<tomas.vondra@enterprisedb.com> wrote:
On 6/13/21 5:25 PM, Bharath Rupireddy wrote:
On Sun, Jun 13, 2021 at 6:10 AM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:
On 2021-Jun-12, Tomas Vondra wrote:
There's one caveat, though - for regular builds the slowdown is pretty
much eliminated. But with valgrind it's still considerably slower. For
postgres_fdw the "make check" used to take ~5 minutes for me, now it
takes >1h. And yes, this is entirely due to the new test case which is
generating / inserting 70k rows. So maybe the test case is not worth it
after all, and we should get rid of it.Hmm, what if the table is made 1600 columns wide -- would inserting 41
rows be sufficient to trigger the problem case? If it does, maybe it
would reduce the runtime for valgrind/cache-clobber animals enough that
it's no longer a concern.Yeah, that's a good idea. PSA patch that creates the table of 1600
columns and inserts 41 rows into the foreign table. If the batch_size
adjustment fix isn't there, we will hit the error. On my dev system,
postgres_fdw contrib regression tests execution time: with and without
the attached patch 4.5 sec and 5.7 sec respectively.But we're discussing cases with valgrind and/or CLOBBER_CACHE_ALWAYS.
Okay. Here are the readings on my dev system:
1) on master with the existing test case with inserting 70K rows:
4263200 ms (71.05 min)
2) with Tomas's patch with the test case modified with 1500 table
columns and 50 rows, (majority of the time ~30min it took in SELECT
create_batch_tables(1500); statement. I measured this time manually
looking at the start and end time of the statement - 6649312 ms (110.8
min)
3) with my patch with test case modified with 1600 table columns and
41 rows: 4003007 ms (66.71 min)
4) on master without the test case at all: 3770722 ms (62.84 min)
With Regards,
Bharath Rupireddy.
On Mon, Jun 14, 2021, 5:33 PM Bharath Rupireddy <
bharath.rupireddyforpostgres@gmail.com> wrote:
Okay. Here are the readings on my dev system:
1) on master with the existing test case with inserting 70K rows:
4263200 ms (71.05 min)
2) with Tomas's patch with the test case modified with 1500 table
columns and 50 rows, (majority of the time ~30min it took in SELECT
create_batch_tables(1500); statement. I measured this time manually
looking at the start and end time of the statement - 6649312 ms (110.8
min)
3) with my patch with test case modified with 1600 table columns and
41 rows: 4003007 ms (66.71 min)
4) on master without the test case at all: 3770722 ms (62.84 min)
I forgot to mention one thing: I ran the above tests with
CLOBBER_CACHE_ALWAYS.
Regards,
Bharath Rupireddy.
Show quoted text
Hi,
On 2021-06-11 18:44:28 -0400, Tom Lane wrote:
Tomas Vondra <tomas.vondra@enterprisedb.com> writes:
There's one caveat, though - for regular builds the slowdown is pretty
much eliminated. But with valgrind it's still considerably slower. For
postgres_fdw the "make check" used to take ~5 minutes for me, now it
takes >1h. And yes, this is entirely due to the new test case which is
generating / inserting 70k rows. So maybe the test case is not worth it
after all, and we should get rid of it.I bet the CLOBBER_CACHE animals won't like it much either.
I suggest what we do is leave it in place for long enough to get
a round of reports from those slow animals, and then (assuming
those reports are positive) drop the test.
I just encountered this test because it doesn't succeed on a 32bit system with
address sanitizer enabled - it runs out of memory. At that point there are
"just" 29895 parameters parsed...
It's also the slowest step on skink (valgrind animal), taking nearly an hour.
I think two years later is long enough to have some confidence in this being
fixed?
Greetings,
Andres Freund
Andres Freund <andres@anarazel.de> writes:
On 2021-06-11 18:44:28 -0400, Tom Lane wrote:
I suggest what we do is leave it in place for long enough to get
a round of reports from those slow animals, and then (assuming
those reports are positive) drop the test.
I think two years later is long enough to have some confidence in this being
fixed?
+1, time to drop it (in the back branches too).
regards, tom lane
On 7/2/23 15:23, Tom Lane wrote:
Andres Freund <andres@anarazel.de> writes:
On 2021-06-11 18:44:28 -0400, Tom Lane wrote:
I suggest what we do is leave it in place for long enough to get
a round of reports from those slow animals, and then (assuming
those reports are positive) drop the test.I think two years later is long enough to have some confidence in this being
fixed?+1, time to drop it (in the back branches too).
OK, will do (unless someone else wants to handle this) on Monday.
regards
--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On 7/2/23 15:50, Tomas Vondra wrote:
On 7/2/23 15:23, Tom Lane wrote:
Andres Freund <andres@anarazel.de> writes:
On 2021-06-11 18:44:28 -0400, Tom Lane wrote:
I suggest what we do is leave it in place for long enough to get
a round of reports from those slow animals, and then (assuming
those reports are positive) drop the test.I think two years later is long enough to have some confidence in this being
fixed?+1, time to drop it (in the back branches too).
OK, will do (unless someone else wants to handle this) on Monday.
FWIW I've removed the test from all branches where it was present.
regards
--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company