PostgreSQL Source Code  git master
deparse.c File Reference
#include "postgres.h"
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "access/table.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_ts_config.h"
#include "catalog/pg_ts_dict.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "commands/tablecmds.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/plannodes.h"
#include "optimizer/optimizer.h"
#include "optimizer/prep.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
#include "postgres_fdw.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
Include dependency graph for deparse.c:

Go to the source code of this file.

Data Structures

struct  foreign_glob_cxt
 
struct  foreign_loc_cxt
 
struct  deparse_expr_cxt
 

Macros

#define REL_ALIAS_PREFIX   "r"
 
#define ADD_REL_QUALIFIER(buf, varno)    appendStringInfo((buf), "%s%d.", REL_ALIAS_PREFIX, (varno))
 
#define SUBQUERY_REL_ALIAS_PREFIX   "s"
 
#define SUBQUERY_COL_ALIAS_PREFIX   "c"
 

Typedefs

typedef struct foreign_glob_cxt foreign_glob_cxt
 
typedef struct foreign_loc_cxt foreign_loc_cxt
 
typedef struct deparse_expr_cxt deparse_expr_cxt
 

Enumerations

enum  FDWCollateState { FDW_COLLATE_NONE , FDW_COLLATE_SAFE , FDW_COLLATE_UNSAFE }
 

Functions

static bool foreign_expr_walker (Node *node, foreign_glob_cxt *glob_cxt, foreign_loc_cxt *outer_cxt, foreign_loc_cxt *case_arg_cxt)
 
static char * deparse_type_name (Oid type_oid, int32 typemod)
 
static void deparseTargetList (StringInfo buf, RangeTblEntry *rte, Index rtindex, Relation rel, bool is_returning, Bitmapset *attrs_used, bool qualify_col, List **retrieved_attrs)
 
static void deparseExplicitTargetList (List *tlist, bool is_returning, List **retrieved_attrs, deparse_expr_cxt *context)
 
static void deparseSubqueryTargetList (deparse_expr_cxt *context)
 
static void deparseReturningList (StringInfo buf, RangeTblEntry *rte, Index rtindex, Relation rel, bool trig_after_row, List *withCheckOptionList, List *returningList, List **retrieved_attrs)
 
static void deparseColumnRef (StringInfo buf, int varno, int varattno, RangeTblEntry *rte, bool qualify_col)
 
static void deparseRelation (StringInfo buf, Relation rel)
 
static void deparseExpr (Expr *node, deparse_expr_cxt *context)
 
static void deparseVar (Var *node, deparse_expr_cxt *context)
 
static void deparseConst (Const *node, deparse_expr_cxt *context, int showtype)
 
static void deparseParam (Param *node, deparse_expr_cxt *context)
 
static void deparseSubscriptingRef (SubscriptingRef *node, deparse_expr_cxt *context)
 
static void deparseFuncExpr (FuncExpr *node, deparse_expr_cxt *context)
 
static void deparseOpExpr (OpExpr *node, deparse_expr_cxt *context)
 
static bool isPlainForeignVar (Expr *node, deparse_expr_cxt *context)
 
static void deparseOperatorName (StringInfo buf, Form_pg_operator opform)
 
static void deparseDistinctExpr (DistinctExpr *node, deparse_expr_cxt *context)
 
static void deparseScalarArrayOpExpr (ScalarArrayOpExpr *node, deparse_expr_cxt *context)
 
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)
 
static void deparseCaseExpr (CaseExpr *node, deparse_expr_cxt *context)
 
static void deparseArrayExpr (ArrayExpr *node, deparse_expr_cxt *context)
 
static void printRemoteParam (int paramindex, Oid paramtype, int32 paramtypmod, deparse_expr_cxt *context)
 
static void printRemotePlaceholder (Oid paramtype, int32 paramtypmod, deparse_expr_cxt *context)
 
static void deparseSelectSql (List *tlist, bool is_subquery, List **retrieved_attrs, deparse_expr_cxt *context)
 
static void deparseLockingClause (deparse_expr_cxt *context)
 
static void appendOrderByClause (List *pathkeys, bool has_final_sort, deparse_expr_cxt *context)
 
static void appendLimitClause (deparse_expr_cxt *context)
 
static void appendConditions (List *exprs, deparse_expr_cxt *context)
 
static void deparseFromExprForRel (StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel, bool use_alias, Index ignore_rel, List **ignore_conds, List **additional_conds, List **params_list)
 
static void appendWhereClause (List *exprs, List *additional_conds, deparse_expr_cxt *context)
 
static void deparseFromExpr (List *quals, deparse_expr_cxt *context)
 
static void deparseRangeTblRef (StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel, bool make_subquery, Index ignore_rel, List **ignore_conds, List **additional_conds, List **params_list)
 
static void deparseAggref (Aggref *node, deparse_expr_cxt *context)
 
static void appendGroupByClause (List *tlist, deparse_expr_cxt *context)
 
static void appendOrderBySuffix (Oid sortop, Oid sortcoltype, bool nulls_first, deparse_expr_cxt *context)
 
static void appendAggOrderBy (List *orderList, List *targetList, deparse_expr_cxt *context)
 
static void appendFunctionName (Oid funcid, deparse_expr_cxt *context)
 
static NodedeparseSortGroupClause (Index ref, List *tlist, bool force_colno, deparse_expr_cxt *context)
 
static bool is_subquery_var (Var *node, RelOptInfo *foreignrel, int *relno, int *colno)
 
static void get_relation_column_alias_ids (Var *node, RelOptInfo *foreignrel, int *relno, int *colno)
 
void classifyConditions (PlannerInfo *root, RelOptInfo *baserel, List *input_conds, List **remote_conds, List **local_conds)
 
bool is_foreign_expr (PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
 
bool is_foreign_param (PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
 
bool is_foreign_pathkey (PlannerInfo *root, RelOptInfo *baserel, PathKey *pathkey)
 
Listbuild_tlist_to_deparse (RelOptInfo *foreignrel)
 
void deparseSelectStmtForRel (StringInfo buf, PlannerInfo *root, RelOptInfo *rel, List *tlist, List *remote_conds, List *pathkeys, bool has_final_sort, bool has_limit, bool is_subquery, List **retrieved_attrs, List **params_list)
 
const char * get_jointype_name (JoinType jointype)
 
void deparseInsertSql (StringInfo buf, RangeTblEntry *rte, Index rtindex, Relation rel, List *targetAttrs, bool doNothing, List *withCheckOptionList, List *returningList, List **retrieved_attrs, int *values_end_len)
 
void rebuildInsertSql (StringInfo buf, Relation rel, char *orig_query, List *target_attrs, int values_end_len, int num_params, int num_rows)
 
void deparseUpdateSql (StringInfo buf, RangeTblEntry *rte, Index rtindex, Relation rel, List *targetAttrs, List *withCheckOptionList, List *returningList, List **retrieved_attrs)
 
void deparseDirectUpdateSql (StringInfo buf, PlannerInfo *root, Index rtindex, Relation rel, RelOptInfo *foreignrel, List *targetlist, List *targetAttrs, List *remote_conds, List **params_list, List *returningList, List **retrieved_attrs)
 
void deparseDeleteSql (StringInfo buf, RangeTblEntry *rte, Index rtindex, Relation rel, List *returningList, List **retrieved_attrs)
 
void deparseDirectDeleteSql (StringInfo buf, PlannerInfo *root, Index rtindex, Relation rel, RelOptInfo *foreignrel, List *remote_conds, List **params_list, List *returningList, List **retrieved_attrs)
 
void deparseAnalyzeSizeSql (StringInfo buf, Relation rel)
 
void deparseAnalyzeInfoSql (StringInfo buf, Relation rel)
 
void deparseAnalyzeSql (StringInfo buf, Relation rel, PgFdwSamplingMethod sample_method, double sample_frac, List **retrieved_attrs)
 
void deparseTruncateSql (StringInfo buf, List *rels, DropBehavior behavior, bool restart_seqs)
 
void deparseStringLiteral (StringInfo buf, const char *val)
 

Macro Definition Documentation

◆ ADD_REL_QUALIFIER

#define ADD_REL_QUALIFIER (   buf,
  varno 
)     appendStringInfo((buf), "%s%d.", REL_ALIAS_PREFIX, (varno))

Definition at line 112 of file deparse.c.

◆ REL_ALIAS_PREFIX

#define REL_ALIAS_PREFIX   "r"

Definition at line 110 of file deparse.c.

◆ SUBQUERY_COL_ALIAS_PREFIX

#define SUBQUERY_COL_ALIAS_PREFIX   "c"

Definition at line 115 of file deparse.c.

◆ SUBQUERY_REL_ALIAS_PREFIX

#define SUBQUERY_REL_ALIAS_PREFIX   "s"

Definition at line 114 of file deparse.c.

Typedef Documentation

◆ deparse_expr_cxt

◆ foreign_glob_cxt

◆ foreign_loc_cxt

Enumeration Type Documentation

◆ FDWCollateState

Enumerator
FDW_COLLATE_NONE 
FDW_COLLATE_SAFE 
FDW_COLLATE_UNSAFE 

Definition at line 80 of file deparse.c.

81 {
82  FDW_COLLATE_NONE, /* expression is of a noncollatable type, or
83  * it has default collation that is not
84  * traceable to a foreign Var */
85  FDW_COLLATE_SAFE, /* collation derives from a foreign Var */
86  FDW_COLLATE_UNSAFE, /* collation is non-default and derives from
87  * something other than a foreign Var */
FDWCollateState
Definition: deparse.c:81
@ FDW_COLLATE_SAFE
Definition: deparse.c:85
@ FDW_COLLATE_UNSAFE
Definition: deparse.c:86
@ FDW_COLLATE_NONE
Definition: deparse.c:82

Function Documentation

◆ appendAggOrderBy()

static void appendAggOrderBy ( List orderList,
List targetList,
deparse_expr_cxt context 
)
static

Definition at line 3749 of file deparse.c.

3750 {
3751  StringInfo buf = context->buf;
3752  ListCell *lc;
3753  bool first = true;
3754 
3755  foreach(lc, orderList)
3756  {
3757  SortGroupClause *srt = (SortGroupClause *) lfirst(lc);
3758  Node *sortexpr;
3759 
3760  if (!first)
3761  appendStringInfoString(buf, ", ");
3762  first = false;
3763 
3764  /* Deparse the sort expression proper. */
3765  sortexpr = deparseSortGroupClause(srt->tleSortGroupRef, targetList,
3766  false, context);
3767  /* Add decoration as needed. */
3768  appendOrderBySuffix(srt->sortop, exprType(sortexpr), srt->nulls_first,
3769  context);
3770  }
3771 }
static Node * deparseSortGroupClause(Index ref, List *tlist, bool force_colno, deparse_expr_cxt *context)
Definition: deparse.c:4067
static void appendOrderBySuffix(Oid sortop, Oid sortcoltype, bool nulls_first, deparse_expr_cxt *context)
Definition: deparse.c:3778
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
#define lfirst(lc)
Definition: pg_list.h:172
static char * buf
Definition: pg_test_fsync.c:73
tree context
Definition: radixtree.h:1797
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:182
Definition: nodes.h:129
Index tleSortGroupRef
Definition: parsenodes.h:1425

References appendOrderBySuffix(), appendStringInfoString(), buf, context, deparseSortGroupClause(), exprType(), lfirst, SortGroupClause::nulls_first, SortGroupClause::sortop, and SortGroupClause::tleSortGroupRef.

Referenced by deparseAggref().

◆ appendConditions()

static void appendConditions ( List exprs,
deparse_expr_cxt context 
)
static

Definition at line 1571 of file deparse.c.

1572 {
1573  int nestlevel;
1574  ListCell *lc;
1575  bool is_first = true;
1576  StringInfo buf = context->buf;
1577 
1578  /* Make sure any constants in the exprs are printed portably */
1579  nestlevel = set_transmission_modes();
1580 
1581  foreach(lc, exprs)
1582  {
1583  Expr *expr = (Expr *) lfirst(lc);
1584 
1585  /* Extract clause from RestrictInfo, if required */
1586  if (IsA(expr, RestrictInfo))
1587  expr = ((RestrictInfo *) expr)->clause;
1588 
1589  /* Connect expressions with "AND" and parenthesize each condition. */
1590  if (!is_first)
1591  appendStringInfoString(buf, " AND ");
1592 
1593  appendStringInfoChar(buf, '(');
1594  deparseExpr(expr, context);
1595  appendStringInfoChar(buf, ')');
1596 
1597  is_first = false;
1598  }
1599 
1600  reset_transmission_modes(nestlevel);
1601 }
static void deparseExpr(Expr *node, deparse_expr_cxt *context)
Definition: deparse.c:2884
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
void reset_transmission_modes(int nestlevel)
int set_transmission_modes(void)
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:194

References appendStringInfoChar(), appendStringInfoString(), buf, context, deparseExpr(), IsA, lfirst, reset_transmission_modes(), and set_transmission_modes().

Referenced by appendWhereClause(), deparseFromExprForRel(), and deparseSelectStmtForRel().

◆ appendFunctionName()

static void appendFunctionName ( Oid  funcid,
deparse_expr_cxt context 
)
static

Definition at line 4032 of file deparse.c.

4033 {
4034  StringInfo buf = context->buf;
4035  HeapTuple proctup;
4036  Form_pg_proc procform;
4037  const char *proname;
4038 
4039  proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
4040  if (!HeapTupleIsValid(proctup))
4041  elog(ERROR, "cache lookup failed for function %u", funcid);
4042  procform = (Form_pg_proc) GETSTRUCT(proctup);
4043 
4044  /* Print schema name only if it's not pg_catalog */
4045  if (procform->pronamespace != PG_CATALOG_NAMESPACE)
4046  {
4047  const char *schemaname;
4048 
4049  schemaname = get_namespace_name(procform->pronamespace);
4050  appendStringInfo(buf, "%s.", quote_identifier(schemaname));
4051  }
4052 
4053  /* Always print the function name */
4054  proname = NameStr(procform->proname);
4056 
4057  ReleaseSysCache(proctup);
4058 }
#define NameStr(name)
Definition: c.h:733
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3344
FormData_pg_proc * Form_pg_proc
Definition: pg_proc.h:136
NameData proname
Definition: pg_proc.h:35
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:12353
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:97
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:266
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:218

References appendStringInfo(), appendStringInfoString(), buf, context, elog, ERROR, get_namespace_name(), GETSTRUCT, HeapTupleIsValid, NameStr, ObjectIdGetDatum(), proname, quote_identifier(), ReleaseSysCache(), and SearchSysCache1().

Referenced by deparseAggref(), and deparseFuncExpr().

◆ appendGroupByClause()

static void appendGroupByClause ( List tlist,
deparse_expr_cxt context 
)
static

Definition at line 3862 of file deparse.c.

3863 {
3864  StringInfo buf = context->buf;
3865  Query *query = context->root->parse;
3866  ListCell *lc;
3867  bool first = true;
3868 
3869  /* Nothing to be done, if there's no GROUP BY clause in the query. */
3870  if (!query->groupClause)
3871  return;
3872 
3873  appendStringInfoString(buf, " GROUP BY ");
3874 
3875  /*
3876  * Queries with grouping sets are not pushed down, so we don't expect
3877  * grouping sets here.
3878  */
3879  Assert(!query->groupingSets);
3880 
3881  /*
3882  * We intentionally print query->groupClause not processed_groupClause,
3883  * leaving it to the remote planner to get rid of any redundant GROUP BY
3884  * items again. This is necessary in case processed_groupClause reduced
3885  * to empty, and in any case the redundancy situation on the remote might
3886  * be different than what we think here.
3887  */
3888  foreach(lc, query->groupClause)
3889  {
3890  SortGroupClause *grp = (SortGroupClause *) lfirst(lc);
3891 
3892  if (!first)
3893  appendStringInfoString(buf, ", ");
3894  first = false;
3895 
3896  deparseSortGroupClause(grp->tleSortGroupRef, tlist, true, context);
3897  }
3898 }
Assert(fmt[strlen(fmt) - 1] !='\n')
List * groupClause
Definition: parsenodes.h:199
List * groupingSets
Definition: parsenodes.h:202

References appendStringInfoString(), Assert(), buf, context, deparseSortGroupClause(), Query::groupClause, Query::groupingSets, lfirst, and SortGroupClause::tleSortGroupRef.

Referenced by deparseSelectStmtForRel().

◆ appendLimitClause()

static void appendLimitClause ( deparse_expr_cxt context)
static

Definition at line 4004 of file deparse.c.

4005 {
4006  PlannerInfo *root = context->root;
4007  StringInfo buf = context->buf;
4008  int nestlevel;
4009 
4010  /* Make sure any constants in the exprs are printed portably */
4011  nestlevel = set_transmission_modes();
4012 
4013  if (root->parse->limitCount)
4014  {
4015  appendStringInfoString(buf, " LIMIT ");
4016  deparseExpr((Expr *) root->parse->limitCount, context);
4017  }
4018  if (root->parse->limitOffset)
4019  {
4020  appendStringInfoString(buf, " OFFSET ");
4021  deparseExpr((Expr *) root->parse->limitOffset, context);
4022  }
4023 
4024  reset_transmission_modes(nestlevel);
4025 }
tree ctl root
Definition: radixtree.h:1840

References appendStringInfoString(), buf, context, deparseExpr(), reset_transmission_modes(), root, and set_transmission_modes().

Referenced by deparseSelectStmtForRel().

◆ appendOrderByClause()

static void appendOrderByClause ( List pathkeys,
bool  has_final_sort,
deparse_expr_cxt context 
)
static

Definition at line 3910 of file deparse.c.

3912 {
3913  ListCell *lcell;
3914  int nestlevel;
3915  StringInfo buf = context->buf;
3916  bool gotone = false;
3917 
3918  /* Make sure any constants in the exprs are printed portably */
3919  nestlevel = set_transmission_modes();
3920 
3921  foreach(lcell, pathkeys)
3922  {
3923  PathKey *pathkey = lfirst(lcell);
3924  EquivalenceMember *em;
3925  Expr *em_expr;
3926  Oid oprid;
3927 
3928  if (has_final_sort)
3929  {
3930  /*
3931  * By construction, context->foreignrel is the input relation to
3932  * the final sort.
3933  */
3934  em = find_em_for_rel_target(context->root,
3935  pathkey->pk_eclass,
3936  context->foreignrel);
3937  }
3938  else
3939  em = find_em_for_rel(context->root,
3940  pathkey->pk_eclass,
3941  context->scanrel);
3942 
3943  /*
3944  * We don't expect any error here; it would mean that shippability
3945  * wasn't verified earlier. For the same reason, we don't recheck
3946  * shippability of the sort operator.
3947  */
3948  if (em == NULL)
3949  elog(ERROR, "could not find pathkey item to sort");
3950 
3951  em_expr = em->em_expr;
3952 
3953  /*
3954  * If the member is a Const expression then we needn't add it to the
3955  * ORDER BY clause. This can happen in UNION ALL queries where the
3956  * union child targetlist has a Const. Adding these would be
3957  * wasteful, but also, for INT columns, an integer literal would be
3958  * seen as an ordinal column position rather than a value to sort by.
3959  * deparseConst() does have code to handle this, but it seems less
3960  * effort on all accounts just to skip these for ORDER BY clauses.
3961  */
3962  if (IsA(em_expr, Const))
3963  continue;
3964 
3965  if (!gotone)
3966  {
3967  appendStringInfoString(buf, " ORDER BY ");
3968  gotone = true;
3969  }
3970  else
3971  appendStringInfoString(buf, ", ");
3972 
3973  /*
3974  * Lookup the operator corresponding to the strategy in the opclass.
3975  * The datatype used by the opfamily is not necessarily the same as
3976  * the expression type (for array types for example).
3977  */
3979  em->em_datatype,
3980  em->em_datatype,
3981  pathkey->pk_strategy);
3982  if (!OidIsValid(oprid))
3983  elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
3984  pathkey->pk_strategy, em->em_datatype, em->em_datatype,
3985  pathkey->pk_opfamily);
3986 
3987  deparseExpr(em_expr, context);
3988 
3989  /*
3990  * Here we need to use the expression's actual type to discover
3991  * whether the desired operator will be the default or not.
3992  */
3993  appendOrderBySuffix(oprid, exprType((Node *) em_expr),
3994  pathkey->pk_nulls_first, context);
3995 
3996  }
3997  reset_transmission_modes(nestlevel);
3998 }
#define OidIsValid(objectId)
Definition: c.h:762
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition: lsyscache.c:166
Oid oprid(Operator op)
Definition: parse_oper.c:238
unsigned int Oid
Definition: postgres_ext.h:31
EquivalenceMember * find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
EquivalenceMember * find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
bool pk_nulls_first
Definition: pathnodes.h:1463
int pk_strategy
Definition: pathnodes.h:1462
Oid pk_opfamily
Definition: pathnodes.h:1461

References appendOrderBySuffix(), appendStringInfoString(), buf, context, deparseExpr(), elog, EquivalenceMember::em_datatype, EquivalenceMember::em_expr, ERROR, exprType(), find_em_for_rel(), find_em_for_rel_target(), get_opfamily_member(), IsA, lfirst, OidIsValid, oprid(), PathKey::pk_nulls_first, PathKey::pk_opfamily, PathKey::pk_strategy, reset_transmission_modes(), and set_transmission_modes().

Referenced by deparseSelectStmtForRel().

◆ appendOrderBySuffix()

static void appendOrderBySuffix ( Oid  sortop,
Oid  sortcoltype,
bool  nulls_first,
deparse_expr_cxt context 
)
static

Definition at line 3778 of file deparse.c.

3780 {
3781  StringInfo buf = context->buf;
3782  TypeCacheEntry *typentry;
3783 
3784  /* See whether operator is default < or > for sort expr's datatype. */
3785  typentry = lookup_type_cache(sortcoltype,
3787 
3788  if (sortop == typentry->lt_opr)
3789  appendStringInfoString(buf, " ASC");
3790  else if (sortop == typentry->gt_opr)
3791  appendStringInfoString(buf, " DESC");
3792  else
3793  {
3794  HeapTuple opertup;
3795  Form_pg_operator operform;
3796 
3797  appendStringInfoString(buf, " USING ");
3798 
3799  /* Append operator name. */
3800  opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(sortop));
3801  if (!HeapTupleIsValid(opertup))
3802  elog(ERROR, "cache lookup failed for operator %u", sortop);
3803  operform = (Form_pg_operator) GETSTRUCT(opertup);
3804  deparseOperatorName(buf, operform);
3805  ReleaseSysCache(opertup);
3806  }
3807 
3808  if (nulls_first)
3809  appendStringInfoString(buf, " NULLS FIRST");
3810  else
3811  appendStringInfoString(buf, " NULLS LAST");
3812 }
static void deparseOperatorName(StringInfo buf, Form_pg_operator opform)
Definition: deparse.c:3406
FormData_pg_operator * Form_pg_operator
Definition: pg_operator.h:83
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:346
#define TYPECACHE_GT_OPR
Definition: typcache.h:139
#define TYPECACHE_LT_OPR
Definition: typcache.h:138

References appendStringInfoString(), buf, context, deparseOperatorName(), elog, ERROR, GETSTRUCT, TypeCacheEntry::gt_opr, HeapTupleIsValid, lookup_type_cache(), TypeCacheEntry::lt_opr, ObjectIdGetDatum(), ReleaseSysCache(), SearchSysCache1(), TYPECACHE_GT_OPR, and TYPECACHE_LT_OPR.

Referenced by appendAggOrderBy(), and appendOrderByClause().

◆ appendWhereClause()

static void appendWhereClause ( List exprs,
List additional_conds,
deparse_expr_cxt context 
)
static

Definition at line 1608 of file deparse.c.

1609 {
1610  StringInfo buf = context->buf;
1611  bool need_and = false;
1612  ListCell *lc;
1613 
1614  if (exprs != NIL || additional_conds != NIL)
1615  appendStringInfoString(buf, " WHERE ");
1616 
1617  /*
1618  * If there are some filters, append them.
1619  */
1620  if (exprs != NIL)
1621  {
1622  appendConditions(exprs, context);
1623  need_and = true;
1624  }
1625 
1626  /*
1627  * If there are some EXISTS conditions, coming from SEMI-JOINS, append
1628  * them.
1629  */
1630  foreach(lc, additional_conds)
1631  {
1632  if (need_and)
1633  appendStringInfoString(buf, " AND ");
1634  appendStringInfoString(buf, (char *) lfirst(lc));
1635  need_and = true;
1636  }
1637 }
static void appendConditions(List *exprs, deparse_expr_cxt *context)
Definition: deparse.c:1571
#define NIL
Definition: pg_list.h:68

References appendConditions(), appendStringInfoString(), buf, context, lfirst, and NIL.

Referenced by deparseDirectDeleteSql(), deparseDirectUpdateSql(), deparseFromExpr(), and deparseFromExprForRel().

◆ build_tlist_to_deparse()

List* build_tlist_to_deparse ( RelOptInfo foreignrel)

Definition at line 1176 of file deparse.c.

1177 {
1178  List *tlist = NIL;
1179  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
1180  ListCell *lc;
1181 
1182  /*
1183  * For an upper relation, we have already built the target list while
1184  * checking shippability, so just return that.
1185  */
1186  if (IS_UPPER_REL(foreignrel))
1187  return fpinfo->grouped_tlist;
1188 
1189  /*
1190  * We require columns specified in foreignrel->reltarget->exprs and those
1191  * required for evaluating the local conditions.
1192  */
1193  tlist = add_to_flat_tlist(tlist,
1194  pull_var_clause((Node *) foreignrel->reltarget->exprs,
1196  foreach(lc, fpinfo->local_conds)
1197  {
1198  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
1199 
1200  tlist = add_to_flat_tlist(tlist,
1201  pull_var_clause((Node *) rinfo->clause,
1203  }
1204 
1205  return tlist;
1206 }
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
#define PVC_RECURSE_PLACEHOLDERS
Definition: optimizer.h:191
#define IS_UPPER_REL(rel)
Definition: pathnodes.h:839
#define lfirst_node(type, lc)
Definition: pg_list.h:176
Definition: pg_list.h:54
List * exprs
Definition: pathnodes.h:1518
struct PathTarget * reltarget
Definition: pathnodes.h:883
Expr * clause
Definition: pathnodes.h:2546
List * add_to_flat_tlist(List *tlist, List *exprs)
Definition: tlist.c:132
List * pull_var_clause(Node *node, int flags)
Definition: var.c:607

References add_to_flat_tlist(), RestrictInfo::clause, PathTarget::exprs, PgFdwRelationInfo::grouped_tlist, if(), IS_UPPER_REL, lfirst_node, PgFdwRelationInfo::local_conds, NIL, pull_var_clause(), PVC_RECURSE_PLACEHOLDERS, and RelOptInfo::reltarget.

Referenced by estimate_path_cost_size(), and postgresGetForeignPlan().

◆ classifyConditions()

void classifyConditions ( PlannerInfo root,
RelOptInfo baserel,
List input_conds,
List **  remote_conds,
List **  local_conds 
)

Definition at line 218 of file deparse.c.

223 {
224  ListCell *lc;
225 
226  *remote_conds = NIL;
227  *local_conds = NIL;
228 
229  foreach(lc, input_conds)
230  {
232 
233  if (is_foreign_expr(root, baserel, ri->clause))
234  *remote_conds = lappend(*remote_conds, ri);
235  else
236  *local_conds = lappend(*local_conds, ri);
237  }
238 }
bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
Definition: deparse.c:244
List * lappend(List *list, void *datum)
Definition: list.c:339

References RestrictInfo::clause, is_foreign_expr(), lappend(), lfirst_node, NIL, and root.

Referenced by estimate_path_cost_size(), and postgresGetForeignRelSize().

◆ deparse_type_name()

static char * deparse_type_name ( Oid  type_oid,
int32  typemod 
)
static

Definition at line 1157 of file deparse.c.

1158 {
1160 
1161  if (!is_builtin(type_oid))
1162  flags |= FORMAT_TYPE_FORCE_QUALIFY;
1163 
1164  return format_type_extended(type_oid, typemod, flags);
1165 }
#define FORMAT_TYPE_TYPEMOD_GIVEN
Definition: builtins.h:124
#define FORMAT_TYPE_FORCE_QUALIFY
Definition: builtins.h:126
uint16 bits16
Definition: c.h:501
char * format_type_extended(Oid type_oid, int32 typemod, bits16 flags)
Definition: format_type.c:112
bool is_builtin(Oid objectId)
Definition: shippable.c:152

References format_type_extended(), FORMAT_TYPE_FORCE_QUALIFY, FORMAT_TYPE_TYPEMOD_GIVEN, and is_builtin().

Referenced by deparseArrayExpr(), deparseConst(), deparseFuncExpr(), deparseRelabelType(), printRemoteParam(), and printRemotePlaceholder().

◆ deparseAggref()

static void deparseAggref ( Aggref node,
deparse_expr_cxt context 
)
static

Definition at line 3657 of file deparse.c.

3658 {
3659  StringInfo buf = context->buf;
3660  bool use_variadic;
3661 
3662  /* Only basic, non-split aggregation accepted. */
3663  Assert(node->aggsplit == AGGSPLIT_SIMPLE);
3664 
3665  /* Check if need to print VARIADIC (cf. ruleutils.c) */
3666  use_variadic = node->aggvariadic;
3667 
3668  /* Find aggregate name from aggfnoid which is a pg_proc entry */
3670  appendStringInfoChar(buf, '(');
3671 
3672  /* Add DISTINCT */
3673  appendStringInfoString(buf, (node->aggdistinct != NIL) ? "DISTINCT " : "");
3674 
3675  if (AGGKIND_IS_ORDERED_SET(node->aggkind))
3676  {
3677  /* Add WITHIN GROUP (ORDER BY ..) */
3678  ListCell *arg;
3679  bool first = true;
3680 
3681  Assert(!node->aggvariadic);
3682  Assert(node->aggorder != NIL);
3683 
3684  foreach(arg, node->aggdirectargs)
3685  {
3686  if (!first)
3687  appendStringInfoString(buf, ", ");
3688  first = false;
3689 
3691  }
3692 
3693  appendStringInfoString(buf, ") WITHIN GROUP (ORDER BY ");
3694  appendAggOrderBy(node->aggorder, node->args, context);
3695  }
3696  else
3697  {
3698  /* aggstar can be set only in zero-argument aggregates */
3699  if (node->aggstar)
3700  appendStringInfoChar(buf, '*');
3701  else
3702  {
3703  ListCell *arg;
3704  bool first = true;
3705 
3706  /* Add all the arguments */
3707  foreach(arg, node->args)
3708  {
3709  TargetEntry *tle = (TargetEntry *) lfirst(arg);
3710  Node *n = (Node *) tle->expr;
3711 
3712  if (tle->resjunk)
3713  continue;
3714 
3715  if (!first)
3716  appendStringInfoString(buf, ", ");
3717  first = false;
3718 
3719  /* Add VARIADIC */
3720  if (use_variadic && lnext(node->args, arg) == NULL)
3721  appendStringInfoString(buf, "VARIADIC ");
3722 
3723  deparseExpr((Expr *) n, context);
3724  }
3725  }
3726 
3727  /* Add ORDER BY */
3728  if (node->aggorder != NIL)
3729  {
3730  appendStringInfoString(buf, " ORDER BY ");
3731  appendAggOrderBy(node->aggorder, node->args, context);
3732  }
3733  }
3734 
3735  /* Add FILTER (WHERE ..) */
3736  if (node->aggfilter != NULL)
3737  {
3738  appendStringInfoString(buf, ") FILTER (WHERE ");
3739  deparseExpr((Expr *) node->aggfilter, context);
3740  }
3741 
3742  appendStringInfoChar(buf, ')');
3743 }
static void appendFunctionName(Oid funcid, deparse_expr_cxt *context)
Definition: deparse.c:4032
static void appendAggOrderBy(List *orderList, List *targetList, deparse_expr_cxt *context)
Definition: deparse.c:3749
@ AGGSPLIT_SIMPLE
Definition: nodes.h:376
void * arg
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:343
Oid aggfnoid
Definition: primnodes.h:430
List * aggdistinct
Definition: primnodes.h:460
List * aggdirectargs
Definition: primnodes.h:451
List * args
Definition: primnodes.h:454
Expr * aggfilter
Definition: primnodes.h:463
List * aggorder
Definition: primnodes.h:457
Expr * expr
Definition: primnodes.h:2065

References Aggref::aggdirectargs, Aggref::aggdistinct, Aggref::aggfilter, Aggref::aggfnoid, Aggref::aggorder, AGGSPLIT_SIMPLE, appendAggOrderBy(), appendFunctionName(), appendStringInfoChar(), appendStringInfoString(), arg, Aggref::args, Assert(), buf, context, deparseExpr(), TargetEntry::expr, if(), lfirst, lnext(), and NIL.

Referenced by deparseExpr().

◆ deparseAnalyzeInfoSql()

void deparseAnalyzeInfoSql ( StringInfo  buf,
Relation  rel 
)

Definition at line 2521 of file deparse.c.

2522 {
2524 
2525  /* We'll need the remote relation name as a literal. */
2527  deparseRelation(&relname, rel);
2528 
2529  appendStringInfoString(buf, "SELECT reltuples, relkind FROM pg_catalog.pg_class WHERE oid = ");
2531  appendStringInfoString(buf, "::pg_catalog.regclass");
2532 }
void deparseStringLiteral(StringInfo buf, const char *val)
Definition: deparse.c:2849
static void deparseRelation(StringInfo buf, Relation rel)
Definition: deparse.c:2809
NameData relname
Definition: pg_class.h:38
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
char data[NAMEDATALEN]
Definition: c.h:729

References appendStringInfoString(), buf, nameData::data, deparseRelation(), deparseStringLiteral(), initStringInfo(), and relname.

Referenced by postgresGetAnalyzeInfoForForeignTable().

◆ deparseAnalyzeSizeSql()

void deparseAnalyzeSizeSql ( StringInfo  buf,
Relation  rel 
)

Definition at line 2499 of file deparse.c.

2500 {
2502 
2503  /* We'll need the remote relation name as a literal. */
2505  deparseRelation(&relname, rel);
2506 
2507  appendStringInfoString(buf, "SELECT pg_catalog.pg_relation_size(");
2509  appendStringInfo(buf, "::pg_catalog.regclass) / %d", BLCKSZ);
2510 }

References appendStringInfo(), appendStringInfoString(), buf, nameData::data, deparseRelation(), deparseStringLiteral(), initStringInfo(), and relname.

Referenced by postgresAnalyzeForeignTable().

◆ deparseAnalyzeSql()

void deparseAnalyzeSql ( StringInfo  buf,
Relation  rel,
PgFdwSamplingMethod  sample_method,
double  sample_frac,
List **  retrieved_attrs 
)

Definition at line 2561 of file deparse.c.

2564 {
2565  Oid relid = RelationGetRelid(rel);
2566  TupleDesc tupdesc = RelationGetDescr(rel);
2567  int i;
2568  char *colname;
2569  List *options;
2570  ListCell *lc;
2571  bool first = true;
2572 
2573  *retrieved_attrs = NIL;
2574 
2575  appendStringInfoString(buf, "SELECT ");
2576  for (i = 0; i < tupdesc->natts; i++)
2577  {
2578  /* Ignore dropped columns. */
2579  if (TupleDescAttr(tupdesc, i)->attisdropped)
2580  continue;
2581 
2582  if (!first)
2583  appendStringInfoString(buf, ", ");
2584  first = false;
2585 
2586  /* Use attribute name or column_name option. */
2587  colname = NameStr(TupleDescAttr(tupdesc, i)->attname);
2588  options = GetForeignColumnOptions(relid, i + 1);
2589 
2590  foreach(lc, options)
2591  {
2592  DefElem *def = (DefElem *) lfirst(lc);
2593 
2594  if (strcmp(def->defname, "column_name") == 0)
2595  {
2596  colname = defGetString(def);
2597  break;
2598  }
2599  }
2600 
2602 
2603  *retrieved_attrs = lappend_int(*retrieved_attrs, i + 1);
2604  }
2605 
2606  /* Don't generate bad syntax for zero-column relation. */
2607  if (first)
2608  appendStringInfoString(buf, "NULL");
2609 
2610  /*
2611  * Construct FROM clause, and perhaps WHERE clause too, depending on the
2612  * selected sampling method.
2613  */
2614  appendStringInfoString(buf, " FROM ");
2615  deparseRelation(buf, rel);
2616 
2617  switch (sample_method)
2618  {
2619  case ANALYZE_SAMPLE_OFF:
2620  /* nothing to do here */
2621  break;
2622 
2623  case ANALYZE_SAMPLE_RANDOM:
2624  appendStringInfo(buf, " WHERE pg_catalog.random() < %f", sample_frac);
2625  break;
2626 
2627  case ANALYZE_SAMPLE_SYSTEM:
2628  appendStringInfo(buf, " TABLESAMPLE SYSTEM(%f)", (100.0 * sample_frac));
2629  break;
2630 
2632  appendStringInfo(buf, " TABLESAMPLE BERNOULLI(%f)", (100.0 * sample_frac));
2633  break;
2634 
2635  case ANALYZE_SAMPLE_AUTO:
2636  /* should have been resolved into actual method */
2637  elog(ERROR, "unexpected sampling method");
2638  break;
2639  }
2640 }
char * defGetString(DefElem *def)
Definition: define.c:48
List * GetForeignColumnOptions(Oid relid, AttrNumber attnum)
Definition: foreign.c:291
int i
Definition: isn.c:73
List * lappend_int(List *list, int datum)
Definition: list.c:357
NameData attname
Definition: pg_attribute.h:41
static char ** options
@ ANALYZE_SAMPLE_AUTO
Definition: postgres_fdw.h:147
@ ANALYZE_SAMPLE_OFF
Definition: postgres_fdw.h:146
@ ANALYZE_SAMPLE_BERNOULLI
Definition: postgres_fdw.h:150
@ ANALYZE_SAMPLE_SYSTEM
Definition: postgres_fdw.h:149
@ ANALYZE_SAMPLE_RANDOM
Definition: postgres_fdw.h:148
#define RelationGetRelid(relation)
Definition: rel.h:507
#define RelationGetDescr(relation)
Definition: rel.h:533
char * defname
Definition: parsenodes.h:811
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92

References ANALYZE_SAMPLE_AUTO, ANALYZE_SAMPLE_BERNOULLI, ANALYZE_SAMPLE_OFF, ANALYZE_SAMPLE_RANDOM, ANALYZE_SAMPLE_SYSTEM, appendStringInfo(), appendStringInfoString(), attname, buf, defGetString(), DefElem::defname, deparseRelation(), elog, ERROR, GetForeignColumnOptions(), i, lappend_int(), lfirst, NameStr, TupleDescData::natts, NIL, options, quote_identifier(), RelationGetDescr, RelationGetRelid, and TupleDescAttr.

Referenced by postgresAcquireSampleRowsFunc().

◆ deparseArrayExpr()

static void deparseArrayExpr ( ArrayExpr node,
deparse_expr_cxt context 
)
static

Definition at line 3631 of file deparse.c.

3632 {
3633  StringInfo buf = context->buf;
3634  bool first = true;
3635  ListCell *lc;
3636 
3637  appendStringInfoString(buf, "ARRAY[");
3638  foreach(lc, node->elements)
3639  {
3640  if (!first)
3641  appendStringInfoString(buf, ", ");
3642  deparseExpr(lfirst(lc), context);
3643  first = false;
3644  }
3645  appendStringInfoChar(buf, ']');
3646 
3647  /* If the array is empty, we need an explicit cast to the array type. */
3648  if (node->elements == NIL)
3649  appendStringInfo(buf, "::%s",
3650  deparse_type_name(node->array_typeid, -1));
3651 }
static char * deparse_type_name(Oid type_oid, int32 typemod)
Definition: deparse.c:1157
List * elements
Definition: primnodes.h:1336

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), buf, context, deparse_type_name(), deparseExpr(), ArrayExpr::elements, lfirst, and NIL.

Referenced by deparseExpr().

◆ deparseBoolExpr()

static void deparseBoolExpr ( BoolExpr node,
deparse_expr_cxt context 
)
static

Definition at line 3510 of file deparse.c.

3511 {
3512  StringInfo buf = context->buf;
3513  const char *op = NULL; /* keep compiler quiet */
3514  bool first;
3515  ListCell *lc;
3516 
3517  switch (node->boolop)
3518  {
3519  case AND_EXPR:
3520  op = "AND";
3521  break;
3522  case OR_EXPR:
3523  op = "OR";
3524  break;
3525  case NOT_EXPR:
3526  appendStringInfoString(buf, "(NOT ");
3527  deparseExpr(linitial(node->args), context);
3528  appendStringInfoChar(buf, ')');
3529  return;
3530  }
3531 
3532  appendStringInfoChar(buf, '(');
3533  first = true;
3534  foreach(lc, node->args)
3535  {
3536  if (!first)
3537  appendStringInfo(buf, " %s ", op);
3538  deparseExpr((Expr *) lfirst(lc), context);
3539  first = false;
3540  }
3541  appendStringInfoChar(buf, ')');
3542 }
#define linitial(l)
Definition: pg_list.h:178
@ AND_EXPR
Definition: primnodes.h:887
@ OR_EXPR
Definition: primnodes.h:887
@ NOT_EXPR
Definition: primnodes.h:887
BoolExprType boolop
Definition: primnodes.h:895
List * args
Definition: primnodes.h:896

References AND_EXPR, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), BoolExpr::args, BoolExpr::boolop, buf, context, deparseExpr(), lfirst, linitial, NOT_EXPR, and OR_EXPR.

Referenced by deparseExpr().

◆ deparseCaseExpr()

static void deparseCaseExpr ( CaseExpr node,
deparse_expr_cxt context 
)
static

Definition at line 3581 of file deparse.c.

3582 {
3583  StringInfo buf = context->buf;
3584  ListCell *lc;
3585 
3586  appendStringInfoString(buf, "(CASE");
3587 
3588  /* If this is a CASE arg WHEN then emit the arg expression */
3589  if (node->arg != NULL)
3590  {
3591  appendStringInfoChar(buf, ' ');
3592  deparseExpr(node->arg, context);
3593  }
3594 
3595  /* Add each condition/result of the CASE clause */
3596  foreach(lc, node->args)
3597  {
3598  CaseWhen *whenclause = (CaseWhen *) lfirst(lc);
3599 
3600  /* WHEN */
3601  appendStringInfoString(buf, " WHEN ");
3602  if (node->arg == NULL) /* CASE WHEN */
3603  deparseExpr(whenclause->expr, context);
3604  else /* CASE arg WHEN */
3605  {
3606  /* Ignore the CaseTestExpr and equality operator. */
3607  deparseExpr(lsecond(castNode(OpExpr, whenclause->expr)->args),
3608  context);
3609  }
3610 
3611  /* THEN */
3612  appendStringInfoString(buf, " THEN ");
3613  deparseExpr(whenclause->result, context);
3614  }
3615 
3616  /* add ELSE if present */
3617  if (node->defresult != NULL)
3618  {
3619  appendStringInfoString(buf, " ELSE ");
3620  deparseExpr(node->defresult, context);
3621  }
3622 
3623  /* append END */
3624  appendStringInfoString(buf, " END)");
3625 }
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
#define lsecond(l)
Definition: pg_list.h:183
Expr * arg
Definition: primnodes.h:1269
Expr * defresult
Definition: primnodes.h:1271
List * args
Definition: primnodes.h:1270
Expr * result
Definition: primnodes.h:1282
Expr * expr
Definition: primnodes.h:1281

References appendStringInfoChar(), appendStringInfoString(), CaseExpr::arg, CaseExpr::args, buf, castNode, context, CaseExpr::defresult, deparseExpr(), CaseWhen::expr, lfirst, lsecond, and CaseWhen::result.

Referenced by deparseExpr().

◆ deparseColumnRef()

static void deparseColumnRef ( StringInfo  buf,
int  varno,
int  varattno,
RangeTblEntry rte,
bool  qualify_col 
)
static

Definition at line 2681 of file deparse.c.

2683 {
2684  /* We support fetching the remote side's CTID and OID. */
2685  if (varattno == SelfItemPointerAttributeNumber)
2686  {
2687  if (qualify_col)
2688  ADD_REL_QUALIFIER(buf, varno);
2689  appendStringInfoString(buf, "ctid");
2690  }
2691  else if (varattno < 0)
2692  {
2693  /*
2694  * All other system attributes are fetched as 0, except for table OID,
2695  * which is fetched as the local table OID. However, we must be
2696  * careful; the table could be beneath an outer join, in which case it
2697  * must go to NULL whenever the rest of the row does.
2698  */
2699  Oid fetchval = 0;
2700 
2701  if (varattno == TableOidAttributeNumber)
2702  fetchval = rte->relid;
2703 
2704  if (qualify_col)
2705  {
2706  appendStringInfoString(buf, "CASE WHEN (");
2707  ADD_REL_QUALIFIER(buf, varno);
2708  appendStringInfo(buf, "*)::text IS NOT NULL THEN %u END", fetchval);
2709  }
2710  else
2711  appendStringInfo(buf, "%u", fetchval);
2712  }
2713  else if (varattno == 0)
2714  {
2715  /* Whole row reference */
2716  Relation rel;
2717  Bitmapset *attrs_used;
2718 
2719  /* Required only to be passed down to deparseTargetList(). */
2720  List *retrieved_attrs;
2721 
2722  /*
2723  * The lock on the relation will be held by upper callers, so it's
2724  * fine to open it with no lock here.
2725  */
2726  rel = table_open(rte->relid, NoLock);
2727 
2728  /*
2729  * The local name of the foreign table can not be recognized by the
2730  * foreign server and the table it references on foreign server might
2731  * have different column ordering or different columns than those
2732  * declared locally. Hence we have to deparse whole-row reference as
2733  * ROW(columns referenced locally). Construct this by deparsing a
2734  * "whole row" attribute.
2735  */
2736  attrs_used = bms_add_member(NULL,
2738 
2739  /*
2740  * In case the whole-row reference is under an outer join then it has
2741  * to go NULL whenever the rest of the row goes NULL. Deparsing a join
2742  * query would always involve multiple relations, thus qualify_col
2743  * would be true.
2744  */
2745  if (qualify_col)
2746  {
2747  appendStringInfoString(buf, "CASE WHEN (");
2748  ADD_REL_QUALIFIER(buf, varno);
2749  appendStringInfoString(buf, "*)::text IS NOT NULL THEN ");
2750  }
2751 
2752  appendStringInfoString(buf, "ROW(");
2753  deparseTargetList(buf, rte, varno, rel, false, attrs_used, qualify_col,
2754  &retrieved_attrs);
2755  appendStringInfoChar(buf, ')');
2756 
2757  /* Complete the CASE WHEN statement started above. */
2758  if (qualify_col)
2759  appendStringInfoString(buf, " END");
2760 
2761  table_close(rel, NoLock);
2762  bms_free(attrs_used);
2763  }
2764  else
2765  {
2766  char *colname = NULL;
2767  List *options;
2768  ListCell *lc;
2769 
2770  /* varno must not be any of OUTER_VAR, INNER_VAR and INDEX_VAR. */
2771  Assert(!IS_SPECIAL_VARNO(varno));
2772 
2773  /*
2774  * If it's a column of a foreign table, and it has the column_name FDW
2775  * option, use that value.
2776  */
2777  options = GetForeignColumnOptions(rte->relid, varattno);
2778  foreach(lc, options)
2779  {
2780  DefElem *def = (DefElem *) lfirst(lc);
2781 
2782  if (strcmp(def->defname, "column_name") == 0)
2783  {
2784  colname = defGetString(def);
2785  break;
2786  }
2787  }
2788 
2789  /*
2790  * If it's a column of a regular table or it doesn't have column_name
2791  * FDW option, use attribute name.
2792  */
2793  if (colname == NULL)
2794  colname = get_attname(rte->relid, varattno, false);
2795 
2796  if (qualify_col)
2797  ADD_REL_QUALIFIER(buf, varno);
2798 
2800  }
2801 }
void bms_free(Bitmapset *a)
Definition: bitmapset.c:239
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:815
static void deparseTargetList(StringInfo buf, RangeTblEntry *rte, Index rtindex, Relation rel, bool is_returning, Bitmapset *attrs_used, bool qualify_col, List **retrieved_attrs)
Definition: deparse.c:1405
#define ADD_REL_QUALIFIER(buf, varno)
Definition: deparse.c:112
#define NoLock
Definition: lockdefs.h:34
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:827
#define IS_SPECIAL_VARNO(varno)
Definition: primnodes.h:227
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27
#define TableOidAttributeNumber
Definition: sysattr.h:26
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40

References ADD_REL_QUALIFIER, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert(), bms_add_member(), bms_free(), buf, defGetString(), DefElem::defname, deparseTargetList(), FirstLowInvalidHeapAttributeNumber, get_attname(), GetForeignColumnOptions(), IS_SPECIAL_VARNO, lfirst, NoLock, options, quote_identifier(), RangeTblEntry::relid, SelfItemPointerAttributeNumber, table_close(), table_open(), and TableOidAttributeNumber.

Referenced by deparseDirectUpdateSql(), deparseInsertSql(), deparseTargetList(), deparseUpdateSql(), and deparseVar().

◆ deparseConst()

static void deparseConst ( Const node,
deparse_expr_cxt context,
int  showtype 
)
static

Definition at line 3020 of file deparse.c.

3021 {
3022  StringInfo buf = context->buf;
3023  Oid typoutput;
3024  bool typIsVarlena;
3025  char *extval;
3026  bool isfloat = false;
3027  bool isstring = false;
3028  bool needlabel;
3029 
3030  if (node->constisnull)
3031  {
3032  appendStringInfoString(buf, "NULL");
3033  if (showtype >= 0)
3034  appendStringInfo(buf, "::%s",
3036  node->consttypmod));
3037  return;
3038  }
3039 
3041  &typoutput, &typIsVarlena);
3042  extval = OidOutputFunctionCall(typoutput, node->constvalue);
3043 
3044  switch (node->consttype)
3045  {
3046  case INT2OID:
3047  case INT4OID:
3048  case INT8OID:
3049  case OIDOID:
3050  case FLOAT4OID:
3051  case FLOAT8OID:
3052  case NUMERICOID:
3053  {
3054  /*
3055  * No need to quote unless it's a special value such as 'NaN'.
3056  * See comments in get_const_expr().
3057  */
3058  if (strspn(extval, "0123456789+-eE.") == strlen(extval))
3059  {
3060  if (extval[0] == '+' || extval[0] == '-')
3061  appendStringInfo(buf, "(%s)", extval);
3062  else
3063  appendStringInfoString(buf, extval);
3064  if (strcspn(extval, "eE.") != strlen(extval))
3065  isfloat = true; /* it looks like a float */
3066  }
3067  else
3068  appendStringInfo(buf, "'%s'", extval);
3069  }
3070  break;
3071  case BITOID:
3072  case VARBITOID:
3073  appendStringInfo(buf, "B'%s'", extval);
3074  break;
3075  case BOOLOID:
3076  if (strcmp(extval, "t") == 0)
3077  appendStringInfoString(buf, "true");
3078  else
3079  appendStringInfoString(buf, "false");
3080  break;
3081  default:
3082  deparseStringLiteral(buf, extval);
3083  isstring = true;
3084  break;
3085  }
3086 
3087  pfree(extval);
3088 
3089  if (showtype == -1)
3090  return; /* never print type label */
3091 
3092  /*
3093  * For showtype == 0, append ::typename unless the constant will be
3094  * implicitly typed as the right type when it is read in.
3095  *
3096  * XXX this code has to be kept in sync with the behavior of the parser,
3097  * especially make_const.
3098  */
3099  switch (node->consttype)
3100  {
3101  case BOOLOID:
3102  case INT4OID:
3103  case UNKNOWNOID:
3104  needlabel = false;
3105  break;
3106  case NUMERICOID:
3107  needlabel = !isfloat || (node->consttypmod >= 0);
3108  break;
3109  default:
3110  if (showtype == -2)
3111  {
3112  /* label unless we printed it as an untyped string */
3113  needlabel = !isstring;
3114  }
3115  else
3116  needlabel = true;
3117  break;
3118  }
3119  if (needlabel || showtype > 0)
3120  appendStringInfo(buf, "::%s",
3122  node->consttypmod));
3123 }
char * OidOutputFunctionCall(Oid functionId, Datum val)
Definition: fmgr.c:1763
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2885
void pfree(void *pointer)
Definition: mcxt.c:1508
Oid consttype
Definition: primnodes.h:298

References appendStringInfo(), appendStringInfoString(), buf, Const::consttype, context, deparse_type_name(), deparseStringLiteral(), getTypeOutputInfo(), OidOutputFunctionCall(), and pfree().

Referenced by deparseExpr(), deparseOpExpr(), and deparseSortGroupClause().

◆ deparseDeleteSql()

void deparseDeleteSql ( StringInfo  buf,
RangeTblEntry rte,
Index  rtindex,
Relation  rel,
List returningList,
List **  retrieved_attrs 
)

Definition at line 2362 of file deparse.c.

2366 {
2367  appendStringInfoString(buf, "DELETE FROM ");
2368  deparseRelation(buf, rel);
2369  appendStringInfoString(buf, " WHERE ctid = $1");
2370 
2371  deparseReturningList(buf, rte, rtindex, rel,
2372  rel->trigdesc && rel->trigdesc->trig_delete_after_row,
2373  NIL, returningList, retrieved_attrs);
2374 }
static void deparseReturningList(StringInfo buf, RangeTblEntry *rte, Index rtindex, Relation rel, bool trig_after_row, List *withCheckOptionList, List *returningList, List **retrieved_attrs)
Definition: deparse.c:2442
TriggerDesc * trigdesc
Definition: rel.h:117
bool trig_delete_after_row
Definition: reltrigger.h:67

References appendStringInfoString(), buf, deparseRelation(), deparseReturningList(), NIL, TriggerDesc::trig_delete_after_row, and RelationData::trigdesc.

Referenced by postgresPlanForeignModify().

◆ deparseDirectDeleteSql()

void deparseDirectDeleteSql ( StringInfo  buf,
PlannerInfo root,
Index  rtindex,
Relation  rel,
RelOptInfo foreignrel,
List remote_conds,
List **  params_list,
List returningList,
List **  retrieved_attrs 
)

Definition at line 2391 of file deparse.c.

2398 {
2400  List *additional_conds = NIL;
2401 
2402  /* Set up context struct for recursion */
2403  context.root = root;
2404  context.foreignrel = foreignrel;
2405  context.scanrel = foreignrel;
2406  context.buf = buf;
2407  context.params_list = params_list;
2408 
2409  appendStringInfoString(buf, "DELETE FROM ");
2410  deparseRelation(buf, rel);
2411  if (foreignrel->reloptkind == RELOPT_JOINREL)
2412  appendStringInfo(buf, " %s%d", REL_ALIAS_PREFIX, rtindex);
2413 
2414  if (foreignrel->reloptkind == RELOPT_JOINREL)
2415  {
2416  List *ignore_conds = NIL;
2417 
2418  appendStringInfoString(buf, " USING ");
2419  deparseFromExprForRel(buf, root, foreignrel, true, rtindex,
2420  &ignore_conds, &additional_conds, params_list);
2421  remote_conds = list_concat(remote_conds, ignore_conds);
2422  }
2423 
2424  appendWhereClause(remote_conds, additional_conds, &context);
2425 
2426  if (additional_conds != NIL)
2427  list_free_deep(additional_conds);
2428 
2429  if (foreignrel->reloptkind == RELOPT_JOINREL)
2430  deparseExplicitTargetList(returningList, true, retrieved_attrs,
2431  &context);
2432  else
2434  rtindex, rel, false,
2435  NIL, returningList, retrieved_attrs);
2436 }
#define REL_ALIAS_PREFIX
Definition: deparse.c:110
static void deparseExplicitTargetList(List *tlist, bool is_returning, List **retrieved_attrs, deparse_expr_cxt *context)
Definition: deparse.c:1681
static void deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel, bool use_alias, Index ignore_rel, List **ignore_conds, List **additional_conds, List **params_list)
Definition: deparse.c:1761
static void appendWhereClause(List *exprs, List *additional_conds, deparse_expr_cxt *context)
Definition: deparse.c:1608
List * list_concat(List *list1, const List *list2)
Definition: list.c:561
void list_free_deep(List *list)
Definition: list.c:1560
#define planner_rt_fetch(rti, root)
Definition: pathnodes.h:560
@ RELOPT_JOINREL
Definition: pathnodes.h:818
RelOptKind reloptkind
Definition: pathnodes.h:855

References appendStringInfo(), appendStringInfoString(), appendWhereClause(), buf, context, deparseExplicitTargetList(), deparseFromExprForRel(), deparseRelation(), deparseReturningList(), list_concat(), list_free_deep(), NIL, planner_rt_fetch, REL_ALIAS_PREFIX, RELOPT_JOINREL, RelOptInfo::reloptkind, and root.

Referenced by postgresPlanDirectModify().

◆ deparseDirectUpdateSql()

void deparseDirectUpdateSql ( StringInfo  buf,
PlannerInfo root,
Index  rtindex,
Relation  rel,
RelOptInfo foreignrel,
List targetlist,
List targetAttrs,
List remote_conds,
List **  params_list,
List returningList,
List **  retrieved_attrs 
)

Definition at line 2276 of file deparse.c.

2285 {
2287  int nestlevel;
2288  bool first;
2289  RangeTblEntry *rte = planner_rt_fetch(rtindex, root);
2290  ListCell *lc,
2291  *lc2;
2292  List *additional_conds = NIL;
2293 
2294  /* Set up context struct for recursion */
2295  context.root = root;
2296  context.foreignrel = foreignrel;
2297  context.scanrel = foreignrel;
2298  context.buf = buf;
2299  context.params_list = params_list;
2300 
2301  appendStringInfoString(buf, "UPDATE ");
2302  deparseRelation(buf, rel);
2303  if (foreignrel->reloptkind == RELOPT_JOINREL)
2304  appendStringInfo(buf, " %s%d", REL_ALIAS_PREFIX, rtindex);
2305  appendStringInfoString(buf, " SET ");
2306 
2307  /* Make sure any constants in the exprs are printed portably */
2308  nestlevel = set_transmission_modes();
2309 
2310  first = true;
2311  forboth(lc, targetlist, lc2, targetAttrs)
2312  {
2313  TargetEntry *tle = lfirst_node(TargetEntry, lc);
2314  int attnum = lfirst_int(lc2);
2315 
2316  /* update's new-value expressions shouldn't be resjunk */
2317  Assert(!tle->resjunk);
2318 
2319  if (!first)
2320  appendStringInfoString(buf, ", ");
2321  first = false;
2322 
2323  deparseColumnRef(buf, rtindex, attnum, rte, false);
2324  appendStringInfoString(buf, " = ");
2325  deparseExpr((Expr *) tle->expr, &context);
2326  }
2327 
2328  reset_transmission_modes(nestlevel);
2329 
2330  if (foreignrel->reloptkind == RELOPT_JOINREL)
2331  {
2332  List *ignore_conds = NIL;
2333 
2334 
2335  appendStringInfoString(buf, " FROM ");
2336  deparseFromExprForRel(buf, root, foreignrel, true, rtindex,
2337  &ignore_conds, &additional_conds, params_list);
2338  remote_conds = list_concat(remote_conds, ignore_conds);
2339  }
2340 
2341  appendWhereClause(remote_conds, additional_conds, &context);
2342 
2343  if (additional_conds != NIL)
2344  list_free_deep(additional_conds);
2345 
2346  if (foreignrel->reloptkind == RELOPT_JOINREL)
2347  deparseExplicitTargetList(returningList, true, retrieved_attrs,
2348  &context);
2349  else
2350  deparseReturningList(buf, rte, rtindex, rel, false,
2351  NIL, returningList, retrieved_attrs);
2352 }
static void deparseColumnRef(StringInfo buf, int varno, int varattno, RangeTblEntry *rte, bool qualify_col)
Definition: deparse.c:2681
int16 attnum
Definition: pg_attribute.h:74
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:518
#define lfirst_int(lc)
Definition: pg_list.h:173

References appendStringInfo(), appendStringInfoString(), appendWhereClause(), Assert(), attnum, buf, context, deparseColumnRef(), deparseExplicitTargetList(), deparseExpr(), deparseFromExprForRel(), deparseRelation(), deparseReturningList(), TargetEntry::expr, forboth, lfirst_int, lfirst_node, list_concat(), list_free_deep(), NIL, planner_rt_fetch, REL_ALIAS_PREFIX, RELOPT_JOINREL, RelOptInfo::reloptkind, reset_transmission_modes(), root, and set_transmission_modes().

Referenced by postgresPlanDirectModify().

◆ deparseDistinctExpr()

static void deparseDistinctExpr ( DistinctExpr node,
deparse_expr_cxt context 
)
static

Definition at line 3434 of file deparse.c.

3435 {
3436  StringInfo buf = context->buf;
3437 
3438  Assert(list_length(node->args) == 2);
3439 
3440  appendStringInfoChar(buf, '(');
3441  deparseExpr(linitial(node->args), context);
3442  appendStringInfoString(buf, " IS DISTINCT FROM ");
3443  deparseExpr(lsecond(node->args), context);
3444  appendStringInfoChar(buf, ')');
3445 }
static int list_length(const List *l)
Definition: pg_list.h:152
List * args
Definition: primnodes.h:792

References appendStringInfoChar(), appendStringInfoString(), OpExpr::args, Assert(), buf, context, deparseExpr(), linitial, list_length(), and lsecond.

Referenced by deparseExpr().

◆ deparseExplicitTargetList()

static void deparseExplicitTargetList ( List tlist,
bool  is_returning,
List **  retrieved_attrs,
deparse_expr_cxt context 
)
static

Definition at line 1681 of file deparse.c.

1685 {
1686  ListCell *lc;
1687  StringInfo buf = context->buf;
1688  int i = 0;
1689 
1690  *retrieved_attrs = NIL;
1691 
1692  foreach(lc, tlist)
1693  {
1694  TargetEntry *tle = lfirst_node(TargetEntry, lc);
1695 
1696  if (i > 0)
1697  appendStringInfoString(buf, ", ");
1698  else if (is_returning)
1699  appendStringInfoString(buf, " RETURNING ");
1700 
1701  deparseExpr((Expr *) tle->expr, context);
1702 
1703  *retrieved_attrs = lappend_int(*retrieved_attrs, i + 1);
1704  i++;
1705  }
1706 
1707  if (i == 0 && !is_returning)
1708  appendStringInfoString(buf, "NULL");
1709 }

References appendStringInfoString(), buf, context, deparseExpr(), TargetEntry::expr, i, lappend_int(), lfirst_node, and NIL.

Referenced by deparseDirectDeleteSql(), deparseDirectUpdateSql(), and deparseSelectSql().

◆ deparseExpr()

static void deparseExpr ( Expr node,
deparse_expr_cxt context 
)
static

Definition at line 2884 of file deparse.c.

2885 {
2886  if (node == NULL)
2887  return;
2888 
2889  switch (nodeTag(node))
2890  {
2891  case T_Var:
2892  deparseVar((Var *) node, context);
2893  break;
2894  case T_Const:
2895  deparseConst((Const *) node, context, 0);
2896  break;
2897  case T_Param:
2898  deparseParam((Param *) node, context);
2899  break;
2900  case T_SubscriptingRef:
2902  break;
2903  case T_FuncExpr:
2904  deparseFuncExpr((FuncExpr *) node, context);
2905  break;
2906  case T_OpExpr:
2907  deparseOpExpr((OpExpr *) node, context);
2908  break;
2909  case T_DistinctExpr:
2911  break;
2912  case T_ScalarArrayOpExpr:
2914  break;
2915  case T_RelabelType:
2917  break;
2918  case T_BoolExpr:
2919  deparseBoolExpr((BoolExpr *) node, context);
2920  break;
2921  case T_NullTest:
2922  deparseNullTest((NullTest *) node, context);
2923  break;
2924  case T_CaseExpr:
2925  deparseCaseExpr((CaseExpr *) node, context);
2926  break;
2927  case T_ArrayExpr:
2928  deparseArrayExpr((ArrayExpr *) node, context);
2929  break;
2930  case T_Aggref:
2931  deparseAggref((Aggref *) node, context);
2932  break;
2933  default:
2934  elog(ERROR, "unsupported expression type for deparse: %d",
2935  (int) nodeTag(node));
2936  break;
2937  }
2938 }
static void deparseCaseExpr(CaseExpr *node, deparse_expr_cxt *context)
Definition: deparse.c:3581
static void deparseBoolExpr(BoolExpr *node, deparse_expr_cxt *context)
Definition: deparse.c:3510
static void deparseAggref(Aggref *node, deparse_expr_cxt *context)
Definition: deparse.c:3657
static void deparseRelabelType(RelabelType *node, deparse_expr_cxt *context)
Definition: deparse.c:3497
static void deparseNullTest(NullTest *node, deparse_expr_cxt *context)
Definition: deparse.c:3548
static void deparseOpExpr(OpExpr *node, deparse_expr_cxt *context)
Definition: deparse.c:3276
static void deparseConst(Const *node, deparse_expr_cxt *context, int showtype)
Definition: deparse.c:3020
static void deparseDistinctExpr(DistinctExpr *node, deparse_expr_cxt *context)
Definition: deparse.c:3434
static void deparseFuncExpr(FuncExpr *node, deparse_expr_cxt *context)
Definition: deparse.c:3213
static void deparseArrayExpr(ArrayExpr *node, deparse_expr_cxt *context)
Definition: deparse.c:3631
static void deparseVar(Var *node, deparse_expr_cxt *context)
Definition: deparse.c:2949
static void deparseSubscriptingRef(SubscriptingRef *node, deparse_expr_cxt *context)
Definition: deparse.c:3167
static void deparseScalarArrayOpExpr(ScalarArrayOpExpr *node, deparse_expr_cxt *context)
Definition: deparse.c:3452
static void deparseParam(Param *node, deparse_expr_cxt *context)
Definition: deparse.c:3134
#define nodeTag(nodeptr)
Definition: nodes.h:133
Definition: primnodes.h:234

References context, deparseAggref(), deparseArrayExpr(), deparseBoolExpr(), deparseCaseExpr(), deparseConst(), deparseDistinctExpr(), deparseFuncExpr(), deparseNullTest(), deparseOpExpr(), deparseParam(), deparseRelabelType(), deparseScalarArrayOpExpr(), deparseSubscriptingRef(), deparseVar(), elog, ERROR, and nodeTag.

Referenced by appendConditions(), appendLimitClause(), appendOrderByClause(), deparseAggref(), deparseArrayExpr(), deparseBoolExpr(), deparseCaseExpr(), deparseDirectUpdateSql(), deparseDistinctExpr(), deparseExplicitTargetList(), deparseFuncExpr(), deparseNullTest(), deparseOpExpr(), deparseRelabelType(), deparseScalarArrayOpExpr(), deparseSortGroupClause(), deparseSubqueryTargetList(), and deparseSubscriptingRef().

◆ deparseFromExpr()

static void deparseFromExpr ( List quals,
deparse_expr_cxt context 
)
static

Definition at line 1373 of file deparse.c.

1374 {
1375  StringInfo buf = context->buf;
1376  RelOptInfo *scanrel = context->scanrel;
1377  List *additional_conds = NIL;
1378 
1379  /* For upper relations, scanrel must be either a joinrel or a baserel */
1380  Assert(!IS_UPPER_REL(context->foreignrel) ||
1381  IS_JOIN_REL(scanrel) || IS_SIMPLE_REL(scanrel));
1382 
1383  /* Construct FROM clause */
1384  appendStringInfoString(buf, " FROM ");
1385  deparseFromExprForRel(buf, context->root, scanrel,
1386  (bms_membership(scanrel->relids) == BMS_MULTIPLE),
1387  (Index) 0, NULL, &additional_conds,
1388  context->params_list);
1389  appendWhereClause(quals, additional_conds, context);
1390  if (additional_conds != NIL)
1391  list_free_deep(additional_conds);
1392 }
BMS_Membership bms_membership(const Bitmapset *a)
Definition: bitmapset.c:781
@ BMS_MULTIPLE
Definition: bitmapset.h:73
unsigned int Index
Definition: c.h:601
#define IS_SIMPLE_REL(rel)
Definition: pathnodes.h:829
#define IS_JOIN_REL(rel)
Definition: pathnodes.h:834
Relids relids
Definition: pathnodes.h:861

References appendStringInfoString(), appendWhereClause(), Assert(), bms_membership(), BMS_MULTIPLE, buf, context, deparseFromExprForRel(), IS_JOIN_REL, IS_SIMPLE_REL, IS_UPPER_REL, list_free_deep(), NIL, and RelOptInfo::relids.

Referenced by deparseSelectStmtForRel().

◆ deparseFromExprForRel()

static void deparseFromExprForRel ( StringInfo  buf,
PlannerInfo root,
RelOptInfo foreignrel,
bool  use_alias,
Index  ignore_rel,
List **  ignore_conds,
List **  additional_conds,
List **  params_list 
)
static

Definition at line 1761 of file deparse.c.

1764 {
1765  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
1766 
1767  if (IS_JOIN_REL(foreignrel))
1768  {
1769  StringInfoData join_sql_o;
1770  StringInfoData join_sql_i;
1771  RelOptInfo *outerrel = fpinfo->outerrel;
1772  RelOptInfo *innerrel = fpinfo->innerrel;
1773  bool outerrel_is_target = false;
1774  bool innerrel_is_target = false;
1775  List *additional_conds_i = NIL;
1776  List *additional_conds_o = NIL;
1777 
1778  if (ignore_rel > 0 && bms_is_member(ignore_rel, foreignrel->relids))
1779  {
1780  /*
1781  * If this is an inner join, add joinclauses to *ignore_conds and
1782  * set it to empty so that those can be deparsed into the WHERE
1783  * clause. Note that since the target relation can never be
1784  * within the nullable side of an outer join, those could safely
1785  * be pulled up into the WHERE clause (see foreign_join_ok()).
1786  * Note also that since the target relation is only inner-joined
1787  * to any other relation in the query, all conditions in the join
1788  * tree mentioning the target relation could be deparsed into the
1789  * WHERE clause by doing this recursively.
1790  */
1791  if (fpinfo->jointype == JOIN_INNER)
1792  {
1793  *ignore_conds = list_concat(*ignore_conds,
1794  fpinfo->joinclauses);
1795  fpinfo->joinclauses = NIL;
1796  }
1797 
1798  /*
1799  * Check if either of the input relations is the target relation.
1800  */
1801  if (outerrel->relid == ignore_rel)
1802  outerrel_is_target = true;
1803  else if (innerrel->relid == ignore_rel)
1804  innerrel_is_target = true;
1805  }
1806 
1807  /* Deparse outer relation if not the target relation. */
1808  if (!outerrel_is_target)
1809  {
1810  initStringInfo(&join_sql_o);
1811  deparseRangeTblRef(&join_sql_o, root, outerrel,
1812  fpinfo->make_outerrel_subquery,
1813  ignore_rel, ignore_conds, &additional_conds_o,
1814  params_list);
1815 
1816  /*
1817  * If inner relation is the target relation, skip deparsing it.
1818  * Note that since the join of the target relation with any other
1819  * relation in the query is an inner join and can never be within
1820  * the nullable side of an outer join, the join could be
1821  * interchanged with higher-level joins (cf. identity 1 on outer
1822  * join reordering shown in src/backend/optimizer/README), which
1823  * means it's safe to skip the target-relation deparsing here.
1824  */
1825  if (innerrel_is_target)
1826  {
1827  Assert(fpinfo->jointype == JOIN_INNER);
1828  Assert(fpinfo->joinclauses == NIL);
1829  appendBinaryStringInfo(buf, join_sql_o.data, join_sql_o.len);
1830  /* Pass EXISTS conditions to upper level */
1831  if (additional_conds_o != NIL)
1832  {
1833  Assert(*additional_conds == NIL);
1834  *additional_conds = additional_conds_o;
1835  }
1836  return;
1837  }
1838  }
1839 
1840  /* Deparse inner relation if not the target relation. */
1841  if (!innerrel_is_target)
1842  {
1843  initStringInfo(&join_sql_i);
1844  deparseRangeTblRef(&join_sql_i, root, innerrel,
1845  fpinfo->make_innerrel_subquery,
1846  ignore_rel, ignore_conds, &additional_conds_i,
1847  params_list);
1848 
1849  /*
1850  * SEMI-JOIN is deparsed as the EXISTS subquery. It references
1851  * outer and inner relations, so it should be evaluated as the
1852  * condition in the upper-level WHERE clause. We deparse the
1853  * condition and pass it to upper level callers as an
1854  * additional_conds list. Upper level callers are responsible for
1855  * inserting conditions from the list where appropriate.
1856  */
1857  if (fpinfo->jointype == JOIN_SEMI)
1858  {
1861 
1862  /* Construct deparsed condition from this SEMI-JOIN */
1863  initStringInfo(&str);
1864  appendStringInfo(&str, "EXISTS (SELECT NULL FROM %s",
1865  join_sql_i.data);
1866 
1867  context.buf = &str;
1868  context.foreignrel = foreignrel;
1869  context.scanrel = foreignrel;
1870  context.root = root;
1871  context.params_list = params_list;
1872 
1873  /*
1874  * Append SEMI-JOIN clauses and EXISTS conditions from lower
1875  * levels to the current EXISTS subquery
1876  */
1877  appendWhereClause(fpinfo->joinclauses, additional_conds_i, &context);
1878 
1879  /*
1880  * EXISTS conditions, coming from lower join levels, have just
1881  * been processed.
1882  */
1883  if (additional_conds_i != NIL)
1884  {
1885  list_free_deep(additional_conds_i);
1886  additional_conds_i = NIL;
1887  }
1888 
1889  /* Close parentheses for EXISTS subquery */
1890  appendStringInfo(&str, ")");
1891 
1892  *additional_conds = lappend(*additional_conds, str.data);
1893  }
1894 
1895  /*
1896  * If outer relation is the target relation, skip deparsing it.
1897  * See the above note about safety.
1898  */
1899  if (outerrel_is_target)
1900  {
1901  Assert(fpinfo->jointype == JOIN_INNER);
1902  Assert(fpinfo->joinclauses == NIL);
1903  appendBinaryStringInfo(buf, join_sql_i.data, join_sql_i.len);
1904  /* Pass EXISTS conditions to the upper call */
1905  if (additional_conds_i != NIL)
1906  {
1907  Assert(*additional_conds == NIL);
1908  *additional_conds = additional_conds_i;
1909  }
1910  return;
1911  }
1912  }
1913 
1914  /* Neither of the relations is the target relation. */
1915  Assert(!outerrel_is_target && !innerrel_is_target);
1916 
1917  /*
1918  * For semijoin FROM clause is deparsed as an outer relation. An inner
1919  * relation and join clauses are converted to EXISTS condition and
1920  * passed to the upper level.
1921  */
1922  if (fpinfo->jointype == JOIN_SEMI)
1923  {
1924  appendStringInfo(buf, "%s", join_sql_o.data);
1925  }
1926  else
1927  {
1928  /*
1929  * For a join relation FROM clause, entry is deparsed as
1930  *
1931  * ((outer relation) <join type> (inner relation) ON
1932  * (joinclauses))
1933  */
1934  appendStringInfo(buf, "(%s %s JOIN %s ON ", join_sql_o.data,
1935  get_jointype_name(fpinfo->jointype), join_sql_i.data);
1936 
1937  /* Append join clause; (TRUE) if no join clause */
1938  if (fpinfo->joinclauses)
1939  {
1941 
1942  context.buf = buf;
1943  context.foreignrel = foreignrel;
1944  context.scanrel = foreignrel;
1945  context.root = root;
1946  context.params_list = params_list;
1947 
1948  appendStringInfoChar(buf, '(');
1950  appendStringInfoChar(buf, ')');
1951  }
1952  else
1953  appendStringInfoString(buf, "(TRUE)");
1954 
1955  /* End the FROM clause entry. */
1956  appendStringInfoChar(buf, ')');
1957  }
1958 
1959  /*
1960  * Construct additional_conds to be passed to the upper caller from
1961  * current level additional_conds and additional_conds, coming from
1962  * inner and outer rels.
1963  */
1964  if (additional_conds_o != NIL)
1965  {
1966  *additional_conds = list_concat(*additional_conds,
1967  additional_conds_o);
1968  list_free(additional_conds_o);
1969  }
1970 
1971  if (additional_conds_i != NIL)
1972  {
1973  *additional_conds = list_concat(*additional_conds,
1974  additional_conds_i);
1975  list_free(additional_conds_i);
1976  }
1977  }
1978  else
1979  {
1980  RangeTblEntry *rte = planner_rt_fetch(foreignrel->relid, root);
1981 
1982  /*
1983  * Core code already has some lock on each rel being planned, so we
1984  * can use NoLock here.
1985  */
1986  Relation rel = table_open(rte->relid, NoLock);
1987 
1988  deparseRelation(buf, rel);
1989 
1990  /*
1991  * Add a unique alias to avoid any conflict in relation names due to
1992  * pulled up subqueries in the query being built for a pushed down
1993  * join.
1994  */
1995  if (use_alias)
1996  appendStringInfo(buf, " %s%d", REL_ALIAS_PREFIX, foreignrel->relid);
1997 
1998  table_close(rel, NoLock);
1999  }
2000 }
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:510
static void deparseRangeTblRef(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel, bool make_subquery, Index ignore_rel, List **ignore_conds, List **additional_conds, List **params_list)
Definition: deparse.c:2008
const char * get_jointype_name(JoinType jointype)
Definition: deparse.c:1641
void list_free(List *list)
Definition: list.c:1546
@ JOIN_SEMI
Definition: nodes.h:307
@ JOIN_INNER
Definition: nodes.h:293
void appendBinaryStringInfo(StringInfo str, const void *data, int datalen)
Definition: stringinfo.c:233
RelOptInfo * outerrel
Definition: postgres_fdw.h:102
RelOptInfo * innerrel
Definition: postgres_fdw.h:103
Index relid
Definition: pathnodes.h:908

References appendBinaryStringInfo(), appendConditions(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), appendWhereClause(), Assert(), bms_is_member(), buf, context, StringInfoData::data, deparseRangeTblRef(), deparseRelation(), get_jointype_name(), if(), initStringInfo(), PgFdwRelationInfo::innerrel, IS_JOIN_REL, JOIN_INNER, JOIN_SEMI, PgFdwRelationInfo::joinclauses, PgFdwRelationInfo::jointype, lappend(), StringInfoData::len, list_concat(), list_free(), list_free_deep(), PgFdwRelationInfo::make_innerrel_subquery, PgFdwRelationInfo::make_outerrel_subquery, NIL, NoLock, PgFdwRelationInfo::outerrel, planner_rt_fetch, REL_ALIAS_PREFIX, RangeTblEntry::relid, RelOptInfo::relid, RelOptInfo::relids, root, generate_unaccent_rules::str, table_close(), and table_open().

Referenced by deparseDirectDeleteSql(), deparseDirectUpdateSql(), deparseFromExpr(), and deparseRangeTblRef().

◆ deparseFuncExpr()

static void deparseFuncExpr ( FuncExpr node,
deparse_expr_cxt context 
)
static

Definition at line 3213 of file deparse.c.

3214 {
3215  StringInfo buf = context->buf;
3216  bool use_variadic;
3217  bool first;
3218  ListCell *arg;
3219 
3220  /*
3221  * If the function call came from an implicit coercion, then just show the
3222  * first argument.
3223  */
3224  if (node->funcformat == COERCE_IMPLICIT_CAST)
3225  {
3226  deparseExpr((Expr *) linitial(node->args), context);
3227  return;
3228  }
3229 
3230  /*
3231  * If the function call came from a cast, then show the first argument
3232  * plus an explicit cast operation.
3233  */
3234  if (node->funcformat == COERCE_EXPLICIT_CAST)
3235  {
3236  Oid rettype = node->funcresulttype;
3237  int32 coercedTypmod;
3238 
3239  /* Get the typmod if this is a length-coercion function */
3240  (void) exprIsLengthCoercion((Node *) node, &coercedTypmod);
3241 
3242  deparseExpr((Expr *) linitial(node->args), context);
3243  appendStringInfo(buf, "::%s",
3244  deparse_type_name(rettype, coercedTypmod));
3245  return;
3246  }
3247 
3248  /* Check if need to print VARIADIC (cf. ruleutils.c) */
3249  use_variadic = node->funcvariadic;
3250 
3251  /*
3252  * Normal function: display as proname(args).
3253  */
3255  appendStringInfoChar(buf, '(');
3256 
3257  /* ... and all the arguments */
3258  first = true;
3259  foreach(arg, node->args)
3260  {
3261  if (!first)
3262  appendStringInfoString(buf, ", ");
3263  if (use_variadic && lnext(node->args, arg) == NULL)
3264  appendStringInfoString(buf, "VARIADIC ");
3266  first = false;
3267  }
3268  appendStringInfoChar(buf, ')');
3269 }
signed int int32
Definition: c.h:481
bool exprIsLengthCoercion(const Node *expr, int32 *coercedTypmod)
Definition: nodeFuncs.c:552
@ COERCE_IMPLICIT_CAST
Definition: primnodes.h:692
@ COERCE_EXPLICIT_CAST
Definition: primnodes.h:691
Oid funcid
Definition: primnodes.h:706
List * args
Definition: primnodes.h:724

References appendFunctionName(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), arg, FuncExpr::args, buf, COERCE_EXPLICIT_CAST, COERCE_IMPLICIT_CAST, context, deparse_type_name(), deparseExpr(), exprIsLengthCoercion(), FuncExpr::funcid, lfirst, linitial, and lnext().

Referenced by deparseExpr().

◆ deparseInsertSql()

void deparseInsertSql ( StringInfo  buf,
RangeTblEntry rte,
Index  rtindex,
Relation  rel,
List targetAttrs,
bool  doNothing,
List withCheckOptionList,
List returningList,
List **  retrieved_attrs,
int *  values_end_len 
)

Definition at line 2083 of file deparse.c.

2088 {
2089  TupleDesc tupdesc = RelationGetDescr(rel);
2090  AttrNumber pindex;
2091  bool first;
2092  ListCell *lc;
2093 
2094  appendStringInfoString(buf, "INSERT INTO ");
2095  deparseRelation(buf, rel);
2096 
2097  if (targetAttrs)
2098  {
2099  appendStringInfoChar(buf, '(');
2100 
2101  first = true;
2102  foreach(lc, targetAttrs)
2103  {
2104  int attnum = lfirst_int(lc);
2105 
2106  if (!first)
2107  appendStringInfoString(buf, ", ");
2108  first = false;
2109 
2110  deparseColumnRef(buf, rtindex, attnum, rte, false);
2111  }
2112 
2113  appendStringInfoString(buf, ") VALUES (");
2114 
2115  pindex = 1;
2116  first = true;
2117  foreach(lc, targetAttrs)
2118  {
2119  int attnum = lfirst_int(lc);
2120  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
2121 
2122  if (!first)
2123  appendStringInfoString(buf, ", ");
2124  first = false;
2125 
2126  if (attr->attgenerated)
2127  appendStringInfoString(buf, "DEFAULT");
2128  else
2129  {
2130  appendStringInfo(buf, "$%d", pindex);
2131  pindex++;
2132  }
2133  }
2134 
2135  appendStringInfoChar(buf, ')');
2136  }
2137  else
2138  appendStringInfoString(buf, " DEFAULT VALUES");
2139  *values_end_len = buf->len;
2140 
2141  if (doNothing)
2142  appendStringInfoString(buf, " ON CONFLICT DO NOTHING");
2143 
2144  deparseReturningList(buf, rte, rtindex, rel,
2145  rel->trigdesc && rel->trigdesc->trig_insert_after_row,
2146  withCheckOptionList, returningList, retrieved_attrs);
2147 }
int16 AttrNumber
Definition: attnum.h:21
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
bool trig_insert_after_row
Definition: reltrigger.h:57

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), attnum, buf, deparseColumnRef(), deparseRelation(), deparseReturningList(), lfirst_int, RelationGetDescr, TriggerDesc::trig_insert_after_row, RelationData::trigdesc, and TupleDescAttr.

Referenced by postgresBeginForeignInsert(), and postgresPlanForeignModify().

◆ deparseLockingClause()

static void deparseLockingClause ( deparse_expr_cxt context)
static

Definition at line 1481 of file deparse.c.

1482 {
1483  StringInfo buf = context->buf;
1484  PlannerInfo *root = context->root;
1485  RelOptInfo *rel = context->scanrel;
1486  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
1487  int relid = -1;
1488 
1489  while ((relid = bms_next_member(rel->relids, relid)) >= 0)
1490  {
1491  /*
1492  * Ignore relation if it appears in a lower subquery. Locking clause
1493  * for such a relation is included in the subquery if necessary.
1494  */
1495  if (bms_is_member(relid, fpinfo->lower_subquery_rels))
1496  continue;
1497 
1498  /*
1499  * Add FOR UPDATE/SHARE if appropriate. We apply locking during the
1500  * initial row fetch, rather than later on as is done for local
1501  * tables. The extra roundtrips involved in trying to duplicate the
1502  * local semantics exactly don't seem worthwhile (see also comments
1503  * for RowMarkType).
1504  *
1505  * Note: because we actually run the query as a cursor, this assumes
1506  * that DECLARE CURSOR ... FOR UPDATE is supported, which it isn't
1507  * before 8.3.
1508  */
1509  if (bms_is_member(relid, root->all_result_relids) &&
1510  (root->parse->commandType == CMD_UPDATE ||
1511  root->parse->commandType == CMD_DELETE))
1512  {
1513  /* Relation is UPDATE/DELETE target, so use FOR UPDATE */
1514  appendStringInfoString(buf, " FOR UPDATE");
1515 
1516  /* Add the relation alias if we are here for a join relation */
1517  if (IS_JOIN_REL(rel))
1518  appendStringInfo(buf, " OF %s%d", REL_ALIAS_PREFIX, relid);
1519  }
1520  else
1521  {
1522  PlanRowMark *rc = get_plan_rowmark(root->rowMarks, relid);
1523 
1524  if (rc)
1525  {
1526  /*
1527  * Relation is specified as a FOR UPDATE/SHARE target, so
1528  * handle that. (But we could also see LCS_NONE, meaning this
1529  * isn't a target relation after all.)
1530  *
1531  * For now, just ignore any [NO] KEY specification, since (a)
1532  * it's not clear what that means for a remote table that we
1533  * don't have complete information about, and (b) it wouldn't
1534  * work anyway on older remote servers. Likewise, we don't
1535  * worry about NOWAIT.
1536  */
1537  switch (rc->strength)
1538  {
1539  case LCS_NONE:
1540  /* No locking needed */
1541  break;
1542  case LCS_FORKEYSHARE:
1543  case LCS_FORSHARE:
1544  appendStringInfoString(buf, " FOR SHARE");
1545  break;
1546  case LCS_FORNOKEYUPDATE:
1547  case LCS_FORUPDATE:
1548  appendStringInfoString(buf, " FOR UPDATE");
1549  break;
1550  }
1551 
1552  /* Add the relation alias if we are here for a join relation */
1553  if (bms_membership(rel->relids) == BMS_MULTIPLE &&
1554  rc->strength != LCS_NONE)
1555  appendStringInfo(buf, " OF %s%d", REL_ALIAS_PREFIX, relid);
1556  }
1557  }
1558  }
1559 }
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1306
@ LCS_FORUPDATE
Definition: lockoptions.h:27
@ LCS_NONE
Definition: lockoptions.h:23
@ LCS_FORSHARE
Definition: lockoptions.h:25
@ LCS_FORKEYSHARE
Definition: lockoptions.h:24
@ LCS_FORNOKEYUPDATE
Definition: lockoptions.h:26
@ CMD_DELETE
Definition: nodes.h:268
@ CMD_UPDATE
Definition: nodes.h:266
while(p+4<=pend)
PlanRowMark * get_plan_rowmark(List *rowmarks, Index rtindex)
Definition: preptlist.c:485
Relids lower_subquery_rels
Definition: postgres_fdw.h:119
LockClauseStrength strength
Definition: plannodes.h:1385

References appendStringInfo(), appendStringInfoString(), bms_is_member(), bms_membership(), BMS_MULTIPLE, bms_next_member(), buf, CMD_DELETE, CMD_UPDATE, context, get_plan_rowmark(), IS_JOIN_REL, LCS_FORKEYSHARE, LCS_FORNOKEYUPDATE, LCS_FORSHARE, LCS_FORUPDATE, LCS_NONE, PgFdwRelationInfo::lower_subquery_rels, REL_ALIAS_PREFIX, RelOptInfo::relids, root, PlanRowMark::strength, and while().

Referenced by deparseSelectStmtForRel().

◆ deparseNullTest()

static void deparseNullTest ( NullTest node,
deparse_expr_cxt context 
)
static

Definition at line 3548 of file deparse.c.

3549 {
3550  StringInfo buf = context->buf;
3551 
3552  appendStringInfoChar(buf, '(');
3553  deparseExpr(node->arg, context);
3554 
3555  /*
3556  * For scalar inputs, we prefer to print as IS [NOT] NULL, which is
3557  * shorter and traditional. If it's a rowtype input but we're applying a
3558  * scalar test, must print IS [NOT] DISTINCT FROM NULL to be semantically
3559  * correct.
3560  */
3561  if (node->argisrow || !type_is_rowtype(exprType((Node *) node->arg)))
3562  {
3563  if (node->nulltesttype == IS_NULL)
3564  appendStringInfoString(buf, " IS NULL)");
3565  else
3566  appendStringInfoString(buf, " IS NOT NULL)");
3567  }
3568  else
3569  {
3570  if (node->nulltesttype == IS_NULL)
3571  appendStringInfoString(buf, " IS NOT DISTINCT FROM NULL)");
3572  else
3573  appendStringInfoString(buf, " IS DISTINCT FROM NULL)");
3574  }
3575 }
bool type_is_rowtype(Oid typid)
Definition: lsyscache.c:2633
@ IS_NULL
Definition: primnodes.h:1837
NullTestType nulltesttype
Definition: primnodes.h:1844
Expr * arg
Definition: primnodes.h:1843

References appendStringInfoChar(), appendStringInfoString(), NullTest::arg, buf, context, deparseExpr(), exprType(), IS_NULL, NullTest::nulltesttype, and type_is_rowtype().

Referenced by deparseExpr().

◆ deparseOperatorName()

static void deparseOperatorName ( StringInfo  buf,
Form_pg_operator  opform 
)
static

Definition at line 3406 of file deparse.c.

3407 {
3408  char *opname;
3409 
3410  /* opname is not a SQL identifier, so we should not quote it. */
3411  opname = NameStr(opform->oprname);
3412 
3413  /* Print schema name only if it's not pg_catalog */
3414  if (opform->oprnamespace != PG_CATALOG_NAMESPACE)
3415  {
3416  const char *opnspname;
3417 
3418  opnspname = get_namespace_name(opform->oprnamespace);
3419  /* Print fully qualified operator name. */
3420  appendStringInfo(buf, "OPERATOR(%s.%s)",
3421  quote_identifier(opnspname), opname);
3422  }
3423  else
3424  {
3425  /* Just print operator name. */
3426  appendStringInfoString(buf, opname);
3427  }
3428 }

References appendStringInfo(), appendStringInfoString(), buf, get_namespace_name(), NameStr, and quote_identifier().

Referenced by appendOrderBySuffix(), deparseOpExpr(), and deparseScalarArrayOpExpr().

◆ deparseOpExpr()

static void deparseOpExpr ( OpExpr node,
deparse_expr_cxt context 
)
static

Definition at line 3276 of file deparse.c.

3277 {
3278  StringInfo buf = context->buf;
3279  HeapTuple tuple;
3280  Form_pg_operator form;
3281  Expr *right;
3282  bool canSuppressRightConstCast = false;
3283  char oprkind;
3284 
3285  /* Retrieve information about the operator from system catalog. */
3286  tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(node->opno));
3287  if (!HeapTupleIsValid(tuple))
3288  elog(ERROR, "cache lookup failed for operator %u", node->opno);
3289  form = (Form_pg_operator) GETSTRUCT(tuple);
3290  oprkind = form->oprkind;
3291 
3292  /* Sanity check. */
3293  Assert((oprkind == 'l' && list_length(node->args) == 1) ||
3294  (oprkind == 'b' && list_length(node->args) == 2));
3295 
3296  right = llast(node->args);
3297 
3298  /* Always parenthesize the expression. */
3299  appendStringInfoChar(buf, '(');
3300 
3301  /* Deparse left operand, if any. */
3302  if (oprkind == 'b')
3303  {
3304  Expr *left = linitial(node->args);
3305  Oid leftType = exprType((Node *) left);
3306  Oid rightType = exprType((Node *) right);
3307  bool canSuppressLeftConstCast = false;
3308 
3309  /*
3310  * When considering a binary operator, if one operand is a Const that
3311  * can be printed as a bare string literal or NULL (i.e., it will look
3312  * like type UNKNOWN to the remote parser), the Const normally
3313  * receives an explicit cast to the operator's input type. However,
3314  * in Const-to-Var comparisons where both operands are of the same
3315  * type, we prefer to suppress the explicit cast, leaving the Const's
3316  * type resolution up to the remote parser. The remote's resolution
3317  * heuristic will assume that an unknown input type being compared to
3318  * a known input type is of that known type as well.
3319  *
3320  * This hack allows some cases to succeed where a remote column is
3321  * declared with a different type in the local (foreign) table. By
3322  * emitting "foreigncol = 'foo'" not "foreigncol = 'foo'::text" or the
3323  * like, we allow the remote parser to pick an "=" operator that's
3324  * compatible with whatever type the remote column really is, such as
3325  * an enum.
3326  *
3327  * We allow cast suppression to happen only when the other operand is
3328  * a plain foreign Var. Although the remote's unknown-type heuristic
3329  * would apply to other cases just as well, we would be taking a
3330  * bigger risk that the inferred type is something unexpected. With
3331  * this restriction, if anything goes wrong it's the user's fault for
3332  * not declaring the local column with the same type as the remote
3333  * column.
3334  */
3335  if (leftType == rightType)
3336  {
3337  if (IsA(left, Const))
3338  canSuppressLeftConstCast = isPlainForeignVar(right, context);
3339  else if (IsA(right, Const))
3340  canSuppressRightConstCast = isPlainForeignVar(left, context);
3341  }
3342 
3343  if (canSuppressLeftConstCast)
3344  deparseConst((Const *) left, context, -2);
3345  else
3346  deparseExpr(left, context);
3347 
3348  appendStringInfoChar(buf, ' ');
3349  }
3350 
3351  /* Deparse operator name. */
3352  deparseOperatorName(buf, form);
3353 
3354  /* Deparse right operand. */
3355  appendStringInfoChar(buf, ' ');
3356 
3357  if (canSuppressRightConstCast)
3358  deparseConst((Const *) right, context, -2);
3359  else
3360  deparseExpr(right, context);
3361 
3362  appendStringInfoChar(buf, ')');
3363 
3364  ReleaseSysCache(tuple);
3365 }
static bool isPlainForeignVar(Expr *node, deparse_expr_cxt *context)
Definition: deparse.c:3371
#define llast(l)
Definition: pg_list.h:198
Oid opno
Definition: primnodes.h:774

References appendStringInfoChar(), OpExpr::args, Assert(), buf, context, deparseConst(), deparseExpr(), deparseOperatorName(), elog, ERROR, exprType(), GETSTRUCT, HeapTupleIsValid, IsA, isPlainForeignVar(), linitial, list_length(), llast, ObjectIdGetDatum(), OpExpr::opno, ReleaseSysCache(), and SearchSysCache1().

Referenced by deparseExpr().

◆ deparseParam()

static void deparseParam ( Param node,
deparse_expr_cxt context 
)
static

Definition at line 3134 of file deparse.c.

3135 {
3136  if (context->params_list)
3137  {
3138  int pindex = 0;
3139  ListCell *lc;
3140 
3141  /* find its index in params_list */
3142  foreach(lc, *context->params_list)
3143  {
3144  pindex++;
3145  if (equal(node, (Node *) lfirst(lc)))
3146  break;
3147  }
3148  if (lc == NULL)
3149  {
3150  /* not in list, so add it */
3151  pindex++;
3152  *context->params_list = lappend(*context->params_list, node);
3153  }
3154 
3155  printRemoteParam(pindex, node->paramtype, node->paramtypmod, context);
3156  }
3157  else
3158  {
3159  printRemotePlaceholder(node->paramtype, node->paramtypmod, context);
3160  }
3161 }
static void printRemoteParam(int paramindex, Oid paramtype, int32 paramtypmod, deparse_expr_cxt *context)
Definition: deparse.c:3823
static void printRemotePlaceholder(Oid paramtype, int32 paramtypmod, deparse_expr_cxt *context)
Definition: deparse.c:3849
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
Oid paramtype
Definition: primnodes.h:364

References context, equal(), lappend(), lfirst, Param::paramtype, printRemoteParam(), and printRemotePlaceholder().

Referenced by deparseExpr().

◆ deparseRangeTblRef()

static void deparseRangeTblRef ( StringInfo  buf,
PlannerInfo root,
RelOptInfo foreignrel,
bool  make_subquery,
Index  ignore_rel,
List **  ignore_conds,
List **  additional_conds,
List **  params_list 
)
static

Definition at line 2008 of file deparse.c.

2011 {
2012  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
2013 
2014  /* Should only be called in these cases. */
2015  Assert(IS_SIMPLE_REL(foreignrel) || IS_JOIN_REL(foreignrel));
2016 
2017  Assert(fpinfo->local_conds == NIL);
2018 
2019  /* If make_subquery is true, deparse the relation as a subquery. */
2020  if (make_subquery)
2021  {
2022  List *retrieved_attrs;
2023  int ncols;
2024 
2025  /*
2026  * The given relation shouldn't contain the target relation, because
2027  * this should only happen for input relations for a full join, and
2028  * such relations can never contain an UPDATE/DELETE target.
2029  */
2030  Assert(ignore_rel == 0 ||
2031  !bms_is_member(ignore_rel, foreignrel->relids));
2032 
2033  /* Deparse the subquery representing the relation. */
2034  appendStringInfoChar(buf, '(');
2035  deparseSelectStmtForRel(buf, root, foreignrel, NIL,
2036  fpinfo->remote_conds, NIL,
2037  false, false, true,
2038  &retrieved_attrs, params_list);
2039  appendStringInfoChar(buf, ')');
2040 
2041  /* Append the relation alias. */
2043  fpinfo->relation_index);
2044 
2045  /*
2046  * Append the column aliases if needed. Note that the subquery emits
2047  * expressions specified in the relation's reltarget (see
2048  * deparseSubqueryTargetList).
2049  */
2050  ncols = list_length(foreignrel->reltarget->exprs);
2051  if (ncols > 0)
2052  {
2053  int i;
2054 
2055  appendStringInfoChar(buf, '(');
2056  for (i = 1; i <= ncols; i++)
2057  {
2058  if (i > 1)
2059  appendStringInfoString(buf, ", ");
2060 
2062  }
2063  appendStringInfoChar(buf, ')');
2064  }
2065  }
2066  else
2067  deparseFromExprForRel(buf, root, foreignrel, true, ignore_rel,
2068  ignore_conds, additional_conds,
2069  params_list);
2070 }
#define SUBQUERY_REL_ALIAS_PREFIX
Definition: deparse.c:114
void deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *rel, List *tlist, List *remote_conds, List *pathkeys, bool has_final_sort, bool has_limit, bool is_subquery, List **retrieved_attrs, List **params_list)
Definition: deparse.c:1233
#define SUBQUERY_COL_ALIAS_PREFIX
Definition: deparse.c:115

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert(), bms_is_member(), buf, deparseFromExprForRel(), deparseSelectStmtForRel(), PathTarget::exprs, i, IS_JOIN_REL, IS_SIMPLE_REL, list_length(), PgFdwRelationInfo::local_conds, NIL, PgFdwRelationInfo::relation_index, RelOptInfo::relids, RelOptInfo::reltarget, PgFdwRelationInfo::remote_conds, root, SUBQUERY_COL_ALIAS_PREFIX, and SUBQUERY_REL_ALIAS_PREFIX.

Referenced by deparseFromExprForRel().

◆ deparseRelabelType()

static void deparseRelabelType ( RelabelType node,
deparse_expr_cxt context 
)
static

Definition at line 3497 of file deparse.c.

3498 {
3499  deparseExpr(node->arg, context);
3500  if (node->relabelformat != COERCE_IMPLICIT_CAST)
3501  appendStringInfo(context->buf, "::%s",
3503  node->resulttypmod));
3504 }
Oid resulttype
Definition: primnodes.h:1141
Expr * arg
Definition: primnodes.h:1140

References appendStringInfo(), RelabelType::arg, COERCE_IMPLICIT_CAST, context, deparse_type_name(), deparseExpr(), and RelabelType::resulttype.

Referenced by deparseExpr().

◆ deparseRelation()

static void deparseRelation ( StringInfo  buf,
Relation  rel 
)
static

Definition at line 2809 of file deparse.c.

2810 {
2811  ForeignTable *table;
2812  const char *nspname = NULL;
2813  const char *relname = NULL;
2814  ListCell *lc;
2815 
2816  /* obtain additional catalog information. */
2817  table = GetForeignTable(RelationGetRelid(rel));
2818 
2819  /*
2820  * Use value of FDW options if any, instead of the name of object itself.
2821  */
2822  foreach(lc, table->options)
2823  {
2824  DefElem *def = (DefElem *) lfirst(lc);
2825 
2826  if (strcmp(def->defname, "schema_name") == 0)
2827  nspname = defGetString(def);
2828  else if (strcmp(def->defname, "table_name") == 0)
2829  relname = defGetString(def);
2830  }
2831 
2832  /*
2833  * Note: we could skip printing the schema name if it's pg_catalog, but
2834  * that doesn't seem worth the trouble.
2835  */
2836  if (nspname == NULL)
2837  nspname = get_namespace_name(RelationGetNamespace(rel));
2838  if (relname == NULL)
2840 
2841  appendStringInfo(buf, "%s.%s",
2843 }
ForeignTable * GetForeignTable(Oid relid)
Definition: foreign.c:253
#define RelationGetRelationName(relation)
Definition: rel.h:541
#define RelationGetNamespace(relation)
Definition: rel.h:548
List * options
Definition: foreign.h:57

References appendStringInfo(), buf, defGetString(), DefElem::defname, get_namespace_name(), GetForeignTable(), lfirst, ForeignTable::options, quote_identifier(), RelationGetNamespace, RelationGetRelationName, RelationGetRelid, and relname.

Referenced by deparseAnalyzeInfoSql(), deparseAnalyzeSizeSql(), deparseAnalyzeSql(), deparseDeleteSql(), deparseDirectDeleteSql(), deparseDirectUpdateSql(), deparseFromExprForRel(), deparseInsertSql(), deparseTruncateSql(), and deparseUpdateSql().

◆ deparseReturningList()

static void deparseReturningList ( StringInfo  buf,
RangeTblEntry rte,
Index  rtindex,
Relation  rel,
bool  trig_after_row,
List withCheckOptionList,
List returningList,
List **  retrieved_attrs 
)
static

Definition at line 2442 of file deparse.c.

2448 {
2449  Bitmapset *attrs_used = NULL;
2450 
2451  if (trig_after_row)
2452  {
2453  /* whole-row reference acquires all non-system columns */
2454  attrs_used =
2456  }
2457 
2458  if (withCheckOptionList != NIL)
2459  {
2460  /*
2461  * We need the attrs, non-system and system, mentioned in the local
2462  * query's WITH CHECK OPTION list.
2463  *
2464  * Note: we do this to ensure that WCO constraints will be evaluated
2465  * on the data actually inserted/updated on the remote side, which
2466  * might differ from the data supplied by the core code, for example
2467  * as a result of remote triggers.
2468  */
2469  pull_varattnos((Node *) withCheckOptionList, rtindex,
2470  &attrs_used);
2471  }
2472 
2473  if (returningList != NIL)
2474  {
2475  /*
2476  * We need the attrs, non-system and system, mentioned in the local
2477  * query's RETURNING list.
2478  */
2479  pull_varattnos((Node *) returningList, rtindex,
2480  &attrs_used);
2481  }
2482 
2483  if (attrs_used != NULL)
2484  deparseTargetList(buf, rte, rtindex, rel, true, attrs_used, false,
2485  retrieved_attrs);
2486  else
2487  *retrieved_attrs = NIL;
2488 }
Bitmapset * bms_make_singleton(int x)
Definition: bitmapset.c:216
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
Definition: var.c:291

References bms_make_singleton(), buf, deparseTargetList(), FirstLowInvalidHeapAttributeNumber, NIL, and pull_varattnos().

Referenced by deparseDeleteSql(), deparseDirectDeleteSql(), deparseDirectUpdateSql(), deparseInsertSql(), and deparseUpdateSql().

◆ deparseScalarArrayOpExpr()

static void deparseScalarArrayOpExpr ( ScalarArrayOpExpr node,
deparse_expr_cxt context 
)
static

Definition at line 3452 of file deparse.c.

3453 {
3454  StringInfo buf = context->buf;
3455  HeapTuple tuple;
3456  Form_pg_operator form;
3457  Expr *arg1;
3458  Expr *arg2;
3459 
3460  /* Retrieve information about the operator from system catalog. */
3461  tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(node->opno));
3462  if (!HeapTupleIsValid(tuple))
3463  elog(ERROR, "cache lookup failed for operator %u", node->opno);
3464  form = (Form_pg_operator) GETSTRUCT(tuple);
3465 
3466  /* Sanity check. */
3467  Assert(list_length(node->args) == 2);
3468 
3469  /* Always parenthesize the expression. */
3470  appendStringInfoChar(buf, '(');
3471 
3472  /* Deparse left operand. */
3473  arg1 = linitial(node->args);
3474  deparseExpr(arg1, context);
3475  appendStringInfoChar(buf, ' ');
3476 
3477  /* Deparse operator name plus decoration. */
3478  deparseOperatorName(buf, form);
3479  appendStringInfo(buf, " %s (", node->useOr ? "ANY" : "ALL");
3480 
3481  /* Deparse right operand. */
3482  arg2 = lsecond(node->args);
3483  deparseExpr(arg2, context);
3484 
3485  appendStringInfoChar(buf, ')');
3486 
3487  /* Always parenthesize the expression. */
3488  appendStringInfoChar(buf, ')');
3489 
3490  ReleaseSysCache(tuple);
3491 }

References appendStringInfo(), appendStringInfoChar(), ScalarArrayOpExpr::args, Assert(), buf, context, deparseExpr(), deparseOperatorName(), elog, ERROR, GETSTRUCT, HeapTupleIsValid, linitial, list_length(), lsecond, ObjectIdGetDatum(), ScalarArrayOpExpr::opno, ReleaseSysCache(), SearchSysCache1(), and ScalarArrayOpExpr::useOr.

Referenced by deparseExpr().

◆ deparseSelectSql()

static void deparseSelectSql ( List tlist,
bool  is_subquery,
List **  retrieved_attrs,
deparse_expr_cxt context 
)
static

Definition at line 1315 of file deparse.c.

1317 {
1318  StringInfo buf = context->buf;
1319  RelOptInfo *foreignrel = context->foreignrel;
1320  PlannerInfo *root = context->root;
1321  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
1322 
1323  /*
1324  * Construct SELECT list
1325  */
1326  appendStringInfoString(buf, "SELECT ");
1327 
1328  if (is_subquery)
1329  {
1330  /*
1331  * For a relation that is deparsed as a subquery, emit expressions
1332  * specified in the relation's reltarget. Note that since this is for
1333  * the subquery, no need to care about *retrieved_attrs.
1334  */
1336  }
1337  else if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
1338  {
1339  /*
1340  * For a join or upper relation the input tlist gives the list of
1341  * columns required to be fetched from the foreign server.
1342  */
1343  deparseExplicitTargetList(tlist, false, retrieved_attrs, context);
1344  }
1345  else
1346  {
1347  /*
1348  * For a base relation fpinfo->attrs_used gives the list of columns
1349  * required to be fetched from the foreign server.
1350  */
1351  RangeTblEntry *rte = planner_rt_fetch(foreignrel->relid, root);
1352 
1353  /*
1354  * Core code already has some lock on each rel being planned, so we
1355  * can use NoLock here.
1356  */
1357  Relation rel = table_open(rte->relid, NoLock);
1358 
1359  deparseTargetList(buf, rte, foreignrel->relid, rel, false,
1360  fpinfo->attrs_used, false, retrieved_attrs);
1361  table_close(rel, NoLock);
1362  }
1363 }
static void deparseSubqueryTargetList(deparse_expr_cxt *context)
Definition: deparse.c:1717
Bitmapset * attrs_used
Definition: postgres_fdw.h:50

References appendStringInfoString(), PgFdwRelationInfo::attrs_used, buf, context, deparseExplicitTargetList(), deparseSubqueryTargetList(), deparseTargetList(), IS_JOIN_REL, IS_UPPER_REL, NoLock, planner_rt_fetch, RangeTblEntry::relid, RelOptInfo::relid, root, table_close(), and table_open().

Referenced by deparseSelectStmtForRel().

◆ deparseSelectStmtForRel()

void deparseSelectStmtForRel ( StringInfo  buf,
PlannerInfo root,
RelOptInfo rel,
List tlist,
List remote_conds,
List pathkeys,
bool  has_final_sort,
bool  has_limit,
bool  is_subquery,
List **  retrieved_attrs,
List **  params_list 
)

Definition at line 1233 of file deparse.c.

1237 {
1239  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private;
1240  List *quals;
1241 
1242  /*
1243  * We handle relations for foreign tables, joins between those and upper
1244  * relations.
1245  */
1246  Assert(IS_JOIN_REL(rel) || IS_SIMPLE_REL(rel) || IS_UPPER_REL(rel));
1247 
1248  /* Fill portions of context common to upper, join and base relation */
1249  context.buf = buf;
1250  context.root = root;
1251  context.foreignrel = rel;
1252  context.scanrel = IS_UPPER_REL(rel) ? fpinfo->outerrel : rel;
1253  context.params_list = params_list;
1254 
1255  /* Construct SELECT clause */
1256  deparseSelectSql(tlist, is_subquery, retrieved_attrs, &context);
1257 
1258  /*
1259  * For upper relations, the WHERE clause is built from the remote
1260  * conditions of the underlying scan relation; otherwise, we can use the
1261  * supplied list of remote conditions directly.
1262  */
1263  if (IS_UPPER_REL(rel))
1264  {
1265  PgFdwRelationInfo *ofpinfo;
1266 
1267  ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
1268  quals = ofpinfo->remote_conds;
1269  }
1270  else
1271  quals = remote_conds;
1272 
1273  /* Construct FROM and WHERE clauses */
1274  deparseFromExpr(quals, &context);
1275 
1276  if (IS_UPPER_REL(rel))
1277  {
1278  /* Append GROUP BY clause */
1279  appendGroupByClause(tlist, &context);
1280 
1281  /* Append HAVING clause */
1282  if (remote_conds)
1283  {
1284  appendStringInfoString(buf, " HAVING ");
1285  appendConditions(remote_conds, &context);
1286  }
1287  }
1288 
1289  /* Add ORDER BY clause if we found any useful pathkeys */
1290  if (pathkeys)
1291  appendOrderByClause(pathkeys, has_final_sort, &context);
1292 
1293  /* Add LIMIT clause if necessary */
1294  if (has_limit)
1296 
1297  /* Add any necessary FOR UPDATE/SHARE. */
1299 }
static void appendGroupByClause(List *tlist, deparse_expr_cxt *context)
Definition: deparse.c:3862
static void deparseFromExpr(List *quals, deparse_expr_cxt *context)
Definition: deparse.c:1373
static void deparseLockingClause(deparse_expr_cxt *context)
Definition: deparse.c:1481
static void appendOrderByClause(List *pathkeys, bool has_final_sort, deparse_expr_cxt *context)
Definition: deparse.c:3910
static void appendLimitClause(deparse_expr_cxt *context)
Definition: deparse.c:4004
static void deparseSelectSql(List *tlist, bool is_subquery, List **retrieved_attrs, deparse_expr_cxt *context)
Definition: deparse.c:1315

References appendConditions(), appendGroupByClause(), appendLimitClause(), appendOrderByClause(), appendStringInfoString(), Assert(), buf, context, deparseFromExpr(), deparseLockingClause(), deparseSelectSql(), IS_JOIN_REL, IS_SIMPLE_REL, IS_UPPER_REL, PgFdwRelationInfo::outerrel, PgFdwRelationInfo::remote_conds, and root.

Referenced by deparseRangeTblRef(), estimate_path_cost_size(), and postgresGetForeignPlan().

◆ deparseSortGroupClause()

static Node * deparseSortGroupClause ( Index  ref,
List tlist,
bool  force_colno,
deparse_expr_cxt context 
)
static

Definition at line 4067 of file deparse.c.

4069 {
4070  StringInfo buf = context->buf;
4071  TargetEntry *tle;
4072  Expr *expr;
4073 
4074  tle = get_sortgroupref_tle(ref, tlist);
4075  expr = tle->expr;
4076 
4077  if (force_colno)
4078  {
4079  /* Use column-number form when requested by caller. */
4080  Assert(!tle->resjunk);
4081  appendStringInfo(buf, "%d", tle->resno);
4082  }
4083  else if (expr && IsA(expr, Const))
4084  {
4085  /*
4086  * Force a typecast here so that we don't emit something like "GROUP
4087  * BY 2", which will be misconstrued as a column position rather than
4088  * a constant.
4089  */
4090  deparseConst((Const *) expr, context, 1);
4091  }
4092  else if (!expr || IsA(expr, Var))
4093  deparseExpr(expr, context);
4094  else
4095  {
4096  /* Always parenthesize the expression. */
4097  appendStringInfoChar(buf, '(');
4098  deparseExpr(expr, context);
4099  appendStringInfoChar(buf, ')');
4100  }
4101 
4102  return (Node *) expr;
4103 }
AttrNumber resno
Definition: primnodes.h:2067
TargetEntry * get_sortgroupref_tle(Index sortref, List *targetList)
Definition: tlist.c:345

References appendStringInfo(), appendStringInfoChar(), Assert(), buf, context, deparseConst(), deparseExpr(), TargetEntry::expr, get_sortgroupref_tle(), IsA, and TargetEntry::resno.

Referenced by appendAggOrderBy(), and appendGroupByClause().

◆ deparseStringLiteral()

void deparseStringLiteral ( StringInfo  buf,
const char *  val 
)

Definition at line 2849 of file deparse.c.

2850 {
2851  const char *valptr;
2852 
2853  /*
2854  * Rather than making assumptions about the remote server's value of
2855  * standard_conforming_strings, always use E'foo' syntax if there are any
2856  * backslashes. This will fail on remote servers before 8.1, but those
2857  * are long out of support.
2858  */
2859  if (strchr(val, '\\') != NULL)
2861  appendStringInfoChar(buf, '\'');
2862  for (valptr = val; *valptr; valptr++)
2863  {
2864  char ch = *valptr;
2865 
2866  if (SQL_STR_DOUBLE(ch, true))
2869  }
2870  appendStringInfoChar(buf, '\'');
2871 }
#define ESCAPE_STRING_SYNTAX
Definition: c.h:1153
#define SQL_STR_DOUBLE(ch, escape_backslash)
Definition: c.h:1150
long val
Definition: informix.c:670

References appendStringInfoChar(), buf, ESCAPE_STRING_SYNTAX, SQL_STR_DOUBLE, and val.

Referenced by deparseAnalyzeInfoSql(), deparseAnalyzeSizeSql(), deparseConst(), and postgresImportForeignSchema().

◆ deparseSubqueryTargetList()

static void deparseSubqueryTargetList ( deparse_expr_cxt context)
static

Definition at line 1717 of file deparse.c.

1718 {
1719  StringInfo buf = context->buf;
1720  RelOptInfo *foreignrel = context->foreignrel;
1721  bool first;
1722  ListCell *lc;
1723 
1724  /* Should only be called in these cases. */
1725  Assert(IS_SIMPLE_REL(foreignrel) || IS_JOIN_REL(foreignrel));
1726 
1727  first = true;
1728  foreach(lc, foreignrel->reltarget->exprs)
1729  {
1730  Node *node = (Node *) lfirst(lc);
1731 
1732  if (!first)
1733  appendStringInfoString(buf, ", ");
1734  first = false;
1735 
1736  deparseExpr((Expr *) node, context);
1737  }
1738 
1739  /* Don't generate bad syntax if no expressions */
1740  if (first)
1741  appendStringInfoString(buf, "NULL");
1742 }

References appendStringInfoString(), Assert(), buf, context, deparseExpr(), PathTarget::exprs, IS_JOIN_REL, IS_SIMPLE_REL, lfirst, and RelOptInfo::reltarget.

Referenced by deparseSelectSql().

◆ deparseSubscriptingRef()

static void deparseSubscriptingRef ( SubscriptingRef node,
deparse_expr_cxt context 
)
static

Definition at line 3167 of file deparse.c.

3168 {
3169  StringInfo buf = context->buf;
3170  ListCell *lowlist_item;
3171  ListCell *uplist_item;
3172 
3173  /* Always parenthesize the expression. */
3174  appendStringInfoChar(buf, '(');
3175 
3176  /*
3177  * Deparse referenced array expression first. If that expression includes
3178  * a cast, we have to parenthesize to prevent the array subscript from
3179  * being taken as typename decoration. We can avoid that in the typical
3180  * case of subscripting a Var, but otherwise do it.
3181  */
3182  if (IsA(node->refexpr, Var))
3183  deparseExpr(node->refexpr, context);
3184  else
3185  {
3186  appendStringInfoChar(buf, '(');
3187  deparseExpr(node->refexpr, context);
3188  appendStringInfoChar(buf, ')');
3189  }
3190 
3191  /* Deparse subscript expressions. */
3192  lowlist_item = list_head(node->reflowerindexpr); /* could be NULL */
3193  foreach(uplist_item, node->refupperindexpr)
3194  {
3195  appendStringInfoChar(buf, '[');
3196  if (lowlist_item)
3197  {
3198  deparseExpr(lfirst(lowlist_item), context);
3199  appendStringInfoChar(buf, ':');
3200  lowlist_item = lnext(node->reflowerindexpr, lowlist_item);
3201  }
3202  deparseExpr(lfirst(uplist_item), context);
3203  appendStringInfoChar(buf, ']');
3204  }
3205 
3206  appendStringInfoChar(buf, ')');
3207 }
static ListCell * list_head(const List *l)
Definition: pg_list.h:128
List * refupperindexpr
Definition: primnodes.h:649
Expr * refexpr
Definition: primnodes.h:657
List * reflowerindexpr
Definition: primnodes.h:655

References appendStringInfoChar(), buf, context, deparseExpr(), IsA, lfirst, list_head(), lnext(), SubscriptingRef::refexpr, SubscriptingRef::reflowerindexpr, and SubscriptingRef::refupperindexpr.

Referenced by deparseExpr().

◆ deparseTargetList()

static void deparseTargetList ( StringInfo  buf,
RangeTblEntry rte,
Index  rtindex,
Relation  rel,
bool  is_returning,
Bitmapset attrs_used,
bool  qualify_col,
List **  retrieved_attrs 
)
static

Definition at line 1405 of file deparse.c.

1413 {
1414  TupleDesc tupdesc = RelationGetDescr(rel);
1415  bool have_wholerow;
1416  bool first;
1417  int i;
1418 
1419  *retrieved_attrs = NIL;
1420 
1421  /* If there's a whole-row reference, we'll need all the columns. */
1423  attrs_used);
1424 
1425  first = true;
1426  for (i = 1; i <= tupdesc->natts; i++)
1427  {
1428  Form_pg_attribute attr = TupleDescAttr(tupdesc, i - 1);
1429 
1430  /* Ignore dropped attributes. */
1431  if (attr->attisdropped)
1432  continue;
1433 
1434  if (have_wholerow ||
1436  attrs_used))
1437  {
1438  if (!first)
1439  appendStringInfoString(buf, ", ");
1440  else if (is_returning)
1441  appendStringInfoString(buf, " RETURNING ");
1442  first = false;
1443 
1444  deparseColumnRef(buf, rtindex, i, rte, qualify_col);
1445 
1446  *retrieved_attrs = lappend_int(*retrieved_attrs, i);
1447  }
1448  }
1449 
1450  /*
1451  * Add ctid if needed. We currently don't support retrieving any other
1452  * system columns.
1453  */
1455  attrs_used))
1456  {
1457  if (!first)
1458  appendStringInfoString(buf, ", ");
1459  else if (is_returning)
1460  appendStringInfoString(buf, " RETURNING ");
1461  first = false;
1462 
1463  if (qualify_col)
1464  ADD_REL_QUALIFIER(buf, rtindex);
1465  appendStringInfoString(buf, "ctid");
1466 
1467  *retrieved_attrs = lappend_int(*retrieved_attrs,
1469  }
1470 
1471  /* Don't generate bad syntax if no undropped columns */
1472  if (first && !is_returning)
1473  appendStringInfoString(buf, "NULL");
1474 }

References ADD_REL_QUALIFIER, appendStringInfoString(), bms_is_member(), buf, deparseColumnRef(), FirstLowInvalidHeapAttributeNumber, i, lappend_int(), TupleDescData::natts, NIL, RelationGetDescr, SelfItemPointerAttributeNumber, and TupleDescAttr.

Referenced by deparseColumnRef(), deparseReturningList(), and deparseSelectSql().

◆ deparseTruncateSql()

void deparseTruncateSql ( StringInfo  buf,
List rels,
DropBehavior  behavior,
bool  restart_seqs 
)

Definition at line 2646 of file deparse.c.

2650 {
2651  ListCell *cell;
2652 
2653  appendStringInfoString(buf, "TRUNCATE ");
2654 
2655  foreach(cell, rels)
2656  {
2657  Relation rel = lfirst(cell);
2658 
2659  if (cell != list_head(rels))
2660  appendStringInfoString(buf, ", ");
2661 
2662  deparseRelation(buf, rel);
2663  }
2664 
2665  appendStringInfo(buf, " %s IDENTITY",
2666  restart_seqs ? "RESTART" : "CONTINUE");
2667 
2668  if (behavior == DROP_RESTRICT)
2669  appendStringInfoString(buf, " RESTRICT");
2670  else if (behavior == DROP_CASCADE)
2671  appendStringInfoString(buf, " CASCADE");
2672 }
@ DROP_CASCADE
Definition: parsenodes.h:2253
@ DROP_RESTRICT
Definition: parsenodes.h:2252

References appendStringInfo(), appendStringInfoString(), buf, deparseRelation(), DROP_CASCADE, DROP_RESTRICT, lfirst, and list_head().

Referenced by postgresExecForeignTruncate().

◆ deparseUpdateSql()

void deparseUpdateSql ( StringInfo  buf,
RangeTblEntry rte,
Index  rtindex,
Relation  rel,
List targetAttrs,
List withCheckOptionList,
List returningList,
List **  retrieved_attrs 
)

Definition at line 2216 of file deparse.c.

2221 {
2222  TupleDesc tupdesc = RelationGetDescr(rel);
2223  AttrNumber pindex;
2224  bool first;
2225  ListCell *lc;
2226 
2227  appendStringInfoString(buf, "UPDATE ");
2228  deparseRelation(buf, rel);
2229  appendStringInfoString(buf, " SET ");
2230 
2231  pindex = 2; /* ctid is always the first param */
2232  first = true;
2233  foreach(lc, targetAttrs)
2234  {
2235  int attnum = lfirst_int(lc);
2236  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
2237 
2238  if (!first)
2239  appendStringInfoString(buf, ", ");
2240  first = false;
2241 
2242  deparseColumnRef(buf, rtindex, attnum, rte, false);
2243  if (attr->attgenerated)
2244  appendStringInfoString(buf, " = DEFAULT");
2245  else
2246  {
2247  appendStringInfo(buf, " = $%d", pindex);
2248  pindex++;
2249  }
2250  }
2251  appendStringInfoString(buf, " WHERE ctid = $1");
2252 
2253  deparseReturningList(buf, rte, rtindex, rel,
2254  rel->trigdesc && rel->trigdesc->trig_update_after_row,
2255  withCheckOptionList, returningList, retrieved_attrs);
2256 }
bool trig_update_after_row
Definition: reltrigger.h:62

References appendStringInfo(), appendStringInfoString(), attnum, buf, deparseColumnRef(), deparseRelation(), deparseReturningList(), lfirst_int, RelationGetDescr, TriggerDesc::trig_update_after_row, RelationData::trigdesc, and TupleDescAttr.

Referenced by postgresPlanForeignModify().

◆ deparseVar()

static void deparseVar ( Var node,
deparse_expr_cxt context 
)
static

Definition at line 2949 of file deparse.c.

2950 {
2951  Relids relids = context->scanrel->relids;
2952  int relno;
2953  int colno;
2954 
2955  /* Qualify columns when multiple relations are involved. */
2956  bool qualify_col = (bms_membership(relids) == BMS_MULTIPLE);
2957 
2958  /*
2959  * If the Var belongs to the foreign relation that is deparsed as a
2960  * subquery, use the relation and column alias to the Var provided by the
2961  * subquery, instead of the remote name.
2962  */
2963  if (is_subquery_var(node, context->scanrel, &relno, &colno))
2964  {
2965  appendStringInfo(context->buf, "%s%d.%s%d",
2967  SUBQUERY_COL_ALIAS_PREFIX, colno);
2968  return;
2969  }
2970 
2971  if (bms_is_member(node->varno, relids) && node->varlevelsup == 0)
2972  deparseColumnRef(context->buf, node->varno, node->varattno,
2973  planner_rt_fetch(node->varno, context->root),
2974  qualify_col);
2975  else
2976  {
2977  /* Treat like a Param */
2978  if (context->params_list)
2979  {
2980  int pindex = 0;
2981  ListCell *lc;
2982 
2983  /* find its index in params_list */
2984  foreach(lc, *context->params_list)
2985  {
2986  pindex++;
2987  if (equal(node, (Node *) lfirst(lc)))
2988  break;
2989  }
2990  if (lc == NULL)
2991  {
2992  /* not in list, so add it */
2993  pindex++;
2994  *context->params_list = lappend(*context->params_list, node);
2995  }
2996 
2997  printRemoteParam(pindex, node->vartype, node->vartypmod, context);
2998  }
2999  else
3000  {
3001  printRemotePlaceholder(node->vartype, node->vartypmod, context);
3002  }
3003  }
3004 }
static bool is_subquery_var(Var *node, RelOptInfo *foreignrel, int *relno, int *colno)
Definition: deparse.c:4112
AttrNumber varattno
Definition: primnodes.h:246
int varno
Definition: primnodes.h:241
Index varlevelsup
Definition: primnodes.h:266

References appendStringInfo(), bms_is_member(), bms_membership(), BMS_MULTIPLE, context, deparseColumnRef(), equal(), is_subquery_var(), lappend(), lfirst, planner_rt_fetch, printRemoteParam(), printRemotePlaceholder(), SUBQUERY_COL_ALIAS_PREFIX, SUBQUERY_REL_ALIAS_PREFIX, Var::varattno, Var::varlevelsup, and Var::varno.

Referenced by deparseExpr().

◆ foreign_expr_walker()

static bool foreign_expr_walker ( Node node,
foreign_glob_cxt glob_cxt,
foreign_loc_cxt outer_cxt,
foreign_loc_cxt case_arg_cxt 
)
static

Definition at line 312 of file deparse.c.

316 {
317  bool check_type = true;
318  PgFdwRelationInfo *fpinfo;
319  foreign_loc_cxt inner_cxt;
320  Oid collation;
322 
323  /* Need do nothing for empty subexpressions */
324  if (node == NULL)
325  return true;
326 
327  /* May need server info from baserel's fdw_private struct */
328  fpinfo = (PgFdwRelationInfo *) (glob_cxt->foreignrel->fdw_private);
329 
330  /* Set up inner_cxt for possible recursion to child nodes */
331  inner_cxt.collation = InvalidOid;
332  inner_cxt.state = FDW_COLLATE_NONE;
333 
334  switch (nodeTag(node))
335  {
336  case T_Var:
337  {
338  Var *var = (Var *) node;
339 
340  /*
341  * If the Var is from the foreign table, we consider its
342  * collation (if any) safe to use. If it is from another
343  * table, we treat its collation the same way as we would a
344  * Param's collation, ie it's not safe for it to have a
345  * non-default collation.
346  */
347  if (bms_is_member(var->varno, glob_cxt->relids) &&
348  var->varlevelsup == 0)
349  {
350  /* Var belongs to foreign table */
351 
352  /*
353  * System columns other than ctid should not be sent to
354  * the remote, since we don't make any effort to ensure
355  * that local and remote values match (tableoid, in
356  * particular, almost certainly doesn't match).
357  */
358  if (var->varattno < 0 &&
360  return false;
361 
362  /* Else check the collation */
363  collation = var->varcollid;
365  }
366  else
367  {
368  /* Var belongs to some other table */
369  collation = var->varcollid;
370  if (collation == InvalidOid ||
371  collation == DEFAULT_COLLATION_OID)
372  {
373  /*
374  * It's noncollatable, or it's safe to combine with a
375  * collatable foreign Var, so set state to NONE.
376  */
378  }
379  else
380  {
381  /*
382  * Do not fail right away, since the Var might appear
383  * in a collation-insensitive context.
384  */
386  }
387  }
388  }
389  break;
390  case T_Const:
391  {
392  Const *c = (Const *) node;
393 
394  /*
395  * Constants of regproc and related types can't be shipped
396  * unless the referenced object is shippable. But NULL's ok.
397  * (See also the related code in dependency.c.)
398  */
399  if (!c->constisnull)
400  {
401  switch (c->consttype)
402  {
403  case REGPROCOID:
404  case REGPROCEDUREOID:
405  if (!is_shippable(DatumGetObjectId(c->constvalue),
406  ProcedureRelationId, fpinfo))
407  return false;
408  break;
409  case REGOPEROID:
410  case REGOPERATOROID:
411  if (!is_shippable(DatumGetObjectId(c->constvalue),
412  OperatorRelationId, fpinfo))
413  return false;
414  break;
415  case REGCLASSOID:
416  if (!is_shippable(DatumGetObjectId(c->constvalue),
417  RelationRelationId, fpinfo))
418  return false;
419  break;
420  case REGTYPEOID:
421  if (!is_shippable(DatumGetObjectId(c->constvalue),
422  TypeRelationId, fpinfo))
423  return false;
424  break;
425  case REGCOLLATIONOID:
426  if (!is_shippable(DatumGetObjectId(c->constvalue),
427  CollationRelationId, fpinfo))
428  return false;
429  break;
430  case REGCONFIGOID:
431 
432  /*
433  * For text search objects only, we weaken the
434  * normal shippability criterion to allow all OIDs
435  * below FirstNormalObjectId. Without this, none
436  * of the initdb-installed TS configurations would
437  * be shippable, which would be quite annoying.
438  */
439  if (DatumGetObjectId(c->constvalue) >= FirstNormalObjectId &&
440  !is_shippable(DatumGetObjectId(c->constvalue),
441  TSConfigRelationId, fpinfo))
442  return false;
443  break;
444  case REGDICTIONARYOID:
445  if (DatumGetObjectId(c->constvalue) >= FirstNormalObjectId &&
446  !is_shippable(DatumGetObjectId(c->constvalue),
447  TSDictionaryRelationId, fpinfo))
448  return false;
449  break;
450  case REGNAMESPACEOID:
451  if (!is_shippable(DatumGetObjectId(c->constvalue),
452  NamespaceRelationId, fpinfo))
453  return false;
454  break;
455  case REGROLEOID:
456  if (!is_shippable(DatumGetObjectId(c->constvalue),
457  AuthIdRelationId, fpinfo))
458  return false;
459  break;
460  }
461  }
462 
463  /*
464  * If the constant has nondefault collation, either it's of a
465  * non-builtin type, or it reflects folding of a CollateExpr.
466  * It's unsafe to send to the remote unless it's used in a
467  * non-collation-sensitive context.
468  */
469  collation = c->constcollid;
470  if (collation == InvalidOid ||
471  collation == DEFAULT_COLLATION_OID)
473  else
475  }
476  break;
477  case T_Param:
478  {
479  Param *p = (Param *) node;
480 
481  /*
482  * If it's a MULTIEXPR Param, punt. We can't tell from here
483  * whether the referenced sublink/subplan contains any remote
484  * Vars; if it does, handling that is too complicated to
485  * consider supporting at present. Fortunately, MULTIEXPR
486  * Params are not reduced to plain PARAM_EXEC until the end of
487  * planning, so we can easily detect this case. (Normal
488  * PARAM_EXEC Params are safe to ship because their values
489  * come from somewhere else in the plan tree; but a MULTIEXPR
490  * references a sub-select elsewhere in the same targetlist,
491  * so we'd be on the hook to evaluate it somehow if we wanted
492  * to handle such cases as direct foreign updates.)
493  */
494  if (p->paramkind == PARAM_MULTIEXPR)
495  return false;
496 
497  /*
498  * Collation rule is same as for Consts and non-foreign Vars.
499  */
500  collation = p->paramcollid;
501  if (collation == InvalidOid ||
502  collation == DEFAULT_COLLATION_OID)
504  else
506  }
507  break;
508  case T_SubscriptingRef:
509  {
510  SubscriptingRef *sr = (SubscriptingRef *) node;
511 
512  /* Assignment should not be in restrictions. */
513  if (sr->refassgnexpr != NULL)
514  return false;
515 
516  /*
517  * Recurse into the remaining subexpressions. The container
518  * subscripts will not affect collation of the SubscriptingRef
519  * result, so do those first and reset inner_cxt afterwards.
520  */
522  glob_cxt, &inner_cxt, case_arg_cxt))
523  return false;
524  inner_cxt.collation = InvalidOid;
525  inner_cxt.state = FDW_COLLATE_NONE;
527  glob_cxt, &inner_cxt, case_arg_cxt))
528  return false;
529  inner_cxt.collation = InvalidOid;
530  inner_cxt.state = FDW_COLLATE_NONE;
531  if (!foreign_expr_walker((Node *) sr->refexpr,
532  glob_cxt, &inner_cxt, case_arg_cxt))
533  return false;
534 
535  /*
536  * Container subscripting typically yields same collation as
537  * refexpr's, but in case it doesn't, use same logic as for
538  * function nodes.
539  */
540  collation = sr->refcollid;
541  if (collation == InvalidOid)
543  else if (inner_cxt.state == FDW_COLLATE_SAFE &&
544  collation == inner_cxt.collation)
546  else if (collation == DEFAULT_COLLATION_OID)
548  else
550  }
551  break;
552  case T_FuncExpr:
553  {
554  FuncExpr *fe = (FuncExpr *) node;
555 
556  /*
557  * If function used by the expression is not shippable, it
558  * can't be sent to remote because it might have incompatible
559  * semantics on remote side.
560  */
561  if (!is_shippable(fe->funcid, ProcedureRelationId, fpinfo))
562  return false;
563 
564  /*
565  * Recurse to input subexpressions.
566  */
567  if (!foreign_expr_walker((Node *) fe->args,
568  glob_cxt, &inner_cxt, case_arg_cxt))
569  return false;
570 
571  /*
572  * If function's input collation is not derived from a foreign
573  * Var, it can't be sent to remote.
574  */
575  if (fe->inputcollid == InvalidOid)
576  /* OK, inputs are all noncollatable */ ;
577  else if (inner_cxt.state != FDW_COLLATE_SAFE ||
578  fe->inputcollid != inner_cxt.collation)
579  return false;
580 
581  /*
582  * Detect whether node is introducing a collation not derived
583  * from a foreign Var. (If so, we just mark it unsafe for now
584  * rather than immediately returning false, since the parent
585  * node might not care.)
586  */
587  collation = fe->funccollid;
588  if (collation == InvalidOid)
590  else if (inner_cxt.state == FDW_COLLATE_SAFE &&
591  collation == inner_cxt.collation)
593  else if (collation == DEFAULT_COLLATION_OID)
595  else
597  }
598  break;
599  case T_OpExpr:
600  case T_DistinctExpr: /* struct-equivalent to OpExpr */
601  {
602  OpExpr *oe = (OpExpr *) node;
603 
604  /*
605  * Similarly, only shippable operators can be sent to remote.
606  * (If the operator is shippable, we assume its underlying
607  * function is too.)
608  */
609  if (!is_shippable(oe->opno, OperatorRelationId, fpinfo))
610  return false;
611 
612  /*
613  * Recurse to input subexpressions.
614  */
615  if (!foreign_expr_walker((Node *) oe->args,
616  glob_cxt, &inner_cxt, case_arg_cxt))
617  return false;
618 
619  /*
620  * If operator's input collation is not derived from a foreign
621  * Var, it can't be sent to remote.
622  */
623  if (oe->inputcollid == InvalidOid)
624  /* OK, inputs are all noncollatable */ ;
625  else if (inner_cxt.state != FDW_COLLATE_SAFE ||
626  oe->inputcollid != inner_cxt.collation)
627  return false;
628 
629  /* Result-collation handling is same as for functions */
630  collation = oe->opcollid;
631  if (collation == InvalidOid)
633  else if (inner_cxt.state == FDW_COLLATE_SAFE &&
634  collation == inner_cxt.collation)
636  else if (collation == DEFAULT_COLLATION_OID)
638  else
640  }
641  break;
642  case T_ScalarArrayOpExpr:
643  {
644  ScalarArrayOpExpr *oe = (ScalarArrayOpExpr *) node;
645 
646  /*
647  * Again, only shippable operators can be sent to remote.
648  */
649  if (!is_shippable(oe->opno, OperatorRelationId, fpinfo))
650  return false;
651 
652  /*
653  * Recurse to input subexpressions.
654  */
655  if (!foreign_expr_walker((Node *) oe->args,
656  glob_cxt, &inner_cxt, case_arg_cxt))
657  return false;
658 
659  /*
660  * If operator's input collation is not derived from a foreign
661  * Var, it can't be sent to remote.
662  */
663  if (oe->inputcollid == InvalidOid)
664  /* OK, inputs are all noncollatable */ ;
665  else if (inner_cxt.state != FDW_COLLATE_SAFE ||
666  oe->inputcollid != inner_cxt.collation)
667  return false;
668 
669  /* Output is always boolean and so noncollatable. */
670  collation = InvalidOid;
672  }
673  break;
674  case T_RelabelType:
675  {
676  RelabelType *r = (RelabelType *) node;
677 
678  /*
679  * Recurse to input subexpression.
680  */
681  if (!foreign_expr_walker((Node *) r->arg,
682  glob_cxt, &inner_cxt, case_arg_cxt))
683  return false;
684 
685  /*
686  * RelabelType must not introduce a collation not derived from
687  * an input foreign Var (same logic as for a real function).
688  */
689  collation = r->resultcollid;
690  if (collation == InvalidOid)
692  else if (inner_cxt.state == FDW_COLLATE_SAFE &&
693  collation == inner_cxt.collation)
695  else if (collation == DEFAULT_COLLATION_OID)
697  else
699  }
700  break;
701  case T_BoolExpr:
702  {
703  BoolExpr *b = (BoolExpr *) node;
704 
705  /*
706  * Recurse to input subexpressions.
707  */
708  if (!foreign_expr_walker((Node *) b->args,
709  glob_cxt, &inner_cxt, case_arg_cxt))
710  return false;
711 
712  /* Output is always boolean and so noncollatable. */
713  collation = InvalidOid;
715  }
716  break;
717  case T_NullTest:
718  {
719  NullTest *nt = (NullTest *) node;
720 
721  /*
722  * Recurse to input subexpressions.
723  */
724  if (!foreign_expr_walker((Node *) nt->arg,
725  glob_cxt, &inner_cxt, case_arg_cxt))
726  return false;
727 
728  /* Output is always boolean and so noncollatable. */
729  collation = InvalidOid;
731  }
732  break;
733  case T_CaseExpr:
734  {
735  CaseExpr *ce = (CaseExpr *) node;
736  foreign_loc_cxt arg_cxt;
737  foreign_loc_cxt tmp_cxt;
738  ListCell *lc;
739 
740  /*
741  * Recurse to CASE's arg expression, if any. Its collation
742  * has to be saved aside for use while examining CaseTestExprs
743  * within the WHEN expressions.
744  */
745  arg_cxt.collation = InvalidOid;
746  arg_cxt.state = FDW_COLLATE_NONE;
747  if (ce->arg)
748  {
749  if (!foreign_expr_walker((Node *) ce->arg,
750  glob_cxt, &arg_cxt, case_arg_cxt))
751  return false;
752  }
753 
754  /* Examine the CaseWhen subexpressions. */
755  foreach(lc, ce->args)
756  {
757  CaseWhen *cw = lfirst_node(CaseWhen, lc);
758 
759  if (ce->arg)
760  {
761  /*
762  * In a CASE-with-arg, the parser should have produced
763  * WHEN clauses of the form "CaseTestExpr = RHS",
764  * possibly with an implicit coercion inserted above
765  * the CaseTestExpr. However in an expression that's
766  * been through the optimizer, the WHEN clause could
767  * be almost anything (since the equality operator
768  * could have been expanded into an inline function).
769  * In such cases forbid pushdown, because
770  * deparseCaseExpr can't handle it.
771  */
772  Node *whenExpr = (Node *) cw->expr;
773  List *opArgs;
774 
775  if (!IsA(whenExpr, OpExpr))
776  return false;
777 
778  opArgs = ((OpExpr *) whenExpr)->args;
779  if (list_length(opArgs) != 2 ||
781  CaseTestExpr))
782  return false;
783  }
784 
785  /*
786  * Recurse to WHEN expression, passing down the arg info.
787  * Its collation doesn't affect the result (really, it
788  * should be boolean and thus not have a collation).
789  */
790  tmp_cxt.collation = InvalidOid;
791  tmp_cxt.state = FDW_COLLATE_NONE;
792  if (!foreign_expr_walker((Node *) cw->expr,
793  glob_cxt, &tmp_cxt, &arg_cxt))
794  return false;
795 
796  /* Recurse to THEN expression. */
797  if (!foreign_expr_walker((Node *) cw->result,
798  glob_cxt, &inner_cxt, case_arg_cxt))
799  return false;
800  }
801 
802  /* Recurse to ELSE expression. */
803  if (!foreign_expr_walker((Node *) ce->defresult,
804  glob_cxt, &inner_cxt, case_arg_cxt))
805  return false;
806 
807  /*
808  * Detect whether node is introducing a collation not derived
809  * from a foreign Var. (If so, we just mark it unsafe for now
810  * rather than immediately returning false, since the parent
811  * node might not care.) This is the same as for function
812  * nodes, except that the input collation is derived from only
813  * the THEN and ELSE subexpressions.
814  */
815  collation = ce->casecollid;
816  if (collation == InvalidOid)
818  else if (inner_cxt.state == FDW_COLLATE_SAFE &&
819  collation == inner_cxt.collation)
821  else if (collation == DEFAULT_COLLATION_OID)
823  else
825  }
826  break;
827  case T_CaseTestExpr:
828  {
829  CaseTestExpr *c = (CaseTestExpr *) node;
830 
831  /* Punt if we seem not to be inside a CASE arg WHEN. */
832  if (!case_arg_cxt)
833  return false;
834 
835  /*
836  * Otherwise, any nondefault collation attached to the
837  * CaseTestExpr node must be derived from foreign Var(s) in
838  * the CASE arg.
839  */
840  collation = c->collation;
841  if (collation == InvalidOid)
843  else if (case_arg_cxt->state == FDW_COLLATE_SAFE &&
844  collation == case_arg_cxt->collation)
846  else if (collation == DEFAULT_COLLATION_OID)
848  else
850  }
851  break;
852  case T_ArrayExpr:
853  {
854  ArrayExpr *a = (ArrayExpr *) node;
855 
856  /*
857  * Recurse to input subexpressions.
858  */
859  if (!foreign_expr_walker((Node *) a->elements,
860  glob_cxt, &inner_cxt, case_arg_cxt))
861  return false;
862 
863  /*
864  * ArrayExpr must not introduce a collation not derived from
865  * an input foreign Var (same logic as for a function).
866  */
867  collation = a->array_collid;
868  if (collation == InvalidOid)
870  else if (inner_cxt.state == FDW_COLLATE_SAFE &&
871  collation == inner_cxt.collation)
873  else if (collation == DEFAULT_COLLATION_OID)
875  else
877  }
878  break;
879  case T_List:
880  {
881  List *l = (List *) node;
882  ListCell *lc;
883 
884  /*
885  * Recurse to component subexpressions.
886  */
887  foreach(lc, l)
888  {
889  if (!foreign_expr_walker((Node *) lfirst(lc),
890  glob_cxt, &inner_cxt, case_arg_cxt))
891  return false;
892  }
893 
894  /*
895  * When processing a list, collation state just bubbles up
896  * from the list elements.
897  */
898  collation = inner_cxt.collation;
899  state = inner_cxt.state;
900 
901  /* Don't apply exprType() to the list. */
902  check_type = false;
903  }
904  break;
905  case T_Aggref:
906  {
907  Aggref *agg = (Aggref *) node;
908  ListCell *lc;
909 
910  /* Not safe to pushdown when not in grouping context */
911  if (!IS_UPPER_REL(glob_cxt->foreignrel))
912  return false;
913 
914  /* Only non-split aggregates are pushable. */
915  if (agg->aggsplit != AGGSPLIT_SIMPLE)
916  return false;
917 
918  /* As usual, it must be shippable. */
919  if (!is_shippable(agg->aggfnoid, ProcedureRelationId, fpinfo))
920  return false;
921 
922  /*
923  * Recurse to input args. aggdirectargs, aggorder and
924  * aggdistinct are all present in args, so no need to check
925  * their shippability explicitly.
926  */
927  foreach(lc, agg->args)
928  {
929  Node *n = (Node *) lfirst(lc);
930 
931  /* If TargetEntry, extract the expression from it */
932  if (IsA(n, TargetEntry))
933  {
934  TargetEntry *tle = (TargetEntry *) n;
935 
936  n = (Node *) tle->expr;
937  }
938 
939  if (!foreign_expr_walker(n,
940  glob_cxt, &inner_cxt, case_arg_cxt))
941  return false;
942  }
943 
944  /*
945  * For aggorder elements, check whether the sort operator, if
946  * specified, is shippable or not.
947  */
948  if (agg->aggorder)
949  {
950  foreach(lc, agg->aggorder)
951  {
952  SortGroupClause *srt = (SortGroupClause *) lfirst(lc);
953  Oid sortcoltype;
954  TypeCacheEntry *typentry;
955  TargetEntry *tle;
956 
958  agg->args);
959  sortcoltype = exprType((Node *) tle->expr);
960  typentry = lookup_type_cache(sortcoltype,
962  /* Check shippability of non-default sort operator. */
963  if (srt->sortop != typentry->lt_opr &&
964  srt->sortop != typentry->gt_opr &&
965  !is_shippable(srt->sortop, OperatorRelationId,
966  fpinfo))
967  return false;
968  }
969  }
970 
971  /* Check aggregate filter */
972  if (!foreign_expr_walker((Node *) agg->aggfilter,
973  glob_cxt, &inner_cxt, case_arg_cxt))
974  return false;
975 
976  /*
977  * If aggregate's input collation is not derived from a
978  * foreign Var, it can't be sent to remote.
979  */
980  if (agg->inputcollid == InvalidOid)
981  /* OK, inputs are all noncollatable */ ;
982  else if (inner_cxt.state != FDW_COLLATE_SAFE ||
983  agg->inputcollid != inner_cxt.collation)
984  return false;
985 
986  /*
987  * Detect whether node is introducing a collation not derived
988  * from a foreign Var. (If so, we just mark it unsafe for now
989  * rather than immediately returning false, since the parent
990  * node might not care.)
991  */
992  collation = agg->aggcollid;
993  if (collation == InvalidOid)
995  else if (inner_cxt.state == FDW_COLLATE_SAFE &&
996  collation == inner_cxt.collation)
998  else if (collation == DEFAULT_COLLATION_OID)
1000  else
1002  }
1003  break;
1004  default:
1005 
1006  /*
1007  * If it's anything else, assume it's unsafe. This list can be
1008  * expanded later, but don't forget to add deparse support below.
1009  */
1010  return false;
1011  }
1012 
1013  /*
1014  * If result type of given expression is not shippable, it can't be sent
1015  * to remote because it might have incompatible semantics on remote side.
1016  */
1017  if (check_type && !is_shippable(exprType(node), TypeRelationId, fpinfo))
1018  return false;
1019 
1020  /*
1021  * Now, merge my collation information into my parent's state.
1022  */
1023  if (state > outer_cxt->state)
1024  {
1025  /* Override previous parent state */
1026  outer_cxt->collation = collation;
1027  outer_cxt->state = state;
1028  }
1029  else if (state == outer_cxt->state)
1030  {
1031  /* Merge, or detect error if there's a collation conflict */
1032  switch (state)
1033  {
1034  case FDW_COLLATE_NONE:
1035  /* Nothing + nothing is still nothing */
1036  break;
1037  case FDW_COLLATE_SAFE:
1038  if (collation != outer_cxt->collation)
1039  {
1040  /*
1041  * Non-default collation always beats default.
1042  */
1043  if (outer_cxt->collation == DEFAULT_COLLATION_OID)
1044  {
1045  /* Override previous parent state */
1046  outer_cxt->collation = collation;
1047  }
1048  else if (collation != DEFAULT_COLLATION_OID)
1049  {
1050  /*
1051  * Conflict; show state as indeterminate. We don't
1052  * want to "return false" right away, since parent
1053  * node might not care about collation.
1054  */
1055  outer_cxt->state = FDW_COLLATE_UNSAFE;
1056  }
1057  }
1058  break;
1059  case FDW_COLLATE_UNSAFE:
1060  /* We're still conflicted ... */
1061  break;
1062  }
1063  }
1064 
1065  /* It looks OK */
1066  return true;
1067 }
static bool foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, foreign_loc_cxt *outer_cxt, foreign_loc_cxt *case_arg_cxt)
Definition: deparse.c:312
int b
Definition: isn.c:70
int a
Definition: isn.c:69
Node * strip_implicit_coercions(Node *node)
Definition: nodeFuncs.c:700
static Oid DatumGetObjectId(Datum X)
Definition: postgres.h:242
#define InvalidOid
Definition: postgres_ext.h:36
bool is_shippable(Oid objectId, Oid classId, PgFdwRelationInfo *fpinfo)
Definition: shippable.c:162
char * c
static int fe(enum e x)
Definition: preproc-init.c:111
@ PARAM_MULTIEXPR
Definition: primnodes.h:356
ParamKind paramkind
Definition: primnodes.h:362
Expr * refassgnexpr
Definition: primnodes.h:659
RelOptInfo * foreignrel
Definition: deparse.c:71
Relids relids
Definition: deparse.c:72
FDWCollateState state
Definition: deparse.c:93
Definition: regguts.h:323
#define FirstNormalObjectId
Definition: transam.h:197

References a, Aggref::aggfilter, Aggref::aggfnoid, Aggref::aggorder, AGGSPLIT_SIMPLE, RelabelType::arg, CaseExpr::arg, NullTest::arg, Aggref::args, OpExpr::args, ScalarArrayOpExpr::args, CaseExpr::args, b, bms_is_member(), foreign_loc_cxt::collation, DatumGetObjectId(), CaseExpr::defresult, CaseWhen::expr, TargetEntry::expr, exprType(), FDW_COLLATE_NONE, FDW_COLLATE_SAFE, FDW_COLLATE_UNSAFE, fe(), FirstNormalObjectId, foreign_glob_cxt::foreignrel, get_sortgroupref_tle(), TypeCacheEntry::gt_opr, if(), InvalidOid, is_shippable(), IS_UPPER_REL, IsA, lfirst, lfirst_node, linitial, list_length(), lookup_type_cache(), TypeCacheEntry::lt_opr, nodeTag, OidIsValid, OpExpr::opno, ScalarArrayOpExpr::opno, PARAM_MULTIEXPR, Param::paramkind, SubscriptingRef::refassgnexpr, SubscriptingRef::refexpr, SubscriptingRef::reflowerindexpr, SubscriptingRef::refupperindexpr, foreign_glob_cxt::relids, CaseWhen::result, SelfItemPointerAttributeNumber, SortGroupClause::sortop, foreign_loc_cxt::state, strip_implicit_coercions(), SortGroupClause::tleSortGroupRef, TYPECACHE_GT_OPR, TYPECACHE_LT_OPR, Var::varattno, Var::varlevelsup, and Var::varno.

Referenced by is_foreign_expr().

◆ get_jointype_name()

const char* get_jointype_name ( JoinType  jointype)

Definition at line 1641 of file deparse.c.

1642 {
1643  switch (jointype)
1644  {
1645  case JOIN_INNER:
1646  return "INNER";
1647 
1648  case JOIN_LEFT:
1649  return "LEFT";
1650 
1651  case JOIN_RIGHT:
1652  return "RIGHT";
1653 
1654  case JOIN_FULL:
1655  return "FULL";
1656 
1657  case JOIN_SEMI:
1658  return "SEMI";
1659 
1660  default:
1661  /* Shouldn't come here, but protect from buggy code. */
1662  elog(ERROR, "unsupported join type %d", jointype);
1663  }
1664 
1665  /* Keep compiler happy */
1666  return NULL;
1667 }
@ JOIN_FULL
Definition: nodes.h:295
@ JOIN_RIGHT
Definition: nodes.h:296
@ JOIN_LEFT
Definition: nodes.h:294

References elog, ERROR, JOIN_FULL, JOIN_INNER, JOIN_LEFT, JOIN_RIGHT, and JOIN_SEMI.

Referenced by deparseFromExprForRel(), and foreign_join_ok().

◆ get_relation_column_alias_ids()

static void get_relation_column_alias_ids ( Var node,
RelOptInfo foreignrel,
int *  relno,
int *  colno 
)
static

Definition at line 4174 of file deparse.c.

4176 {
4177  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
4178  int i;
4179  ListCell *lc;
4180 
4181  /* Get the relation alias ID */
4182  *relno = fpinfo->relation_index;
4183 
4184  /* Get the column alias ID */
4185  i = 1;
4186  foreach(lc, foreignrel->reltarget->exprs)
4187  {
4188  Var *tlvar = (Var *) lfirst(lc);
4189 
4190  /*
4191  * Match reltarget entries only on varno/varattno. Ideally there
4192  * would be some cross-check on varnullingrels, but it's unclear what
4193  * to do exactly; we don't have enough context to know what that value
4194  * should be.
4195  */
4196  if (IsA(tlvar, Var) &&
4197  tlvar->varno == node->varno &&
4198  tlvar->varattno == node->varattno)
4199  {
4200  *colno = i;
4201  return;
4202  }
4203  i++;
4204  }
4205 
4206  /* Shouldn't get here */
4207  elog(ERROR, "unexpected expression in subquery output");
4208 }

References elog, ERROR, PathTarget::exprs, i, IsA, lfirst, PgFdwRelationInfo::relation_index, RelOptInfo::reltarget, Var::varattno, and Var::varno.

Referenced by is_subquery_var().

◆ is_foreign_expr()

bool is_foreign_expr ( PlannerInfo root,
RelOptInfo baserel,
Expr expr 
)

Definition at line 244 of file deparse.c.

247 {
248  foreign_glob_cxt glob_cxt;
249  foreign_loc_cxt loc_cxt;
250  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) (baserel->fdw_private);
251 
252  /*
253  * Check that the expression consists of nodes that are safe to execute
254  * remotely.
255  */
256  glob_cxt.root = root;
257  glob_cxt.foreignrel = baserel;
258 
259  /*
260  * For an upper relation, use relids from its underneath scan relation,
261  * because the upperrel's own relids currently aren't set to anything
262  * meaningful by the core code. For other relation, use their own relids.
263  */
264  if (IS_UPPER_REL(baserel))
265  glob_cxt.relids = fpinfo->outerrel->relids;
266  else
267  glob_cxt.relids = baserel->relids;
268  loc_cxt.collation = InvalidOid;
269  loc_cxt.state = FDW_COLLATE_NONE;
270  if (!foreign_expr_walker((Node *) expr, &glob_cxt, &loc_cxt, NULL))
271  return false;
272 
273  /*
274  * If the expression has a valid collation that does not arise from a
275  * foreign var, the expression can not be sent over.
276  */
277  if (loc_cxt.state == FDW_COLLATE_UNSAFE)
278  return false;
279 
280  /*
281  * An expression which includes any mutable functions can't be sent over
282  * because its result is not stable. For example, sending now() remote
283  * side could cause confusion from clock offsets. Future versions might
284  * be able to make this choice with more granularity. (We check this last
285  * because it requires a lot of expensive catalog lookups.)
286  */
287  if (contain_mutable_functions((Node *) expr))
288  return false;
289 
290  /* OK to evaluate on the remote server */
291  return true;
292 }
bool contain_mutable_functions(Node *clause)
Definition: clauses.c:370
PlannerInfo * root
Definition: deparse.c:70

References foreign_loc_cxt::collation, contain_mutable_functions(), FDW_COLLATE_NONE, FDW_COLLATE_UNSAFE, foreign_expr_walker(), foreign_glob_cxt::foreignrel, if(), InvalidOid, IS_UPPER_REL, PgFdwRelationInfo::outerrel, foreign_glob_cxt::relids, RelOptInfo::relids, foreign_glob_cxt::root, root, and foreign_loc_cxt::state.

Referenced by add_foreign_final_paths(), classifyConditions(), find_em_for_rel(), find_em_for_rel_target(), foreign_grouping_ok(), foreign_join_ok(), postgresGetForeignPaths(), postgresGetForeignPlan(), and postgresPlanDirectModify().

◆ is_foreign_param()

bool is_foreign_param ( PlannerInfo root,
RelOptInfo baserel,
Expr expr 
)

Definition at line 1082 of file deparse.c.

1085 {
1086  if (expr == NULL)
1087  return false;
1088 
1089  switch (nodeTag(expr))
1090  {
1091  case T_Var:
1092  {
1093  /* It would have to be sent unless it's a foreign Var */
1094  Var *var = (Var *) expr;
1095  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) (baserel->fdw_private);
1096  Relids relids;
1097 
1098  if (IS_UPPER_REL(baserel))
1099  relids = fpinfo->outerrel->relids;
1100  else
1101  relids = baserel->relids;
1102 
1103  if (bms_is_member(var->varno, relids) && var->varlevelsup == 0)
1104  return false; /* foreign Var, so not a param */
1105  else
1106  return true; /* it'd have to be a param */
1107  break;
1108  }
1109  case T_Param:
1110  /* Params always have to be sent to the foreign server */
1111  return true;
1112  default:
1113  break;
1114  }
1115  return false;
1116 }

References bms_is_member(), if(), IS_UPPER_REL, nodeTag, PgFdwRelationInfo::outerrel, RelOptInfo::relids, Var::varlevelsup, and Var::varno.

Referenced by foreign_grouping_ok().

◆ is_foreign_pathkey()

bool is_foreign_pathkey ( PlannerInfo root,
RelOptInfo baserel,
PathKey pathkey 
)

Definition at line 1123 of file deparse.c.

1126 {
1127  EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
1128  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) baserel->fdw_private;
1129 
1130  /*
1131  * is_foreign_expr would detect volatile expressions as well, but checking
1132  * ec_has_volatile here saves some cycles.
1133  */
1134  if (pathkey_ec->ec_has_volatile)
1135  return false;
1136 
1137  /* can't push down the sort if the pathkey's opfamily is not shippable */
1138  if (!is_shippable(pathkey->pk_opfamily, OperatorFamilyRelationId, fpinfo))
1139  return false;
1140 
1141  /* can push if a suitable EC member exists */
1142  return (find_em_for_rel(root, pathkey_ec, baserel) != NULL);
1143 }

References EquivalenceClass::ec_has_volatile, find_em_for_rel(), if(), is_shippable(), PathKey::pk_opfamily, and root.

Referenced by get_useful_pathkeys_for_relation().

◆ is_subquery_var()

static bool is_subquery_var ( Var node,
RelOptInfo foreignrel,
int *  relno,
int *  colno 
)
static

Definition at line 4112 of file deparse.c.

4113 {
4114  PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
4115  RelOptInfo *outerrel = fpinfo->outerrel;
4116  RelOptInfo *innerrel = fpinfo->innerrel;
4117 
4118  /* Should only be called in these cases. */
4119  Assert(IS_SIMPLE_REL(foreignrel) || IS_JOIN_REL(foreignrel));
4120 
4121  /*
4122  * If the given relation isn't a join relation, it doesn't have any lower
4123  * subqueries, so the Var isn't a subquery output column.
4124  */
4125  if (!IS_JOIN_REL(foreignrel))
4126  return false;
4127 
4128  /*
4129  * If the Var doesn't belong to any lower subqueries, it isn't a subquery
4130  * output column.
4131  */
4132  if (!bms_is_member(node->varno, fpinfo->lower_subquery_rels))
4133  return false;
4134 
4135  if (bms_is_member(node->varno, outerrel->relids))
4136  {
4137  /*
4138  * If outer relation is deparsed as a subquery, the Var is an output
4139  * column of the subquery; get the IDs for the relation/column alias.
4140  */
4141  if (fpinfo->make_outerrel_subquery)
4142  {
4143  get_relation_column_alias_ids(node, outerrel, relno, colno);
4144  return true;
4145  }
4146 
4147  /* Otherwise, recurse into the outer relation. */
4148  return is_subquery_var(node, outerrel, relno, colno);
4149  }
4150  else
4151  {
4152  Assert(bms_is_member(node->varno, innerrel->relids));
4153 
4154  /*
4155  * If inner relation is deparsed as a subquery, the Var is an output
4156  * column of the subquery; get the IDs for the relation/column alias.
4157  */
4158  if (fpinfo->make_innerrel_subquery)
4159  {
4160  get_relation_column_alias_ids(node, innerrel, relno, colno);
4161  return true;
4162  }
4163 
4164  /* Otherwise, recurse into the inner relation. */
4165  return is_subquery_var(node, innerrel, relno, colno);
4166  }
4167 }
static void get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel, int *relno, int *colno)
Definition: deparse.c:4174

References Assert(), bms_is_member(), get_relation_column_alias_ids(), PgFdwRelationInfo::innerrel, IS_JOIN_REL, IS_SIMPLE_REL, PgFdwRelationInfo::lower_subquery_rels, PgFdwRelationInfo::make_innerrel_subquery, PgFdwRelationInfo::make_outerrel_subquery, PgFdwRelationInfo::outerrel, and Var::varno.

Referenced by deparseVar().

◆ isPlainForeignVar()

static bool isPlainForeignVar ( Expr node,
deparse_expr_cxt context 
)
static

Definition at line 3371 of file deparse.c.

3372 {
3373  /*
3374  * We allow the foreign Var to have an implicit RelabelType, mainly so
3375  * that this'll work with varchar columns. Note that deparseRelabelType
3376  * will not print such a cast, so we're not breaking the restriction that
3377  * the expression print as a plain Var. We won't risk it for an implicit
3378  * cast that requires a function, nor for non-implicit RelabelType; such
3379  * cases seem too likely to involve semantics changes compared to what
3380  * would happen on the remote side.
3381  */
3382  if (IsA(node, RelabelType) &&
3383  ((RelabelType *) node)->relabelformat == COERCE_IMPLICIT_CAST)
3384  node = ((RelabelType *) node)->arg;
3385 
3386  if (IsA(node, Var))
3387  {
3388  /*
3389  * The Var must be one that'll deparse as a foreign column reference
3390  * (cf. deparseVar).
3391  */
3392  Var *var = (Var *) node;
3393  Relids relids = context->scanrel->relids;
3394 
3395  if (bms_is_member(var->varno, relids) && var->varlevelsup == 0)
3396  return true;
3397  }
3398 
3399  return false;
3400 }

References bms_is_member(), COERCE_IMPLICIT_CAST, context, IsA, Var::varlevelsup, and Var::varno.

Referenced by deparseOpExpr().

◆ printRemoteParam()

static void printRemoteParam ( int  paramindex,
Oid  paramtype,
int32  paramtypmod,
deparse_expr_cxt context 
)
static

Definition at line 3823 of file deparse.c.

3825 {
3826  StringInfo buf = context->buf;
3827  char *ptypename = deparse_type_name(paramtype, paramtypmod);
3828 
3829  appendStringInfo(buf, "$%d::%s", paramindex, ptypename);
3830 }

References appendStringInfo(), buf, context, and deparse_type_name().

Referenced by deparseParam(), and deparseVar().

◆ printRemotePlaceholder()

static void printRemotePlaceholder ( Oid  paramtype,
int32  paramtypmod,
deparse_expr_cxt context 
)
static

Definition at line 3849 of file deparse.c.

3851 {
3852  StringInfo buf = context->buf;
3853  char *ptypename = deparse_type_name(paramtype, paramtypmod);
3854 
3855  appendStringInfo(buf, "((SELECT null::%s)::%s)", ptypename, ptypename);
3856 }

References appendStringInfo(), buf, context, and deparse_type_name().

Referenced by deparseParam(), and deparseVar().

◆ rebuildInsertSql()

void rebuildInsertSql ( StringInfo  buf,
Relation  rel,
char *  orig_query,
List target_attrs,
int  values_end_len,
int  num_params,
int  num_rows 
)

Definition at line 2156 of file deparse.c.

2160 {
2161  TupleDesc tupdesc = RelationGetDescr(rel);
2162  int i;
2163  int pindex;
2164  bool first;
2165  ListCell *lc;
2166 
2167  /* Make sure the values_end_len is sensible */
2168  Assert((values_end_len > 0) && (values_end_len <= strlen(orig_query)));
2169 
2170  /* Copy up to the end of the first record from the original query */
2171  appendBinaryStringInfo(buf, orig_query, values_end_len);
2172 
2173  /*
2174  * Add records to VALUES clause (we already have parameters for the first
2175  * row, so start at the right offset).
2176  */
2177  pindex = num_params + 1;
2178  for (i = 0; i < num_rows; i++)
2179  {
2180  appendStringInfoString(buf, ", (");
2181 
2182  first = true;
2183  foreach(lc, target_attrs)
2184  {
2185  int attnum = lfirst_int(lc);
2186  Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
2187 
2188  if (!first)
2189  appendStringInfoString(buf, ", ");
2190  first = false;
2191 
2192  if (attr->attgenerated)
2193  appendStringInfoString(buf, "DEFAULT");
2194  else
2195  {
2196  appendStringInfo(buf, "$%d", pindex);
2197  pindex++;
2198  }
2199  }
2200 
2201  appendStringInfoChar(buf, ')');
2202  }
2203 
2204  /* Copy stuff after VALUES clause from the original query */
2205  appendStringInfoString(buf, orig_query + values_end_len);
2206 }

References appendBinaryStringInfo(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert(), attnum, buf, i, lfirst_int, RelationGetDescr, and TupleDescAttr.

Referenced by execute_foreign_modify().