PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
parse_utilcmd.h File Reference
Include dependency graph for parse_utilcmd.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

ListtransformCreateStmt (CreateStmt *stmt, const char *queryString)
 
ListtransformAlterTableStmt (Oid relid, AlterTableStmt *stmt, const char *queryString)
 
IndexStmttransformIndexStmt (Oid relid, IndexStmt *stmt, const char *queryString)
 
void transformRuleStmt (RuleStmt *stmt, const char *queryString, List **actions, Node **whereClause)
 
ListtransformCreateSchemaStmt (CreateSchemaStmt *stmt)
 
NodetransformPartitionBound (ParseState *pstate, Relation parent, Node *bound)
 

Function Documentation

List* transformAlterTableStmt ( Oid  relid,
AlterTableStmt stmt,
const char *  queryString 
)

Definition at line 2505 of file parse_utilcmd.c.

References addRangeTableEntryForRelation(), addRTEtoQuery(), CreateStmtContext::alist, AT_AddColumn, AT_AddColumnToView, AT_AddConstraint, AT_AddIndex, AT_AddIndexConstraint, AT_AlterColumnType, AT_AttachPartition, AT_DetachPartition, AT_ProcessedConstraint, CreateStmtContext::blist, PartitionCmd::bound, castNode, CreateStmtContext::ckconstraints, AlterTableStmt::cmds, CreateStmtContext::columns, CONSTR_FOREIGN, ColumnDef::constraints, ColumnDef::cooked_default, copyObject(), AlterTableCmd::def, elog, ERROR, EXPR_KIND_ALTER_COL_TRANSFORM, CreateStmtContext::fkconstraints, CreateStmtContext::hasoids, IndexStmt::indexOid, CreateStmtContext::inh_indexes, CreateStmtContext::inhRelations, IsA, CreateStmtContext::isalter, CreateStmtContext::isforeign, CreateStmtContext::ispartitioned, CreateStmtContext::ixconstraints, lappend(), lfirst, list_concat(), make_parsestate(), makeNode, NIL, nodeTag, NoLock, NULL, OBJECT_FOREIGN_TABLE, OidIsValid, ParseState::p_sourcetext, CreateStmtContext::partbound, CreateStmtContext::pkey, CreateStmtContext::pstate, ColumnDef::raw_default, RelationData::rd_rel, CreateStmtContext::rel, CreateStmtContext::relation, AlterTableStmt::relation, relation_close(), relation_open(), AlterTableStmt::relkind, RELKIND_PARTITIONED_TABLE, result, CreateStmtContext::stmtType, AlterTableCmd::subtype, transformCheckConstraints(), transformColumnDefinition(), transformExpr(), transformFKConstraints(), transformIndexConstraints(), transformIndexStmt(), transformPartitionCmd(), and transformTableConstraint().

Referenced by ATPostAlterTypeParse(), and ProcessUtilitySlow().

2507 {
2508  Relation rel;
2509  ParseState *pstate;
2510  CreateStmtContext cxt;
2511  List *result;
2512  List *save_alist;
2513  ListCell *lcmd,
2514  *l;
2515  List *newcmds = NIL;
2516  bool skipValidation = true;
2517  AlterTableCmd *newcmd;
2518  RangeTblEntry *rte;
2519 
2520  /*
2521  * We must not scribble on the passed-in AlterTableStmt, so copy it. (This
2522  * is overkill, but easy.)
2523  */
2524  stmt = (AlterTableStmt *) copyObject(stmt);
2525 
2526  /* Caller is responsible for locking the relation */
2527  rel = relation_open(relid, NoLock);
2528 
2529  /* Set up pstate */
2530  pstate = make_parsestate(NULL);
2531  pstate->p_sourcetext = queryString;
2532  rte = addRangeTableEntryForRelation(pstate,
2533  rel,
2534  NULL,
2535  false,
2536  true);
2537  addRTEtoQuery(pstate, rte, false, true, true);
2538 
2539  /* Set up CreateStmtContext */
2540  cxt.pstate = pstate;
2541  if (stmt->relkind == OBJECT_FOREIGN_TABLE)
2542  {
2543  cxt.stmtType = "ALTER FOREIGN TABLE";
2544  cxt.isforeign = true;
2545  }
2546  else
2547  {
2548  cxt.stmtType = "ALTER TABLE";
2549  cxt.isforeign = false;
2550  }
2551  cxt.relation = stmt->relation;
2552  cxt.rel = rel;
2553  cxt.inhRelations = NIL;
2554  cxt.isalter = true;
2555  cxt.hasoids = false; /* need not be right */
2556  cxt.columns = NIL;
2557  cxt.ckconstraints = NIL;
2558  cxt.fkconstraints = NIL;
2559  cxt.ixconstraints = NIL;
2560  cxt.inh_indexes = NIL;
2561  cxt.blist = NIL;
2562  cxt.alist = NIL;
2563  cxt.pkey = NULL;
2564  cxt.ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
2565  cxt.partbound = NULL;
2566 
2567  /*
2568  * The only subtypes that currently require parse transformation handling
2569  * are ADD COLUMN, ADD CONSTRAINT and SET DATA TYPE. These largely re-use
2570  * code from CREATE TABLE.
2571  */
2572  foreach(lcmd, stmt->cmds)
2573  {
2574  AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
2575 
2576  switch (cmd->subtype)
2577  {
2578  case AT_AddColumn:
2579  case AT_AddColumnToView:
2580  {
2581  ColumnDef *def = castNode(ColumnDef, cmd->def);
2582 
2583  transformColumnDefinition(&cxt, def);
2584 
2585  /*
2586  * If the column has a non-null default, we can't skip
2587  * validation of foreign keys.
2588  */
2589  if (def->raw_default != NULL)
2590  skipValidation = false;
2591 
2592  /*
2593  * All constraints are processed in other ways. Remove the
2594  * original list
2595  */
2596  def->constraints = NIL;
2597 
2598  newcmds = lappend(newcmds, cmd);
2599  break;
2600  }
2601 
2602  case AT_AddConstraint:
2603 
2604  /*
2605  * The original AddConstraint cmd node doesn't go to newcmds
2606  */
2607  if (IsA(cmd->def, Constraint))
2608  {
2609  transformTableConstraint(&cxt, (Constraint *) cmd->def);
2610  if (((Constraint *) cmd->def)->contype == CONSTR_FOREIGN)
2611  skipValidation = false;
2612  }
2613  else
2614  elog(ERROR, "unrecognized node type: %d",
2615  (int) nodeTag(cmd->def));
2616  break;
2617 
2619 
2620  /*
2621  * Already-transformed ADD CONSTRAINT, so just make it look
2622  * like the standard case.
2623  */
2624  cmd->subtype = AT_AddConstraint;
2625  newcmds = lappend(newcmds, cmd);
2626  break;
2627 
2628  case AT_AlterColumnType:
2629  {
2630  ColumnDef *def = (ColumnDef *) cmd->def;
2631 
2632  /*
2633  * For ALTER COLUMN TYPE, transform the USING clause if
2634  * one was specified.
2635  */
2636  if (def->raw_default)
2637  {
2638  def->cooked_default =
2639  transformExpr(pstate, def->raw_default,
2641  }
2642 
2643  newcmds = lappend(newcmds, cmd);
2644  break;
2645  }
2646 
2647  case AT_AttachPartition:
2648  case AT_DetachPartition:
2649  {
2650  PartitionCmd *partcmd = (PartitionCmd *) cmd->def;
2651 
2652  transformPartitionCmd(&cxt, partcmd);
2653  /* assign transformed value of the partition bound */
2654  partcmd->bound = cxt.partbound;
2655  }
2656 
2657  newcmds = lappend(newcmds, cmd);
2658  break;
2659 
2660  default:
2661  newcmds = lappend(newcmds, cmd);
2662  break;
2663  }
2664  }
2665 
2666  /*
2667  * transformIndexConstraints wants cxt.alist to contain only index
2668  * statements, so transfer anything we already have into save_alist
2669  * immediately.
2670  */
2671  save_alist = cxt.alist;
2672  cxt.alist = NIL;
2673 
2674  /* Postprocess constraints */
2676  transformFKConstraints(&cxt, skipValidation, true);
2677  transformCheckConstraints(&cxt, false);
2678 
2679  /*
2680  * Push any index-creation commands into the ALTER, so that they can be
2681  * scheduled nicely by tablecmds.c. Note that tablecmds.c assumes that
2682  * the IndexStmt attached to an AT_AddIndex or AT_AddIndexConstraint
2683  * subcommand has already been through transformIndexStmt.
2684  */
2685  foreach(l, cxt.alist)
2686  {
2687  IndexStmt *idxstmt = castNode(IndexStmt, lfirst(l));
2688 
2689  idxstmt = transformIndexStmt(relid, idxstmt, queryString);
2690  newcmd = makeNode(AlterTableCmd);
2692  newcmd->def = (Node *) idxstmt;
2693  newcmds = lappend(newcmds, newcmd);
2694  }
2695  cxt.alist = NIL;
2696 
2697  /* Append any CHECK or FK constraints to the commands list */
2698  foreach(l, cxt.ckconstraints)
2699  {
2700  newcmd = makeNode(AlterTableCmd);
2701  newcmd->subtype = AT_AddConstraint;
2702  newcmd->def = (Node *) lfirst(l);
2703  newcmds = lappend(newcmds, newcmd);
2704  }
2705  foreach(l, cxt.fkconstraints)
2706  {
2707  newcmd = makeNode(AlterTableCmd);
2708  newcmd->subtype = AT_AddConstraint;
2709  newcmd->def = (Node *) lfirst(l);
2710  newcmds = lappend(newcmds, newcmd);
2711  }
2712 
2713  /* Close rel */
2714  relation_close(rel, NoLock);
2715 
2716  /*
2717  * Output results.
2718  */
2719  stmt->cmds = newcmds;
2720 
2721  result = lappend(cxt.blist, stmt);
2722  result = list_concat(result, cxt.alist);
2723  result = list_concat(result, save_alist);
2724 
2725  return result;
2726 }
#define NIL
Definition: pg_list.h:69
#define IsA(nodeptr, _type_)
Definition: nodes.h:557
#define castNode(_type_, nodeptr)
Definition: nodes.h:575
Node * bound
Definition: parsenodes.h:814
List * constraints
Definition: parsenodes.h:642
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:147
Definition: nodes.h:506
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: heapam.c:1263
AlterTableType subtype
Definition: parsenodes.h:1725
List * list_concat(List *list1, List *list2)
Definition: list.c:321
return result
Definition: formatting.c:1618
Form_pg_class rd_rel
Definition: rel.h:114
#define OidIsValid(objectId)
Definition: c.h:538
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:44
void * copyObject(const void *from)
Definition: copyfuncs.c:4619
ParseState * pstate
Definition: parse_utilcmd.c:74
Node * cooked_default
Definition: parsenodes.h:639
Oid indexOid
Definition: parsenodes.h:2648
RangeVar * relation
Definition: parse_utilcmd.c:76
IndexStmt * pkey
Definition: parse_utilcmd.c:91
#define ERROR
Definition: elog.h:43
#define NoLock
Definition: lockdefs.h:34
const char * p_sourcetext
Definition: parse_node.h:167
ObjectType relkind
Definition: parsenodes.h:1643
void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
Node * raw_default
Definition: parsenodes.h:638
List * lappend(List *list, void *datum)
Definition: list.c:128
#define RELKIND_PARTITIONED_TABLE
Definition: pg_class.h:168
#define makeNode(_type_)
Definition: nodes.h:554
#define NULL
Definition: c.h:229
#define lfirst(lc)
Definition: pg_list.h:106
static void transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
RangeTblEntry * addRangeTableEntryForRelation(ParseState *pstate, Relation rel, Alias *alias, bool inh, bool inFromCl)
static void transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd)
static void transformFKConstraints(CreateStmtContext *cxt, bool skipValidation, bool isAddConstraint)
#define nodeTag(nodeptr)
Definition: nodes.h:511
const char * stmtType
Definition: parse_utilcmd.c:75
RangeVar * relation
Definition: parsenodes.h:1641
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1117
#define elog
Definition: elog.h:219
static void transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
static void transformIndexConstraints(CreateStmtContext *cxt)
IndexStmt * transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
Definition: pg_list.h:45
static void transformCheckConstraints(CreateStmtContext *cxt, bool skipValidation)
List* transformCreateSchemaStmt ( CreateSchemaStmt stmt)

Definition at line 2907 of file parse_utilcmd.c.

References CreateSchemaStmtContext::authrole, CreateSchemaStmt::authrole, element(), elog, ERROR, CreateSchemaStmtContext::grants, CreateSchemaStmtContext::indexes, lappend(), lfirst, list_concat(), NIL, nodeTag, CreateStmt::relation, CreateTrigStmt::relation, IndexStmt::relation, result, CreateSchemaStmt::schemaElts, RangeVar::schemaname, CreateSchemaStmtContext::schemaname, CreateSchemaStmt::schemaname, CreateSeqStmt::sequence, CreateSchemaStmtContext::sequences, setSchemaName(), CreateSchemaStmtContext::stmtType, T_CreateSeqStmt, T_CreateStmt, T_CreateTrigStmt, T_GrantStmt, T_IndexStmt, T_ViewStmt, CreateSchemaStmtContext::tables, CreateSchemaStmtContext::triggers, ViewStmt::view, and CreateSchemaStmtContext::views.

Referenced by CreateSchemaCommand().

2908 {
2910  List *result;
2911  ListCell *elements;
2912 
2913  cxt.stmtType = "CREATE SCHEMA";
2914  cxt.schemaname = stmt->schemaname;
2915  cxt.authrole = (RoleSpec *) stmt->authrole;
2916  cxt.sequences = NIL;
2917  cxt.tables = NIL;
2918  cxt.views = NIL;
2919  cxt.indexes = NIL;
2920  cxt.triggers = NIL;
2921  cxt.grants = NIL;
2922 
2923  /*
2924  * Run through each schema element in the schema element list. Separate
2925  * statements by type, and do preliminary analysis.
2926  */
2927  foreach(elements, stmt->schemaElts)
2928  {
2929  Node *element = lfirst(elements);
2930 
2931  switch (nodeTag(element))
2932  {
2933  case T_CreateSeqStmt:
2934  {
2935  CreateSeqStmt *elp = (CreateSeqStmt *) element;
2936 
2938  cxt.sequences = lappend(cxt.sequences, element);
2939  }
2940  break;
2941 
2942  case T_CreateStmt:
2943  {
2944  CreateStmt *elp = (CreateStmt *) element;
2945 
2947 
2948  /*
2949  * XXX todo: deal with constraints
2950  */
2951  cxt.tables = lappend(cxt.tables, element);
2952  }
2953  break;
2954 
2955  case T_ViewStmt:
2956  {
2957  ViewStmt *elp = (ViewStmt *) element;
2958 
2959  setSchemaName(cxt.schemaname, &elp->view->schemaname);
2960 
2961  /*
2962  * XXX todo: deal with references between views
2963  */
2964  cxt.views = lappend(cxt.views, element);
2965  }
2966  break;
2967 
2968  case T_IndexStmt:
2969  {
2970  IndexStmt *elp = (IndexStmt *) element;
2971 
2973  cxt.indexes = lappend(cxt.indexes, element);
2974  }
2975  break;
2976 
2977  case T_CreateTrigStmt:
2978  {
2979  CreateTrigStmt *elp = (CreateTrigStmt *) element;
2980 
2982  cxt.triggers = lappend(cxt.triggers, element);
2983  }
2984  break;
2985 
2986  case T_GrantStmt:
2987  cxt.grants = lappend(cxt.grants, element);
2988  break;
2989 
2990  default:
2991  elog(ERROR, "unrecognized node type: %d",
2992  (int) nodeTag(element));
2993  }
2994  }
2995 
2996  result = NIL;
2997  result = list_concat(result, cxt.sequences);
2998  result = list_concat(result, cxt.tables);
2999  result = list_concat(result, cxt.views);
3000  result = list_concat(result, cxt.indexes);
3001  result = list_concat(result, cxt.triggers);
3002  result = list_concat(result, cxt.grants);
3003 
3004  return result;
3005 }
RangeVar * relation
Definition: parsenodes.h:1948
#define NIL
Definition: pg_list.h:69
Definition: nodes.h:506
List * list_concat(List *list1, List *list2)
Definition: list.c:321
return result
Definition: formatting.c:1618
RangeVar * view
Definition: parsenodes.h:2941
char * schemaname
Definition: primnodes.h:67
RangeVar * relation
Definition: parsenodes.h:2640
RoleSpec * authrole
Definition: parsenodes.h:1623
#define ERROR
Definition: elog.h:43
List * lappend(List *list, void *datum)
Definition: list.c:128
static chr element(struct vars *v, const chr *startp, const chr *endp)
Definition: regc_locale.c:380
RangeVar * sequence
Definition: parsenodes.h:2412
#define lfirst(lc)
Definition: pg_list.h:106
static void setSchemaName(char *context_schema, char **stmt_schema_name)
#define nodeTag(nodeptr)
Definition: nodes.h:511
RangeVar * relation
Definition: parsenodes.h:2297
#define elog
Definition: elog.h:219
Definition: pg_list.h:45
List* transformCreateStmt ( CreateStmt stmt,
const char *  queryString 
)

Definition at line 154 of file parse_utilcmd.c.

References CreateStmtContext::alist, Assert, CreateStmtContext::blist, cancel_parser_errposition_callback(), CreateStmtContext::ckconstraints, CreateStmtContext::columns, CreateStmt::constraints, copyObject(), element(), elog, ereport, errcode(), errmsg(), ERROR, CreateStmtContext::fkconstraints, get_namespace_name(), CreateStmtContext::hasoids, CreateStmt::if_not_exists, CreateStmtContext::inh_indexes, CreateStmtContext::inhRelations, CreateStmt::inhRelations, interpretOidsOption(), IsA, CreateStmtContext::isalter, CreateStmtContext::isforeign, CreateStmtContext::ispartitioned, CreateStmtContext::ixconstraints, lappend(), lcons(), lfirst, list_concat(), list_length(), RangeVar::location, make_parsestate(), makeDefElem(), makeInteger(), NIL, nodeTag, NoLock, NOTICE, NULL, CreateStmt::ofTypename, OidIsValid, CreateStmt::options, ParseState::p_sourcetext, CreateStmt::partbound, PARTITION_MAX_KEYS, PartitionSpec::partParams, CreateStmt::partspec, pg_strcasecmp(), CreateStmtContext::pkey, CreateStmtContext::pstate, RangeVarGetAndCheckCreationNamespace(), CreateStmtContext::rel, CreateStmtContext::relation, CreateStmt::relation, RangeVar::relname, RangeVar::relpersistence, RELPERSISTENCE_TEMP, result, RangeVar::schemaname, setup_parser_errposition_callback(), CreateStmtContext::stmtType, PartitionSpec::strategy, T_ColumnDef, T_Constraint, T_TableLikeClause, CreateStmt::tableElts, transformCheckConstraints(), transformColumnDefinition(), transformFKConstraints(), transformIndexConstraints(), transformOfType(), transformTableConstraint(), and transformTableLikeClause().

Referenced by ProcessUtilitySlow().

155 {
156  ParseState *pstate;
157  CreateStmtContext cxt;
158  List *result;
159  List *save_alist;
160  ListCell *elements;
161  Oid namespaceid;
162  Oid existing_relid;
163  ParseCallbackState pcbstate;
164  bool like_found = false;
165 
166  /*
167  * We must not scribble on the passed-in CreateStmt, so copy it. (This is
168  * overkill, but easy.)
169  */
170  stmt = (CreateStmt *) copyObject(stmt);
171 
172  /* Set up pstate */
173  pstate = make_parsestate(NULL);
174  pstate->p_sourcetext = queryString;
175 
176  /*
177  * Look up the creation namespace. This also checks permissions on the
178  * target namespace, locks it against concurrent drops, checks for a
179  * preexisting relation in that namespace with the same name, and updates
180  * stmt->relation->relpersistence if the selected namespace is temporary.
181  */
182  setup_parser_errposition_callback(&pcbstate, pstate,
183  stmt->relation->location);
184  namespaceid =
186  &existing_relid);
188 
189  /*
190  * If the relation already exists and the user specified "IF NOT EXISTS",
191  * bail out with a NOTICE.
192  */
193  if (stmt->if_not_exists && OidIsValid(existing_relid))
194  {
195  ereport(NOTICE,
196  (errcode(ERRCODE_DUPLICATE_TABLE),
197  errmsg("relation \"%s\" already exists, skipping",
198  stmt->relation->relname)));
199  return NIL;
200  }
201 
202  /*
203  * If the target relation name isn't schema-qualified, make it so. This
204  * prevents some corner cases in which added-on rewritten commands might
205  * think they should apply to other relations that have the same name and
206  * are earlier in the search path. But a local temp table is effectively
207  * specified to be in pg_temp, so no need for anything extra in that case.
208  */
209  if (stmt->relation->schemaname == NULL
211  stmt->relation->schemaname = get_namespace_name(namespaceid);
212 
213  /* Set up CreateStmtContext */
214  cxt.pstate = pstate;
215  if (IsA(stmt, CreateForeignTableStmt))
216  {
217  cxt.stmtType = "CREATE FOREIGN TABLE";
218  cxt.isforeign = true;
219  }
220  else
221  {
222  cxt.stmtType = "CREATE TABLE";
223  cxt.isforeign = false;
224  }
225  cxt.relation = stmt->relation;
226  cxt.rel = NULL;
227  cxt.inhRelations = stmt->inhRelations;
228  cxt.isalter = false;
229  cxt.columns = NIL;
230  cxt.ckconstraints = NIL;
231  cxt.fkconstraints = NIL;
232  cxt.ixconstraints = NIL;
233  cxt.inh_indexes = NIL;
234  cxt.blist = NIL;
235  cxt.alist = NIL;
236  cxt.pkey = NULL;
237  cxt.ispartitioned = stmt->partspec != NULL;
238 
239  /*
240  * Notice that we allow OIDs here only for plain tables, even though
241  * foreign tables also support them. This is necessary because the
242  * default_with_oids GUC must apply only to plain tables and not any other
243  * relkind; doing otherwise would break existing pg_dump files. We could
244  * allow explicit "WITH OIDS" while not allowing default_with_oids to
245  * affect other relkinds, but it would complicate interpretOidsOption(),
246  * and right now there's no WITH OIDS option in CREATE FOREIGN TABLE
247  * anyway.
248  */
249  cxt.hasoids = interpretOidsOption(stmt->options, !cxt.isforeign);
250 
251  Assert(!stmt->ofTypename || !stmt->inhRelations); /* grammar enforces */
252 
253  if (stmt->ofTypename)
254  transformOfType(&cxt, stmt->ofTypename);
255 
256  if (stmt->partspec)
257  {
258  int partnatts = list_length(stmt->partspec->partParams);
259 
260  if (stmt->inhRelations && !stmt->partbound)
261  ereport(ERROR,
262  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
263  errmsg("cannot create partitioned table as inheritance child")));
264 
265  if (partnatts > PARTITION_MAX_KEYS)
266  ereport(ERROR,
267  (errcode(ERRCODE_TOO_MANY_COLUMNS),
268  errmsg("cannot partition using more than %d columns",
270 
271  if (!pg_strcasecmp(stmt->partspec->strategy, "list") &&
272  partnatts > 1)
273  ereport(ERROR,
274  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
275  errmsg("cannot list partition using more than one column")));
276  }
277 
278  /*
279  * Run through each primary element in the table creation clause. Separate
280  * column defs from constraints, and do preliminary analysis.
281  */
282  foreach(elements, stmt->tableElts)
283  {
284  Node *element = lfirst(elements);
285 
286  switch (nodeTag(element))
287  {
288  case T_ColumnDef:
289  transformColumnDefinition(&cxt, (ColumnDef *) element);
290  break;
291 
292  case T_Constraint:
293  transformTableConstraint(&cxt, (Constraint *) element);
294  break;
295 
296  case T_TableLikeClause:
297  like_found = true;
298  transformTableLikeClause(&cxt, (TableLikeClause *) element);
299  break;
300 
301  default:
302  elog(ERROR, "unrecognized node type: %d",
303  (int) nodeTag(element));
304  break;
305  }
306  }
307 
308  /*
309  * If we had any LIKE tables, they may require creation of an OID column
310  * even though the command's own WITH clause didn't ask for one (or,
311  * perhaps, even specifically rejected having one). Insert a WITH option
312  * to ensure that happens. We prepend to the list because the first oid
313  * option will be honored, and we want to override anything already there.
314  * (But note that DefineRelation will override this again to add an OID
315  * column if one appears in an inheritance parent table.)
316  */
317  if (like_found && cxt.hasoids)
318  stmt->options = lcons(makeDefElem("oids",
319  (Node *) makeInteger(true), -1),
320  stmt->options);
321 
322  /*
323  * transformIndexConstraints wants cxt.alist to contain only index
324  * statements, so transfer anything we already have into save_alist.
325  */
326  save_alist = cxt.alist;
327  cxt.alist = NIL;
328 
329  Assert(stmt->constraints == NIL);
330 
331  /*
332  * Postprocess constraints that give rise to index definitions.
333  */
335 
336  /*
337  * Postprocess foreign-key constraints.
338  */
339  transformFKConstraints(&cxt, true, false);
340 
341  /*
342  * Postprocess check constraints.
343  */
344  transformCheckConstraints(&cxt, true);
345 
346  /*
347  * Output results.
348  */
349  stmt->tableElts = cxt.columns;
350  stmt->constraints = cxt.ckconstraints;
351 
352  result = lappend(cxt.blist, stmt);
353  result = list_concat(result, cxt.alist);
354  result = list_concat(result, save_alist);
355 
356  return result;
357 }
RangeVar * relation
Definition: parsenodes.h:1948
#define NIL
Definition: pg_list.h:69
List * inhRelations
Definition: parsenodes.h:1950
#define IsA(nodeptr, _type_)
Definition: nodes.h:557
List * partParams
Definition: parsenodes.h:765
Definition: nodes.h:506
int errcode(int sqlerrcode)
Definition: elog.c:575
#define PARTITION_MAX_KEYS
List * list_concat(List *list1, List *list2)
Definition: list.c:321
return result
Definition: formatting.c:1618
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:538
DefElem * makeDefElem(char *name, Node *arg, int location)
Definition: makefuncs.c:543
char * schemaname
Definition: primnodes.h:67
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:44
void * copyObject(const void *from)
Definition: copyfuncs.c:4619
List * constraints
Definition: parsenodes.h:1955
bool if_not_exists
Definition: parsenodes.h:1959
int location
Definition: primnodes.h:73
ParseState * pstate
Definition: parse_utilcmd.c:74
char * relname
Definition: primnodes.h:68
Value * makeInteger(long i)
Definition: value.c:23
RangeVar * relation
Definition: parse_utilcmd.c:76
IndexStmt * pkey
Definition: parse_utilcmd.c:91
void cancel_parser_errposition_callback(ParseCallbackState *pcbstate)
Definition: parse_node.c:159
#define ERROR
Definition: elog.h:43
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3006
#define NoLock
Definition: lockdefs.h:34
List * options
Definition: parsenodes.h:1956
const char * p_sourcetext
Definition: parse_node.h:167
#define ereport(elevel, rest)
Definition: elog.h:122
void setup_parser_errposition_callback(ParseCallbackState *pcbstate, ParseState *pstate, int location)
Definition: parse_node.c:143
List * lappend(List *list, void *datum)
Definition: list.c:128
static chr element(struct vars *v, const chr *startp, const chr *endp)
Definition: regc_locale.c:380
Oid RangeVarGetAndCheckCreationNamespace(RangeVar *relation, LOCKMODE lockmode, Oid *existing_relation_id)
Definition: namespace.c:507
Node * partbound
Definition: parsenodes.h:1952
#define NOTICE
Definition: elog.h:37
List * tableElts
Definition: parsenodes.h:1949
List * lcons(void *datum, List *list)
Definition: list.c:259
static void transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
#define NULL
Definition: c.h:229
#define Assert(condition)
Definition: c.h:675
#define lfirst(lc)
Definition: pg_list.h:106
static void transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
static int list_length(const List *l)
Definition: pg_list.h:89
static void transformFKConstraints(CreateStmtContext *cxt, bool skipValidation, bool isAddConstraint)
#define nodeTag(nodeptr)
Definition: nodes.h:511
const char * stmtType
Definition: parse_utilcmd.c:75
char relpersistence
Definition: primnodes.h:71
bool interpretOidsOption(List *defList, bool allowOids)
Definition: parse_clause.c:244
char * strategy
Definition: parsenodes.h:764
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define elog
Definition: elog.h:219
#define RELPERSISTENCE_TEMP
Definition: pg_class.h:172
static void transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
static void transformIndexConstraints(CreateStmtContext *cxt)
PartitionSpec * partspec
Definition: parsenodes.h:1953
Definition: pg_list.h:45
static void transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_clause)
TypeName * ofTypename
Definition: parsenodes.h:1954
static void transformCheckConstraints(CreateStmtContext *cxt, bool skipValidation)
IndexStmt* transformIndexStmt ( Oid  relid,
IndexStmt stmt,
const char *  queryString 
)

Definition at line 2095 of file parse_utilcmd.c.

References addRangeTableEntryForRelation(), addRTEtoQuery(), assign_expr_collations(), copyObject(), ereport, errcode(), errmsg(), ERROR, IndexElem::expr, EXPR_KIND_INDEX_EXPRESSION, EXPR_KIND_INDEX_PREDICATE, FigureIndexColname(), free_parsestate(), heap_close, IndexElem::indexcolname, IndexStmt::indexParams, lfirst, list_length(), make_parsestate(), NoLock, NULL, ParseState::p_rtable, ParseState::p_sourcetext, relation_open(), IndexStmt::transformed, transformExpr(), transformWhereClause(), and IndexStmt::whereClause.

Referenced by ATPostAlterTypeParse(), ProcessUtilitySlow(), and transformAlterTableStmt().

2096 {
2097  ParseState *pstate;
2098  RangeTblEntry *rte;
2099  ListCell *l;
2100  Relation rel;
2101 
2102  /* Nothing to do if statement already transformed. */
2103  if (stmt->transformed)
2104  return stmt;
2105 
2106  /*
2107  * We must not scribble on the passed-in IndexStmt, so copy it. (This is
2108  * overkill, but easy.)
2109  */
2110  stmt = (IndexStmt *) copyObject(stmt);
2111 
2112  /* Set up pstate */
2113  pstate = make_parsestate(NULL);
2114  pstate->p_sourcetext = queryString;
2115 
2116  /*
2117  * Put the parent table into the rtable so that the expressions can refer
2118  * to its fields without qualification. Caller is responsible for locking
2119  * relation, but we still need to open it.
2120  */
2121  rel = relation_open(relid, NoLock);
2122  rte = addRangeTableEntryForRelation(pstate, rel, NULL, false, true);
2123 
2124  /* no to join list, yes to namespaces */
2125  addRTEtoQuery(pstate, rte, false, true, true);
2126 
2127  /* take care of the where clause */
2128  if (stmt->whereClause)
2129  {
2130  stmt->whereClause = transformWhereClause(pstate,
2131  stmt->whereClause,
2133  "WHERE");
2134  /* we have to fix its collations too */
2135  assign_expr_collations(pstate, stmt->whereClause);
2136  }
2137 
2138  /* take care of any index expressions */
2139  foreach(l, stmt->indexParams)
2140  {
2141  IndexElem *ielem = (IndexElem *) lfirst(l);
2142 
2143  if (ielem->expr)
2144  {
2145  /* Extract preliminary index col name before transforming expr */
2146  if (ielem->indexcolname == NULL)
2147  ielem->indexcolname = FigureIndexColname(ielem->expr);
2148 
2149  /* Now do parse transformation of the expression */
2150  ielem->expr = transformExpr(pstate, ielem->expr,
2152 
2153  /* We have to fix its collations too */
2154  assign_expr_collations(pstate, ielem->expr);
2155 
2156  /*
2157  * transformExpr() should have already rejected subqueries,
2158  * aggregates, window functions, and SRFs, based on the EXPR_KIND_
2159  * for an index expression.
2160  *
2161  * DefineIndex() will make more checks.
2162  */
2163  }
2164  }
2165 
2166  /*
2167  * Check that only the base rel is mentioned. (This should be dead code
2168  * now that add_missing_from is history.)
2169  */
2170  if (list_length(pstate->p_rtable) != 1)
2171  ereport(ERROR,
2172  (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
2173  errmsg("index expressions and predicates can refer only to the table being indexed")));
2174 
2175  free_parsestate(pstate);
2176 
2177  /* Close relation */
2178  heap_close(rel, NoLock);
2179 
2180  /* Mark statement as successfully transformed */
2181  stmt->transformed = true;
2182 
2183  return stmt;
2184 }
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:147
Node * whereClause
Definition: parsenodes.h:2645
int errcode(int sqlerrcode)
Definition: elog.c:575
char * FigureIndexColname(Node *node)
#define heap_close(r, l)
Definition: heapam.h:97
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:44
void * copyObject(const void *from)
Definition: copyfuncs.c:4619
void assign_expr_collations(ParseState *pstate, Node *expr)
Node * expr
Definition: parsenodes.h:678
#define ERROR
Definition: elog.h:43
#define NoLock
Definition: lockdefs.h:34
bool transformed
Definition: parsenodes.h:2655
char * indexcolname
Definition: parsenodes.h:679
const char * p_sourcetext
Definition: parse_node.h:167
#define ereport(elevel, rest)
Definition: elog.h:122
void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
#define NULL
Definition: c.h:229
#define lfirst(lc)
Definition: pg_list.h:106
Node * transformWhereClause(ParseState *pstate, Node *clause, ParseExprKind exprKind, const char *constructName)
List * indexParams
Definition: parsenodes.h:2643
static int list_length(const List *l)
Definition: pg_list.h:89
RangeTblEntry * addRangeTableEntryForRelation(ParseState *pstate, Relation rel, Alias *alias, bool inh, bool inFromCl)
int errmsg(const char *fmt,...)
Definition: elog.c:797
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1117
void free_parsestate(ParseState *pstate)
Definition: parse_node.c:75
List * p_rtable
Definition: parse_node.h:168
Node* transformPartitionBound ( ParseState pstate,
Relation  parent,
Node bound 
)

Definition at line 3056 of file parse_utilcmd.c.

References Assert, COERCE_IMPLICIT_CAST, coerce_to_target_type(), COERCION_ASSIGNMENT, copyObject(), deparse_context_for(), deparse_expression(), elog, equal(), ereport, errcode(), errmsg(), ERROR, expression_planner(), exprLocation(), exprType(), forboth, format_type_be(), get_partition_col_typid(), get_partition_col_typmod(), get_partition_exprs(), get_partition_natts(), get_partition_strategy(), get_relid_attribute_name(), i, PartitionRangeDatum::infinite, lappend(), lfirst, linitial, list_length(), list_nth(), PartitionBoundSpec::listdatums, A_Const::location, PartitionBoundSpec::lowerdatums, make_const(), NIL, NULL, parser_errposition(), PartitionKeyData::partattrs, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, RelationGetPartitionKey, RelationGetRelationName, RelationGetRelid, PartitionBoundSpec::strategy, PartitionBoundSpec::upperdatums, A_Const::val, value, and PartitionRangeDatum::value.

Referenced by DefineRelation(), and transformPartitionCmd().

3057 {
3058  PartitionBoundSpec *spec = (PartitionBoundSpec *) bound,
3059  *result_spec;
3060  PartitionKey key = RelationGetPartitionKey(parent);
3061  char strategy = get_partition_strategy(key);
3062  int partnatts = get_partition_natts(key);
3063  List *partexprs = get_partition_exprs(key);
3064 
3065  result_spec = copyObject(spec);
3066 
3067  if (strategy == PARTITION_STRATEGY_LIST)
3068  {
3069  ListCell *cell;
3070  char *colname;
3071 
3072  /* Get the only column's name in case we need to output an error */
3073  if (key->partattrs[0] != 0)
3074  colname = get_relid_attribute_name(RelationGetRelid(parent),
3075  key->partattrs[0]);
3076  else
3077  colname = deparse_expression((Node *) linitial(partexprs),
3079  RelationGetRelid(parent)),
3080  false, false);
3081 
3082  if (spec->strategy != PARTITION_STRATEGY_LIST)
3083  ereport(ERROR,
3084  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3085  errmsg("invalid bound specification for a list partition"),
3086  parser_errposition(pstate, exprLocation(bound))));
3087 
3088  result_spec->listdatums = NIL;
3089  foreach(cell, spec->listdatums)
3090  {
3091  A_Const *con = (A_Const *) lfirst(cell);
3092  Node *value;
3093  ListCell *cell2;
3094  bool duplicate;
3095 
3096  value = (Node *) make_const(pstate, &con->val, con->location);
3097  value = coerce_to_target_type(pstate,
3098  value, exprType(value),
3099  get_partition_col_typid(key, 0),
3100  get_partition_col_typmod(key, 0),
3103  -1);
3104 
3105  if (value == NULL)
3106  ereport(ERROR,
3107  (errcode(ERRCODE_DATATYPE_MISMATCH),
3108  errmsg("specified value cannot be cast to type \"%s\" of column \"%s\"",
3110  colname),
3111  parser_errposition(pstate,
3112  exprLocation((Node *) con))));
3113 
3114  /* Simplify the expression */
3115  value = (Node *) expression_planner((Expr *) value);
3116 
3117  /* Don't add to the result if the value is a duplicate */
3118  duplicate = false;
3119  foreach(cell2, result_spec->listdatums)
3120  {
3121  Const *value2 = (Const *) lfirst(cell2);
3122 
3123  if (equal(value, value2))
3124  {
3125  duplicate = true;
3126  break;
3127  }
3128  }
3129  if (duplicate)
3130  continue;
3131 
3132  result_spec->listdatums = lappend(result_spec->listdatums,
3133  value);
3134  }
3135  }
3136  else if (strategy == PARTITION_STRATEGY_RANGE)
3137  {
3138  ListCell *cell1,
3139  *cell2;
3140  int i,
3141  j;
3142  char *colname;
3143 
3144  if (spec->strategy != PARTITION_STRATEGY_RANGE)
3145  ereport(ERROR,
3146  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3147  errmsg("invalid bound specification for a range partition"),
3148  parser_errposition(pstate, exprLocation(bound))));
3149 
3150  Assert(spec->lowerdatums != NIL && spec->upperdatums != NIL);
3151 
3152  if (list_length(spec->lowerdatums) != partnatts)
3153  ereport(ERROR,
3154  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3155  errmsg("FROM must specify exactly one value per partitioning column")));
3156  if (list_length(spec->upperdatums) != partnatts)
3157  ereport(ERROR,
3158  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3159  errmsg("TO must specify exactly one value per partitioning column")));
3160 
3161  i = j = 0;
3162  result_spec->lowerdatums = result_spec->upperdatums = NIL;
3163  forboth(cell1, spec->lowerdatums, cell2, spec->upperdatums)
3164  {
3165  PartitionRangeDatum *ldatum,
3166  *rdatum;
3167  Node *value;
3168  A_Const *lcon = NULL,
3169  *rcon = NULL;
3170 
3171  ldatum = (PartitionRangeDatum *) lfirst(cell1);
3172  rdatum = (PartitionRangeDatum *) lfirst(cell2);
3173  /* Get the column's name in case we need to output an error */
3174  if (key->partattrs[i] != 0)
3175  colname = get_relid_attribute_name(RelationGetRelid(parent),
3176  key->partattrs[i]);
3177  else
3178  {
3179  colname = deparse_expression((Node *) list_nth(partexprs, j),
3181  RelationGetRelid(parent)),
3182  false, false);
3183  ++j;
3184  }
3185 
3186  if (!ldatum->infinite)
3187  lcon = (A_Const *) ldatum->value;
3188  if (!rdatum->infinite)
3189  rcon = (A_Const *) rdatum->value;
3190 
3191  if (lcon)
3192  {
3193  value = (Node *) make_const(pstate, &lcon->val, lcon->location);
3194  if (((Const *) value)->constisnull)
3195  ereport(ERROR,
3196  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3197  errmsg("cannot specify NULL in range bound")));
3198  value = coerce_to_target_type(pstate,
3199  value, exprType(value),
3200  get_partition_col_typid(key, i),
3201  get_partition_col_typmod(key, i),
3204  -1);
3205  if (value == NULL)
3206  ereport(ERROR,
3207  (errcode(ERRCODE_DATATYPE_MISMATCH),
3208  errmsg("specified value cannot be cast to type \"%s\" of column \"%s\"",
3210  colname),
3211  parser_errposition(pstate, exprLocation((Node *) ldatum))));
3212 
3213  /* Simplify the expression */
3214  value = (Node *) expression_planner((Expr *) value);
3215  ldatum->value = value;
3216  }
3217 
3218  if (rcon)
3219  {
3220  value = (Node *) make_const(pstate, &rcon->val, rcon->location);
3221  if (((Const *) value)->constisnull)
3222  ereport(ERROR,
3223  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3224  errmsg("cannot specify NULL in range bound")));
3225  value = coerce_to_target_type(pstate,
3226  value, exprType(value),
3227  get_partition_col_typid(key, i),
3228  get_partition_col_typmod(key, i),
3231  -1);
3232  if (value == NULL)
3233  ereport(ERROR,
3234  (errcode(ERRCODE_DATATYPE_MISMATCH),
3235  errmsg("specified value cannot be cast to type \"%s\" of column \"%s\"",
3237  colname),
3238  parser_errposition(pstate, exprLocation((Node *) rdatum))));
3239 
3240  /* Simplify the expression */
3241  value = (Node *) expression_planner((Expr *) value);
3242  rdatum->value = value;
3243  }
3244 
3245  result_spec->lowerdatums = lappend(result_spec->lowerdatums,
3246  copyObject(ldatum));
3247  result_spec->upperdatums = lappend(result_spec->upperdatums,
3248  copyObject(rdatum));
3249 
3250  ++i;
3251  }
3252  }
3253  else
3254  elog(ERROR, "unexpected partition strategy: %d", (int) strategy);
3255 
3256  return (Node *) result_spec;
3257 }
#define NIL
Definition: pg_list.h:69
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:174
int exprLocation(const Node *expr)
Definition: nodeFuncs.c:1204
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:2946
Expr * expression_planner(Expr *expr)
Definition: planner.c:5927
Definition: nodes.h:506
int errcode(int sqlerrcode)
Definition: elog.c:575
char * format_type_be(Oid type_oid)
Definition: format_type.c:94
Const * make_const(ParseState *pstate, Value *value, int location)
Definition: parse_node.c:468
void * copyObject(const void *from)
Definition: copyfuncs.c:4619
static struct @114 value
static int get_partition_natts(PartitionKey key)
Definition: rel.h:597
#define linitial(l)
Definition: pg_list.h:110
#define ERROR
Definition: elog.h:43
Node * coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, Oid targettype, int32 targettypmod, CoercionContext ccontext, CoercionForm cformat, int location)
Definition: parse_coerce.c:77
void * list_nth(const List *list, int n)
Definition: list.c:410
static List * get_partition_exprs(PartitionKey key)
Definition: rel.h:603
List * deparse_context_for(const char *aliasname, Oid relid)
Definition: ruleutils.c:2946
#define RelationGetRelationName(relation)
Definition: rel.h:437
#define ereport(elevel, rest)
Definition: elog.h:122
List * lappend(List *list, void *datum)
Definition: list.c:128
static int32 get_partition_col_typmod(PartitionKey key, int col)
Definition: rel.h:624
char * get_relid_attribute_name(Oid relid, AttrNumber attnum)
Definition: lsyscache.c:801
AttrNumber * partattrs
Definition: rel.h:56
static Oid get_partition_col_typid(PartitionKey key, int col)
Definition: rel.h:618
static int get_partition_strategy(PartitionKey key)
Definition: rel.h:591
#define NULL
Definition: c.h:229
#define Assert(condition)
Definition: c.h:675
#define lfirst(lc)
Definition: pg_list.h:106
int location
Definition: parsenodes.h:280
char * deparse_expression(Node *expr, List *dpcontext, bool forceprefix, bool showimplicit)
Definition: ruleutils.c:2887
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
static int list_length(const List *l)
Definition: pg_list.h:89
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:109
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:769
#define RelationGetPartitionKey(relation)
Definition: rel.h:585
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:770
int errmsg(const char *fmt,...)
Definition: elog.c:797
int i
#define elog
Definition: elog.h:219
Value val
Definition: parsenodes.h:279
Definition: pg_list.h:45
#define RelationGetRelid(relation)
Definition: rel.h:417
void transformRuleStmt ( RuleStmt stmt,
const char *  queryString,
List **  actions,
Node **  whereClause 
)

Definition at line 2200 of file parse_utilcmd.c.

References AccessExclusiveLock, RuleStmt::actions, addRangeTableEntryForRelation(), addRTEtoQuery(), assign_expr_collations(), CMD_DELETE, CMD_INSERT, CMD_NOTHING, CMD_SELECT, CMD_UPDATE, CMD_UTILITY, Query::commandType, copyObject(), Query::cteList, elog, ereport, errcode(), errmsg(), ERROR, RuleStmt::event, EXPR_KIND_WHERE, free_parsestate(), FromExpr::fromlist, getInsertSelectQuery(), heap_close, heap_openrv(), Query::jointree, lappend(), lfirst, list_length(), list_make1, make_parsestate(), makeAlias(), makeFromExpr(), makeNode, NIL, NoLock, NULL, ParseState::p_joinlist, ParseState::p_rtable, ParseState::p_sourcetext, PRS2_NEW_VARNO, PRS2_OLD_VARNO, rangeTableEntry_used(), RelationData::rd_rel, RuleStmt::relation, RELKIND_MATVIEW, RangeTblEntry::requiredPerms, Query::rtable, Query::setOperations, transformStmt(), transformWhereClause(), and RuleStmt::whereClause.

Referenced by DefineRule().

2202 {
2203  Relation rel;
2204  ParseState *pstate;
2205  RangeTblEntry *oldrte;
2206  RangeTblEntry *newrte;
2207 
2208  /*
2209  * To avoid deadlock, make sure the first thing we do is grab
2210  * AccessExclusiveLock on the target relation. This will be needed by
2211  * DefineQueryRewrite(), and we don't want to grab a lesser lock
2212  * beforehand.
2213  */
2215 
2216  if (rel->rd_rel->relkind == RELKIND_MATVIEW)
2217  ereport(ERROR,
2218  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2219  errmsg("rules on materialized views are not supported")));
2220 
2221  /* Set up pstate */
2222  pstate = make_parsestate(NULL);
2223  pstate->p_sourcetext = queryString;
2224 
2225  /*
2226  * NOTE: 'OLD' must always have a varno equal to 1 and 'NEW' equal to 2.
2227  * Set up their RTEs in the main pstate for use in parsing the rule
2228  * qualification.
2229  */
2230  oldrte = addRangeTableEntryForRelation(pstate, rel,
2231  makeAlias("old", NIL),
2232  false, false);
2233  newrte = addRangeTableEntryForRelation(pstate, rel,
2234  makeAlias("new", NIL),
2235  false, false);
2236  /* Must override addRangeTableEntry's default access-check flags */
2237  oldrte->requiredPerms = 0;
2238  newrte->requiredPerms = 0;
2239 
2240  /*
2241  * They must be in the namespace too for lookup purposes, but only add the
2242  * one(s) that are relevant for the current kind of rule. In an UPDATE
2243  * rule, quals must refer to OLD.field or NEW.field to be unambiguous, but
2244  * there's no need to be so picky for INSERT & DELETE. We do not add them
2245  * to the joinlist.
2246  */
2247  switch (stmt->event)
2248  {
2249  case CMD_SELECT:
2250  addRTEtoQuery(pstate, oldrte, false, true, true);
2251  break;
2252  case CMD_UPDATE:
2253  addRTEtoQuery(pstate, oldrte, false, true, true);
2254  addRTEtoQuery(pstate, newrte, false, true, true);
2255  break;
2256  case CMD_INSERT:
2257  addRTEtoQuery(pstate, newrte, false, true, true);
2258  break;
2259  case CMD_DELETE:
2260  addRTEtoQuery(pstate, oldrte, false, true, true);
2261  break;
2262  default:
2263  elog(ERROR, "unrecognized event type: %d",
2264  (int) stmt->event);
2265  break;
2266  }
2267 
2268  /* take care of the where clause */
2269  *whereClause = transformWhereClause(pstate,
2270  (Node *) copyObject(stmt->whereClause),
2272  "WHERE");
2273  /* we have to fix its collations too */
2274  assign_expr_collations(pstate, *whereClause);
2275 
2276  /* this is probably dead code without add_missing_from: */
2277  if (list_length(pstate->p_rtable) != 2) /* naughty, naughty... */
2278  ereport(ERROR,
2279  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2280  errmsg("rule WHERE condition cannot contain references to other relations")));
2281 
2282  /*
2283  * 'instead nothing' rules with a qualification need a query rangetable so
2284  * the rewrite handler can add the negated rule qualification to the
2285  * original query. We create a query with the new command type CMD_NOTHING
2286  * here that is treated specially by the rewrite system.
2287  */
2288  if (stmt->actions == NIL)
2289  {
2290  Query *nothing_qry = makeNode(Query);
2291 
2292  nothing_qry->commandType = CMD_NOTHING;
2293  nothing_qry->rtable = pstate->p_rtable;
2294  nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */
2295 
2296  *actions = list_make1(nothing_qry);
2297  }
2298  else
2299  {
2300  ListCell *l;
2301  List *newactions = NIL;
2302 
2303  /*
2304  * transform each statement, like parse_sub_analyze()
2305  */
2306  foreach(l, stmt->actions)
2307  {
2308  Node *action = (Node *) lfirst(l);
2309  ParseState *sub_pstate = make_parsestate(NULL);
2310  Query *sub_qry,
2311  *top_subqry;
2312  bool has_old,
2313  has_new;
2314 
2315  /*
2316  * Since outer ParseState isn't parent of inner, have to pass down
2317  * the query text by hand.
2318  */
2319  sub_pstate->p_sourcetext = queryString;
2320 
2321  /*
2322  * Set up OLD/NEW in the rtable for this statement. The entries
2323  * are added only to relnamespace, not varnamespace, because we
2324  * don't want them to be referred to by unqualified field names
2325  * nor "*" in the rule actions. We decide later whether to put
2326  * them in the joinlist.
2327  */
2328  oldrte = addRangeTableEntryForRelation(sub_pstate, rel,
2329  makeAlias("old", NIL),
2330  false, false);
2331  newrte = addRangeTableEntryForRelation(sub_pstate, rel,
2332  makeAlias("new", NIL),
2333  false, false);
2334  oldrte->requiredPerms = 0;
2335  newrte->requiredPerms = 0;
2336  addRTEtoQuery(sub_pstate, oldrte, false, true, false);
2337  addRTEtoQuery(sub_pstate, newrte, false, true, false);
2338 
2339  /* Transform the rule action statement */
2340  top_subqry = transformStmt(sub_pstate,
2341  (Node *) copyObject(action));
2342 
2343  /*
2344  * We cannot support utility-statement actions (eg NOTIFY) with
2345  * nonempty rule WHERE conditions, because there's no way to make
2346  * the utility action execute conditionally.
2347  */
2348  if (top_subqry->commandType == CMD_UTILITY &&
2349  *whereClause != NULL)
2350  ereport(ERROR,
2351  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2352  errmsg("rules with WHERE conditions can only have SELECT, INSERT, UPDATE, or DELETE actions")));
2353 
2354  /*
2355  * If the action is INSERT...SELECT, OLD/NEW have been pushed down
2356  * into the SELECT, and that's what we need to look at. (Ugly
2357  * kluge ... try to fix this when we redesign querytrees.)
2358  */
2359  sub_qry = getInsertSelectQuery(top_subqry, NULL);
2360 
2361  /*
2362  * If the sub_qry is a setop, we cannot attach any qualifications
2363  * to it, because the planner won't notice them. This could
2364  * perhaps be relaxed someday, but for now, we may as well reject
2365  * such a rule immediately.
2366  */
2367  if (sub_qry->setOperations != NULL && *whereClause != NULL)
2368  ereport(ERROR,
2369  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2370  errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
2371 
2372  /*
2373  * Validate action's use of OLD/NEW, qual too
2374  */
2375  has_old =
2376  rangeTableEntry_used((Node *) sub_qry, PRS2_OLD_VARNO, 0) ||
2377  rangeTableEntry_used(*whereClause, PRS2_OLD_VARNO, 0);
2378  has_new =
2379  rangeTableEntry_used((Node *) sub_qry, PRS2_NEW_VARNO, 0) ||
2380  rangeTableEntry_used(*whereClause, PRS2_NEW_VARNO, 0);
2381 
2382  switch (stmt->event)
2383  {
2384  case CMD_SELECT:
2385  if (has_old)
2386  ereport(ERROR,
2387  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2388  errmsg("ON SELECT rule cannot use OLD")));
2389  if (has_new)
2390  ereport(ERROR,
2391  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2392  errmsg("ON SELECT rule cannot use NEW")));
2393  break;
2394  case CMD_UPDATE:
2395  /* both are OK */
2396  break;
2397  case CMD_INSERT:
2398  if (has_old)
2399  ereport(ERROR,
2400  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2401  errmsg("ON INSERT rule cannot use OLD")));
2402  break;
2403  case CMD_DELETE:
2404  if (has_new)
2405  ereport(ERROR,
2406  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2407  errmsg("ON DELETE rule cannot use NEW")));
2408  break;
2409  default:
2410  elog(ERROR, "unrecognized event type: %d",
2411  (int) stmt->event);
2412  break;
2413  }
2414 
2415  /*
2416  * OLD/NEW are not allowed in WITH queries, because they would
2417  * amount to outer references for the WITH, which we disallow.
2418  * However, they were already in the outer rangetable when we
2419  * analyzed the query, so we have to check.
2420  *
2421  * Note that in the INSERT...SELECT case, we need to examine the
2422  * CTE lists of both top_subqry and sub_qry.
2423  *
2424  * Note that we aren't digging into the body of the query looking
2425  * for WITHs in nested sub-SELECTs. A WITH down there can
2426  * legitimately refer to OLD/NEW, because it'd be an
2427  * indirect-correlated outer reference.
2428  */
2429  if (rangeTableEntry_used((Node *) top_subqry->cteList,
2430  PRS2_OLD_VARNO, 0) ||
2431  rangeTableEntry_used((Node *) sub_qry->cteList,
2432  PRS2_OLD_VARNO, 0))
2433  ereport(ERROR,
2434  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2435  errmsg("cannot refer to OLD within WITH query")));
2436  if (rangeTableEntry_used((Node *) top_subqry->cteList,
2437  PRS2_NEW_VARNO, 0) ||
2438  rangeTableEntry_used((Node *) sub_qry->cteList,
2439  PRS2_NEW_VARNO, 0))
2440  ereport(ERROR,
2441  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2442  errmsg("cannot refer to NEW within WITH query")));
2443 
2444  /*
2445  * For efficiency's sake, add OLD to the rule action's jointree
2446  * only if it was actually referenced in the statement or qual.
2447  *
2448  * For INSERT, NEW is not really a relation (only a reference to
2449  * the to-be-inserted tuple) and should never be added to the
2450  * jointree.
2451  *
2452  * For UPDATE, we treat NEW as being another kind of reference to
2453  * OLD, because it represents references to *transformed* tuples
2454  * of the existing relation. It would be wrong to enter NEW
2455  * separately in the jointree, since that would cause a double
2456  * join of the updated relation. It's also wrong to fail to make
2457  * a jointree entry if only NEW and not OLD is mentioned.
2458  */
2459  if (has_old || (has_new && stmt->event == CMD_UPDATE))
2460  {
2461  /*
2462  * If sub_qry is a setop, manipulating its jointree will do no
2463  * good at all, because the jointree is dummy. (This should be
2464  * a can't-happen case because of prior tests.)
2465  */
2466  if (sub_qry->setOperations != NULL)
2467  ereport(ERROR,
2468  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2469  errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
2470  /* hack so we can use addRTEtoQuery() */
2471  sub_pstate->p_rtable = sub_qry->rtable;
2472  sub_pstate->p_joinlist = sub_qry->jointree->fromlist;
2473  addRTEtoQuery(sub_pstate, oldrte, true, false, false);
2474  sub_qry->jointree->fromlist = sub_pstate->p_joinlist;
2475  }
2476 
2477  newactions = lappend(newactions, top_subqry);
2478 
2479  free_parsestate(sub_pstate);
2480  }
2481 
2482  *actions = newactions;
2483  }
2484 
2485  free_parsestate(pstate);
2486 
2487  /* Close relation, but keep the exclusive lock */
2488  heap_close(rel, NoLock);
2489 }
#define NIL
Definition: pg_list.h:69
Query * getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr)
Definition: rewriteManip.c:921
FromExpr * makeFromExpr(List *fromlist, Node *quals)
Definition: makefuncs.c:282
FromExpr * jointree
Definition: parsenodes.h:129
#define RELKIND_MATVIEW
Definition: pg_class.h:165
Definition: nodes.h:506
int errcode(int sqlerrcode)
Definition: elog.c:575
List * actions
Definition: parsenodes.h:2818
AclMode requiredPerms
Definition: parsenodes.h:1004
List * fromlist
Definition: primnodes.h:1455
#define heap_close(r, l)
Definition: heapam.h:97
Form_pg_class rd_rel
Definition: rel.h:114
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:44
void * copyObject(const void *from)
Definition: copyfuncs.c:4619
#define list_make1(x1)
Definition: pg_list.h:133
void assign_expr_collations(ParseState *pstate, Node *expr)
Alias * makeAlias(const char *aliasname, List *colnames)
Definition: makefuncs.c:384
List * rtable
Definition: parsenodes.h:128
#define ERROR
Definition: elog.h:43
#define NoLock
Definition: lockdefs.h:34
const char * p_sourcetext
Definition: parse_node.h:167
#define ereport(elevel, rest)
Definition: elog.h:122
void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
Query * transformStmt(ParseState *pstate, Node *parseTree)
Definition: analyze.c:247
List * lappend(List *list, void *datum)
Definition: list.c:128
#define PRS2_OLD_VARNO
Definition: primnodes.h:160
Relation heap_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition: heapam.c:1315
CmdType commandType
Definition: parsenodes.h:103
#define makeNode(_type_)
Definition: nodes.h:554
CmdType event
Definition: parsenodes.h:2816
#define NULL
Definition: c.h:229
#define lfirst(lc)
Definition: pg_list.h:106
Node * transformWhereClause(ParseState *pstate, Node *clause, ParseExprKind exprKind, const char *constructName)
Node * whereClause
Definition: parsenodes.h:2815
static int list_length(const List *l)
Definition: pg_list.h:89
RangeTblEntry * addRangeTableEntryForRelation(ParseState *pstate, Relation rel, Alias *alias, bool inh, bool inFromCl)
#define AccessExclusiveLock
Definition: lockdefs.h:46
List * cteList
Definition: parsenodes.h:126
Node * setOperations
Definition: parsenodes.h:154
int errmsg(const char *fmt,...)
Definition: elog.c:797
List * p_joinlist
Definition: parse_node.h:170
bool rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
Definition: rewriteManip.c:889
#define elog
Definition: elog.h:219
RangeVar * relation
Definition: parsenodes.h:2813
void free_parsestate(ParseState *pstate)
Definition: parse_node.c:75
Definition: pg_list.h:45
#define PRS2_NEW_VARNO
Definition: primnodes.h:161
List * p_rtable
Definition: parse_node.h:168