2013-02-21 11:26:23 +01:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* deparse.c
|
|
|
|
* Query deparser for postgres_fdw
|
|
|
|
*
|
|
|
|
* This file includes functions that examine query WHERE clauses to see
|
|
|
|
* whether they're safe to send to the remote server for execution, as
|
|
|
|
* well as functions to construct the query text to be sent. The latter
|
|
|
|
* functionality is annoyingly duplicative of ruleutils.c, but there are
|
|
|
|
* enough special considerations that it seems best to keep this separate.
|
|
|
|
* One saving grace is that we only need deparse logic for node types that
|
|
|
|
* we consider safe to send.
|
|
|
|
*
|
2013-02-22 12:03:46 +01:00
|
|
|
* We assume that the remote session's search_path is exactly "pg_catalog",
|
|
|
|
* and thus we need schema-qualify all and only names outside pg_catalog.
|
|
|
|
*
|
2013-03-14 00:46:31 +01:00
|
|
|
* We do not consider that it is ever safe to send COLLATE expressions to
|
|
|
|
* the remote server: it might not have the same collation names we do.
|
|
|
|
* (Later we might consider it safe to send COLLATE "C", but even that would
|
Improve handling of collations in contrib/postgres_fdw.
If we have a local Var of say varchar type with default collation, and
we apply a RelabelType to convert that to text with default collation, we
don't want to consider that as creating an FDW_COLLATE_UNSAFE situation.
It should be okay to compare that to a remote Var, so long as the remote
Var determines the comparison collation. (When we actually ship such an
expression to the remote side, the local Var would become a Param with
default collation, meaning the remote Var would in fact control the
comparison collation, because non-default implicit collation overrides
default implicit collation in parse_collate.c.) To fix, be more precise
about what FDW_COLLATE_NONE means: it applies either to a noncollatable
data type or to a collatable type with default collation, if that collation
can't be traced to a remote Var. (When it can, FDW_COLLATE_SAFE is
appropriate.) We were essentially using that interpretation already at
the Var/Const/Param level, but we weren't bubbling it up properly.
An alternative fix would be to introduce a separate FDW_COLLATE_DEFAULT
value to describe the second situation, but that would add more code
without changing the actual behavior, so it didn't seem worthwhile.
Also, since we're clarifying the rule to be that we care about whether
operator/function input collations match, there seems no need to fail
immediately upon seeing a Const/Param/non-foreign-Var with nondefault
collation. We only have to reject if it appears in a collation-sensitive
context (for example, "var IS NOT NULL" is perfectly safe from a collation
standpoint, whatever collation the var has). So just set the state to
UNSAFE rather than failing immediately.
Per report from Jeevan Chalke. This essentially corrects some sloppy
thinking in commit ed3ddf918b59545583a4b374566bc1148e75f593, so back-patch
to 9.3 where that logic appeared.
2015-09-24 18:47:29 +02:00
|
|
|
* fail on old remote servers.) An expression is considered safe to send
|
|
|
|
* only if all operator/function input collations used in it are traceable to
|
|
|
|
* Var(s) of the foreign table. That implies that if the remote server gets
|
|
|
|
* a different answer than we do, the foreign table's columns are not marked
|
|
|
|
* with collations that match the remote table's columns, which we can
|
|
|
|
* consider to be user error.
|
2013-03-14 00:46:31 +01:00
|
|
|
*
|
2022-01-08 01:04:57 +01:00
|
|
|
* Portions Copyright (c) 2012-2022, PostgreSQL Global Development Group
|
2013-02-21 11:26:23 +01:00
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
|
|
|
* contrib/postgres_fdw/deparse.c
|
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
|
|
|
#include "access/htup_details.h"
|
|
|
|
#include "access/sysattr.h"
|
2019-01-21 19:18:20 +01:00
|
|
|
#include "access/table.h"
|
2016-10-21 15:54:29 +02:00
|
|
|
#include "catalog/pg_aggregate.h"
|
2013-03-14 00:46:31 +01:00
|
|
|
#include "catalog/pg_collation.h"
|
2013-02-22 12:03:46 +01:00
|
|
|
#include "catalog/pg_namespace.h"
|
2013-02-21 11:26:23 +01:00
|
|
|
#include "catalog/pg_operator.h"
|
|
|
|
#include "catalog/pg_proc.h"
|
|
|
|
#include "catalog/pg_type.h"
|
|
|
|
#include "commands/defrem.h"
|
2016-02-09 20:00:50 +01:00
|
|
|
#include "nodes/makefuncs.h"
|
2013-02-21 11:26:23 +01:00
|
|
|
#include "nodes/nodeFuncs.h"
|
2016-01-28 22:44:01 +01:00
|
|
|
#include "nodes/plannodes.h"
|
2019-01-29 21:48:51 +01:00
|
|
|
#include "optimizer/optimizer.h"
|
2016-01-28 22:44:01 +01:00
|
|
|
#include "optimizer/prep.h"
|
2016-02-09 20:00:50 +01:00
|
|
|
#include "optimizer/tlist.h"
|
2013-02-21 11:26:23 +01:00
|
|
|
#include "parser/parsetree.h"
|
2019-10-23 05:56:22 +02:00
|
|
|
#include "postgres_fdw.h"
|
2013-02-21 11:26:23 +01:00
|
|
|
#include "utils/builtins.h"
|
|
|
|
#include "utils/lsyscache.h"
|
2014-11-15 01:48:53 +01:00
|
|
|
#include "utils/rel.h"
|
2013-02-21 11:26:23 +01:00
|
|
|
#include "utils/syscache.h"
|
2016-10-21 15:54:29 +02:00
|
|
|
#include "utils/typcache.h"
|
Allow TRUNCATE command to truncate foreign tables.
This commit introduces new foreign data wrapper API for TRUNCATE.
It extends TRUNCATE command so that it accepts foreign tables as
the targets to truncate and invokes that API. Also it extends postgres_fdw
so that it can issue TRUNCATE command to foreign servers, by adding
new routine for that TRUNCATE API.
The information about options specified in TRUNCATE command, e.g.,
ONLY, CACADE, etc is passed to FDW via API. The list of foreign tables to
truncate is also passed to FDW. FDW truncates the foreign data sources
that the passed foreign tables specify, based on those information.
For example, postgres_fdw constructs TRUNCATE command using them
and issues it to the foreign server.
For performance, TRUNCATE command invokes the FDW routine for
TRUNCATE once per foreign server that foreign tables to truncate belong to.
Author: Kazutaka Onishi, Kohei KaiGai, slightly modified by Fujii Masao
Reviewed-by: Bharath Rupireddy, Michael Paquier, Zhihong Yu, Alvaro Herrera, Stephen Frost, Ashutosh Bapat, Amit Langote, Daniel Gustafsson, Ibrar Ahmed, Fujii Masao
Discussion: https://postgr.es/m/CAOP8fzb_gkReLput7OvOK+8NHgw-RKqNv59vem7=524krQTcWA@mail.gmail.com
Discussion: https://postgr.es/m/CAJuF6cMWDDqU-vn_knZgma+2GMaout68YUgn1uyDnexRhqqM5Q@mail.gmail.com
2021-04-08 13:56:08 +02:00
|
|
|
#include "commands/tablecmds.h"
|
2013-02-21 11:26:23 +01:00
|
|
|
|
|
|
|
/*
|
2013-03-14 00:46:31 +01:00
|
|
|
* Global context for foreign_expr_walker's search of an expression tree.
|
2013-02-21 11:26:23 +01:00
|
|
|
*/
|
2013-03-14 00:46:31 +01:00
|
|
|
typedef struct foreign_glob_cxt
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
2013-03-22 00:43:59 +01:00
|
|
|
PlannerInfo *root; /* global planner state */
|
|
|
|
RelOptInfo *foreignrel; /* the foreign relation we are planning for */
|
2016-10-21 15:54:29 +02:00
|
|
|
Relids relids; /* relids of base relations in the underlying
|
|
|
|
* scan */
|
2013-03-14 00:46:31 +01:00
|
|
|
} foreign_glob_cxt;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Local (per-tree-level) context for foreign_expr_walker's search.
|
|
|
|
* This is concerned with identifying collations used in the expression.
|
|
|
|
*/
|
|
|
|
typedef enum
|
|
|
|
{
|
Improve handling of collations in contrib/postgres_fdw.
If we have a local Var of say varchar type with default collation, and
we apply a RelabelType to convert that to text with default collation, we
don't want to consider that as creating an FDW_COLLATE_UNSAFE situation.
It should be okay to compare that to a remote Var, so long as the remote
Var determines the comparison collation. (When we actually ship such an
expression to the remote side, the local Var would become a Param with
default collation, meaning the remote Var would in fact control the
comparison collation, because non-default implicit collation overrides
default implicit collation in parse_collate.c.) To fix, be more precise
about what FDW_COLLATE_NONE means: it applies either to a noncollatable
data type or to a collatable type with default collation, if that collation
can't be traced to a remote Var. (When it can, FDW_COLLATE_SAFE is
appropriate.) We were essentially using that interpretation already at
the Var/Const/Param level, but we weren't bubbling it up properly.
An alternative fix would be to introduce a separate FDW_COLLATE_DEFAULT
value to describe the second situation, but that would add more code
without changing the actual behavior, so it didn't seem worthwhile.
Also, since we're clarifying the rule to be that we care about whether
operator/function input collations match, there seems no need to fail
immediately upon seeing a Const/Param/non-foreign-Var with nondefault
collation. We only have to reject if it appears in a collation-sensitive
context (for example, "var IS NOT NULL" is perfectly safe from a collation
standpoint, whatever collation the var has). So just set the state to
UNSAFE rather than failing immediately.
Per report from Jeevan Chalke. This essentially corrects some sloppy
thinking in commit ed3ddf918b59545583a4b374566bc1148e75f593, so back-patch
to 9.3 where that logic appeared.
2015-09-24 18:47:29 +02:00
|
|
|
FDW_COLLATE_NONE, /* expression is of a noncollatable type, or
|
|
|
|
* it has default collation that is not
|
|
|
|
* traceable to a foreign Var */
|
2013-03-14 00:46:31 +01:00
|
|
|
FDW_COLLATE_SAFE, /* collation derives from a foreign Var */
|
Improve handling of collations in contrib/postgres_fdw.
If we have a local Var of say varchar type with default collation, and
we apply a RelabelType to convert that to text with default collation, we
don't want to consider that as creating an FDW_COLLATE_UNSAFE situation.
It should be okay to compare that to a remote Var, so long as the remote
Var determines the comparison collation. (When we actually ship such an
expression to the remote side, the local Var would become a Param with
default collation, meaning the remote Var would in fact control the
comparison collation, because non-default implicit collation overrides
default implicit collation in parse_collate.c.) To fix, be more precise
about what FDW_COLLATE_NONE means: it applies either to a noncollatable
data type or to a collatable type with default collation, if that collation
can't be traced to a remote Var. (When it can, FDW_COLLATE_SAFE is
appropriate.) We were essentially using that interpretation already at
the Var/Const/Param level, but we weren't bubbling it up properly.
An alternative fix would be to introduce a separate FDW_COLLATE_DEFAULT
value to describe the second situation, but that would add more code
without changing the actual behavior, so it didn't seem worthwhile.
Also, since we're clarifying the rule to be that we care about whether
operator/function input collations match, there seems no need to fail
immediately upon seeing a Const/Param/non-foreign-Var with nondefault
collation. We only have to reject if it appears in a collation-sensitive
context (for example, "var IS NOT NULL" is perfectly safe from a collation
standpoint, whatever collation the var has). So just set the state to
UNSAFE rather than failing immediately.
Per report from Jeevan Chalke. This essentially corrects some sloppy
thinking in commit ed3ddf918b59545583a4b374566bc1148e75f593, so back-patch
to 9.3 where that logic appeared.
2015-09-24 18:47:29 +02:00
|
|
|
FDW_COLLATE_UNSAFE /* collation is non-default and derives from
|
|
|
|
* something other than a foreign Var */
|
2013-03-14 00:46:31 +01:00
|
|
|
} FDWCollateState;
|
|
|
|
|
|
|
|
typedef struct foreign_loc_cxt
|
|
|
|
{
|
|
|
|
Oid collation; /* OID of current collation, if any */
|
|
|
|
FDWCollateState state; /* state of current collation choice */
|
|
|
|
} foreign_loc_cxt;
|
2013-02-21 11:26:23 +01:00
|
|
|
|
2013-03-22 00:43:59 +01:00
|
|
|
/*
|
|
|
|
* Context for deparseExpr
|
|
|
|
*/
|
|
|
|
typedef struct deparse_expr_cxt
|
|
|
|
{
|
|
|
|
PlannerInfo *root; /* global planner state */
|
|
|
|
RelOptInfo *foreignrel; /* the foreign relation we are planning for */
|
2016-10-21 15:54:29 +02:00
|
|
|
RelOptInfo *scanrel; /* the underlying scan relation. Same as
|
|
|
|
* foreignrel, when that represents a join or
|
|
|
|
* a base relation. */
|
2013-03-22 00:43:59 +01:00
|
|
|
StringInfo buf; /* output buffer to append to */
|
|
|
|
List **params_list; /* exprs that will become remote Params */
|
|
|
|
} deparse_expr_cxt;
|
|
|
|
|
2016-02-09 20:00:50 +01:00
|
|
|
#define REL_ALIAS_PREFIX "r"
|
|
|
|
/* Handy macro to add relation name qualification */
|
|
|
|
#define ADD_REL_QUALIFIER(buf, varno) \
|
|
|
|
appendStringInfo((buf), "%s%d.", REL_ALIAS_PREFIX, (varno))
|
2017-03-16 18:34:59 +01:00
|
|
|
#define SUBQUERY_REL_ALIAS_PREFIX "s"
|
|
|
|
#define SUBQUERY_COL_ALIAS_PREFIX "c"
|
2016-02-09 20:00:50 +01:00
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
/*
|
|
|
|
* Functions to determine whether an expression can be evaluated safely on
|
|
|
|
* remote server.
|
|
|
|
*/
|
2013-03-14 00:46:31 +01:00
|
|
|
static bool foreign_expr_walker(Node *node,
|
2019-05-22 19:04:48 +02:00
|
|
|
foreign_glob_cxt *glob_cxt,
|
2021-07-30 19:39:48 +02:00
|
|
|
foreign_loc_cxt *outer_cxt,
|
|
|
|
foreign_loc_cxt *case_arg_cxt);
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
static char *deparse_type_name(Oid type_oid, int32 typemod);
|
2013-02-21 11:26:23 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Functions to construct string representation of a node tree.
|
|
|
|
*/
|
2013-03-10 19:14:53 +01:00
|
|
|
static void deparseTargetList(StringInfo buf,
|
2019-05-22 19:04:48 +02:00
|
|
|
RangeTblEntry *rte,
|
|
|
|
Index rtindex,
|
|
|
|
Relation rel,
|
|
|
|
bool is_returning,
|
|
|
|
Bitmapset *attrs_used,
|
|
|
|
bool qualify_col,
|
|
|
|
List **retrieved_attrs);
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
static void deparseExplicitTargetList(List *tlist,
|
2019-05-22 19:04:48 +02:00
|
|
|
bool is_returning,
|
|
|
|
List **retrieved_attrs,
|
|
|
|
deparse_expr_cxt *context);
|
2017-03-16 18:34:59 +01:00
|
|
|
static void deparseSubqueryTargetList(deparse_expr_cxt *context);
|
Fix interaction of foreign tuple routing with remote triggers.
Without these fixes, changes to the inserted tuple made by remote
triggers are ignored when building local RETURNING tuples.
In the core code, call ExecInitRoutingInfo at a later point from
within ExecInitPartitionInfo so that the FDW callback gets invoked
after the returning list has been built. But move CheckValidResultRel
out of ExecInitRoutingInfo so that it can happen at an earlier stage.
In postgres_fdw, refactor assorted deparsing functions to work with
the RTE rather than the PlannerInfo, which saves us having to
construct a fake PlannerInfo in cases where we don't have a real one.
Then, we can pass down a constructed RTE that yields the correct
deparse result when no real one exists. Unfortunately, this
necessitates a hack that understands how the core code manages RT
indexes for update tuple routing, which is ugly, but we don't have a
better idea right now.
Original report, analysis, and patch by Etsuro Fujita. Heavily
refactored by me. Then worked over some more by Amit Langote.
Discussion: http://postgr.es/m/5AD4882B.10002@lab.ntt.co.jp
2018-05-01 19:21:46 +02:00
|
|
|
static void deparseReturningList(StringInfo buf, RangeTblEntry *rte,
|
2019-05-22 19:04:48 +02:00
|
|
|
Index rtindex, Relation rel,
|
|
|
|
bool trig_after_row,
|
|
|
|
List *withCheckOptionList,
|
|
|
|
List *returningList,
|
|
|
|
List **retrieved_attrs);
|
2013-02-21 11:26:23 +01:00
|
|
|
static void deparseColumnRef(StringInfo buf, int varno, int varattno,
|
2019-05-22 19:04:48 +02:00
|
|
|
RangeTblEntry *rte, bool qualify_col);
|
2013-03-12 23:58:13 +01:00
|
|
|
static void deparseRelation(StringInfo buf, Relation rel);
|
2013-03-22 00:43:59 +01:00
|
|
|
static void deparseExpr(Expr *expr, deparse_expr_cxt *context);
|
|
|
|
static void deparseVar(Var *node, deparse_expr_cxt *context);
|
2016-10-21 15:54:29 +02:00
|
|
|
static void deparseConst(Const *node, deparse_expr_cxt *context, int showtype);
|
2013-03-22 00:43:59 +01:00
|
|
|
static void deparseParam(Param *node, deparse_expr_cxt *context);
|
2019-02-01 16:50:32 +01:00
|
|
|
static void deparseSubscriptingRef(SubscriptingRef *node, deparse_expr_cxt *context);
|
2013-03-22 00:43:59 +01:00
|
|
|
static void deparseFuncExpr(FuncExpr *node, deparse_expr_cxt *context);
|
|
|
|
static void deparseOpExpr(OpExpr *node, deparse_expr_cxt *context);
|
postgres_fdw: suppress casts on constants in limited cases.
When deparsing an expression of the form "remote_var OP constant",
we'd normally apply a cast to the constant to make sure that the
remote parser thinks it's of the same type we do. However, doing
so is often not necessary, and it causes problems if the user has
intentionally declared the local column as being of a different
type than the remote column. A plausible use-case for that is
using text to represent a type that's an enum on the remote side.
A comparison on such a column will get shipped as "var = 'foo'::text",
which blows up on the remote side because there's no enum = text
operator. But if we simply leave off the explicit cast, the
comparison will do exactly what the user wants.
It's possible to do this without major risk of semantic problems, by
relying on the longstanding parser heuristic that "if one operand of
an operator is of type unknown, while the other one has a known type,
assume that the unknown operand is also of that type". Hence, this
patch leaves off the cast only if (a) the operator inputs have the same
type locally; (b) the constant will print as a string literal or NULL,
both of which are initially taken as type unknown; and (c) the non-Const
input is a plain foreign Var. Rule (c) guarantees that the remote
parser will know the type of the non-Const input; moreover, it means
that if this cast-omission does cause any semantic surprises, that can
only happen in cases where the local column has a different type than
the remote column. That wasn't guaranteed to work anyway, and this
patch should represent a net usability gain for such cases.
One point that I (tgl) remain slightly uncomfortable with is that we
will ignore an implicit RelabelType when deciding if the non-Const input
is a plain Var. That makes it a little squishy to argue that the remote
should resolve the Const as being of the same type as its Var, because
then our Const is not the same type as our Var. However, if we don't do
that, then this hack won't work as desired if the user chooses to use
varchar rather than text to represent some remote column. That seems
useful, so do it like this for now. We might have to give up the
RelabelType-ignoring bit if any problems surface.
Dian Fay, with review and kibitzing by me
Discussion: https://postgr.es/m/C9LU294V7K4F.34LRRDU449O45@lamia
2021-11-12 17:50:40 +01:00
|
|
|
static bool isPlainForeignVar(Expr *node, deparse_expr_cxt *context);
|
2013-02-22 12:03:46 +01:00
|
|
|
static void deparseOperatorName(StringInfo buf, Form_pg_operator opform);
|
2013-03-22 00:43:59 +01:00
|
|
|
static void deparseDistinctExpr(DistinctExpr *node, deparse_expr_cxt *context);
|
|
|
|
static void deparseScalarArrayOpExpr(ScalarArrayOpExpr *node,
|
2019-05-22 19:04:48 +02:00
|
|
|
deparse_expr_cxt *context);
|
2013-03-22 00:43:59 +01:00
|
|
|
static void deparseRelabelType(RelabelType *node, deparse_expr_cxt *context);
|
|
|
|
static void deparseBoolExpr(BoolExpr *node, deparse_expr_cxt *context);
|
|
|
|
static void deparseNullTest(NullTest *node, deparse_expr_cxt *context);
|
2021-07-30 19:39:48 +02:00
|
|
|
static void deparseCaseExpr(CaseExpr *node, deparse_expr_cxt *context);
|
2013-03-22 00:43:59 +01:00
|
|
|
static void deparseArrayExpr(ArrayExpr *node, deparse_expr_cxt *context);
|
2014-04-16 23:21:57 +02:00
|
|
|
static void printRemoteParam(int paramindex, Oid paramtype, int32 paramtypmod,
|
2019-05-22 19:04:48 +02:00
|
|
|
deparse_expr_cxt *context);
|
2014-04-16 23:21:57 +02:00
|
|
|
static void printRemotePlaceholder(Oid paramtype, int32 paramtypmod,
|
2019-05-22 19:04:48 +02:00
|
|
|
deparse_expr_cxt *context);
|
2017-03-16 18:34:59 +01:00
|
|
|
static void deparseSelectSql(List *tlist, bool is_subquery, List **retrieved_attrs,
|
2019-05-22 19:04:48 +02:00
|
|
|
deparse_expr_cxt *context);
|
2016-01-30 16:32:38 +01:00
|
|
|
static void deparseLockingClause(deparse_expr_cxt *context);
|
2019-04-02 12:20:30 +02:00
|
|
|
static void appendOrderByClause(List *pathkeys, bool has_final_sort,
|
2019-05-22 19:04:48 +02:00
|
|
|
deparse_expr_cxt *context);
|
2019-04-02 13:30:45 +02:00
|
|
|
static void appendLimitClause(deparse_expr_cxt *context);
|
2016-02-09 20:00:50 +01:00
|
|
|
static void appendConditions(List *exprs, deparse_expr_cxt *context);
|
|
|
|
static void deparseFromExprForRel(StringInfo buf, PlannerInfo *root,
|
2019-05-22 19:04:48 +02:00
|
|
|
RelOptInfo *foreignrel, bool use_alias,
|
|
|
|
Index ignore_rel, List **ignore_conds,
|
|
|
|
List **params_list);
|
2016-10-21 15:54:29 +02:00
|
|
|
static void deparseFromExpr(List *quals, deparse_expr_cxt *context);
|
2017-03-16 18:34:59 +01:00
|
|
|
static void deparseRangeTblRef(StringInfo buf, PlannerInfo *root,
|
2019-05-22 19:04:48 +02:00
|
|
|
RelOptInfo *foreignrel, bool make_subquery,
|
|
|
|
Index ignore_rel, List **ignore_conds, List **params_list);
|
2016-10-21 15:54:29 +02:00
|
|
|
static void deparseAggref(Aggref *node, deparse_expr_cxt *context);
|
|
|
|
static void appendGroupByClause(List *tlist, deparse_expr_cxt *context);
|
|
|
|
static void appendAggOrderBy(List *orderList, List *targetList,
|
2019-05-22 19:04:48 +02:00
|
|
|
deparse_expr_cxt *context);
|
2016-10-21 15:54:29 +02:00
|
|
|
static void appendFunctionName(Oid funcid, deparse_expr_cxt *context);
|
2018-01-12 22:52:49 +01:00
|
|
|
static Node *deparseSortGroupClause(Index ref, List *tlist, bool force_colno,
|
2019-05-22 19:04:48 +02:00
|
|
|
deparse_expr_cxt *context);
|
2013-02-21 11:26:23 +01:00
|
|
|
|
2017-03-16 18:34:59 +01:00
|
|
|
/*
|
|
|
|
* Helper functions
|
|
|
|
*/
|
|
|
|
static bool is_subquery_var(Var *node, RelOptInfo *foreignrel,
|
2019-05-22 19:04:48 +02:00
|
|
|
int *relno, int *colno);
|
2017-03-16 18:34:59 +01:00
|
|
|
static void get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
|
2019-05-22 19:04:48 +02:00
|
|
|
int *relno, int *colno);
|
2017-03-16 18:34:59 +01:00
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
|
|
|
|
/*
|
2014-03-07 22:35:58 +01:00
|
|
|
* Examine each qual clause in input_conds, and classify them into two groups,
|
|
|
|
* which are returned as two lists:
|
2013-03-22 00:43:59 +01:00
|
|
|
* - remote_conds contains expressions that can be evaluated remotely
|
|
|
|
* - local_conds contains expressions that can't be evaluated remotely
|
2013-02-21 11:26:23 +01:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
classifyConditions(PlannerInfo *root,
|
|
|
|
RelOptInfo *baserel,
|
2014-03-07 22:35:58 +01:00
|
|
|
List *input_conds,
|
2013-02-21 11:26:23 +01:00
|
|
|
List **remote_conds,
|
2013-03-22 00:43:59 +01:00
|
|
|
List **local_conds)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
*remote_conds = NIL;
|
|
|
|
*local_conds = NIL;
|
|
|
|
|
2014-03-07 22:35:58 +01:00
|
|
|
foreach(lc, input_conds)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
Handle restriction clause lists more uniformly in postgres_fdw.
Clauses in the lists retained by postgres_fdw during planning were
sometimes bare boolean clauses, sometimes RestrictInfos, and sometimes
a mixture of the two in the same list. The comment about that situation
didn't come close to telling the full truth, either. Aside from being
confusing, this had a couple of bad practical consequences:
* waste of planning cycles due to inability to cache per-clause selectivity
and cost estimates;
* sometimes, RestrictInfos would sneak into the fdw_private list of a
finished Plan node, causing failures if, for example, we tried to ship
the Plan tree to a parallel worker.
(It may well be that it's a bug in the parallel-query logic that we
would ever try to ship such a plan to a parallel worker, but in any
case this deserves to be cleaned up.)
To fix, rearrange so that clause lists in PgFdwRelationInfo are always
lists of RestrictInfos, and then strip the RestrictInfos at the last
minute when making a Plan node. In passing do a bit of refactoring and
comment cleanup in postgresGetForeignPlan and foreign_join_ok.
Although the messiness here dates back at least to 9.6, there's no evidence
that it causes anything worse than wasted planning cycles in 9.6, so no
back-patch for now.
Per fuzz testing by Andreas Seltenreich.
Tom Lane and Ashutosh Bapat
Discussion: https://postgr.es/m/87tw5x4vcu.fsf@credativ.de
2017-04-11 17:58:59 +02:00
|
|
|
RestrictInfo *ri = lfirst_node(RestrictInfo, lc);
|
2013-02-21 11:26:23 +01:00
|
|
|
|
2013-03-22 00:43:59 +01:00
|
|
|
if (is_foreign_expr(root, baserel, ri->clause))
|
|
|
|
*remote_conds = lappend(*remote_conds, ri);
|
2013-02-21 11:26:23 +01:00
|
|
|
else
|
|
|
|
*local_conds = lappend(*local_conds, ri);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns true if given expr is safe to evaluate on the foreign server.
|
|
|
|
*/
|
2013-03-22 00:43:59 +01:00
|
|
|
bool
|
2013-02-21 11:26:23 +01:00
|
|
|
is_foreign_expr(PlannerInfo *root,
|
|
|
|
RelOptInfo *baserel,
|
2013-03-22 00:43:59 +01:00
|
|
|
Expr *expr)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
2013-03-14 00:46:31 +01:00
|
|
|
foreign_glob_cxt glob_cxt;
|
|
|
|
foreign_loc_cxt loc_cxt;
|
2016-10-21 15:54:29 +02:00
|
|
|
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) (baserel->fdw_private);
|
2013-02-21 11:26:23 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check that the expression consists of nodes that are safe to execute
|
|
|
|
* remotely.
|
|
|
|
*/
|
2013-03-14 00:46:31 +01:00
|
|
|
glob_cxt.root = root;
|
|
|
|
glob_cxt.foreignrel = baserel;
|
2016-10-21 15:54:29 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* For an upper relation, use relids from its underneath scan relation,
|
|
|
|
* because the upperrel's own relids currently aren't set to anything
|
|
|
|
* meaningful by the core code. For other relation, use their own relids.
|
|
|
|
*/
|
Abstract logic to allow for multiple kinds of child rels.
Currently, the only type of child relation is an "other member rel",
which is the child of a baserel, but in the future joins and even
upper relations may have child rels. To facilitate that, introduce
macros that test to test for particular RelOptKind values, and use
them in various places where they help to clarify the sense of a test.
(For example, a test may allow RELOPT_OTHER_MEMBER_REL either because
it intends to allow child rels, or because it intends to allow simple
rels.)
Also, remove find_childrel_top_parent, which will not work for a
child rel that is not a baserel. Instead, add a new RelOptInfo
member top_parent_relids to track the same kind of information in a
more generic manner.
Ashutosh Bapat, slightly tweaked by me. Review and testing of the
patch set from which this was taken by Rajkumar Raghuwanshi and Rafia
Sabih.
Discussion: http://postgr.es/m/CA+TgmoagTnF2yqR3PT2rv=om=wJiZ4-A+ATwdnriTGku1CLYxA@mail.gmail.com
2017-04-04 04:41:31 +02:00
|
|
|
if (IS_UPPER_REL(baserel))
|
2016-10-21 15:54:29 +02:00
|
|
|
glob_cxt.relids = fpinfo->outerrel->relids;
|
|
|
|
else
|
|
|
|
glob_cxt.relids = baserel->relids;
|
2013-03-14 00:46:31 +01:00
|
|
|
loc_cxt.collation = InvalidOid;
|
|
|
|
loc_cxt.state = FDW_COLLATE_NONE;
|
2021-07-30 19:39:48 +02:00
|
|
|
if (!foreign_expr_walker((Node *) expr, &glob_cxt, &loc_cxt, NULL))
|
2013-02-21 11:26:23 +01:00
|
|
|
return false;
|
|
|
|
|
2015-11-03 18:46:06 +01:00
|
|
|
/*
|
|
|
|
* If the expression has a valid collation that does not arise from a
|
|
|
|
* foreign var, the expression can not be sent over.
|
|
|
|
*/
|
|
|
|
if (loc_cxt.state == FDW_COLLATE_UNSAFE)
|
|
|
|
return false;
|
2013-03-14 00:46:31 +01:00
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
/*
|
|
|
|
* An expression which includes any mutable functions can't be sent over
|
|
|
|
* because its result is not stable. For example, sending now() remote
|
|
|
|
* side could cause confusion from clock offsets. Future versions might
|
|
|
|
* be able to make this choice with more granularity. (We check this last
|
|
|
|
* because it requires a lot of expensive catalog lookups.)
|
|
|
|
*/
|
|
|
|
if (contain_mutable_functions((Node *) expr))
|
|
|
|
return false;
|
|
|
|
|
2013-03-22 00:43:59 +01:00
|
|
|
/* OK to evaluate on the remote server */
|
2013-02-21 11:26:23 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2013-03-14 00:46:31 +01:00
|
|
|
* Check if expression is safe to execute remotely, and return true if so.
|
|
|
|
*
|
2013-03-22 00:43:59 +01:00
|
|
|
* In addition, *outer_cxt is updated with collation information.
|
2013-03-14 00:46:31 +01:00
|
|
|
*
|
2021-07-30 19:39:48 +02:00
|
|
|
* case_arg_cxt is NULL if this subexpression is not inside a CASE-with-arg.
|
|
|
|
* Otherwise, it points to the collation info derived from the arg expression,
|
|
|
|
* which must be consulted by any CaseTestExpr.
|
|
|
|
*
|
2013-03-14 00:46:31 +01:00
|
|
|
* We must check that the expression contains only node types we can deparse,
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
* that all types/functions/operators are safe to send (they are "shippable"),
|
|
|
|
* and that all collations used in the expression derive from Vars of the
|
|
|
|
* foreign table. Because of the latter, the logic is pretty close to
|
|
|
|
* assign_collations_walker() in parse_collate.c, though we can assume here
|
|
|
|
* that the given expression is valid. Note function mutability is not
|
|
|
|
* currently considered here.
|
2013-02-21 11:26:23 +01:00
|
|
|
*/
|
|
|
|
static bool
|
2013-03-14 00:46:31 +01:00
|
|
|
foreign_expr_walker(Node *node,
|
|
|
|
foreign_glob_cxt *glob_cxt,
|
2021-07-30 19:39:48 +02:00
|
|
|
foreign_loc_cxt *outer_cxt,
|
|
|
|
foreign_loc_cxt *case_arg_cxt)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
|
|
|
bool check_type = true;
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
PgFdwRelationInfo *fpinfo;
|
2013-03-14 00:46:31 +01:00
|
|
|
foreign_loc_cxt inner_cxt;
|
|
|
|
Oid collation;
|
|
|
|
FDWCollateState state;
|
2013-02-21 11:26:23 +01:00
|
|
|
|
2013-03-14 00:46:31 +01:00
|
|
|
/* Need do nothing for empty subexpressions */
|
2013-02-21 11:26:23 +01:00
|
|
|
if (node == NULL)
|
2013-03-14 00:46:31 +01:00
|
|
|
return true;
|
|
|
|
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
/* May need server info from baserel's fdw_private struct */
|
|
|
|
fpinfo = (PgFdwRelationInfo *) (glob_cxt->foreignrel->fdw_private);
|
|
|
|
|
2013-03-14 00:46:31 +01:00
|
|
|
/* Set up inner_cxt for possible recursion to child nodes */
|
|
|
|
inner_cxt.collation = InvalidOid;
|
|
|
|
inner_cxt.state = FDW_COLLATE_NONE;
|
2013-02-21 11:26:23 +01:00
|
|
|
|
|
|
|
switch (nodeTag(node))
|
|
|
|
{
|
|
|
|
case T_Var:
|
|
|
|
{
|
2013-03-14 00:46:31 +01:00
|
|
|
Var *var = (Var *) node;
|
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
/*
|
2013-03-22 00:43:59 +01:00
|
|
|
* If the Var is from the foreign table, we consider its
|
2014-05-06 18:12:18 +02:00
|
|
|
* collation (if any) safe to use. If it is from another
|
2013-03-22 00:43:59 +01:00
|
|
|
* table, we treat its collation the same way as we would a
|
|
|
|
* Param's collation, ie it's not safe for it to have a
|
|
|
|
* non-default collation.
|
2013-02-21 11:26:23 +01:00
|
|
|
*/
|
2016-10-21 15:54:29 +02:00
|
|
|
if (bms_is_member(var->varno, glob_cxt->relids) &&
|
2013-03-22 00:43:59 +01:00
|
|
|
var->varlevelsup == 0)
|
|
|
|
{
|
|
|
|
/* Var belongs to foreign table */
|
2014-11-22 22:01:05 +01:00
|
|
|
|
|
|
|
/*
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
* System columns other than ctid should not be sent to
|
|
|
|
* the remote, since we don't make any effort to ensure
|
|
|
|
* that local and remote values match (tableoid, in
|
2014-11-22 22:01:05 +01:00
|
|
|
* particular, almost certainly doesn't match).
|
|
|
|
*/
|
|
|
|
if (var->varattno < 0 &&
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
var->varattno != SelfItemPointerAttributeNumber)
|
2014-11-22 22:01:05 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Else check the collation */
|
2013-03-22 00:43:59 +01:00
|
|
|
collation = var->varcollid;
|
|
|
|
state = OidIsValid(collation) ? FDW_COLLATE_SAFE : FDW_COLLATE_NONE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Var belongs to some other table */
|
Improve handling of collations in contrib/postgres_fdw.
If we have a local Var of say varchar type with default collation, and
we apply a RelabelType to convert that to text with default collation, we
don't want to consider that as creating an FDW_COLLATE_UNSAFE situation.
It should be okay to compare that to a remote Var, so long as the remote
Var determines the comparison collation. (When we actually ship such an
expression to the remote side, the local Var would become a Param with
default collation, meaning the remote Var would in fact control the
comparison collation, because non-default implicit collation overrides
default implicit collation in parse_collate.c.) To fix, be more precise
about what FDW_COLLATE_NONE means: it applies either to a noncollatable
data type or to a collatable type with default collation, if that collation
can't be traced to a remote Var. (When it can, FDW_COLLATE_SAFE is
appropriate.) We were essentially using that interpretation already at
the Var/Const/Param level, but we weren't bubbling it up properly.
An alternative fix would be to introduce a separate FDW_COLLATE_DEFAULT
value to describe the second situation, but that would add more code
without changing the actual behavior, so it didn't seem worthwhile.
Also, since we're clarifying the rule to be that we care about whether
operator/function input collations match, there seems no need to fail
immediately upon seeing a Const/Param/non-foreign-Var with nondefault
collation. We only have to reject if it appears in a collation-sensitive
context (for example, "var IS NOT NULL" is perfectly safe from a collation
standpoint, whatever collation the var has). So just set the state to
UNSAFE rather than failing immediately.
Per report from Jeevan Chalke. This essentially corrects some sloppy
thinking in commit ed3ddf918b59545583a4b374566bc1148e75f593, so back-patch
to 9.3 where that logic appeared.
2015-09-24 18:47:29 +02:00
|
|
|
collation = var->varcollid;
|
|
|
|
if (collation == InvalidOid ||
|
|
|
|
collation == DEFAULT_COLLATION_OID)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* It's noncollatable, or it's safe to combine with a
|
|
|
|
* collatable foreign Var, so set state to NONE.
|
|
|
|
*/
|
|
|
|
state = FDW_COLLATE_NONE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Do not fail right away, since the Var might appear
|
|
|
|
* in a collation-insensitive context.
|
|
|
|
*/
|
|
|
|
state = FDW_COLLATE_UNSAFE;
|
|
|
|
}
|
2013-03-22 00:43:59 +01:00
|
|
|
}
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_Const:
|
2013-03-14 00:46:31 +01:00
|
|
|
{
|
|
|
|
Const *c = (Const *) node;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the constant has nondefault collation, either it's of a
|
Improve handling of collations in contrib/postgres_fdw.
If we have a local Var of say varchar type with default collation, and
we apply a RelabelType to convert that to text with default collation, we
don't want to consider that as creating an FDW_COLLATE_UNSAFE situation.
It should be okay to compare that to a remote Var, so long as the remote
Var determines the comparison collation. (When we actually ship such an
expression to the remote side, the local Var would become a Param with
default collation, meaning the remote Var would in fact control the
comparison collation, because non-default implicit collation overrides
default implicit collation in parse_collate.c.) To fix, be more precise
about what FDW_COLLATE_NONE means: it applies either to a noncollatable
data type or to a collatable type with default collation, if that collation
can't be traced to a remote Var. (When it can, FDW_COLLATE_SAFE is
appropriate.) We were essentially using that interpretation already at
the Var/Const/Param level, but we weren't bubbling it up properly.
An alternative fix would be to introduce a separate FDW_COLLATE_DEFAULT
value to describe the second situation, but that would add more code
without changing the actual behavior, so it didn't seem worthwhile.
Also, since we're clarifying the rule to be that we care about whether
operator/function input collations match, there seems no need to fail
immediately upon seeing a Const/Param/non-foreign-Var with nondefault
collation. We only have to reject if it appears in a collation-sensitive
context (for example, "var IS NOT NULL" is perfectly safe from a collation
standpoint, whatever collation the var has). So just set the state to
UNSAFE rather than failing immediately.
Per report from Jeevan Chalke. This essentially corrects some sloppy
thinking in commit ed3ddf918b59545583a4b374566bc1148e75f593, so back-patch
to 9.3 where that logic appeared.
2015-09-24 18:47:29 +02:00
|
|
|
* non-builtin type, or it reflects folding of a CollateExpr.
|
|
|
|
* It's unsafe to send to the remote unless it's used in a
|
|
|
|
* non-collation-sensitive context.
|
2013-03-14 00:46:31 +01:00
|
|
|
*/
|
Improve handling of collations in contrib/postgres_fdw.
If we have a local Var of say varchar type with default collation, and
we apply a RelabelType to convert that to text with default collation, we
don't want to consider that as creating an FDW_COLLATE_UNSAFE situation.
It should be okay to compare that to a remote Var, so long as the remote
Var determines the comparison collation. (When we actually ship such an
expression to the remote side, the local Var would become a Param with
default collation, meaning the remote Var would in fact control the
comparison collation, because non-default implicit collation overrides
default implicit collation in parse_collate.c.) To fix, be more precise
about what FDW_COLLATE_NONE means: it applies either to a noncollatable
data type or to a collatable type with default collation, if that collation
can't be traced to a remote Var. (When it can, FDW_COLLATE_SAFE is
appropriate.) We were essentially using that interpretation already at
the Var/Const/Param level, but we weren't bubbling it up properly.
An alternative fix would be to introduce a separate FDW_COLLATE_DEFAULT
value to describe the second situation, but that would add more code
without changing the actual behavior, so it didn't seem worthwhile.
Also, since we're clarifying the rule to be that we care about whether
operator/function input collations match, there seems no need to fail
immediately upon seeing a Const/Param/non-foreign-Var with nondefault
collation. We only have to reject if it appears in a collation-sensitive
context (for example, "var IS NOT NULL" is perfectly safe from a collation
standpoint, whatever collation the var has). So just set the state to
UNSAFE rather than failing immediately.
Per report from Jeevan Chalke. This essentially corrects some sloppy
thinking in commit ed3ddf918b59545583a4b374566bc1148e75f593, so back-patch
to 9.3 where that logic appeared.
2015-09-24 18:47:29 +02:00
|
|
|
collation = c->constcollid;
|
|
|
|
if (collation == InvalidOid ||
|
|
|
|
collation == DEFAULT_COLLATION_OID)
|
|
|
|
state = FDW_COLLATE_NONE;
|
|
|
|
else
|
|
|
|
state = FDW_COLLATE_UNSAFE;
|
2013-03-14 00:46:31 +01:00
|
|
|
}
|
2013-02-21 11:26:23 +01:00
|
|
|
break;
|
|
|
|
case T_Param:
|
|
|
|
{
|
|
|
|
Param *p = (Param *) node;
|
|
|
|
|
In postgres_fdw, don't try to ship MULTIEXPR updates to remote server.
In a statement like "UPDATE remote_tab SET (x,y) = (SELECT ...)",
we'd conclude that the statement could be directly executed remotely,
because the sub-SELECT is in a resjunk tlist item that's not examined
for shippability. Currently that ends up crashing if the sub-SELECT
contains any remote Vars. Prevent the crash by deeming MULTIEXEC
Params to be unshippable.
This is a bit of a brute-force solution, since if the sub-SELECT
*doesn't* contain any remote Vars, the current execution technology
would work; but that's not a terribly common use-case for this syntax,
I think. In any case, we generally don't try to ship sub-SELECTs, so
it won't surprise anybody that this doesn't end up as a remote direct
update. I'd be inclined to see if that general limitation can be fixed
before worrying about this case further.
Per report from Lukáš Sobotka.
Back-patch to 9.6. 9.5 had MULTIEXPR, but we didn't try to perform
remote direct updates then, so the case didn't arise anyway.
Discussion: https://postgr.es/m/CAJif3k+iA_ekBB5Zw2hDBaE1wtiQa4LH4_JUXrrMGwTrH0J01Q@mail.gmail.com
2020-01-26 20:31:08 +01:00
|
|
|
/*
|
|
|
|
* If it's a MULTIEXPR Param, punt. We can't tell from here
|
|
|
|
* whether the referenced sublink/subplan contains any remote
|
|
|
|
* Vars; if it does, handling that is too complicated to
|
|
|
|
* consider supporting at present. Fortunately, MULTIEXPR
|
|
|
|
* Params are not reduced to plain PARAM_EXEC until the end of
|
|
|
|
* planning, so we can easily detect this case. (Normal
|
|
|
|
* PARAM_EXEC Params are safe to ship because their values
|
|
|
|
* come from somewhere else in the plan tree; but a MULTIEXPR
|
|
|
|
* references a sub-select elsewhere in the same targetlist,
|
|
|
|
* so we'd be on the hook to evaluate it somehow if we wanted
|
|
|
|
* to handle such cases as direct foreign updates.)
|
|
|
|
*/
|
|
|
|
if (p->paramkind == PARAM_MULTIEXPR)
|
|
|
|
return false;
|
|
|
|
|
2013-03-14 00:46:31 +01:00
|
|
|
/*
|
Improve handling of collations in contrib/postgres_fdw.
If we have a local Var of say varchar type with default collation, and
we apply a RelabelType to convert that to text with default collation, we
don't want to consider that as creating an FDW_COLLATE_UNSAFE situation.
It should be okay to compare that to a remote Var, so long as the remote
Var determines the comparison collation. (When we actually ship such an
expression to the remote side, the local Var would become a Param with
default collation, meaning the remote Var would in fact control the
comparison collation, because non-default implicit collation overrides
default implicit collation in parse_collate.c.) To fix, be more precise
about what FDW_COLLATE_NONE means: it applies either to a noncollatable
data type or to a collatable type with default collation, if that collation
can't be traced to a remote Var. (When it can, FDW_COLLATE_SAFE is
appropriate.) We were essentially using that interpretation already at
the Var/Const/Param level, but we weren't bubbling it up properly.
An alternative fix would be to introduce a separate FDW_COLLATE_DEFAULT
value to describe the second situation, but that would add more code
without changing the actual behavior, so it didn't seem worthwhile.
Also, since we're clarifying the rule to be that we care about whether
operator/function input collations match, there seems no need to fail
immediately upon seeing a Const/Param/non-foreign-Var with nondefault
collation. We only have to reject if it appears in a collation-sensitive
context (for example, "var IS NOT NULL" is perfectly safe from a collation
standpoint, whatever collation the var has). So just set the state to
UNSAFE rather than failing immediately.
Per report from Jeevan Chalke. This essentially corrects some sloppy
thinking in commit ed3ddf918b59545583a4b374566bc1148e75f593, so back-patch
to 9.3 where that logic appeared.
2015-09-24 18:47:29 +02:00
|
|
|
* Collation rule is same as for Consts and non-foreign Vars.
|
2013-03-14 00:46:31 +01:00
|
|
|
*/
|
Improve handling of collations in contrib/postgres_fdw.
If we have a local Var of say varchar type with default collation, and
we apply a RelabelType to convert that to text with default collation, we
don't want to consider that as creating an FDW_COLLATE_UNSAFE situation.
It should be okay to compare that to a remote Var, so long as the remote
Var determines the comparison collation. (When we actually ship such an
expression to the remote side, the local Var would become a Param with
default collation, meaning the remote Var would in fact control the
comparison collation, because non-default implicit collation overrides
default implicit collation in parse_collate.c.) To fix, be more precise
about what FDW_COLLATE_NONE means: it applies either to a noncollatable
data type or to a collatable type with default collation, if that collation
can't be traced to a remote Var. (When it can, FDW_COLLATE_SAFE is
appropriate.) We were essentially using that interpretation already at
the Var/Const/Param level, but we weren't bubbling it up properly.
An alternative fix would be to introduce a separate FDW_COLLATE_DEFAULT
value to describe the second situation, but that would add more code
without changing the actual behavior, so it didn't seem worthwhile.
Also, since we're clarifying the rule to be that we care about whether
operator/function input collations match, there seems no need to fail
immediately upon seeing a Const/Param/non-foreign-Var with nondefault
collation. We only have to reject if it appears in a collation-sensitive
context (for example, "var IS NOT NULL" is perfectly safe from a collation
standpoint, whatever collation the var has). So just set the state to
UNSAFE rather than failing immediately.
Per report from Jeevan Chalke. This essentially corrects some sloppy
thinking in commit ed3ddf918b59545583a4b374566bc1148e75f593, so back-patch
to 9.3 where that logic appeared.
2015-09-24 18:47:29 +02:00
|
|
|
collation = p->paramcollid;
|
|
|
|
if (collation == InvalidOid ||
|
|
|
|
collation == DEFAULT_COLLATION_OID)
|
|
|
|
state = FDW_COLLATE_NONE;
|
|
|
|
else
|
|
|
|
state = FDW_COLLATE_UNSAFE;
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
|
|
|
break;
|
2019-02-01 16:50:32 +01:00
|
|
|
case T_SubscriptingRef:
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
2019-02-01 16:50:32 +01:00
|
|
|
SubscriptingRef *sr = (SubscriptingRef *) node;
|
2013-02-21 11:26:23 +01:00
|
|
|
|
|
|
|
/* Assignment should not be in restrictions. */
|
2019-02-01 16:50:32 +01:00
|
|
|
if (sr->refassgnexpr != NULL)
|
2013-03-14 00:46:31 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
Support subscripting of arbitrary types, not only arrays.
This patch generalizes the subscripting infrastructure so that any
data type can be subscripted, if it provides a handler function to
define what that means. Traditional variable-length (varlena) arrays
all use array_subscript_handler(), while the existing fixed-length
types that support subscripting use raw_array_subscript_handler().
It's expected that other types that want to use subscripting notation
will define their own handlers. (This patch provides no such new
features, though; it only lays the foundation for them.)
To do this, move the parser's semantic processing of subscripts
(including coercion to whatever data type is required) into a
method callback supplied by the handler. On the execution side,
replace the ExecEvalSubscriptingRef* layer of functions with direct
calls to callback-supplied execution routines. (Thus, essentially
no new run-time overhead should be caused by this patch. Indeed,
there is room to remove some overhead by supplying specialized
execution routines. This patch does a little bit in that line,
but more could be done.)
Additional work is required here and there to remove formerly
hard-wired assumptions about the result type, collation, etc
of a SubscriptingRef expression node; and to remove assumptions
that the subscript values must be integers.
One useful side-effect of this is that we now have a less squishy
mechanism for identifying whether a data type is a "true" array:
instead of wiring in weird rules about typlen, we can look to see
if pg_type.typsubscript == F_ARRAY_SUBSCRIPT_HANDLER. For this
to be bulletproof, we have to forbid user-defined types from using
that handler directly; but there seems no good reason for them to
do so.
This patch also removes assumptions that the number of subscripts
is limited to MAXDIM (6), or indeed has any hard-wired limit.
That limit still applies to types handled by array_subscript_handler
or raw_array_subscript_handler, but to discourage other dependencies
on this constant, I've moved it from c.h to utils/array.h.
Dmitry Dolgov, reviewed at various times by Tom Lane, Arthur Zakirov,
Peter Eisentraut, Pavel Stehule
Discussion: https://postgr.es/m/CA+q6zcVDuGBv=M0FqBYX8DPebS3F_0KQ6OVFobGJPM507_SZ_w@mail.gmail.com
Discussion: https://postgr.es/m/CA+q6zcVovR+XY4mfk-7oNk-rF91gH0PebnNfuUjuuDsyHjOcVA@mail.gmail.com
2020-12-09 18:40:37 +01:00
|
|
|
* Recurse into the remaining subexpressions. The container
|
|
|
|
* subscripts will not affect collation of the SubscriptingRef
|
|
|
|
* result, so do those first and reset inner_cxt afterwards.
|
2013-03-14 00:46:31 +01:00
|
|
|
*/
|
2019-02-01 16:50:32 +01:00
|
|
|
if (!foreign_expr_walker((Node *) sr->refupperindexpr,
|
2021-07-30 19:39:48 +02:00
|
|
|
glob_cxt, &inner_cxt, case_arg_cxt))
|
2013-03-14 00:46:31 +01:00
|
|
|
return false;
|
Support subscripting of arbitrary types, not only arrays.
This patch generalizes the subscripting infrastructure so that any
data type can be subscripted, if it provides a handler function to
define what that means. Traditional variable-length (varlena) arrays
all use array_subscript_handler(), while the existing fixed-length
types that support subscripting use raw_array_subscript_handler().
It's expected that other types that want to use subscripting notation
will define their own handlers. (This patch provides no such new
features, though; it only lays the foundation for them.)
To do this, move the parser's semantic processing of subscripts
(including coercion to whatever data type is required) into a
method callback supplied by the handler. On the execution side,
replace the ExecEvalSubscriptingRef* layer of functions with direct
calls to callback-supplied execution routines. (Thus, essentially
no new run-time overhead should be caused by this patch. Indeed,
there is room to remove some overhead by supplying specialized
execution routines. This patch does a little bit in that line,
but more could be done.)
Additional work is required here and there to remove formerly
hard-wired assumptions about the result type, collation, etc
of a SubscriptingRef expression node; and to remove assumptions
that the subscript values must be integers.
One useful side-effect of this is that we now have a less squishy
mechanism for identifying whether a data type is a "true" array:
instead of wiring in weird rules about typlen, we can look to see
if pg_type.typsubscript == F_ARRAY_SUBSCRIPT_HANDLER. For this
to be bulletproof, we have to forbid user-defined types from using
that handler directly; but there seems no good reason for them to
do so.
This patch also removes assumptions that the number of subscripts
is limited to MAXDIM (6), or indeed has any hard-wired limit.
That limit still applies to types handled by array_subscript_handler
or raw_array_subscript_handler, but to discourage other dependencies
on this constant, I've moved it from c.h to utils/array.h.
Dmitry Dolgov, reviewed at various times by Tom Lane, Arthur Zakirov,
Peter Eisentraut, Pavel Stehule
Discussion: https://postgr.es/m/CA+q6zcVDuGBv=M0FqBYX8DPebS3F_0KQ6OVFobGJPM507_SZ_w@mail.gmail.com
Discussion: https://postgr.es/m/CA+q6zcVovR+XY4mfk-7oNk-rF91gH0PebnNfuUjuuDsyHjOcVA@mail.gmail.com
2020-12-09 18:40:37 +01:00
|
|
|
inner_cxt.collation = InvalidOid;
|
|
|
|
inner_cxt.state = FDW_COLLATE_NONE;
|
2019-02-01 16:50:32 +01:00
|
|
|
if (!foreign_expr_walker((Node *) sr->reflowerindexpr,
|
2021-07-30 19:39:48 +02:00
|
|
|
glob_cxt, &inner_cxt, case_arg_cxt))
|
2013-03-14 00:46:31 +01:00
|
|
|
return false;
|
Support subscripting of arbitrary types, not only arrays.
This patch generalizes the subscripting infrastructure so that any
data type can be subscripted, if it provides a handler function to
define what that means. Traditional variable-length (varlena) arrays
all use array_subscript_handler(), while the existing fixed-length
types that support subscripting use raw_array_subscript_handler().
It's expected that other types that want to use subscripting notation
will define their own handlers. (This patch provides no such new
features, though; it only lays the foundation for them.)
To do this, move the parser's semantic processing of subscripts
(including coercion to whatever data type is required) into a
method callback supplied by the handler. On the execution side,
replace the ExecEvalSubscriptingRef* layer of functions with direct
calls to callback-supplied execution routines. (Thus, essentially
no new run-time overhead should be caused by this patch. Indeed,
there is room to remove some overhead by supplying specialized
execution routines. This patch does a little bit in that line,
but more could be done.)
Additional work is required here and there to remove formerly
hard-wired assumptions about the result type, collation, etc
of a SubscriptingRef expression node; and to remove assumptions
that the subscript values must be integers.
One useful side-effect of this is that we now have a less squishy
mechanism for identifying whether a data type is a "true" array:
instead of wiring in weird rules about typlen, we can look to see
if pg_type.typsubscript == F_ARRAY_SUBSCRIPT_HANDLER. For this
to be bulletproof, we have to forbid user-defined types from using
that handler directly; but there seems no good reason for them to
do so.
This patch also removes assumptions that the number of subscripts
is limited to MAXDIM (6), or indeed has any hard-wired limit.
That limit still applies to types handled by array_subscript_handler
or raw_array_subscript_handler, but to discourage other dependencies
on this constant, I've moved it from c.h to utils/array.h.
Dmitry Dolgov, reviewed at various times by Tom Lane, Arthur Zakirov,
Peter Eisentraut, Pavel Stehule
Discussion: https://postgr.es/m/CA+q6zcVDuGBv=M0FqBYX8DPebS3F_0KQ6OVFobGJPM507_SZ_w@mail.gmail.com
Discussion: https://postgr.es/m/CA+q6zcVovR+XY4mfk-7oNk-rF91gH0PebnNfuUjuuDsyHjOcVA@mail.gmail.com
2020-12-09 18:40:37 +01:00
|
|
|
inner_cxt.collation = InvalidOid;
|
|
|
|
inner_cxt.state = FDW_COLLATE_NONE;
|
2019-02-01 16:50:32 +01:00
|
|
|
if (!foreign_expr_walker((Node *) sr->refexpr,
|
2021-07-30 19:39:48 +02:00
|
|
|
glob_cxt, &inner_cxt, case_arg_cxt))
|
2013-03-14 00:46:31 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
Support subscripting of arbitrary types, not only arrays.
This patch generalizes the subscripting infrastructure so that any
data type can be subscripted, if it provides a handler function to
define what that means. Traditional variable-length (varlena) arrays
all use array_subscript_handler(), while the existing fixed-length
types that support subscripting use raw_array_subscript_handler().
It's expected that other types that want to use subscripting notation
will define their own handlers. (This patch provides no such new
features, though; it only lays the foundation for them.)
To do this, move the parser's semantic processing of subscripts
(including coercion to whatever data type is required) into a
method callback supplied by the handler. On the execution side,
replace the ExecEvalSubscriptingRef* layer of functions with direct
calls to callback-supplied execution routines. (Thus, essentially
no new run-time overhead should be caused by this patch. Indeed,
there is room to remove some overhead by supplying specialized
execution routines. This patch does a little bit in that line,
but more could be done.)
Additional work is required here and there to remove formerly
hard-wired assumptions about the result type, collation, etc
of a SubscriptingRef expression node; and to remove assumptions
that the subscript values must be integers.
One useful side-effect of this is that we now have a less squishy
mechanism for identifying whether a data type is a "true" array:
instead of wiring in weird rules about typlen, we can look to see
if pg_type.typsubscript == F_ARRAY_SUBSCRIPT_HANDLER. For this
to be bulletproof, we have to forbid user-defined types from using
that handler directly; but there seems no good reason for them to
do so.
This patch also removes assumptions that the number of subscripts
is limited to MAXDIM (6), or indeed has any hard-wired limit.
That limit still applies to types handled by array_subscript_handler
or raw_array_subscript_handler, but to discourage other dependencies
on this constant, I've moved it from c.h to utils/array.h.
Dmitry Dolgov, reviewed at various times by Tom Lane, Arthur Zakirov,
Peter Eisentraut, Pavel Stehule
Discussion: https://postgr.es/m/CA+q6zcVDuGBv=M0FqBYX8DPebS3F_0KQ6OVFobGJPM507_SZ_w@mail.gmail.com
Discussion: https://postgr.es/m/CA+q6zcVovR+XY4mfk-7oNk-rF91gH0PebnNfuUjuuDsyHjOcVA@mail.gmail.com
2020-12-09 18:40:37 +01:00
|
|
|
* Container subscripting typically yields same collation as
|
|
|
|
* refexpr's, but in case it doesn't, use same logic as for
|
|
|
|
* function nodes.
|
2013-03-14 00:46:31 +01:00
|
|
|
*/
|
2019-02-01 16:50:32 +01:00
|
|
|
collation = sr->refcollid;
|
2013-03-14 00:46:31 +01:00
|
|
|
if (collation == InvalidOid)
|
|
|
|
state = FDW_COLLATE_NONE;
|
|
|
|
else if (inner_cxt.state == FDW_COLLATE_SAFE &&
|
|
|
|
collation == inner_cxt.collation)
|
|
|
|
state = FDW_COLLATE_SAFE;
|
Improve handling of collations in contrib/postgres_fdw.
If we have a local Var of say varchar type with default collation, and
we apply a RelabelType to convert that to text with default collation, we
don't want to consider that as creating an FDW_COLLATE_UNSAFE situation.
It should be okay to compare that to a remote Var, so long as the remote
Var determines the comparison collation. (When we actually ship such an
expression to the remote side, the local Var would become a Param with
default collation, meaning the remote Var would in fact control the
comparison collation, because non-default implicit collation overrides
default implicit collation in parse_collate.c.) To fix, be more precise
about what FDW_COLLATE_NONE means: it applies either to a noncollatable
data type or to a collatable type with default collation, if that collation
can't be traced to a remote Var. (When it can, FDW_COLLATE_SAFE is
appropriate.) We were essentially using that interpretation already at
the Var/Const/Param level, but we weren't bubbling it up properly.
An alternative fix would be to introduce a separate FDW_COLLATE_DEFAULT
value to describe the second situation, but that would add more code
without changing the actual behavior, so it didn't seem worthwhile.
Also, since we're clarifying the rule to be that we care about whether
operator/function input collations match, there seems no need to fail
immediately upon seeing a Const/Param/non-foreign-Var with nondefault
collation. We only have to reject if it appears in a collation-sensitive
context (for example, "var IS NOT NULL" is perfectly safe from a collation
standpoint, whatever collation the var has). So just set the state to
UNSAFE rather than failing immediately.
Per report from Jeevan Chalke. This essentially corrects some sloppy
thinking in commit ed3ddf918b59545583a4b374566bc1148e75f593, so back-patch
to 9.3 where that logic appeared.
2015-09-24 18:47:29 +02:00
|
|
|
else if (collation == DEFAULT_COLLATION_OID)
|
|
|
|
state = FDW_COLLATE_NONE;
|
2013-03-14 00:46:31 +01:00
|
|
|
else
|
|
|
|
state = FDW_COLLATE_UNSAFE;
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_FuncExpr:
|
|
|
|
{
|
2013-03-14 00:46:31 +01:00
|
|
|
FuncExpr *fe = (FuncExpr *) node;
|
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
/*
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
* If function used by the expression is not shippable, it
|
2013-02-21 11:26:23 +01:00
|
|
|
* can't be sent to remote because it might have incompatible
|
|
|
|
* semantics on remote side.
|
|
|
|
*/
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
if (!is_shippable(fe->funcid, ProcedureRelationId, fpinfo))
|
2013-03-14 00:46:31 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Recurse to input subexpressions.
|
|
|
|
*/
|
|
|
|
if (!foreign_expr_walker((Node *) fe->args,
|
2021-07-30 19:39:48 +02:00
|
|
|
glob_cxt, &inner_cxt, case_arg_cxt))
|
2013-03-14 00:46:31 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If function's input collation is not derived from a foreign
|
|
|
|
* Var, it can't be sent to remote.
|
|
|
|
*/
|
|
|
|
if (fe->inputcollid == InvalidOid)
|
|
|
|
/* OK, inputs are all noncollatable */ ;
|
|
|
|
else if (inner_cxt.state != FDW_COLLATE_SAFE ||
|
|
|
|
fe->inputcollid != inner_cxt.collation)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Detect whether node is introducing a collation not derived
|
2014-05-06 18:12:18 +02:00
|
|
|
* from a foreign Var. (If so, we just mark it unsafe for now
|
2013-03-14 00:46:31 +01:00
|
|
|
* rather than immediately returning false, since the parent
|
|
|
|
* node might not care.)
|
|
|
|
*/
|
|
|
|
collation = fe->funccollid;
|
|
|
|
if (collation == InvalidOid)
|
|
|
|
state = FDW_COLLATE_NONE;
|
|
|
|
else if (inner_cxt.state == FDW_COLLATE_SAFE &&
|
|
|
|
collation == inner_cxt.collation)
|
|
|
|
state = FDW_COLLATE_SAFE;
|
Improve handling of collations in contrib/postgres_fdw.
If we have a local Var of say varchar type with default collation, and
we apply a RelabelType to convert that to text with default collation, we
don't want to consider that as creating an FDW_COLLATE_UNSAFE situation.
It should be okay to compare that to a remote Var, so long as the remote
Var determines the comparison collation. (When we actually ship such an
expression to the remote side, the local Var would become a Param with
default collation, meaning the remote Var would in fact control the
comparison collation, because non-default implicit collation overrides
default implicit collation in parse_collate.c.) To fix, be more precise
about what FDW_COLLATE_NONE means: it applies either to a noncollatable
data type or to a collatable type with default collation, if that collation
can't be traced to a remote Var. (When it can, FDW_COLLATE_SAFE is
appropriate.) We were essentially using that interpretation already at
the Var/Const/Param level, but we weren't bubbling it up properly.
An alternative fix would be to introduce a separate FDW_COLLATE_DEFAULT
value to describe the second situation, but that would add more code
without changing the actual behavior, so it didn't seem worthwhile.
Also, since we're clarifying the rule to be that we care about whether
operator/function input collations match, there seems no need to fail
immediately upon seeing a Const/Param/non-foreign-Var with nondefault
collation. We only have to reject if it appears in a collation-sensitive
context (for example, "var IS NOT NULL" is perfectly safe from a collation
standpoint, whatever collation the var has). So just set the state to
UNSAFE rather than failing immediately.
Per report from Jeevan Chalke. This essentially corrects some sloppy
thinking in commit ed3ddf918b59545583a4b374566bc1148e75f593, so back-patch
to 9.3 where that logic appeared.
2015-09-24 18:47:29 +02:00
|
|
|
else if (collation == DEFAULT_COLLATION_OID)
|
|
|
|
state = FDW_COLLATE_NONE;
|
2013-03-14 00:46:31 +01:00
|
|
|
else
|
|
|
|
state = FDW_COLLATE_UNSAFE;
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_OpExpr:
|
|
|
|
case T_DistinctExpr: /* struct-equivalent to OpExpr */
|
|
|
|
{
|
2013-03-14 00:46:31 +01:00
|
|
|
OpExpr *oe = (OpExpr *) node;
|
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
/*
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
* Similarly, only shippable operators can be sent to remote.
|
|
|
|
* (If the operator is shippable, we assume its underlying
|
|
|
|
* function is too.)
|
2013-02-21 11:26:23 +01:00
|
|
|
*/
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
if (!is_shippable(oe->opno, OperatorRelationId, fpinfo))
|
2013-03-14 00:46:31 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Recurse to input subexpressions.
|
|
|
|
*/
|
|
|
|
if (!foreign_expr_walker((Node *) oe->args,
|
2021-07-30 19:39:48 +02:00
|
|
|
glob_cxt, &inner_cxt, case_arg_cxt))
|
2013-03-14 00:46:31 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If operator's input collation is not derived from a foreign
|
|
|
|
* Var, it can't be sent to remote.
|
|
|
|
*/
|
|
|
|
if (oe->inputcollid == InvalidOid)
|
|
|
|
/* OK, inputs are all noncollatable */ ;
|
|
|
|
else if (inner_cxt.state != FDW_COLLATE_SAFE ||
|
|
|
|
oe->inputcollid != inner_cxt.collation)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Result-collation handling is same as for functions */
|
|
|
|
collation = oe->opcollid;
|
|
|
|
if (collation == InvalidOid)
|
|
|
|
state = FDW_COLLATE_NONE;
|
|
|
|
else if (inner_cxt.state == FDW_COLLATE_SAFE &&
|
|
|
|
collation == inner_cxt.collation)
|
|
|
|
state = FDW_COLLATE_SAFE;
|
Improve handling of collations in contrib/postgres_fdw.
If we have a local Var of say varchar type with default collation, and
we apply a RelabelType to convert that to text with default collation, we
don't want to consider that as creating an FDW_COLLATE_UNSAFE situation.
It should be okay to compare that to a remote Var, so long as the remote
Var determines the comparison collation. (When we actually ship such an
expression to the remote side, the local Var would become a Param with
default collation, meaning the remote Var would in fact control the
comparison collation, because non-default implicit collation overrides
default implicit collation in parse_collate.c.) To fix, be more precise
about what FDW_COLLATE_NONE means: it applies either to a noncollatable
data type or to a collatable type with default collation, if that collation
can't be traced to a remote Var. (When it can, FDW_COLLATE_SAFE is
appropriate.) We were essentially using that interpretation already at
the Var/Const/Param level, but we weren't bubbling it up properly.
An alternative fix would be to introduce a separate FDW_COLLATE_DEFAULT
value to describe the second situation, but that would add more code
without changing the actual behavior, so it didn't seem worthwhile.
Also, since we're clarifying the rule to be that we care about whether
operator/function input collations match, there seems no need to fail
immediately upon seeing a Const/Param/non-foreign-Var with nondefault
collation. We only have to reject if it appears in a collation-sensitive
context (for example, "var IS NOT NULL" is perfectly safe from a collation
standpoint, whatever collation the var has). So just set the state to
UNSAFE rather than failing immediately.
Per report from Jeevan Chalke. This essentially corrects some sloppy
thinking in commit ed3ddf918b59545583a4b374566bc1148e75f593, so back-patch
to 9.3 where that logic appeared.
2015-09-24 18:47:29 +02:00
|
|
|
else if (collation == DEFAULT_COLLATION_OID)
|
|
|
|
state = FDW_COLLATE_NONE;
|
2013-03-14 00:46:31 +01:00
|
|
|
else
|
|
|
|
state = FDW_COLLATE_UNSAFE;
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_ScalarArrayOpExpr:
|
|
|
|
{
|
2013-03-14 00:46:31 +01:00
|
|
|
ScalarArrayOpExpr *oe = (ScalarArrayOpExpr *) node;
|
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
/*
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
* Again, only shippable operators can be sent to remote.
|
2013-02-21 11:26:23 +01:00
|
|
|
*/
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
if (!is_shippable(oe->opno, OperatorRelationId, fpinfo))
|
2013-03-14 00:46:31 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Recurse to input subexpressions.
|
|
|
|
*/
|
|
|
|
if (!foreign_expr_walker((Node *) oe->args,
|
2021-07-30 19:39:48 +02:00
|
|
|
glob_cxt, &inner_cxt, case_arg_cxt))
|
2013-03-14 00:46:31 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If operator's input collation is not derived from a foreign
|
|
|
|
* Var, it can't be sent to remote.
|
|
|
|
*/
|
|
|
|
if (oe->inputcollid == InvalidOid)
|
|
|
|
/* OK, inputs are all noncollatable */ ;
|
|
|
|
else if (inner_cxt.state != FDW_COLLATE_SAFE ||
|
|
|
|
oe->inputcollid != inner_cxt.collation)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Output is always boolean and so noncollatable. */
|
|
|
|
collation = InvalidOid;
|
|
|
|
state = FDW_COLLATE_NONE;
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_RelabelType:
|
2013-03-14 00:46:31 +01:00
|
|
|
{
|
|
|
|
RelabelType *r = (RelabelType *) node;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Recurse to input subexpression.
|
|
|
|
*/
|
|
|
|
if (!foreign_expr_walker((Node *) r->arg,
|
2021-07-30 19:39:48 +02:00
|
|
|
glob_cxt, &inner_cxt, case_arg_cxt))
|
2013-03-14 00:46:31 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* RelabelType must not introduce a collation not derived from
|
Improve handling of collations in contrib/postgres_fdw.
If we have a local Var of say varchar type with default collation, and
we apply a RelabelType to convert that to text with default collation, we
don't want to consider that as creating an FDW_COLLATE_UNSAFE situation.
It should be okay to compare that to a remote Var, so long as the remote
Var determines the comparison collation. (When we actually ship such an
expression to the remote side, the local Var would become a Param with
default collation, meaning the remote Var would in fact control the
comparison collation, because non-default implicit collation overrides
default implicit collation in parse_collate.c.) To fix, be more precise
about what FDW_COLLATE_NONE means: it applies either to a noncollatable
data type or to a collatable type with default collation, if that collation
can't be traced to a remote Var. (When it can, FDW_COLLATE_SAFE is
appropriate.) We were essentially using that interpretation already at
the Var/Const/Param level, but we weren't bubbling it up properly.
An alternative fix would be to introduce a separate FDW_COLLATE_DEFAULT
value to describe the second situation, but that would add more code
without changing the actual behavior, so it didn't seem worthwhile.
Also, since we're clarifying the rule to be that we care about whether
operator/function input collations match, there seems no need to fail
immediately upon seeing a Const/Param/non-foreign-Var with nondefault
collation. We only have to reject if it appears in a collation-sensitive
context (for example, "var IS NOT NULL" is perfectly safe from a collation
standpoint, whatever collation the var has). So just set the state to
UNSAFE rather than failing immediately.
Per report from Jeevan Chalke. This essentially corrects some sloppy
thinking in commit ed3ddf918b59545583a4b374566bc1148e75f593, so back-patch
to 9.3 where that logic appeared.
2015-09-24 18:47:29 +02:00
|
|
|
* an input foreign Var (same logic as for a real function).
|
2013-03-14 00:46:31 +01:00
|
|
|
*/
|
|
|
|
collation = r->resultcollid;
|
|
|
|
if (collation == InvalidOid)
|
|
|
|
state = FDW_COLLATE_NONE;
|
|
|
|
else if (inner_cxt.state == FDW_COLLATE_SAFE &&
|
|
|
|
collation == inner_cxt.collation)
|
|
|
|
state = FDW_COLLATE_SAFE;
|
Improve handling of collations in contrib/postgres_fdw.
If we have a local Var of say varchar type with default collation, and
we apply a RelabelType to convert that to text with default collation, we
don't want to consider that as creating an FDW_COLLATE_UNSAFE situation.
It should be okay to compare that to a remote Var, so long as the remote
Var determines the comparison collation. (When we actually ship such an
expression to the remote side, the local Var would become a Param with
default collation, meaning the remote Var would in fact control the
comparison collation, because non-default implicit collation overrides
default implicit collation in parse_collate.c.) To fix, be more precise
about what FDW_COLLATE_NONE means: it applies either to a noncollatable
data type or to a collatable type with default collation, if that collation
can't be traced to a remote Var. (When it can, FDW_COLLATE_SAFE is
appropriate.) We were essentially using that interpretation already at
the Var/Const/Param level, but we weren't bubbling it up properly.
An alternative fix would be to introduce a separate FDW_COLLATE_DEFAULT
value to describe the second situation, but that would add more code
without changing the actual behavior, so it didn't seem worthwhile.
Also, since we're clarifying the rule to be that we care about whether
operator/function input collations match, there seems no need to fail
immediately upon seeing a Const/Param/non-foreign-Var with nondefault
collation. We only have to reject if it appears in a collation-sensitive
context (for example, "var IS NOT NULL" is perfectly safe from a collation
standpoint, whatever collation the var has). So just set the state to
UNSAFE rather than failing immediately.
Per report from Jeevan Chalke. This essentially corrects some sloppy
thinking in commit ed3ddf918b59545583a4b374566bc1148e75f593, so back-patch
to 9.3 where that logic appeared.
2015-09-24 18:47:29 +02:00
|
|
|
else if (collation == DEFAULT_COLLATION_OID)
|
|
|
|
state = FDW_COLLATE_NONE;
|
2013-03-14 00:46:31 +01:00
|
|
|
else
|
|
|
|
state = FDW_COLLATE_UNSAFE;
|
|
|
|
}
|
|
|
|
break;
|
2013-02-21 11:26:23 +01:00
|
|
|
case T_BoolExpr:
|
2013-03-14 00:46:31 +01:00
|
|
|
{
|
|
|
|
BoolExpr *b = (BoolExpr *) node;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Recurse to input subexpressions.
|
|
|
|
*/
|
|
|
|
if (!foreign_expr_walker((Node *) b->args,
|
2021-07-30 19:39:48 +02:00
|
|
|
glob_cxt, &inner_cxt, case_arg_cxt))
|
2013-03-14 00:46:31 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Output is always boolean and so noncollatable. */
|
|
|
|
collation = InvalidOid;
|
|
|
|
state = FDW_COLLATE_NONE;
|
|
|
|
}
|
|
|
|
break;
|
2013-02-21 11:26:23 +01:00
|
|
|
case T_NullTest:
|
2013-03-14 00:46:31 +01:00
|
|
|
{
|
|
|
|
NullTest *nt = (NullTest *) node;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Recurse to input subexpressions.
|
|
|
|
*/
|
|
|
|
if (!foreign_expr_walker((Node *) nt->arg,
|
2021-07-30 19:39:48 +02:00
|
|
|
glob_cxt, &inner_cxt, case_arg_cxt))
|
2013-03-14 00:46:31 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Output is always boolean and so noncollatable. */
|
|
|
|
collation = InvalidOid;
|
|
|
|
state = FDW_COLLATE_NONE;
|
|
|
|
}
|
|
|
|
break;
|
2021-07-30 19:39:48 +02:00
|
|
|
case T_CaseExpr:
|
|
|
|
{
|
|
|
|
CaseExpr *ce = (CaseExpr *) node;
|
|
|
|
foreign_loc_cxt arg_cxt;
|
|
|
|
foreign_loc_cxt tmp_cxt;
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Recurse to CASE's arg expression, if any. Its collation
|
|
|
|
* has to be saved aside for use while examining CaseTestExprs
|
|
|
|
* within the WHEN expressions.
|
|
|
|
*/
|
|
|
|
arg_cxt.collation = InvalidOid;
|
|
|
|
arg_cxt.state = FDW_COLLATE_NONE;
|
|
|
|
if (ce->arg)
|
|
|
|
{
|
|
|
|
if (!foreign_expr_walker((Node *) ce->arg,
|
|
|
|
glob_cxt, &arg_cxt, case_arg_cxt))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Examine the CaseWhen subexpressions. */
|
|
|
|
foreach(lc, ce->args)
|
|
|
|
{
|
|
|
|
CaseWhen *cw = lfirst_node(CaseWhen, lc);
|
|
|
|
|
|
|
|
if (ce->arg)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* In a CASE-with-arg, the parser should have produced
|
|
|
|
* WHEN clauses of the form "CaseTestExpr = RHS",
|
|
|
|
* possibly with an implicit coercion inserted above
|
|
|
|
* the CaseTestExpr. However in an expression that's
|
|
|
|
* been through the optimizer, the WHEN clause could
|
|
|
|
* be almost anything (since the equality operator
|
|
|
|
* could have been expanded into an inline function).
|
|
|
|
* In such cases forbid pushdown, because
|
|
|
|
* deparseCaseExpr can't handle it.
|
|
|
|
*/
|
|
|
|
Node *whenExpr = (Node *) cw->expr;
|
|
|
|
List *opArgs;
|
|
|
|
|
|
|
|
if (!IsA(whenExpr, OpExpr))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
opArgs = ((OpExpr *) whenExpr)->args;
|
|
|
|
if (list_length(opArgs) != 2 ||
|
|
|
|
!IsA(strip_implicit_coercions(linitial(opArgs)),
|
|
|
|
CaseTestExpr))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Recurse to WHEN expression, passing down the arg info.
|
|
|
|
* Its collation doesn't affect the result (really, it
|
|
|
|
* should be boolean and thus not have a collation).
|
|
|
|
*/
|
|
|
|
tmp_cxt.collation = InvalidOid;
|
|
|
|
tmp_cxt.state = FDW_COLLATE_NONE;
|
|
|
|
if (!foreign_expr_walker((Node *) cw->expr,
|
|
|
|
glob_cxt, &tmp_cxt, &arg_cxt))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Recurse to THEN expression. */
|
|
|
|
if (!foreign_expr_walker((Node *) cw->result,
|
|
|
|
glob_cxt, &inner_cxt, case_arg_cxt))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Recurse to ELSE expression. */
|
|
|
|
if (!foreign_expr_walker((Node *) ce->defresult,
|
|
|
|
glob_cxt, &inner_cxt, case_arg_cxt))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Detect whether node is introducing a collation not derived
|
|
|
|
* from a foreign Var. (If so, we just mark it unsafe for now
|
|
|
|
* rather than immediately returning false, since the parent
|
|
|
|
* node might not care.) This is the same as for function
|
|
|
|
* nodes, except that the input collation is derived from only
|
|
|
|
* the THEN and ELSE subexpressions.
|
|
|
|
*/
|
|
|
|
collation = ce->casecollid;
|
|
|
|
if (collation == InvalidOid)
|
|
|
|
state = FDW_COLLATE_NONE;
|
|
|
|
else if (inner_cxt.state == FDW_COLLATE_SAFE &&
|
|
|
|
collation == inner_cxt.collation)
|
|
|
|
state = FDW_COLLATE_SAFE;
|
|
|
|
else if (collation == DEFAULT_COLLATION_OID)
|
|
|
|
state = FDW_COLLATE_NONE;
|
|
|
|
else
|
|
|
|
state = FDW_COLLATE_UNSAFE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_CaseTestExpr:
|
|
|
|
{
|
|
|
|
CaseTestExpr *c = (CaseTestExpr *) node;
|
|
|
|
|
|
|
|
/* Punt if we seem not to be inside a CASE arg WHEN. */
|
|
|
|
if (!case_arg_cxt)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Otherwise, any nondefault collation attached to the
|
|
|
|
* CaseTestExpr node must be derived from foreign Var(s) in
|
|
|
|
* the CASE arg.
|
|
|
|
*/
|
|
|
|
collation = c->collation;
|
|
|
|
if (collation == InvalidOid)
|
|
|
|
state = FDW_COLLATE_NONE;
|
|
|
|
else if (case_arg_cxt->state == FDW_COLLATE_SAFE &&
|
|
|
|
collation == case_arg_cxt->collation)
|
|
|
|
state = FDW_COLLATE_SAFE;
|
|
|
|
else if (collation == DEFAULT_COLLATION_OID)
|
|
|
|
state = FDW_COLLATE_NONE;
|
|
|
|
else
|
|
|
|
state = FDW_COLLATE_UNSAFE;
|
|
|
|
}
|
|
|
|
break;
|
2013-02-21 11:26:23 +01:00
|
|
|
case T_ArrayExpr:
|
2013-03-14 00:46:31 +01:00
|
|
|
{
|
|
|
|
ArrayExpr *a = (ArrayExpr *) node;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Recurse to input subexpressions.
|
|
|
|
*/
|
|
|
|
if (!foreign_expr_walker((Node *) a->elements,
|
2021-07-30 19:39:48 +02:00
|
|
|
glob_cxt, &inner_cxt, case_arg_cxt))
|
2013-03-14 00:46:31 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ArrayExpr must not introduce a collation not derived from
|
Improve handling of collations in contrib/postgres_fdw.
If we have a local Var of say varchar type with default collation, and
we apply a RelabelType to convert that to text with default collation, we
don't want to consider that as creating an FDW_COLLATE_UNSAFE situation.
It should be okay to compare that to a remote Var, so long as the remote
Var determines the comparison collation. (When we actually ship such an
expression to the remote side, the local Var would become a Param with
default collation, meaning the remote Var would in fact control the
comparison collation, because non-default implicit collation overrides
default implicit collation in parse_collate.c.) To fix, be more precise
about what FDW_COLLATE_NONE means: it applies either to a noncollatable
data type or to a collatable type with default collation, if that collation
can't be traced to a remote Var. (When it can, FDW_COLLATE_SAFE is
appropriate.) We were essentially using that interpretation already at
the Var/Const/Param level, but we weren't bubbling it up properly.
An alternative fix would be to introduce a separate FDW_COLLATE_DEFAULT
value to describe the second situation, but that would add more code
without changing the actual behavior, so it didn't seem worthwhile.
Also, since we're clarifying the rule to be that we care about whether
operator/function input collations match, there seems no need to fail
immediately upon seeing a Const/Param/non-foreign-Var with nondefault
collation. We only have to reject if it appears in a collation-sensitive
context (for example, "var IS NOT NULL" is perfectly safe from a collation
standpoint, whatever collation the var has). So just set the state to
UNSAFE rather than failing immediately.
Per report from Jeevan Chalke. This essentially corrects some sloppy
thinking in commit ed3ddf918b59545583a4b374566bc1148e75f593, so back-patch
to 9.3 where that logic appeared.
2015-09-24 18:47:29 +02:00
|
|
|
* an input foreign Var (same logic as for a function).
|
2013-03-14 00:46:31 +01:00
|
|
|
*/
|
|
|
|
collation = a->array_collid;
|
|
|
|
if (collation == InvalidOid)
|
|
|
|
state = FDW_COLLATE_NONE;
|
|
|
|
else if (inner_cxt.state == FDW_COLLATE_SAFE &&
|
|
|
|
collation == inner_cxt.collation)
|
|
|
|
state = FDW_COLLATE_SAFE;
|
Improve handling of collations in contrib/postgres_fdw.
If we have a local Var of say varchar type with default collation, and
we apply a RelabelType to convert that to text with default collation, we
don't want to consider that as creating an FDW_COLLATE_UNSAFE situation.
It should be okay to compare that to a remote Var, so long as the remote
Var determines the comparison collation. (When we actually ship such an
expression to the remote side, the local Var would become a Param with
default collation, meaning the remote Var would in fact control the
comparison collation, because non-default implicit collation overrides
default implicit collation in parse_collate.c.) To fix, be more precise
about what FDW_COLLATE_NONE means: it applies either to a noncollatable
data type or to a collatable type with default collation, if that collation
can't be traced to a remote Var. (When it can, FDW_COLLATE_SAFE is
appropriate.) We were essentially using that interpretation already at
the Var/Const/Param level, but we weren't bubbling it up properly.
An alternative fix would be to introduce a separate FDW_COLLATE_DEFAULT
value to describe the second situation, but that would add more code
without changing the actual behavior, so it didn't seem worthwhile.
Also, since we're clarifying the rule to be that we care about whether
operator/function input collations match, there seems no need to fail
immediately upon seeing a Const/Param/non-foreign-Var with nondefault
collation. We only have to reject if it appears in a collation-sensitive
context (for example, "var IS NOT NULL" is perfectly safe from a collation
standpoint, whatever collation the var has). So just set the state to
UNSAFE rather than failing immediately.
Per report from Jeevan Chalke. This essentially corrects some sloppy
thinking in commit ed3ddf918b59545583a4b374566bc1148e75f593, so back-patch
to 9.3 where that logic appeared.
2015-09-24 18:47:29 +02:00
|
|
|
else if (collation == DEFAULT_COLLATION_OID)
|
|
|
|
state = FDW_COLLATE_NONE;
|
2013-03-14 00:46:31 +01:00
|
|
|
else
|
|
|
|
state = FDW_COLLATE_UNSAFE;
|
|
|
|
}
|
2013-02-21 11:26:23 +01:00
|
|
|
break;
|
|
|
|
case T_List:
|
2013-03-14 00:46:31 +01:00
|
|
|
{
|
|
|
|
List *l = (List *) node;
|
|
|
|
ListCell *lc;
|
2013-02-21 11:26:23 +01:00
|
|
|
|
2013-03-14 00:46:31 +01:00
|
|
|
/*
|
|
|
|
* Recurse to component subexpressions.
|
|
|
|
*/
|
|
|
|
foreach(lc, l)
|
|
|
|
{
|
|
|
|
if (!foreign_expr_walker((Node *) lfirst(lc),
|
2021-07-30 19:39:48 +02:00
|
|
|
glob_cxt, &inner_cxt, case_arg_cxt))
|
2013-03-14 00:46:31 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When processing a list, collation state just bubbles up
|
|
|
|
* from the list elements.
|
|
|
|
*/
|
|
|
|
collation = inner_cxt.collation;
|
|
|
|
state = inner_cxt.state;
|
|
|
|
|
|
|
|
/* Don't apply exprType() to the list. */
|
|
|
|
check_type = false;
|
|
|
|
}
|
2013-02-21 11:26:23 +01:00
|
|
|
break;
|
2016-10-21 15:54:29 +02:00
|
|
|
case T_Aggref:
|
|
|
|
{
|
|
|
|
Aggref *agg = (Aggref *) node;
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
/* Not safe to pushdown when not in grouping context */
|
Abstract logic to allow for multiple kinds of child rels.
Currently, the only type of child relation is an "other member rel",
which is the child of a baserel, but in the future joins and even
upper relations may have child rels. To facilitate that, introduce
macros that test to test for particular RelOptKind values, and use
them in various places where they help to clarify the sense of a test.
(For example, a test may allow RELOPT_OTHER_MEMBER_REL either because
it intends to allow child rels, or because it intends to allow simple
rels.)
Also, remove find_childrel_top_parent, which will not work for a
child rel that is not a baserel. Instead, add a new RelOptInfo
member top_parent_relids to track the same kind of information in a
more generic manner.
Ashutosh Bapat, slightly tweaked by me. Review and testing of the
patch set from which this was taken by Rajkumar Raghuwanshi and Rafia
Sabih.
Discussion: http://postgr.es/m/CA+TgmoagTnF2yqR3PT2rv=om=wJiZ4-A+ATwdnriTGku1CLYxA@mail.gmail.com
2017-04-04 04:41:31 +02:00
|
|
|
if (!IS_UPPER_REL(glob_cxt->foreignrel))
|
2016-10-21 15:54:29 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Only non-split aggregates are pushable. */
|
|
|
|
if (agg->aggsplit != AGGSPLIT_SIMPLE)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* As usual, it must be shippable. */
|
|
|
|
if (!is_shippable(agg->aggfnoid, ProcedureRelationId, fpinfo))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Recurse to input args. aggdirectargs, aggorder and
|
|
|
|
* aggdistinct are all present in args, so no need to check
|
|
|
|
* their shippability explicitly.
|
|
|
|
*/
|
|
|
|
foreach(lc, agg->args)
|
|
|
|
{
|
|
|
|
Node *n = (Node *) lfirst(lc);
|
|
|
|
|
|
|
|
/* If TargetEntry, extract the expression from it */
|
|
|
|
if (IsA(n, TargetEntry))
|
|
|
|
{
|
|
|
|
TargetEntry *tle = (TargetEntry *) n;
|
|
|
|
|
|
|
|
n = (Node *) tle->expr;
|
|
|
|
}
|
|
|
|
|
2021-07-30 19:39:48 +02:00
|
|
|
if (!foreign_expr_walker(n,
|
|
|
|
glob_cxt, &inner_cxt, case_arg_cxt))
|
2016-10-21 15:54:29 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For aggorder elements, check whether the sort operator, if
|
|
|
|
* specified, is shippable or not.
|
|
|
|
*/
|
|
|
|
if (agg->aggorder)
|
|
|
|
{
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
foreach(lc, agg->aggorder)
|
|
|
|
{
|
|
|
|
SortGroupClause *srt = (SortGroupClause *) lfirst(lc);
|
|
|
|
Oid sortcoltype;
|
|
|
|
TypeCacheEntry *typentry;
|
|
|
|
TargetEntry *tle;
|
|
|
|
|
|
|
|
tle = get_sortgroupref_tle(srt->tleSortGroupRef,
|
|
|
|
agg->args);
|
|
|
|
sortcoltype = exprType((Node *) tle->expr);
|
|
|
|
typentry = lookup_type_cache(sortcoltype,
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
|
2016-10-21 15:54:29 +02:00
|
|
|
/* Check shippability of non-default sort operator. */
|
|
|
|
if (srt->sortop != typentry->lt_opr &&
|
|
|
|
srt->sortop != typentry->gt_opr &&
|
|
|
|
!is_shippable(srt->sortop, OperatorRelationId,
|
|
|
|
fpinfo))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check aggregate filter */
|
|
|
|
if (!foreign_expr_walker((Node *) agg->aggfilter,
|
2021-07-30 19:39:48 +02:00
|
|
|
glob_cxt, &inner_cxt, case_arg_cxt))
|
2016-10-21 15:54:29 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If aggregate's input collation is not derived from a
|
|
|
|
* foreign Var, it can't be sent to remote.
|
|
|
|
*/
|
|
|
|
if (agg->inputcollid == InvalidOid)
|
|
|
|
/* OK, inputs are all noncollatable */ ;
|
|
|
|
else if (inner_cxt.state != FDW_COLLATE_SAFE ||
|
|
|
|
agg->inputcollid != inner_cxt.collation)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Detect whether node is introducing a collation not derived
|
|
|
|
* from a foreign Var. (If so, we just mark it unsafe for now
|
|
|
|
* rather than immediately returning false, since the parent
|
|
|
|
* node might not care.)
|
|
|
|
*/
|
|
|
|
collation = agg->aggcollid;
|
|
|
|
if (collation == InvalidOid)
|
|
|
|
state = FDW_COLLATE_NONE;
|
|
|
|
else if (inner_cxt.state == FDW_COLLATE_SAFE &&
|
|
|
|
collation == inner_cxt.collation)
|
|
|
|
state = FDW_COLLATE_SAFE;
|
|
|
|
else if (collation == DEFAULT_COLLATION_OID)
|
|
|
|
state = FDW_COLLATE_NONE;
|
|
|
|
else
|
|
|
|
state = FDW_COLLATE_UNSAFE;
|
|
|
|
}
|
|
|
|
break;
|
2013-02-21 11:26:23 +01:00
|
|
|
default:
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If it's anything else, assume it's unsafe. This list can be
|
|
|
|
* expanded later, but don't forget to add deparse support below.
|
|
|
|
*/
|
2013-03-14 00:46:31 +01:00
|
|
|
return false;
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
* If result type of given expression is not shippable, it can't be sent
|
|
|
|
* to remote because it might have incompatible semantics on remote side.
|
2013-02-21 11:26:23 +01:00
|
|
|
*/
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
if (check_type && !is_shippable(exprType(node), TypeRelationId, fpinfo))
|
2013-03-14 00:46:31 +01:00
|
|
|
return false;
|
2013-02-21 11:26:23 +01:00
|
|
|
|
2013-03-14 00:46:31 +01:00
|
|
|
/*
|
|
|
|
* Now, merge my collation information into my parent's state.
|
|
|
|
*/
|
|
|
|
if (state > outer_cxt->state)
|
|
|
|
{
|
|
|
|
/* Override previous parent state */
|
|
|
|
outer_cxt->collation = collation;
|
|
|
|
outer_cxt->state = state;
|
|
|
|
}
|
|
|
|
else if (state == outer_cxt->state)
|
|
|
|
{
|
|
|
|
/* Merge, or detect error if there's a collation conflict */
|
|
|
|
switch (state)
|
|
|
|
{
|
|
|
|
case FDW_COLLATE_NONE:
|
|
|
|
/* Nothing + nothing is still nothing */
|
|
|
|
break;
|
|
|
|
case FDW_COLLATE_SAFE:
|
|
|
|
if (collation != outer_cxt->collation)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Non-default collation always beats default.
|
|
|
|
*/
|
|
|
|
if (outer_cxt->collation == DEFAULT_COLLATION_OID)
|
|
|
|
{
|
|
|
|
/* Override previous parent state */
|
|
|
|
outer_cxt->collation = collation;
|
|
|
|
}
|
|
|
|
else if (collation != DEFAULT_COLLATION_OID)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Conflict; show state as indeterminate. We don't
|
|
|
|
* want to "return false" right away, since parent
|
|
|
|
* node might not care about collation.
|
|
|
|
*/
|
|
|
|
outer_cxt->state = FDW_COLLATE_UNSAFE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case FDW_COLLATE_UNSAFE:
|
|
|
|
/* We're still conflicted ... */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* It looks OK */
|
|
|
|
return true;
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
|
|
|
|
Avoid postgres_fdw crash for a targetlist entry that's just a Param.
foreign_grouping_ok() is willing to put fairly arbitrary expressions into
the targetlist of a remote SELECT that's doing grouping or aggregation on
the remote side, including expressions that have no foreign component to
them at all. This is possibly a bit dubious from an efficiency standpoint;
but it rises to the level of a crash-causing bug if the expression is just
a Param or non-foreign Var. In that case, the expression will necessarily
also appear in the fdw_exprs list of values we need to send to the remote
server, and then setrefs.c's set_foreignscan_references will mistakenly
replace the fdw_exprs entry with a Var referencing the targetlist result.
The root cause of this problem is bad design in commit e7cb7ee14: it put
logic into set_foreignscan_references that IMV is postgres_fdw-specific,
and yet this bug shows that it isn't postgres_fdw-specific enough. The
transformation being done on fdw_exprs assumes that fdw_exprs is to be
evaluated with the fdw_scan_tlist as input, which is not how postgres_fdw
uses it; yet it could be the right thing for some other FDW. (In the
bigger picture, setrefs.c has no business assuming this for the other
expression fields of a ForeignScan either.)
The right fix therefore would be to expand the FDW API so that the
FDW could inform setrefs.c how it intends to evaluate these various
expressions. We can't change that in the back branches though, and we
also can't just summarily change setrefs.c's behavior there, or we're
likely to break external FDWs.
As a stopgap, therefore, hack up postgres_fdw so that it won't attempt
to send targetlist entries that look exactly like the fdw_exprs entries
they'd produce. In most cases this actually produces a superior plan,
IMO, with less data needing to be transmitted and returned; so we probably
ought to think harder about whether we should ship tlist expressions at
all when they don't contain any foreign Vars or Aggs. But that's an
optimization not a bug fix so I left it for later. One case where this
produces an inferior plan is where the expression in question is actually
a GROUP BY expression: then the restriction prevents us from using remote
grouping. It might be possible to work around that (since that would
reduce to group-by-a-constant on the remote side); but it seems like a
pretty unlikely corner case, so I'm not sure it's worth expending effort
solely to improve that. In any case the right long-term answer is to fix
the API as sketched above, and then revert this hack.
Per bug #15781 from Sean Johnston. Back-patch to v10 where the problem
was introduced.
Discussion: https://postgr.es/m/15781-2601b1002bad087c@postgresql.org
2019-04-27 19:15:54 +02:00
|
|
|
/*
|
|
|
|
* Returns true if given expr is something we'd have to send the value of
|
|
|
|
* to the foreign server.
|
|
|
|
*
|
|
|
|
* This should return true when the expression is a shippable node that
|
|
|
|
* deparseExpr would add to context->params_list. Note that we don't care
|
|
|
|
* if the expression *contains* such a node, only whether one appears at top
|
|
|
|
* level. We need this to detect cases where setrefs.c would recognize a
|
|
|
|
* false match between an fdw_exprs item (which came from the params_list)
|
|
|
|
* and an entry in fdw_scan_tlist (which we're considering putting the given
|
|
|
|
* expression into).
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
is_foreign_param(PlannerInfo *root,
|
|
|
|
RelOptInfo *baserel,
|
|
|
|
Expr *expr)
|
|
|
|
{
|
|
|
|
if (expr == NULL)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
switch (nodeTag(expr))
|
|
|
|
{
|
|
|
|
case T_Var:
|
|
|
|
{
|
|
|
|
/* It would have to be sent unless it's a foreign Var */
|
|
|
|
Var *var = (Var *) expr;
|
|
|
|
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) (baserel->fdw_private);
|
|
|
|
Relids relids;
|
|
|
|
|
|
|
|
if (IS_UPPER_REL(baserel))
|
|
|
|
relids = fpinfo->outerrel->relids;
|
|
|
|
else
|
|
|
|
relids = baserel->relids;
|
|
|
|
|
|
|
|
if (bms_is_member(var->varno, relids) && var->varlevelsup == 0)
|
|
|
|
return false; /* foreign Var, so not a param */
|
|
|
|
else
|
|
|
|
return true; /* it'd have to be a param */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case T_Param:
|
|
|
|
/* Params always have to be sent to the foreign server */
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
/*
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
* Convert type OID + typmod info into a type name we can ship to the remote
|
|
|
|
* server. Someplace else had better have verified that this type name is
|
|
|
|
* expected to be known on the remote end.
|
2013-02-22 12:03:46 +01:00
|
|
|
*
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
* This is almost just format_type_with_typemod(), except that if left to its
|
|
|
|
* own devices, that function will make schema-qualification decisions based
|
|
|
|
* on the local search_path, which is wrong. We must schema-qualify all
|
|
|
|
* type names that are not in pg_catalog. We assume here that built-in types
|
|
|
|
* are all in pg_catalog and need not be qualified; otherwise, qualify.
|
2013-02-21 11:26:23 +01:00
|
|
|
*/
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
static char *
|
|
|
|
deparse_type_name(Oid type_oid, int32 typemod)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
2018-03-01 17:37:46 +01:00
|
|
|
bits16 flags = FORMAT_TYPE_TYPEMOD_GIVEN;
|
2018-02-17 23:02:15 +01:00
|
|
|
|
|
|
|
if (!is_builtin(type_oid))
|
|
|
|
flags |= FORMAT_TYPE_FORCE_QUALIFY;
|
|
|
|
|
|
|
|
return format_type_extended(type_oid, typemod, flags);
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
|
|
|
|
2016-01-30 16:32:38 +01:00
|
|
|
/*
|
2016-02-09 20:00:50 +01:00
|
|
|
* Build the targetlist for given relation to be deparsed as SELECT clause.
|
2016-01-30 16:32:38 +01:00
|
|
|
*
|
2016-02-09 20:00:50 +01:00
|
|
|
* The output targetlist contains the columns that need to be fetched from the
|
2016-10-21 15:54:29 +02:00
|
|
|
* foreign server for the given relation. If foreignrel is an upper relation,
|
2016-11-01 21:21:29 +01:00
|
|
|
* then the output targetlist can also contain expressions to be evaluated on
|
2016-10-21 15:54:29 +02:00
|
|
|
* foreign server.
|
2016-02-09 20:00:50 +01:00
|
|
|
*/
|
|
|
|
List *
|
|
|
|
build_tlist_to_deparse(RelOptInfo *foreignrel)
|
|
|
|
{
|
|
|
|
List *tlist = NIL;
|
|
|
|
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
|
Handle restriction clause lists more uniformly in postgres_fdw.
Clauses in the lists retained by postgres_fdw during planning were
sometimes bare boolean clauses, sometimes RestrictInfos, and sometimes
a mixture of the two in the same list. The comment about that situation
didn't come close to telling the full truth, either. Aside from being
confusing, this had a couple of bad practical consequences:
* waste of planning cycles due to inability to cache per-clause selectivity
and cost estimates;
* sometimes, RestrictInfos would sneak into the fdw_private list of a
finished Plan node, causing failures if, for example, we tried to ship
the Plan tree to a parallel worker.
(It may well be that it's a bug in the parallel-query logic that we
would ever try to ship such a plan to a parallel worker, but in any
case this deserves to be cleaned up.)
To fix, rearrange so that clause lists in PgFdwRelationInfo are always
lists of RestrictInfos, and then strip the RestrictInfos at the last
minute when making a Plan node. In passing do a bit of refactoring and
comment cleanup in postgresGetForeignPlan and foreign_join_ok.
Although the messiness here dates back at least to 9.6, there's no evidence
that it causes anything worse than wasted planning cycles in 9.6, so no
back-patch for now.
Per fuzz testing by Andreas Seltenreich.
Tom Lane and Ashutosh Bapat
Discussion: https://postgr.es/m/87tw5x4vcu.fsf@credativ.de
2017-04-11 17:58:59 +02:00
|
|
|
ListCell *lc;
|
2016-02-09 20:00:50 +01:00
|
|
|
|
2016-10-21 15:54:29 +02:00
|
|
|
/*
|
|
|
|
* For an upper relation, we have already built the target list while
|
|
|
|
* checking shippability, so just return that.
|
|
|
|
*/
|
Abstract logic to allow for multiple kinds of child rels.
Currently, the only type of child relation is an "other member rel",
which is the child of a baserel, but in the future joins and even
upper relations may have child rels. To facilitate that, introduce
macros that test to test for particular RelOptKind values, and use
them in various places where they help to clarify the sense of a test.
(For example, a test may allow RELOPT_OTHER_MEMBER_REL either because
it intends to allow child rels, or because it intends to allow simple
rels.)
Also, remove find_childrel_top_parent, which will not work for a
child rel that is not a baserel. Instead, add a new RelOptInfo
member top_parent_relids to track the same kind of information in a
more generic manner.
Ashutosh Bapat, slightly tweaked by me. Review and testing of the
patch set from which this was taken by Rajkumar Raghuwanshi and Rafia
Sabih.
Discussion: http://postgr.es/m/CA+TgmoagTnF2yqR3PT2rv=om=wJiZ4-A+ATwdnriTGku1CLYxA@mail.gmail.com
2017-04-04 04:41:31 +02:00
|
|
|
if (IS_UPPER_REL(foreignrel))
|
2016-10-21 15:54:29 +02:00
|
|
|
return fpinfo->grouped_tlist;
|
|
|
|
|
2016-02-09 20:00:50 +01:00
|
|
|
/*
|
2016-03-14 21:59:59 +01:00
|
|
|
* We require columns specified in foreignrel->reltarget->exprs and those
|
2016-02-09 20:00:50 +01:00
|
|
|
* required for evaluating the local conditions.
|
|
|
|
*/
|
2016-06-14 17:48:27 +02:00
|
|
|
tlist = add_to_flat_tlist(tlist,
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
pull_var_clause((Node *) foreignrel->reltarget->exprs,
|
|
|
|
PVC_RECURSE_PLACEHOLDERS));
|
Handle restriction clause lists more uniformly in postgres_fdw.
Clauses in the lists retained by postgres_fdw during planning were
sometimes bare boolean clauses, sometimes RestrictInfos, and sometimes
a mixture of the two in the same list. The comment about that situation
didn't come close to telling the full truth, either. Aside from being
confusing, this had a couple of bad practical consequences:
* waste of planning cycles due to inability to cache per-clause selectivity
and cost estimates;
* sometimes, RestrictInfos would sneak into the fdw_private list of a
finished Plan node, causing failures if, for example, we tried to ship
the Plan tree to a parallel worker.
(It may well be that it's a bug in the parallel-query logic that we
would ever try to ship such a plan to a parallel worker, but in any
case this deserves to be cleaned up.)
To fix, rearrange so that clause lists in PgFdwRelationInfo are always
lists of RestrictInfos, and then strip the RestrictInfos at the last
minute when making a Plan node. In passing do a bit of refactoring and
comment cleanup in postgresGetForeignPlan and foreign_join_ok.
Although the messiness here dates back at least to 9.6, there's no evidence
that it causes anything worse than wasted planning cycles in 9.6, so no
back-patch for now.
Per fuzz testing by Andreas Seltenreich.
Tom Lane and Ashutosh Bapat
Discussion: https://postgr.es/m/87tw5x4vcu.fsf@credativ.de
2017-04-11 17:58:59 +02:00
|
|
|
foreach(lc, fpinfo->local_conds)
|
|
|
|
{
|
|
|
|
RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
|
|
|
|
|
|
|
|
tlist = add_to_flat_tlist(tlist,
|
|
|
|
pull_var_clause((Node *) rinfo->clause,
|
|
|
|
PVC_RECURSE_PLACEHOLDERS));
|
|
|
|
}
|
2016-02-09 20:00:50 +01:00
|
|
|
|
|
|
|
return tlist;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Deparse SELECT statement for given relation into buf.
|
2016-01-30 16:32:38 +01:00
|
|
|
*
|
2016-02-09 20:00:50 +01:00
|
|
|
* tlist contains the list of desired columns to be fetched from foreign server.
|
|
|
|
* For a base relation fpinfo->attrs_used is used to construct SELECT clause,
|
|
|
|
* hence the tlist is ignored for a base relation.
|
2016-01-30 16:32:38 +01:00
|
|
|
*
|
2016-10-21 15:54:29 +02:00
|
|
|
* remote_conds is the list of conditions to be deparsed into the WHERE clause
|
|
|
|
* (or, in the case of upper relations, into the HAVING clause).
|
2016-01-30 16:32:38 +01:00
|
|
|
*
|
|
|
|
* If params_list is not NULL, it receives a list of Params and other-relation
|
|
|
|
* Vars used in the clauses; these values must be transmitted to the remote
|
|
|
|
* server as parameter values.
|
|
|
|
*
|
|
|
|
* If params_list is NULL, we're generating the query for EXPLAIN purposes,
|
|
|
|
* so Params and other-relation Vars should be replaced by dummy values.
|
2016-02-09 20:00:50 +01:00
|
|
|
*
|
|
|
|
* pathkeys is the list of pathkeys to order the result by.
|
|
|
|
*
|
2017-03-16 18:34:59 +01:00
|
|
|
* is_subquery is the flag to indicate whether to deparse the specified
|
|
|
|
* relation as a subquery.
|
|
|
|
*
|
2016-02-09 20:00:50 +01:00
|
|
|
* List of columns selected is returned in retrieved_attrs.
|
2016-01-30 16:32:38 +01:00
|
|
|
*/
|
2018-02-19 18:07:44 +01:00
|
|
|
void
|
2016-01-30 16:32:38 +01:00
|
|
|
deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *rel,
|
2016-02-09 20:00:50 +01:00
|
|
|
List *tlist, List *remote_conds, List *pathkeys,
|
2019-04-02 13:30:45 +02:00
|
|
|
bool has_final_sort, bool has_limit, bool is_subquery,
|
2019-04-02 12:20:30 +02:00
|
|
|
List **retrieved_attrs, List **params_list)
|
2016-01-30 16:32:38 +01:00
|
|
|
{
|
|
|
|
deparse_expr_cxt context;
|
2016-10-21 15:54:29 +02:00
|
|
|
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
|
|
|
|
List *quals;
|
2016-01-30 16:32:38 +01:00
|
|
|
|
2016-10-21 15:54:29 +02:00
|
|
|
/*
|
|
|
|
* We handle relations for foreign tables, joins between those and upper
|
|
|
|
* relations.
|
|
|
|
*/
|
Abstract logic to allow for multiple kinds of child rels.
Currently, the only type of child relation is an "other member rel",
which is the child of a baserel, but in the future joins and even
upper relations may have child rels. To facilitate that, introduce
macros that test to test for particular RelOptKind values, and use
them in various places where they help to clarify the sense of a test.
(For example, a test may allow RELOPT_OTHER_MEMBER_REL either because
it intends to allow child rels, or because it intends to allow simple
rels.)
Also, remove find_childrel_top_parent, which will not work for a
child rel that is not a baserel. Instead, add a new RelOptInfo
member top_parent_relids to track the same kind of information in a
more generic manner.
Ashutosh Bapat, slightly tweaked by me. Review and testing of the
patch set from which this was taken by Rajkumar Raghuwanshi and Rafia
Sabih.
Discussion: http://postgr.es/m/CA+TgmoagTnF2yqR3PT2rv=om=wJiZ4-A+ATwdnriTGku1CLYxA@mail.gmail.com
2017-04-04 04:41:31 +02:00
|
|
|
Assert(IS_JOIN_REL(rel) || IS_SIMPLE_REL(rel) || IS_UPPER_REL(rel));
|
2016-01-30 16:32:38 +01:00
|
|
|
|
2016-10-21 15:54:29 +02:00
|
|
|
/* Fill portions of context common to upper, join and base relation */
|
2016-01-30 16:32:38 +01:00
|
|
|
context.buf = buf;
|
|
|
|
context.root = root;
|
|
|
|
context.foreignrel = rel;
|
Abstract logic to allow for multiple kinds of child rels.
Currently, the only type of child relation is an "other member rel",
which is the child of a baserel, but in the future joins and even
upper relations may have child rels. To facilitate that, introduce
macros that test to test for particular RelOptKind values, and use
them in various places where they help to clarify the sense of a test.
(For example, a test may allow RELOPT_OTHER_MEMBER_REL either because
it intends to allow child rels, or because it intends to allow simple
rels.)
Also, remove find_childrel_top_parent, which will not work for a
child rel that is not a baserel. Instead, add a new RelOptInfo
member top_parent_relids to track the same kind of information in a
more generic manner.
Ashutosh Bapat, slightly tweaked by me. Review and testing of the
patch set from which this was taken by Rajkumar Raghuwanshi and Rafia
Sabih.
Discussion: http://postgr.es/m/CA+TgmoagTnF2yqR3PT2rv=om=wJiZ4-A+ATwdnriTGku1CLYxA@mail.gmail.com
2017-04-04 04:41:31 +02:00
|
|
|
context.scanrel = IS_UPPER_REL(rel) ? fpinfo->outerrel : rel;
|
2016-01-30 16:32:38 +01:00
|
|
|
context.params_list = params_list;
|
|
|
|
|
2016-10-21 15:54:29 +02:00
|
|
|
/* Construct SELECT clause */
|
2017-03-16 18:34:59 +01:00
|
|
|
deparseSelectSql(tlist, is_subquery, retrieved_attrs, &context);
|
2016-01-30 16:32:38 +01:00
|
|
|
|
2016-02-09 20:00:50 +01:00
|
|
|
/*
|
2016-10-21 15:54:29 +02:00
|
|
|
* For upper relations, the WHERE clause is built from the remote
|
|
|
|
* conditions of the underlying scan relation; otherwise, we can use the
|
|
|
|
* supplied list of remote conditions directly.
|
2016-02-09 20:00:50 +01:00
|
|
|
*/
|
Abstract logic to allow for multiple kinds of child rels.
Currently, the only type of child relation is an "other member rel",
which is the child of a baserel, but in the future joins and even
upper relations may have child rels. To facilitate that, introduce
macros that test to test for particular RelOptKind values, and use
them in various places where they help to clarify the sense of a test.
(For example, a test may allow RELOPT_OTHER_MEMBER_REL either because
it intends to allow child rels, or because it intends to allow simple
rels.)
Also, remove find_childrel_top_parent, which will not work for a
child rel that is not a baserel. Instead, add a new RelOptInfo
member top_parent_relids to track the same kind of information in a
more generic manner.
Ashutosh Bapat, slightly tweaked by me. Review and testing of the
patch set from which this was taken by Rajkumar Raghuwanshi and Rafia
Sabih.
Discussion: http://postgr.es/m/CA+TgmoagTnF2yqR3PT2rv=om=wJiZ4-A+ATwdnriTGku1CLYxA@mail.gmail.com
2017-04-04 04:41:31 +02:00
|
|
|
if (IS_UPPER_REL(rel))
|
2016-02-09 20:00:50 +01:00
|
|
|
{
|
2016-10-21 15:54:29 +02:00
|
|
|
PgFdwRelationInfo *ofpinfo;
|
|
|
|
|
|
|
|
ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
|
|
|
|
quals = ofpinfo->remote_conds;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
quals = remote_conds;
|
|
|
|
|
|
|
|
/* Construct FROM and WHERE clauses */
|
|
|
|
deparseFromExpr(quals, &context);
|
|
|
|
|
Abstract logic to allow for multiple kinds of child rels.
Currently, the only type of child relation is an "other member rel",
which is the child of a baserel, but in the future joins and even
upper relations may have child rels. To facilitate that, introduce
macros that test to test for particular RelOptKind values, and use
them in various places where they help to clarify the sense of a test.
(For example, a test may allow RELOPT_OTHER_MEMBER_REL either because
it intends to allow child rels, or because it intends to allow simple
rels.)
Also, remove find_childrel_top_parent, which will not work for a
child rel that is not a baserel. Instead, add a new RelOptInfo
member top_parent_relids to track the same kind of information in a
more generic manner.
Ashutosh Bapat, slightly tweaked by me. Review and testing of the
patch set from which this was taken by Rajkumar Raghuwanshi and Rafia
Sabih.
Discussion: http://postgr.es/m/CA+TgmoagTnF2yqR3PT2rv=om=wJiZ4-A+ATwdnriTGku1CLYxA@mail.gmail.com
2017-04-04 04:41:31 +02:00
|
|
|
if (IS_UPPER_REL(rel))
|
2016-10-21 15:54:29 +02:00
|
|
|
{
|
|
|
|
/* Append GROUP BY clause */
|
|
|
|
appendGroupByClause(tlist, &context);
|
|
|
|
|
|
|
|
/* Append HAVING clause */
|
|
|
|
if (remote_conds)
|
|
|
|
{
|
2017-08-16 05:34:39 +02:00
|
|
|
appendStringInfoString(buf, " HAVING ");
|
2016-10-21 15:54:29 +02:00
|
|
|
appendConditions(remote_conds, &context);
|
|
|
|
}
|
2016-02-09 20:00:50 +01:00
|
|
|
}
|
2016-01-30 16:32:38 +01:00
|
|
|
|
|
|
|
/* Add ORDER BY clause if we found any useful pathkeys */
|
|
|
|
if (pathkeys)
|
2019-04-02 12:20:30 +02:00
|
|
|
appendOrderByClause(pathkeys, has_final_sort, &context);
|
2016-01-30 16:32:38 +01:00
|
|
|
|
2019-04-02 13:30:45 +02:00
|
|
|
/* Add LIMIT clause if necessary */
|
|
|
|
if (has_limit)
|
|
|
|
appendLimitClause(&context);
|
|
|
|
|
2016-01-30 16:32:38 +01:00
|
|
|
/* Add any necessary FOR UPDATE/SHARE. */
|
|
|
|
deparseLockingClause(&context);
|
|
|
|
}
|
2013-02-21 11:26:23 +01:00
|
|
|
|
|
|
|
/*
|
2013-03-10 19:14:53 +01:00
|
|
|
* Construct a simple SELECT statement that retrieves desired columns
|
2014-05-06 18:12:18 +02:00
|
|
|
* of the specified foreign table, and append it to "buf". The output
|
2016-10-21 15:54:29 +02:00
|
|
|
* contains just "SELECT ... ".
|
2013-03-22 05:31:11 +01:00
|
|
|
*
|
|
|
|
* We also create an integer List of the columns being retrieved, which is
|
2017-03-16 18:34:59 +01:00
|
|
|
* returned to *retrieved_attrs, unless we deparse the specified relation
|
|
|
|
* as a subquery.
|
2016-02-09 20:00:50 +01:00
|
|
|
*
|
2017-03-16 18:34:59 +01:00
|
|
|
* tlist is the list of desired columns. is_subquery is the flag to
|
|
|
|
* indicate whether to deparse the specified relation as a subquery.
|
|
|
|
* Read prologue of deparseSelectStmtForRel() for details.
|
2013-02-21 11:26:23 +01:00
|
|
|
*/
|
2016-02-06 18:21:14 +01:00
|
|
|
static void
|
2017-03-16 18:34:59 +01:00
|
|
|
deparseSelectSql(List *tlist, bool is_subquery, List **retrieved_attrs,
|
|
|
|
deparse_expr_cxt *context)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
2016-01-30 16:32:38 +01:00
|
|
|
StringInfo buf = context->buf;
|
|
|
|
RelOptInfo *foreignrel = context->foreignrel;
|
|
|
|
PlannerInfo *root = context->root;
|
2016-02-09 20:00:50 +01:00
|
|
|
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
|
2013-02-21 11:26:23 +01:00
|
|
|
|
2013-03-10 19:14:53 +01:00
|
|
|
/*
|
|
|
|
* Construct SELECT list
|
|
|
|
*/
|
|
|
|
appendStringInfoString(buf, "SELECT ");
|
2016-02-09 20:00:50 +01:00
|
|
|
|
2017-03-16 18:34:59 +01:00
|
|
|
if (is_subquery)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* For a relation that is deparsed as a subquery, emit expressions
|
2017-05-17 22:31:56 +02:00
|
|
|
* specified in the relation's reltarget. Note that since this is for
|
|
|
|
* the subquery, no need to care about *retrieved_attrs.
|
2017-03-16 18:34:59 +01:00
|
|
|
*/
|
|
|
|
deparseSubqueryTargetList(context);
|
|
|
|
}
|
Abstract logic to allow for multiple kinds of child rels.
Currently, the only type of child relation is an "other member rel",
which is the child of a baserel, but in the future joins and even
upper relations may have child rels. To facilitate that, introduce
macros that test to test for particular RelOptKind values, and use
them in various places where they help to clarify the sense of a test.
(For example, a test may allow RELOPT_OTHER_MEMBER_REL either because
it intends to allow child rels, or because it intends to allow simple
rels.)
Also, remove find_childrel_top_parent, which will not work for a
child rel that is not a baserel. Instead, add a new RelOptInfo
member top_parent_relids to track the same kind of information in a
more generic manner.
Ashutosh Bapat, slightly tweaked by me. Review and testing of the
patch set from which this was taken by Rajkumar Raghuwanshi and Rafia
Sabih.
Discussion: http://postgr.es/m/CA+TgmoagTnF2yqR3PT2rv=om=wJiZ4-A+ATwdnriTGku1CLYxA@mail.gmail.com
2017-04-04 04:41:31 +02:00
|
|
|
else if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
|
2016-02-09 20:00:50 +01:00
|
|
|
{
|
2017-03-16 18:34:59 +01:00
|
|
|
/*
|
|
|
|
* For a join or upper relation the input tlist gives the list of
|
|
|
|
* columns required to be fetched from the foreign server.
|
|
|
|
*/
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
deparseExplicitTargetList(tlist, false, retrieved_attrs, context);
|
2016-02-09 20:00:50 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* For a base relation fpinfo->attrs_used gives the list of columns
|
|
|
|
* required to be fetched from the foreign server.
|
|
|
|
*/
|
|
|
|
RangeTblEntry *rte = planner_rt_fetch(foreignrel->relid, root);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Core code already has some lock on each rel being planned, so we
|
|
|
|
* can use NoLock here.
|
|
|
|
*/
|
2019-01-21 19:32:19 +01:00
|
|
|
Relation rel = table_open(rte->relid, NoLock);
|
2016-02-09 20:00:50 +01:00
|
|
|
|
Fix interaction of foreign tuple routing with remote triggers.
Without these fixes, changes to the inserted tuple made by remote
triggers are ignored when building local RETURNING tuples.
In the core code, call ExecInitRoutingInfo at a later point from
within ExecInitPartitionInfo so that the FDW callback gets invoked
after the returning list has been built. But move CheckValidResultRel
out of ExecInitRoutingInfo so that it can happen at an earlier stage.
In postgres_fdw, refactor assorted deparsing functions to work with
the RTE rather than the PlannerInfo, which saves us having to
construct a fake PlannerInfo in cases where we don't have a real one.
Then, we can pass down a constructed RTE that yields the correct
deparse result when no real one exists. Unfortunately, this
necessitates a hack that understands how the core code manages RT
indexes for update tuple routing, which is ugly, but we don't have a
better idea right now.
Original report, analysis, and patch by Etsuro Fujita. Heavily
refactored by me. Then worked over some more by Amit Langote.
Discussion: http://postgr.es/m/5AD4882B.10002@lab.ntt.co.jp
2018-05-01 19:21:46 +02:00
|
|
|
deparseTargetList(buf, rte, foreignrel->relid, rel, false,
|
2016-02-09 20:00:50 +01:00
|
|
|
fpinfo->attrs_used, false, retrieved_attrs);
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(rel, NoLock);
|
2016-02-09 20:00:50 +01:00
|
|
|
}
|
2016-10-21 15:54:29 +02:00
|
|
|
}
|
2013-02-21 11:26:23 +01:00
|
|
|
|
2016-10-21 15:54:29 +02:00
|
|
|
/*
|
|
|
|
* Construct a FROM clause and, if needed, a WHERE clause, and append those to
|
|
|
|
* "buf".
|
|
|
|
*
|
|
|
|
* quals is the list of clauses to be included in the WHERE clause.
|
Handle restriction clause lists more uniformly in postgres_fdw.
Clauses in the lists retained by postgres_fdw during planning were
sometimes bare boolean clauses, sometimes RestrictInfos, and sometimes
a mixture of the two in the same list. The comment about that situation
didn't come close to telling the full truth, either. Aside from being
confusing, this had a couple of bad practical consequences:
* waste of planning cycles due to inability to cache per-clause selectivity
and cost estimates;
* sometimes, RestrictInfos would sneak into the fdw_private list of a
finished Plan node, causing failures if, for example, we tried to ship
the Plan tree to a parallel worker.
(It may well be that it's a bug in the parallel-query logic that we
would ever try to ship such a plan to a parallel worker, but in any
case this deserves to be cleaned up.)
To fix, rearrange so that clause lists in PgFdwRelationInfo are always
lists of RestrictInfos, and then strip the RestrictInfos at the last
minute when making a Plan node. In passing do a bit of refactoring and
comment cleanup in postgresGetForeignPlan and foreign_join_ok.
Although the messiness here dates back at least to 9.6, there's no evidence
that it causes anything worse than wasted planning cycles in 9.6, so no
back-patch for now.
Per fuzz testing by Andreas Seltenreich.
Tom Lane and Ashutosh Bapat
Discussion: https://postgr.es/m/87tw5x4vcu.fsf@credativ.de
2017-04-11 17:58:59 +02:00
|
|
|
* (These may or may not include RestrictInfo decoration.)
|
2016-10-21 15:54:29 +02:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
deparseFromExpr(List *quals, deparse_expr_cxt *context)
|
|
|
|
{
|
|
|
|
StringInfo buf = context->buf;
|
|
|
|
RelOptInfo *scanrel = context->scanrel;
|
|
|
|
|
|
|
|
/* For upper relations, scanrel must be either a joinrel or a baserel */
|
Abstract logic to allow for multiple kinds of child rels.
Currently, the only type of child relation is an "other member rel",
which is the child of a baserel, but in the future joins and even
upper relations may have child rels. To facilitate that, introduce
macros that test to test for particular RelOptKind values, and use
them in various places where they help to clarify the sense of a test.
(For example, a test may allow RELOPT_OTHER_MEMBER_REL either because
it intends to allow child rels, or because it intends to allow simple
rels.)
Also, remove find_childrel_top_parent, which will not work for a
child rel that is not a baserel. Instead, add a new RelOptInfo
member top_parent_relids to track the same kind of information in a
more generic manner.
Ashutosh Bapat, slightly tweaked by me. Review and testing of the
patch set from which this was taken by Rajkumar Raghuwanshi and Rafia
Sabih.
Discussion: http://postgr.es/m/CA+TgmoagTnF2yqR3PT2rv=om=wJiZ4-A+ATwdnriTGku1CLYxA@mail.gmail.com
2017-04-04 04:41:31 +02:00
|
|
|
Assert(!IS_UPPER_REL(context->foreignrel) ||
|
|
|
|
IS_JOIN_REL(scanrel) || IS_SIMPLE_REL(scanrel));
|
2016-10-21 15:54:29 +02:00
|
|
|
|
|
|
|
/* Construct FROM clause */
|
2013-03-10 19:14:53 +01:00
|
|
|
appendStringInfoString(buf, " FROM ");
|
2016-10-21 15:54:29 +02:00
|
|
|
deparseFromExprForRel(buf, context->root, scanrel,
|
2018-07-01 08:10:10 +02:00
|
|
|
(bms_membership(scanrel->relids) == BMS_MULTIPLE),
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
(Index) 0, NULL, context->params_list);
|
2016-10-21 15:54:29 +02:00
|
|
|
|
|
|
|
/* Construct WHERE clause */
|
|
|
|
if (quals != NIL)
|
|
|
|
{
|
2017-08-16 05:34:39 +02:00
|
|
|
appendStringInfoString(buf, " WHERE ");
|
2016-10-21 15:54:29 +02:00
|
|
|
appendConditions(quals, context);
|
|
|
|
}
|
2013-03-10 19:14:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Emit a target list that retrieves the columns specified in attrs_used.
|
2016-02-05 04:15:50 +01:00
|
|
|
* This is used for both SELECT and RETURNING targetlists; the is_returning
|
|
|
|
* parameter is true only for a RETURNING targetlist.
|
2013-03-10 19:14:53 +01:00
|
|
|
*
|
2013-03-22 05:31:11 +01:00
|
|
|
* The tlist text is appended to buf, and we also create an integer List
|
|
|
|
* of the columns being retrieved, which is returned to *retrieved_attrs.
|
2016-02-09 20:00:50 +01:00
|
|
|
*
|
|
|
|
* If qualify_col is true, add relation alias before the column name.
|
2013-03-10 19:14:53 +01:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
deparseTargetList(StringInfo buf,
|
Fix interaction of foreign tuple routing with remote triggers.
Without these fixes, changes to the inserted tuple made by remote
triggers are ignored when building local RETURNING tuples.
In the core code, call ExecInitRoutingInfo at a later point from
within ExecInitPartitionInfo so that the FDW callback gets invoked
after the returning list has been built. But move CheckValidResultRel
out of ExecInitRoutingInfo so that it can happen at an earlier stage.
In postgres_fdw, refactor assorted deparsing functions to work with
the RTE rather than the PlannerInfo, which saves us having to
construct a fake PlannerInfo in cases where we don't have a real one.
Then, we can pass down a constructed RTE that yields the correct
deparse result when no real one exists. Unfortunately, this
necessitates a hack that understands how the core code manages RT
indexes for update tuple routing, which is ugly, but we don't have a
better idea right now.
Original report, analysis, and patch by Etsuro Fujita. Heavily
refactored by me. Then worked over some more by Amit Langote.
Discussion: http://postgr.es/m/5AD4882B.10002@lab.ntt.co.jp
2018-05-01 19:21:46 +02:00
|
|
|
RangeTblEntry *rte,
|
2013-03-10 19:14:53 +01:00
|
|
|
Index rtindex,
|
|
|
|
Relation rel,
|
2016-02-05 04:15:50 +01:00
|
|
|
bool is_returning,
|
2013-03-22 05:31:11 +01:00
|
|
|
Bitmapset *attrs_used,
|
2016-02-09 20:00:50 +01:00
|
|
|
bool qualify_col,
|
2013-03-22 05:31:11 +01:00
|
|
|
List **retrieved_attrs)
|
2013-03-10 19:14:53 +01:00
|
|
|
{
|
|
|
|
TupleDesc tupdesc = RelationGetDescr(rel);
|
|
|
|
bool have_wholerow;
|
|
|
|
bool first;
|
|
|
|
int i;
|
2013-02-21 11:26:23 +01:00
|
|
|
|
2013-03-22 05:31:11 +01:00
|
|
|
*retrieved_attrs = NIL;
|
|
|
|
|
2013-02-22 15:21:50 +01:00
|
|
|
/* If there's a whole-row reference, we'll need all the columns. */
|
|
|
|
have_wholerow = bms_is_member(0 - FirstLowInvalidHeapAttributeNumber,
|
|
|
|
attrs_used);
|
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
first = true;
|
2013-03-10 19:14:53 +01:00
|
|
|
for (i = 1; i <= tupdesc->natts; i++)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
2017-08-20 20:19:07 +02:00
|
|
|
Form_pg_attribute attr = TupleDescAttr(tupdesc, i - 1);
|
2013-03-10 19:14:53 +01:00
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
/* Ignore dropped attributes. */
|
2013-03-10 19:14:53 +01:00
|
|
|
if (attr->attisdropped)
|
2013-02-21 11:26:23 +01:00
|
|
|
continue;
|
|
|
|
|
2013-02-22 15:21:50 +01:00
|
|
|
if (have_wholerow ||
|
2013-03-10 19:14:53 +01:00
|
|
|
bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
|
2013-02-21 11:26:23 +01:00
|
|
|
attrs_used))
|
2013-03-22 05:31:11 +01:00
|
|
|
{
|
|
|
|
if (!first)
|
|
|
|
appendStringInfoString(buf, ", ");
|
2016-02-05 04:15:50 +01:00
|
|
|
else if (is_returning)
|
|
|
|
appendStringInfoString(buf, " RETURNING ");
|
2013-03-22 05:31:11 +01:00
|
|
|
first = false;
|
|
|
|
|
Fix interaction of foreign tuple routing with remote triggers.
Without these fixes, changes to the inserted tuple made by remote
triggers are ignored when building local RETURNING tuples.
In the core code, call ExecInitRoutingInfo at a later point from
within ExecInitPartitionInfo so that the FDW callback gets invoked
after the returning list has been built. But move CheckValidResultRel
out of ExecInitRoutingInfo so that it can happen at an earlier stage.
In postgres_fdw, refactor assorted deparsing functions to work with
the RTE rather than the PlannerInfo, which saves us having to
construct a fake PlannerInfo in cases where we don't have a real one.
Then, we can pass down a constructed RTE that yields the correct
deparse result when no real one exists. Unfortunately, this
necessitates a hack that understands how the core code manages RT
indexes for update tuple routing, which is ugly, but we don't have a
better idea right now.
Original report, analysis, and patch by Etsuro Fujita. Heavily
refactored by me. Then worked over some more by Amit Langote.
Discussion: http://postgr.es/m/5AD4882B.10002@lab.ntt.co.jp
2018-05-01 19:21:46 +02:00
|
|
|
deparseColumnRef(buf, rtindex, i, rte, qualify_col);
|
2013-03-22 05:31:11 +01:00
|
|
|
|
|
|
|
*retrieved_attrs = lappend_int(*retrieved_attrs, i);
|
|
|
|
}
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
* Add ctid if needed. We currently don't support retrieving any other
|
|
|
|
* system columns.
|
2013-02-21 11:26:23 +01:00
|
|
|
*/
|
2013-03-10 19:14:53 +01:00
|
|
|
if (bms_is_member(SelfItemPointerAttributeNumber - FirstLowInvalidHeapAttributeNumber,
|
|
|
|
attrs_used))
|
|
|
|
{
|
|
|
|
if (!first)
|
|
|
|
appendStringInfoString(buf, ", ");
|
2016-02-05 04:15:50 +01:00
|
|
|
else if (is_returning)
|
|
|
|
appendStringInfoString(buf, " RETURNING ");
|
2013-03-10 19:14:53 +01:00
|
|
|
first = false;
|
|
|
|
|
2016-02-09 20:00:50 +01:00
|
|
|
if (qualify_col)
|
|
|
|
ADD_REL_QUALIFIER(buf, rtindex);
|
2013-03-10 19:14:53 +01:00
|
|
|
appendStringInfoString(buf, "ctid");
|
2013-03-22 05:31:11 +01:00
|
|
|
|
|
|
|
*retrieved_attrs = lappend_int(*retrieved_attrs,
|
|
|
|
SelfItemPointerAttributeNumber);
|
2013-03-10 19:14:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Don't generate bad syntax if no undropped columns */
|
2016-02-05 04:15:50 +01:00
|
|
|
if (first && !is_returning)
|
2013-03-10 19:14:53 +01:00
|
|
|
appendStringInfoString(buf, "NULL");
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
|
|
|
|
2016-01-28 22:44:01 +01:00
|
|
|
/*
|
2016-08-24 22:29:10 +02:00
|
|
|
* Deparse the appropriate locking clause (FOR UPDATE or FOR SHARE) for a
|
2016-10-21 15:54:29 +02:00
|
|
|
* given relation (context->scanrel).
|
2016-01-28 22:44:01 +01:00
|
|
|
*/
|
2016-01-30 16:32:38 +01:00
|
|
|
static void
|
|
|
|
deparseLockingClause(deparse_expr_cxt *context)
|
2016-01-28 22:44:01 +01:00
|
|
|
{
|
2016-01-30 16:32:38 +01:00
|
|
|
StringInfo buf = context->buf;
|
|
|
|
PlannerInfo *root = context->root;
|
2016-10-21 15:54:29 +02:00
|
|
|
RelOptInfo *rel = context->scanrel;
|
2017-03-16 18:34:59 +01:00
|
|
|
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
|
2016-02-09 20:00:50 +01:00
|
|
|
int relid = -1;
|
2016-01-30 16:32:38 +01:00
|
|
|
|
2016-02-09 20:00:50 +01:00
|
|
|
while ((relid = bms_next_member(rel->relids, relid)) >= 0)
|
2016-01-28 22:44:01 +01:00
|
|
|
{
|
2017-03-16 18:34:59 +01:00
|
|
|
/*
|
|
|
|
* Ignore relation if it appears in a lower subquery. Locking clause
|
|
|
|
* for such a relation is included in the subquery if necessary.
|
|
|
|
*/
|
|
|
|
if (bms_is_member(relid, fpinfo->lower_subquery_rels))
|
|
|
|
continue;
|
|
|
|
|
2016-02-09 20:00:50 +01:00
|
|
|
/*
|
|
|
|
* Add FOR UPDATE/SHARE if appropriate. We apply locking during the
|
|
|
|
* initial row fetch, rather than later on as is done for local
|
|
|
|
* tables. The extra roundtrips involved in trying to duplicate the
|
|
|
|
* local semantics exactly don't seem worthwhile (see also comments
|
|
|
|
* for RowMarkType).
|
|
|
|
*
|
|
|
|
* Note: because we actually run the query as a cursor, this assumes
|
|
|
|
* that DECLARE CURSOR ... FOR UPDATE is supported, which it isn't
|
|
|
|
* before 8.3.
|
|
|
|
*/
|
Rework planning and execution of UPDATE and DELETE.
This patch makes two closely related sets of changes:
1. For UPDATE, the subplan of the ModifyTable node now only delivers
the new values of the changed columns (i.e., the expressions computed
in the query's SET clause) plus row identity information such as CTID.
ModifyTable must re-fetch the original tuple to merge in the old
values of any unchanged columns. The core advantage of this is that
the changed columns are uniform across all tables of an inherited or
partitioned target relation, whereas the other columns might not be.
A secondary advantage, when the UPDATE involves joins, is that less
data needs to pass through the plan tree. The disadvantage of course
is an extra fetch of each tuple to be updated. However, that seems to
be very nearly free in context; even worst-case tests don't show it to
add more than a couple percent to the total query cost. At some point
it might be interesting to combine the re-fetch with the tuple access
that ModifyTable must do anyway to mark the old tuple dead; but that
would require a good deal of refactoring and it seems it wouldn't buy
all that much, so this patch doesn't attempt it.
2. For inherited UPDATE/DELETE, instead of generating a separate
subplan for each target relation, we now generate a single subplan
that is just exactly like a SELECT's plan, then stick ModifyTable
on top of that. To let ModifyTable know which target relation a
given incoming row refers to, a tableoid junk column is added to
the row identity information. This gets rid of the horrid hack
that was inheritance_planner(), eliminating O(N^2) planning cost
and memory consumption in cases where there were many unprunable
target relations.
Point 2 of course requires point 1, so that there is a uniform
definition of the non-junk columns to be returned by the subplan.
We can't insist on uniform definition of the row identity junk
columns however, if we want to keep the ability to have both
plain and foreign tables in a partitioning hierarchy. Since
it wouldn't scale very far to have every child table have its
own row identity column, this patch includes provisions to merge
similar row identity columns into one column of the subplan result.
In particular, we can merge the whole-row Vars typically used as
row identity by FDWs into one column by pretending they are type
RECORD. (It's still okay for the actual composite Datums to be
labeled with the table's rowtype OID, though.)
There is more that can be done to file down residual inefficiencies
in this patch, but it seems to be committable now.
FDW authors should note several API changes:
* The argument list for AddForeignUpdateTargets() has changed, and so
has the method it must use for adding junk columns to the query. Call
add_row_identity_var() instead of manipulating the parse tree directly.
You might want to reconsider exactly what you're adding, too.
* PlanDirectModify() must now work a little harder to find the
ForeignScan plan node; if the foreign table is part of a partitioning
hierarchy then the ForeignScan might not be the direct child of
ModifyTable. See postgres_fdw for sample code.
* To check whether a relation is a target relation, it's no
longer sufficient to compare its relid to root->parse->resultRelation.
Instead, check it against all_result_relids or leaf_result_relids,
as appropriate.
Amit Langote and Tom Lane
Discussion: https://postgr.es/m/CA+HiwqHpHdqdDn48yCEhynnniahH78rwcrv1rEX65-fsZGBOLQ@mail.gmail.com
2021-03-31 17:52:34 +02:00
|
|
|
if (bms_is_member(relid, root->all_result_relids) &&
|
2016-02-09 20:00:50 +01:00
|
|
|
(root->parse->commandType == CMD_UPDATE ||
|
|
|
|
root->parse->commandType == CMD_DELETE))
|
|
|
|
{
|
|
|
|
/* Relation is UPDATE/DELETE target, so use FOR UPDATE */
|
|
|
|
appendStringInfoString(buf, " FOR UPDATE");
|
2016-01-28 22:44:01 +01:00
|
|
|
|
2016-02-09 20:00:50 +01:00
|
|
|
/* Add the relation alias if we are here for a join relation */
|
Abstract logic to allow for multiple kinds of child rels.
Currently, the only type of child relation is an "other member rel",
which is the child of a baserel, but in the future joins and even
upper relations may have child rels. To facilitate that, introduce
macros that test to test for particular RelOptKind values, and use
them in various places where they help to clarify the sense of a test.
(For example, a test may allow RELOPT_OTHER_MEMBER_REL either because
it intends to allow child rels, or because it intends to allow simple
rels.)
Also, remove find_childrel_top_parent, which will not work for a
child rel that is not a baserel. Instead, add a new RelOptInfo
member top_parent_relids to track the same kind of information in a
more generic manner.
Ashutosh Bapat, slightly tweaked by me. Review and testing of the
patch set from which this was taken by Rajkumar Raghuwanshi and Rafia
Sabih.
Discussion: http://postgr.es/m/CA+TgmoagTnF2yqR3PT2rv=om=wJiZ4-A+ATwdnriTGku1CLYxA@mail.gmail.com
2017-04-04 04:41:31 +02:00
|
|
|
if (IS_JOIN_REL(rel))
|
2016-02-09 20:00:50 +01:00
|
|
|
appendStringInfo(buf, " OF %s%d", REL_ALIAS_PREFIX, relid);
|
|
|
|
}
|
|
|
|
else
|
2016-01-28 22:44:01 +01:00
|
|
|
{
|
2016-02-09 20:00:50 +01:00
|
|
|
PlanRowMark *rc = get_plan_rowmark(root->rowMarks, relid);
|
|
|
|
|
|
|
|
if (rc)
|
2016-01-28 22:44:01 +01:00
|
|
|
{
|
2016-02-09 20:00:50 +01:00
|
|
|
/*
|
|
|
|
* Relation is specified as a FOR UPDATE/SHARE target, so
|
|
|
|
* handle that. (But we could also see LCS_NONE, meaning this
|
|
|
|
* isn't a target relation after all.)
|
|
|
|
*
|
|
|
|
* For now, just ignore any [NO] KEY specification, since (a)
|
|
|
|
* it's not clear what that means for a remote table that we
|
|
|
|
* don't have complete information about, and (b) it wouldn't
|
|
|
|
* work anyway on older remote servers. Likewise, we don't
|
|
|
|
* worry about NOWAIT.
|
|
|
|
*/
|
|
|
|
switch (rc->strength)
|
|
|
|
{
|
|
|
|
case LCS_NONE:
|
|
|
|
/* No locking needed */
|
|
|
|
break;
|
|
|
|
case LCS_FORKEYSHARE:
|
|
|
|
case LCS_FORSHARE:
|
|
|
|
appendStringInfoString(buf, " FOR SHARE");
|
|
|
|
break;
|
|
|
|
case LCS_FORNOKEYUPDATE:
|
|
|
|
case LCS_FORUPDATE:
|
|
|
|
appendStringInfoString(buf, " FOR UPDATE");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add the relation alias if we are here for a join relation */
|
2018-07-01 08:10:10 +02:00
|
|
|
if (bms_membership(rel->relids) == BMS_MULTIPLE &&
|
2016-02-09 20:00:50 +01:00
|
|
|
rc->strength != LCS_NONE)
|
|
|
|
appendStringInfo(buf, " OF %s%d", REL_ALIAS_PREFIX, relid);
|
2016-01-28 22:44:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
/*
|
2016-02-09 20:00:50 +01:00
|
|
|
* Deparse conditions from the provided list and append them to buf.
|
|
|
|
*
|
|
|
|
* The conditions in the list are assumed to be ANDed. This function is used to
|
2016-10-21 15:54:29 +02:00
|
|
|
* deparse WHERE clauses, JOIN .. ON clauses and HAVING clauses.
|
Handle restriction clause lists more uniformly in postgres_fdw.
Clauses in the lists retained by postgres_fdw during planning were
sometimes bare boolean clauses, sometimes RestrictInfos, and sometimes
a mixture of the two in the same list. The comment about that situation
didn't come close to telling the full truth, either. Aside from being
confusing, this had a couple of bad practical consequences:
* waste of planning cycles due to inability to cache per-clause selectivity
and cost estimates;
* sometimes, RestrictInfos would sneak into the fdw_private list of a
finished Plan node, causing failures if, for example, we tried to ship
the Plan tree to a parallel worker.
(It may well be that it's a bug in the parallel-query logic that we
would ever try to ship such a plan to a parallel worker, but in any
case this deserves to be cleaned up.)
To fix, rearrange so that clause lists in PgFdwRelationInfo are always
lists of RestrictInfos, and then strip the RestrictInfos at the last
minute when making a Plan node. In passing do a bit of refactoring and
comment cleanup in postgresGetForeignPlan and foreign_join_ok.
Although the messiness here dates back at least to 9.6, there's no evidence
that it causes anything worse than wasted planning cycles in 9.6, so no
back-patch for now.
Per fuzz testing by Andreas Seltenreich.
Tom Lane and Ashutosh Bapat
Discussion: https://postgr.es/m/87tw5x4vcu.fsf@credativ.de
2017-04-11 17:58:59 +02:00
|
|
|
*
|
|
|
|
* Depending on the caller, the list elements might be either RestrictInfos
|
|
|
|
* or bare clauses.
|
2013-02-21 11:26:23 +01:00
|
|
|
*/
|
2016-01-30 16:32:38 +01:00
|
|
|
static void
|
2016-02-09 20:00:50 +01:00
|
|
|
appendConditions(List *exprs, deparse_expr_cxt *context)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
2013-03-12 02:31:28 +01:00
|
|
|
int nestlevel;
|
2013-02-21 11:26:23 +01:00
|
|
|
ListCell *lc;
|
2016-01-30 16:32:38 +01:00
|
|
|
bool is_first = true;
|
|
|
|
StringInfo buf = context->buf;
|
2013-03-22 00:43:59 +01:00
|
|
|
|
2013-03-12 02:31:28 +01:00
|
|
|
/* Make sure any constants in the exprs are printed portably */
|
|
|
|
nestlevel = set_transmission_modes();
|
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
foreach(lc, exprs)
|
|
|
|
{
|
2016-02-09 20:00:50 +01:00
|
|
|
Expr *expr = (Expr *) lfirst(lc);
|
|
|
|
|
Handle restriction clause lists more uniformly in postgres_fdw.
Clauses in the lists retained by postgres_fdw during planning were
sometimes bare boolean clauses, sometimes RestrictInfos, and sometimes
a mixture of the two in the same list. The comment about that situation
didn't come close to telling the full truth, either. Aside from being
confusing, this had a couple of bad practical consequences:
* waste of planning cycles due to inability to cache per-clause selectivity
and cost estimates;
* sometimes, RestrictInfos would sneak into the fdw_private list of a
finished Plan node, causing failures if, for example, we tried to ship
the Plan tree to a parallel worker.
(It may well be that it's a bug in the parallel-query logic that we
would ever try to ship such a plan to a parallel worker, but in any
case this deserves to be cleaned up.)
To fix, rearrange so that clause lists in PgFdwRelationInfo are always
lists of RestrictInfos, and then strip the RestrictInfos at the last
minute when making a Plan node. In passing do a bit of refactoring and
comment cleanup in postgresGetForeignPlan and foreign_join_ok.
Although the messiness here dates back at least to 9.6, there's no evidence
that it causes anything worse than wasted planning cycles in 9.6, so no
back-patch for now.
Per fuzz testing by Andreas Seltenreich.
Tom Lane and Ashutosh Bapat
Discussion: https://postgr.es/m/87tw5x4vcu.fsf@credativ.de
2017-04-11 17:58:59 +02:00
|
|
|
/* Extract clause from RestrictInfo, if required */
|
2016-02-09 20:00:50 +01:00
|
|
|
if (IsA(expr, RestrictInfo))
|
Handle restriction clause lists more uniformly in postgres_fdw.
Clauses in the lists retained by postgres_fdw during planning were
sometimes bare boolean clauses, sometimes RestrictInfos, and sometimes
a mixture of the two in the same list. The comment about that situation
didn't come close to telling the full truth, either. Aside from being
confusing, this had a couple of bad practical consequences:
* waste of planning cycles due to inability to cache per-clause selectivity
and cost estimates;
* sometimes, RestrictInfos would sneak into the fdw_private list of a
finished Plan node, causing failures if, for example, we tried to ship
the Plan tree to a parallel worker.
(It may well be that it's a bug in the parallel-query logic that we
would ever try to ship such a plan to a parallel worker, but in any
case this deserves to be cleaned up.)
To fix, rearrange so that clause lists in PgFdwRelationInfo are always
lists of RestrictInfos, and then strip the RestrictInfos at the last
minute when making a Plan node. In passing do a bit of refactoring and
comment cleanup in postgresGetForeignPlan and foreign_join_ok.
Although the messiness here dates back at least to 9.6, there's no evidence
that it causes anything worse than wasted planning cycles in 9.6, so no
back-patch for now.
Per fuzz testing by Andreas Seltenreich.
Tom Lane and Ashutosh Bapat
Discussion: https://postgr.es/m/87tw5x4vcu.fsf@credativ.de
2017-04-11 17:58:59 +02:00
|
|
|
expr = ((RestrictInfo *) expr)->clause;
|
2013-02-21 11:26:23 +01:00
|
|
|
|
|
|
|
/* Connect expressions with "AND" and parenthesize each condition. */
|
2016-02-09 20:00:50 +01:00
|
|
|
if (!is_first)
|
2013-03-10 19:14:53 +01:00
|
|
|
appendStringInfoString(buf, " AND ");
|
2013-02-21 11:26:23 +01:00
|
|
|
|
|
|
|
appendStringInfoChar(buf, '(');
|
2016-02-09 20:00:50 +01:00
|
|
|
deparseExpr(expr, context);
|
2013-02-21 11:26:23 +01:00
|
|
|
appendStringInfoChar(buf, ')');
|
|
|
|
|
|
|
|
is_first = false;
|
|
|
|
}
|
2013-03-12 02:31:28 +01:00
|
|
|
|
|
|
|
reset_transmission_modes(nestlevel);
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
|
|
|
|
2016-02-09 20:00:50 +01:00
|
|
|
/* Output join name for given join type */
|
2018-02-19 18:07:44 +01:00
|
|
|
const char *
|
2016-02-09 20:00:50 +01:00
|
|
|
get_jointype_name(JoinType jointype)
|
|
|
|
{
|
|
|
|
switch (jointype)
|
|
|
|
{
|
|
|
|
case JOIN_INNER:
|
|
|
|
return "INNER";
|
|
|
|
|
|
|
|
case JOIN_LEFT:
|
|
|
|
return "LEFT";
|
|
|
|
|
|
|
|
case JOIN_RIGHT:
|
|
|
|
return "RIGHT";
|
|
|
|
|
|
|
|
case JOIN_FULL:
|
|
|
|
return "FULL";
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* Shouldn't come here, but protect from buggy code. */
|
|
|
|
elog(ERROR, "unsupported join type %d", jointype);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Keep compiler happy */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Deparse given targetlist and append it to context->buf.
|
|
|
|
*
|
|
|
|
* tlist is list of TargetEntry's which in turn contain Var nodes.
|
|
|
|
*
|
|
|
|
* retrieved_attrs is the list of continuously increasing integers starting
|
|
|
|
* from 1. It has same number of entries as tlist.
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
*
|
|
|
|
* This is used for both SELECT and RETURNING targetlists; the is_returning
|
|
|
|
* parameter is true only for a RETURNING targetlist.
|
2016-02-09 20:00:50 +01:00
|
|
|
*/
|
|
|
|
static void
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
deparseExplicitTargetList(List *tlist,
|
|
|
|
bool is_returning,
|
|
|
|
List **retrieved_attrs,
|
2016-02-09 20:00:50 +01:00
|
|
|
deparse_expr_cxt *context)
|
|
|
|
{
|
|
|
|
ListCell *lc;
|
|
|
|
StringInfo buf = context->buf;
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
*retrieved_attrs = NIL;
|
|
|
|
|
|
|
|
foreach(lc, tlist)
|
|
|
|
{
|
Improve castNode notation by introducing list-extraction-specific variants.
This extends the castNode() notation introduced by commit 5bcab1114 to
provide, in one step, extraction of a list cell's pointer and coercion to
a concrete node type. For example, "lfirst_node(Foo, lc)" is the same
as "castNode(Foo, lfirst(lc))". Almost half of the uses of castNode
that have appeared so far include a list extraction call, so this is
pretty widely useful, and it saves a few more keystrokes compared to the
old way.
As with the previous patch, back-patch the addition of these macros to
pg_list.h, so that the notation will be available when back-patching.
Patch by me, after an idea of Andrew Gierth's.
Discussion: https://postgr.es/m/14197.1491841216@sss.pgh.pa.us
2017-04-10 19:51:29 +02:00
|
|
|
TargetEntry *tle = lfirst_node(TargetEntry, lc);
|
2016-02-09 20:00:50 +01:00
|
|
|
|
|
|
|
if (i > 0)
|
|
|
|
appendStringInfoString(buf, ", ");
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
else if (is_returning)
|
|
|
|
appendStringInfoString(buf, " RETURNING ");
|
|
|
|
|
2016-10-21 15:54:29 +02:00
|
|
|
deparseExpr((Expr *) tle->expr, context);
|
2016-02-09 20:00:50 +01:00
|
|
|
|
|
|
|
*retrieved_attrs = lappend_int(*retrieved_attrs, i + 1);
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
if (i == 0 && !is_returning)
|
2016-02-09 20:00:50 +01:00
|
|
|
appendStringInfoString(buf, "NULL");
|
|
|
|
}
|
|
|
|
|
2017-03-16 18:34:59 +01:00
|
|
|
/*
|
|
|
|
* Emit expressions specified in the given relation's reltarget.
|
|
|
|
*
|
|
|
|
* This is used for deparsing the given relation as a subquery.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
deparseSubqueryTargetList(deparse_expr_cxt *context)
|
|
|
|
{
|
|
|
|
StringInfo buf = context->buf;
|
|
|
|
RelOptInfo *foreignrel = context->foreignrel;
|
|
|
|
bool first;
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
/* Should only be called in these cases. */
|
Abstract logic to allow for multiple kinds of child rels.
Currently, the only type of child relation is an "other member rel",
which is the child of a baserel, but in the future joins and even
upper relations may have child rels. To facilitate that, introduce
macros that test to test for particular RelOptKind values, and use
them in various places where they help to clarify the sense of a test.
(For example, a test may allow RELOPT_OTHER_MEMBER_REL either because
it intends to allow child rels, or because it intends to allow simple
rels.)
Also, remove find_childrel_top_parent, which will not work for a
child rel that is not a baserel. Instead, add a new RelOptInfo
member top_parent_relids to track the same kind of information in a
more generic manner.
Ashutosh Bapat, slightly tweaked by me. Review and testing of the
patch set from which this was taken by Rajkumar Raghuwanshi and Rafia
Sabih.
Discussion: http://postgr.es/m/CA+TgmoagTnF2yqR3PT2rv=om=wJiZ4-A+ATwdnriTGku1CLYxA@mail.gmail.com
2017-04-04 04:41:31 +02:00
|
|
|
Assert(IS_SIMPLE_REL(foreignrel) || IS_JOIN_REL(foreignrel));
|
2017-03-16 18:34:59 +01:00
|
|
|
|
|
|
|
first = true;
|
|
|
|
foreach(lc, foreignrel->reltarget->exprs)
|
|
|
|
{
|
|
|
|
Node *node = (Node *) lfirst(lc);
|
|
|
|
|
|
|
|
if (!first)
|
|
|
|
appendStringInfoString(buf, ", ");
|
|
|
|
first = false;
|
|
|
|
|
|
|
|
deparseExpr((Expr *) node, context);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Don't generate bad syntax if no expressions */
|
|
|
|
if (first)
|
|
|
|
appendStringInfoString(buf, "NULL");
|
|
|
|
}
|
|
|
|
|
2016-02-09 20:00:50 +01:00
|
|
|
/*
|
|
|
|
* Construct FROM clause for given relation
|
|
|
|
*
|
|
|
|
* The function constructs ... JOIN ... ON ... for join relation. For a base
|
|
|
|
* relation it just returns schema-qualified tablename, with the appropriate
|
|
|
|
* alias if so requested.
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
*
|
|
|
|
* 'ignore_rel' is either zero or the RT index of a target relation. In the
|
|
|
|
* latter case the function constructs FROM clause of UPDATE or USING clause
|
|
|
|
* of DELETE; it deparses the join relation as if the relation never contained
|
|
|
|
* the target relation, and creates a List of conditions to be deparsed into
|
|
|
|
* the top-level WHERE clause, which is returned to *ignore_conds.
|
2016-02-09 20:00:50 +01:00
|
|
|
*/
|
2016-02-12 17:20:16 +01:00
|
|
|
static void
|
2016-02-09 20:00:50 +01:00
|
|
|
deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
bool use_alias, Index ignore_rel, List **ignore_conds,
|
|
|
|
List **params_list)
|
2016-02-09 20:00:50 +01:00
|
|
|
{
|
|
|
|
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
|
|
|
|
|
Abstract logic to allow for multiple kinds of child rels.
Currently, the only type of child relation is an "other member rel",
which is the child of a baserel, but in the future joins and even
upper relations may have child rels. To facilitate that, introduce
macros that test to test for particular RelOptKind values, and use
them in various places where they help to clarify the sense of a test.
(For example, a test may allow RELOPT_OTHER_MEMBER_REL either because
it intends to allow child rels, or because it intends to allow simple
rels.)
Also, remove find_childrel_top_parent, which will not work for a
child rel that is not a baserel. Instead, add a new RelOptInfo
member top_parent_relids to track the same kind of information in a
more generic manner.
Ashutosh Bapat, slightly tweaked by me. Review and testing of the
patch set from which this was taken by Rajkumar Raghuwanshi and Rafia
Sabih.
Discussion: http://postgr.es/m/CA+TgmoagTnF2yqR3PT2rv=om=wJiZ4-A+ATwdnriTGku1CLYxA@mail.gmail.com
2017-04-04 04:41:31 +02:00
|
|
|
if (IS_JOIN_REL(foreignrel))
|
2016-02-09 20:00:50 +01:00
|
|
|
{
|
|
|
|
StringInfoData join_sql_o;
|
|
|
|
StringInfoData join_sql_i;
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
RelOptInfo *outerrel = fpinfo->outerrel;
|
|
|
|
RelOptInfo *innerrel = fpinfo->innerrel;
|
|
|
|
bool outerrel_is_target = false;
|
|
|
|
bool innerrel_is_target = false;
|
2016-02-09 20:00:50 +01:00
|
|
|
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
if (ignore_rel > 0 && bms_is_member(ignore_rel, foreignrel->relids))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If this is an inner join, add joinclauses to *ignore_conds and
|
|
|
|
* set it to empty so that those can be deparsed into the WHERE
|
|
|
|
* clause. Note that since the target relation can never be
|
|
|
|
* within the nullable side of an outer join, those could safely
|
|
|
|
* be pulled up into the WHERE clause (see foreign_join_ok()).
|
|
|
|
* Note also that since the target relation is only inner-joined
|
|
|
|
* to any other relation in the query, all conditions in the join
|
|
|
|
* tree mentioning the target relation could be deparsed into the
|
|
|
|
* WHERE clause by doing this recursively.
|
|
|
|
*/
|
|
|
|
if (fpinfo->jointype == JOIN_INNER)
|
|
|
|
{
|
|
|
|
*ignore_conds = list_concat(*ignore_conds,
|
Rationalize use of list_concat + list_copy combinations.
In the wake of commit 1cff1b95a, the result of list_concat no longer
shares the ListCells of the second input. Therefore, we can replace
"list_concat(x, list_copy(y))" with just "list_concat(x, y)".
To improve call sites that were list_copy'ing the first argument,
or both arguments, invent "list_concat_copy()" which produces a new
list sharing no ListCells with either input. (This is a bit faster
than "list_concat(list_copy(x), y)" because it makes the result list
the right size to start with.)
In call sites that were not list_copy'ing the second argument, the new
semantics mean that we are usually leaking the second List's storage,
since typically there is no remaining pointer to it. We considered
inventing another list_copy variant that would list_free the second
input, but concluded that for most call sites it isn't worth worrying
about, given the relative compactness of the new List representation.
(Note that in cases where such leakage would happen, the old code
already leaked the second List's header; so we're only discussing
the size of the leak not whether there is one. I did adjust two or
three places that had been troubling to free that header so that
they manually free the whole second List.)
Patch by me; thanks to David Rowley for review.
Discussion: https://postgr.es/m/11587.1550975080@sss.pgh.pa.us
2019-08-12 17:20:18 +02:00
|
|
|
fpinfo->joinclauses);
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
fpinfo->joinclauses = NIL;
|
|
|
|
}
|
2016-02-09 20:00:50 +01:00
|
|
|
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
/*
|
|
|
|
* Check if either of the input relations is the target relation.
|
|
|
|
*/
|
|
|
|
if (outerrel->relid == ignore_rel)
|
|
|
|
outerrel_is_target = true;
|
|
|
|
else if (innerrel->relid == ignore_rel)
|
|
|
|
innerrel_is_target = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Deparse outer relation if not the target relation. */
|
|
|
|
if (!outerrel_is_target)
|
|
|
|
{
|
|
|
|
initStringInfo(&join_sql_o);
|
|
|
|
deparseRangeTblRef(&join_sql_o, root, outerrel,
|
|
|
|
fpinfo->make_outerrel_subquery,
|
|
|
|
ignore_rel, ignore_conds, params_list);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If inner relation is the target relation, skip deparsing it.
|
|
|
|
* Note that since the join of the target relation with any other
|
|
|
|
* relation in the query is an inner join and can never be within
|
|
|
|
* the nullable side of an outer join, the join could be
|
|
|
|
* interchanged with higher-level joins (cf. identity 1 on outer
|
|
|
|
* join reordering shown in src/backend/optimizer/README), which
|
|
|
|
* means it's safe to skip the target-relation deparsing here.
|
|
|
|
*/
|
|
|
|
if (innerrel_is_target)
|
|
|
|
{
|
|
|
|
Assert(fpinfo->jointype == JOIN_INNER);
|
|
|
|
Assert(fpinfo->joinclauses == NIL);
|
2019-07-22 14:14:11 +02:00
|
|
|
appendBinaryStringInfo(buf, join_sql_o.data, join_sql_o.len);
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Deparse inner relation if not the target relation. */
|
|
|
|
if (!innerrel_is_target)
|
|
|
|
{
|
|
|
|
initStringInfo(&join_sql_i);
|
|
|
|
deparseRangeTblRef(&join_sql_i, root, innerrel,
|
|
|
|
fpinfo->make_innerrel_subquery,
|
|
|
|
ignore_rel, ignore_conds, params_list);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If outer relation is the target relation, skip deparsing it.
|
|
|
|
* See the above note about safety.
|
|
|
|
*/
|
|
|
|
if (outerrel_is_target)
|
|
|
|
{
|
|
|
|
Assert(fpinfo->jointype == JOIN_INNER);
|
|
|
|
Assert(fpinfo->joinclauses == NIL);
|
2019-07-22 14:14:11 +02:00
|
|
|
appendBinaryStringInfo(buf, join_sql_i.data, join_sql_i.len);
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Neither of the relations is the target relation. */
|
|
|
|
Assert(!outerrel_is_target && !innerrel_is_target);
|
2016-02-09 20:00:50 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* For a join relation FROM clause entry is deparsed as
|
|
|
|
*
|
2016-08-24 22:29:10 +02:00
|
|
|
* ((outer relation) <join type> (inner relation) ON (joinclauses))
|
2016-02-09 20:00:50 +01:00
|
|
|
*/
|
|
|
|
appendStringInfo(buf, "(%s %s JOIN %s ON ", join_sql_o.data,
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
get_jointype_name(fpinfo->jointype), join_sql_i.data);
|
2016-02-09 20:00:50 +01:00
|
|
|
|
|
|
|
/* Append join clause; (TRUE) if no join clause */
|
|
|
|
if (fpinfo->joinclauses)
|
|
|
|
{
|
|
|
|
deparse_expr_cxt context;
|
|
|
|
|
|
|
|
context.buf = buf;
|
|
|
|
context.foreignrel = foreignrel;
|
2016-10-21 15:54:29 +02:00
|
|
|
context.scanrel = foreignrel;
|
2016-02-09 20:00:50 +01:00
|
|
|
context.root = root;
|
|
|
|
context.params_list = params_list;
|
|
|
|
|
2017-08-16 05:34:39 +02:00
|
|
|
appendStringInfoChar(buf, '(');
|
2016-02-09 20:00:50 +01:00
|
|
|
appendConditions(fpinfo->joinclauses, &context);
|
2017-08-16 05:34:39 +02:00
|
|
|
appendStringInfoChar(buf, ')');
|
2016-02-09 20:00:50 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
appendStringInfoString(buf, "(TRUE)");
|
|
|
|
|
|
|
|
/* End the FROM clause entry. */
|
2017-08-16 05:34:39 +02:00
|
|
|
appendStringInfoChar(buf, ')');
|
2016-02-09 20:00:50 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
RangeTblEntry *rte = planner_rt_fetch(foreignrel->relid, root);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Core code already has some lock on each rel being planned, so we
|
|
|
|
* can use NoLock here.
|
|
|
|
*/
|
2019-01-21 19:32:19 +01:00
|
|
|
Relation rel = table_open(rte->relid, NoLock);
|
2016-02-09 20:00:50 +01:00
|
|
|
|
|
|
|
deparseRelation(buf, rel);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add a unique alias to avoid any conflict in relation names due to
|
|
|
|
* pulled up subqueries in the query being built for a pushed down
|
|
|
|
* join.
|
|
|
|
*/
|
|
|
|
if (use_alias)
|
|
|
|
appendStringInfo(buf, " %s%d", REL_ALIAS_PREFIX, foreignrel->relid);
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(rel, NoLock);
|
2016-02-09 20:00:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-16 18:34:59 +01:00
|
|
|
/*
|
|
|
|
* Append FROM clause entry for the given relation into buf.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
deparseRangeTblRef(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel,
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
bool make_subquery, Index ignore_rel, List **ignore_conds,
|
|
|
|
List **params_list)
|
2017-03-16 18:34:59 +01:00
|
|
|
{
|
|
|
|
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
|
|
|
|
|
|
|
|
/* Should only be called in these cases. */
|
Abstract logic to allow for multiple kinds of child rels.
Currently, the only type of child relation is an "other member rel",
which is the child of a baserel, but in the future joins and even
upper relations may have child rels. To facilitate that, introduce
macros that test to test for particular RelOptKind values, and use
them in various places where they help to clarify the sense of a test.
(For example, a test may allow RELOPT_OTHER_MEMBER_REL either because
it intends to allow child rels, or because it intends to allow simple
rels.)
Also, remove find_childrel_top_parent, which will not work for a
child rel that is not a baserel. Instead, add a new RelOptInfo
member top_parent_relids to track the same kind of information in a
more generic manner.
Ashutosh Bapat, slightly tweaked by me. Review and testing of the
patch set from which this was taken by Rajkumar Raghuwanshi and Rafia
Sabih.
Discussion: http://postgr.es/m/CA+TgmoagTnF2yqR3PT2rv=om=wJiZ4-A+ATwdnriTGku1CLYxA@mail.gmail.com
2017-04-04 04:41:31 +02:00
|
|
|
Assert(IS_SIMPLE_REL(foreignrel) || IS_JOIN_REL(foreignrel));
|
2017-03-16 18:34:59 +01:00
|
|
|
|
|
|
|
Assert(fpinfo->local_conds == NIL);
|
|
|
|
|
|
|
|
/* If make_subquery is true, deparse the relation as a subquery. */
|
|
|
|
if (make_subquery)
|
|
|
|
{
|
|
|
|
List *retrieved_attrs;
|
|
|
|
int ncols;
|
|
|
|
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
/*
|
|
|
|
* The given relation shouldn't contain the target relation, because
|
|
|
|
* this should only happen for input relations for a full join, and
|
|
|
|
* such relations can never contain an UPDATE/DELETE target.
|
|
|
|
*/
|
|
|
|
Assert(ignore_rel == 0 ||
|
|
|
|
!bms_is_member(ignore_rel, foreignrel->relids));
|
|
|
|
|
2017-03-16 18:34:59 +01:00
|
|
|
/* Deparse the subquery representing the relation. */
|
|
|
|
appendStringInfoChar(buf, '(');
|
|
|
|
deparseSelectStmtForRel(buf, root, foreignrel, NIL,
|
2019-04-02 13:30:45 +02:00
|
|
|
fpinfo->remote_conds, NIL,
|
|
|
|
false, false, true,
|
2017-03-16 18:34:59 +01:00
|
|
|
&retrieved_attrs, params_list);
|
|
|
|
appendStringInfoChar(buf, ')');
|
|
|
|
|
|
|
|
/* Append the relation alias. */
|
|
|
|
appendStringInfo(buf, " %s%d", SUBQUERY_REL_ALIAS_PREFIX,
|
|
|
|
fpinfo->relation_index);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Append the column aliases if needed. Note that the subquery emits
|
|
|
|
* expressions specified in the relation's reltarget (see
|
|
|
|
* deparseSubqueryTargetList).
|
|
|
|
*/
|
|
|
|
ncols = list_length(foreignrel->reltarget->exprs);
|
|
|
|
if (ncols > 0)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
appendStringInfoChar(buf, '(');
|
|
|
|
for (i = 1; i <= ncols; i++)
|
|
|
|
{
|
|
|
|
if (i > 1)
|
|
|
|
appendStringInfoString(buf, ", ");
|
|
|
|
|
|
|
|
appendStringInfo(buf, "%s%d", SUBQUERY_COL_ALIAS_PREFIX, i);
|
|
|
|
}
|
|
|
|
appendStringInfoChar(buf, ')');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
deparseFromExprForRel(buf, root, foreignrel, true, ignore_rel,
|
|
|
|
ignore_conds, params_list);
|
2017-03-16 18:34:59 +01:00
|
|
|
}
|
|
|
|
|
2013-03-10 19:14:53 +01:00
|
|
|
/*
|
|
|
|
* deparse remote INSERT statement
|
2013-03-22 05:31:11 +01:00
|
|
|
*
|
|
|
|
* The statement text is appended to buf, and we also create an integer List
|
Fix WITH CHECK OPTION on views referencing postgres_fdw tables.
If a view references a foreign table, and the foreign table has a
BEFORE INSERT trigger, then it's possible for a tuple inserted or
updated through the view to be changed such that it violates the
view's WITH CHECK OPTION constraint.
Before this commit, postgres_fdw handled this case inconsistently. A
RETURNING clause on the INSERT or UPDATE statement targeting the view
would cause the finally-inserted tuple to be read back, and the WITH
CHECK OPTION violation would throw an error. But without a RETURNING
clause, postgres_fdw would not read the final tuple back, and WITH
CHECK OPTION would not throw an error for the violation (or may throw
an error when there is no real violation). AFTER ROW triggers on the
foreign table had a similar effect as a RETURNING clause on the INSERT
or UPDATE statement.
To fix, this commit retrieves the attributes needed to enforce the
WITH CHECK OPTION constraint along with the attributes needed for the
RETURNING clause (if any) from the remote side. Thus, the WITH CHECK
OPTION constraint is always evaluated against the final tuple after
any triggers on the remote side.
This fix may be considered inconsistent with CHECK constraints
declared on foreign tables, which are not enforced locally at all
(because the constraint is on a remote object). The discussion
concluded that this difference is reasonable, because the WITH CHECK
OPTION is a constraint on the local view (not any remote object);
therefore it only makes sense to enforce its WITH CHECK OPTION
constraint locally.
Author: Etsuro Fujita
Reviewed-by: Arthur Zakirov, Stephen Frost
Discussion: https://www.postgresql.org/message-id/7eb58fab-fd3b-781b-ac33-f7cfec96021f%40lab.ntt.co.jp
2018-07-08 09:14:51 +02:00
|
|
|
* of the columns being retrieved by WITH CHECK OPTION or RETURNING (if any),
|
|
|
|
* which is returned to *retrieved_attrs.
|
2021-01-20 23:05:46 +01:00
|
|
|
*
|
|
|
|
* This also stores end position of the VALUES clause, so that we can rebuild
|
|
|
|
* an INSERT for a batch of rows later.
|
2013-03-10 19:14:53 +01:00
|
|
|
*/
|
|
|
|
void
|
Fix interaction of foreign tuple routing with remote triggers.
Without these fixes, changes to the inserted tuple made by remote
triggers are ignored when building local RETURNING tuples.
In the core code, call ExecInitRoutingInfo at a later point from
within ExecInitPartitionInfo so that the FDW callback gets invoked
after the returning list has been built. But move CheckValidResultRel
out of ExecInitRoutingInfo so that it can happen at an earlier stage.
In postgres_fdw, refactor assorted deparsing functions to work with
the RTE rather than the PlannerInfo, which saves us having to
construct a fake PlannerInfo in cases where we don't have a real one.
Then, we can pass down a constructed RTE that yields the correct
deparse result when no real one exists. Unfortunately, this
necessitates a hack that understands how the core code manages RT
indexes for update tuple routing, which is ugly, but we don't have a
better idea right now.
Original report, analysis, and patch by Etsuro Fujita. Heavily
refactored by me. Then worked over some more by Amit Langote.
Discussion: http://postgr.es/m/5AD4882B.10002@lab.ntt.co.jp
2018-05-01 19:21:46 +02:00
|
|
|
deparseInsertSql(StringInfo buf, RangeTblEntry *rte,
|
2013-03-12 23:58:13 +01:00
|
|
|
Index rtindex, Relation rel,
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
2015-05-08 05:31:36 +02:00
|
|
|
List *targetAttrs, bool doNothing,
|
Fix WITH CHECK OPTION on views referencing postgres_fdw tables.
If a view references a foreign table, and the foreign table has a
BEFORE INSERT trigger, then it's possible for a tuple inserted or
updated through the view to be changed such that it violates the
view's WITH CHECK OPTION constraint.
Before this commit, postgres_fdw handled this case inconsistently. A
RETURNING clause on the INSERT or UPDATE statement targeting the view
would cause the finally-inserted tuple to be read back, and the WITH
CHECK OPTION violation would throw an error. But without a RETURNING
clause, postgres_fdw would not read the final tuple back, and WITH
CHECK OPTION would not throw an error for the violation (or may throw
an error when there is no real violation). AFTER ROW triggers on the
foreign table had a similar effect as a RETURNING clause on the INSERT
or UPDATE statement.
To fix, this commit retrieves the attributes needed to enforce the
WITH CHECK OPTION constraint along with the attributes needed for the
RETURNING clause (if any) from the remote side. Thus, the WITH CHECK
OPTION constraint is always evaluated against the final tuple after
any triggers on the remote side.
This fix may be considered inconsistent with CHECK constraints
declared on foreign tables, which are not enforced locally at all
(because the constraint is on a remote object). The discussion
concluded that this difference is reasonable, because the WITH CHECK
OPTION is a constraint on the local view (not any remote object);
therefore it only makes sense to enforce its WITH CHECK OPTION
constraint locally.
Author: Etsuro Fujita
Reviewed-by: Arthur Zakirov, Stephen Frost
Discussion: https://www.postgresql.org/message-id/7eb58fab-fd3b-781b-ac33-f7cfec96021f%40lab.ntt.co.jp
2018-07-08 09:14:51 +02:00
|
|
|
List *withCheckOptionList, List *returningList,
|
2021-01-20 23:05:46 +01:00
|
|
|
List **retrieved_attrs, int *values_end_len)
|
2013-03-10 19:14:53 +01:00
|
|
|
{
|
2021-08-05 13:00:00 +02:00
|
|
|
TupleDesc tupdesc = RelationGetDescr(rel);
|
2013-03-10 19:14:53 +01:00
|
|
|
AttrNumber pindex;
|
|
|
|
bool first;
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
appendStringInfoString(buf, "INSERT INTO ");
|
2013-03-12 23:58:13 +01:00
|
|
|
deparseRelation(buf, rel);
|
2013-03-10 19:14:53 +01:00
|
|
|
|
2013-03-11 19:26:05 +01:00
|
|
|
if (targetAttrs)
|
2013-03-10 19:14:53 +01:00
|
|
|
{
|
2013-10-31 15:55:59 +01:00
|
|
|
appendStringInfoChar(buf, '(');
|
2013-03-10 19:14:53 +01:00
|
|
|
|
2013-03-11 19:26:05 +01:00
|
|
|
first = true;
|
|
|
|
foreach(lc, targetAttrs)
|
|
|
|
{
|
|
|
|
int attnum = lfirst_int(lc);
|
2013-03-10 19:14:53 +01:00
|
|
|
|
2013-03-11 19:26:05 +01:00
|
|
|
if (!first)
|
|
|
|
appendStringInfoString(buf, ", ");
|
|
|
|
first = false;
|
2013-03-10 19:14:53 +01:00
|
|
|
|
Fix interaction of foreign tuple routing with remote triggers.
Without these fixes, changes to the inserted tuple made by remote
triggers are ignored when building local RETURNING tuples.
In the core code, call ExecInitRoutingInfo at a later point from
within ExecInitPartitionInfo so that the FDW callback gets invoked
after the returning list has been built. But move CheckValidResultRel
out of ExecInitRoutingInfo so that it can happen at an earlier stage.
In postgres_fdw, refactor assorted deparsing functions to work with
the RTE rather than the PlannerInfo, which saves us having to
construct a fake PlannerInfo in cases where we don't have a real one.
Then, we can pass down a constructed RTE that yields the correct
deparse result when no real one exists. Unfortunately, this
necessitates a hack that understands how the core code manages RT
indexes for update tuple routing, which is ugly, but we don't have a
better idea right now.
Original report, analysis, and patch by Etsuro Fujita. Heavily
refactored by me. Then worked over some more by Amit Langote.
Discussion: http://postgr.es/m/5AD4882B.10002@lab.ntt.co.jp
2018-05-01 19:21:46 +02:00
|
|
|
deparseColumnRef(buf, rtindex, attnum, rte, false);
|
2013-03-11 19:26:05 +01:00
|
|
|
}
|
2013-03-10 19:14:53 +01:00
|
|
|
|
2013-03-11 19:26:05 +01:00
|
|
|
appendStringInfoString(buf, ") VALUES (");
|
2013-03-10 19:14:53 +01:00
|
|
|
|
2013-03-11 19:26:05 +01:00
|
|
|
pindex = 1;
|
|
|
|
first = true;
|
|
|
|
foreach(lc, targetAttrs)
|
|
|
|
{
|
2021-08-05 13:00:00 +02:00
|
|
|
int attnum = lfirst_int(lc);
|
|
|
|
Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
|
|
|
|
|
2013-03-11 19:26:05 +01:00
|
|
|
if (!first)
|
|
|
|
appendStringInfoString(buf, ", ");
|
|
|
|
first = false;
|
|
|
|
|
2021-08-05 13:00:00 +02:00
|
|
|
if (attr->attgenerated)
|
|
|
|
appendStringInfoString(buf, "DEFAULT");
|
|
|
|
else
|
|
|
|
{
|
|
|
|
appendStringInfo(buf, "$%d", pindex);
|
|
|
|
pindex++;
|
|
|
|
}
|
2013-03-11 19:26:05 +01:00
|
|
|
}
|
2013-03-10 19:14:53 +01:00
|
|
|
|
2013-10-31 15:55:59 +01:00
|
|
|
appendStringInfoChar(buf, ')');
|
2013-03-11 19:26:05 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
appendStringInfoString(buf, " DEFAULT VALUES");
|
2021-01-20 23:05:46 +01:00
|
|
|
*values_end_len = buf->len;
|
2013-03-10 19:14:53 +01:00
|
|
|
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
2015-05-08 05:31:36 +02:00
|
|
|
if (doNothing)
|
|
|
|
appendStringInfoString(buf, " ON CONFLICT DO NOTHING");
|
|
|
|
|
Fix interaction of foreign tuple routing with remote triggers.
Without these fixes, changes to the inserted tuple made by remote
triggers are ignored when building local RETURNING tuples.
In the core code, call ExecInitRoutingInfo at a later point from
within ExecInitPartitionInfo so that the FDW callback gets invoked
after the returning list has been built. But move CheckValidResultRel
out of ExecInitRoutingInfo so that it can happen at an earlier stage.
In postgres_fdw, refactor assorted deparsing functions to work with
the RTE rather than the PlannerInfo, which saves us having to
construct a fake PlannerInfo in cases where we don't have a real one.
Then, we can pass down a constructed RTE that yields the correct
deparse result when no real one exists. Unfortunately, this
necessitates a hack that understands how the core code manages RT
indexes for update tuple routing, which is ugly, but we don't have a
better idea right now.
Original report, analysis, and patch by Etsuro Fujita. Heavily
refactored by me. Then worked over some more by Amit Langote.
Discussion: http://postgr.es/m/5AD4882B.10002@lab.ntt.co.jp
2018-05-01 19:21:46 +02:00
|
|
|
deparseReturningList(buf, rte, rtindex, rel,
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
rel->trigdesc && rel->trigdesc->trig_insert_after_row,
|
Fix WITH CHECK OPTION on views referencing postgres_fdw tables.
If a view references a foreign table, and the foreign table has a
BEFORE INSERT trigger, then it's possible for a tuple inserted or
updated through the view to be changed such that it violates the
view's WITH CHECK OPTION constraint.
Before this commit, postgres_fdw handled this case inconsistently. A
RETURNING clause on the INSERT or UPDATE statement targeting the view
would cause the finally-inserted tuple to be read back, and the WITH
CHECK OPTION violation would throw an error. But without a RETURNING
clause, postgres_fdw would not read the final tuple back, and WITH
CHECK OPTION would not throw an error for the violation (or may throw
an error when there is no real violation). AFTER ROW triggers on the
foreign table had a similar effect as a RETURNING clause on the INSERT
or UPDATE statement.
To fix, this commit retrieves the attributes needed to enforce the
WITH CHECK OPTION constraint along with the attributes needed for the
RETURNING clause (if any) from the remote side. Thus, the WITH CHECK
OPTION constraint is always evaluated against the final tuple after
any triggers on the remote side.
This fix may be considered inconsistent with CHECK constraints
declared on foreign tables, which are not enforced locally at all
(because the constraint is on a remote object). The discussion
concluded that this difference is reasonable, because the WITH CHECK
OPTION is a constraint on the local view (not any remote object);
therefore it only makes sense to enforce its WITH CHECK OPTION
constraint locally.
Author: Etsuro Fujita
Reviewed-by: Arthur Zakirov, Stephen Frost
Discussion: https://www.postgresql.org/message-id/7eb58fab-fd3b-781b-ac33-f7cfec96021f%40lab.ntt.co.jp
2018-07-08 09:14:51 +02:00
|
|
|
withCheckOptionList, returningList, retrieved_attrs);
|
2013-03-10 19:14:53 +01:00
|
|
|
}
|
|
|
|
|
2021-01-20 23:05:46 +01:00
|
|
|
/*
|
|
|
|
* rebuild remote INSERT statement
|
|
|
|
*
|
|
|
|
* Provided a number of rows in a batch, builds INSERT statement with the
|
|
|
|
* right number of parameters.
|
|
|
|
*/
|
|
|
|
void
|
2021-08-05 13:00:00 +02:00
|
|
|
rebuildInsertSql(StringInfo buf, Relation rel,
|
|
|
|
char *orig_query, List *target_attrs,
|
|
|
|
int values_end_len, int num_params,
|
2021-01-20 23:05:46 +01:00
|
|
|
int num_rows)
|
|
|
|
{
|
2021-08-05 13:00:00 +02:00
|
|
|
TupleDesc tupdesc = RelationGetDescr(rel);
|
|
|
|
int i;
|
2021-01-20 23:05:46 +01:00
|
|
|
int pindex;
|
|
|
|
bool first;
|
2021-08-05 13:00:00 +02:00
|
|
|
ListCell *lc;
|
2021-01-20 23:05:46 +01:00
|
|
|
|
|
|
|
/* Make sure the values_end_len is sensible */
|
|
|
|
Assert((values_end_len > 0) && (values_end_len <= strlen(orig_query)));
|
|
|
|
|
|
|
|
/* Copy up to the end of the first record from the original query */
|
|
|
|
appendBinaryStringInfo(buf, orig_query, values_end_len);
|
|
|
|
|
|
|
|
/*
|
2021-05-12 19:14:10 +02:00
|
|
|
* Add records to VALUES clause (we already have parameters for the first
|
|
|
|
* row, so start at the right offset).
|
2021-01-20 23:05:46 +01:00
|
|
|
*/
|
2021-08-05 13:00:00 +02:00
|
|
|
pindex = num_params + 1;
|
2021-01-20 23:05:46 +01:00
|
|
|
for (i = 0; i < num_rows; i++)
|
|
|
|
{
|
|
|
|
appendStringInfoString(buf, ", (");
|
|
|
|
|
|
|
|
first = true;
|
2021-08-05 13:00:00 +02:00
|
|
|
foreach(lc, target_attrs)
|
2021-01-20 23:05:46 +01:00
|
|
|
{
|
2021-08-05 13:00:00 +02:00
|
|
|
int attnum = lfirst_int(lc);
|
|
|
|
Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
|
|
|
|
|
2021-01-20 23:05:46 +01:00
|
|
|
if (!first)
|
|
|
|
appendStringInfoString(buf, ", ");
|
|
|
|
first = false;
|
|
|
|
|
2021-08-05 13:00:00 +02:00
|
|
|
if (attr->attgenerated)
|
|
|
|
appendStringInfoString(buf, "DEFAULT");
|
|
|
|
else
|
|
|
|
{
|
|
|
|
appendStringInfo(buf, "$%d", pindex);
|
|
|
|
pindex++;
|
|
|
|
}
|
2021-01-20 23:05:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
appendStringInfoChar(buf, ')');
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy stuff after VALUES clause from the original query */
|
|
|
|
appendStringInfoString(buf, orig_query + values_end_len);
|
|
|
|
}
|
|
|
|
|
2013-03-10 19:14:53 +01:00
|
|
|
/*
|
|
|
|
* deparse remote UPDATE statement
|
2013-03-22 05:31:11 +01:00
|
|
|
*
|
|
|
|
* The statement text is appended to buf, and we also create an integer List
|
Fix WITH CHECK OPTION on views referencing postgres_fdw tables.
If a view references a foreign table, and the foreign table has a
BEFORE INSERT trigger, then it's possible for a tuple inserted or
updated through the view to be changed such that it violates the
view's WITH CHECK OPTION constraint.
Before this commit, postgres_fdw handled this case inconsistently. A
RETURNING clause on the INSERT or UPDATE statement targeting the view
would cause the finally-inserted tuple to be read back, and the WITH
CHECK OPTION violation would throw an error. But without a RETURNING
clause, postgres_fdw would not read the final tuple back, and WITH
CHECK OPTION would not throw an error for the violation (or may throw
an error when there is no real violation). AFTER ROW triggers on the
foreign table had a similar effect as a RETURNING clause on the INSERT
or UPDATE statement.
To fix, this commit retrieves the attributes needed to enforce the
WITH CHECK OPTION constraint along with the attributes needed for the
RETURNING clause (if any) from the remote side. Thus, the WITH CHECK
OPTION constraint is always evaluated against the final tuple after
any triggers on the remote side.
This fix may be considered inconsistent with CHECK constraints
declared on foreign tables, which are not enforced locally at all
(because the constraint is on a remote object). The discussion
concluded that this difference is reasonable, because the WITH CHECK
OPTION is a constraint on the local view (not any remote object);
therefore it only makes sense to enforce its WITH CHECK OPTION
constraint locally.
Author: Etsuro Fujita
Reviewed-by: Arthur Zakirov, Stephen Frost
Discussion: https://www.postgresql.org/message-id/7eb58fab-fd3b-781b-ac33-f7cfec96021f%40lab.ntt.co.jp
2018-07-08 09:14:51 +02:00
|
|
|
* of the columns being retrieved by WITH CHECK OPTION or RETURNING (if any),
|
|
|
|
* which is returned to *retrieved_attrs.
|
2013-03-10 19:14:53 +01:00
|
|
|
*/
|
|
|
|
void
|
Fix interaction of foreign tuple routing with remote triggers.
Without these fixes, changes to the inserted tuple made by remote
triggers are ignored when building local RETURNING tuples.
In the core code, call ExecInitRoutingInfo at a later point from
within ExecInitPartitionInfo so that the FDW callback gets invoked
after the returning list has been built. But move CheckValidResultRel
out of ExecInitRoutingInfo so that it can happen at an earlier stage.
In postgres_fdw, refactor assorted deparsing functions to work with
the RTE rather than the PlannerInfo, which saves us having to
construct a fake PlannerInfo in cases where we don't have a real one.
Then, we can pass down a constructed RTE that yields the correct
deparse result when no real one exists. Unfortunately, this
necessitates a hack that understands how the core code manages RT
indexes for update tuple routing, which is ugly, but we don't have a
better idea right now.
Original report, analysis, and patch by Etsuro Fujita. Heavily
refactored by me. Then worked over some more by Amit Langote.
Discussion: http://postgr.es/m/5AD4882B.10002@lab.ntt.co.jp
2018-05-01 19:21:46 +02:00
|
|
|
deparseUpdateSql(StringInfo buf, RangeTblEntry *rte,
|
2013-03-12 23:58:13 +01:00
|
|
|
Index rtindex, Relation rel,
|
Fix WITH CHECK OPTION on views referencing postgres_fdw tables.
If a view references a foreign table, and the foreign table has a
BEFORE INSERT trigger, then it's possible for a tuple inserted or
updated through the view to be changed such that it violates the
view's WITH CHECK OPTION constraint.
Before this commit, postgres_fdw handled this case inconsistently. A
RETURNING clause on the INSERT or UPDATE statement targeting the view
would cause the finally-inserted tuple to be read back, and the WITH
CHECK OPTION violation would throw an error. But without a RETURNING
clause, postgres_fdw would not read the final tuple back, and WITH
CHECK OPTION would not throw an error for the violation (or may throw
an error when there is no real violation). AFTER ROW triggers on the
foreign table had a similar effect as a RETURNING clause on the INSERT
or UPDATE statement.
To fix, this commit retrieves the attributes needed to enforce the
WITH CHECK OPTION constraint along with the attributes needed for the
RETURNING clause (if any) from the remote side. Thus, the WITH CHECK
OPTION constraint is always evaluated against the final tuple after
any triggers on the remote side.
This fix may be considered inconsistent with CHECK constraints
declared on foreign tables, which are not enforced locally at all
(because the constraint is on a remote object). The discussion
concluded that this difference is reasonable, because the WITH CHECK
OPTION is a constraint on the local view (not any remote object);
therefore it only makes sense to enforce its WITH CHECK OPTION
constraint locally.
Author: Etsuro Fujita
Reviewed-by: Arthur Zakirov, Stephen Frost
Discussion: https://www.postgresql.org/message-id/7eb58fab-fd3b-781b-ac33-f7cfec96021f%40lab.ntt.co.jp
2018-07-08 09:14:51 +02:00
|
|
|
List *targetAttrs,
|
|
|
|
List *withCheckOptionList, List *returningList,
|
2013-03-22 05:31:11 +01:00
|
|
|
List **retrieved_attrs)
|
2013-03-10 19:14:53 +01:00
|
|
|
{
|
2021-08-05 13:00:00 +02:00
|
|
|
TupleDesc tupdesc = RelationGetDescr(rel);
|
2013-03-10 19:14:53 +01:00
|
|
|
AttrNumber pindex;
|
|
|
|
bool first;
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
appendStringInfoString(buf, "UPDATE ");
|
2013-03-12 23:58:13 +01:00
|
|
|
deparseRelation(buf, rel);
|
2013-03-10 19:14:53 +01:00
|
|
|
appendStringInfoString(buf, " SET ");
|
|
|
|
|
|
|
|
pindex = 2; /* ctid is always the first param */
|
|
|
|
first = true;
|
|
|
|
foreach(lc, targetAttrs)
|
|
|
|
{
|
|
|
|
int attnum = lfirst_int(lc);
|
2021-08-05 13:00:00 +02:00
|
|
|
Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
|
2013-03-10 19:14:53 +01:00
|
|
|
|
|
|
|
if (!first)
|
|
|
|
appendStringInfoString(buf, ", ");
|
|
|
|
first = false;
|
|
|
|
|
Fix interaction of foreign tuple routing with remote triggers.
Without these fixes, changes to the inserted tuple made by remote
triggers are ignored when building local RETURNING tuples.
In the core code, call ExecInitRoutingInfo at a later point from
within ExecInitPartitionInfo so that the FDW callback gets invoked
after the returning list has been built. But move CheckValidResultRel
out of ExecInitRoutingInfo so that it can happen at an earlier stage.
In postgres_fdw, refactor assorted deparsing functions to work with
the RTE rather than the PlannerInfo, which saves us having to
construct a fake PlannerInfo in cases where we don't have a real one.
Then, we can pass down a constructed RTE that yields the correct
deparse result when no real one exists. Unfortunately, this
necessitates a hack that understands how the core code manages RT
indexes for update tuple routing, which is ugly, but we don't have a
better idea right now.
Original report, analysis, and patch by Etsuro Fujita. Heavily
refactored by me. Then worked over some more by Amit Langote.
Discussion: http://postgr.es/m/5AD4882B.10002@lab.ntt.co.jp
2018-05-01 19:21:46 +02:00
|
|
|
deparseColumnRef(buf, rtindex, attnum, rte, false);
|
2021-08-05 13:00:00 +02:00
|
|
|
if (attr->attgenerated)
|
|
|
|
appendStringInfoString(buf, " = DEFAULT");
|
|
|
|
else
|
|
|
|
{
|
|
|
|
appendStringInfo(buf, " = $%d", pindex);
|
|
|
|
pindex++;
|
|
|
|
}
|
2013-03-10 19:14:53 +01:00
|
|
|
}
|
|
|
|
appendStringInfoString(buf, " WHERE ctid = $1");
|
|
|
|
|
Fix interaction of foreign tuple routing with remote triggers.
Without these fixes, changes to the inserted tuple made by remote
triggers are ignored when building local RETURNING tuples.
In the core code, call ExecInitRoutingInfo at a later point from
within ExecInitPartitionInfo so that the FDW callback gets invoked
after the returning list has been built. But move CheckValidResultRel
out of ExecInitRoutingInfo so that it can happen at an earlier stage.
In postgres_fdw, refactor assorted deparsing functions to work with
the RTE rather than the PlannerInfo, which saves us having to
construct a fake PlannerInfo in cases where we don't have a real one.
Then, we can pass down a constructed RTE that yields the correct
deparse result when no real one exists. Unfortunately, this
necessitates a hack that understands how the core code manages RT
indexes for update tuple routing, which is ugly, but we don't have a
better idea right now.
Original report, analysis, and patch by Etsuro Fujita. Heavily
refactored by me. Then worked over some more by Amit Langote.
Discussion: http://postgr.es/m/5AD4882B.10002@lab.ntt.co.jp
2018-05-01 19:21:46 +02:00
|
|
|
deparseReturningList(buf, rte, rtindex, rel,
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
rel->trigdesc && rel->trigdesc->trig_update_after_row,
|
Fix WITH CHECK OPTION on views referencing postgres_fdw tables.
If a view references a foreign table, and the foreign table has a
BEFORE INSERT trigger, then it's possible for a tuple inserted or
updated through the view to be changed such that it violates the
view's WITH CHECK OPTION constraint.
Before this commit, postgres_fdw handled this case inconsistently. A
RETURNING clause on the INSERT or UPDATE statement targeting the view
would cause the finally-inserted tuple to be read back, and the WITH
CHECK OPTION violation would throw an error. But without a RETURNING
clause, postgres_fdw would not read the final tuple back, and WITH
CHECK OPTION would not throw an error for the violation (or may throw
an error when there is no real violation). AFTER ROW triggers on the
foreign table had a similar effect as a RETURNING clause on the INSERT
or UPDATE statement.
To fix, this commit retrieves the attributes needed to enforce the
WITH CHECK OPTION constraint along with the attributes needed for the
RETURNING clause (if any) from the remote side. Thus, the WITH CHECK
OPTION constraint is always evaluated against the final tuple after
any triggers on the remote side.
This fix may be considered inconsistent with CHECK constraints
declared on foreign tables, which are not enforced locally at all
(because the constraint is on a remote object). The discussion
concluded that this difference is reasonable, because the WITH CHECK
OPTION is a constraint on the local view (not any remote object);
therefore it only makes sense to enforce its WITH CHECK OPTION
constraint locally.
Author: Etsuro Fujita
Reviewed-by: Arthur Zakirov, Stephen Frost
Discussion: https://www.postgresql.org/message-id/7eb58fab-fd3b-781b-ac33-f7cfec96021f%40lab.ntt.co.jp
2018-07-08 09:14:51 +02:00
|
|
|
withCheckOptionList, returningList, retrieved_attrs);
|
2013-03-10 19:14:53 +01:00
|
|
|
}
|
|
|
|
|
2016-03-18 18:48:58 +01:00
|
|
|
/*
|
|
|
|
* deparse remote UPDATE statement
|
|
|
|
*
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
* 'buf' is the output buffer to append the statement to
|
|
|
|
* 'rtindex' is the RT index of the associated target relation
|
|
|
|
* 'rel' is the relation descriptor for the target relation
|
|
|
|
* 'foreignrel' is the RelOptInfo for the target relation or the join relation
|
|
|
|
* containing all base relations in the query
|
|
|
|
* 'targetlist' is the tlist of the underlying foreign-scan plan node
|
Rework planning and execution of UPDATE and DELETE.
This patch makes two closely related sets of changes:
1. For UPDATE, the subplan of the ModifyTable node now only delivers
the new values of the changed columns (i.e., the expressions computed
in the query's SET clause) plus row identity information such as CTID.
ModifyTable must re-fetch the original tuple to merge in the old
values of any unchanged columns. The core advantage of this is that
the changed columns are uniform across all tables of an inherited or
partitioned target relation, whereas the other columns might not be.
A secondary advantage, when the UPDATE involves joins, is that less
data needs to pass through the plan tree. The disadvantage of course
is an extra fetch of each tuple to be updated. However, that seems to
be very nearly free in context; even worst-case tests don't show it to
add more than a couple percent to the total query cost. At some point
it might be interesting to combine the re-fetch with the tuple access
that ModifyTable must do anyway to mark the old tuple dead; but that
would require a good deal of refactoring and it seems it wouldn't buy
all that much, so this patch doesn't attempt it.
2. For inherited UPDATE/DELETE, instead of generating a separate
subplan for each target relation, we now generate a single subplan
that is just exactly like a SELECT's plan, then stick ModifyTable
on top of that. To let ModifyTable know which target relation a
given incoming row refers to, a tableoid junk column is added to
the row identity information. This gets rid of the horrid hack
that was inheritance_planner(), eliminating O(N^2) planning cost
and memory consumption in cases where there were many unprunable
target relations.
Point 2 of course requires point 1, so that there is a uniform
definition of the non-junk columns to be returned by the subplan.
We can't insist on uniform definition of the row identity junk
columns however, if we want to keep the ability to have both
plain and foreign tables in a partitioning hierarchy. Since
it wouldn't scale very far to have every child table have its
own row identity column, this patch includes provisions to merge
similar row identity columns into one column of the subplan result.
In particular, we can merge the whole-row Vars typically used as
row identity by FDWs into one column by pretending they are type
RECORD. (It's still okay for the actual composite Datums to be
labeled with the table's rowtype OID, though.)
There is more that can be done to file down residual inefficiencies
in this patch, but it seems to be committable now.
FDW authors should note several API changes:
* The argument list for AddForeignUpdateTargets() has changed, and so
has the method it must use for adding junk columns to the query. Call
add_row_identity_var() instead of manipulating the parse tree directly.
You might want to reconsider exactly what you're adding, too.
* PlanDirectModify() must now work a little harder to find the
ForeignScan plan node; if the foreign table is part of a partitioning
hierarchy then the ForeignScan might not be the direct child of
ModifyTable. See postgres_fdw for sample code.
* To check whether a relation is a target relation, it's no
longer sufficient to compare its relid to root->parse->resultRelation.
Instead, check it against all_result_relids or leaf_result_relids,
as appropriate.
Amit Langote and Tom Lane
Discussion: https://postgr.es/m/CA+HiwqHpHdqdDn48yCEhynnniahH78rwcrv1rEX65-fsZGBOLQ@mail.gmail.com
2021-03-31 17:52:34 +02:00
|
|
|
* (note that this only contains new-value expressions and junk attrs)
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
* 'targetAttrs' is the target columns of the UPDATE
|
|
|
|
* 'remote_conds' is the qual clauses that must be evaluated remotely
|
|
|
|
* '*params_list' is an output list of exprs that will become remote Params
|
|
|
|
* 'returningList' is the RETURNING targetlist
|
|
|
|
* '*retrieved_attrs' is an output list of integers of columns being retrieved
|
|
|
|
* by RETURNING (if any)
|
2016-03-18 18:48:58 +01:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
deparseDirectUpdateSql(StringInfo buf, PlannerInfo *root,
|
|
|
|
Index rtindex, Relation rel,
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
RelOptInfo *foreignrel,
|
2016-03-18 18:48:58 +01:00
|
|
|
List *targetlist,
|
|
|
|
List *targetAttrs,
|
|
|
|
List *remote_conds,
|
|
|
|
List **params_list,
|
|
|
|
List *returningList,
|
|
|
|
List **retrieved_attrs)
|
|
|
|
{
|
|
|
|
deparse_expr_cxt context;
|
|
|
|
int nestlevel;
|
|
|
|
bool first;
|
Fix interaction of foreign tuple routing with remote triggers.
Without these fixes, changes to the inserted tuple made by remote
triggers are ignored when building local RETURNING tuples.
In the core code, call ExecInitRoutingInfo at a later point from
within ExecInitPartitionInfo so that the FDW callback gets invoked
after the returning list has been built. But move CheckValidResultRel
out of ExecInitRoutingInfo so that it can happen at an earlier stage.
In postgres_fdw, refactor assorted deparsing functions to work with
the RTE rather than the PlannerInfo, which saves us having to
construct a fake PlannerInfo in cases where we don't have a real one.
Then, we can pass down a constructed RTE that yields the correct
deparse result when no real one exists. Unfortunately, this
necessitates a hack that understands how the core code manages RT
indexes for update tuple routing, which is ugly, but we don't have a
better idea right now.
Original report, analysis, and patch by Etsuro Fujita. Heavily
refactored by me. Then worked over some more by Amit Langote.
Discussion: http://postgr.es/m/5AD4882B.10002@lab.ntt.co.jp
2018-05-01 19:21:46 +02:00
|
|
|
RangeTblEntry *rte = planner_rt_fetch(rtindex, root);
|
Rework planning and execution of UPDATE and DELETE.
This patch makes two closely related sets of changes:
1. For UPDATE, the subplan of the ModifyTable node now only delivers
the new values of the changed columns (i.e., the expressions computed
in the query's SET clause) plus row identity information such as CTID.
ModifyTable must re-fetch the original tuple to merge in the old
values of any unchanged columns. The core advantage of this is that
the changed columns are uniform across all tables of an inherited or
partitioned target relation, whereas the other columns might not be.
A secondary advantage, when the UPDATE involves joins, is that less
data needs to pass through the plan tree. The disadvantage of course
is an extra fetch of each tuple to be updated. However, that seems to
be very nearly free in context; even worst-case tests don't show it to
add more than a couple percent to the total query cost. At some point
it might be interesting to combine the re-fetch with the tuple access
that ModifyTable must do anyway to mark the old tuple dead; but that
would require a good deal of refactoring and it seems it wouldn't buy
all that much, so this patch doesn't attempt it.
2. For inherited UPDATE/DELETE, instead of generating a separate
subplan for each target relation, we now generate a single subplan
that is just exactly like a SELECT's plan, then stick ModifyTable
on top of that. To let ModifyTable know which target relation a
given incoming row refers to, a tableoid junk column is added to
the row identity information. This gets rid of the horrid hack
that was inheritance_planner(), eliminating O(N^2) planning cost
and memory consumption in cases where there were many unprunable
target relations.
Point 2 of course requires point 1, so that there is a uniform
definition of the non-junk columns to be returned by the subplan.
We can't insist on uniform definition of the row identity junk
columns however, if we want to keep the ability to have both
plain and foreign tables in a partitioning hierarchy. Since
it wouldn't scale very far to have every child table have its
own row identity column, this patch includes provisions to merge
similar row identity columns into one column of the subplan result.
In particular, we can merge the whole-row Vars typically used as
row identity by FDWs into one column by pretending they are type
RECORD. (It's still okay for the actual composite Datums to be
labeled with the table's rowtype OID, though.)
There is more that can be done to file down residual inefficiencies
in this patch, but it seems to be committable now.
FDW authors should note several API changes:
* The argument list for AddForeignUpdateTargets() has changed, and so
has the method it must use for adding junk columns to the query. Call
add_row_identity_var() instead of manipulating the parse tree directly.
You might want to reconsider exactly what you're adding, too.
* PlanDirectModify() must now work a little harder to find the
ForeignScan plan node; if the foreign table is part of a partitioning
hierarchy then the ForeignScan might not be the direct child of
ModifyTable. See postgres_fdw for sample code.
* To check whether a relation is a target relation, it's no
longer sufficient to compare its relid to root->parse->resultRelation.
Instead, check it against all_result_relids or leaf_result_relids,
as appropriate.
Amit Langote and Tom Lane
Discussion: https://postgr.es/m/CA+HiwqHpHdqdDn48yCEhynnniahH78rwcrv1rEX65-fsZGBOLQ@mail.gmail.com
2021-03-31 17:52:34 +02:00
|
|
|
ListCell *lc,
|
|
|
|
*lc2;
|
2016-03-18 18:48:58 +01:00
|
|
|
|
|
|
|
/* Set up context struct for recursion */
|
|
|
|
context.root = root;
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
context.foreignrel = foreignrel;
|
|
|
|
context.scanrel = foreignrel;
|
2016-03-18 18:48:58 +01:00
|
|
|
context.buf = buf;
|
|
|
|
context.params_list = params_list;
|
|
|
|
|
|
|
|
appendStringInfoString(buf, "UPDATE ");
|
|
|
|
deparseRelation(buf, rel);
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
if (foreignrel->reloptkind == RELOPT_JOINREL)
|
|
|
|
appendStringInfo(buf, " %s%d", REL_ALIAS_PREFIX, rtindex);
|
2016-03-18 18:48:58 +01:00
|
|
|
appendStringInfoString(buf, " SET ");
|
|
|
|
|
|
|
|
/* Make sure any constants in the exprs are printed portably */
|
|
|
|
nestlevel = set_transmission_modes();
|
|
|
|
|
|
|
|
first = true;
|
Rework planning and execution of UPDATE and DELETE.
This patch makes two closely related sets of changes:
1. For UPDATE, the subplan of the ModifyTable node now only delivers
the new values of the changed columns (i.e., the expressions computed
in the query's SET clause) plus row identity information such as CTID.
ModifyTable must re-fetch the original tuple to merge in the old
values of any unchanged columns. The core advantage of this is that
the changed columns are uniform across all tables of an inherited or
partitioned target relation, whereas the other columns might not be.
A secondary advantage, when the UPDATE involves joins, is that less
data needs to pass through the plan tree. The disadvantage of course
is an extra fetch of each tuple to be updated. However, that seems to
be very nearly free in context; even worst-case tests don't show it to
add more than a couple percent to the total query cost. At some point
it might be interesting to combine the re-fetch with the tuple access
that ModifyTable must do anyway to mark the old tuple dead; but that
would require a good deal of refactoring and it seems it wouldn't buy
all that much, so this patch doesn't attempt it.
2. For inherited UPDATE/DELETE, instead of generating a separate
subplan for each target relation, we now generate a single subplan
that is just exactly like a SELECT's plan, then stick ModifyTable
on top of that. To let ModifyTable know which target relation a
given incoming row refers to, a tableoid junk column is added to
the row identity information. This gets rid of the horrid hack
that was inheritance_planner(), eliminating O(N^2) planning cost
and memory consumption in cases where there were many unprunable
target relations.
Point 2 of course requires point 1, so that there is a uniform
definition of the non-junk columns to be returned by the subplan.
We can't insist on uniform definition of the row identity junk
columns however, if we want to keep the ability to have both
plain and foreign tables in a partitioning hierarchy. Since
it wouldn't scale very far to have every child table have its
own row identity column, this patch includes provisions to merge
similar row identity columns into one column of the subplan result.
In particular, we can merge the whole-row Vars typically used as
row identity by FDWs into one column by pretending they are type
RECORD. (It's still okay for the actual composite Datums to be
labeled with the table's rowtype OID, though.)
There is more that can be done to file down residual inefficiencies
in this patch, but it seems to be committable now.
FDW authors should note several API changes:
* The argument list for AddForeignUpdateTargets() has changed, and so
has the method it must use for adding junk columns to the query. Call
add_row_identity_var() instead of manipulating the parse tree directly.
You might want to reconsider exactly what you're adding, too.
* PlanDirectModify() must now work a little harder to find the
ForeignScan plan node; if the foreign table is part of a partitioning
hierarchy then the ForeignScan might not be the direct child of
ModifyTable. See postgres_fdw for sample code.
* To check whether a relation is a target relation, it's no
longer sufficient to compare its relid to root->parse->resultRelation.
Instead, check it against all_result_relids or leaf_result_relids,
as appropriate.
Amit Langote and Tom Lane
Discussion: https://postgr.es/m/CA+HiwqHpHdqdDn48yCEhynnniahH78rwcrv1rEX65-fsZGBOLQ@mail.gmail.com
2021-03-31 17:52:34 +02:00
|
|
|
forboth(lc, targetlist, lc2, targetAttrs)
|
2016-03-18 18:48:58 +01:00
|
|
|
{
|
Rework planning and execution of UPDATE and DELETE.
This patch makes two closely related sets of changes:
1. For UPDATE, the subplan of the ModifyTable node now only delivers
the new values of the changed columns (i.e., the expressions computed
in the query's SET clause) plus row identity information such as CTID.
ModifyTable must re-fetch the original tuple to merge in the old
values of any unchanged columns. The core advantage of this is that
the changed columns are uniform across all tables of an inherited or
partitioned target relation, whereas the other columns might not be.
A secondary advantage, when the UPDATE involves joins, is that less
data needs to pass through the plan tree. The disadvantage of course
is an extra fetch of each tuple to be updated. However, that seems to
be very nearly free in context; even worst-case tests don't show it to
add more than a couple percent to the total query cost. At some point
it might be interesting to combine the re-fetch with the tuple access
that ModifyTable must do anyway to mark the old tuple dead; but that
would require a good deal of refactoring and it seems it wouldn't buy
all that much, so this patch doesn't attempt it.
2. For inherited UPDATE/DELETE, instead of generating a separate
subplan for each target relation, we now generate a single subplan
that is just exactly like a SELECT's plan, then stick ModifyTable
on top of that. To let ModifyTable know which target relation a
given incoming row refers to, a tableoid junk column is added to
the row identity information. This gets rid of the horrid hack
that was inheritance_planner(), eliminating O(N^2) planning cost
and memory consumption in cases where there were many unprunable
target relations.
Point 2 of course requires point 1, so that there is a uniform
definition of the non-junk columns to be returned by the subplan.
We can't insist on uniform definition of the row identity junk
columns however, if we want to keep the ability to have both
plain and foreign tables in a partitioning hierarchy. Since
it wouldn't scale very far to have every child table have its
own row identity column, this patch includes provisions to merge
similar row identity columns into one column of the subplan result.
In particular, we can merge the whole-row Vars typically used as
row identity by FDWs into one column by pretending they are type
RECORD. (It's still okay for the actual composite Datums to be
labeled with the table's rowtype OID, though.)
There is more that can be done to file down residual inefficiencies
in this patch, but it seems to be committable now.
FDW authors should note several API changes:
* The argument list for AddForeignUpdateTargets() has changed, and so
has the method it must use for adding junk columns to the query. Call
add_row_identity_var() instead of manipulating the parse tree directly.
You might want to reconsider exactly what you're adding, too.
* PlanDirectModify() must now work a little harder to find the
ForeignScan plan node; if the foreign table is part of a partitioning
hierarchy then the ForeignScan might not be the direct child of
ModifyTable. See postgres_fdw for sample code.
* To check whether a relation is a target relation, it's no
longer sufficient to compare its relid to root->parse->resultRelation.
Instead, check it against all_result_relids or leaf_result_relids,
as appropriate.
Amit Langote and Tom Lane
Discussion: https://postgr.es/m/CA+HiwqHpHdqdDn48yCEhynnniahH78rwcrv1rEX65-fsZGBOLQ@mail.gmail.com
2021-03-31 17:52:34 +02:00
|
|
|
TargetEntry *tle = lfirst_node(TargetEntry, lc);
|
|
|
|
int attnum = lfirst_int(lc2);
|
2016-03-18 18:48:58 +01:00
|
|
|
|
Rework planning and execution of UPDATE and DELETE.
This patch makes two closely related sets of changes:
1. For UPDATE, the subplan of the ModifyTable node now only delivers
the new values of the changed columns (i.e., the expressions computed
in the query's SET clause) plus row identity information such as CTID.
ModifyTable must re-fetch the original tuple to merge in the old
values of any unchanged columns. The core advantage of this is that
the changed columns are uniform across all tables of an inherited or
partitioned target relation, whereas the other columns might not be.
A secondary advantage, when the UPDATE involves joins, is that less
data needs to pass through the plan tree. The disadvantage of course
is an extra fetch of each tuple to be updated. However, that seems to
be very nearly free in context; even worst-case tests don't show it to
add more than a couple percent to the total query cost. At some point
it might be interesting to combine the re-fetch with the tuple access
that ModifyTable must do anyway to mark the old tuple dead; but that
would require a good deal of refactoring and it seems it wouldn't buy
all that much, so this patch doesn't attempt it.
2. For inherited UPDATE/DELETE, instead of generating a separate
subplan for each target relation, we now generate a single subplan
that is just exactly like a SELECT's plan, then stick ModifyTable
on top of that. To let ModifyTable know which target relation a
given incoming row refers to, a tableoid junk column is added to
the row identity information. This gets rid of the horrid hack
that was inheritance_planner(), eliminating O(N^2) planning cost
and memory consumption in cases where there were many unprunable
target relations.
Point 2 of course requires point 1, so that there is a uniform
definition of the non-junk columns to be returned by the subplan.
We can't insist on uniform definition of the row identity junk
columns however, if we want to keep the ability to have both
plain and foreign tables in a partitioning hierarchy. Since
it wouldn't scale very far to have every child table have its
own row identity column, this patch includes provisions to merge
similar row identity columns into one column of the subplan result.
In particular, we can merge the whole-row Vars typically used as
row identity by FDWs into one column by pretending they are type
RECORD. (It's still okay for the actual composite Datums to be
labeled with the table's rowtype OID, though.)
There is more that can be done to file down residual inefficiencies
in this patch, but it seems to be committable now.
FDW authors should note several API changes:
* The argument list for AddForeignUpdateTargets() has changed, and so
has the method it must use for adding junk columns to the query. Call
add_row_identity_var() instead of manipulating the parse tree directly.
You might want to reconsider exactly what you're adding, too.
* PlanDirectModify() must now work a little harder to find the
ForeignScan plan node; if the foreign table is part of a partitioning
hierarchy then the ForeignScan might not be the direct child of
ModifyTable. See postgres_fdw for sample code.
* To check whether a relation is a target relation, it's no
longer sufficient to compare its relid to root->parse->resultRelation.
Instead, check it against all_result_relids or leaf_result_relids,
as appropriate.
Amit Langote and Tom Lane
Discussion: https://postgr.es/m/CA+HiwqHpHdqdDn48yCEhynnniahH78rwcrv1rEX65-fsZGBOLQ@mail.gmail.com
2021-03-31 17:52:34 +02:00
|
|
|
/* update's new-value expressions shouldn't be resjunk */
|
|
|
|
Assert(!tle->resjunk);
|
2016-03-21 16:59:49 +01:00
|
|
|
|
2016-03-18 18:48:58 +01:00
|
|
|
if (!first)
|
|
|
|
appendStringInfoString(buf, ", ");
|
|
|
|
first = false;
|
|
|
|
|
Fix interaction of foreign tuple routing with remote triggers.
Without these fixes, changes to the inserted tuple made by remote
triggers are ignored when building local RETURNING tuples.
In the core code, call ExecInitRoutingInfo at a later point from
within ExecInitPartitionInfo so that the FDW callback gets invoked
after the returning list has been built. But move CheckValidResultRel
out of ExecInitRoutingInfo so that it can happen at an earlier stage.
In postgres_fdw, refactor assorted deparsing functions to work with
the RTE rather than the PlannerInfo, which saves us having to
construct a fake PlannerInfo in cases where we don't have a real one.
Then, we can pass down a constructed RTE that yields the correct
deparse result when no real one exists. Unfortunately, this
necessitates a hack that understands how the core code manages RT
indexes for update tuple routing, which is ugly, but we don't have a
better idea right now.
Original report, analysis, and patch by Etsuro Fujita. Heavily
refactored by me. Then worked over some more by Amit Langote.
Discussion: http://postgr.es/m/5AD4882B.10002@lab.ntt.co.jp
2018-05-01 19:21:46 +02:00
|
|
|
deparseColumnRef(buf, rtindex, attnum, rte, false);
|
2016-03-18 18:48:58 +01:00
|
|
|
appendStringInfoString(buf, " = ");
|
|
|
|
deparseExpr((Expr *) tle->expr, &context);
|
|
|
|
}
|
|
|
|
|
|
|
|
reset_transmission_modes(nestlevel);
|
|
|
|
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
if (foreignrel->reloptkind == RELOPT_JOINREL)
|
|
|
|
{
|
|
|
|
List *ignore_conds = NIL;
|
|
|
|
|
2019-07-04 03:01:13 +02:00
|
|
|
appendStringInfoString(buf, " FROM ");
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
deparseFromExprForRel(buf, root, foreignrel, true, rtindex,
|
|
|
|
&ignore_conds, params_list);
|
|
|
|
remote_conds = list_concat(remote_conds, ignore_conds);
|
|
|
|
}
|
|
|
|
|
2016-03-18 18:48:58 +01:00
|
|
|
if (remote_conds)
|
|
|
|
{
|
2017-08-16 05:34:39 +02:00
|
|
|
appendStringInfoString(buf, " WHERE ");
|
2016-03-18 18:48:58 +01:00
|
|
|
appendConditions(remote_conds, &context);
|
|
|
|
}
|
|
|
|
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
if (foreignrel->reloptkind == RELOPT_JOINREL)
|
|
|
|
deparseExplicitTargetList(returningList, true, retrieved_attrs,
|
|
|
|
&context);
|
|
|
|
else
|
Fix interaction of foreign tuple routing with remote triggers.
Without these fixes, changes to the inserted tuple made by remote
triggers are ignored when building local RETURNING tuples.
In the core code, call ExecInitRoutingInfo at a later point from
within ExecInitPartitionInfo so that the FDW callback gets invoked
after the returning list has been built. But move CheckValidResultRel
out of ExecInitRoutingInfo so that it can happen at an earlier stage.
In postgres_fdw, refactor assorted deparsing functions to work with
the RTE rather than the PlannerInfo, which saves us having to
construct a fake PlannerInfo in cases where we don't have a real one.
Then, we can pass down a constructed RTE that yields the correct
deparse result when no real one exists. Unfortunately, this
necessitates a hack that understands how the core code manages RT
indexes for update tuple routing, which is ugly, but we don't have a
better idea right now.
Original report, analysis, and patch by Etsuro Fujita. Heavily
refactored by me. Then worked over some more by Amit Langote.
Discussion: http://postgr.es/m/5AD4882B.10002@lab.ntt.co.jp
2018-05-01 19:21:46 +02:00
|
|
|
deparseReturningList(buf, rte, rtindex, rel, false,
|
Fix WITH CHECK OPTION on views referencing postgres_fdw tables.
If a view references a foreign table, and the foreign table has a
BEFORE INSERT trigger, then it's possible for a tuple inserted or
updated through the view to be changed such that it violates the
view's WITH CHECK OPTION constraint.
Before this commit, postgres_fdw handled this case inconsistently. A
RETURNING clause on the INSERT or UPDATE statement targeting the view
would cause the finally-inserted tuple to be read back, and the WITH
CHECK OPTION violation would throw an error. But without a RETURNING
clause, postgres_fdw would not read the final tuple back, and WITH
CHECK OPTION would not throw an error for the violation (or may throw
an error when there is no real violation). AFTER ROW triggers on the
foreign table had a similar effect as a RETURNING clause on the INSERT
or UPDATE statement.
To fix, this commit retrieves the attributes needed to enforce the
WITH CHECK OPTION constraint along with the attributes needed for the
RETURNING clause (if any) from the remote side. Thus, the WITH CHECK
OPTION constraint is always evaluated against the final tuple after
any triggers on the remote side.
This fix may be considered inconsistent with CHECK constraints
declared on foreign tables, which are not enforced locally at all
(because the constraint is on a remote object). The discussion
concluded that this difference is reasonable, because the WITH CHECK
OPTION is a constraint on the local view (not any remote object);
therefore it only makes sense to enforce its WITH CHECK OPTION
constraint locally.
Author: Etsuro Fujita
Reviewed-by: Arthur Zakirov, Stephen Frost
Discussion: https://www.postgresql.org/message-id/7eb58fab-fd3b-781b-ac33-f7cfec96021f%40lab.ntt.co.jp
2018-07-08 09:14:51 +02:00
|
|
|
NIL, returningList, retrieved_attrs);
|
2016-03-18 18:48:58 +01:00
|
|
|
}
|
|
|
|
|
2013-03-10 19:14:53 +01:00
|
|
|
/*
|
|
|
|
* deparse remote DELETE statement
|
2013-03-22 05:31:11 +01:00
|
|
|
*
|
|
|
|
* The statement text is appended to buf, and we also create an integer List
|
|
|
|
* of the columns being retrieved by RETURNING (if any), which is returned
|
|
|
|
* to *retrieved_attrs.
|
2013-03-10 19:14:53 +01:00
|
|
|
*/
|
|
|
|
void
|
Fix interaction of foreign tuple routing with remote triggers.
Without these fixes, changes to the inserted tuple made by remote
triggers are ignored when building local RETURNING tuples.
In the core code, call ExecInitRoutingInfo at a later point from
within ExecInitPartitionInfo so that the FDW callback gets invoked
after the returning list has been built. But move CheckValidResultRel
out of ExecInitRoutingInfo so that it can happen at an earlier stage.
In postgres_fdw, refactor assorted deparsing functions to work with
the RTE rather than the PlannerInfo, which saves us having to
construct a fake PlannerInfo in cases where we don't have a real one.
Then, we can pass down a constructed RTE that yields the correct
deparse result when no real one exists. Unfortunately, this
necessitates a hack that understands how the core code manages RT
indexes for update tuple routing, which is ugly, but we don't have a
better idea right now.
Original report, analysis, and patch by Etsuro Fujita. Heavily
refactored by me. Then worked over some more by Amit Langote.
Discussion: http://postgr.es/m/5AD4882B.10002@lab.ntt.co.jp
2018-05-01 19:21:46 +02:00
|
|
|
deparseDeleteSql(StringInfo buf, RangeTblEntry *rte,
|
2013-03-12 23:58:13 +01:00
|
|
|
Index rtindex, Relation rel,
|
2013-03-22 05:31:11 +01:00
|
|
|
List *returningList,
|
|
|
|
List **retrieved_attrs)
|
2013-03-10 19:14:53 +01:00
|
|
|
{
|
|
|
|
appendStringInfoString(buf, "DELETE FROM ");
|
2013-03-12 23:58:13 +01:00
|
|
|
deparseRelation(buf, rel);
|
2013-03-10 19:14:53 +01:00
|
|
|
appendStringInfoString(buf, " WHERE ctid = $1");
|
|
|
|
|
Fix interaction of foreign tuple routing with remote triggers.
Without these fixes, changes to the inserted tuple made by remote
triggers are ignored when building local RETURNING tuples.
In the core code, call ExecInitRoutingInfo at a later point from
within ExecInitPartitionInfo so that the FDW callback gets invoked
after the returning list has been built. But move CheckValidResultRel
out of ExecInitRoutingInfo so that it can happen at an earlier stage.
In postgres_fdw, refactor assorted deparsing functions to work with
the RTE rather than the PlannerInfo, which saves us having to
construct a fake PlannerInfo in cases where we don't have a real one.
Then, we can pass down a constructed RTE that yields the correct
deparse result when no real one exists. Unfortunately, this
necessitates a hack that understands how the core code manages RT
indexes for update tuple routing, which is ugly, but we don't have a
better idea right now.
Original report, analysis, and patch by Etsuro Fujita. Heavily
refactored by me. Then worked over some more by Amit Langote.
Discussion: http://postgr.es/m/5AD4882B.10002@lab.ntt.co.jp
2018-05-01 19:21:46 +02:00
|
|
|
deparseReturningList(buf, rte, rtindex, rel,
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
rel->trigdesc && rel->trigdesc->trig_delete_after_row,
|
Fix WITH CHECK OPTION on views referencing postgres_fdw tables.
If a view references a foreign table, and the foreign table has a
BEFORE INSERT trigger, then it's possible for a tuple inserted or
updated through the view to be changed such that it violates the
view's WITH CHECK OPTION constraint.
Before this commit, postgres_fdw handled this case inconsistently. A
RETURNING clause on the INSERT or UPDATE statement targeting the view
would cause the finally-inserted tuple to be read back, and the WITH
CHECK OPTION violation would throw an error. But without a RETURNING
clause, postgres_fdw would not read the final tuple back, and WITH
CHECK OPTION would not throw an error for the violation (or may throw
an error when there is no real violation). AFTER ROW triggers on the
foreign table had a similar effect as a RETURNING clause on the INSERT
or UPDATE statement.
To fix, this commit retrieves the attributes needed to enforce the
WITH CHECK OPTION constraint along with the attributes needed for the
RETURNING clause (if any) from the remote side. Thus, the WITH CHECK
OPTION constraint is always evaluated against the final tuple after
any triggers on the remote side.
This fix may be considered inconsistent with CHECK constraints
declared on foreign tables, which are not enforced locally at all
(because the constraint is on a remote object). The discussion
concluded that this difference is reasonable, because the WITH CHECK
OPTION is a constraint on the local view (not any remote object);
therefore it only makes sense to enforce its WITH CHECK OPTION
constraint locally.
Author: Etsuro Fujita
Reviewed-by: Arthur Zakirov, Stephen Frost
Discussion: https://www.postgresql.org/message-id/7eb58fab-fd3b-781b-ac33-f7cfec96021f%40lab.ntt.co.jp
2018-07-08 09:14:51 +02:00
|
|
|
NIL, returningList, retrieved_attrs);
|
2013-03-10 19:14:53 +01:00
|
|
|
}
|
|
|
|
|
2016-03-18 18:48:58 +01:00
|
|
|
/*
|
|
|
|
* deparse remote DELETE statement
|
|
|
|
*
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
* 'buf' is the output buffer to append the statement to
|
|
|
|
* 'rtindex' is the RT index of the associated target relation
|
|
|
|
* 'rel' is the relation descriptor for the target relation
|
|
|
|
* 'foreignrel' is the RelOptInfo for the target relation or the join relation
|
|
|
|
* containing all base relations in the query
|
|
|
|
* 'remote_conds' is the qual clauses that must be evaluated remotely
|
|
|
|
* '*params_list' is an output list of exprs that will become remote Params
|
|
|
|
* 'returningList' is the RETURNING targetlist
|
|
|
|
* '*retrieved_attrs' is an output list of integers of columns being retrieved
|
|
|
|
* by RETURNING (if any)
|
2016-03-18 18:48:58 +01:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
deparseDirectDeleteSql(StringInfo buf, PlannerInfo *root,
|
|
|
|
Index rtindex, Relation rel,
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
RelOptInfo *foreignrel,
|
2016-03-18 18:48:58 +01:00
|
|
|
List *remote_conds,
|
|
|
|
List **params_list,
|
|
|
|
List *returningList,
|
|
|
|
List **retrieved_attrs)
|
|
|
|
{
|
|
|
|
deparse_expr_cxt context;
|
|
|
|
|
|
|
|
/* Set up context struct for recursion */
|
|
|
|
context.root = root;
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
context.foreignrel = foreignrel;
|
|
|
|
context.scanrel = foreignrel;
|
2016-03-18 18:48:58 +01:00
|
|
|
context.buf = buf;
|
|
|
|
context.params_list = params_list;
|
|
|
|
|
|
|
|
appendStringInfoString(buf, "DELETE FROM ");
|
|
|
|
deparseRelation(buf, rel);
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
if (foreignrel->reloptkind == RELOPT_JOINREL)
|
|
|
|
appendStringInfo(buf, " %s%d", REL_ALIAS_PREFIX, rtindex);
|
|
|
|
|
|
|
|
if (foreignrel->reloptkind == RELOPT_JOINREL)
|
|
|
|
{
|
|
|
|
List *ignore_conds = NIL;
|
|
|
|
|
2019-07-04 03:01:13 +02:00
|
|
|
appendStringInfoString(buf, " USING ");
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
deparseFromExprForRel(buf, root, foreignrel, true, rtindex,
|
|
|
|
&ignore_conds, params_list);
|
|
|
|
remote_conds = list_concat(remote_conds, ignore_conds);
|
|
|
|
}
|
2016-03-18 18:48:58 +01:00
|
|
|
|
|
|
|
if (remote_conds)
|
|
|
|
{
|
2017-08-16 05:34:39 +02:00
|
|
|
appendStringInfoString(buf, " WHERE ");
|
2016-03-18 18:48:58 +01:00
|
|
|
appendConditions(remote_conds, &context);
|
|
|
|
}
|
|
|
|
|
postgres_fdw: Push down UPDATE/DELETE joins to remote servers.
Commit 0bf3ae88af330496517722e391e7c975e6bad219 allowed direct
foreign table modification; instead of fetching each row, updating
it locally, and then pushing the modification back to the remote
side, we would instead do all the work on the remote server via a
single remote UPDATE or DELETE command. However, that commit only
enabled this optimization when join tree consisted only of the
target table.
This change allows the same optimization when an UPDATE statement
has a FROM clause or a DELETE statement has a USING clause. This
works much like ordinary foreign join pushdown, in that the tables
must be on the same remote server, relevant parts of the query
must be pushdown-safe, and so forth.
Etsuro Fujita, reviewed by Ashutosh Bapat, Rushabh Lathia, and me.
Some formatting corrections by me.
Discussion: http://postgr.es/m/5A57193A.2080003@lab.ntt.co.jp
Discussion: http://postgr.es/m/b9cee735-62f8-6c07-7528-6364ce9347d0@lab.ntt.co.jp
2018-02-07 21:34:30 +01:00
|
|
|
if (foreignrel->reloptkind == RELOPT_JOINREL)
|
|
|
|
deparseExplicitTargetList(returningList, true, retrieved_attrs,
|
|
|
|
&context);
|
|
|
|
else
|
Fix interaction of foreign tuple routing with remote triggers.
Without these fixes, changes to the inserted tuple made by remote
triggers are ignored when building local RETURNING tuples.
In the core code, call ExecInitRoutingInfo at a later point from
within ExecInitPartitionInfo so that the FDW callback gets invoked
after the returning list has been built. But move CheckValidResultRel
out of ExecInitRoutingInfo so that it can happen at an earlier stage.
In postgres_fdw, refactor assorted deparsing functions to work with
the RTE rather than the PlannerInfo, which saves us having to
construct a fake PlannerInfo in cases where we don't have a real one.
Then, we can pass down a constructed RTE that yields the correct
deparse result when no real one exists. Unfortunately, this
necessitates a hack that understands how the core code manages RT
indexes for update tuple routing, which is ugly, but we don't have a
better idea right now.
Original report, analysis, and patch by Etsuro Fujita. Heavily
refactored by me. Then worked over some more by Amit Langote.
Discussion: http://postgr.es/m/5AD4882B.10002@lab.ntt.co.jp
2018-05-01 19:21:46 +02:00
|
|
|
deparseReturningList(buf, planner_rt_fetch(rtindex, root),
|
|
|
|
rtindex, rel, false,
|
Fix WITH CHECK OPTION on views referencing postgres_fdw tables.
If a view references a foreign table, and the foreign table has a
BEFORE INSERT trigger, then it's possible for a tuple inserted or
updated through the view to be changed such that it violates the
view's WITH CHECK OPTION constraint.
Before this commit, postgres_fdw handled this case inconsistently. A
RETURNING clause on the INSERT or UPDATE statement targeting the view
would cause the finally-inserted tuple to be read back, and the WITH
CHECK OPTION violation would throw an error. But without a RETURNING
clause, postgres_fdw would not read the final tuple back, and WITH
CHECK OPTION would not throw an error for the violation (or may throw
an error when there is no real violation). AFTER ROW triggers on the
foreign table had a similar effect as a RETURNING clause on the INSERT
or UPDATE statement.
To fix, this commit retrieves the attributes needed to enforce the
WITH CHECK OPTION constraint along with the attributes needed for the
RETURNING clause (if any) from the remote side. Thus, the WITH CHECK
OPTION constraint is always evaluated against the final tuple after
any triggers on the remote side.
This fix may be considered inconsistent with CHECK constraints
declared on foreign tables, which are not enforced locally at all
(because the constraint is on a remote object). The discussion
concluded that this difference is reasonable, because the WITH CHECK
OPTION is a constraint on the local view (not any remote object);
therefore it only makes sense to enforce its WITH CHECK OPTION
constraint locally.
Author: Etsuro Fujita
Reviewed-by: Arthur Zakirov, Stephen Frost
Discussion: https://www.postgresql.org/message-id/7eb58fab-fd3b-781b-ac33-f7cfec96021f%40lab.ntt.co.jp
2018-07-08 09:14:51 +02:00
|
|
|
NIL, returningList, retrieved_attrs);
|
2016-03-18 18:48:58 +01:00
|
|
|
}
|
|
|
|
|
2013-03-10 19:14:53 +01:00
|
|
|
/*
|
2014-03-23 07:16:34 +01:00
|
|
|
* Add a RETURNING clause, if needed, to an INSERT/UPDATE/DELETE.
|
2013-03-10 19:14:53 +01:00
|
|
|
*/
|
|
|
|
static void
|
Fix interaction of foreign tuple routing with remote triggers.
Without these fixes, changes to the inserted tuple made by remote
triggers are ignored when building local RETURNING tuples.
In the core code, call ExecInitRoutingInfo at a later point from
within ExecInitPartitionInfo so that the FDW callback gets invoked
after the returning list has been built. But move CheckValidResultRel
out of ExecInitRoutingInfo so that it can happen at an earlier stage.
In postgres_fdw, refactor assorted deparsing functions to work with
the RTE rather than the PlannerInfo, which saves us having to
construct a fake PlannerInfo in cases where we don't have a real one.
Then, we can pass down a constructed RTE that yields the correct
deparse result when no real one exists. Unfortunately, this
necessitates a hack that understands how the core code manages RT
indexes for update tuple routing, which is ugly, but we don't have a
better idea right now.
Original report, analysis, and patch by Etsuro Fujita. Heavily
refactored by me. Then worked over some more by Amit Langote.
Discussion: http://postgr.es/m/5AD4882B.10002@lab.ntt.co.jp
2018-05-01 19:21:46 +02:00
|
|
|
deparseReturningList(StringInfo buf, RangeTblEntry *rte,
|
2013-03-10 19:14:53 +01:00
|
|
|
Index rtindex, Relation rel,
|
2014-03-23 07:16:34 +01:00
|
|
|
bool trig_after_row,
|
Fix WITH CHECK OPTION on views referencing postgres_fdw tables.
If a view references a foreign table, and the foreign table has a
BEFORE INSERT trigger, then it's possible for a tuple inserted or
updated through the view to be changed such that it violates the
view's WITH CHECK OPTION constraint.
Before this commit, postgres_fdw handled this case inconsistently. A
RETURNING clause on the INSERT or UPDATE statement targeting the view
would cause the finally-inserted tuple to be read back, and the WITH
CHECK OPTION violation would throw an error. But without a RETURNING
clause, postgres_fdw would not read the final tuple back, and WITH
CHECK OPTION would not throw an error for the violation (or may throw
an error when there is no real violation). AFTER ROW triggers on the
foreign table had a similar effect as a RETURNING clause on the INSERT
or UPDATE statement.
To fix, this commit retrieves the attributes needed to enforce the
WITH CHECK OPTION constraint along with the attributes needed for the
RETURNING clause (if any) from the remote side. Thus, the WITH CHECK
OPTION constraint is always evaluated against the final tuple after
any triggers on the remote side.
This fix may be considered inconsistent with CHECK constraints
declared on foreign tables, which are not enforced locally at all
(because the constraint is on a remote object). The discussion
concluded that this difference is reasonable, because the WITH CHECK
OPTION is a constraint on the local view (not any remote object);
therefore it only makes sense to enforce its WITH CHECK OPTION
constraint locally.
Author: Etsuro Fujita
Reviewed-by: Arthur Zakirov, Stephen Frost
Discussion: https://www.postgresql.org/message-id/7eb58fab-fd3b-781b-ac33-f7cfec96021f%40lab.ntt.co.jp
2018-07-08 09:14:51 +02:00
|
|
|
List *withCheckOptionList,
|
2013-03-22 05:31:11 +01:00
|
|
|
List *returningList,
|
|
|
|
List **retrieved_attrs)
|
2013-03-10 19:14:53 +01:00
|
|
|
{
|
2014-03-23 07:16:34 +01:00
|
|
|
Bitmapset *attrs_used = NULL;
|
2013-03-10 19:14:53 +01:00
|
|
|
|
2014-03-23 07:16:34 +01:00
|
|
|
if (trig_after_row)
|
|
|
|
{
|
|
|
|
/* whole-row reference acquires all non-system columns */
|
|
|
|
attrs_used =
|
|
|
|
bms_make_singleton(0 - FirstLowInvalidHeapAttributeNumber);
|
|
|
|
}
|
2013-03-10 19:14:53 +01:00
|
|
|
|
Fix WITH CHECK OPTION on views referencing postgres_fdw tables.
If a view references a foreign table, and the foreign table has a
BEFORE INSERT trigger, then it's possible for a tuple inserted or
updated through the view to be changed such that it violates the
view's WITH CHECK OPTION constraint.
Before this commit, postgres_fdw handled this case inconsistently. A
RETURNING clause on the INSERT or UPDATE statement targeting the view
would cause the finally-inserted tuple to be read back, and the WITH
CHECK OPTION violation would throw an error. But without a RETURNING
clause, postgres_fdw would not read the final tuple back, and WITH
CHECK OPTION would not throw an error for the violation (or may throw
an error when there is no real violation). AFTER ROW triggers on the
foreign table had a similar effect as a RETURNING clause on the INSERT
or UPDATE statement.
To fix, this commit retrieves the attributes needed to enforce the
WITH CHECK OPTION constraint along with the attributes needed for the
RETURNING clause (if any) from the remote side. Thus, the WITH CHECK
OPTION constraint is always evaluated against the final tuple after
any triggers on the remote side.
This fix may be considered inconsistent with CHECK constraints
declared on foreign tables, which are not enforced locally at all
(because the constraint is on a remote object). The discussion
concluded that this difference is reasonable, because the WITH CHECK
OPTION is a constraint on the local view (not any remote object);
therefore it only makes sense to enforce its WITH CHECK OPTION
constraint locally.
Author: Etsuro Fujita
Reviewed-by: Arthur Zakirov, Stephen Frost
Discussion: https://www.postgresql.org/message-id/7eb58fab-fd3b-781b-ac33-f7cfec96021f%40lab.ntt.co.jp
2018-07-08 09:14:51 +02:00
|
|
|
if (withCheckOptionList != NIL)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We need the attrs, non-system and system, mentioned in the local
|
|
|
|
* query's WITH CHECK OPTION list.
|
|
|
|
*
|
|
|
|
* Note: we do this to ensure that WCO constraints will be evaluated
|
|
|
|
* on the data actually inserted/updated on the remote side, which
|
|
|
|
* might differ from the data supplied by the core code, for example
|
|
|
|
* as a result of remote triggers.
|
|
|
|
*/
|
|
|
|
pull_varattnos((Node *) withCheckOptionList, rtindex,
|
|
|
|
&attrs_used);
|
|
|
|
}
|
|
|
|
|
2014-03-23 07:16:34 +01:00
|
|
|
if (returningList != NIL)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We need the attrs, non-system and system, mentioned in the local
|
|
|
|
* query's RETURNING list.
|
|
|
|
*/
|
|
|
|
pull_varattnos((Node *) returningList, rtindex,
|
|
|
|
&attrs_used);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (attrs_used != NULL)
|
Fix interaction of foreign tuple routing with remote triggers.
Without these fixes, changes to the inserted tuple made by remote
triggers are ignored when building local RETURNING tuples.
In the core code, call ExecInitRoutingInfo at a later point from
within ExecInitPartitionInfo so that the FDW callback gets invoked
after the returning list has been built. But move CheckValidResultRel
out of ExecInitRoutingInfo so that it can happen at an earlier stage.
In postgres_fdw, refactor assorted deparsing functions to work with
the RTE rather than the PlannerInfo, which saves us having to
construct a fake PlannerInfo in cases where we don't have a real one.
Then, we can pass down a constructed RTE that yields the correct
deparse result when no real one exists. Unfortunately, this
necessitates a hack that understands how the core code manages RT
indexes for update tuple routing, which is ugly, but we don't have a
better idea right now.
Original report, analysis, and patch by Etsuro Fujita. Heavily
refactored by me. Then worked over some more by Amit Langote.
Discussion: http://postgr.es/m/5AD4882B.10002@lab.ntt.co.jp
2018-05-01 19:21:46 +02:00
|
|
|
deparseTargetList(buf, rte, rtindex, rel, true, attrs_used, false,
|
2014-03-23 07:16:34 +01:00
|
|
|
retrieved_attrs);
|
|
|
|
else
|
|
|
|
*retrieved_attrs = NIL;
|
2013-03-10 19:14:53 +01:00
|
|
|
}
|
|
|
|
|
2013-02-22 16:56:06 +01:00
|
|
|
/*
|
|
|
|
* Construct SELECT statement to acquire size in blocks of given relation.
|
|
|
|
*
|
|
|
|
* Note: we use local definition of block size, not remote definition.
|
|
|
|
* This is perhaps debatable.
|
|
|
|
*
|
|
|
|
* Note: pg_relation_size() exists in 8.1 and later.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
deparseAnalyzeSizeSql(StringInfo buf, Relation rel)
|
|
|
|
{
|
|
|
|
StringInfoData relname;
|
|
|
|
|
|
|
|
/* We'll need the remote relation name as a literal. */
|
|
|
|
initStringInfo(&relname);
|
2013-03-12 23:58:13 +01:00
|
|
|
deparseRelation(&relname, rel);
|
2013-02-22 16:56:06 +01:00
|
|
|
|
2013-10-31 15:55:59 +01:00
|
|
|
appendStringInfoString(buf, "SELECT pg_catalog.pg_relation_size(");
|
2013-02-22 16:56:06 +01:00
|
|
|
deparseStringLiteral(buf, relname.data);
|
|
|
|
appendStringInfo(buf, "::pg_catalog.regclass) / %d", BLCKSZ);
|
|
|
|
}
|
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
/*
|
|
|
|
* Construct SELECT statement to acquire sample rows of given relation.
|
|
|
|
*
|
2013-03-22 05:31:11 +01:00
|
|
|
* SELECT command is appended to buf, and list of columns retrieved
|
|
|
|
* is returned to *retrieved_attrs.
|
2013-02-21 11:26:23 +01:00
|
|
|
*/
|
|
|
|
void
|
2013-03-22 05:31:11 +01:00
|
|
|
deparseAnalyzeSql(StringInfo buf, Relation rel, List **retrieved_attrs)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
|
|
|
Oid relid = RelationGetRelid(rel);
|
|
|
|
TupleDesc tupdesc = RelationGetDescr(rel);
|
|
|
|
int i;
|
|
|
|
char *colname;
|
|
|
|
List *options;
|
|
|
|
ListCell *lc;
|
|
|
|
bool first = true;
|
|
|
|
|
2013-03-22 05:31:11 +01:00
|
|
|
*retrieved_attrs = NIL;
|
|
|
|
|
2013-03-10 19:14:53 +01:00
|
|
|
appendStringInfoString(buf, "SELECT ");
|
2013-02-21 11:26:23 +01:00
|
|
|
for (i = 0; i < tupdesc->natts; i++)
|
|
|
|
{
|
|
|
|
/* Ignore dropped columns. */
|
2017-08-20 20:19:07 +02:00
|
|
|
if (TupleDescAttr(tupdesc, i)->attisdropped)
|
2013-02-21 11:26:23 +01:00
|
|
|
continue;
|
|
|
|
|
2013-03-10 19:14:53 +01:00
|
|
|
if (!first)
|
|
|
|
appendStringInfoString(buf, ", ");
|
|
|
|
first = false;
|
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
/* Use attribute name or column_name option. */
|
2017-08-20 20:19:07 +02:00
|
|
|
colname = NameStr(TupleDescAttr(tupdesc, i)->attname);
|
2013-02-21 11:26:23 +01:00
|
|
|
options = GetForeignColumnOptions(relid, i + 1);
|
|
|
|
|
|
|
|
foreach(lc, options)
|
|
|
|
{
|
|
|
|
DefElem *def = (DefElem *) lfirst(lc);
|
|
|
|
|
|
|
|
if (strcmp(def->defname, "column_name") == 0)
|
|
|
|
{
|
|
|
|
colname = defGetString(def);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
appendStringInfoString(buf, quote_identifier(colname));
|
2013-03-22 05:31:11 +01:00
|
|
|
|
|
|
|
*retrieved_attrs = lappend_int(*retrieved_attrs, i + 1);
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Don't generate bad syntax for zero-column relation. */
|
|
|
|
if (first)
|
2013-03-10 19:14:53 +01:00
|
|
|
appendStringInfoString(buf, "NULL");
|
2013-02-21 11:26:23 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Construct FROM clause
|
|
|
|
*/
|
2013-03-10 19:14:53 +01:00
|
|
|
appendStringInfoString(buf, " FROM ");
|
2013-03-12 23:58:13 +01:00
|
|
|
deparseRelation(buf, rel);
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
|
|
|
|
Allow TRUNCATE command to truncate foreign tables.
This commit introduces new foreign data wrapper API for TRUNCATE.
It extends TRUNCATE command so that it accepts foreign tables as
the targets to truncate and invokes that API. Also it extends postgres_fdw
so that it can issue TRUNCATE command to foreign servers, by adding
new routine for that TRUNCATE API.
The information about options specified in TRUNCATE command, e.g.,
ONLY, CACADE, etc is passed to FDW via API. The list of foreign tables to
truncate is also passed to FDW. FDW truncates the foreign data sources
that the passed foreign tables specify, based on those information.
For example, postgres_fdw constructs TRUNCATE command using them
and issues it to the foreign server.
For performance, TRUNCATE command invokes the FDW routine for
TRUNCATE once per foreign server that foreign tables to truncate belong to.
Author: Kazutaka Onishi, Kohei KaiGai, slightly modified by Fujii Masao
Reviewed-by: Bharath Rupireddy, Michael Paquier, Zhihong Yu, Alvaro Herrera, Stephen Frost, Ashutosh Bapat, Amit Langote, Daniel Gustafsson, Ibrar Ahmed, Fujii Masao
Discussion: https://postgr.es/m/CAOP8fzb_gkReLput7OvOK+8NHgw-RKqNv59vem7=524krQTcWA@mail.gmail.com
Discussion: https://postgr.es/m/CAJuF6cMWDDqU-vn_knZgma+2GMaout68YUgn1uyDnexRhqqM5Q@mail.gmail.com
2021-04-08 13:56:08 +02:00
|
|
|
/*
|
|
|
|
* Construct a simple "TRUNCATE rel" statement
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
deparseTruncateSql(StringInfo buf,
|
|
|
|
List *rels,
|
|
|
|
DropBehavior behavior,
|
|
|
|
bool restart_seqs)
|
|
|
|
{
|
2021-04-27 07:41:27 +02:00
|
|
|
ListCell *cell;
|
Allow TRUNCATE command to truncate foreign tables.
This commit introduces new foreign data wrapper API for TRUNCATE.
It extends TRUNCATE command so that it accepts foreign tables as
the targets to truncate and invokes that API. Also it extends postgres_fdw
so that it can issue TRUNCATE command to foreign servers, by adding
new routine for that TRUNCATE API.
The information about options specified in TRUNCATE command, e.g.,
ONLY, CACADE, etc is passed to FDW via API. The list of foreign tables to
truncate is also passed to FDW. FDW truncates the foreign data sources
that the passed foreign tables specify, based on those information.
For example, postgres_fdw constructs TRUNCATE command using them
and issues it to the foreign server.
For performance, TRUNCATE command invokes the FDW routine for
TRUNCATE once per foreign server that foreign tables to truncate belong to.
Author: Kazutaka Onishi, Kohei KaiGai, slightly modified by Fujii Masao
Reviewed-by: Bharath Rupireddy, Michael Paquier, Zhihong Yu, Alvaro Herrera, Stephen Frost, Ashutosh Bapat, Amit Langote, Daniel Gustafsson, Ibrar Ahmed, Fujii Masao
Discussion: https://postgr.es/m/CAOP8fzb_gkReLput7OvOK+8NHgw-RKqNv59vem7=524krQTcWA@mail.gmail.com
Discussion: https://postgr.es/m/CAJuF6cMWDDqU-vn_knZgma+2GMaout68YUgn1uyDnexRhqqM5Q@mail.gmail.com
2021-04-08 13:56:08 +02:00
|
|
|
|
|
|
|
appendStringInfoString(buf, "TRUNCATE ");
|
|
|
|
|
2021-04-27 07:41:27 +02:00
|
|
|
foreach(cell, rels)
|
Allow TRUNCATE command to truncate foreign tables.
This commit introduces new foreign data wrapper API for TRUNCATE.
It extends TRUNCATE command so that it accepts foreign tables as
the targets to truncate and invokes that API. Also it extends postgres_fdw
so that it can issue TRUNCATE command to foreign servers, by adding
new routine for that TRUNCATE API.
The information about options specified in TRUNCATE command, e.g.,
ONLY, CACADE, etc is passed to FDW via API. The list of foreign tables to
truncate is also passed to FDW. FDW truncates the foreign data sources
that the passed foreign tables specify, based on those information.
For example, postgres_fdw constructs TRUNCATE command using them
and issues it to the foreign server.
For performance, TRUNCATE command invokes the FDW routine for
TRUNCATE once per foreign server that foreign tables to truncate belong to.
Author: Kazutaka Onishi, Kohei KaiGai, slightly modified by Fujii Masao
Reviewed-by: Bharath Rupireddy, Michael Paquier, Zhihong Yu, Alvaro Herrera, Stephen Frost, Ashutosh Bapat, Amit Langote, Daniel Gustafsson, Ibrar Ahmed, Fujii Masao
Discussion: https://postgr.es/m/CAOP8fzb_gkReLput7OvOK+8NHgw-RKqNv59vem7=524krQTcWA@mail.gmail.com
Discussion: https://postgr.es/m/CAJuF6cMWDDqU-vn_knZgma+2GMaout68YUgn1uyDnexRhqqM5Q@mail.gmail.com
2021-04-08 13:56:08 +02:00
|
|
|
{
|
2021-04-27 07:41:27 +02:00
|
|
|
Relation rel = lfirst(cell);
|
Allow TRUNCATE command to truncate foreign tables.
This commit introduces new foreign data wrapper API for TRUNCATE.
It extends TRUNCATE command so that it accepts foreign tables as
the targets to truncate and invokes that API. Also it extends postgres_fdw
so that it can issue TRUNCATE command to foreign servers, by adding
new routine for that TRUNCATE API.
The information about options specified in TRUNCATE command, e.g.,
ONLY, CACADE, etc is passed to FDW via API. The list of foreign tables to
truncate is also passed to FDW. FDW truncates the foreign data sources
that the passed foreign tables specify, based on those information.
For example, postgres_fdw constructs TRUNCATE command using them
and issues it to the foreign server.
For performance, TRUNCATE command invokes the FDW routine for
TRUNCATE once per foreign server that foreign tables to truncate belong to.
Author: Kazutaka Onishi, Kohei KaiGai, slightly modified by Fujii Masao
Reviewed-by: Bharath Rupireddy, Michael Paquier, Zhihong Yu, Alvaro Herrera, Stephen Frost, Ashutosh Bapat, Amit Langote, Daniel Gustafsson, Ibrar Ahmed, Fujii Masao
Discussion: https://postgr.es/m/CAOP8fzb_gkReLput7OvOK+8NHgw-RKqNv59vem7=524krQTcWA@mail.gmail.com
Discussion: https://postgr.es/m/CAJuF6cMWDDqU-vn_knZgma+2GMaout68YUgn1uyDnexRhqqM5Q@mail.gmail.com
2021-04-08 13:56:08 +02:00
|
|
|
|
2021-04-27 07:41:27 +02:00
|
|
|
if (cell != list_head(rels))
|
Allow TRUNCATE command to truncate foreign tables.
This commit introduces new foreign data wrapper API for TRUNCATE.
It extends TRUNCATE command so that it accepts foreign tables as
the targets to truncate and invokes that API. Also it extends postgres_fdw
so that it can issue TRUNCATE command to foreign servers, by adding
new routine for that TRUNCATE API.
The information about options specified in TRUNCATE command, e.g.,
ONLY, CACADE, etc is passed to FDW via API. The list of foreign tables to
truncate is also passed to FDW. FDW truncates the foreign data sources
that the passed foreign tables specify, based on those information.
For example, postgres_fdw constructs TRUNCATE command using them
and issues it to the foreign server.
For performance, TRUNCATE command invokes the FDW routine for
TRUNCATE once per foreign server that foreign tables to truncate belong to.
Author: Kazutaka Onishi, Kohei KaiGai, slightly modified by Fujii Masao
Reviewed-by: Bharath Rupireddy, Michael Paquier, Zhihong Yu, Alvaro Herrera, Stephen Frost, Ashutosh Bapat, Amit Langote, Daniel Gustafsson, Ibrar Ahmed, Fujii Masao
Discussion: https://postgr.es/m/CAOP8fzb_gkReLput7OvOK+8NHgw-RKqNv59vem7=524krQTcWA@mail.gmail.com
Discussion: https://postgr.es/m/CAJuF6cMWDDqU-vn_knZgma+2GMaout68YUgn1uyDnexRhqqM5Q@mail.gmail.com
2021-04-08 13:56:08 +02:00
|
|
|
appendStringInfoString(buf, ", ");
|
|
|
|
|
|
|
|
deparseRelation(buf, rel);
|
|
|
|
}
|
|
|
|
|
|
|
|
appendStringInfo(buf, " %s IDENTITY",
|
|
|
|
restart_seqs ? "RESTART" : "CONTINUE");
|
|
|
|
|
|
|
|
if (behavior == DROP_RESTRICT)
|
|
|
|
appendStringInfoString(buf, " RESTRICT");
|
|
|
|
else if (behavior == DROP_CASCADE)
|
|
|
|
appendStringInfoString(buf, " CASCADE");
|
|
|
|
}
|
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
/*
|
|
|
|
* Construct name to use for given column, and emit it into buf.
|
|
|
|
* If it has a column_name FDW option, use that instead of attribute name.
|
2016-02-09 20:00:50 +01:00
|
|
|
*
|
|
|
|
* If qualify_col is true, qualify column name with the alias of relation.
|
2013-02-21 11:26:23 +01:00
|
|
|
*/
|
|
|
|
static void
|
Fix interaction of foreign tuple routing with remote triggers.
Without these fixes, changes to the inserted tuple made by remote
triggers are ignored when building local RETURNING tuples.
In the core code, call ExecInitRoutingInfo at a later point from
within ExecInitPartitionInfo so that the FDW callback gets invoked
after the returning list has been built. But move CheckValidResultRel
out of ExecInitRoutingInfo so that it can happen at an earlier stage.
In postgres_fdw, refactor assorted deparsing functions to work with
the RTE rather than the PlannerInfo, which saves us having to
construct a fake PlannerInfo in cases where we don't have a real one.
Then, we can pass down a constructed RTE that yields the correct
deparse result when no real one exists. Unfortunately, this
necessitates a hack that understands how the core code manages RT
indexes for update tuple routing, which is ugly, but we don't have a
better idea right now.
Original report, analysis, and patch by Etsuro Fujita. Heavily
refactored by me. Then worked over some more by Amit Langote.
Discussion: http://postgr.es/m/5AD4882B.10002@lab.ntt.co.jp
2018-05-01 19:21:46 +02:00
|
|
|
deparseColumnRef(StringInfo buf, int varno, int varattno, RangeTblEntry *rte,
|
2016-02-09 20:00:50 +01:00
|
|
|
bool qualify_col)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
2016-08-26 15:33:57 +02:00
|
|
|
/* We support fetching the remote side's CTID and OID. */
|
2016-02-09 20:00:50 +01:00
|
|
|
if (varattno == SelfItemPointerAttributeNumber)
|
|
|
|
{
|
|
|
|
if (qualify_col)
|
|
|
|
ADD_REL_QUALIFIER(buf, varno);
|
|
|
|
appendStringInfoString(buf, "ctid");
|
|
|
|
}
|
2016-04-15 17:58:56 +02:00
|
|
|
else if (varattno < 0)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* All other system attributes are fetched as 0, except for table OID,
|
|
|
|
* which is fetched as the local table OID. However, we must be
|
2016-06-10 00:02:36 +02:00
|
|
|
* careful; the table could be beneath an outer join, in which case it
|
|
|
|
* must go to NULL whenever the rest of the row does.
|
2016-04-15 17:58:56 +02:00
|
|
|
*/
|
2016-06-10 00:02:36 +02:00
|
|
|
Oid fetchval = 0;
|
2016-04-15 17:58:56 +02:00
|
|
|
|
|
|
|
if (varattno == TableOidAttributeNumber)
|
|
|
|
fetchval = rte->relid;
|
|
|
|
|
|
|
|
if (qualify_col)
|
|
|
|
{
|
2016-06-24 21:06:19 +02:00
|
|
|
appendStringInfoString(buf, "CASE WHEN (");
|
2016-04-15 17:58:56 +02:00
|
|
|
ADD_REL_QUALIFIER(buf, varno);
|
2016-07-01 16:13:06 +02:00
|
|
|
appendStringInfo(buf, "*)::text IS NOT NULL THEN %u END", fetchval);
|
2016-04-15 17:58:56 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
appendStringInfo(buf, "%u", fetchval);
|
|
|
|
}
|
2016-02-09 20:00:50 +01:00
|
|
|
else if (varattno == 0)
|
|
|
|
{
|
|
|
|
/* Whole row reference */
|
|
|
|
Relation rel;
|
|
|
|
Bitmapset *attrs_used;
|
2013-02-21 11:26:23 +01:00
|
|
|
|
2016-02-09 20:00:50 +01:00
|
|
|
/* Required only to be passed down to deparseTargetList(). */
|
|
|
|
List *retrieved_attrs;
|
2013-02-21 11:26:23 +01:00
|
|
|
|
2016-02-09 20:00:50 +01:00
|
|
|
/*
|
|
|
|
* The lock on the relation will be held by upper callers, so it's
|
|
|
|
* fine to open it with no lock here.
|
|
|
|
*/
|
2019-01-21 19:32:19 +01:00
|
|
|
rel = table_open(rte->relid, NoLock);
|
2016-02-09 20:00:50 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The local name of the foreign table can not be recognized by the
|
|
|
|
* foreign server and the table it references on foreign server might
|
|
|
|
* have different column ordering or different columns than those
|
|
|
|
* declared locally. Hence we have to deparse whole-row reference as
|
|
|
|
* ROW(columns referenced locally). Construct this by deparsing a
|
|
|
|
* "whole row" attribute.
|
|
|
|
*/
|
|
|
|
attrs_used = bms_add_member(NULL,
|
|
|
|
0 - FirstLowInvalidHeapAttributeNumber);
|
2016-04-15 17:58:56 +02:00
|
|
|
|
|
|
|
/*
|
2016-06-10 00:02:36 +02:00
|
|
|
* In case the whole-row reference is under an outer join then it has
|
2016-07-25 10:51:26 +02:00
|
|
|
* to go NULL whenever the rest of the row goes NULL. Deparsing a join
|
2016-06-10 00:02:36 +02:00
|
|
|
* query would always involve multiple relations, thus qualify_col
|
|
|
|
* would be true.
|
2016-04-15 17:58:56 +02:00
|
|
|
*/
|
|
|
|
if (qualify_col)
|
|
|
|
{
|
2016-06-24 21:06:19 +02:00
|
|
|
appendStringInfoString(buf, "CASE WHEN (");
|
2016-04-15 17:58:56 +02:00
|
|
|
ADD_REL_QUALIFIER(buf, varno);
|
2017-08-16 05:34:39 +02:00
|
|
|
appendStringInfoString(buf, "*)::text IS NOT NULL THEN ");
|
2016-04-15 17:58:56 +02:00
|
|
|
}
|
|
|
|
|
2016-02-09 20:00:50 +01:00
|
|
|
appendStringInfoString(buf, "ROW(");
|
Fix interaction of foreign tuple routing with remote triggers.
Without these fixes, changes to the inserted tuple made by remote
triggers are ignored when building local RETURNING tuples.
In the core code, call ExecInitRoutingInfo at a later point from
within ExecInitPartitionInfo so that the FDW callback gets invoked
after the returning list has been built. But move CheckValidResultRel
out of ExecInitRoutingInfo so that it can happen at an earlier stage.
In postgres_fdw, refactor assorted deparsing functions to work with
the RTE rather than the PlannerInfo, which saves us having to
construct a fake PlannerInfo in cases where we don't have a real one.
Then, we can pass down a constructed RTE that yields the correct
deparse result when no real one exists. Unfortunately, this
necessitates a hack that understands how the core code manages RT
indexes for update tuple routing, which is ugly, but we don't have a
better idea right now.
Original report, analysis, and patch by Etsuro Fujita. Heavily
refactored by me. Then worked over some more by Amit Langote.
Discussion: http://postgr.es/m/5AD4882B.10002@lab.ntt.co.jp
2018-05-01 19:21:46 +02:00
|
|
|
deparseTargetList(buf, rte, varno, rel, false, attrs_used, qualify_col,
|
2016-02-09 20:00:50 +01:00
|
|
|
&retrieved_attrs);
|
2017-08-16 05:34:39 +02:00
|
|
|
appendStringInfoChar(buf, ')');
|
2016-04-15 17:58:56 +02:00
|
|
|
|
|
|
|
/* Complete the CASE WHEN statement started above. */
|
|
|
|
if (qualify_col)
|
2017-08-16 05:34:39 +02:00
|
|
|
appendStringInfoString(buf, " END");
|
2016-04-15 17:58:56 +02:00
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(rel, NoLock);
|
2016-02-09 20:00:50 +01:00
|
|
|
bms_free(attrs_used);
|
|
|
|
}
|
|
|
|
else
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
2016-02-09 20:00:50 +01:00
|
|
|
char *colname = NULL;
|
|
|
|
List *options;
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
/* varno must not be any of OUTER_VAR, INNER_VAR and INDEX_VAR. */
|
|
|
|
Assert(!IS_SPECIAL_VARNO(varno));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If it's a column of a foreign table, and it has the column_name FDW
|
|
|
|
* option, use that value.
|
|
|
|
*/
|
|
|
|
options = GetForeignColumnOptions(rte->relid, varattno);
|
|
|
|
foreach(lc, options)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
2016-02-09 20:00:50 +01:00
|
|
|
DefElem *def = (DefElem *) lfirst(lc);
|
|
|
|
|
|
|
|
if (strcmp(def->defname, "column_name") == 0)
|
|
|
|
{
|
|
|
|
colname = defGetString(def);
|
|
|
|
break;
|
|
|
|
}
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
|
|
|
|
2016-02-09 20:00:50 +01:00
|
|
|
/*
|
|
|
|
* If it's a column of a regular table or it doesn't have column_name
|
|
|
|
* FDW option, use attribute name.
|
|
|
|
*/
|
|
|
|
if (colname == NULL)
|
2018-02-12 23:30:30 +01:00
|
|
|
colname = get_attname(rte->relid, varattno, false);
|
2016-02-09 20:00:50 +01:00
|
|
|
|
|
|
|
if (qualify_col)
|
|
|
|
ADD_REL_QUALIFIER(buf, varno);
|
2013-02-21 11:26:23 +01:00
|
|
|
|
2016-02-09 20:00:50 +01:00
|
|
|
appendStringInfoString(buf, quote_identifier(colname));
|
|
|
|
}
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Append remote name of specified foreign table to buf.
|
|
|
|
* Use value of table_name FDW option (if any) instead of relation's name.
|
|
|
|
* Similarly, schema_name FDW option overrides schema name.
|
|
|
|
*/
|
|
|
|
static void
|
2013-03-12 23:58:13 +01:00
|
|
|
deparseRelation(StringInfo buf, Relation rel)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
|
|
|
ForeignTable *table;
|
|
|
|
const char *nspname = NULL;
|
|
|
|
const char *relname = NULL;
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
/* obtain additional catalog information. */
|
2013-03-12 23:58:13 +01:00
|
|
|
table = GetForeignTable(RelationGetRelid(rel));
|
2013-02-21 11:26:23 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Use value of FDW options if any, instead of the name of object itself.
|
|
|
|
*/
|
|
|
|
foreach(lc, table->options)
|
|
|
|
{
|
|
|
|
DefElem *def = (DefElem *) lfirst(lc);
|
|
|
|
|
|
|
|
if (strcmp(def->defname, "schema_name") == 0)
|
|
|
|
nspname = defGetString(def);
|
|
|
|
else if (strcmp(def->defname, "table_name") == 0)
|
|
|
|
relname = defGetString(def);
|
|
|
|
}
|
|
|
|
|
2013-02-22 12:03:46 +01:00
|
|
|
/*
|
2013-03-10 19:14:53 +01:00
|
|
|
* Note: we could skip printing the schema name if it's pg_catalog, but
|
|
|
|
* that doesn't seem worth the trouble.
|
2013-02-22 12:03:46 +01:00
|
|
|
*/
|
2013-02-21 11:26:23 +01:00
|
|
|
if (nspname == NULL)
|
2013-03-12 23:58:13 +01:00
|
|
|
nspname = get_namespace_name(RelationGetNamespace(rel));
|
2013-02-21 11:26:23 +01:00
|
|
|
if (relname == NULL)
|
2013-03-12 23:58:13 +01:00
|
|
|
relname = RelationGetRelationName(rel);
|
2013-02-21 11:26:23 +01:00
|
|
|
|
|
|
|
appendStringInfo(buf, "%s.%s",
|
|
|
|
quote_identifier(nspname), quote_identifier(relname));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Append a SQL string literal representing "val" to buf.
|
|
|
|
*/
|
2014-07-10 21:01:31 +02:00
|
|
|
void
|
2013-02-21 11:26:23 +01:00
|
|
|
deparseStringLiteral(StringInfo buf, const char *val)
|
|
|
|
{
|
|
|
|
const char *valptr;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Rather than making assumptions about the remote server's value of
|
|
|
|
* standard_conforming_strings, always use E'foo' syntax if there are any
|
|
|
|
* backslashes. This will fail on remote servers before 8.1, but those
|
|
|
|
* are long out of support.
|
|
|
|
*/
|
|
|
|
if (strchr(val, '\\') != NULL)
|
|
|
|
appendStringInfoChar(buf, ESCAPE_STRING_SYNTAX);
|
|
|
|
appendStringInfoChar(buf, '\'');
|
|
|
|
for (valptr = val; *valptr; valptr++)
|
|
|
|
{
|
|
|
|
char ch = *valptr;
|
|
|
|
|
|
|
|
if (SQL_STR_DOUBLE(ch, true))
|
|
|
|
appendStringInfoChar(buf, ch);
|
|
|
|
appendStringInfoChar(buf, ch);
|
|
|
|
}
|
|
|
|
appendStringInfoChar(buf, '\'');
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2013-03-22 00:43:59 +01:00
|
|
|
* Deparse given expression into context->buf.
|
2013-02-21 11:26:23 +01:00
|
|
|
*
|
|
|
|
* This function must support all the same node types that foreign_expr_walker
|
|
|
|
* accepts.
|
|
|
|
*
|
|
|
|
* Note: unlike ruleutils.c, we just use a simple hard-wired parenthesization
|
|
|
|
* scheme: anything more complex than a Var, Const, function call or cast
|
|
|
|
* should be self-parenthesized.
|
|
|
|
*/
|
|
|
|
static void
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseExpr(Expr *node, deparse_expr_cxt *context)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
|
|
|
if (node == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch (nodeTag(node))
|
|
|
|
{
|
|
|
|
case T_Var:
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseVar((Var *) node, context);
|
2013-02-21 11:26:23 +01:00
|
|
|
break;
|
|
|
|
case T_Const:
|
2016-10-21 15:54:29 +02:00
|
|
|
deparseConst((Const *) node, context, 0);
|
2013-02-21 11:26:23 +01:00
|
|
|
break;
|
|
|
|
case T_Param:
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseParam((Param *) node, context);
|
2013-02-21 11:26:23 +01:00
|
|
|
break;
|
2019-02-01 16:50:32 +01:00
|
|
|
case T_SubscriptingRef:
|
|
|
|
deparseSubscriptingRef((SubscriptingRef *) node, context);
|
2013-02-21 11:26:23 +01:00
|
|
|
break;
|
|
|
|
case T_FuncExpr:
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseFuncExpr((FuncExpr *) node, context);
|
2013-02-21 11:26:23 +01:00
|
|
|
break;
|
|
|
|
case T_OpExpr:
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseOpExpr((OpExpr *) node, context);
|
2013-02-21 11:26:23 +01:00
|
|
|
break;
|
|
|
|
case T_DistinctExpr:
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseDistinctExpr((DistinctExpr *) node, context);
|
2013-02-21 11:26:23 +01:00
|
|
|
break;
|
|
|
|
case T_ScalarArrayOpExpr:
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseScalarArrayOpExpr((ScalarArrayOpExpr *) node, context);
|
2013-02-21 11:26:23 +01:00
|
|
|
break;
|
|
|
|
case T_RelabelType:
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseRelabelType((RelabelType *) node, context);
|
2013-02-21 11:26:23 +01:00
|
|
|
break;
|
|
|
|
case T_BoolExpr:
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseBoolExpr((BoolExpr *) node, context);
|
2013-02-21 11:26:23 +01:00
|
|
|
break;
|
|
|
|
case T_NullTest:
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseNullTest((NullTest *) node, context);
|
2013-02-21 11:26:23 +01:00
|
|
|
break;
|
2021-07-30 19:39:48 +02:00
|
|
|
case T_CaseExpr:
|
|
|
|
deparseCaseExpr((CaseExpr *) node, context);
|
|
|
|
break;
|
2013-02-21 11:26:23 +01:00
|
|
|
case T_ArrayExpr:
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseArrayExpr((ArrayExpr *) node, context);
|
2013-02-21 11:26:23 +01:00
|
|
|
break;
|
2016-10-21 15:54:29 +02:00
|
|
|
case T_Aggref:
|
|
|
|
deparseAggref((Aggref *) node, context);
|
|
|
|
break;
|
2013-02-21 11:26:23 +01:00
|
|
|
default:
|
|
|
|
elog(ERROR, "unsupported expression type for deparse: %d",
|
|
|
|
(int) nodeTag(node));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2013-03-22 00:43:59 +01:00
|
|
|
* Deparse given Var node into context->buf.
|
|
|
|
*
|
|
|
|
* If the Var belongs to the foreign relation, just print its remote name.
|
|
|
|
* Otherwise, it's effectively a Param (and will in fact be a Param at
|
|
|
|
* run time). Handle it the same way we handle plain Params --- see
|
|
|
|
* deparseParam for comments.
|
2013-02-21 11:26:23 +01:00
|
|
|
*/
|
|
|
|
static void
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseVar(Var *node, deparse_expr_cxt *context)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
2016-10-21 15:54:29 +02:00
|
|
|
Relids relids = context->scanrel->relids;
|
2017-03-16 18:34:59 +01:00
|
|
|
int relno;
|
|
|
|
int colno;
|
2013-03-22 00:43:59 +01:00
|
|
|
|
2016-10-21 15:54:29 +02:00
|
|
|
/* Qualify columns when multiple relations are involved. */
|
2018-07-01 08:10:10 +02:00
|
|
|
bool qualify_col = (bms_membership(relids) == BMS_MULTIPLE);
|
2016-10-21 15:54:29 +02:00
|
|
|
|
2017-03-16 18:34:59 +01:00
|
|
|
/*
|
|
|
|
* If the Var belongs to the foreign relation that is deparsed as a
|
2017-05-17 22:31:56 +02:00
|
|
|
* subquery, use the relation and column alias to the Var provided by the
|
|
|
|
* subquery, instead of the remote name.
|
2017-03-16 18:34:59 +01:00
|
|
|
*/
|
|
|
|
if (is_subquery_var(node, context->scanrel, &relno, &colno))
|
|
|
|
{
|
|
|
|
appendStringInfo(context->buf, "%s%d.%s%d",
|
|
|
|
SUBQUERY_REL_ALIAS_PREFIX, relno,
|
|
|
|
SUBQUERY_COL_ALIAS_PREFIX, colno);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-10-21 15:54:29 +02:00
|
|
|
if (bms_is_member(node->varno, relids) && node->varlevelsup == 0)
|
2016-02-09 20:00:50 +01:00
|
|
|
deparseColumnRef(context->buf, node->varno, node->varattno,
|
Fix interaction of foreign tuple routing with remote triggers.
Without these fixes, changes to the inserted tuple made by remote
triggers are ignored when building local RETURNING tuples.
In the core code, call ExecInitRoutingInfo at a later point from
within ExecInitPartitionInfo so that the FDW callback gets invoked
after the returning list has been built. But move CheckValidResultRel
out of ExecInitRoutingInfo so that it can happen at an earlier stage.
In postgres_fdw, refactor assorted deparsing functions to work with
the RTE rather than the PlannerInfo, which saves us having to
construct a fake PlannerInfo in cases where we don't have a real one.
Then, we can pass down a constructed RTE that yields the correct
deparse result when no real one exists. Unfortunately, this
necessitates a hack that understands how the core code manages RT
indexes for update tuple routing, which is ugly, but we don't have a
better idea right now.
Original report, analysis, and patch by Etsuro Fujita. Heavily
refactored by me. Then worked over some more by Amit Langote.
Discussion: http://postgr.es/m/5AD4882B.10002@lab.ntt.co.jp
2018-05-01 19:21:46 +02:00
|
|
|
planner_rt_fetch(node->varno, context->root),
|
|
|
|
qualify_col);
|
2013-03-22 00:43:59 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Treat like a Param */
|
|
|
|
if (context->params_list)
|
|
|
|
{
|
|
|
|
int pindex = 0;
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
/* find its index in params_list */
|
|
|
|
foreach(lc, *context->params_list)
|
|
|
|
{
|
|
|
|
pindex++;
|
|
|
|
if (equal(node, (Node *) lfirst(lc)))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (lc == NULL)
|
|
|
|
{
|
|
|
|
/* not in list, so add it */
|
|
|
|
pindex++;
|
|
|
|
*context->params_list = lappend(*context->params_list, node);
|
|
|
|
}
|
|
|
|
|
2014-04-16 23:21:57 +02:00
|
|
|
printRemoteParam(pindex, node->vartype, node->vartypmod, context);
|
2013-03-22 00:43:59 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-04-16 23:21:57 +02:00
|
|
|
printRemotePlaceholder(node->vartype, node->vartypmod, context);
|
2013-03-22 00:43:59 +01:00
|
|
|
}
|
|
|
|
}
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2013-03-22 00:43:59 +01:00
|
|
|
* Deparse given constant value into context->buf.
|
2013-02-21 11:26:23 +01:00
|
|
|
*
|
|
|
|
* This function has to be kept in sync with ruleutils.c's get_const_expr.
|
postgres_fdw: suppress casts on constants in limited cases.
When deparsing an expression of the form "remote_var OP constant",
we'd normally apply a cast to the constant to make sure that the
remote parser thinks it's of the same type we do. However, doing
so is often not necessary, and it causes problems if the user has
intentionally declared the local column as being of a different
type than the remote column. A plausible use-case for that is
using text to represent a type that's an enum on the remote side.
A comparison on such a column will get shipped as "var = 'foo'::text",
which blows up on the remote side because there's no enum = text
operator. But if we simply leave off the explicit cast, the
comparison will do exactly what the user wants.
It's possible to do this without major risk of semantic problems, by
relying on the longstanding parser heuristic that "if one operand of
an operator is of type unknown, while the other one has a known type,
assume that the unknown operand is also of that type". Hence, this
patch leaves off the cast only if (a) the operator inputs have the same
type locally; (b) the constant will print as a string literal or NULL,
both of which are initially taken as type unknown; and (c) the non-Const
input is a plain foreign Var. Rule (c) guarantees that the remote
parser will know the type of the non-Const input; moreover, it means
that if this cast-omission does cause any semantic surprises, that can
only happen in cases where the local column has a different type than
the remote column. That wasn't guaranteed to work anyway, and this
patch should represent a net usability gain for such cases.
One point that I (tgl) remain slightly uncomfortable with is that we
will ignore an implicit RelabelType when deciding if the non-Const input
is a plain Var. That makes it a little squishy to argue that the remote
should resolve the Const as being of the same type as its Var, because
then our Const is not the same type as our Var. However, if we don't do
that, then this hack won't work as desired if the user chooses to use
varchar rather than text to represent some remote column. That seems
useful, so do it like this for now. We might have to give up the
RelabelType-ignoring bit if any problems surface.
Dian Fay, with review and kibitzing by me
Discussion: https://postgr.es/m/C9LU294V7K4F.34LRRDU449O45@lamia
2021-11-12 17:50:40 +01:00
|
|
|
*
|
|
|
|
* As in that function, showtype can be -1 to never show "::typename"
|
|
|
|
* decoration, +1 to always show it, or 0 to show it only if the constant
|
|
|
|
* wouldn't be assumed to be the right type by default.
|
|
|
|
*
|
|
|
|
* In addition, this code allows showtype to be -2 to indicate that we should
|
|
|
|
* not show "::typename" decoration if the constant is printed as an untyped
|
|
|
|
* literal or NULL (while in other cases, behaving as for showtype == 0).
|
2013-02-21 11:26:23 +01:00
|
|
|
*/
|
|
|
|
static void
|
2016-10-21 15:54:29 +02:00
|
|
|
deparseConst(Const *node, deparse_expr_cxt *context, int showtype)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
2013-03-22 00:43:59 +01:00
|
|
|
StringInfo buf = context->buf;
|
2013-02-21 11:26:23 +01:00
|
|
|
Oid typoutput;
|
|
|
|
bool typIsVarlena;
|
|
|
|
char *extval;
|
|
|
|
bool isfloat = false;
|
postgres_fdw: suppress casts on constants in limited cases.
When deparsing an expression of the form "remote_var OP constant",
we'd normally apply a cast to the constant to make sure that the
remote parser thinks it's of the same type we do. However, doing
so is often not necessary, and it causes problems if the user has
intentionally declared the local column as being of a different
type than the remote column. A plausible use-case for that is
using text to represent a type that's an enum on the remote side.
A comparison on such a column will get shipped as "var = 'foo'::text",
which blows up on the remote side because there's no enum = text
operator. But if we simply leave off the explicit cast, the
comparison will do exactly what the user wants.
It's possible to do this without major risk of semantic problems, by
relying on the longstanding parser heuristic that "if one operand of
an operator is of type unknown, while the other one has a known type,
assume that the unknown operand is also of that type". Hence, this
patch leaves off the cast only if (a) the operator inputs have the same
type locally; (b) the constant will print as a string literal or NULL,
both of which are initially taken as type unknown; and (c) the non-Const
input is a plain foreign Var. Rule (c) guarantees that the remote
parser will know the type of the non-Const input; moreover, it means
that if this cast-omission does cause any semantic surprises, that can
only happen in cases where the local column has a different type than
the remote column. That wasn't guaranteed to work anyway, and this
patch should represent a net usability gain for such cases.
One point that I (tgl) remain slightly uncomfortable with is that we
will ignore an implicit RelabelType when deciding if the non-Const input
is a plain Var. That makes it a little squishy to argue that the remote
should resolve the Const as being of the same type as its Var, because
then our Const is not the same type as our Var. However, if we don't do
that, then this hack won't work as desired if the user chooses to use
varchar rather than text to represent some remote column. That seems
useful, so do it like this for now. We might have to give up the
RelabelType-ignoring bit if any problems surface.
Dian Fay, with review and kibitzing by me
Discussion: https://postgr.es/m/C9LU294V7K4F.34LRRDU449O45@lamia
2021-11-12 17:50:40 +01:00
|
|
|
bool isstring = false;
|
2013-02-21 11:26:23 +01:00
|
|
|
bool needlabel;
|
|
|
|
|
|
|
|
if (node->constisnull)
|
|
|
|
{
|
2013-10-31 15:55:59 +01:00
|
|
|
appendStringInfoString(buf, "NULL");
|
2016-10-21 15:54:29 +02:00
|
|
|
if (showtype >= 0)
|
|
|
|
appendStringInfo(buf, "::%s",
|
|
|
|
deparse_type_name(node->consttype,
|
|
|
|
node->consttypmod));
|
2013-02-21 11:26:23 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
getTypeOutputInfo(node->consttype,
|
|
|
|
&typoutput, &typIsVarlena);
|
|
|
|
extval = OidOutputFunctionCall(typoutput, node->constvalue);
|
|
|
|
|
|
|
|
switch (node->consttype)
|
|
|
|
{
|
|
|
|
case INT2OID:
|
|
|
|
case INT4OID:
|
|
|
|
case INT8OID:
|
|
|
|
case OIDOID:
|
|
|
|
case FLOAT4OID:
|
|
|
|
case FLOAT8OID:
|
|
|
|
case NUMERICOID:
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* No need to quote unless it's a special value such as 'NaN'.
|
|
|
|
* See comments in get_const_expr().
|
|
|
|
*/
|
|
|
|
if (strspn(extval, "0123456789+-eE.") == strlen(extval))
|
|
|
|
{
|
|
|
|
if (extval[0] == '+' || extval[0] == '-')
|
|
|
|
appendStringInfo(buf, "(%s)", extval);
|
|
|
|
else
|
|
|
|
appendStringInfoString(buf, extval);
|
|
|
|
if (strcspn(extval, "eE.") != strlen(extval))
|
|
|
|
isfloat = true; /* it looks like a float */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
appendStringInfo(buf, "'%s'", extval);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BITOID:
|
|
|
|
case VARBITOID:
|
|
|
|
appendStringInfo(buf, "B'%s'", extval);
|
|
|
|
break;
|
|
|
|
case BOOLOID:
|
|
|
|
if (strcmp(extval, "t") == 0)
|
|
|
|
appendStringInfoString(buf, "true");
|
|
|
|
else
|
|
|
|
appendStringInfoString(buf, "false");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
deparseStringLiteral(buf, extval);
|
postgres_fdw: suppress casts on constants in limited cases.
When deparsing an expression of the form "remote_var OP constant",
we'd normally apply a cast to the constant to make sure that the
remote parser thinks it's of the same type we do. However, doing
so is often not necessary, and it causes problems if the user has
intentionally declared the local column as being of a different
type than the remote column. A plausible use-case for that is
using text to represent a type that's an enum on the remote side.
A comparison on such a column will get shipped as "var = 'foo'::text",
which blows up on the remote side because there's no enum = text
operator. But if we simply leave off the explicit cast, the
comparison will do exactly what the user wants.
It's possible to do this without major risk of semantic problems, by
relying on the longstanding parser heuristic that "if one operand of
an operator is of type unknown, while the other one has a known type,
assume that the unknown operand is also of that type". Hence, this
patch leaves off the cast only if (a) the operator inputs have the same
type locally; (b) the constant will print as a string literal or NULL,
both of which are initially taken as type unknown; and (c) the non-Const
input is a plain foreign Var. Rule (c) guarantees that the remote
parser will know the type of the non-Const input; moreover, it means
that if this cast-omission does cause any semantic surprises, that can
only happen in cases where the local column has a different type than
the remote column. That wasn't guaranteed to work anyway, and this
patch should represent a net usability gain for such cases.
One point that I (tgl) remain slightly uncomfortable with is that we
will ignore an implicit RelabelType when deciding if the non-Const input
is a plain Var. That makes it a little squishy to argue that the remote
should resolve the Const as being of the same type as its Var, because
then our Const is not the same type as our Var. However, if we don't do
that, then this hack won't work as desired if the user chooses to use
varchar rather than text to represent some remote column. That seems
useful, so do it like this for now. We might have to give up the
RelabelType-ignoring bit if any problems surface.
Dian Fay, with review and kibitzing by me
Discussion: https://postgr.es/m/C9LU294V7K4F.34LRRDU449O45@lamia
2021-11-12 17:50:40 +01:00
|
|
|
isstring = true;
|
2013-02-21 11:26:23 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-10-21 15:54:29 +02:00
|
|
|
pfree(extval);
|
|
|
|
|
postgres_fdw: suppress casts on constants in limited cases.
When deparsing an expression of the form "remote_var OP constant",
we'd normally apply a cast to the constant to make sure that the
remote parser thinks it's of the same type we do. However, doing
so is often not necessary, and it causes problems if the user has
intentionally declared the local column as being of a different
type than the remote column. A plausible use-case for that is
using text to represent a type that's an enum on the remote side.
A comparison on such a column will get shipped as "var = 'foo'::text",
which blows up on the remote side because there's no enum = text
operator. But if we simply leave off the explicit cast, the
comparison will do exactly what the user wants.
It's possible to do this without major risk of semantic problems, by
relying on the longstanding parser heuristic that "if one operand of
an operator is of type unknown, while the other one has a known type,
assume that the unknown operand is also of that type". Hence, this
patch leaves off the cast only if (a) the operator inputs have the same
type locally; (b) the constant will print as a string literal or NULL,
both of which are initially taken as type unknown; and (c) the non-Const
input is a plain foreign Var. Rule (c) guarantees that the remote
parser will know the type of the non-Const input; moreover, it means
that if this cast-omission does cause any semantic surprises, that can
only happen in cases where the local column has a different type than
the remote column. That wasn't guaranteed to work anyway, and this
patch should represent a net usability gain for such cases.
One point that I (tgl) remain slightly uncomfortable with is that we
will ignore an implicit RelabelType when deciding if the non-Const input
is a plain Var. That makes it a little squishy to argue that the remote
should resolve the Const as being of the same type as its Var, because
then our Const is not the same type as our Var. However, if we don't do
that, then this hack won't work as desired if the user chooses to use
varchar rather than text to represent some remote column. That seems
useful, so do it like this for now. We might have to give up the
RelabelType-ignoring bit if any problems surface.
Dian Fay, with review and kibitzing by me
Discussion: https://postgr.es/m/C9LU294V7K4F.34LRRDU449O45@lamia
2021-11-12 17:50:40 +01:00
|
|
|
if (showtype == -1)
|
|
|
|
return; /* never print type label */
|
2016-10-21 15:54:29 +02:00
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
/*
|
2016-10-21 15:54:29 +02:00
|
|
|
* For showtype == 0, append ::typename unless the constant will be
|
|
|
|
* implicitly typed as the right type when it is read in.
|
2013-02-21 11:26:23 +01:00
|
|
|
*
|
|
|
|
* XXX this code has to be kept in sync with the behavior of the parser,
|
|
|
|
* especially make_const.
|
|
|
|
*/
|
|
|
|
switch (node->consttype)
|
|
|
|
{
|
|
|
|
case BOOLOID:
|
|
|
|
case INT4OID:
|
|
|
|
case UNKNOWNOID:
|
|
|
|
needlabel = false;
|
|
|
|
break;
|
|
|
|
case NUMERICOID:
|
|
|
|
needlabel = !isfloat || (node->consttypmod >= 0);
|
|
|
|
break;
|
|
|
|
default:
|
postgres_fdw: suppress casts on constants in limited cases.
When deparsing an expression of the form "remote_var OP constant",
we'd normally apply a cast to the constant to make sure that the
remote parser thinks it's of the same type we do. However, doing
so is often not necessary, and it causes problems if the user has
intentionally declared the local column as being of a different
type than the remote column. A plausible use-case for that is
using text to represent a type that's an enum on the remote side.
A comparison on such a column will get shipped as "var = 'foo'::text",
which blows up on the remote side because there's no enum = text
operator. But if we simply leave off the explicit cast, the
comparison will do exactly what the user wants.
It's possible to do this without major risk of semantic problems, by
relying on the longstanding parser heuristic that "if one operand of
an operator is of type unknown, while the other one has a known type,
assume that the unknown operand is also of that type". Hence, this
patch leaves off the cast only if (a) the operator inputs have the same
type locally; (b) the constant will print as a string literal or NULL,
both of which are initially taken as type unknown; and (c) the non-Const
input is a plain foreign Var. Rule (c) guarantees that the remote
parser will know the type of the non-Const input; moreover, it means
that if this cast-omission does cause any semantic surprises, that can
only happen in cases where the local column has a different type than
the remote column. That wasn't guaranteed to work anyway, and this
patch should represent a net usability gain for such cases.
One point that I (tgl) remain slightly uncomfortable with is that we
will ignore an implicit RelabelType when deciding if the non-Const input
is a plain Var. That makes it a little squishy to argue that the remote
should resolve the Const as being of the same type as its Var, because
then our Const is not the same type as our Var. However, if we don't do
that, then this hack won't work as desired if the user chooses to use
varchar rather than text to represent some remote column. That seems
useful, so do it like this for now. We might have to give up the
RelabelType-ignoring bit if any problems surface.
Dian Fay, with review and kibitzing by me
Discussion: https://postgr.es/m/C9LU294V7K4F.34LRRDU449O45@lamia
2021-11-12 17:50:40 +01:00
|
|
|
if (showtype == -2)
|
|
|
|
{
|
|
|
|
/* label unless we printed it as an untyped string */
|
|
|
|
needlabel = !isstring;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
needlabel = true;
|
2013-02-21 11:26:23 +01:00
|
|
|
break;
|
|
|
|
}
|
2016-10-21 15:54:29 +02:00
|
|
|
if (needlabel || showtype > 0)
|
2013-02-21 11:26:23 +01:00
|
|
|
appendStringInfo(buf, "::%s",
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
deparse_type_name(node->consttype,
|
|
|
|
node->consttypmod));
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2013-03-22 00:43:59 +01:00
|
|
|
* Deparse given Param node.
|
2013-02-21 11:26:23 +01:00
|
|
|
*
|
2013-03-22 00:43:59 +01:00
|
|
|
* If we're generating the query "for real", add the Param to
|
|
|
|
* context->params_list if it's not already present, and then use its index
|
2014-04-16 23:21:57 +02:00
|
|
|
* in that list as the remote parameter number. During EXPLAIN, there's
|
|
|
|
* no need to identify a parameter number.
|
2013-02-21 11:26:23 +01:00
|
|
|
*/
|
|
|
|
static void
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseParam(Param *node, deparse_expr_cxt *context)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
2013-03-22 00:43:59 +01:00
|
|
|
if (context->params_list)
|
|
|
|
{
|
|
|
|
int pindex = 0;
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
/* find its index in params_list */
|
|
|
|
foreach(lc, *context->params_list)
|
|
|
|
{
|
|
|
|
pindex++;
|
|
|
|
if (equal(node, (Node *) lfirst(lc)))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (lc == NULL)
|
|
|
|
{
|
|
|
|
/* not in list, so add it */
|
|
|
|
pindex++;
|
|
|
|
*context->params_list = lappend(*context->params_list, node);
|
|
|
|
}
|
|
|
|
|
2014-04-16 23:21:57 +02:00
|
|
|
printRemoteParam(pindex, node->paramtype, node->paramtypmod, context);
|
2013-03-22 00:43:59 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-04-16 23:21:57 +02:00
|
|
|
printRemotePlaceholder(node->paramtype, node->paramtypmod, context);
|
2013-03-22 00:43:59 +01:00
|
|
|
}
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2019-02-01 16:50:32 +01:00
|
|
|
* Deparse a container subscript expression.
|
2013-02-21 11:26:23 +01:00
|
|
|
*/
|
|
|
|
static void
|
2019-02-01 16:50:32 +01:00
|
|
|
deparseSubscriptingRef(SubscriptingRef *node, deparse_expr_cxt *context)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
2013-03-22 00:43:59 +01:00
|
|
|
StringInfo buf = context->buf;
|
2013-02-21 11:26:23 +01:00
|
|
|
ListCell *lowlist_item;
|
|
|
|
ListCell *uplist_item;
|
|
|
|
|
|
|
|
/* Always parenthesize the expression. */
|
|
|
|
appendStringInfoChar(buf, '(');
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Deparse referenced array expression first. If that expression includes
|
|
|
|
* a cast, we have to parenthesize to prevent the array subscript from
|
2014-05-06 18:12:18 +02:00
|
|
|
* being taken as typename decoration. We can avoid that in the typical
|
2013-02-21 11:26:23 +01:00
|
|
|
* case of subscripting a Var, but otherwise do it.
|
|
|
|
*/
|
|
|
|
if (IsA(node->refexpr, Var))
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseExpr(node->refexpr, context);
|
2013-02-21 11:26:23 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
appendStringInfoChar(buf, '(');
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseExpr(node->refexpr, context);
|
2013-02-21 11:26:23 +01:00
|
|
|
appendStringInfoChar(buf, ')');
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Deparse subscript expressions. */
|
|
|
|
lowlist_item = list_head(node->reflowerindexpr); /* could be NULL */
|
|
|
|
foreach(uplist_item, node->refupperindexpr)
|
|
|
|
{
|
|
|
|
appendStringInfoChar(buf, '[');
|
|
|
|
if (lowlist_item)
|
|
|
|
{
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseExpr(lfirst(lowlist_item), context);
|
2013-02-21 11:26:23 +01:00
|
|
|
appendStringInfoChar(buf, ':');
|
Represent Lists as expansible arrays, not chains of cons-cells.
Originally, Postgres Lists were a more or less exact reimplementation of
Lisp lists, which consist of chains of separately-allocated cons cells,
each having a value and a next-cell link. We'd hacked that once before
(commit d0b4399d8) to add a separate List header, but the data was still
in cons cells. That makes some operations -- notably list_nth() -- O(N),
and it's bulky because of the next-cell pointers and per-cell palloc
overhead, and it's very cache-unfriendly if the cons cells end up
scattered around rather than being adjacent.
In this rewrite, we still have List headers, but the data is in a
resizable array of values, with no next-cell links. Now we need at
most two palloc's per List, and often only one, since we can allocate
some values in the same palloc call as the List header. (Of course,
extending an existing List may require repalloc's to enlarge the array.
But this involves just O(log N) allocations not O(N).)
Of course this is not without downsides. The key difficulty is that
addition or deletion of a list entry may now cause other entries to
move, which it did not before.
For example, that breaks foreach() and sister macros, which historically
used a pointer to the current cons-cell as loop state. We can repair
those macros transparently by making their actual loop state be an
integer list index; the exposed "ListCell *" pointer is no longer state
carried across loop iterations, but is just a derived value. (In
practice, modern compilers can optimize things back to having just one
loop state value, at least for simple cases with inline loop bodies.)
In principle, this is a semantics change for cases where the loop body
inserts or deletes list entries ahead of the current loop index; but
I found no such cases in the Postgres code.
The change is not at all transparent for code that doesn't use foreach()
but chases lists "by hand" using lnext(). The largest share of such
code in the backend is in loops that were maintaining "prev" and "next"
variables in addition to the current-cell pointer, in order to delete
list cells efficiently using list_delete_cell(). However, we no longer
need a previous-cell pointer to delete a list cell efficiently. Keeping
a next-cell pointer doesn't work, as explained above, but we can improve
matters by changing such code to use a regular foreach() loop and then
using the new macro foreach_delete_current() to delete the current cell.
(This macro knows how to update the associated foreach loop's state so
that no cells will be missed in the traversal.)
There remains a nontrivial risk of code assuming that a ListCell *
pointer will remain good over an operation that could now move the list
contents. To help catch such errors, list.c can be compiled with a new
define symbol DEBUG_LIST_MEMORY_USAGE that forcibly moves list contents
whenever that could possibly happen. This makes list operations
significantly more expensive so it's not normally turned on (though it
is on by default if USE_VALGRIND is on).
There are two notable API differences from the previous code:
* lnext() now requires the List's header pointer in addition to the
current cell's address.
* list_delete_cell() no longer requires a previous-cell argument.
These changes are somewhat unfortunate, but on the other hand code using
either function needs inspection to see if it is assuming anything
it shouldn't, so it's not all bad.
Programmers should be aware of these significant performance changes:
* list_nth() and related functions are now O(1); so there's no
major access-speed difference between a list and an array.
* Inserting or deleting a list element now takes time proportional to
the distance to the end of the list, due to moving the array elements.
(However, it typically *doesn't* require palloc or pfree, so except in
long lists it's probably still faster than before.) Notably, lcons()
used to be about the same cost as lappend(), but that's no longer true
if the list is long. Code that uses lcons() and list_delete_first()
to maintain a stack might usefully be rewritten to push and pop at the
end of the list rather than the beginning.
* There are now list_insert_nth...() and list_delete_nth...() functions
that add or remove a list cell identified by index. These have the
data-movement penalty explained above, but there's no search penalty.
* list_concat() and variants now copy the second list's data into
storage belonging to the first list, so there is no longer any
sharing of cells between the input lists. The second argument is
now declared "const List *" to reflect that it isn't changed.
This patch just does the minimum needed to get the new implementation
in place and fix bugs exposed by the regression tests. As suggested
by the foregoing, there's a fair amount of followup work remaining to
do.
Also, the ENABLE_LIST_COMPAT macros are finally removed in this
commit. Code using those should have been gone a dozen years ago.
Patch by me; thanks to David Rowley, Jesper Pedersen, and others
for review.
Discussion: https://postgr.es/m/11587.1550975080@sss.pgh.pa.us
2019-07-15 19:41:58 +02:00
|
|
|
lowlist_item = lnext(node->reflowerindexpr, lowlist_item);
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseExpr(lfirst(uplist_item), context);
|
2013-02-21 11:26:23 +01:00
|
|
|
appendStringInfoChar(buf, ']');
|
|
|
|
}
|
|
|
|
|
|
|
|
appendStringInfoChar(buf, ')');
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2013-03-22 00:43:59 +01:00
|
|
|
* Deparse a function call.
|
2013-02-21 11:26:23 +01:00
|
|
|
*/
|
|
|
|
static void
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseFuncExpr(FuncExpr *node, deparse_expr_cxt *context)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
2013-03-22 00:43:59 +01:00
|
|
|
StringInfo buf = context->buf;
|
2013-02-21 11:26:23 +01:00
|
|
|
bool use_variadic;
|
|
|
|
bool first;
|
|
|
|
ListCell *arg;
|
|
|
|
|
2013-02-22 13:30:21 +01:00
|
|
|
/*
|
|
|
|
* If the function call came from an implicit coercion, then just show the
|
|
|
|
* first argument.
|
|
|
|
*/
|
|
|
|
if (node->funcformat == COERCE_IMPLICIT_CAST)
|
|
|
|
{
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseExpr((Expr *) linitial(node->args), context);
|
2013-02-22 13:30:21 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the function call came from a cast, then show the first argument
|
|
|
|
* plus an explicit cast operation.
|
|
|
|
*/
|
|
|
|
if (node->funcformat == COERCE_EXPLICIT_CAST)
|
|
|
|
{
|
|
|
|
Oid rettype = node->funcresulttype;
|
|
|
|
int32 coercedTypmod;
|
|
|
|
|
|
|
|
/* Get the typmod if this is a length-coercion function */
|
|
|
|
(void) exprIsLengthCoercion((Node *) node, &coercedTypmod);
|
|
|
|
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseExpr((Expr *) linitial(node->args), context);
|
2013-02-22 13:30:21 +01:00
|
|
|
appendStringInfo(buf, "::%s",
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
deparse_type_name(rettype, coercedTypmod));
|
2013-02-22 13:30:21 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
/* Check if need to print VARIADIC (cf. ruleutils.c) */
|
Fix non-equivalence of VARIADIC and non-VARIADIC function call formats.
For variadic functions (other than VARIADIC ANY), the syntaxes foo(x,y,...)
and foo(VARIADIC ARRAY[x,y,...]) should be considered equivalent, since the
former is converted to the latter at parse time. They have indeed been
equivalent, in all releases before 9.3. However, commit 75b39e790 made an
ill-considered decision to record which syntax had been used in FuncExpr
nodes, and then to make equal() test that in checking node equality ---
which caused the syntaxes to not be seen as equivalent by the planner.
This is the underlying cause of bug #9817 from Dmitry Ryabov.
It might seem that a quick fix would be to make equal() disregard
FuncExpr.funcvariadic, but the same commit made that untenable, because
the field actually *is* semantically significant for some VARIADIC ANY
functions. This patch instead adopts the approach of redefining
funcvariadic (and aggvariadic, in HEAD) as meaning that the last argument
is a variadic array, whether it got that way by parser intervention or was
supplied explicitly by the user. Therefore the value will always be true
for non-ANY variadic functions, restoring the principle of equivalence.
(However, the planner will continue to consider use of VARIADIC as a
meaningful difference for VARIADIC ANY functions, even though some such
functions might disregard it.)
In HEAD, this change lets us simplify the decompilation logic in
ruleutils.c, since the funcvariadic/aggvariadic flag tells directly whether
to print VARIADIC. However, in 9.3 we have to continue to cope with
existing stored rules/views that might contain the previous definition.
Fortunately, this just means no change in ruleutils.c, since its existing
behavior effectively ignores funcvariadic for all cases other than VARIADIC
ANY functions.
In HEAD, bump catversion to reflect the fact that FuncExpr.funcvariadic
changed meanings; this is sort of pro forma, since I don't believe any
built-in views are affected.
Unfortunately, this patch doesn't magically fix everything for affected
9.3 users. After installing 9.3.5, they might need to recreate their
rules/views/indexes containing variadic function calls in order to get
everything consistent with the new definition. As in the cited bug,
the symptom of a problem would be failure to use a nominally matching
index that has a variadic function call in its definition. We'll need
to mention this in the 9.3.5 release notes.
2014-04-04 04:02:24 +02:00
|
|
|
use_variadic = node->funcvariadic;
|
2013-02-21 11:26:23 +01:00
|
|
|
|
2016-10-21 15:54:29 +02:00
|
|
|
/*
|
|
|
|
* Normal function: display as proname(args).
|
|
|
|
*/
|
|
|
|
appendFunctionName(node->funcid, context);
|
|
|
|
appendStringInfoChar(buf, '(');
|
2013-02-22 12:03:46 +01:00
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
/* ... and all the arguments */
|
|
|
|
first = true;
|
|
|
|
foreach(arg, node->args)
|
|
|
|
{
|
|
|
|
if (!first)
|
|
|
|
appendStringInfoString(buf, ", ");
|
Represent Lists as expansible arrays, not chains of cons-cells.
Originally, Postgres Lists were a more or less exact reimplementation of
Lisp lists, which consist of chains of separately-allocated cons cells,
each having a value and a next-cell link. We'd hacked that once before
(commit d0b4399d8) to add a separate List header, but the data was still
in cons cells. That makes some operations -- notably list_nth() -- O(N),
and it's bulky because of the next-cell pointers and per-cell palloc
overhead, and it's very cache-unfriendly if the cons cells end up
scattered around rather than being adjacent.
In this rewrite, we still have List headers, but the data is in a
resizable array of values, with no next-cell links. Now we need at
most two palloc's per List, and often only one, since we can allocate
some values in the same palloc call as the List header. (Of course,
extending an existing List may require repalloc's to enlarge the array.
But this involves just O(log N) allocations not O(N).)
Of course this is not without downsides. The key difficulty is that
addition or deletion of a list entry may now cause other entries to
move, which it did not before.
For example, that breaks foreach() and sister macros, which historically
used a pointer to the current cons-cell as loop state. We can repair
those macros transparently by making their actual loop state be an
integer list index; the exposed "ListCell *" pointer is no longer state
carried across loop iterations, but is just a derived value. (In
practice, modern compilers can optimize things back to having just one
loop state value, at least for simple cases with inline loop bodies.)
In principle, this is a semantics change for cases where the loop body
inserts or deletes list entries ahead of the current loop index; but
I found no such cases in the Postgres code.
The change is not at all transparent for code that doesn't use foreach()
but chases lists "by hand" using lnext(). The largest share of such
code in the backend is in loops that were maintaining "prev" and "next"
variables in addition to the current-cell pointer, in order to delete
list cells efficiently using list_delete_cell(). However, we no longer
need a previous-cell pointer to delete a list cell efficiently. Keeping
a next-cell pointer doesn't work, as explained above, but we can improve
matters by changing such code to use a regular foreach() loop and then
using the new macro foreach_delete_current() to delete the current cell.
(This macro knows how to update the associated foreach loop's state so
that no cells will be missed in the traversal.)
There remains a nontrivial risk of code assuming that a ListCell *
pointer will remain good over an operation that could now move the list
contents. To help catch such errors, list.c can be compiled with a new
define symbol DEBUG_LIST_MEMORY_USAGE that forcibly moves list contents
whenever that could possibly happen. This makes list operations
significantly more expensive so it's not normally turned on (though it
is on by default if USE_VALGRIND is on).
There are two notable API differences from the previous code:
* lnext() now requires the List's header pointer in addition to the
current cell's address.
* list_delete_cell() no longer requires a previous-cell argument.
These changes are somewhat unfortunate, but on the other hand code using
either function needs inspection to see if it is assuming anything
it shouldn't, so it's not all bad.
Programmers should be aware of these significant performance changes:
* list_nth() and related functions are now O(1); so there's no
major access-speed difference between a list and an array.
* Inserting or deleting a list element now takes time proportional to
the distance to the end of the list, due to moving the array elements.
(However, it typically *doesn't* require palloc or pfree, so except in
long lists it's probably still faster than before.) Notably, lcons()
used to be about the same cost as lappend(), but that's no longer true
if the list is long. Code that uses lcons() and list_delete_first()
to maintain a stack might usefully be rewritten to push and pop at the
end of the list rather than the beginning.
* There are now list_insert_nth...() and list_delete_nth...() functions
that add or remove a list cell identified by index. These have the
data-movement penalty explained above, but there's no search penalty.
* list_concat() and variants now copy the second list's data into
storage belonging to the first list, so there is no longer any
sharing of cells between the input lists. The second argument is
now declared "const List *" to reflect that it isn't changed.
This patch just does the minimum needed to get the new implementation
in place and fix bugs exposed by the regression tests. As suggested
by the foregoing, there's a fair amount of followup work remaining to
do.
Also, the ENABLE_LIST_COMPAT macros are finally removed in this
commit. Code using those should have been gone a dozen years ago.
Patch by me; thanks to David Rowley, Jesper Pedersen, and others
for review.
Discussion: https://postgr.es/m/11587.1550975080@sss.pgh.pa.us
2019-07-15 19:41:58 +02:00
|
|
|
if (use_variadic && lnext(node->args, arg) == NULL)
|
2013-02-21 11:26:23 +01:00
|
|
|
appendStringInfoString(buf, "VARIADIC ");
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseExpr((Expr *) lfirst(arg), context);
|
2013-02-21 11:26:23 +01:00
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
appendStringInfoChar(buf, ')');
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2014-05-06 18:12:18 +02:00
|
|
|
* Deparse given operator expression. To avoid problems around
|
2013-02-22 12:03:46 +01:00
|
|
|
* priority of operations, we always parenthesize the arguments.
|
2013-02-21 11:26:23 +01:00
|
|
|
*/
|
|
|
|
static void
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseOpExpr(OpExpr *node, deparse_expr_cxt *context)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
2013-03-22 00:43:59 +01:00
|
|
|
StringInfo buf = context->buf;
|
2013-02-21 11:26:23 +01:00
|
|
|
HeapTuple tuple;
|
|
|
|
Form_pg_operator form;
|
postgres_fdw: suppress casts on constants in limited cases.
When deparsing an expression of the form "remote_var OP constant",
we'd normally apply a cast to the constant to make sure that the
remote parser thinks it's of the same type we do. However, doing
so is often not necessary, and it causes problems if the user has
intentionally declared the local column as being of a different
type than the remote column. A plausible use-case for that is
using text to represent a type that's an enum on the remote side.
A comparison on such a column will get shipped as "var = 'foo'::text",
which blows up on the remote side because there's no enum = text
operator. But if we simply leave off the explicit cast, the
comparison will do exactly what the user wants.
It's possible to do this without major risk of semantic problems, by
relying on the longstanding parser heuristic that "if one operand of
an operator is of type unknown, while the other one has a known type,
assume that the unknown operand is also of that type". Hence, this
patch leaves off the cast only if (a) the operator inputs have the same
type locally; (b) the constant will print as a string literal or NULL,
both of which are initially taken as type unknown; and (c) the non-Const
input is a plain foreign Var. Rule (c) guarantees that the remote
parser will know the type of the non-Const input; moreover, it means
that if this cast-omission does cause any semantic surprises, that can
only happen in cases where the local column has a different type than
the remote column. That wasn't guaranteed to work anyway, and this
patch should represent a net usability gain for such cases.
One point that I (tgl) remain slightly uncomfortable with is that we
will ignore an implicit RelabelType when deciding if the non-Const input
is a plain Var. That makes it a little squishy to argue that the remote
should resolve the Const as being of the same type as its Var, because
then our Const is not the same type as our Var. However, if we don't do
that, then this hack won't work as desired if the user chooses to use
varchar rather than text to represent some remote column. That seems
useful, so do it like this for now. We might have to give up the
RelabelType-ignoring bit if any problems surface.
Dian Fay, with review and kibitzing by me
Discussion: https://postgr.es/m/C9LU294V7K4F.34LRRDU449O45@lamia
2021-11-12 17:50:40 +01:00
|
|
|
Expr *right;
|
|
|
|
bool canSuppressRightConstCast = false;
|
2013-02-21 11:26:23 +01:00
|
|
|
char oprkind;
|
|
|
|
|
|
|
|
/* Retrieve information about the operator from system catalog. */
|
|
|
|
tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(node->opno));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
elog(ERROR, "cache lookup failed for operator %u", node->opno);
|
|
|
|
form = (Form_pg_operator) GETSTRUCT(tuple);
|
|
|
|
oprkind = form->oprkind;
|
|
|
|
|
|
|
|
/* Sanity check. */
|
Remove support for postfix (right-unary) operators.
This feature has been a thorn in our sides for a long time, causing
many grammatical ambiguity problems. It doesn't seem worth the
pain to continue to support it, so remove it.
There are some follow-on improvements we can make in the grammar,
but this commit only removes the bare minimum number of productions,
plus assorted backend support code.
Note that pg_dump and psql continue to have full support, since
they may be used against older servers. However, pg_dump warns
about postfix operators. There is also a check in pg_upgrade.
Documentation-wise, I (tgl) largely removed the "left unary"
terminology in favor of saying "prefix operator", which is
a more standard and IMO less confusing term.
I included a catversion bump, although no initial catalog data
changes here, to mark the boundary at which oprkind = 'r'
stopped being valid in pg_operator.
Mark Dilger, based on work by myself and Robert Haas;
review by John Naylor
Discussion: https://postgr.es/m/38ca86db-42ab-9b48-2902-337a0d6b8311@2ndquadrant.com
2020-09-18 01:38:05 +02:00
|
|
|
Assert((oprkind == 'l' && list_length(node->args) == 1) ||
|
2013-02-21 11:26:23 +01:00
|
|
|
(oprkind == 'b' && list_length(node->args) == 2));
|
|
|
|
|
postgres_fdw: suppress casts on constants in limited cases.
When deparsing an expression of the form "remote_var OP constant",
we'd normally apply a cast to the constant to make sure that the
remote parser thinks it's of the same type we do. However, doing
so is often not necessary, and it causes problems if the user has
intentionally declared the local column as being of a different
type than the remote column. A plausible use-case for that is
using text to represent a type that's an enum on the remote side.
A comparison on such a column will get shipped as "var = 'foo'::text",
which blows up on the remote side because there's no enum = text
operator. But if we simply leave off the explicit cast, the
comparison will do exactly what the user wants.
It's possible to do this without major risk of semantic problems, by
relying on the longstanding parser heuristic that "if one operand of
an operator is of type unknown, while the other one has a known type,
assume that the unknown operand is also of that type". Hence, this
patch leaves off the cast only if (a) the operator inputs have the same
type locally; (b) the constant will print as a string literal or NULL,
both of which are initially taken as type unknown; and (c) the non-Const
input is a plain foreign Var. Rule (c) guarantees that the remote
parser will know the type of the non-Const input; moreover, it means
that if this cast-omission does cause any semantic surprises, that can
only happen in cases where the local column has a different type than
the remote column. That wasn't guaranteed to work anyway, and this
patch should represent a net usability gain for such cases.
One point that I (tgl) remain slightly uncomfortable with is that we
will ignore an implicit RelabelType when deciding if the non-Const input
is a plain Var. That makes it a little squishy to argue that the remote
should resolve the Const as being of the same type as its Var, because
then our Const is not the same type as our Var. However, if we don't do
that, then this hack won't work as desired if the user chooses to use
varchar rather than text to represent some remote column. That seems
useful, so do it like this for now. We might have to give up the
RelabelType-ignoring bit if any problems surface.
Dian Fay, with review and kibitzing by me
Discussion: https://postgr.es/m/C9LU294V7K4F.34LRRDU449O45@lamia
2021-11-12 17:50:40 +01:00
|
|
|
right = llast(node->args);
|
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
/* Always parenthesize the expression. */
|
|
|
|
appendStringInfoChar(buf, '(');
|
|
|
|
|
Remove support for postfix (right-unary) operators.
This feature has been a thorn in our sides for a long time, causing
many grammatical ambiguity problems. It doesn't seem worth the
pain to continue to support it, so remove it.
There are some follow-on improvements we can make in the grammar,
but this commit only removes the bare minimum number of productions,
plus assorted backend support code.
Note that pg_dump and psql continue to have full support, since
they may be used against older servers. However, pg_dump warns
about postfix operators. There is also a check in pg_upgrade.
Documentation-wise, I (tgl) largely removed the "left unary"
terminology in favor of saying "prefix operator", which is
a more standard and IMO less confusing term.
I included a catversion bump, although no initial catalog data
changes here, to mark the boundary at which oprkind = 'r'
stopped being valid in pg_operator.
Mark Dilger, based on work by myself and Robert Haas;
review by John Naylor
Discussion: https://postgr.es/m/38ca86db-42ab-9b48-2902-337a0d6b8311@2ndquadrant.com
2020-09-18 01:38:05 +02:00
|
|
|
/* Deparse left operand, if any. */
|
|
|
|
if (oprkind == 'b')
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
postgres_fdw: suppress casts on constants in limited cases.
When deparsing an expression of the form "remote_var OP constant",
we'd normally apply a cast to the constant to make sure that the
remote parser thinks it's of the same type we do. However, doing
so is often not necessary, and it causes problems if the user has
intentionally declared the local column as being of a different
type than the remote column. A plausible use-case for that is
using text to represent a type that's an enum on the remote side.
A comparison on such a column will get shipped as "var = 'foo'::text",
which blows up on the remote side because there's no enum = text
operator. But if we simply leave off the explicit cast, the
comparison will do exactly what the user wants.
It's possible to do this without major risk of semantic problems, by
relying on the longstanding parser heuristic that "if one operand of
an operator is of type unknown, while the other one has a known type,
assume that the unknown operand is also of that type". Hence, this
patch leaves off the cast only if (a) the operator inputs have the same
type locally; (b) the constant will print as a string literal or NULL,
both of which are initially taken as type unknown; and (c) the non-Const
input is a plain foreign Var. Rule (c) guarantees that the remote
parser will know the type of the non-Const input; moreover, it means
that if this cast-omission does cause any semantic surprises, that can
only happen in cases where the local column has a different type than
the remote column. That wasn't guaranteed to work anyway, and this
patch should represent a net usability gain for such cases.
One point that I (tgl) remain slightly uncomfortable with is that we
will ignore an implicit RelabelType when deciding if the non-Const input
is a plain Var. That makes it a little squishy to argue that the remote
should resolve the Const as being of the same type as its Var, because
then our Const is not the same type as our Var. However, if we don't do
that, then this hack won't work as desired if the user chooses to use
varchar rather than text to represent some remote column. That seems
useful, so do it like this for now. We might have to give up the
RelabelType-ignoring bit if any problems surface.
Dian Fay, with review and kibitzing by me
Discussion: https://postgr.es/m/C9LU294V7K4F.34LRRDU449O45@lamia
2021-11-12 17:50:40 +01:00
|
|
|
Expr *left = linitial(node->args);
|
|
|
|
Oid leftType = exprType((Node *) left);
|
|
|
|
Oid rightType = exprType((Node *) right);
|
|
|
|
bool canSuppressLeftConstCast = false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When considering a binary operator, if one operand is a Const that
|
|
|
|
* can be printed as a bare string literal or NULL (i.e., it will look
|
|
|
|
* like type UNKNOWN to the remote parser), the Const normally
|
|
|
|
* receives an explicit cast to the operator's input type. However,
|
|
|
|
* in Const-to-Var comparisons where both operands are of the same
|
|
|
|
* type, we prefer to suppress the explicit cast, leaving the Const's
|
|
|
|
* type resolution up to the remote parser. The remote's resolution
|
|
|
|
* heuristic will assume that an unknown input type being compared to
|
|
|
|
* a known input type is of that known type as well.
|
|
|
|
*
|
|
|
|
* This hack allows some cases to succeed where a remote column is
|
|
|
|
* declared with a different type in the local (foreign) table. By
|
|
|
|
* emitting "foreigncol = 'foo'" not "foreigncol = 'foo'::text" or the
|
|
|
|
* like, we allow the remote parser to pick an "=" operator that's
|
|
|
|
* compatible with whatever type the remote column really is, such as
|
|
|
|
* an enum.
|
|
|
|
*
|
|
|
|
* We allow cast suppression to happen only when the other operand is
|
|
|
|
* a plain foreign Var. Although the remote's unknown-type heuristic
|
|
|
|
* would apply to other cases just as well, we would be taking a
|
|
|
|
* bigger risk that the inferred type is something unexpected. With
|
|
|
|
* this restriction, if anything goes wrong it's the user's fault for
|
|
|
|
* not declaring the local column with the same type as the remote
|
|
|
|
* column.
|
|
|
|
*/
|
|
|
|
if (leftType == rightType)
|
|
|
|
{
|
|
|
|
if (IsA(left, Const))
|
|
|
|
canSuppressLeftConstCast = isPlainForeignVar(right, context);
|
|
|
|
else if (IsA(right, Const))
|
|
|
|
canSuppressRightConstCast = isPlainForeignVar(left, context);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (canSuppressLeftConstCast)
|
|
|
|
deparseConst((Const *) left, context, -2);
|
|
|
|
else
|
|
|
|
deparseExpr(left, context);
|
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
appendStringInfoChar(buf, ' ');
|
|
|
|
}
|
|
|
|
|
2013-02-22 12:03:46 +01:00
|
|
|
/* Deparse operator name. */
|
|
|
|
deparseOperatorName(buf, form);
|
2013-02-21 11:26:23 +01:00
|
|
|
|
|
|
|
/* Deparse right operand. */
|
Remove support for postfix (right-unary) operators.
This feature has been a thorn in our sides for a long time, causing
many grammatical ambiguity problems. It doesn't seem worth the
pain to continue to support it, so remove it.
There are some follow-on improvements we can make in the grammar,
but this commit only removes the bare minimum number of productions,
plus assorted backend support code.
Note that pg_dump and psql continue to have full support, since
they may be used against older servers. However, pg_dump warns
about postfix operators. There is also a check in pg_upgrade.
Documentation-wise, I (tgl) largely removed the "left unary"
terminology in favor of saying "prefix operator", which is
a more standard and IMO less confusing term.
I included a catversion bump, although no initial catalog data
changes here, to mark the boundary at which oprkind = 'r'
stopped being valid in pg_operator.
Mark Dilger, based on work by myself and Robert Haas;
review by John Naylor
Discussion: https://postgr.es/m/38ca86db-42ab-9b48-2902-337a0d6b8311@2ndquadrant.com
2020-09-18 01:38:05 +02:00
|
|
|
appendStringInfoChar(buf, ' ');
|
postgres_fdw: suppress casts on constants in limited cases.
When deparsing an expression of the form "remote_var OP constant",
we'd normally apply a cast to the constant to make sure that the
remote parser thinks it's of the same type we do. However, doing
so is often not necessary, and it causes problems if the user has
intentionally declared the local column as being of a different
type than the remote column. A plausible use-case for that is
using text to represent a type that's an enum on the remote side.
A comparison on such a column will get shipped as "var = 'foo'::text",
which blows up on the remote side because there's no enum = text
operator. But if we simply leave off the explicit cast, the
comparison will do exactly what the user wants.
It's possible to do this without major risk of semantic problems, by
relying on the longstanding parser heuristic that "if one operand of
an operator is of type unknown, while the other one has a known type,
assume that the unknown operand is also of that type". Hence, this
patch leaves off the cast only if (a) the operator inputs have the same
type locally; (b) the constant will print as a string literal or NULL,
both of which are initially taken as type unknown; and (c) the non-Const
input is a plain foreign Var. Rule (c) guarantees that the remote
parser will know the type of the non-Const input; moreover, it means
that if this cast-omission does cause any semantic surprises, that can
only happen in cases where the local column has a different type than
the remote column. That wasn't guaranteed to work anyway, and this
patch should represent a net usability gain for such cases.
One point that I (tgl) remain slightly uncomfortable with is that we
will ignore an implicit RelabelType when deciding if the non-Const input
is a plain Var. That makes it a little squishy to argue that the remote
should resolve the Const as being of the same type as its Var, because
then our Const is not the same type as our Var. However, if we don't do
that, then this hack won't work as desired if the user chooses to use
varchar rather than text to represent some remote column. That seems
useful, so do it like this for now. We might have to give up the
RelabelType-ignoring bit if any problems surface.
Dian Fay, with review and kibitzing by me
Discussion: https://postgr.es/m/C9LU294V7K4F.34LRRDU449O45@lamia
2021-11-12 17:50:40 +01:00
|
|
|
|
|
|
|
if (canSuppressRightConstCast)
|
|
|
|
deparseConst((Const *) right, context, -2);
|
|
|
|
else
|
|
|
|
deparseExpr(right, context);
|
2013-02-21 11:26:23 +01:00
|
|
|
|
|
|
|
appendStringInfoChar(buf, ')');
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
}
|
|
|
|
|
postgres_fdw: suppress casts on constants in limited cases.
When deparsing an expression of the form "remote_var OP constant",
we'd normally apply a cast to the constant to make sure that the
remote parser thinks it's of the same type we do. However, doing
so is often not necessary, and it causes problems if the user has
intentionally declared the local column as being of a different
type than the remote column. A plausible use-case for that is
using text to represent a type that's an enum on the remote side.
A comparison on such a column will get shipped as "var = 'foo'::text",
which blows up on the remote side because there's no enum = text
operator. But if we simply leave off the explicit cast, the
comparison will do exactly what the user wants.
It's possible to do this without major risk of semantic problems, by
relying on the longstanding parser heuristic that "if one operand of
an operator is of type unknown, while the other one has a known type,
assume that the unknown operand is also of that type". Hence, this
patch leaves off the cast only if (a) the operator inputs have the same
type locally; (b) the constant will print as a string literal or NULL,
both of which are initially taken as type unknown; and (c) the non-Const
input is a plain foreign Var. Rule (c) guarantees that the remote
parser will know the type of the non-Const input; moreover, it means
that if this cast-omission does cause any semantic surprises, that can
only happen in cases where the local column has a different type than
the remote column. That wasn't guaranteed to work anyway, and this
patch should represent a net usability gain for such cases.
One point that I (tgl) remain slightly uncomfortable with is that we
will ignore an implicit RelabelType when deciding if the non-Const input
is a plain Var. That makes it a little squishy to argue that the remote
should resolve the Const as being of the same type as its Var, because
then our Const is not the same type as our Var. However, if we don't do
that, then this hack won't work as desired if the user chooses to use
varchar rather than text to represent some remote column. That seems
useful, so do it like this for now. We might have to give up the
RelabelType-ignoring bit if any problems surface.
Dian Fay, with review and kibitzing by me
Discussion: https://postgr.es/m/C9LU294V7K4F.34LRRDU449O45@lamia
2021-11-12 17:50:40 +01:00
|
|
|
/*
|
|
|
|
* Will "node" deparse as a plain foreign Var?
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
isPlainForeignVar(Expr *node, deparse_expr_cxt *context)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We allow the foreign Var to have an implicit RelabelType, mainly so
|
|
|
|
* that this'll work with varchar columns. Note that deparseRelabelType
|
|
|
|
* will not print such a cast, so we're not breaking the restriction that
|
|
|
|
* the expression print as a plain Var. We won't risk it for an implicit
|
|
|
|
* cast that requires a function, nor for non-implicit RelabelType; such
|
|
|
|
* cases seem too likely to involve semantics changes compared to what
|
|
|
|
* would happen on the remote side.
|
|
|
|
*/
|
|
|
|
if (IsA(node, RelabelType) &&
|
|
|
|
((RelabelType *) node)->relabelformat == COERCE_IMPLICIT_CAST)
|
|
|
|
node = ((RelabelType *) node)->arg;
|
|
|
|
|
|
|
|
if (IsA(node, Var))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* The Var must be one that'll deparse as a foreign column reference
|
|
|
|
* (cf. deparseVar).
|
|
|
|
*/
|
|
|
|
Var *var = (Var *) node;
|
|
|
|
Relids relids = context->scanrel->relids;
|
|
|
|
|
|
|
|
if (bms_is_member(var->varno, relids) && var->varlevelsup == 0)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-02-22 12:03:46 +01:00
|
|
|
/*
|
|
|
|
* Print the name of an operator.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
deparseOperatorName(StringInfo buf, Form_pg_operator opform)
|
|
|
|
{
|
|
|
|
char *opname;
|
|
|
|
|
|
|
|
/* opname is not a SQL identifier, so we should not quote it. */
|
|
|
|
opname = NameStr(opform->oprname);
|
|
|
|
|
|
|
|
/* Print schema name only if it's not pg_catalog */
|
|
|
|
if (opform->oprnamespace != PG_CATALOG_NAMESPACE)
|
|
|
|
{
|
|
|
|
const char *opnspname;
|
|
|
|
|
|
|
|
opnspname = get_namespace_name(opform->oprnamespace);
|
|
|
|
/* Print fully qualified operator name. */
|
|
|
|
appendStringInfo(buf, "OPERATOR(%s.%s)",
|
|
|
|
quote_identifier(opnspname), opname);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Just print operator name. */
|
2013-10-31 15:55:59 +01:00
|
|
|
appendStringInfoString(buf, opname);
|
2013-02-22 12:03:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
/*
|
|
|
|
* Deparse IS DISTINCT FROM.
|
|
|
|
*/
|
|
|
|
static void
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseDistinctExpr(DistinctExpr *node, deparse_expr_cxt *context)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
2013-03-22 00:43:59 +01:00
|
|
|
StringInfo buf = context->buf;
|
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
Assert(list_length(node->args) == 2);
|
|
|
|
|
|
|
|
appendStringInfoChar(buf, '(');
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseExpr(linitial(node->args), context);
|
2013-03-10 19:14:53 +01:00
|
|
|
appendStringInfoString(buf, " IS DISTINCT FROM ");
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseExpr(lsecond(node->args), context);
|
2013-02-21 11:26:23 +01:00
|
|
|
appendStringInfoChar(buf, ')');
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2014-05-06 18:12:18 +02:00
|
|
|
* Deparse given ScalarArrayOpExpr expression. To avoid problems
|
2013-02-22 12:03:46 +01:00
|
|
|
* around priority of operations, we always parenthesize the arguments.
|
2013-02-21 11:26:23 +01:00
|
|
|
*/
|
|
|
|
static void
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseScalarArrayOpExpr(ScalarArrayOpExpr *node, deparse_expr_cxt *context)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
2013-03-22 00:43:59 +01:00
|
|
|
StringInfo buf = context->buf;
|
2013-02-21 11:26:23 +01:00
|
|
|
HeapTuple tuple;
|
|
|
|
Form_pg_operator form;
|
|
|
|
Expr *arg1;
|
|
|
|
Expr *arg2;
|
|
|
|
|
|
|
|
/* Retrieve information about the operator from system catalog. */
|
|
|
|
tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(node->opno));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
elog(ERROR, "cache lookup failed for operator %u", node->opno);
|
|
|
|
form = (Form_pg_operator) GETSTRUCT(tuple);
|
|
|
|
|
|
|
|
/* Sanity check. */
|
|
|
|
Assert(list_length(node->args) == 2);
|
|
|
|
|
|
|
|
/* Always parenthesize the expression. */
|
|
|
|
appendStringInfoChar(buf, '(');
|
|
|
|
|
|
|
|
/* Deparse left operand. */
|
|
|
|
arg1 = linitial(node->args);
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseExpr(arg1, context);
|
2013-02-22 12:03:46 +01:00
|
|
|
appendStringInfoChar(buf, ' ');
|
2013-02-21 11:26:23 +01:00
|
|
|
|
2013-02-22 12:03:46 +01:00
|
|
|
/* Deparse operator name plus decoration. */
|
|
|
|
deparseOperatorName(buf, form);
|
|
|
|
appendStringInfo(buf, " %s (", node->useOr ? "ANY" : "ALL");
|
2013-02-21 11:26:23 +01:00
|
|
|
|
|
|
|
/* Deparse right operand. */
|
|
|
|
arg2 = lsecond(node->args);
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseExpr(arg2, context);
|
2013-02-21 11:26:23 +01:00
|
|
|
|
|
|
|
appendStringInfoChar(buf, ')');
|
|
|
|
|
|
|
|
/* Always parenthesize the expression. */
|
|
|
|
appendStringInfoChar(buf, ')');
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Deparse a RelabelType (binary-compatible cast) node.
|
|
|
|
*/
|
|
|
|
static void
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseRelabelType(RelabelType *node, deparse_expr_cxt *context)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseExpr(node->arg, context);
|
2013-02-22 13:30:21 +01:00
|
|
|
if (node->relabelformat != COERCE_IMPLICIT_CAST)
|
2013-03-22 00:43:59 +01:00
|
|
|
appendStringInfo(context->buf, "::%s",
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
deparse_type_name(node->resulttype,
|
|
|
|
node->resulttypmod));
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Deparse a BoolExpr node.
|
|
|
|
*/
|
|
|
|
static void
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseBoolExpr(BoolExpr *node, deparse_expr_cxt *context)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
2013-03-22 00:43:59 +01:00
|
|
|
StringInfo buf = context->buf;
|
2013-02-21 11:26:23 +01:00
|
|
|
const char *op = NULL; /* keep compiler quiet */
|
|
|
|
bool first;
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
switch (node->boolop)
|
|
|
|
{
|
|
|
|
case AND_EXPR:
|
|
|
|
op = "AND";
|
|
|
|
break;
|
|
|
|
case OR_EXPR:
|
|
|
|
op = "OR";
|
|
|
|
break;
|
|
|
|
case NOT_EXPR:
|
2013-03-10 19:14:53 +01:00
|
|
|
appendStringInfoString(buf, "(NOT ");
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseExpr(linitial(node->args), context);
|
2013-02-21 11:26:23 +01:00
|
|
|
appendStringInfoChar(buf, ')');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
appendStringInfoChar(buf, '(');
|
|
|
|
first = true;
|
|
|
|
foreach(lc, node->args)
|
|
|
|
{
|
|
|
|
if (!first)
|
|
|
|
appendStringInfo(buf, " %s ", op);
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseExpr((Expr *) lfirst(lc), context);
|
2013-02-21 11:26:23 +01:00
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
appendStringInfoChar(buf, ')');
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Deparse IS [NOT] NULL expression.
|
|
|
|
*/
|
|
|
|
static void
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseNullTest(NullTest *node, deparse_expr_cxt *context)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
2013-03-22 00:43:59 +01:00
|
|
|
StringInfo buf = context->buf;
|
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
appendStringInfoChar(buf, '(');
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseExpr(node->arg, context);
|
Fix assorted fallout from IS [NOT] NULL patch.
Commits 4452000f3 et al established semantics for NullTest.argisrow that
are a bit different from its initial conception: rather than being merely
a cache of whether we've determined the input to have composite type,
the flag now has the further meaning that we should apply field-by-field
testing as per the standard's definition of IS [NOT] NULL. If argisrow
is false and yet the input has composite type, the construct instead has
the semantics of IS [NOT] DISTINCT FROM NULL. Update the comments in
primnodes.h to clarify this, and fix ruleutils.c and deparse.c to print
such cases correctly. In the case of ruleutils.c, this merely results in
cosmetic changes in EXPLAIN output, since the case can't currently arise
in stored rules. However, it represents a live bug for deparse.c, which
would formerly have sent a remote query that had semantics different
from the local behavior. (From the user's standpoint, this means that
testing a remote nested-composite column for null-ness could have had
unexpected recursive behavior much like that fixed in 4452000f3.)
In a related but somewhat independent fix, make plancat.c set argisrow
to false in all NullTest expressions constructed to represent "attnotnull"
constructs. Since attnotnull is actually enforced as a simple null-value
check, this is a more accurate representation of the semantics; we were
previously overpromising what it meant for composite columns, which might
possibly lead to incorrect planner optimizations. (It seems that what the
SQL spec expects a NOT NULL constraint to mean is an IS NOT NULL test, so
arguably we are violating the spec and should fix attnotnull to do the
other thing. If we ever do, this part should get reverted.)
Back-patch, same as the previous commit.
Discussion: <10682.1469566308@sss.pgh.pa.us>
2016-07-28 22:09:15 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* For scalar inputs, we prefer to print as IS [NOT] NULL, which is
|
|
|
|
* shorter and traditional. If it's a rowtype input but we're applying a
|
|
|
|
* scalar test, must print IS [NOT] DISTINCT FROM NULL to be semantically
|
|
|
|
* correct.
|
|
|
|
*/
|
|
|
|
if (node->argisrow || !type_is_rowtype(exprType((Node *) node->arg)))
|
|
|
|
{
|
|
|
|
if (node->nulltesttype == IS_NULL)
|
|
|
|
appendStringInfoString(buf, " IS NULL)");
|
|
|
|
else
|
|
|
|
appendStringInfoString(buf, " IS NOT NULL)");
|
|
|
|
}
|
2013-02-21 11:26:23 +01:00
|
|
|
else
|
Fix assorted fallout from IS [NOT] NULL patch.
Commits 4452000f3 et al established semantics for NullTest.argisrow that
are a bit different from its initial conception: rather than being merely
a cache of whether we've determined the input to have composite type,
the flag now has the further meaning that we should apply field-by-field
testing as per the standard's definition of IS [NOT] NULL. If argisrow
is false and yet the input has composite type, the construct instead has
the semantics of IS [NOT] DISTINCT FROM NULL. Update the comments in
primnodes.h to clarify this, and fix ruleutils.c and deparse.c to print
such cases correctly. In the case of ruleutils.c, this merely results in
cosmetic changes in EXPLAIN output, since the case can't currently arise
in stored rules. However, it represents a live bug for deparse.c, which
would formerly have sent a remote query that had semantics different
from the local behavior. (From the user's standpoint, this means that
testing a remote nested-composite column for null-ness could have had
unexpected recursive behavior much like that fixed in 4452000f3.)
In a related but somewhat independent fix, make plancat.c set argisrow
to false in all NullTest expressions constructed to represent "attnotnull"
constructs. Since attnotnull is actually enforced as a simple null-value
check, this is a more accurate representation of the semantics; we were
previously overpromising what it meant for composite columns, which might
possibly lead to incorrect planner optimizations. (It seems that what the
SQL spec expects a NOT NULL constraint to mean is an IS NOT NULL test, so
arguably we are violating the spec and should fix attnotnull to do the
other thing. If we ever do, this part should get reverted.)
Back-patch, same as the previous commit.
Discussion: <10682.1469566308@sss.pgh.pa.us>
2016-07-28 22:09:15 +02:00
|
|
|
{
|
|
|
|
if (node->nulltesttype == IS_NULL)
|
|
|
|
appendStringInfoString(buf, " IS NOT DISTINCT FROM NULL)");
|
|
|
|
else
|
|
|
|
appendStringInfoString(buf, " IS DISTINCT FROM NULL)");
|
|
|
|
}
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
|
|
|
|
2021-07-30 19:39:48 +02:00
|
|
|
/*
|
|
|
|
* Deparse CASE expression
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
deparseCaseExpr(CaseExpr *node, deparse_expr_cxt *context)
|
|
|
|
{
|
|
|
|
StringInfo buf = context->buf;
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
appendStringInfoString(buf, "(CASE");
|
|
|
|
|
|
|
|
/* If this is a CASE arg WHEN then emit the arg expression */
|
|
|
|
if (node->arg != NULL)
|
|
|
|
{
|
|
|
|
appendStringInfoChar(buf, ' ');
|
|
|
|
deparseExpr(node->arg, context);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add each condition/result of the CASE clause */
|
|
|
|
foreach(lc, node->args)
|
|
|
|
{
|
|
|
|
CaseWhen *whenclause = (CaseWhen *) lfirst(lc);
|
|
|
|
|
|
|
|
/* WHEN */
|
|
|
|
appendStringInfoString(buf, " WHEN ");
|
|
|
|
if (node->arg == NULL) /* CASE WHEN */
|
|
|
|
deparseExpr(whenclause->expr, context);
|
|
|
|
else /* CASE arg WHEN */
|
|
|
|
{
|
|
|
|
/* Ignore the CaseTestExpr and equality operator. */
|
|
|
|
deparseExpr(lsecond(castNode(OpExpr, whenclause->expr)->args),
|
|
|
|
context);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* THEN */
|
|
|
|
appendStringInfoString(buf, " THEN ");
|
|
|
|
deparseExpr(whenclause->result, context);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add ELSE if present */
|
|
|
|
if (node->defresult != NULL)
|
|
|
|
{
|
|
|
|
appendStringInfoString(buf, " ELSE ");
|
|
|
|
deparseExpr(node->defresult, context);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* append END */
|
|
|
|
appendStringInfoString(buf, " END)");
|
|
|
|
}
|
|
|
|
|
2013-02-21 11:26:23 +01:00
|
|
|
/*
|
|
|
|
* Deparse ARRAY[...] construct.
|
|
|
|
*/
|
|
|
|
static void
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseArrayExpr(ArrayExpr *node, deparse_expr_cxt *context)
|
2013-02-21 11:26:23 +01:00
|
|
|
{
|
2013-03-22 00:43:59 +01:00
|
|
|
StringInfo buf = context->buf;
|
2013-02-21 11:26:23 +01:00
|
|
|
bool first = true;
|
|
|
|
ListCell *lc;
|
|
|
|
|
2013-03-10 19:14:53 +01:00
|
|
|
appendStringInfoString(buf, "ARRAY[");
|
2013-02-21 11:26:23 +01:00
|
|
|
foreach(lc, node->elements)
|
|
|
|
{
|
|
|
|
if (!first)
|
2013-03-10 19:14:53 +01:00
|
|
|
appendStringInfoString(buf, ", ");
|
2013-03-22 00:43:59 +01:00
|
|
|
deparseExpr(lfirst(lc), context);
|
2013-02-21 11:26:23 +01:00
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
appendStringInfoChar(buf, ']');
|
|
|
|
|
|
|
|
/* If the array is empty, we need an explicit cast to the array type. */
|
|
|
|
if (node->elements == NIL)
|
|
|
|
appendStringInfo(buf, "::%s",
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
deparse_type_name(node->array_typeid, -1));
|
2013-02-21 11:26:23 +01:00
|
|
|
}
|
2014-04-16 23:21:57 +02:00
|
|
|
|
2016-10-21 15:54:29 +02:00
|
|
|
/*
|
|
|
|
* Deparse an Aggref node.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
deparseAggref(Aggref *node, deparse_expr_cxt *context)
|
|
|
|
{
|
|
|
|
StringInfo buf = context->buf;
|
|
|
|
bool use_variadic;
|
|
|
|
|
|
|
|
/* Only basic, non-split aggregation accepted. */
|
|
|
|
Assert(node->aggsplit == AGGSPLIT_SIMPLE);
|
|
|
|
|
|
|
|
/* Check if need to print VARIADIC (cf. ruleutils.c) */
|
|
|
|
use_variadic = node->aggvariadic;
|
|
|
|
|
|
|
|
/* Find aggregate name from aggfnoid which is a pg_proc entry */
|
|
|
|
appendFunctionName(node->aggfnoid, context);
|
|
|
|
appendStringInfoChar(buf, '(');
|
|
|
|
|
|
|
|
/* Add DISTINCT */
|
2017-08-16 05:34:39 +02:00
|
|
|
appendStringInfoString(buf, (node->aggdistinct != NIL) ? "DISTINCT " : "");
|
2016-10-21 15:54:29 +02:00
|
|
|
|
|
|
|
if (AGGKIND_IS_ORDERED_SET(node->aggkind))
|
|
|
|
{
|
|
|
|
/* Add WITHIN GROUP (ORDER BY ..) */
|
|
|
|
ListCell *arg;
|
|
|
|
bool first = true;
|
|
|
|
|
|
|
|
Assert(!node->aggvariadic);
|
|
|
|
Assert(node->aggorder != NIL);
|
|
|
|
|
|
|
|
foreach(arg, node->aggdirectargs)
|
|
|
|
{
|
|
|
|
if (!first)
|
|
|
|
appendStringInfoString(buf, ", ");
|
|
|
|
first = false;
|
|
|
|
|
|
|
|
deparseExpr((Expr *) lfirst(arg), context);
|
|
|
|
}
|
|
|
|
|
|
|
|
appendStringInfoString(buf, ") WITHIN GROUP (ORDER BY ");
|
|
|
|
appendAggOrderBy(node->aggorder, node->args, context);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* aggstar can be set only in zero-argument aggregates */
|
|
|
|
if (node->aggstar)
|
|
|
|
appendStringInfoChar(buf, '*');
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ListCell *arg;
|
|
|
|
bool first = true;
|
|
|
|
|
|
|
|
/* Add all the arguments */
|
|
|
|
foreach(arg, node->args)
|
|
|
|
{
|
|
|
|
TargetEntry *tle = (TargetEntry *) lfirst(arg);
|
|
|
|
Node *n = (Node *) tle->expr;
|
|
|
|
|
|
|
|
if (tle->resjunk)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!first)
|
|
|
|
appendStringInfoString(buf, ", ");
|
|
|
|
first = false;
|
|
|
|
|
|
|
|
/* Add VARIADIC */
|
Represent Lists as expansible arrays, not chains of cons-cells.
Originally, Postgres Lists were a more or less exact reimplementation of
Lisp lists, which consist of chains of separately-allocated cons cells,
each having a value and a next-cell link. We'd hacked that once before
(commit d0b4399d8) to add a separate List header, but the data was still
in cons cells. That makes some operations -- notably list_nth() -- O(N),
and it's bulky because of the next-cell pointers and per-cell palloc
overhead, and it's very cache-unfriendly if the cons cells end up
scattered around rather than being adjacent.
In this rewrite, we still have List headers, but the data is in a
resizable array of values, with no next-cell links. Now we need at
most two palloc's per List, and often only one, since we can allocate
some values in the same palloc call as the List header. (Of course,
extending an existing List may require repalloc's to enlarge the array.
But this involves just O(log N) allocations not O(N).)
Of course this is not without downsides. The key difficulty is that
addition or deletion of a list entry may now cause other entries to
move, which it did not before.
For example, that breaks foreach() and sister macros, which historically
used a pointer to the current cons-cell as loop state. We can repair
those macros transparently by making their actual loop state be an
integer list index; the exposed "ListCell *" pointer is no longer state
carried across loop iterations, but is just a derived value. (In
practice, modern compilers can optimize things back to having just one
loop state value, at least for simple cases with inline loop bodies.)
In principle, this is a semantics change for cases where the loop body
inserts or deletes list entries ahead of the current loop index; but
I found no such cases in the Postgres code.
The change is not at all transparent for code that doesn't use foreach()
but chases lists "by hand" using lnext(). The largest share of such
code in the backend is in loops that were maintaining "prev" and "next"
variables in addition to the current-cell pointer, in order to delete
list cells efficiently using list_delete_cell(). However, we no longer
need a previous-cell pointer to delete a list cell efficiently. Keeping
a next-cell pointer doesn't work, as explained above, but we can improve
matters by changing such code to use a regular foreach() loop and then
using the new macro foreach_delete_current() to delete the current cell.
(This macro knows how to update the associated foreach loop's state so
that no cells will be missed in the traversal.)
There remains a nontrivial risk of code assuming that a ListCell *
pointer will remain good over an operation that could now move the list
contents. To help catch such errors, list.c can be compiled with a new
define symbol DEBUG_LIST_MEMORY_USAGE that forcibly moves list contents
whenever that could possibly happen. This makes list operations
significantly more expensive so it's not normally turned on (though it
is on by default if USE_VALGRIND is on).
There are two notable API differences from the previous code:
* lnext() now requires the List's header pointer in addition to the
current cell's address.
* list_delete_cell() no longer requires a previous-cell argument.
These changes are somewhat unfortunate, but on the other hand code using
either function needs inspection to see if it is assuming anything
it shouldn't, so it's not all bad.
Programmers should be aware of these significant performance changes:
* list_nth() and related functions are now O(1); so there's no
major access-speed difference between a list and an array.
* Inserting or deleting a list element now takes time proportional to
the distance to the end of the list, due to moving the array elements.
(However, it typically *doesn't* require palloc or pfree, so except in
long lists it's probably still faster than before.) Notably, lcons()
used to be about the same cost as lappend(), but that's no longer true
if the list is long. Code that uses lcons() and list_delete_first()
to maintain a stack might usefully be rewritten to push and pop at the
end of the list rather than the beginning.
* There are now list_insert_nth...() and list_delete_nth...() functions
that add or remove a list cell identified by index. These have the
data-movement penalty explained above, but there's no search penalty.
* list_concat() and variants now copy the second list's data into
storage belonging to the first list, so there is no longer any
sharing of cells between the input lists. The second argument is
now declared "const List *" to reflect that it isn't changed.
This patch just does the minimum needed to get the new implementation
in place and fix bugs exposed by the regression tests. As suggested
by the foregoing, there's a fair amount of followup work remaining to
do.
Also, the ENABLE_LIST_COMPAT macros are finally removed in this
commit. Code using those should have been gone a dozen years ago.
Patch by me; thanks to David Rowley, Jesper Pedersen, and others
for review.
Discussion: https://postgr.es/m/11587.1550975080@sss.pgh.pa.us
2019-07-15 19:41:58 +02:00
|
|
|
if (use_variadic && lnext(node->args, arg) == NULL)
|
2016-10-21 15:54:29 +02:00
|
|
|
appendStringInfoString(buf, "VARIADIC ");
|
|
|
|
|
|
|
|
deparseExpr((Expr *) n, context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add ORDER BY */
|
|
|
|
if (node->aggorder != NIL)
|
|
|
|
{
|
|
|
|
appendStringInfoString(buf, " ORDER BY ");
|
|
|
|
appendAggOrderBy(node->aggorder, node->args, context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add FILTER (WHERE ..) */
|
|
|
|
if (node->aggfilter != NULL)
|
|
|
|
{
|
|
|
|
appendStringInfoString(buf, ") FILTER (WHERE ");
|
|
|
|
deparseExpr((Expr *) node->aggfilter, context);
|
|
|
|
}
|
|
|
|
|
|
|
|
appendStringInfoChar(buf, ')');
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Append ORDER BY within aggregate function.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
appendAggOrderBy(List *orderList, List *targetList, deparse_expr_cxt *context)
|
|
|
|
{
|
|
|
|
StringInfo buf = context->buf;
|
|
|
|
ListCell *lc;
|
|
|
|
bool first = true;
|
|
|
|
|
|
|
|
foreach(lc, orderList)
|
|
|
|
{
|
|
|
|
SortGroupClause *srt = (SortGroupClause *) lfirst(lc);
|
|
|
|
Node *sortexpr;
|
|
|
|
Oid sortcoltype;
|
|
|
|
TypeCacheEntry *typentry;
|
|
|
|
|
|
|
|
if (!first)
|
|
|
|
appendStringInfoString(buf, ", ");
|
|
|
|
first = false;
|
|
|
|
|
|
|
|
sortexpr = deparseSortGroupClause(srt->tleSortGroupRef, targetList,
|
2018-01-12 22:52:49 +01:00
|
|
|
false, context);
|
2016-10-21 15:54:29 +02:00
|
|
|
sortcoltype = exprType(sortexpr);
|
|
|
|
/* See whether operator is default < or > for datatype */
|
|
|
|
typentry = lookup_type_cache(sortcoltype,
|
|
|
|
TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
|
|
|
|
if (srt->sortop == typentry->lt_opr)
|
|
|
|
appendStringInfoString(buf, " ASC");
|
|
|
|
else if (srt->sortop == typentry->gt_opr)
|
|
|
|
appendStringInfoString(buf, " DESC");
|
|
|
|
else
|
|
|
|
{
|
|
|
|
HeapTuple opertup;
|
|
|
|
Form_pg_operator operform;
|
|
|
|
|
|
|
|
appendStringInfoString(buf, " USING ");
|
|
|
|
|
|
|
|
/* Append operator name. */
|
|
|
|
opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(srt->sortop));
|
|
|
|
if (!HeapTupleIsValid(opertup))
|
|
|
|
elog(ERROR, "cache lookup failed for operator %u", srt->sortop);
|
|
|
|
operform = (Form_pg_operator) GETSTRUCT(opertup);
|
|
|
|
deparseOperatorName(buf, operform);
|
|
|
|
ReleaseSysCache(opertup);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (srt->nulls_first)
|
|
|
|
appendStringInfoString(buf, " NULLS FIRST");
|
|
|
|
else
|
|
|
|
appendStringInfoString(buf, " NULLS LAST");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-16 23:21:57 +02:00
|
|
|
/*
|
|
|
|
* Print the representation of a parameter to be sent to the remote side.
|
|
|
|
*
|
|
|
|
* Note: we always label the Param's type explicitly rather than relying on
|
|
|
|
* transmitting a numeric type OID in PQexecParams(). This allows us to
|
|
|
|
* avoid assuming that types have the same OIDs on the remote side as they
|
|
|
|
* do locally --- they need only have the same names.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
printRemoteParam(int paramindex, Oid paramtype, int32 paramtypmod,
|
|
|
|
deparse_expr_cxt *context)
|
|
|
|
{
|
|
|
|
StringInfo buf = context->buf;
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
char *ptypename = deparse_type_name(paramtype, paramtypmod);
|
2014-04-16 23:21:57 +02:00
|
|
|
|
|
|
|
appendStringInfo(buf, "$%d::%s", paramindex, ptypename);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Print the representation of a placeholder for a parameter that will be
|
|
|
|
* sent to the remote side at execution time.
|
|
|
|
*
|
|
|
|
* This is used when we're just trying to EXPLAIN the remote query.
|
|
|
|
* We don't have the actual value of the runtime parameter yet, and we don't
|
|
|
|
* want the remote planner to generate a plan that depends on such a value
|
2014-05-06 18:12:18 +02:00
|
|
|
* anyway. Thus, we can't do something simple like "$1::paramtype".
|
2014-04-16 23:21:57 +02:00
|
|
|
* Instead, we emit "((SELECT null::paramtype)::paramtype)".
|
|
|
|
* In all extant versions of Postgres, the planner will see that as an unknown
|
|
|
|
* constant value, which is what we want. This might need adjustment if we
|
|
|
|
* ever make the planner flatten scalar subqueries. Note: the reason for the
|
|
|
|
* apparently useless outer cast is to ensure that the representation as a
|
|
|
|
* whole will be parsed as an a_expr and not a select_with_parens; the latter
|
|
|
|
* would do the wrong thing in the context "x = ANY(...)".
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
printRemotePlaceholder(Oid paramtype, int32 paramtypmod,
|
|
|
|
deparse_expr_cxt *context)
|
|
|
|
{
|
|
|
|
StringInfo buf = context->buf;
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
char *ptypename = deparse_type_name(paramtype, paramtypmod);
|
2014-04-16 23:21:57 +02:00
|
|
|
|
|
|
|
appendStringInfo(buf, "((SELECT null::%s)::%s)", ptypename, ptypename);
|
|
|
|
}
|
2015-11-03 18:46:06 +01:00
|
|
|
|
2016-10-21 15:54:29 +02:00
|
|
|
/*
|
|
|
|
* Deparse GROUP BY clause.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
appendGroupByClause(List *tlist, deparse_expr_cxt *context)
|
|
|
|
{
|
|
|
|
StringInfo buf = context->buf;
|
|
|
|
Query *query = context->root->parse;
|
|
|
|
ListCell *lc;
|
|
|
|
bool first = true;
|
|
|
|
|
|
|
|
/* Nothing to be done, if there's no GROUP BY clause in the query. */
|
|
|
|
if (!query->groupClause)
|
|
|
|
return;
|
|
|
|
|
2017-08-16 05:34:39 +02:00
|
|
|
appendStringInfoString(buf, " GROUP BY ");
|
2016-10-21 15:54:29 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Queries with grouping sets are not pushed down, so we don't expect
|
|
|
|
* grouping sets here.
|
|
|
|
*/
|
|
|
|
Assert(!query->groupingSets);
|
|
|
|
|
|
|
|
foreach(lc, query->groupClause)
|
|
|
|
{
|
|
|
|
SortGroupClause *grp = (SortGroupClause *) lfirst(lc);
|
|
|
|
|
|
|
|
if (!first)
|
|
|
|
appendStringInfoString(buf, ", ");
|
|
|
|
first = false;
|
|
|
|
|
2018-01-12 22:52:49 +01:00
|
|
|
deparseSortGroupClause(grp->tleSortGroupRef, tlist, true, context);
|
2016-10-21 15:54:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-03 18:46:06 +01:00
|
|
|
/*
|
|
|
|
* Deparse ORDER BY clause according to the given pathkeys for given base
|
|
|
|
* relation. From given pathkeys expressions belonging entirely to the given
|
|
|
|
* base relation are obtained and deparsed.
|
|
|
|
*/
|
2016-01-30 16:32:38 +01:00
|
|
|
static void
|
2019-04-02 12:20:30 +02:00
|
|
|
appendOrderByClause(List *pathkeys, bool has_final_sort,
|
|
|
|
deparse_expr_cxt *context)
|
2015-11-03 18:46:06 +01:00
|
|
|
{
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
ListCell *lcell;
|
|
|
|
int nestlevel;
|
|
|
|
char *delim = " ";
|
2016-10-21 15:54:29 +02:00
|
|
|
RelOptInfo *baserel = context->scanrel;
|
2016-01-30 16:32:38 +01:00
|
|
|
StringInfo buf = context->buf;
|
2015-11-03 18:46:06 +01:00
|
|
|
|
|
|
|
/* Make sure any constants in the exprs are printed portably */
|
|
|
|
nestlevel = set_transmission_modes();
|
|
|
|
|
2017-08-16 05:34:39 +02:00
|
|
|
appendStringInfoString(buf, " ORDER BY");
|
2015-11-03 18:46:06 +01:00
|
|
|
foreach(lcell, pathkeys)
|
|
|
|
{
|
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server's
options, whereupon we will treat immutable functions and operators of those
extensions as candidates to be sent for remote execution.
Whitelisting an extension in this way basically promises that the extension
exists on the remote server and behaves compatibly with the local instance.
We have no way to prove that formally, so we have to rely on the user to
get it right. But this seems like something that people can usually get
right in practice.
We might in future allow functions and operators to be whitelisted
individually, but extension granularity is a very convenient special case,
so it got done first.
The patch as-committed lacks any regression tests, which is unfortunate,
but introducing dependencies on other extensions for testing purposes
would break "make installcheck" scenarios, which is worse. I have some
ideas about klugy ways around that, but it seems like material for a
separate patch. For the moment, leave the problem open.
Paul Ramsey, hacked up a bit more by me
2015-11-04 00:42:02 +01:00
|
|
|
PathKey *pathkey = lfirst(lcell);
|
|
|
|
Expr *em_expr;
|
2015-11-03 18:46:06 +01:00
|
|
|
|
2019-04-02 12:20:30 +02:00
|
|
|
if (has_final_sort)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* By construction, context->foreignrel is the input relation to
|
|
|
|
* the final sort.
|
|
|
|
*/
|
|
|
|
em_expr = find_em_expr_for_input_target(context->root,
|
|
|
|
pathkey->pk_eclass,
|
|
|
|
context->foreignrel->reltarget);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
em_expr = find_em_expr_for_rel(pathkey->pk_eclass, baserel);
|
|
|
|
|
2015-11-03 18:46:06 +01:00
|
|
|
Assert(em_expr != NULL);
|
|
|
|
|
|
|
|
appendStringInfoString(buf, delim);
|
2016-01-30 16:32:38 +01:00
|
|
|
deparseExpr(em_expr, context);
|
2015-11-03 18:46:06 +01:00
|
|
|
if (pathkey->pk_strategy == BTLessStrategyNumber)
|
|
|
|
appendStringInfoString(buf, " ASC");
|
|
|
|
else
|
|
|
|
appendStringInfoString(buf, " DESC");
|
|
|
|
|
|
|
|
if (pathkey->pk_nulls_first)
|
|
|
|
appendStringInfoString(buf, " NULLS FIRST");
|
2016-03-04 17:35:46 +01:00
|
|
|
else
|
|
|
|
appendStringInfoString(buf, " NULLS LAST");
|
2015-11-03 18:46:06 +01:00
|
|
|
|
|
|
|
delim = ", ";
|
|
|
|
}
|
|
|
|
reset_transmission_modes(nestlevel);
|
|
|
|
}
|
2016-10-21 15:54:29 +02:00
|
|
|
|
2019-04-02 13:30:45 +02:00
|
|
|
/*
|
|
|
|
* Deparse LIMIT/OFFSET clause.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
appendLimitClause(deparse_expr_cxt *context)
|
|
|
|
{
|
|
|
|
PlannerInfo *root = context->root;
|
|
|
|
StringInfo buf = context->buf;
|
|
|
|
int nestlevel;
|
|
|
|
|
|
|
|
/* Make sure any constants in the exprs are printed portably */
|
|
|
|
nestlevel = set_transmission_modes();
|
|
|
|
|
|
|
|
if (root->parse->limitCount)
|
|
|
|
{
|
|
|
|
appendStringInfoString(buf, " LIMIT ");
|
|
|
|
deparseExpr((Expr *) root->parse->limitCount, context);
|
|
|
|
}
|
|
|
|
if (root->parse->limitOffset)
|
|
|
|
{
|
|
|
|
appendStringInfoString(buf, " OFFSET ");
|
|
|
|
deparseExpr((Expr *) root->parse->limitOffset, context);
|
|
|
|
}
|
|
|
|
|
|
|
|
reset_transmission_modes(nestlevel);
|
|
|
|
}
|
|
|
|
|
2016-10-21 15:54:29 +02:00
|
|
|
/*
|
|
|
|
* appendFunctionName
|
|
|
|
* Deparses function name from given function oid.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
appendFunctionName(Oid funcid, deparse_expr_cxt *context)
|
|
|
|
{
|
|
|
|
StringInfo buf = context->buf;
|
|
|
|
HeapTuple proctup;
|
|
|
|
Form_pg_proc procform;
|
|
|
|
const char *proname;
|
|
|
|
|
|
|
|
proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
|
|
|
|
if (!HeapTupleIsValid(proctup))
|
|
|
|
elog(ERROR, "cache lookup failed for function %u", funcid);
|
|
|
|
procform = (Form_pg_proc) GETSTRUCT(proctup);
|
|
|
|
|
|
|
|
/* Print schema name only if it's not pg_catalog */
|
|
|
|
if (procform->pronamespace != PG_CATALOG_NAMESPACE)
|
|
|
|
{
|
|
|
|
const char *schemaname;
|
|
|
|
|
|
|
|
schemaname = get_namespace_name(procform->pronamespace);
|
|
|
|
appendStringInfo(buf, "%s.", quote_identifier(schemaname));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Always print the function name */
|
|
|
|
proname = NameStr(procform->proname);
|
2017-08-16 05:34:39 +02:00
|
|
|
appendStringInfoString(buf, quote_identifier(proname));
|
2016-10-21 15:54:29 +02:00
|
|
|
|
|
|
|
ReleaseSysCache(proctup);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Appends a sort or group clause.
|
|
|
|
*
|
|
|
|
* Like get_rule_sortgroupclause(), returns the expression tree, so caller
|
|
|
|
* need not find it again.
|
|
|
|
*/
|
|
|
|
static Node *
|
2018-01-12 22:52:49 +01:00
|
|
|
deparseSortGroupClause(Index ref, List *tlist, bool force_colno,
|
|
|
|
deparse_expr_cxt *context)
|
2016-10-21 15:54:29 +02:00
|
|
|
{
|
|
|
|
StringInfo buf = context->buf;
|
|
|
|
TargetEntry *tle;
|
|
|
|
Expr *expr;
|
|
|
|
|
|
|
|
tle = get_sortgroupref_tle(ref, tlist);
|
|
|
|
expr = tle->expr;
|
|
|
|
|
2018-01-12 22:52:49 +01:00
|
|
|
if (force_colno)
|
|
|
|
{
|
|
|
|
/* Use column-number form when requested by caller. */
|
|
|
|
Assert(!tle->resjunk);
|
|
|
|
appendStringInfo(buf, "%d", tle->resno);
|
|
|
|
}
|
|
|
|
else if (expr && IsA(expr, Const))
|
2016-10-21 15:54:29 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Force a typecast here so that we don't emit something like "GROUP
|
|
|
|
* BY 2", which will be misconstrued as a column position rather than
|
|
|
|
* a constant.
|
|
|
|
*/
|
|
|
|
deparseConst((Const *) expr, context, 1);
|
|
|
|
}
|
|
|
|
else if (!expr || IsA(expr, Var))
|
|
|
|
deparseExpr(expr, context);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Always parenthesize the expression. */
|
2017-08-16 05:34:39 +02:00
|
|
|
appendStringInfoChar(buf, '(');
|
2016-10-21 15:54:29 +02:00
|
|
|
deparseExpr(expr, context);
|
2017-08-16 05:34:39 +02:00
|
|
|
appendStringInfoChar(buf, ')');
|
2016-10-21 15:54:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return (Node *) expr;
|
|
|
|
}
|
2017-03-16 18:34:59 +01:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns true if given Var is deparsed as a subquery output column, in
|
|
|
|
* which case, *relno and *colno are set to the IDs for the relation and
|
|
|
|
* column alias to the Var provided by the subquery.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
is_subquery_var(Var *node, RelOptInfo *foreignrel, int *relno, int *colno)
|
|
|
|
{
|
|
|
|
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
|
|
|
|
RelOptInfo *outerrel = fpinfo->outerrel;
|
|
|
|
RelOptInfo *innerrel = fpinfo->innerrel;
|
|
|
|
|
|
|
|
/* Should only be called in these cases. */
|
Abstract logic to allow for multiple kinds of child rels.
Currently, the only type of child relation is an "other member rel",
which is the child of a baserel, but in the future joins and even
upper relations may have child rels. To facilitate that, introduce
macros that test to test for particular RelOptKind values, and use
them in various places where they help to clarify the sense of a test.
(For example, a test may allow RELOPT_OTHER_MEMBER_REL either because
it intends to allow child rels, or because it intends to allow simple
rels.)
Also, remove find_childrel_top_parent, which will not work for a
child rel that is not a baserel. Instead, add a new RelOptInfo
member top_parent_relids to track the same kind of information in a
more generic manner.
Ashutosh Bapat, slightly tweaked by me. Review and testing of the
patch set from which this was taken by Rajkumar Raghuwanshi and Rafia
Sabih.
Discussion: http://postgr.es/m/CA+TgmoagTnF2yqR3PT2rv=om=wJiZ4-A+ATwdnriTGku1CLYxA@mail.gmail.com
2017-04-04 04:41:31 +02:00
|
|
|
Assert(IS_SIMPLE_REL(foreignrel) || IS_JOIN_REL(foreignrel));
|
2017-03-16 18:34:59 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the given relation isn't a join relation, it doesn't have any lower
|
|
|
|
* subqueries, so the Var isn't a subquery output column.
|
|
|
|
*/
|
Abstract logic to allow for multiple kinds of child rels.
Currently, the only type of child relation is an "other member rel",
which is the child of a baserel, but in the future joins and even
upper relations may have child rels. To facilitate that, introduce
macros that test to test for particular RelOptKind values, and use
them in various places where they help to clarify the sense of a test.
(For example, a test may allow RELOPT_OTHER_MEMBER_REL either because
it intends to allow child rels, or because it intends to allow simple
rels.)
Also, remove find_childrel_top_parent, which will not work for a
child rel that is not a baserel. Instead, add a new RelOptInfo
member top_parent_relids to track the same kind of information in a
more generic manner.
Ashutosh Bapat, slightly tweaked by me. Review and testing of the
patch set from which this was taken by Rajkumar Raghuwanshi and Rafia
Sabih.
Discussion: http://postgr.es/m/CA+TgmoagTnF2yqR3PT2rv=om=wJiZ4-A+ATwdnriTGku1CLYxA@mail.gmail.com
2017-04-04 04:41:31 +02:00
|
|
|
if (!IS_JOIN_REL(foreignrel))
|
2017-03-16 18:34:59 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the Var doesn't belong to any lower subqueries, it isn't a subquery
|
|
|
|
* output column.
|
|
|
|
*/
|
|
|
|
if (!bms_is_member(node->varno, fpinfo->lower_subquery_rels))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (bms_is_member(node->varno, outerrel->relids))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If outer relation is deparsed as a subquery, the Var is an output
|
|
|
|
* column of the subquery; get the IDs for the relation/column alias.
|
|
|
|
*/
|
|
|
|
if (fpinfo->make_outerrel_subquery)
|
|
|
|
{
|
|
|
|
get_relation_column_alias_ids(node, outerrel, relno, colno);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Otherwise, recurse into the outer relation. */
|
|
|
|
return is_subquery_var(node, outerrel, relno, colno);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Assert(bms_is_member(node->varno, innerrel->relids));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If inner relation is deparsed as a subquery, the Var is an output
|
|
|
|
* column of the subquery; get the IDs for the relation/column alias.
|
|
|
|
*/
|
|
|
|
if (fpinfo->make_innerrel_subquery)
|
|
|
|
{
|
|
|
|
get_relation_column_alias_ids(node, innerrel, relno, colno);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Otherwise, recurse into the inner relation. */
|
|
|
|
return is_subquery_var(node, innerrel, relno, colno);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the IDs for the relation and column alias to given Var belonging to
|
|
|
|
* given relation, which are returned into *relno and *colno.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
|
|
|
|
int *relno, int *colno)
|
|
|
|
{
|
|
|
|
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
|
|
|
|
int i;
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
/* Get the relation alias ID */
|
|
|
|
*relno = fpinfo->relation_index;
|
|
|
|
|
|
|
|
/* Get the column alias ID */
|
|
|
|
i = 1;
|
|
|
|
foreach(lc, foreignrel->reltarget->exprs)
|
|
|
|
{
|
|
|
|
if (equal(lfirst(lc), (Node *) node))
|
|
|
|
{
|
|
|
|
*colno = i;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Shouldn't get here */
|
|
|
|
elog(ERROR, "unexpected expression in subquery output");
|
|
|
|
}
|