From: | Konstantin Knizhnik <knizhnik(at)garret(dot)ru> |
---|---|
To: | Jelte Fennema <me(at)jeltef(dot)nl>, PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org> |
Subject: | Re: Support prepared statement invalidation when result types change |
Date: | 2023-08-28 13:05:28 |
Message-ID: | 4663e991-a797-8993-5ebd-16f9a483a1bb@garret.ru |
Views: | Raw Message | Whole Thread | Download mbox | Resend email |
Thread: | |
Lists: | pgsql-hackers |
On 25.08.2023 8:57 PM, Jelte Fennema wrote:
> The cached plan for a prepared statements can get invalidated when DDL
> changes the tables used in the query, or when search_path changes. When
> this happens the prepared statement can still be executed, but it will
> be replanned in the new context. This means that the prepared statement
> will do something different e.g. in case of search_path changes it will
> select data from a completely different table. This won't throw an
> error, because it is considered the responsibility of the operator and
> query writers that the query will still do the intended thing.
>
> However, we would throw an error if the the result of the query is of a
> different type than it was before:
> ERROR: cached plan must not change result type
>
> This requirement was not documented anywhere and it
> can thus be a surprising error to hit. But it's actually not needed for
> this to be an error, as long as we send the correct RowDescription there
> does not have to be a problem for clients when the result types or
> column counts change.
>
> This patch starts to allow a prepared statement to continue to work even
> when the result type changes.
>
> Without this change all clients that automatically prepare queries as a
> performance optimization will need to handle or avoid the error somehow,
> often resulting in deallocating and re-preparing queries when its
> usually not necessary. With this change connection poolers can also
> safely prepare the same query only once on a connection and share this
> one prepared query across clients that prepared that exact same query.
>
> Some relevant previous discussions:
> [1]: https://www.postgresql.org/message-id/flat/CAB%3DJe-GQOW7kU9Hn3AqP1vhaZg_wE9Lz6F4jSp-7cm9_M6DyVA%40mail.gmail.com
> [2]: https://stackoverflow.com/questions/2783813/postgres-error-cached-plan-must-not-change-result-type
> [3]: https://stackoverflow.com/questions/42119365/how-to-avoid-cached-plan-must-not-change-result-type-error
> [4]: https://github.com/pgjdbc/pgjdbc/pull/451
> [5]: https://github.com/pgbouncer/pgbouncer/pull/845#discussion_r1305295551
> [6]: https://github.com/jackc/pgx/issues/927
> [7]: https://elixirforum.com/t/postgrex-errors-with-cached-plan-must-not-change-result-type-during-migration/51235/2
> [8]: https://github.com/rails/rails/issues/12330
The following assignment of format is not corrects:
/* Do nothing if portal won't return tuples */
if (portal->tupDesc == NULL)
+ {
+ /*
+ * For SELECT like queries we delay filling in the tupDesc
until after
+ * PortalRunUtility, because we don't know what rows an EXECUTE
+ * command will return. Because we delay setting tupDesc, we
also need
+ * to delay setting formats. We do this in a pretty hacky way, by
+ * temporarily setting the portal formats to the passed in formats.
+ * Then once we fill in tupDesc, we call PortalSetResultFormat
again
+ * with portal->formats to fill in the final formats value.
+ */
+ if (portal->strategy == PORTAL_UTIL_SELECT)
+ portal->formats = formats;
return;
because it is create in other memory context:
postgres.c:
/* Done storing stuff in portal's context */
MemoryContextSwitchTo(oldContext);
...
/* Get the result format codes */
numRFormats = pq_getmsgint(input_message, 2);
if (numRFormats > 0)
{
rformats = palloc_array(int16, numRFormats);
for (int i = 0; i < numRFormats; i++)
rformats[i] = pq_getmsgint(input_message, 2);
}
It has to be copied as below:
portal->formats = (int16 *)
MemoryContextAlloc(portal->portalContext,
natts * sizeof(int16));
memcpy(portal->formats, formats, natts * sizeof(int16));
or alternatively MemoryContextSwitchTo(oldContext) should be moved
after initialization of rformat
From | Date | Subject | |
---|---|---|---|
Next Message | Daniel Gustafsson | 2023-08-28 13:11:15 | Is pg_regress --use-existing used by anyone or is it broken? |
Previous Message | Hayato Kuroda (Fujitsu) | 2023-08-28 13:01:51 | RE: [PoC] pg_upgrade: allow to upgrade publisher node |