PostgreSQL Source Code  git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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)
 
AlterTableStmttransformAlterTableStmt (Oid relid, AlterTableStmt *stmt, const char *queryString, List **beforeStmts, List **afterStmts)
 
IndexStmttransformIndexStmt (Oid relid, IndexStmt *stmt, const char *queryString)
 
CreateStatsStmttransformStatsStmt (Oid relid, CreateStatsStmt *stmt, const char *queryString)
 
void transformRuleStmt (RuleStmt *stmt, const char *queryString, List **actions, Node **whereClause)
 
ListtransformCreateSchemaStmtElements (List *schemaElts, const char *schemaName)
 
PartitionBoundSpectransformPartitionBound (ParseState *pstate, Relation parent, PartitionBoundSpec *spec)
 
ListexpandTableLikeClause (RangeVar *heapRel, TableLikeClause *table_like_clause)
 
IndexStmtgenerateClonedIndexStmt (RangeVar *heapRel, Relation source_idx, const struct AttrMap *attmap, Oid *constraintOid)
 

Function Documentation

◆ expandTableLikeClause()

List* expandTableLikeClause ( RangeVar heapRel,
TableLikeClause table_like_clause 
)

Definition at line 1317 of file parse_utilcmd.c.

1318 {
1319  List *result = NIL;
1320  List *atsubcmds = NIL;
1321  AttrNumber parent_attno;
1322  Relation relation;
1323  Relation childrel;
1324  TupleDesc tupleDesc;
1325  TupleConstr *constr;
1326  AttrMap *attmap;
1327  char *comment;
1328 
1329  /*
1330  * Open the relation referenced by the LIKE clause. We should still have
1331  * the table lock obtained by transformTableLikeClause (and this'll throw
1332  * an assertion failure if not). Hence, no need to recheck privileges
1333  * etc. We must open the rel by OID not name, to be sure we get the same
1334  * table.
1335  */
1336  if (!OidIsValid(table_like_clause->relationOid))
1337  elog(ERROR, "expandTableLikeClause called on untransformed LIKE clause");
1338 
1339  relation = relation_open(table_like_clause->relationOid, NoLock);
1340 
1341  tupleDesc = RelationGetDescr(relation);
1342  constr = tupleDesc->constr;
1343 
1344  /*
1345  * Open the newly-created child relation; we have lock on that too.
1346  */
1347  childrel = relation_openrv(heapRel, NoLock);
1348 
1349  /*
1350  * Construct a map from the LIKE relation's attnos to the child rel's.
1351  * This re-checks type match etc, although it shouldn't be possible to
1352  * have a failure since both tables are locked.
1353  */
1354  attmap = build_attrmap_by_name(RelationGetDescr(childrel),
1355  tupleDesc,
1356  false);
1357 
1358  /*
1359  * Process defaults, if required.
1360  */
1361  if ((table_like_clause->options &
1363  constr != NULL)
1364  {
1365  for (parent_attno = 1; parent_attno <= tupleDesc->natts;
1366  parent_attno++)
1367  {
1368  Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
1369  parent_attno - 1);
1370 
1371  /*
1372  * Ignore dropped columns in the parent.
1373  */
1374  if (attribute->attisdropped)
1375  continue;
1376 
1377  /*
1378  * Copy default, if present and it should be copied. We have
1379  * separate options for plain default expressions and GENERATED
1380  * defaults.
1381  */
1382  if (attribute->atthasdef &&
1383  (attribute->attgenerated ?
1384  (table_like_clause->options & CREATE_TABLE_LIKE_GENERATED) :
1385  (table_like_clause->options & CREATE_TABLE_LIKE_DEFAULTS)))
1386  {
1387  Node *this_default;
1388  AlterTableCmd *atsubcmd;
1389  bool found_whole_row;
1390 
1391  this_default = TupleDescGetDefault(tupleDesc, parent_attno);
1392  if (this_default == NULL)
1393  elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
1394  parent_attno, RelationGetRelationName(relation));
1395 
1396  atsubcmd = makeNode(AlterTableCmd);
1397  atsubcmd->subtype = AT_CookedColumnDefault;
1398  atsubcmd->num = attmap->attnums[parent_attno - 1];
1399  atsubcmd->def = map_variable_attnos(this_default,
1400  1, 0,
1401  attmap,
1402  InvalidOid,
1403  &found_whole_row);
1404 
1405  /*
1406  * Prevent this for the same reason as for constraints below.
1407  * Note that defaults cannot contain any vars, so it's OK that
1408  * the error message refers to generated columns.
1409  */
1410  if (found_whole_row)
1411  ereport(ERROR,
1412  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1413  errmsg("cannot convert whole-row table reference"),
1414  errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
1415  NameStr(attribute->attname),
1416  RelationGetRelationName(relation))));
1417 
1418  atsubcmds = lappend(atsubcmds, atsubcmd);
1419  }
1420  }
1421  }
1422 
1423  /*
1424  * Copy CHECK constraints if requested, being careful to adjust attribute
1425  * numbers so they match the child.
1426  */
1427  if ((table_like_clause->options & CREATE_TABLE_LIKE_CONSTRAINTS) &&
1428  constr != NULL)
1429  {
1430  int ccnum;
1431 
1432  for (ccnum = 0; ccnum < constr->num_check; ccnum++)
1433  {
1434  char *ccname = constr->check[ccnum].ccname;
1435  char *ccbin = constr->check[ccnum].ccbin;
1436  bool ccnoinherit = constr->check[ccnum].ccnoinherit;
1437  Node *ccbin_node;
1438  bool found_whole_row;
1439  Constraint *n;
1440  AlterTableCmd *atsubcmd;
1441 
1442  ccbin_node = map_variable_attnos(stringToNode(ccbin),
1443  1, 0,
1444  attmap,
1445  InvalidOid, &found_whole_row);
1446 
1447  /*
1448  * We reject whole-row variables because the whole point of LIKE
1449  * is that the new table's rowtype might later diverge from the
1450  * parent's. So, while translation might be possible right now,
1451  * it wouldn't be possible to guarantee it would work in future.
1452  */
1453  if (found_whole_row)
1454  ereport(ERROR,
1455  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1456  errmsg("cannot convert whole-row table reference"),
1457  errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
1458  ccname,
1459  RelationGetRelationName(relation))));
1460 
1461  n = makeNode(Constraint);
1462  n->contype = CONSTR_CHECK;
1463  n->conname = pstrdup(ccname);
1464  n->location = -1;
1465  n->is_no_inherit = ccnoinherit;
1466  n->raw_expr = NULL;
1467  n->cooked_expr = nodeToString(ccbin_node);
1468 
1469  /* We can skip validation, since the new table should be empty. */
1470  n->skip_validation = true;
1471  n->initially_valid = true;
1472 
1473  atsubcmd = makeNode(AlterTableCmd);
1474  atsubcmd->subtype = AT_AddConstraint;
1475  atsubcmd->def = (Node *) n;
1476  atsubcmds = lappend(atsubcmds, atsubcmd);
1477 
1478  /* Copy comment on constraint */
1479  if ((table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS) &&
1481  n->conname, false),
1482  ConstraintRelationId,
1483  0)) != NULL)
1484  {
1486 
1487  stmt->objtype = OBJECT_TABCONSTRAINT;
1488  stmt->object = (Node *) list_make3(makeString(heapRel->schemaname),
1489  makeString(heapRel->relname),
1490  makeString(n->conname));
1491  stmt->comment = comment;
1492 
1493  result = lappend(result, stmt);
1494  }
1495  }
1496  }
1497 
1498  /*
1499  * If we generated any ALTER TABLE actions above, wrap them into a single
1500  * ALTER TABLE command. Stick it at the front of the result, so it runs
1501  * before any CommentStmts we made above.
1502  */
1503  if (atsubcmds)
1504  {
1506 
1507  atcmd->relation = copyObject(heapRel);
1508  atcmd->cmds = atsubcmds;
1509  atcmd->objtype = OBJECT_TABLE;
1510  atcmd->missing_ok = false;
1511  result = lcons(atcmd, result);
1512  }
1513 
1514  /*
1515  * Process indexes if required.
1516  */
1517  if ((table_like_clause->options & CREATE_TABLE_LIKE_INDEXES) &&
1518  relation->rd_rel->relhasindex)
1519  {
1520  List *parent_indexes;
1521  ListCell *l;
1522 
1523  parent_indexes = RelationGetIndexList(relation);
1524 
1525  foreach(l, parent_indexes)
1526  {
1527  Oid parent_index_oid = lfirst_oid(l);
1528  Relation parent_index;
1529  IndexStmt *index_stmt;
1530 
1531  parent_index = index_open(parent_index_oid, AccessShareLock);
1532 
1533  /* Build CREATE INDEX statement to recreate the parent_index */
1534  index_stmt = generateClonedIndexStmt(heapRel,
1535  parent_index,
1536  attmap,
1537  NULL);
1538 
1539  /* Copy comment on index, if requested */
1540  if (table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS)
1541  {
1542  comment = GetComment(parent_index_oid, RelationRelationId, 0);
1543 
1544  /*
1545  * We make use of IndexStmt's idxcomment option, so as not to
1546  * need to know now what name the index will have.
1547  */
1548  index_stmt->idxcomment = comment;
1549  }
1550 
1551  result = lappend(result, index_stmt);
1552 
1553  index_close(parent_index, AccessShareLock);
1554  }
1555  }
1556 
1557  /*
1558  * Process extended statistics if required.
1559  */
1560  if (table_like_clause->options & CREATE_TABLE_LIKE_STATISTICS)
1561  {
1562  List *parent_extstats;
1563  ListCell *l;
1564 
1565  parent_extstats = RelationGetStatExtList(relation);
1566 
1567  foreach(l, parent_extstats)
1568  {
1569  Oid parent_stat_oid = lfirst_oid(l);
1570  CreateStatsStmt *stats_stmt;
1571 
1572  stats_stmt = generateClonedExtStatsStmt(heapRel,
1573  RelationGetRelid(childrel),
1574  parent_stat_oid,
1575  attmap);
1576 
1577  /* Copy comment on statistics object, if requested */
1578  if (table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS)
1579  {
1580  comment = GetComment(parent_stat_oid, StatisticExtRelationId, 0);
1581 
1582  /*
1583  * We make use of CreateStatsStmt's stxcomment option, so as
1584  * not to need to know now what name the statistics will have.
1585  */
1586  stats_stmt->stxcomment = comment;
1587  }
1588 
1589  result = lappend(result, stats_stmt);
1590  }
1591 
1592  list_free(parent_extstats);
1593  }
1594 
1595  /* Done with child rel */
1596  table_close(childrel, NoLock);
1597 
1598  /*
1599  * Close the parent rel, but keep our AccessShareLock on it until xact
1600  * commit. That will prevent someone else from deleting or ALTERing the
1601  * parent before the child is committed.
1602  */
1603  table_close(relation, NoLock);
1604 
1605  return result;
1606 }
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:177
int16 AttrNumber
Definition: attnum.h:21
#define NameStr(name)
Definition: c.h:751
#define OidIsValid(objectId)
Definition: c.h:780
char * GetComment(Oid oid, Oid classoid, int32 subid)
Definition: comment.c:410
int errdetail(const char *fmt,...)
Definition: elog.c:1203
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define ereport(elevel,...)
Definition: elog.h:149
#define stmt
Definition: indent_codes.h:59
#define comment
Definition: indent_codes.h:49
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:177
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:133
List * lappend(List *list, void *datum)
Definition: list.c:339
void list_free(List *list)
Definition: list.c:1546
List * lcons(void *datum, List *list)
Definition: list.c:495
#define NoLock
Definition: lockdefs.h:34
#define AccessShareLock
Definition: lockdefs.h:36
char * pstrdup(const char *in)
Definition: mcxt.c:1696
#define copyObject(obj)
Definition: nodes.h:224
#define makeNode(_type_)
Definition: nodes.h:155
char * nodeToString(const void *obj)
Definition: outfuncs.c:794
IndexStmt * generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx, const AttrMap *attmap, Oid *constraintOid)
static CreateStatsStmt * generateClonedExtStatsStmt(RangeVar *heapRel, Oid heapRelid, Oid source_statsid, const AttrMap *attmap)
@ CONSTR_CHECK
Definition: parsenodes.h:2730
@ OBJECT_TABLE
Definition: parsenodes.h:2309
@ OBJECT_TABCONSTRAINT
Definition: parsenodes.h:2308
@ AT_AddConstraint
Definition: parsenodes.h:2376
@ AT_CookedColumnDefault
Definition: parsenodes.h:2363
@ CREATE_TABLE_LIKE_COMMENTS
Definition: parsenodes.h:763
@ CREATE_TABLE_LIKE_GENERATED
Definition: parsenodes.h:767
@ CREATE_TABLE_LIKE_INDEXES
Definition: parsenodes.h:769
@ CREATE_TABLE_LIKE_DEFAULTS
Definition: parsenodes.h:766
@ CREATE_TABLE_LIKE_STATISTICS
Definition: parsenodes.h:770
@ CREATE_TABLE_LIKE_CONSTRAINTS
Definition: parsenodes.h:765
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
Oid get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok)
#define NIL
Definition: pg_list.h:68
#define list_make3(x1, x2, x3)
Definition: pg_list.h:216
#define lfirst_oid(lc)
Definition: pg_list.h:174
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
void * stringToNode(const char *str)
Definition: read.c:90
#define RelationGetRelid(relation)
Definition: rel.h:505
#define RelationGetDescr(relation)
Definition: rel.h:531
#define RelationGetRelationName(relation)
Definition: rel.h:539
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4767
List * RelationGetStatExtList(Relation relation)
Definition: relcache.c:4908
Node * map_variable_attnos(Node *node, int target_varno, int sublevels_up, const AttrMap *attno_map, Oid to_rowtype, bool *found_whole_row)
Relation relation_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition: relation.c:137
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:47
AlterTableType subtype
Definition: parsenodes.h:2438
RangeVar * relation
Definition: parsenodes.h:2352
ObjectType objtype
Definition: parsenodes.h:2354
Definition: attmap.h:35
AttrNumber * attnums
Definition: attmap.h:36
char * ccname
Definition: tupdesc.h:30
bool ccnoinherit
Definition: tupdesc.h:33
char * ccbin
Definition: tupdesc.h:31
ParseLoc location
Definition: parsenodes.h:2798
ConstrType contype
Definition: parsenodes.h:2756
bool is_no_inherit
Definition: parsenodes.h:2762
char * cooked_expr
Definition: parsenodes.h:2765
bool initially_valid
Definition: parsenodes.h:2761
bool skip_validation
Definition: parsenodes.h:2760
Node * raw_expr
Definition: parsenodes.h:2763
char * conname
Definition: parsenodes.h:2757
char * idxcomment
Definition: parsenodes.h:3387
Definition: pg_list.h:54
Definition: nodes.h:129
char * relname
Definition: primnodes.h:82
char * schemaname
Definition: primnodes.h:79
Form_pg_class rd_rel
Definition: rel.h:111
ConstrCheck * check
Definition: tupdesc.h:40
uint16 num_check
Definition: tupdesc.h:43
TupleConstr * constr
Definition: tupdesc.h:85
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Node * TupleDescGetDefault(TupleDesc tupdesc, AttrNumber attnum)
Definition: tupdesc.c:899
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
String * makeString(char *str)
Definition: value.c:63

References AccessShareLock, AT_AddConstraint, AT_CookedColumnDefault, AttrMap::attnums, build_attrmap_by_name(), ConstrCheck::ccbin, ConstrCheck::ccname, ConstrCheck::ccnoinherit, TupleConstr::check, AlterTableStmt::cmds, comment, Constraint::conname, TupleDescData::constr, CONSTR_CHECK, Constraint::contype, Constraint::cooked_expr, copyObject, CREATE_TABLE_LIKE_COMMENTS, CREATE_TABLE_LIKE_CONSTRAINTS, CREATE_TABLE_LIKE_DEFAULTS, CREATE_TABLE_LIKE_GENERATED, CREATE_TABLE_LIKE_INDEXES, CREATE_TABLE_LIKE_STATISTICS, AlterTableCmd::def, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, generateClonedExtStatsStmt(), generateClonedIndexStmt(), get_relation_constraint_oid(), GetComment(), IndexStmt::idxcomment, index_close(), index_open(), Constraint::initially_valid, InvalidOid, Constraint::is_no_inherit, lappend(), lcons(), lfirst_oid, list_free(), list_make3, Constraint::location, makeNode, makeString(), map_variable_attnos(), AlterTableStmt::missing_ok, NameStr, TupleDescData::natts, NIL, nodeToString(), NoLock, AlterTableCmd::num, TupleConstr::num_check, OBJECT_TABCONSTRAINT, OBJECT_TABLE, AlterTableStmt::objtype, OidIsValid, TableLikeClause::options, pstrdup(), Constraint::raw_expr, RelationData::rd_rel, AlterTableStmt::relation, relation_open(), relation_openrv(), RelationGetDescr, RelationGetIndexList(), RelationGetRelationName, RelationGetRelid, RelationGetStatExtList(), TableLikeClause::relationOid, RangeVar::relname, RangeVar::schemaname, Constraint::skip_validation, stmt, stringToNode(), CreateStatsStmt::stxcomment, AlterTableCmd::subtype, table_close(), TupleDescAttr, and TupleDescGetDefault().

Referenced by ProcessUtilitySlow().

◆ generateClonedIndexStmt()

IndexStmt* generateClonedIndexStmt ( RangeVar heapRel,
Relation  source_idx,
const struct AttrMap attmap,
Oid constraintOid 
)

◆ transformAlterTableStmt()

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

Definition at line 3492 of file parse_utilcmd.c.

3495 {
3496  Relation rel;
3497  TupleDesc tupdesc;
3498  ParseState *pstate;
3499  CreateStmtContext cxt;
3500  List *save_alist;
3501  ListCell *lcmd,
3502  *l;
3503  List *newcmds = NIL;
3504  bool skipValidation = true;
3505  AlterTableCmd *newcmd;
3506  ParseNamespaceItem *nsitem;
3507 
3508  /* Caller is responsible for locking the relation */
3509  rel = relation_open(relid, NoLock);
3510  tupdesc = RelationGetDescr(rel);
3511 
3512  /* Set up pstate */
3513  pstate = make_parsestate(NULL);
3514  pstate->p_sourcetext = queryString;
3515  nsitem = addRangeTableEntryForRelation(pstate,
3516  rel,
3518  NULL,
3519  false,
3520  true);
3521  addNSItemToQuery(pstate, nsitem, false, true, true);
3522 
3523  /* Set up CreateStmtContext */
3524  cxt.pstate = pstate;
3525  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
3526  {
3527  cxt.stmtType = "ALTER FOREIGN TABLE";
3528  cxt.isforeign = true;
3529  }
3530  else
3531  {
3532  cxt.stmtType = "ALTER TABLE";
3533  cxt.isforeign = false;
3534  }
3535  cxt.relation = stmt->relation;
3536  cxt.rel = rel;
3537  cxt.inhRelations = NIL;
3538  cxt.isalter = true;
3539  cxt.columns = NIL;
3540  cxt.ckconstraints = NIL;
3541  cxt.nnconstraints = NIL;
3542  cxt.fkconstraints = NIL;
3543  cxt.ixconstraints = NIL;
3544  cxt.likeclauses = NIL;
3545  cxt.blist = NIL;
3546  cxt.alist = NIL;
3547  cxt.pkey = NULL;
3548  cxt.ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
3549  cxt.partbound = NULL;
3550  cxt.ofType = false;
3551 
3552  /*
3553  * Transform ALTER subcommands that need it (most don't). These largely
3554  * re-use code from CREATE TABLE.
3555  */
3556  foreach(lcmd, stmt->cmds)
3557  {
3558  AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
3559 
3560  switch (cmd->subtype)
3561  {
3562  case AT_AddColumn:
3563  {
3564  ColumnDef *def = castNode(ColumnDef, cmd->def);
3565 
3566  transformColumnDefinition(&cxt, def);
3567 
3568  /*
3569  * If the column has a non-null default, we can't skip
3570  * validation of foreign keys.
3571  */
3572  if (def->raw_default != NULL)
3573  skipValidation = false;
3574 
3575  /*
3576  * All constraints are processed in other ways. Remove the
3577  * original list
3578  */
3579  def->constraints = NIL;
3580 
3581  newcmds = lappend(newcmds, cmd);
3582  break;
3583  }
3584 
3585  case AT_AddConstraint:
3586 
3587  /*
3588  * The original AddConstraint cmd node doesn't go to newcmds
3589  */
3590  if (IsA(cmd->def, Constraint))
3591  {
3592  transformTableConstraint(&cxt, (Constraint *) cmd->def);
3593  if (((Constraint *) cmd->def)->contype == CONSTR_FOREIGN)
3594  skipValidation = false;
3595  }
3596  else
3597  elog(ERROR, "unrecognized node type: %d",
3598  (int) nodeTag(cmd->def));
3599  break;
3600 
3601  case AT_AlterColumnType:
3602  {
3603  ColumnDef *def = castNode(ColumnDef, cmd->def);
3605 
3606  /*
3607  * For ALTER COLUMN TYPE, transform the USING clause if
3608  * one was specified.
3609  */
3610  if (def->raw_default)
3611  {
3612  def->cooked_default =
3613  transformExpr(pstate, def->raw_default,
3615  }
3616 
3617  /*
3618  * For identity column, create ALTER SEQUENCE command to
3619  * change the data type of the sequence. Identity sequence
3620  * is associated with the top level partitioned table.
3621  * Hence ignore partitions.
3622  */
3623  if (!RelationGetForm(rel)->relispartition)
3624  {
3625  attnum = get_attnum(relid, cmd->name);
3626  if (attnum == InvalidAttrNumber)
3627  ereport(ERROR,
3628  (errcode(ERRCODE_UNDEFINED_COLUMN),
3629  errmsg("column \"%s\" of relation \"%s\" does not exist",
3630  cmd->name, RelationGetRelationName(rel))));
3631 
3632  if (attnum > 0 &&
3633  TupleDescAttr(tupdesc, attnum - 1)->attidentity)
3634  {
3635  Oid seq_relid = getIdentitySequence(rel, attnum, false);
3636  Oid typeOid = typenameTypeId(pstate, def->typeName);
3637  AlterSeqStmt *altseqstmt = makeNode(AlterSeqStmt);
3638 
3639  altseqstmt->sequence
3641  get_rel_name(seq_relid),
3642  -1);
3643  altseqstmt->options = list_make1(makeDefElem("as",
3644  (Node *) makeTypeNameFromOid(typeOid, -1),
3645  -1));
3646  altseqstmt->for_identity = true;
3647  cxt.blist = lappend(cxt.blist, altseqstmt);
3648  }
3649  }
3650 
3651  newcmds = lappend(newcmds, cmd);
3652  break;
3653  }
3654 
3655  case AT_AddIdentity:
3656  {
3657  Constraint *def = castNode(Constraint, cmd->def);
3658  ColumnDef *newdef = makeNode(ColumnDef);
3660 
3661  newdef->colname = cmd->name;
3662  newdef->identity = def->generated_when;
3663  cmd->def = (Node *) newdef;
3664 
3665  attnum = get_attnum(relid, cmd->name);
3666  if (attnum == InvalidAttrNumber)
3667  ereport(ERROR,
3668  (errcode(ERRCODE_UNDEFINED_COLUMN),
3669  errmsg("column \"%s\" of relation \"%s\" does not exist",
3670  cmd->name, RelationGetRelationName(rel))));
3671 
3672  generateSerialExtraStmts(&cxt, newdef,
3673  get_atttype(relid, attnum),
3674  def->options, true, true,
3675  NULL, NULL);
3676 
3677  newcmds = lappend(newcmds, cmd);
3678  break;
3679  }
3680 
3681  case AT_SetIdentity:
3682  {
3683  /*
3684  * Create an ALTER SEQUENCE statement for the internal
3685  * sequence of the identity column.
3686  */
3687  ListCell *lc;
3688  List *newseqopts = NIL;
3689  List *newdef = NIL;
3691  Oid seq_relid;
3692 
3693  /*
3694  * Split options into those handled by ALTER SEQUENCE and
3695  * those for ALTER TABLE proper.
3696  */
3697  foreach(lc, castNode(List, cmd->def))
3698  {
3699  DefElem *def = lfirst_node(DefElem, lc);
3700 
3701  if (strcmp(def->defname, "generated") == 0)
3702  newdef = lappend(newdef, def);
3703  else
3704  newseqopts = lappend(newseqopts, def);
3705  }
3706 
3707  attnum = get_attnum(relid, cmd->name);
3708  if (attnum == InvalidAttrNumber)
3709  ereport(ERROR,
3710  (errcode(ERRCODE_UNDEFINED_COLUMN),
3711  errmsg("column \"%s\" of relation \"%s\" does not exist",
3712  cmd->name, RelationGetRelationName(rel))));
3713 
3714  seq_relid = getIdentitySequence(rel, attnum, true);
3715 
3716  if (seq_relid)
3717  {
3718  AlterSeqStmt *seqstmt;
3719 
3720  seqstmt = makeNode(AlterSeqStmt);
3722  get_rel_name(seq_relid), -1);
3723  seqstmt->options = newseqopts;
3724  seqstmt->for_identity = true;
3725  seqstmt->missing_ok = false;
3726 
3727  cxt.blist = lappend(cxt.blist, seqstmt);
3728  }
3729 
3730  /*
3731  * If column was not an identity column, we just let the
3732  * ALTER TABLE command error out later. (There are cases
3733  * this fails to cover, but we'll need to restructure
3734  * where creation of the sequence dependency linkage
3735  * happens before we can fix it.)
3736  */
3737 
3738  cmd->def = (Node *) newdef;
3739  newcmds = lappend(newcmds, cmd);
3740  break;
3741  }
3742 
3743  case AT_AttachPartition:
3744  case AT_DetachPartition:
3745  {
3746  PartitionCmd *partcmd = (PartitionCmd *) cmd->def;
3747 
3748  transformPartitionCmd(&cxt, partcmd);
3749  /* assign transformed value of the partition bound */
3750  partcmd->bound = cxt.partbound;
3751  }
3752 
3753  newcmds = lappend(newcmds, cmd);
3754  break;
3755 
3756  default:
3757 
3758  /*
3759  * Currently, we shouldn't actually get here for subcommand
3760  * types that don't require transformation; but if we do, just
3761  * emit them unchanged.
3762  */
3763  newcmds = lappend(newcmds, cmd);
3764  break;
3765  }
3766  }
3767 
3768  /*
3769  * Transfer anything we already have in cxt.alist into save_alist, to keep
3770  * it separate from the output of transformIndexConstraints.
3771  */
3772  save_alist = cxt.alist;
3773  cxt.alist = NIL;
3774 
3775  /* Postprocess constraints */
3777  transformFKConstraints(&cxt, skipValidation, true);
3778  transformCheckConstraints(&cxt, false);
3779 
3780  /*
3781  * Push any index-creation commands into the ALTER, so that they can be
3782  * scheduled nicely by tablecmds.c. Note that tablecmds.c assumes that
3783  * the IndexStmt attached to an AT_AddIndex or AT_AddIndexConstraint
3784  * subcommand has already been through transformIndexStmt.
3785  */
3786  foreach(l, cxt.alist)
3787  {
3788  Node *istmt = (Node *) lfirst(l);
3789 
3790  /*
3791  * We assume here that cxt.alist contains only IndexStmts generated
3792  * from primary key constraints.
3793  */
3794  if (IsA(istmt, IndexStmt))
3795  {
3796  IndexStmt *idxstmt = (IndexStmt *) istmt;
3797 
3798  idxstmt = transformIndexStmt(relid, idxstmt, queryString);
3799  newcmd = makeNode(AlterTableCmd);
3801  newcmd->def = (Node *) idxstmt;
3802  newcmds = lappend(newcmds, newcmd);
3803  }
3804  else
3805  elog(ERROR, "unexpected stmt type %d", (int) nodeTag(istmt));
3806  }
3807  cxt.alist = NIL;
3808 
3809  /* Append any CHECK, NOT NULL or FK constraints to the commands list */
3811  {
3812  newcmd = makeNode(AlterTableCmd);
3813  newcmd->subtype = AT_AddConstraint;
3814  newcmd->def = (Node *) def;
3815  newcmds = lappend(newcmds, newcmd);
3816  }
3818  {
3819  newcmd = makeNode(AlterTableCmd);
3820  newcmd->subtype = AT_AddConstraint;
3821  newcmd->def = (Node *) def;
3822  newcmds = lappend(newcmds, newcmd);
3823  }
3825  {
3826  newcmd = makeNode(AlterTableCmd);
3827  newcmd->subtype = AT_AddConstraint;
3828  newcmd->def = (Node *) def;
3829  newcmds = lappend(newcmds, newcmd);
3830  }
3831 
3832  /* Close rel */
3833  relation_close(rel, NoLock);
3834 
3835  /*
3836  * Output results.
3837  */
3838  stmt->cmds = newcmds;
3839 
3840  *beforeStmts = cxt.blist;
3841  *afterStmts = list_concat(cxt.alist, save_alist);
3842 
3843  return stmt;
3844 }
#define InvalidAttrNumber
Definition: attnum.h:23
List * list_concat(List *list1, const List *list2)
Definition: list.c:561
AttrNumber get_attnum(Oid relid, const char *attname)
Definition: lsyscache.c:858
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3366
Oid get_rel_namespace(Oid relid)
Definition: lsyscache.c:1952
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1928
Oid get_atttype(Oid relid, AttrNumber attnum)
Definition: lsyscache.c:913
RangeVar * makeRangeVar(char *schemaname, char *relname, int location)
Definition: makefuncs.c:424
DefElem * makeDefElem(char *name, Node *arg, int location)
Definition: makefuncs.c:587
TypeName * makeTypeNameFromOid(Oid typeOid, int32 typmod)
Definition: makefuncs.c:497
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
#define nodeTag(nodeptr)
Definition: nodes.h:133
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:118
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:39
@ EXPR_KIND_ALTER_COL_TRANSFORM
Definition: parse_node.h:75
ParseNamespaceItem * addRangeTableEntryForRelation(ParseState *pstate, Relation rel, int lockmode, Alias *alias, bool inh, bool inFromCl)
void addNSItemToQuery(ParseState *pstate, ParseNamespaceItem *nsitem, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
Oid typenameTypeId(ParseState *pstate, const TypeName *typeName)
Definition: parse_type.c:291
static void generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column, Oid seqtypid, List *seqoptions, bool for_identity, bool col_exists, char **snamespace_p, char **sname_p)
static void transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
static void transformIndexConstraints(CreateStmtContext *cxt)
static void transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
IndexStmt * transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
static void transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd)
static void transformCheckConstraints(CreateStmtContext *cxt, bool skipValidation)
static void transformFKConstraints(CreateStmtContext *cxt, bool skipValidation, bool isAddConstraint)
@ CONSTR_FOREIGN
Definition: parsenodes.h:2734
@ AT_AddIndexConstraint
Definition: parsenodes.h:2381
@ AT_SetIdentity
Definition: parsenodes.h:2423
@ AT_AddIndex
Definition: parsenodes.h:2374
@ AT_AddIdentity
Definition: parsenodes.h:2422
@ AT_AlterColumnType
Definition: parsenodes.h:2384
@ AT_DetachPartition
Definition: parsenodes.h:2420
@ AT_AttachPartition
Definition: parsenodes.h:2419
@ AT_AddColumn
Definition: parsenodes.h:2360
int16 attnum
Definition: pg_attribute.h:74
Oid getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
Definition: pg_depend.c:945
#define lfirst(lc)
Definition: pg_list.h:172
#define lfirst_node(type, lc)
Definition: pg_list.h:176
#define list_make1(x1)
Definition: pg_list.h:212
#define foreach_node(type, var, lst)
Definition: pg_list.h:496
#define RelationGetForm(relation)
Definition: rel.h:499
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:205
List * options
Definition: parsenodes.h:3157
RangeVar * sequence
Definition: parsenodes.h:3156
bool for_identity
Definition: parsenodes.h:3158
char identity
Definition: parsenodes.h:739
List * constraints
Definition: parsenodes.h:745
Node * cooked_default
Definition: parsenodes.h:738
char * colname
Definition: parsenodes.h:728
TypeName * typeName
Definition: parsenodes.h:729
Node * raw_default
Definition: parsenodes.h:737
List * options
Definition: parsenodes.h:2776
char generated_when
Definition: parsenodes.h:2767
IndexStmt * pkey
Definition: parse_utilcmd.c:92
const char * stmtType
Definition: parse_utilcmd.c:76
RangeVar * relation
Definition: parse_utilcmd.c:77
ParseState * pstate
Definition: parse_utilcmd.c:75
PartitionBoundSpec * partbound
Definition: parse_utilcmd.c:94
char * defname
Definition: parsenodes.h:817
Oid indexOid
Definition: parsenodes.h:3388
const char * p_sourcetext
Definition: parse_node.h:209
PartitionBoundSpec * bound
Definition: parsenodes.h:949

References AccessShareLock, addNSItemToQuery(), addRangeTableEntryForRelation(), CreateStmtContext::alist, AT_AddColumn, AT_AddConstraint, AT_AddIdentity, AT_AddIndex, AT_AddIndexConstraint, AT_AlterColumnType, AT_AttachPartition, AT_DetachPartition, AT_SetIdentity, attnum, CreateStmtContext::blist, PartitionCmd::bound, castNode, CreateStmtContext::ckconstraints, ColumnDef::colname, CreateStmtContext::columns, CONSTR_FOREIGN, ColumnDef::constraints, ColumnDef::cooked_default, AlterTableCmd::def, DefElem::defname, elog, ereport, errcode(), errmsg(), ERROR, EXPR_KIND_ALTER_COL_TRANSFORM, CreateStmtContext::fkconstraints, AlterSeqStmt::for_identity, foreach_node, Constraint::generated_when, generateSerialExtraStmts(), get_attnum(), get_atttype(), get_namespace_name(), get_rel_name(), get_rel_namespace(), getIdentitySequence(), ColumnDef::identity, IndexStmt::indexOid, CreateStmtContext::inhRelations, InvalidAttrNumber, IsA, CreateStmtContext::isalter, CreateStmtContext::isforeign, CreateStmtContext::ispartitioned, CreateStmtContext::ixconstraints, lappend(), lfirst, lfirst_node, CreateStmtContext::likeclauses, list_concat(), list_make1, make_parsestate(), makeDefElem(), makeNode, makeRangeVar(), makeTypeNameFromOid(), AlterSeqStmt::missing_ok, AlterTableCmd::name, NIL, CreateStmtContext::nnconstraints, nodeTag, NoLock, CreateStmtContext::ofType, OidIsValid, Constraint::options, AlterSeqStmt::options, ParseState::p_sourcetext, CreateStmtContext::partbound, CreateStmtContext::pkey, CreateStmtContext::pstate, ColumnDef::raw_default, RelationData::rd_rel, CreateStmtContext::rel, CreateStmtContext::relation, relation_close(), relation_open(), RelationGetDescr, RelationGetForm, RelationGetRelationName, AlterSeqStmt::sequence, stmt, CreateStmtContext::stmtType, AlterTableCmd::subtype, transformCheckConstraints(), transformColumnDefinition(), transformExpr(), transformFKConstraints(), transformIndexConstraints(), transformIndexStmt(), transformPartitionCmd(), transformTableConstraint(), TupleDescAttr, ColumnDef::typeName, and typenameTypeId().

Referenced by ATParseTransformCmd(), and ATPostAlterTypeParse().

◆ transformCreateSchemaStmtElements()

List* transformCreateSchemaStmtElements ( List schemaElts,
const char *  schemaName 
)

Definition at line 4029 of file parse_utilcmd.c.

4030 {
4032  List *result;
4033  ListCell *elements;
4034 
4035  cxt.schemaname = schemaName;
4036  cxt.sequences = NIL;
4037  cxt.tables = NIL;
4038  cxt.views = NIL;
4039  cxt.indexes = NIL;
4040  cxt.triggers = NIL;
4041  cxt.grants = NIL;
4042 
4043  /*
4044  * Run through each schema element in the schema element list. Separate
4045  * statements by type, and do preliminary analysis.
4046  */
4047  foreach(elements, schemaElts)
4048  {
4049  Node *element = lfirst(elements);
4050 
4051  switch (nodeTag(element))
4052  {
4053  case T_CreateSeqStmt:
4054  {
4055  CreateSeqStmt *elp = (CreateSeqStmt *) element;
4056 
4058  cxt.sequences = lappend(cxt.sequences, element);
4059  }
4060  break;
4061 
4062  case T_CreateStmt:
4063  {
4064  CreateStmt *elp = (CreateStmt *) element;
4065 
4067 
4068  /*
4069  * XXX todo: deal with constraints
4070  */
4071  cxt.tables = lappend(cxt.tables, element);
4072  }
4073  break;
4074 
4075  case T_ViewStmt:
4076  {
4077  ViewStmt *elp = (ViewStmt *) element;
4078 
4079  setSchemaName(cxt.schemaname, &elp->view->schemaname);
4080 
4081  /*
4082  * XXX todo: deal with references between views
4083  */
4084  cxt.views = lappend(cxt.views, element);
4085  }
4086  break;
4087 
4088  case T_IndexStmt:
4089  {
4090  IndexStmt *elp = (IndexStmt *) element;
4091 
4093  cxt.indexes = lappend(cxt.indexes, element);
4094  }
4095  break;
4096 
4097  case T_CreateTrigStmt:
4098  {
4100 
4102  cxt.triggers = lappend(cxt.triggers, element);
4103  }
4104  break;
4105 
4106  case T_GrantStmt:
4107  cxt.grants = lappend(cxt.grants, element);
4108  break;
4109 
4110  default:
4111  elog(ERROR, "unrecognized node type: %d",
4112  (int) nodeTag(element));
4113  }
4114  }
4115 
4116  result = NIL;
4117  result = list_concat(result, cxt.sequences);
4118  result = list_concat(result, cxt.tables);
4119  result = list_concat(result, cxt.views);
4120  result = list_concat(result, cxt.indexes);
4121  result = list_concat(result, cxt.triggers);
4122  result = list_concat(result, cxt.grants);
4123 
4124  return result;
4125 }
static void setSchemaName(const char *context_schema, char **stmt_schema_name)
static chr element(struct vars *v, const chr *startp, const chr *endp)
Definition: regc_locale.c:376
RangeVar * sequence
Definition: parsenodes.h:3146
RangeVar * relation
Definition: parsenodes.h:2675
RangeVar * relation
Definition: parsenodes.h:3033
RangeVar * relation
Definition: parsenodes.h:3378
RangeVar * view
Definition: parsenodes.h:3771

References element(), elog, ERROR, CreateSchemaStmtContext::grants, CreateSchemaStmtContext::indexes, lappend(), lfirst, list_concat(), NIL, nodeTag, CreateStmt::relation, CreateTrigStmt::relation, IndexStmt::relation, CreateSchemaStmtContext::schemaname, RangeVar::schemaname, CreateSeqStmt::sequence, CreateSchemaStmtContext::sequences, setSchemaName(), CreateSchemaStmtContext::tables, CreateSchemaStmtContext::triggers, ViewStmt::view, and CreateSchemaStmtContext::views.

Referenced by CreateSchemaCommand().

◆ transformCreateStmt()

List* transformCreateStmt ( CreateStmt stmt,
const char *  queryString 
)

Definition at line 164 of file parse_utilcmd.c.

165 {
166  ParseState *pstate;
167  CreateStmtContext cxt;
168  List *result;
169  List *save_alist;
170  ListCell *elements;
171  Oid namespaceid;
172  Oid existing_relid;
173  ParseCallbackState pcbstate;
174 
175  /* Set up pstate */
176  pstate = make_parsestate(NULL);
177  pstate->p_sourcetext = queryString;
178 
179  /*
180  * Look up the creation namespace. This also checks permissions on the
181  * target namespace, locks it against concurrent drops, checks for a
182  * preexisting relation in that namespace with the same name, and updates
183  * stmt->relation->relpersistence if the selected namespace is temporary.
184  */
185  setup_parser_errposition_callback(&pcbstate, pstate,
186  stmt->relation->location);
187  namespaceid =
189  &existing_relid);
191 
192  /*
193  * If the relation already exists and the user specified "IF NOT EXISTS",
194  * bail out with a NOTICE.
195  */
196  if (stmt->if_not_exists && OidIsValid(existing_relid))
197  {
198  /*
199  * If we are in an extension script, insist that the pre-existing
200  * object be a member of the extension, to avoid security risks.
201  */
202  ObjectAddress address;
203 
204  ObjectAddressSet(address, RelationRelationId, existing_relid);
206 
207  /* OK to skip */
208  ereport(NOTICE,
209  (errcode(ERRCODE_DUPLICATE_TABLE),
210  errmsg("relation \"%s\" already exists, skipping",
211  stmt->relation->relname)));
212  return NIL;
213  }
214 
215  /*
216  * If the target relation name isn't schema-qualified, make it so. This
217  * prevents some corner cases in which added-on rewritten commands might
218  * think they should apply to other relations that have the same name and
219  * are earlier in the search path. But a local temp table is effectively
220  * specified to be in pg_temp, so no need for anything extra in that case.
221  */
222  if (stmt->relation->schemaname == NULL
223  && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
224  stmt->relation->schemaname = get_namespace_name(namespaceid);
225 
226  /* Set up CreateStmtContext */
227  cxt.pstate = pstate;
229  {
230  cxt.stmtType = "CREATE FOREIGN TABLE";
231  cxt.isforeign = true;
232  }
233  else
234  {
235  cxt.stmtType = "CREATE TABLE";
236  cxt.isforeign = false;
237  }
238  cxt.relation = stmt->relation;
239  cxt.rel = NULL;
240  cxt.inhRelations = stmt->inhRelations;
241  cxt.isalter = false;
242  cxt.columns = NIL;
243  cxt.ckconstraints = NIL;
244  cxt.nnconstraints = NIL;
245  cxt.fkconstraints = NIL;
246  cxt.ixconstraints = NIL;
247  cxt.likeclauses = NIL;
248  cxt.blist = NIL;
249  cxt.alist = NIL;
250  cxt.pkey = NULL;
251  cxt.ispartitioned = stmt->partspec != NULL;
252  cxt.partbound = stmt->partbound;
253  cxt.ofType = (stmt->ofTypename != NULL);
254 
255  Assert(!stmt->ofTypename || !stmt->inhRelations); /* grammar enforces */
256 
257  if (stmt->ofTypename)
258  transformOfType(&cxt, stmt->ofTypename);
259 
260  if (stmt->partspec)
261  {
262  if (stmt->inhRelations && !stmt->partbound)
263  ereport(ERROR,
264  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
265  errmsg("cannot create partitioned table as inheritance child")));
266  }
267 
268  /*
269  * Run through each primary element in the table creation clause. Separate
270  * column defs from constraints, and do preliminary analysis.
271  */
272  foreach(elements, stmt->tableElts)
273  {
274  Node *element = lfirst(elements);
275 
276  switch (nodeTag(element))
277  {
278  case T_ColumnDef:
280  break;
281 
282  case T_Constraint:
284  break;
285 
286  case T_TableLikeClause:
288  break;
289 
290  default:
291  elog(ERROR, "unrecognized node type: %d",
292  (int) nodeTag(element));
293  break;
294  }
295  }
296 
297  /*
298  * Transfer anything we already have in cxt.alist into save_alist, to keep
299  * it separate from the output of transformIndexConstraints. (This may
300  * not be necessary anymore, but we'll keep doing it to preserve the
301  * historical order of execution of the alist commands.)
302  */
303  save_alist = cxt.alist;
304  cxt.alist = NIL;
305 
306  Assert(stmt->constraints == NIL);
307 
308  /*
309  * Before processing index constraints, which could include a primary key,
310  * we must scan all not-null constraints to propagate the is_not_null flag
311  * to each corresponding ColumnDef. This is necessary because table-level
312  * not-null constraints have not been marked in each ColumnDef, and the PK
313  * processing code needs to know whether one constraint has already been
314  * declared in order not to declare a redundant one.
315  */
317  {
318  char *colname = strVal(linitial(nn->keys));
319 
320  foreach_node(ColumnDef, cd, cxt.columns)
321  {
322  /* not our column? */
323  if (strcmp(cd->colname, colname) != 0)
324  continue;
325  /* Already marked not-null? Nothing to do */
326  if (cd->is_not_null)
327  break;
328  /* Bingo, we're done for this constraint */
329  cd->is_not_null = true;
330  break;
331  }
332  }
333 
334  /*
335  * Postprocess constraints that give rise to index definitions.
336  */
338 
339  /*
340  * Re-consideration of LIKE clauses should happen after creation of
341  * indexes, but before creation of foreign keys. This order is critical
342  * because a LIKE clause may attempt to create a primary key. If there's
343  * also a pkey in the main CREATE TABLE list, creation of that will not
344  * check for a duplicate at runtime (since index_check_primary_key()
345  * expects that we rejected dups here). Creation of the LIKE-generated
346  * pkey behaves like ALTER TABLE ADD, so it will check, but obviously that
347  * only works if it happens second. On the other hand, we want to make
348  * pkeys before foreign key constraints, in case the user tries to make a
349  * self-referential FK.
350  */
351  cxt.alist = list_concat(cxt.alist, cxt.likeclauses);
352 
353  /*
354  * Postprocess foreign-key constraints.
355  */
356  transformFKConstraints(&cxt, true, false);
357 
358  /*
359  * Postprocess check constraints.
360  *
361  * For regular tables all constraints can be marked valid immediately,
362  * because the table is new therefore empty. Not so for foreign tables.
363  */
365 
366  /*
367  * Output results.
368  */
369  stmt->tableElts = cxt.columns;
370  stmt->constraints = cxt.ckconstraints;
371  stmt->nnconstraints = cxt.nnconstraints;
372 
373  result = lappend(cxt.blist, stmt);
374  result = list_concat(result, cxt.alist);
375  result = list_concat(result, save_alist);
376 
377  return result;
378 }
#define Assert(condition)
Definition: c.h:863
#define NOTICE
Definition: elog.h:35
Oid RangeVarGetAndCheckCreationNamespace(RangeVar *relation, LOCKMODE lockmode, Oid *existing_relation_id)
Definition: namespace.c:739
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
void cancel_parser_errposition_callback(ParseCallbackState *pcbstate)
Definition: parse_node.c:156
void setup_parser_errposition_callback(ParseCallbackState *pcbstate, ParseState *pstate, int location)
Definition: parse_node.c:140
static void transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_clause)
static void transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
void checkMembershipInCurrentExtension(const ObjectAddress *object)
Definition: pg_depend.c:258
#define linitial(l)
Definition: pg_list.h:178
#define strVal(v)
Definition: value.h:82

References CreateStmtContext::alist, Assert, CreateStmtContext::blist, cancel_parser_errposition_callback(), checkMembershipInCurrentExtension(), CreateStmtContext::ckconstraints, CreateStmtContext::columns, element(), elog, ereport, errcode(), errmsg(), ERROR, CreateStmtContext::fkconstraints, foreach_node, get_namespace_name(), CreateStmtContext::inhRelations, IsA, CreateStmtContext::isalter, CreateStmtContext::isforeign, CreateStmtContext::ispartitioned, CreateStmtContext::ixconstraints, lappend(), lfirst, CreateStmtContext::likeclauses, linitial, list_concat(), make_parsestate(), NIL, CreateStmtContext::nnconstraints, nodeTag, NoLock, NOTICE, ObjectAddressSet, CreateStmtContext::ofType, OidIsValid, ParseState::p_sourcetext, CreateStmtContext::partbound, CreateStmtContext::pkey, CreateStmtContext::pstate, RangeVarGetAndCheckCreationNamespace(), CreateStmtContext::rel, CreateStmtContext::relation, setup_parser_errposition_callback(), stmt, CreateStmtContext::stmtType, strVal, transformCheckConstraints(), transformColumnDefinition(), transformFKConstraints(), transformIndexConstraints(), transformOfType(), transformTableConstraint(), and transformTableLikeClause().

Referenced by ProcessUtilitySlow().

◆ transformIndexStmt()

IndexStmt* transformIndexStmt ( Oid  relid,
IndexStmt stmt,
const char *  queryString 
)

Definition at line 3016 of file parse_utilcmd.c.

3017 {
3018  ParseState *pstate;
3019  ParseNamespaceItem *nsitem;
3020  ListCell *l;
3021  Relation rel;
3022 
3023  /* Nothing to do if statement already transformed. */
3024  if (stmt->transformed)
3025  return stmt;
3026 
3027  /* Set up pstate */
3028  pstate = make_parsestate(NULL);
3029  pstate->p_sourcetext = queryString;
3030 
3031  /*
3032  * Put the parent table into the rtable so that the expressions can refer
3033  * to its fields without qualification. Caller is responsible for locking
3034  * relation, but we still need to open it.
3035  */
3036  rel = relation_open(relid, NoLock);
3037  nsitem = addRangeTableEntryForRelation(pstate, rel,
3039  NULL, false, true);
3040 
3041  /* no to join list, yes to namespaces */
3042  addNSItemToQuery(pstate, nsitem, false, true, true);
3043 
3044  /* take care of the where clause */
3045  if (stmt->whereClause)
3046  {
3047  stmt->whereClause = transformWhereClause(pstate,
3048  stmt->whereClause,
3050  "WHERE");
3051  /* we have to fix its collations too */
3052  assign_expr_collations(pstate, stmt->whereClause);
3053  }
3054 
3055  /* take care of any index expressions */
3056  foreach(l, stmt->indexParams)
3057  {
3058  IndexElem *ielem = (IndexElem *) lfirst(l);
3059 
3060  if (ielem->expr)
3061  {
3062  /* Extract preliminary index col name before transforming expr */
3063  if (ielem->indexcolname == NULL)
3064  ielem->indexcolname = FigureIndexColname(ielem->expr);
3065 
3066  /* Now do parse transformation of the expression */
3067  ielem->expr = transformExpr(pstate, ielem->expr,
3069 
3070  /* We have to fix its collations too */
3071  assign_expr_collations(pstate, ielem->expr);
3072 
3073  /*
3074  * transformExpr() should have already rejected subqueries,
3075  * aggregates, window functions, and SRFs, based on the EXPR_KIND_
3076  * for an index expression.
3077  *
3078  * DefineIndex() will make more checks.
3079  */
3080  }
3081  }
3082 
3083  /*
3084  * Check that only the base rel is mentioned. (This should be dead code
3085  * now that add_missing_from is history.)
3086  */
3087  if (list_length(pstate->p_rtable) != 1)
3088  ereport(ERROR,
3089  (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
3090  errmsg("index expressions and predicates can refer only to the table being indexed")));
3091 
3092  free_parsestate(pstate);
3093 
3094  /* Close relation */
3095  table_close(rel, NoLock);
3096 
3097  /* Mark statement as successfully transformed */
3098  stmt->transformed = true;
3099 
3100  return stmt;
3101 }
Node * transformWhereClause(ParseState *pstate, Node *clause, ParseExprKind exprKind, const char *constructName)
void assign_expr_collations(ParseState *pstate, Node *expr)
void free_parsestate(ParseState *pstate)
Definition: parse_node.c:72
@ EXPR_KIND_INDEX_EXPRESSION
Definition: parse_node.h:72
@ EXPR_KIND_INDEX_PREDICATE
Definition: parse_node.h:73
char * FigureIndexColname(Node *node)
static int list_length(const List *l)
Definition: pg_list.h:152
Node * expr
Definition: parsenodes.h:786
char * indexcolname
Definition: parsenodes.h:787
List * p_rtable
Definition: parse_node.h:212

References AccessShareLock, addNSItemToQuery(), addRangeTableEntryForRelation(), assign_expr_collations(), ereport, errcode(), errmsg(), ERROR, IndexElem::expr, EXPR_KIND_INDEX_EXPRESSION, EXPR_KIND_INDEX_PREDICATE, FigureIndexColname(), free_parsestate(), IndexElem::indexcolname, lfirst, list_length(), make_parsestate(), NoLock, ParseState::p_rtable, ParseState::p_sourcetext, relation_open(), stmt, table_close(), transformExpr(), and transformWhereClause().

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

◆ transformPartitionBound()

PartitionBoundSpec* transformPartitionBound ( ParseState pstate,
Relation  parent,
PartitionBoundSpec spec 
)

Definition at line 4205 of file parse_utilcmd.c.

4207 {
4208  PartitionBoundSpec *result_spec;
4210  char strategy = get_partition_strategy(key);
4211  int partnatts = get_partition_natts(key);
4212  List *partexprs = get_partition_exprs(key);
4213 
4214  /* Avoid scribbling on input */
4215  result_spec = copyObject(spec);
4216 
4217  if (spec->is_default)
4218  {
4219  /*
4220  * Hash partitioning does not support a default partition; there's no
4221  * use case for it (since the set of partitions to create is perfectly
4222  * defined), and if users do get into it accidentally, it's hard to
4223  * back out from it afterwards.
4224  */
4225  if (strategy == PARTITION_STRATEGY_HASH)
4226  ereport(ERROR,
4227  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4228  errmsg("a hash-partitioned table may not have a default partition")));
4229 
4230  /*
4231  * In case of the default partition, parser had no way to identify the
4232  * partition strategy. Assign the parent's strategy to the default
4233  * partition bound spec.
4234  */
4235  result_spec->strategy = strategy;
4236 
4237  return result_spec;
4238  }
4239 
4240  if (strategy == PARTITION_STRATEGY_HASH)
4241  {
4242  if (spec->strategy != PARTITION_STRATEGY_HASH)
4243  ereport(ERROR,
4244  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4245  errmsg("invalid bound specification for a hash partition"),
4246  parser_errposition(pstate, exprLocation((Node *) spec))));
4247 
4248  if (spec->modulus <= 0)
4249  ereport(ERROR,
4250  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4251  errmsg("modulus for hash partition must be an integer value greater than zero")));
4252 
4253  Assert(spec->remainder >= 0);
4254 
4255  if (spec->remainder >= spec->modulus)
4256  ereport(ERROR,
4257  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4258  errmsg("remainder for hash partition must be less than modulus")));
4259  }
4260  else if (strategy == PARTITION_STRATEGY_LIST)
4261  {
4262  ListCell *cell;
4263  char *colname;
4264  Oid coltype;
4265  int32 coltypmod;
4266  Oid partcollation;
4267 
4268  if (spec->strategy != PARTITION_STRATEGY_LIST)
4269  ereport(ERROR,
4270  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4271  errmsg("invalid bound specification for a list partition"),
4272  parser_errposition(pstate, exprLocation((Node *) spec))));
4273 
4274  /* Get the only column's name in case we need to output an error */
4275  if (key->partattrs[0] != 0)
4276  colname = get_attname(RelationGetRelid(parent),
4277  key->partattrs[0], false);
4278  else
4279  colname = deparse_expression((Node *) linitial(partexprs),
4281  RelationGetRelid(parent)),
4282  false, false);
4283  /* Need its type data too */
4284  coltype = get_partition_col_typid(key, 0);
4285  coltypmod = get_partition_col_typmod(key, 0);
4286  partcollation = get_partition_col_collation(key, 0);
4287 
4288  result_spec->listdatums = NIL;
4289  foreach(cell, spec->listdatums)
4290  {
4291  Node *expr = lfirst(cell);
4292  Const *value;
4293  ListCell *cell2;
4294  bool duplicate;
4295 
4296  value = transformPartitionBoundValue(pstate, expr,
4297  colname, coltype, coltypmod,
4298  partcollation);
4299 
4300  /* Don't add to the result if the value is a duplicate */
4301  duplicate = false;
4302  foreach(cell2, result_spec->listdatums)
4303  {
4304  Const *value2 = lfirst_node(Const, cell2);
4305 
4306  if (equal(value, value2))
4307  {
4308  duplicate = true;
4309  break;
4310  }
4311  }
4312  if (duplicate)
4313  continue;
4314 
4315  result_spec->listdatums = lappend(result_spec->listdatums,
4316  value);
4317  }
4318  }
4319  else if (strategy == PARTITION_STRATEGY_RANGE)
4320  {
4321  if (spec->strategy != PARTITION_STRATEGY_RANGE)
4322  ereport(ERROR,
4323  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4324  errmsg("invalid bound specification for a range partition"),
4325  parser_errposition(pstate, exprLocation((Node *) spec))));
4326 
4327  if (list_length(spec->lowerdatums) != partnatts)
4328  ereport(ERROR,
4329  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4330  errmsg("FROM must specify exactly one value per partitioning column")));
4331  if (list_length(spec->upperdatums) != partnatts)
4332  ereport(ERROR,
4333  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4334  errmsg("TO must specify exactly one value per partitioning column")));
4335 
4336  /*
4337  * Convert raw parse nodes into PartitionRangeDatum nodes and perform
4338  * any necessary validation.
4339  */
4340  result_spec->lowerdatums =
4342  parent);
4343  result_spec->upperdatums =
4345  parent);
4346  }
4347  else
4348  elog(ERROR, "unexpected partition strategy: %d", (int) strategy);
4349 
4350  return result_spec;
4351 }
signed int int32
Definition: c.h:508
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
static struct @160 value
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:827
int exprLocation(const Node *expr)
Definition: nodeFuncs.c:1380
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:106
static List * transformPartitionRangeBounds(ParseState *pstate, List *blist, Relation parent)
static Const * transformPartitionBoundValue(ParseState *pstate, Node *val, const char *colName, Oid colType, int32 colTypmod, Oid partCollation)
@ PARTITION_STRATEGY_HASH
Definition: parsenodes.h:876
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:874
@ PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:875
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:51
static int get_partition_strategy(PartitionKey key)
Definition: partcache.h:59
static int32 get_partition_col_typmod(PartitionKey key, int col)
Definition: partcache.h:92
static int get_partition_natts(PartitionKey key)
Definition: partcache.h:65
static Oid get_partition_col_typid(PartitionKey key, int col)
Definition: partcache.h:86
static Oid get_partition_col_collation(PartitionKey key, int col)
Definition: partcache.h:98
static List * get_partition_exprs(PartitionKey key)
Definition: partcache.h:71
char * deparse_expression(Node *expr, List *dpcontext, bool forceprefix, bool showimplicit)
Definition: ruleutils.c:3635
List * deparse_context_for(const char *aliasname, Oid relid)
Definition: ruleutils.c:3698

References Assert, copyObject, deparse_context_for(), deparse_expression(), elog, equal(), ereport, errcode(), errmsg(), ERROR, exprLocation(), get_attname(), get_partition_col_collation(), get_partition_col_typid(), get_partition_col_typmod(), get_partition_exprs(), get_partition_natts(), get_partition_strategy(), PartitionBoundSpec::is_default, sort-test::key, lappend(), lfirst, lfirst_node, linitial, list_length(), PartitionBoundSpec::listdatums, PartitionBoundSpec::lowerdatums, NIL, parser_errposition(), PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, RelationGetPartitionKey(), RelationGetRelationName, RelationGetRelid, PartitionBoundSpec::strategy, transformPartitionBoundValue(), transformPartitionRangeBounds(), PartitionBoundSpec::upperdatums, and value.

Referenced by DefineRelation(), and transformPartitionCmd().

◆ transformRuleStmt()

void transformRuleStmt ( RuleStmt stmt,
const char *  queryString,
List **  actions,
Node **  whereClause 
)

Definition at line 3186 of file parse_utilcmd.c.

3188 {
3189  Relation rel;
3190  ParseState *pstate;
3191  ParseNamespaceItem *oldnsitem;
3192  ParseNamespaceItem *newnsitem;
3193 
3194  /*
3195  * To avoid deadlock, make sure the first thing we do is grab
3196  * AccessExclusiveLock on the target relation. This will be needed by
3197  * DefineQueryRewrite(), and we don't want to grab a lesser lock
3198  * beforehand.
3199  */
3200  rel = table_openrv(stmt->relation, AccessExclusiveLock);
3201 
3202  if (rel->rd_rel->relkind == RELKIND_MATVIEW)
3203  ereport(ERROR,
3204  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3205  errmsg("rules on materialized views are not supported")));
3206 
3207  /* Set up pstate */
3208  pstate = make_parsestate(NULL);
3209  pstate->p_sourcetext = queryString;
3210 
3211  /*
3212  * NOTE: 'OLD' must always have a varno equal to 1 and 'NEW' equal to 2.
3213  * Set up their ParseNamespaceItems in the main pstate for use in parsing
3214  * the rule qualification.
3215  */
3216  oldnsitem = addRangeTableEntryForRelation(pstate, rel,
3218  makeAlias("old", NIL),
3219  false, false);
3220  newnsitem = addRangeTableEntryForRelation(pstate, rel,
3222  makeAlias("new", NIL),
3223  false, false);
3224 
3225  /*
3226  * They must be in the namespace too for lookup purposes, but only add the
3227  * one(s) that are relevant for the current kind of rule. In an UPDATE
3228  * rule, quals must refer to OLD.field or NEW.field to be unambiguous, but
3229  * there's no need to be so picky for INSERT & DELETE. We do not add them
3230  * to the joinlist.
3231  */
3232  switch (stmt->event)
3233  {
3234  case CMD_SELECT:
3235  addNSItemToQuery(pstate, oldnsitem, false, true, true);
3236  break;
3237  case CMD_UPDATE:
3238  addNSItemToQuery(pstate, oldnsitem, false, true, true);
3239  addNSItemToQuery(pstate, newnsitem, false, true, true);
3240  break;
3241  case CMD_INSERT:
3242  addNSItemToQuery(pstate, newnsitem, false, true, true);
3243  break;
3244  case CMD_DELETE:
3245  addNSItemToQuery(pstate, oldnsitem, false, true, true);
3246  break;
3247  default:
3248  elog(ERROR, "unrecognized event type: %d",
3249  (int) stmt->event);
3250  break;
3251  }
3252 
3253  /* take care of the where clause */
3254  *whereClause = transformWhereClause(pstate,
3255  stmt->whereClause,
3257  "WHERE");
3258  /* we have to fix its collations too */
3259  assign_expr_collations(pstate, *whereClause);
3260 
3261  /* this is probably dead code without add_missing_from: */
3262  if (list_length(pstate->p_rtable) != 2) /* naughty, naughty... */
3263  ereport(ERROR,
3264  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3265  errmsg("rule WHERE condition cannot contain references to other relations")));
3266 
3267  /*
3268  * 'instead nothing' rules with a qualification need a query rangetable so
3269  * the rewrite handler can add the negated rule qualification to the
3270  * original query. We create a query with the new command type CMD_NOTHING
3271  * here that is treated specially by the rewrite system.
3272  */
3273  if (stmt->actions == NIL)
3274  {
3275  Query *nothing_qry = makeNode(Query);
3276 
3277  nothing_qry->commandType = CMD_NOTHING;
3278  nothing_qry->rtable = pstate->p_rtable;
3279  nothing_qry->rteperminfos = pstate->p_rteperminfos;
3280  nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */
3281 
3282  *actions = list_make1(nothing_qry);
3283  }
3284  else
3285  {
3286  ListCell *l;
3287  List *newactions = NIL;
3288 
3289  /*
3290  * transform each statement, like parse_sub_analyze()
3291  */
3292  foreach(l, stmt->actions)
3293  {
3294  Node *action = (Node *) lfirst(l);
3295  ParseState *sub_pstate = make_parsestate(NULL);
3296  Query *sub_qry,
3297  *top_subqry;
3298  bool has_old,
3299  has_new;
3300 
3301  /*
3302  * Since outer ParseState isn't parent of inner, have to pass down
3303  * the query text by hand.
3304  */
3305  sub_pstate->p_sourcetext = queryString;
3306 
3307  /*
3308  * Set up OLD/NEW in the rtable for this statement. The entries
3309  * are added only to relnamespace, not varnamespace, because we
3310  * don't want them to be referred to by unqualified field names
3311  * nor "*" in the rule actions. We decide later whether to put
3312  * them in the joinlist.
3313  */
3314  oldnsitem = addRangeTableEntryForRelation(sub_pstate, rel,
3316  makeAlias("old", NIL),
3317  false, false);
3318  newnsitem = addRangeTableEntryForRelation(sub_pstate, rel,
3320  makeAlias("new", NIL),
3321  false, false);
3322  addNSItemToQuery(sub_pstate, oldnsitem, false, true, false);
3323  addNSItemToQuery(sub_pstate, newnsitem, false, true, false);
3324 
3325  /* Transform the rule action statement */
3326  top_subqry = transformStmt(sub_pstate, action);
3327 
3328  /*
3329  * We cannot support utility-statement actions (eg NOTIFY) with
3330  * nonempty rule WHERE conditions, because there's no way to make
3331  * the utility action execute conditionally.
3332  */
3333  if (top_subqry->commandType == CMD_UTILITY &&
3334  *whereClause != NULL)
3335  ereport(ERROR,
3336  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3337  errmsg("rules with WHERE conditions can only have SELECT, INSERT, UPDATE, or DELETE actions")));
3338 
3339  /*
3340  * If the action is INSERT...SELECT, OLD/NEW have been pushed down
3341  * into the SELECT, and that's what we need to look at. (Ugly
3342  * kluge ... try to fix this when we redesign querytrees.)
3343  */
3344  sub_qry = getInsertSelectQuery(top_subqry, NULL);
3345 
3346  /*
3347  * If the sub_qry is a setop, we cannot attach any qualifications
3348  * to it, because the planner won't notice them. This could
3349  * perhaps be relaxed someday, but for now, we may as well reject
3350  * such a rule immediately.
3351  */
3352  if (sub_qry->setOperations != NULL && *whereClause != NULL)
3353  ereport(ERROR,
3354  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3355  errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
3356 
3357  /*
3358  * Validate action's use of OLD/NEW, qual too
3359  */
3360  has_old =
3361  rangeTableEntry_used((Node *) sub_qry, PRS2_OLD_VARNO, 0) ||
3362  rangeTableEntry_used(*whereClause, PRS2_OLD_VARNO, 0);
3363  has_new =
3364  rangeTableEntry_used((Node *) sub_qry, PRS2_NEW_VARNO, 0) ||
3365  rangeTableEntry_used(*whereClause, PRS2_NEW_VARNO, 0);
3366 
3367  switch (stmt->event)
3368  {
3369  case CMD_SELECT:
3370  if (has_old)
3371  ereport(ERROR,
3372  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3373  errmsg("ON SELECT rule cannot use OLD")));
3374  if (has_new)
3375  ereport(ERROR,
3376  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3377  errmsg("ON SELECT rule cannot use NEW")));
3378  break;
3379  case CMD_UPDATE:
3380  /* both are OK */
3381  break;
3382  case CMD_INSERT:
3383  if (has_old)
3384  ereport(ERROR,
3385  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3386  errmsg("ON INSERT rule cannot use OLD")));
3387  break;
3388  case CMD_DELETE:
3389  if (has_new)
3390  ereport(ERROR,
3391  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3392  errmsg("ON DELETE rule cannot use NEW")));
3393  break;
3394  default:
3395  elog(ERROR, "unrecognized event type: %d",
3396  (int) stmt->event);
3397  break;
3398  }
3399 
3400  /*
3401  * OLD/NEW are not allowed in WITH queries, because they would
3402  * amount to outer references for the WITH, which we disallow.
3403  * However, they were already in the outer rangetable when we
3404  * analyzed the query, so we have to check.
3405  *
3406  * Note that in the INSERT...SELECT case, we need to examine the
3407  * CTE lists of both top_subqry and sub_qry.
3408  *
3409  * Note that we aren't digging into the body of the query looking
3410  * for WITHs in nested sub-SELECTs. A WITH down there can
3411  * legitimately refer to OLD/NEW, because it'd be an
3412  * indirect-correlated outer reference.
3413  */
3414  if (rangeTableEntry_used((Node *) top_subqry->cteList,
3415  PRS2_OLD_VARNO, 0) ||
3416  rangeTableEntry_used((Node *) sub_qry->cteList,
3417  PRS2_OLD_VARNO, 0))
3418  ereport(ERROR,
3419  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3420  errmsg("cannot refer to OLD within WITH query")));
3421  if (rangeTableEntry_used((Node *) top_subqry->cteList,
3422  PRS2_NEW_VARNO, 0) ||
3423  rangeTableEntry_used((Node *) sub_qry->cteList,
3424  PRS2_NEW_VARNO, 0))
3425  ereport(ERROR,
3426  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3427  errmsg("cannot refer to NEW within WITH query")));
3428 
3429  /*
3430  * For efficiency's sake, add OLD to the rule action's jointree
3431  * only if it was actually referenced in the statement or qual.
3432  *
3433  * For INSERT, NEW is not really a relation (only a reference to
3434  * the to-be-inserted tuple) and should never be added to the
3435  * jointree.
3436  *
3437  * For UPDATE, we treat NEW as being another kind of reference to
3438  * OLD, because it represents references to *transformed* tuples
3439  * of the existing relation. It would be wrong to enter NEW
3440  * separately in the jointree, since that would cause a double
3441  * join of the updated relation. It's also wrong to fail to make
3442  * a jointree entry if only NEW and not OLD is mentioned.
3443  */
3444  if (has_old || (has_new && stmt->event == CMD_UPDATE))
3445  {
3446  RangeTblRef *rtr;
3447 
3448  /*
3449  * If sub_qry is a setop, manipulating its jointree will do no
3450  * good at all, because the jointree is dummy. (This should be
3451  * a can't-happen case because of prior tests.)
3452  */
3453  if (sub_qry->setOperations != NULL)
3454  ereport(ERROR,
3455  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3456  errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
3457  /* hackishly add OLD to the already-built FROM clause */
3458  rtr = makeNode(RangeTblRef);
3459  rtr->rtindex = oldnsitem->p_rtindex;
3460  sub_qry->jointree->fromlist =
3461  lappend(sub_qry->jointree->fromlist, rtr);
3462  }
3463 
3464  newactions = lappend(newactions, top_subqry);
3465 
3466  free_parsestate(sub_pstate);
3467  }
3468 
3469  *actions = newactions;
3470  }
3471 
3472  free_parsestate(pstate);
3473 
3474  /* Close relation, but keep the exclusive lock */
3475  table_close(rel, NoLock);
3476 }
#define AccessExclusiveLock
Definition: lockdefs.h:43
Alias * makeAlias(const char *aliasname, List *colnames)
Definition: makefuncs.c:389
FromExpr * makeFromExpr(List *fromlist, Node *quals)
Definition: makefuncs.c:287
@ CMD_UTILITY
Definition: nodes.h:270
@ CMD_INSERT
Definition: nodes.h:267
@ CMD_DELETE
Definition: nodes.h:268
@ CMD_UPDATE
Definition: nodes.h:266
@ CMD_SELECT
Definition: nodes.h:265
@ CMD_NOTHING
Definition: nodes.h:272
@ EXPR_KIND_WHERE
Definition: parse_node.h:46
Query * transformStmt(ParseState *pstate, Node *parseTree)
Definition: analyze.c:396
#define PRS2_OLD_VARNO
Definition: primnodes.h:244
#define PRS2_NEW_VARNO
Definition: primnodes.h:245
Query * getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr)
Definition: rewriteManip.c:998
bool rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
Definition: rewriteManip.c:966
List * fromlist
Definition: primnodes.h:2308
List * p_rteperminfos
Definition: parse_node.h:213
FromExpr * jointree
Definition: parsenodes.h:177
Node * setOperations
Definition: parsenodes.h:221
List * cteList
Definition: parsenodes.h:168
List * rtable
Definition: parsenodes.h:170
CmdType commandType
Definition: parsenodes.h:121
Relation table_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition: table.c:83

References AccessExclusiveLock, AccessShareLock, generate_unaccent_rules::action, addNSItemToQuery(), addRangeTableEntryForRelation(), assign_expr_collations(), CMD_DELETE, CMD_INSERT, CMD_NOTHING, CMD_SELECT, CMD_UPDATE, CMD_UTILITY, Query::commandType, Query::cteList, elog, ereport, errcode(), errmsg(), ERROR, EXPR_KIND_WHERE, free_parsestate(), FromExpr::fromlist, getInsertSelectQuery(), Query::jointree, lappend(), lfirst, list_length(), list_make1, make_parsestate(), makeAlias(), makeFromExpr(), makeNode, NIL, NoLock, ParseState::p_rtable, ParseState::p_rteperminfos, ParseNamespaceItem::p_rtindex, ParseState::p_sourcetext, PRS2_NEW_VARNO, PRS2_OLD_VARNO, rangeTableEntry_used(), RelationData::rd_rel, Query::rtable, RangeTblRef::rtindex, Query::setOperations, stmt, table_close(), table_openrv(), transformStmt(), and transformWhereClause().

Referenced by DefineRule().

◆ transformStatsStmt()

CreateStatsStmt* transformStatsStmt ( Oid  relid,
CreateStatsStmt stmt,
const char *  queryString 
)

Definition at line 3111 of file parse_utilcmd.c.

3112 {
3113  ParseState *pstate;
3114  ParseNamespaceItem *nsitem;
3115  ListCell *l;
3116  Relation rel;
3117 
3118  /* Nothing to do if statement already transformed. */
3119  if (stmt->transformed)
3120  return stmt;
3121 
3122  /* Set up pstate */
3123  pstate = make_parsestate(NULL);
3124  pstate->p_sourcetext = queryString;
3125 
3126  /*
3127  * Put the parent table into the rtable so that the expressions can refer
3128  * to its fields without qualification. Caller is responsible for locking
3129  * relation, but we still need to open it.
3130  */
3131  rel = relation_open(relid, NoLock);
3132  nsitem = addRangeTableEntryForRelation(pstate, rel,
3134  NULL, false, true);
3135 
3136  /* no to join list, yes to namespaces */
3137  addNSItemToQuery(pstate, nsitem, false, true, true);
3138 
3139  /* take care of any expressions */
3140  foreach(l, stmt->exprs)
3141  {
3142  StatsElem *selem = (StatsElem *) lfirst(l);
3143 
3144  if (selem->expr)
3145  {
3146  /* Now do parse transformation of the expression */
3147  selem->expr = transformExpr(pstate, selem->expr,
3149 
3150  /* We have to fix its collations too */
3151  assign_expr_collations(pstate, selem->expr);
3152  }
3153  }
3154 
3155  /*
3156  * Check that only the base rel is mentioned. (This should be dead code
3157  * now that add_missing_from is history.)
3158  */
3159  if (list_length(pstate->p_rtable) != 1)
3160  ereport(ERROR,
3161  (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
3162  errmsg("statistics expressions can refer only to the table being referenced")));
3163 
3164  free_parsestate(pstate);
3165 
3166  /* Close relation */
3167  table_close(rel, NoLock);
3168 
3169  /* Mark statement as successfully transformed */
3170  stmt->transformed = true;
3171 
3172  return stmt;
3173 }
@ EXPR_KIND_STATS_EXPRESSION
Definition: parse_node.h:74
Node * expr
Definition: parsenodes.h:3434

References AccessShareLock, addNSItemToQuery(), addRangeTableEntryForRelation(), assign_expr_collations(), ereport, errcode(), errmsg(), ERROR, StatsElem::expr, EXPR_KIND_STATS_EXPRESSION, free_parsestate(), lfirst, list_length(), make_parsestate(), NoLock, ParseState::p_rtable, ParseState::p_sourcetext, relation_open(), stmt, table_close(), and transformExpr().

Referenced by ATPostAlterTypeParse(), and ProcessUtilitySlow().