PostgreSQL Source Code  git master
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 1144 of file parse_utilcmd.c.

1145 {
1146  List *result = NIL;
1147  List *atsubcmds = NIL;
1148  AttrNumber parent_attno;
1149  Relation relation;
1150  Relation childrel;
1151  TupleDesc tupleDesc;
1152  TupleConstr *constr;
1153  AttrMap *attmap;
1154  char *comment;
1155 
1156  /*
1157  * Open the relation referenced by the LIKE clause. We should still have
1158  * the table lock obtained by transformTableLikeClause (and this'll throw
1159  * an assertion failure if not). Hence, no need to recheck privileges
1160  * etc. We must open the rel by OID not name, to be sure we get the same
1161  * table.
1162  */
1163  if (!OidIsValid(table_like_clause->relationOid))
1164  elog(ERROR, "expandTableLikeClause called on untransformed LIKE clause");
1165 
1166  relation = relation_open(table_like_clause->relationOid, NoLock);
1167 
1168  tupleDesc = RelationGetDescr(relation);
1169  constr = tupleDesc->constr;
1170 
1171  /*
1172  * Open the newly-created child relation; we have lock on that too.
1173  */
1174  childrel = relation_openrv(heapRel, NoLock);
1175 
1176  /*
1177  * Construct a map from the LIKE relation's attnos to the child rel's.
1178  * This re-checks type match etc, although it shouldn't be possible to
1179  * have a failure since both tables are locked.
1180  */
1181  attmap = build_attrmap_by_name(RelationGetDescr(childrel),
1182  tupleDesc,
1183  false);
1184 
1185  /*
1186  * Process defaults, if required.
1187  */
1188  if ((table_like_clause->options &
1190  constr != NULL)
1191  {
1192  for (parent_attno = 1; parent_attno <= tupleDesc->natts;
1193  parent_attno++)
1194  {
1195  Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
1196  parent_attno - 1);
1197 
1198  /*
1199  * Ignore dropped columns in the parent.
1200  */
1201  if (attribute->attisdropped)
1202  continue;
1203 
1204  /*
1205  * Copy default, if present and it should be copied. We have
1206  * separate options for plain default expressions and GENERATED
1207  * defaults.
1208  */
1209  if (attribute->atthasdef &&
1210  (attribute->attgenerated ?
1211  (table_like_clause->options & CREATE_TABLE_LIKE_GENERATED) :
1212  (table_like_clause->options & CREATE_TABLE_LIKE_DEFAULTS)))
1213  {
1214  Node *this_default;
1215  AlterTableCmd *atsubcmd;
1216  bool found_whole_row;
1217 
1218  this_default = TupleDescGetDefault(tupleDesc, parent_attno);
1219  if (this_default == NULL)
1220  elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
1221  parent_attno, RelationGetRelationName(relation));
1222 
1223  atsubcmd = makeNode(AlterTableCmd);
1224  atsubcmd->subtype = AT_CookedColumnDefault;
1225  atsubcmd->num = attmap->attnums[parent_attno - 1];
1226  atsubcmd->def = map_variable_attnos(this_default,
1227  1, 0,
1228  attmap,
1229  InvalidOid,
1230  &found_whole_row);
1231 
1232  /*
1233  * Prevent this for the same reason as for constraints below.
1234  * Note that defaults cannot contain any vars, so it's OK that
1235  * the error message refers to generated columns.
1236  */
1237  if (found_whole_row)
1238  ereport(ERROR,
1239  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1240  errmsg("cannot convert whole-row table reference"),
1241  errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
1242  NameStr(attribute->attname),
1243  RelationGetRelationName(relation))));
1244 
1245  atsubcmds = lappend(atsubcmds, atsubcmd);
1246  }
1247  }
1248  }
1249 
1250  /*
1251  * Copy CHECK constraints if requested, being careful to adjust attribute
1252  * numbers so they match the child.
1253  */
1254  if ((table_like_clause->options & CREATE_TABLE_LIKE_CONSTRAINTS) &&
1255  constr != NULL)
1256  {
1257  int ccnum;
1258 
1259  for (ccnum = 0; ccnum < constr->num_check; ccnum++)
1260  {
1261  char *ccname = constr->check[ccnum].ccname;
1262  char *ccbin = constr->check[ccnum].ccbin;
1263  bool ccnoinherit = constr->check[ccnum].ccnoinherit;
1264  Node *ccbin_node;
1265  bool found_whole_row;
1266  Constraint *n;
1267  AlterTableCmd *atsubcmd;
1268 
1269  ccbin_node = map_variable_attnos(stringToNode(ccbin),
1270  1, 0,
1271  attmap,
1272  InvalidOid, &found_whole_row);
1273 
1274  /*
1275  * We reject whole-row variables because the whole point of LIKE
1276  * is that the new table's rowtype might later diverge from the
1277  * parent's. So, while translation might be possible right now,
1278  * it wouldn't be possible to guarantee it would work in future.
1279  */
1280  if (found_whole_row)
1281  ereport(ERROR,
1282  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1283  errmsg("cannot convert whole-row table reference"),
1284  errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
1285  ccname,
1286  RelationGetRelationName(relation))));
1287 
1288  n = makeNode(Constraint);
1289  n->contype = CONSTR_CHECK;
1290  n->conname = pstrdup(ccname);
1291  n->location = -1;
1292  n->is_no_inherit = ccnoinherit;
1293  n->raw_expr = NULL;
1294  n->cooked_expr = nodeToString(ccbin_node);
1295 
1296  /* We can skip validation, since the new table should be empty. */
1297  n->skip_validation = true;
1298  n->initially_valid = true;
1299 
1300  atsubcmd = makeNode(AlterTableCmd);
1301  atsubcmd->subtype = AT_AddConstraint;
1302  atsubcmd->def = (Node *) n;
1303  atsubcmds = lappend(atsubcmds, atsubcmd);
1304 
1305  /* Copy comment on constraint */
1306  if ((table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS) &&
1308  n->conname, false),
1309  ConstraintRelationId,
1310  0)) != NULL)
1311  {
1313 
1314  stmt->objtype = OBJECT_TABCONSTRAINT;
1315  stmt->object = (Node *) list_make3(makeString(heapRel->schemaname),
1316  makeString(heapRel->relname),
1317  makeString(n->conname));
1318  stmt->comment = comment;
1319 
1320  result = lappend(result, stmt);
1321  }
1322  }
1323  }
1324 
1325  /*
1326  * If we generated any ALTER TABLE actions above, wrap them into a single
1327  * ALTER TABLE command. Stick it at the front of the result, so it runs
1328  * before any CommentStmts we made above.
1329  */
1330  if (atsubcmds)
1331  {
1333 
1334  atcmd->relation = copyObject(heapRel);
1335  atcmd->cmds = atsubcmds;
1336  atcmd->objtype = OBJECT_TABLE;
1337  atcmd->missing_ok = false;
1338  result = lcons(atcmd, result);
1339  }
1340 
1341  /*
1342  * Process indexes if required.
1343  */
1344  if ((table_like_clause->options & CREATE_TABLE_LIKE_INDEXES) &&
1345  relation->rd_rel->relhasindex)
1346  {
1347  List *parent_indexes;
1348  ListCell *l;
1349 
1350  parent_indexes = RelationGetIndexList(relation);
1351 
1352  foreach(l, parent_indexes)
1353  {
1354  Oid parent_index_oid = lfirst_oid(l);
1355  Relation parent_index;
1356  IndexStmt *index_stmt;
1357 
1358  parent_index = index_open(parent_index_oid, AccessShareLock);
1359 
1360  /* Build CREATE INDEX statement to recreate the parent_index */
1361  index_stmt = generateClonedIndexStmt(heapRel,
1362  parent_index,
1363  attmap,
1364  NULL);
1365 
1366  /* Copy comment on index, if requested */
1367  if (table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS)
1368  {
1369  comment = GetComment(parent_index_oid, RelationRelationId, 0);
1370 
1371  /*
1372  * We make use of IndexStmt's idxcomment option, so as not to
1373  * need to know now what name the index will have.
1374  */
1375  index_stmt->idxcomment = comment;
1376  }
1377 
1378  result = lappend(result, index_stmt);
1379 
1380  index_close(parent_index, AccessShareLock);
1381  }
1382  }
1383 
1384  /*
1385  * Process extended statistics if required.
1386  */
1387  if (table_like_clause->options & CREATE_TABLE_LIKE_STATISTICS)
1388  {
1389  List *parent_extstats;
1390  ListCell *l;
1391 
1392  parent_extstats = RelationGetStatExtList(relation);
1393 
1394  foreach(l, parent_extstats)
1395  {
1396  Oid parent_stat_oid = lfirst_oid(l);
1397  CreateStatsStmt *stats_stmt;
1398 
1399  stats_stmt = generateClonedExtStatsStmt(heapRel,
1400  RelationGetRelid(childrel),
1401  parent_stat_oid,
1402  attmap);
1403 
1404  /* Copy comment on statistics object, if requested */
1405  if (table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS)
1406  {
1407  comment = GetComment(parent_stat_oid, StatisticExtRelationId, 0);
1408 
1409  /*
1410  * We make use of CreateStatsStmt's stxcomment option, so as
1411  * not to need to know now what name the statistics will have.
1412  */
1413  stats_stmt->stxcomment = comment;
1414  }
1415 
1416  result = lappend(result, stats_stmt);
1417  }
1418 
1419  list_free(parent_extstats);
1420  }
1421 
1422  /* Done with child rel */
1423  table_close(childrel, NoLock);
1424 
1425  /*
1426  * Close the parent rel, but keep our AccessShareLock on it until xact
1427  * commit. That will prevent someone else from deleting or ALTERing the
1428  * parent before the child is committed.
1429  */
1430  table_close(relation, NoLock);
1431 
1432  return result;
1433 }
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:746
#define OidIsValid(objectId)
Definition: c.h:775
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:857
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#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:1695
#define copyObject(obj)
Definition: nodes.h:224
#define makeNode(_type_)
Definition: nodes.h:155
char * nodeToString(const void *obj)
Definition: outfuncs.c:791
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:2710
@ OBJECT_TABLE
Definition: parsenodes.h:2302
@ OBJECT_TABCONSTRAINT
Definition: parsenodes.h:2301
@ AT_AddConstraint
Definition: parsenodes.h:2370
@ AT_CookedColumnDefault
Definition: parsenodes.h:2356
@ CREATE_TABLE_LIKE_COMMENTS
Definition: parsenodes.h:761
@ CREATE_TABLE_LIKE_GENERATED
Definition: parsenodes.h:765
@ CREATE_TABLE_LIKE_INDEXES
Definition: parsenodes.h:767
@ CREATE_TABLE_LIKE_DEFAULTS
Definition: parsenodes.h:764
@ CREATE_TABLE_LIKE_STATISTICS
Definition: parsenodes.h:768
@ CREATE_TABLE_LIKE_CONSTRAINTS
Definition: parsenodes.h:763
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:4800
List * RelationGetStatExtList(Relation relation)
Definition: relcache.c:4921
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:2434
RangeVar * relation
Definition: parsenodes.h:2345
ObjectType objtype
Definition: parsenodes.h:2347
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:2777
ConstrType contype
Definition: parsenodes.h:2736
bool is_no_inherit
Definition: parsenodes.h:2742
char * cooked_expr
Definition: parsenodes.h:2745
bool initially_valid
Definition: parsenodes.h:2741
bool skip_validation
Definition: parsenodes.h:2740
Node * raw_expr
Definition: parsenodes.h:2743
char * conname
Definition: parsenodes.h:2737
char * idxcomment
Definition: parsenodes.h:3366
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 3402 of file parse_utilcmd.c.

3405 {
3406  Relation rel;
3407  TupleDesc tupdesc;
3408  ParseState *pstate;
3409  CreateStmtContext cxt;
3410  List *save_alist;
3411  ListCell *lcmd,
3412  *l;
3413  List *newcmds = NIL;
3414  bool skipValidation = true;
3415  AlterTableCmd *newcmd;
3416  ParseNamespaceItem *nsitem;
3417 
3418  /* Caller is responsible for locking the relation */
3419  rel = relation_open(relid, NoLock);
3420  tupdesc = RelationGetDescr(rel);
3421 
3422  /* Set up pstate */
3423  pstate = make_parsestate(NULL);
3424  pstate->p_sourcetext = queryString;
3425  nsitem = addRangeTableEntryForRelation(pstate,
3426  rel,
3428  NULL,
3429  false,
3430  true);
3431  addNSItemToQuery(pstate, nsitem, false, true, true);
3432 
3433  /* Set up CreateStmtContext */
3434  cxt.pstate = pstate;
3435  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
3436  {
3437  cxt.stmtType = "ALTER FOREIGN TABLE";
3438  cxt.isforeign = true;
3439  }
3440  else
3441  {
3442  cxt.stmtType = "ALTER TABLE";
3443  cxt.isforeign = false;
3444  }
3445  cxt.relation = stmt->relation;
3446  cxt.rel = rel;
3447  cxt.inhRelations = NIL;
3448  cxt.isalter = true;
3449  cxt.columns = NIL;
3450  cxt.ckconstraints = NIL;
3451  cxt.fkconstraints = NIL;
3452  cxt.ixconstraints = NIL;
3453  cxt.likeclauses = NIL;
3454  cxt.blist = NIL;
3455  cxt.alist = NIL;
3456  cxt.pkey = NULL;
3457  cxt.ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
3458  cxt.partbound = NULL;
3459  cxt.ofType = false;
3460 
3461  /*
3462  * Transform ALTER subcommands that need it (most don't). These largely
3463  * re-use code from CREATE TABLE.
3464  */
3465  foreach(lcmd, stmt->cmds)
3466  {
3467  AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
3468 
3469  switch (cmd->subtype)
3470  {
3471  case AT_AddColumn:
3472  {
3473  ColumnDef *def = castNode(ColumnDef, cmd->def);
3474 
3475  transformColumnDefinition(&cxt, def);
3476 
3477  /*
3478  * If the column has a non-null default, we can't skip
3479  * validation of foreign keys.
3480  */
3481  if (def->raw_default != NULL)
3482  skipValidation = false;
3483 
3484  /*
3485  * All constraints are processed in other ways. Remove the
3486  * original list
3487  */
3488  def->constraints = NIL;
3489 
3490  newcmds = lappend(newcmds, cmd);
3491  break;
3492  }
3493 
3494  case AT_AddConstraint:
3495 
3496  /*
3497  * The original AddConstraint cmd node doesn't go to newcmds
3498  */
3499  if (IsA(cmd->def, Constraint))
3500  {
3501  transformTableConstraint(&cxt, (Constraint *) cmd->def);
3502  if (((Constraint *) cmd->def)->contype == CONSTR_FOREIGN)
3503  skipValidation = false;
3504  }
3505  else
3506  elog(ERROR, "unrecognized node type: %d",
3507  (int) nodeTag(cmd->def));
3508  break;
3509 
3510  case AT_AlterColumnType:
3511  {
3512  ColumnDef *def = castNode(ColumnDef, cmd->def);
3514 
3515  /*
3516  * For ALTER COLUMN TYPE, transform the USING clause if
3517  * one was specified.
3518  */
3519  if (def->raw_default)
3520  {
3521  def->cooked_default =
3522  transformExpr(pstate, def->raw_default,
3524  }
3525 
3526  /*
3527  * For identity column, create ALTER SEQUENCE command to
3528  * change the data type of the sequence. Identity sequence
3529  * is associated with the top level partitioned table.
3530  * Hence ignore partitions.
3531  */
3532  if (!RelationGetForm(rel)->relispartition)
3533  {
3534  attnum = get_attnum(relid, cmd->name);
3535  if (attnum == InvalidAttrNumber)
3536  ereport(ERROR,
3537  (errcode(ERRCODE_UNDEFINED_COLUMN),
3538  errmsg("column \"%s\" of relation \"%s\" does not exist",
3539  cmd->name, RelationGetRelationName(rel))));
3540 
3541  if (attnum > 0 &&
3542  TupleDescAttr(tupdesc, attnum - 1)->attidentity)
3543  {
3544  Oid seq_relid = getIdentitySequence(rel, attnum, false);
3545  Oid typeOid = typenameTypeId(pstate, def->typeName);
3546  AlterSeqStmt *altseqstmt = makeNode(AlterSeqStmt);
3547 
3548  altseqstmt->sequence
3550  get_rel_name(seq_relid),
3551  -1);
3552  altseqstmt->options = list_make1(makeDefElem("as",
3553  (Node *) makeTypeNameFromOid(typeOid, -1),
3554  -1));
3555  altseqstmt->for_identity = true;
3556  cxt.blist = lappend(cxt.blist, altseqstmt);
3557  }
3558  }
3559 
3560  newcmds = lappend(newcmds, cmd);
3561  break;
3562  }
3563 
3564  case AT_AddIdentity:
3565  {
3566  Constraint *def = castNode(Constraint, cmd->def);
3567  ColumnDef *newdef = makeNode(ColumnDef);
3569 
3570  newdef->colname = cmd->name;
3571  newdef->identity = def->generated_when;
3572  cmd->def = (Node *) newdef;
3573 
3574  attnum = get_attnum(relid, cmd->name);
3575  if (attnum == InvalidAttrNumber)
3576  ereport(ERROR,
3577  (errcode(ERRCODE_UNDEFINED_COLUMN),
3578  errmsg("column \"%s\" of relation \"%s\" does not exist",
3579  cmd->name, RelationGetRelationName(rel))));
3580 
3581  generateSerialExtraStmts(&cxt, newdef,
3582  get_atttype(relid, attnum),
3583  def->options, true, true,
3584  NULL, NULL);
3585 
3586  newcmds = lappend(newcmds, cmd);
3587  break;
3588  }
3589 
3590  case AT_SetIdentity:
3591  {
3592  /*
3593  * Create an ALTER SEQUENCE statement for the internal
3594  * sequence of the identity column.
3595  */
3596  ListCell *lc;
3597  List *newseqopts = NIL;
3598  List *newdef = NIL;
3600  Oid seq_relid;
3601 
3602  /*
3603  * Split options into those handled by ALTER SEQUENCE and
3604  * those for ALTER TABLE proper.
3605  */
3606  foreach(lc, castNode(List, cmd->def))
3607  {
3608  DefElem *def = lfirst_node(DefElem, lc);
3609 
3610  if (strcmp(def->defname, "generated") == 0)
3611  newdef = lappend(newdef, def);
3612  else
3613  newseqopts = lappend(newseqopts, def);
3614  }
3615 
3616  attnum = get_attnum(relid, cmd->name);
3617  if (attnum == InvalidAttrNumber)
3618  ereport(ERROR,
3619  (errcode(ERRCODE_UNDEFINED_COLUMN),
3620  errmsg("column \"%s\" of relation \"%s\" does not exist",
3621  cmd->name, RelationGetRelationName(rel))));
3622 
3623  seq_relid = getIdentitySequence(rel, attnum, true);
3624 
3625  if (seq_relid)
3626  {
3627  AlterSeqStmt *seqstmt;
3628 
3629  seqstmt = makeNode(AlterSeqStmt);
3631  get_rel_name(seq_relid), -1);
3632  seqstmt->options = newseqopts;
3633  seqstmt->for_identity = true;
3634  seqstmt->missing_ok = false;
3635 
3636  cxt.blist = lappend(cxt.blist, seqstmt);
3637  }
3638 
3639  /*
3640  * If column was not an identity column, we just let the
3641  * ALTER TABLE command error out later. (There are cases
3642  * this fails to cover, but we'll need to restructure
3643  * where creation of the sequence dependency linkage
3644  * happens before we can fix it.)
3645  */
3646 
3647  cmd->def = (Node *) newdef;
3648  newcmds = lappend(newcmds, cmd);
3649  break;
3650  }
3651 
3652  case AT_AttachPartition:
3653  case AT_DetachPartition:
3654  {
3655  PartitionCmd *partcmd = (PartitionCmd *) cmd->def;
3656 
3657  transformPartitionCmd(&cxt, partcmd->bound);
3658  /* assign transformed value of the partition bound */
3659  partcmd->bound = cxt.partbound;
3660  }
3661 
3662  newcmds = lappend(newcmds, cmd);
3663  break;
3664 
3665  case AT_SplitPartition:
3666  case AT_MergePartitions:
3667  {
3668  PartitionCmd *partcmd = (PartitionCmd *) cmd->def;
3669 
3670  if (list_length(partcmd->partlist) < 2)
3671  ereport(ERROR,
3672  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3673  errmsg("list of new partitions should contain at least two items")));
3674 
3675  if (cmd->subtype == AT_SplitPartition)
3676  transformPartitionCmdForSplit(&cxt, partcmd);
3677  else
3678  transformPartitionCmdForMerge(&cxt, partcmd);
3679  newcmds = lappend(newcmds, cmd);
3680  break;
3681  }
3682 
3683  default:
3684 
3685  /*
3686  * Currently, we shouldn't actually get here for subcommand
3687  * types that don't require transformation; but if we do, just
3688  * emit them unchanged.
3689  */
3690  newcmds = lappend(newcmds, cmd);
3691  break;
3692  }
3693  }
3694 
3695  /*
3696  * Transfer anything we already have in cxt.alist into save_alist, to keep
3697  * it separate from the output of transformIndexConstraints.
3698  */
3699  save_alist = cxt.alist;
3700  cxt.alist = NIL;
3701 
3702  /* Postprocess constraints */
3704  transformFKConstraints(&cxt, skipValidation, true);
3705  transformCheckConstraints(&cxt, false);
3706 
3707  /*
3708  * Push any index-creation commands into the ALTER, so that they can be
3709  * scheduled nicely by tablecmds.c. Note that tablecmds.c assumes that
3710  * the IndexStmt attached to an AT_AddIndex or AT_AddIndexConstraint
3711  * subcommand has already been through transformIndexStmt.
3712  */
3713  foreach(l, cxt.alist)
3714  {
3715  Node *istmt = (Node *) lfirst(l);
3716 
3717  /*
3718  * We assume here that cxt.alist contains only IndexStmts and possibly
3719  * ALTER TABLE SET NOT NULL statements generated from primary key
3720  * constraints. We absorb the subcommands of the latter directly.
3721  */
3722  if (IsA(istmt, IndexStmt))
3723  {
3724  IndexStmt *idxstmt = (IndexStmt *) istmt;
3725 
3726  idxstmt = transformIndexStmt(relid, idxstmt, queryString);
3727  newcmd = makeNode(AlterTableCmd);
3729  newcmd->def = (Node *) idxstmt;
3730  newcmds = lappend(newcmds, newcmd);
3731  }
3732  else if (IsA(istmt, AlterTableStmt))
3733  {
3734  AlterTableStmt *alterstmt = (AlterTableStmt *) istmt;
3735 
3736  newcmds = list_concat(newcmds, alterstmt->cmds);
3737  }
3738  else
3739  elog(ERROR, "unexpected stmt type %d", (int) nodeTag(istmt));
3740  }
3741  cxt.alist = NIL;
3742 
3743  /* Append any CHECK or FK constraints to the commands list */
3744  foreach(l, cxt.ckconstraints)
3745  {
3746  newcmd = makeNode(AlterTableCmd);
3747  newcmd->subtype = AT_AddConstraint;
3748  newcmd->def = (Node *) lfirst_node(Constraint, l);
3749  newcmds = lappend(newcmds, newcmd);
3750  }
3751  foreach(l, cxt.fkconstraints)
3752  {
3753  newcmd = makeNode(AlterTableCmd);
3754  newcmd->subtype = AT_AddConstraint;
3755  newcmd->def = (Node *) lfirst_node(Constraint, l);
3756  newcmds = lappend(newcmds, newcmd);
3757  }
3758 
3759  /* Close rel */
3760  relation_close(rel, NoLock);
3761 
3762  /*
3763  * Output results.
3764  */
3765  stmt->cmds = newcmds;
3766 
3767  *beforeStmts = cxt.blist;
3768  *afterStmts = list_concat(cxt.alist, save_alist);
3769 
3770  return stmt;
3771 }
#define InvalidAttrNumber
Definition: attnum.h:23
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
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:564
TypeName * makeTypeNameFromOid(Oid typeOid, int32 typmod)
Definition: makefuncs.c:474
#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:121
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 transformPartitionCmdForSplit(CreateStmtContext *cxt, PartitionCmd *partcmd)
static void transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
static void transformPartitionCmdForMerge(CreateStmtContext *cxt, PartitionCmd *partcmd)
IndexStmt * transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
static void transformPartitionCmd(CreateStmtContext *cxt, PartitionBoundSpec *bound)
static void transformCheckConstraints(CreateStmtContext *cxt, bool skipValidation)
static void transformFKConstraints(CreateStmtContext *cxt, bool skipValidation, bool isAddConstraint)
@ CONSTR_FOREIGN
Definition: parsenodes.h:2714
@ AT_AddIndexConstraint
Definition: parsenodes.h:2375
@ AT_MergePartitions
Definition: parsenodes.h:2417
@ AT_SetIdentity
Definition: parsenodes.h:2419
@ AT_AddIndex
Definition: parsenodes.h:2368
@ AT_AddIdentity
Definition: parsenodes.h:2418
@ AT_AlterColumnType
Definition: parsenodes.h:2378
@ AT_DetachPartition
Definition: parsenodes.h:2414
@ AT_AttachPartition
Definition: parsenodes.h:2413
@ AT_SplitPartition
Definition: parsenodes.h:2416
@ AT_AddColumn
Definition: parsenodes.h:2353
int16 attnum
Definition: pg_attribute.h:74
Oid getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
Definition: pg_depend.c:946
#define lfirst(lc)
Definition: pg_list.h:172
#define lfirst_node(type, lc)
Definition: pg_list.h:176
static int list_length(const List *l)
Definition: pg_list.h:152
#define list_make1(x1)
Definition: pg_list.h:212
#define RelationGetForm(relation)
Definition: rel.h:499
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:205
List * options
Definition: parsenodes.h:3136
RangeVar * sequence
Definition: parsenodes.h:3135
bool for_identity
Definition: parsenodes.h:3137
char identity
Definition: parsenodes.h:737
List * constraints
Definition: parsenodes.h:743
Node * cooked_default
Definition: parsenodes.h:736
char * colname
Definition: parsenodes.h:726
TypeName * typeName
Definition: parsenodes.h:727
Node * raw_default
Definition: parsenodes.h:735
List * options
Definition: parsenodes.h:2757
char generated_when
Definition: parsenodes.h:2747
IndexStmt * pkey
Definition: parse_utilcmd.c:94
const char * stmtType
Definition: parse_utilcmd.c:79
RangeVar * relation
Definition: parse_utilcmd.c:80
ParseState * pstate
Definition: parse_utilcmd.c:78
PartitionBoundSpec * partbound
Definition: parse_utilcmd.c:96
char * defname
Definition: parsenodes.h:815
Oid indexOid
Definition: parsenodes.h:3367
const char * p_sourcetext
Definition: parse_node.h:193
PartitionBoundSpec * bound
Definition: parsenodes.h:958
List * partlist
Definition: parsenodes.h:959

References AccessShareLock, addNSItemToQuery(), addRangeTableEntryForRelation(), CreateStmtContext::alist, AT_AddColumn, AT_AddConstraint, AT_AddIdentity, AT_AddIndex, AT_AddIndexConstraint, AT_AlterColumnType, AT_AttachPartition, AT_DetachPartition, AT_MergePartitions, AT_SetIdentity, AT_SplitPartition, attnum, CreateStmtContext::blist, PartitionCmd::bound, castNode, CreateStmtContext::ckconstraints, AlterTableStmt::cmds, 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, Constraint::generated_when, generateSerialExtraStmts(), get_attnum(), get_atttype(), get_namespace_name(), get_rel_name(), get_rel_namespace(), getIdentitySequence(), ColumnDef::identity, if(), IndexStmt::indexOid, CreateStmtContext::inhRelations, InvalidAttrNumber, IsA, CreateStmtContext::isalter, CreateStmtContext::isforeign, CreateStmtContext::ispartitioned, CreateStmtContext::ixconstraints, lappend(), lfirst, lfirst_node, CreateStmtContext::likeclauses, list_concat(), list_length(), list_make1, make_parsestate(), makeDefElem(), makeNode, makeRangeVar(), makeTypeNameFromOid(), AlterSeqStmt::missing_ok, AlterTableCmd::name, NIL, nodeTag, NoLock, CreateStmtContext::ofType, OidIsValid, Constraint::options, AlterSeqStmt::options, ParseState::p_sourcetext, CreateStmtContext::partbound, PartitionCmd::partlist, 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(), transformPartitionCmdForMerge(), transformPartitionCmdForSplit(), transformTableConstraint(), TupleDescAttr, ColumnDef::typeName, and typenameTypeId().

Referenced by ATParseTransformCmd(), and ATPostAlterTypeParse().

◆ transformCreateSchemaStmtElements()

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

Definition at line 3956 of file parse_utilcmd.c.

3957 {
3959  List *result;
3960  ListCell *elements;
3961 
3962  cxt.schemaname = schemaName;
3963  cxt.sequences = NIL;
3964  cxt.tables = NIL;
3965  cxt.views = NIL;
3966  cxt.indexes = NIL;
3967  cxt.triggers = NIL;
3968  cxt.grants = NIL;
3969 
3970  /*
3971  * Run through each schema element in the schema element list. Separate
3972  * statements by type, and do preliminary analysis.
3973  */
3974  foreach(elements, schemaElts)
3975  {
3976  Node *element = lfirst(elements);
3977 
3978  switch (nodeTag(element))
3979  {
3980  case T_CreateSeqStmt:
3981  {
3982  CreateSeqStmt *elp = (CreateSeqStmt *) element;
3983 
3985  cxt.sequences = lappend(cxt.sequences, element);
3986  }
3987  break;
3988 
3989  case T_CreateStmt:
3990  {
3991  CreateStmt *elp = (CreateStmt *) element;
3992 
3994 
3995  /*
3996  * XXX todo: deal with constraints
3997  */
3998  cxt.tables = lappend(cxt.tables, element);
3999  }
4000  break;
4001 
4002  case T_ViewStmt:
4003  {
4004  ViewStmt *elp = (ViewStmt *) element;
4005 
4006  setSchemaName(cxt.schemaname, &elp->view->schemaname);
4007 
4008  /*
4009  * XXX todo: deal with references between views
4010  */
4011  cxt.views = lappend(cxt.views, element);
4012  }
4013  break;
4014 
4015  case T_IndexStmt:
4016  {
4017  IndexStmt *elp = (IndexStmt *) element;
4018 
4020  cxt.indexes = lappend(cxt.indexes, element);
4021  }
4022  break;
4023 
4024  case T_CreateTrigStmt:
4025  {
4027 
4029  cxt.triggers = lappend(cxt.triggers, element);
4030  }
4031  break;
4032 
4033  case T_GrantStmt:
4034  cxt.grants = lappend(cxt.grants, element);
4035  break;
4036 
4037  default:
4038  elog(ERROR, "unrecognized node type: %d",
4039  (int) nodeTag(element));
4040  }
4041  }
4042 
4043  result = NIL;
4044  result = list_concat(result, cxt.sequences);
4045  result = list_concat(result, cxt.tables);
4046  result = list_concat(result, cxt.views);
4047  result = list_concat(result, cxt.indexes);
4048  result = list_concat(result, cxt.triggers);
4049  result = list_concat(result, cxt.grants);
4050 
4051  return result;
4052 }
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:3125
RangeVar * relation
Definition: parsenodes.h:2656
RangeVar * relation
Definition: parsenodes.h:3012
RangeVar * relation
Definition: parsenodes.h:3357
RangeVar * view
Definition: parsenodes.h:3748

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 166 of file parse_utilcmd.c.

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

References CreateStmtContext::alist, Assert, CreateStmtContext::blist, cancel_parser_errposition_callback(), checkMembershipInCurrentExtension(), CreateStmtContext::ckconstraints, CreateStmtContext::columns, element(), elog, ereport, errcode(), errmsg(), ERROR, CreateStmtContext::fkconstraints, get_namespace_name(), CreateStmtContext::inhRelations, IsA, CreateStmtContext::isalter, CreateStmtContext::isforeign, CreateStmtContext::ispartitioned, CreateStmtContext::ixconstraints, lappend(), lfirst, CreateStmtContext::likeclauses, list_concat(), make_parsestate(), NIL, 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, transformCheckConstraints(), transformColumnDefinition(), transformFKConstraints(), transformIndexConstraints(), transformOfType(), transformTableConstraint(), and transformTableLikeClause().

Referenced by ProcessUtilitySlow().

◆ transformIndexStmt()

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

Definition at line 2772 of file parse_utilcmd.c.

2773 {
2774  ParseState *pstate;
2775  ParseNamespaceItem *nsitem;
2776  ListCell *l;
2777  Relation rel;
2778 
2779  /* Nothing to do if statement already transformed. */
2780  if (stmt->transformed)
2781  return stmt;
2782 
2783  /* Set up pstate */
2784  pstate = make_parsestate(NULL);
2785  pstate->p_sourcetext = queryString;
2786 
2787  /*
2788  * Put the parent table into the rtable so that the expressions can refer
2789  * to its fields without qualification. Caller is responsible for locking
2790  * relation, but we still need to open it.
2791  */
2792  rel = relation_open(relid, NoLock);
2793  nsitem = addRangeTableEntryForRelation(pstate, rel,
2795  NULL, false, true);
2796 
2797  /* no to join list, yes to namespaces */
2798  addNSItemToQuery(pstate, nsitem, false, true, true);
2799 
2800  /* take care of the where clause */
2801  if (stmt->whereClause)
2802  {
2803  stmt->whereClause = transformWhereClause(pstate,
2804  stmt->whereClause,
2806  "WHERE");
2807  /* we have to fix its collations too */
2808  assign_expr_collations(pstate, stmt->whereClause);
2809  }
2810 
2811  /* take care of any index expressions */
2812  foreach(l, stmt->indexParams)
2813  {
2814  IndexElem *ielem = (IndexElem *) lfirst(l);
2815 
2816  if (ielem->expr)
2817  {
2818  /* Extract preliminary index col name before transforming expr */
2819  if (ielem->indexcolname == NULL)
2820  ielem->indexcolname = FigureIndexColname(ielem->expr);
2821 
2822  /* Now do parse transformation of the expression */
2823  ielem->expr = transformExpr(pstate, ielem->expr,
2825 
2826  /* We have to fix its collations too */
2827  assign_expr_collations(pstate, ielem->expr);
2828 
2829  /*
2830  * transformExpr() should have already rejected subqueries,
2831  * aggregates, window functions, and SRFs, based on the EXPR_KIND_
2832  * for an index expression.
2833  *
2834  * DefineIndex() will make more checks.
2835  */
2836  }
2837  }
2838 
2839  /*
2840  * Check that only the base rel is mentioned. (This should be dead code
2841  * now that add_missing_from is history.)
2842  */
2843  if (list_length(pstate->p_rtable) != 1)
2844  ereport(ERROR,
2845  (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
2846  errmsg("index expressions and predicates can refer only to the table being indexed")));
2847 
2848  free_parsestate(pstate);
2849 
2850  /* Close relation */
2851  table_close(rel, NoLock);
2852 
2853  /* Mark statement as successfully transformed */
2854  stmt->transformed = true;
2855 
2856  return stmt;
2857 }
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)
Node * expr
Definition: parsenodes.h:784
char * indexcolname
Definition: parsenodes.h:785
List * p_rtable
Definition: parse_node.h:194

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 4132 of file parse_utilcmd.c.

4134 {
4135  PartitionBoundSpec *result_spec;
4137  char strategy = get_partition_strategy(key);
4138  int partnatts = get_partition_natts(key);
4139  List *partexprs = get_partition_exprs(key);
4140 
4141  /* Avoid scribbling on input */
4142  result_spec = copyObject(spec);
4143 
4144  if (spec->is_default)
4145  {
4146  /*
4147  * Hash partitioning does not support a default partition; there's no
4148  * use case for it (since the set of partitions to create is perfectly
4149  * defined), and if users do get into it accidentally, it's hard to
4150  * back out from it afterwards.
4151  */
4152  if (strategy == PARTITION_STRATEGY_HASH)
4153  ereport(ERROR,
4154  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4155  errmsg("a hash-partitioned table may not have a default partition")));
4156 
4157  /*
4158  * In case of the default partition, parser had no way to identify the
4159  * partition strategy. Assign the parent's strategy to the default
4160  * partition bound spec.
4161  */
4162  result_spec->strategy = strategy;
4163 
4164  return result_spec;
4165  }
4166 
4167  if (strategy == PARTITION_STRATEGY_HASH)
4168  {
4169  if (spec->strategy != PARTITION_STRATEGY_HASH)
4170  ereport(ERROR,
4171  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4172  errmsg("invalid bound specification for a hash partition"),
4173  parser_errposition(pstate, exprLocation((Node *) spec))));
4174 
4175  if (spec->modulus <= 0)
4176  ereport(ERROR,
4177  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4178  errmsg("modulus for hash partition must be an integer value greater than zero")));
4179 
4180  Assert(spec->remainder >= 0);
4181 
4182  if (spec->remainder >= spec->modulus)
4183  ereport(ERROR,
4184  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4185  errmsg("remainder for hash partition must be less than modulus")));
4186  }
4187  else if (strategy == PARTITION_STRATEGY_LIST)
4188  {
4189  ListCell *cell;
4190  char *colname;
4191  Oid coltype;
4192  int32 coltypmod;
4193  Oid partcollation;
4194 
4195  if (spec->strategy != PARTITION_STRATEGY_LIST)
4196  ereport(ERROR,
4197  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4198  errmsg("invalid bound specification for a list partition"),
4199  parser_errposition(pstate, exprLocation((Node *) spec))));
4200 
4201  /* Get the only column's name in case we need to output an error */
4202  if (key->partattrs[0] != 0)
4203  colname = get_attname(RelationGetRelid(parent),
4204  key->partattrs[0], false);
4205  else
4206  colname = deparse_expression((Node *) linitial(partexprs),
4208  RelationGetRelid(parent)),
4209  false, false);
4210  /* Need its type data too */
4211  coltype = get_partition_col_typid(key, 0);
4212  coltypmod = get_partition_col_typmod(key, 0);
4213  partcollation = get_partition_col_collation(key, 0);
4214 
4215  result_spec->listdatums = NIL;
4216  foreach(cell, spec->listdatums)
4217  {
4218  Node *expr = lfirst(cell);
4219  Const *value;
4220  ListCell *cell2;
4221  bool duplicate;
4222 
4223  value = transformPartitionBoundValue(pstate, expr,
4224  colname, coltype, coltypmod,
4225  partcollation);
4226 
4227  /* Don't add to the result if the value is a duplicate */
4228  duplicate = false;
4229  foreach(cell2, result_spec->listdatums)
4230  {
4231  Const *value2 = lfirst_node(Const, cell2);
4232 
4233  if (equal(value, value2))
4234  {
4235  duplicate = true;
4236  break;
4237  }
4238  }
4239  if (duplicate)
4240  continue;
4241 
4242  result_spec->listdatums = lappend(result_spec->listdatums,
4243  value);
4244  }
4245  }
4246  else if (strategy == PARTITION_STRATEGY_RANGE)
4247  {
4248  if (spec->strategy != PARTITION_STRATEGY_RANGE)
4249  ereport(ERROR,
4250  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4251  errmsg("invalid bound specification for a range partition"),
4252  parser_errposition(pstate, exprLocation((Node *) spec))));
4253 
4254  if (list_length(spec->lowerdatums) != partnatts)
4255  ereport(ERROR,
4256  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4257  errmsg("FROM must specify exactly one value per partitioning column")));
4258  if (list_length(spec->upperdatums) != partnatts)
4259  ereport(ERROR,
4260  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4261  errmsg("TO must specify exactly one value per partitioning column")));
4262 
4263  /*
4264  * Convert raw parse nodes into PartitionRangeDatum nodes and perform
4265  * any necessary validation.
4266  */
4267  result_spec->lowerdatums =
4269  parent);
4270  result_spec->upperdatums =
4272  parent);
4273  }
4274  else
4275  elog(ERROR, "unexpected partition strategy: %d", (int) strategy);
4276 
4277  return result_spec;
4278 }
signed int int32
Definition: c.h:494
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
static struct @155 value
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:827
int exprLocation(const Node *expr)
Definition: nodeFuncs.c:1386
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:874
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:872
@ PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:873
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
#define linitial(l)
Definition: pg_list.h:178
char * deparse_expression(Node *expr, List *dpcontext, bool forceprefix, bool showimplicit)
Definition: ruleutils.c:3598
List * deparse_context_for(const char *aliasname, Oid relid)
Definition: ruleutils.c:3658

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 2942 of file parse_utilcmd.c.

2944 {
2945  Relation rel;
2946  ParseState *pstate;
2947  ParseNamespaceItem *oldnsitem;
2948  ParseNamespaceItem *newnsitem;
2949 
2950  /*
2951  * To avoid deadlock, make sure the first thing we do is grab
2952  * AccessExclusiveLock on the target relation. This will be needed by
2953  * DefineQueryRewrite(), and we don't want to grab a lesser lock
2954  * beforehand.
2955  */
2956  rel = table_openrv(stmt->relation, AccessExclusiveLock);
2957 
2958  if (rel->rd_rel->relkind == RELKIND_MATVIEW)
2959  ereport(ERROR,
2960  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2961  errmsg("rules on materialized views are not supported")));
2962 
2963  /* Set up pstate */
2964  pstate = make_parsestate(NULL);
2965  pstate->p_sourcetext = queryString;
2966 
2967  /*
2968  * NOTE: 'OLD' must always have a varno equal to 1 and 'NEW' equal to 2.
2969  * Set up their ParseNamespaceItems in the main pstate for use in parsing
2970  * the rule qualification.
2971  */
2972  oldnsitem = addRangeTableEntryForRelation(pstate, rel,
2974  makeAlias("old", NIL),
2975  false, false);
2976  newnsitem = addRangeTableEntryForRelation(pstate, rel,
2978  makeAlias("new", NIL),
2979  false, false);
2980 
2981  /*
2982  * They must be in the namespace too for lookup purposes, but only add the
2983  * one(s) that are relevant for the current kind of rule. In an UPDATE
2984  * rule, quals must refer to OLD.field or NEW.field to be unambiguous, but
2985  * there's no need to be so picky for INSERT & DELETE. We do not add them
2986  * to the joinlist.
2987  */
2988  switch (stmt->event)
2989  {
2990  case CMD_SELECT:
2991  addNSItemToQuery(pstate, oldnsitem, false, true, true);
2992  break;
2993  case CMD_UPDATE:
2994  addNSItemToQuery(pstate, oldnsitem, false, true, true);
2995  addNSItemToQuery(pstate, newnsitem, false, true, true);
2996  break;
2997  case CMD_INSERT:
2998  addNSItemToQuery(pstate, newnsitem, false, true, true);
2999  break;
3000  case CMD_DELETE:
3001  addNSItemToQuery(pstate, oldnsitem, false, true, true);
3002  break;
3003  default:
3004  elog(ERROR, "unrecognized event type: %d",
3005  (int) stmt->event);
3006  break;
3007  }
3008 
3009  /* take care of the where clause */
3010  *whereClause = transformWhereClause(pstate,
3011  stmt->whereClause,
3013  "WHERE");
3014  /* we have to fix its collations too */
3015  assign_expr_collations(pstate, *whereClause);
3016 
3017  /* this is probably dead code without add_missing_from: */
3018  if (list_length(pstate->p_rtable) != 2) /* naughty, naughty... */
3019  ereport(ERROR,
3020  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3021  errmsg("rule WHERE condition cannot contain references to other relations")));
3022 
3023  /*
3024  * 'instead nothing' rules with a qualification need a query rangetable so
3025  * the rewrite handler can add the negated rule qualification to the
3026  * original query. We create a query with the new command type CMD_NOTHING
3027  * here that is treated specially by the rewrite system.
3028  */
3029  if (stmt->actions == NIL)
3030  {
3031  Query *nothing_qry = makeNode(Query);
3032 
3033  nothing_qry->commandType = CMD_NOTHING;
3034  nothing_qry->rtable = pstate->p_rtable;
3035  nothing_qry->rteperminfos = pstate->p_rteperminfos;
3036  nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */
3037 
3038  *actions = list_make1(nothing_qry);
3039  }
3040  else
3041  {
3042  ListCell *l;
3043  List *newactions = NIL;
3044 
3045  /*
3046  * transform each statement, like parse_sub_analyze()
3047  */
3048  foreach(l, stmt->actions)
3049  {
3050  Node *action = (Node *) lfirst(l);
3051  ParseState *sub_pstate = make_parsestate(NULL);
3052  Query *sub_qry,
3053  *top_subqry;
3054  bool has_old,
3055  has_new;
3056 
3057  /*
3058  * Since outer ParseState isn't parent of inner, have to pass down
3059  * the query text by hand.
3060  */
3061  sub_pstate->p_sourcetext = queryString;
3062 
3063  /*
3064  * Set up OLD/NEW in the rtable for this statement. The entries
3065  * are added only to relnamespace, not varnamespace, because we
3066  * don't want them to be referred to by unqualified field names
3067  * nor "*" in the rule actions. We decide later whether to put
3068  * them in the joinlist.
3069  */
3070  oldnsitem = addRangeTableEntryForRelation(sub_pstate, rel,
3072  makeAlias("old", NIL),
3073  false, false);
3074  newnsitem = addRangeTableEntryForRelation(sub_pstate, rel,
3076  makeAlias("new", NIL),
3077  false, false);
3078  addNSItemToQuery(sub_pstate, oldnsitem, false, true, false);
3079  addNSItemToQuery(sub_pstate, newnsitem, false, true, false);
3080 
3081  /* Transform the rule action statement */
3082  top_subqry = transformStmt(sub_pstate, action);
3083 
3084  /*
3085  * We cannot support utility-statement actions (eg NOTIFY) with
3086  * nonempty rule WHERE conditions, because there's no way to make
3087  * the utility action execute conditionally.
3088  */
3089  if (top_subqry->commandType == CMD_UTILITY &&
3090  *whereClause != NULL)
3091  ereport(ERROR,
3092  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3093  errmsg("rules with WHERE conditions can only have SELECT, INSERT, UPDATE, or DELETE actions")));
3094 
3095  /*
3096  * If the action is INSERT...SELECT, OLD/NEW have been pushed down
3097  * into the SELECT, and that's what we need to look at. (Ugly
3098  * kluge ... try to fix this when we redesign querytrees.)
3099  */
3100  sub_qry = getInsertSelectQuery(top_subqry, NULL);
3101 
3102  /*
3103  * If the sub_qry is a setop, we cannot attach any qualifications
3104  * to it, because the planner won't notice them. This could
3105  * perhaps be relaxed someday, but for now, we may as well reject
3106  * such a rule immediately.
3107  */
3108  if (sub_qry->setOperations != NULL && *whereClause != NULL)
3109  ereport(ERROR,
3110  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3111  errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
3112 
3113  /*
3114  * Validate action's use of OLD/NEW, qual too
3115  */
3116  has_old =
3117  rangeTableEntry_used((Node *) sub_qry, PRS2_OLD_VARNO, 0) ||
3118  rangeTableEntry_used(*whereClause, PRS2_OLD_VARNO, 0);
3119  has_new =
3120  rangeTableEntry_used((Node *) sub_qry, PRS2_NEW_VARNO, 0) ||
3121  rangeTableEntry_used(*whereClause, PRS2_NEW_VARNO, 0);
3122 
3123  switch (stmt->event)
3124  {
3125  case CMD_SELECT:
3126  if (has_old)
3127  ereport(ERROR,
3128  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3129  errmsg("ON SELECT rule cannot use OLD")));
3130  if (has_new)
3131  ereport(ERROR,
3132  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3133  errmsg("ON SELECT rule cannot use NEW")));
3134  break;
3135  case CMD_UPDATE:
3136  /* both are OK */
3137  break;
3138  case CMD_INSERT:
3139  if (has_old)
3140  ereport(ERROR,
3141  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3142  errmsg("ON INSERT rule cannot use OLD")));
3143  break;
3144  case CMD_DELETE:
3145  if (has_new)
3146  ereport(ERROR,
3147  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3148  errmsg("ON DELETE rule cannot use NEW")));
3149  break;
3150  default:
3151  elog(ERROR, "unrecognized event type: %d",
3152  (int) stmt->event);
3153  break;
3154  }
3155 
3156  /*
3157  * OLD/NEW are not allowed in WITH queries, because they would
3158  * amount to outer references for the WITH, which we disallow.
3159  * However, they were already in the outer rangetable when we
3160  * analyzed the query, so we have to check.
3161  *
3162  * Note that in the INSERT...SELECT case, we need to examine the
3163  * CTE lists of both top_subqry and sub_qry.
3164  *
3165  * Note that we aren't digging into the body of the query looking
3166  * for WITHs in nested sub-SELECTs. A WITH down there can
3167  * legitimately refer to OLD/NEW, because it'd be an
3168  * indirect-correlated outer reference.
3169  */
3170  if (rangeTableEntry_used((Node *) top_subqry->cteList,
3171  PRS2_OLD_VARNO, 0) ||
3172  rangeTableEntry_used((Node *) sub_qry->cteList,
3173  PRS2_OLD_VARNO, 0))
3174  ereport(ERROR,
3175  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3176  errmsg("cannot refer to OLD within WITH query")));
3177  if (rangeTableEntry_used((Node *) top_subqry->cteList,
3178  PRS2_NEW_VARNO, 0) ||
3179  rangeTableEntry_used((Node *) sub_qry->cteList,
3180  PRS2_NEW_VARNO, 0))
3181  ereport(ERROR,
3182  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3183  errmsg("cannot refer to NEW within WITH query")));
3184 
3185  /*
3186  * For efficiency's sake, add OLD to the rule action's jointree
3187  * only if it was actually referenced in the statement or qual.
3188  *
3189  * For INSERT, NEW is not really a relation (only a reference to
3190  * the to-be-inserted tuple) and should never be added to the
3191  * jointree.
3192  *
3193  * For UPDATE, we treat NEW as being another kind of reference to
3194  * OLD, because it represents references to *transformed* tuples
3195  * of the existing relation. It would be wrong to enter NEW
3196  * separately in the jointree, since that would cause a double
3197  * join of the updated relation. It's also wrong to fail to make
3198  * a jointree entry if only NEW and not OLD is mentioned.
3199  */
3200  if (has_old || (has_new && stmt->event == CMD_UPDATE))
3201  {
3202  RangeTblRef *rtr;
3203 
3204  /*
3205  * If sub_qry is a setop, manipulating its jointree will do no
3206  * good at all, because the jointree is dummy. (This should be
3207  * a can't-happen case because of prior tests.)
3208  */
3209  if (sub_qry->setOperations != NULL)
3210  ereport(ERROR,
3211  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3212  errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
3213  /* hackishly add OLD to the already-built FROM clause */
3214  rtr = makeNode(RangeTblRef);
3215  rtr->rtindex = oldnsitem->p_rtindex;
3216  sub_qry->jointree->fromlist =
3217  lappend(sub_qry->jointree->fromlist, rtr);
3218  }
3219 
3220  newactions = lappend(newactions, top_subqry);
3221 
3222  free_parsestate(sub_pstate);
3223  }
3224 
3225  *actions = newactions;
3226  }
3227 
3228  free_parsestate(pstate);
3229 
3230  /* Close relation, but keep the exclusive lock */
3231  table_close(rel, NoLock);
3232 }
#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:311
#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:2310
List * p_rteperminfos
Definition: parse_node.h:195
FromExpr * jointree
Definition: parsenodes.h:175
Node * setOperations
Definition: parsenodes.h:219
List * cteList
Definition: parsenodes.h:166
List * rtable
Definition: parsenodes.h:168
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 2867 of file parse_utilcmd.c.

2868 {
2869  ParseState *pstate;
2870  ParseNamespaceItem *nsitem;
2871  ListCell *l;
2872  Relation rel;
2873 
2874  /* Nothing to do if statement already transformed. */
2875  if (stmt->transformed)
2876  return stmt;
2877 
2878  /* Set up pstate */
2879  pstate = make_parsestate(NULL);
2880  pstate->p_sourcetext = queryString;
2881 
2882  /*
2883  * Put the parent table into the rtable so that the expressions can refer
2884  * to its fields without qualification. Caller is responsible for locking
2885  * relation, but we still need to open it.
2886  */
2887  rel = relation_open(relid, NoLock);
2888  nsitem = addRangeTableEntryForRelation(pstate, rel,
2890  NULL, false, true);
2891 
2892  /* no to join list, yes to namespaces */
2893  addNSItemToQuery(pstate, nsitem, false, true, true);
2894 
2895  /* take care of any expressions */
2896  foreach(l, stmt->exprs)
2897  {
2898  StatsElem *selem = (StatsElem *) lfirst(l);
2899 
2900  if (selem->expr)
2901  {
2902  /* Now do parse transformation of the expression */
2903  selem->expr = transformExpr(pstate, selem->expr,
2905 
2906  /* We have to fix its collations too */
2907  assign_expr_collations(pstate, selem->expr);
2908  }
2909  }
2910 
2911  /*
2912  * Check that only the base rel is mentioned. (This should be dead code
2913  * now that add_missing_from is history.)
2914  */
2915  if (list_length(pstate->p_rtable) != 1)
2916  ereport(ERROR,
2917  (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
2918  errmsg("statistics expressions can refer only to the table being referenced")));
2919 
2920  free_parsestate(pstate);
2921 
2922  /* Close relation */
2923  table_close(rel, NoLock);
2924 
2925  /* Mark statement as successfully transformed */
2926  stmt->transformed = true;
2927 
2928  return stmt;
2929 }
@ EXPR_KIND_STATS_EXPRESSION
Definition: parse_node.h:74
Node * expr
Definition: parsenodes.h:3412

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().