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)
 
void transformRuleStmt (RuleStmt *stmt, const char *queryString, List **actions, Node **whereClause)
 
ListtransformCreateSchemaStmt (CreateSchemaStmt *stmt)
 
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 1176 of file parse_utilcmd.c.

References AccessShareLock, Assert, AT_AddConstraint, AT_CookedColumnDefault, AttrMap::attnums, build_attrmap_by_name(), ConstrCheck::ccbin, ConstrCheck::ccname, ConstrCheck::ccnoinherit, TupleConstr::check, AlterTableStmt::cmds, CommentStmt::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, AlterTableCmd::def, TupleConstr::defval, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, generateClonedIndexStmt(), get_relation_constraint_oid(), GetComment(), i, IndexStmt::idxcomment, index_close(), index_open(), Constraint::initially_valid, InvalidOid, Constraint::is_no_inherit, lappend(), lcons(), lfirst_oid, list_make3, Constraint::location, makeNode, makeString(), map_variable_attnos(), AlterTableStmt::missing_ok, NameStr, TupleDescData::natts, NIL, nodeToString(), NoLock, AlterTableCmd::num, TupleConstr::num_check, TupleConstr::num_defval, CommentStmt::object, OBJECT_TABCONSTRAINT, OBJECT_TABLE, AlterTableStmt::objtype, CommentStmt::objtype, OidIsValid, TableLikeClause::options, pstrdup(), Constraint::raw_expr, RelationData::rd_rel, AlterTableStmt::relation, relation_open(), relation_openrv(), RelationGetDescr, RelationGetIndexList(), RelationGetRelationName, RelationGetRelid, TableLikeClause::relationOid, RangeVar::relname, RangeVar::schemaname, Constraint::skip_validation, stringToNode(), AlterTableCmd::subtype, table_close(), and TupleDescAttr.

Referenced by ProcessUtilitySlow().

1177 {
1178  List *result = NIL;
1179  List *atsubcmds = NIL;
1180  AttrNumber parent_attno;
1181  Relation relation;
1182  Relation childrel;
1183  TupleDesc tupleDesc;
1184  TupleConstr *constr;
1185  AttrMap *attmap;
1186  char *comment;
1187 
1188  /*
1189  * Open the relation referenced by the LIKE clause. We should still have
1190  * the table lock obtained by transformTableLikeClause (and this'll throw
1191  * an assertion failure if not). Hence, no need to recheck privileges
1192  * etc. We must open the rel by OID not name, to be sure we get the same
1193  * table.
1194  */
1195  if (!OidIsValid(table_like_clause->relationOid))
1196  elog(ERROR, "expandTableLikeClause called on untransformed LIKE clause");
1197 
1198  relation = relation_open(table_like_clause->relationOid, NoLock);
1199 
1200  tupleDesc = RelationGetDescr(relation);
1201  constr = tupleDesc->constr;
1202 
1203  /*
1204  * Open the newly-created child relation; we have lock on that too.
1205  */
1206  childrel = relation_openrv(heapRel, NoLock);
1207 
1208  /*
1209  * Construct a map from the LIKE relation's attnos to the child rel's.
1210  * This re-checks type match etc, although it shouldn't be possible to
1211  * have a failure since both tables are locked.
1212  */
1213  attmap = build_attrmap_by_name(RelationGetDescr(childrel),
1214  tupleDesc);
1215 
1216  /*
1217  * Process defaults, if required.
1218  */
1219  if ((table_like_clause->options &
1221  constr != NULL)
1222  {
1223  AttrDefault *attrdef = constr->defval;
1224 
1225  for (parent_attno = 1; parent_attno <= tupleDesc->natts;
1226  parent_attno++)
1227  {
1228  Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
1229  parent_attno - 1);
1230 
1231  /*
1232  * Ignore dropped columns in the parent.
1233  */
1234  if (attribute->attisdropped)
1235  continue;
1236 
1237  /*
1238  * Copy default, if present and it should be copied. We have
1239  * separate options for plain default expressions and GENERATED
1240  * defaults.
1241  */
1242  if (attribute->atthasdef &&
1243  (attribute->attgenerated ?
1244  (table_like_clause->options & CREATE_TABLE_LIKE_GENERATED) :
1245  (table_like_clause->options & CREATE_TABLE_LIKE_DEFAULTS)))
1246  {
1247  Node *this_default = NULL;
1248  AlterTableCmd *atsubcmd;
1249  bool found_whole_row;
1250 
1251  /* Find default in constraint structure */
1252  for (int i = 0; i < constr->num_defval; i++)
1253  {
1254  if (attrdef[i].adnum == parent_attno)
1255  {
1256  this_default = stringToNode(attrdef[i].adbin);
1257  break;
1258  }
1259  }
1260  Assert(this_default != NULL);
1261 
1262  atsubcmd = makeNode(AlterTableCmd);
1263  atsubcmd->subtype = AT_CookedColumnDefault;
1264  atsubcmd->num = attmap->attnums[parent_attno - 1];
1265  atsubcmd->def = map_variable_attnos(this_default,
1266  1, 0,
1267  attmap,
1268  InvalidOid,
1269  &found_whole_row);
1270 
1271  /*
1272  * Prevent this for the same reason as for constraints below.
1273  * Note that defaults cannot contain any vars, so it's OK that
1274  * the error message refers to generated columns.
1275  */
1276  if (found_whole_row)
1277  ereport(ERROR,
1278  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1279  errmsg("cannot convert whole-row table reference"),
1280  errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
1281  NameStr(attribute->attname),
1282  RelationGetRelationName(relation))));
1283 
1284  atsubcmds = lappend(atsubcmds, atsubcmd);
1285  }
1286  }
1287  }
1288 
1289  /*
1290  * Copy CHECK constraints if requested, being careful to adjust attribute
1291  * numbers so they match the child.
1292  */
1293  if ((table_like_clause->options & CREATE_TABLE_LIKE_CONSTRAINTS) &&
1294  constr != NULL)
1295  {
1296  int ccnum;
1297 
1298  for (ccnum = 0; ccnum < constr->num_check; ccnum++)
1299  {
1300  char *ccname = constr->check[ccnum].ccname;
1301  char *ccbin = constr->check[ccnum].ccbin;
1302  bool ccnoinherit = constr->check[ccnum].ccnoinherit;
1303  Node *ccbin_node;
1304  bool found_whole_row;
1305  Constraint *n;
1306  AlterTableCmd *atsubcmd;
1307 
1308  ccbin_node = map_variable_attnos(stringToNode(ccbin),
1309  1, 0,
1310  attmap,
1311  InvalidOid, &found_whole_row);
1312 
1313  /*
1314  * We reject whole-row variables because the whole point of LIKE
1315  * is that the new table's rowtype might later diverge from the
1316  * parent's. So, while translation might be possible right now,
1317  * it wouldn't be possible to guarantee it would work in future.
1318  */
1319  if (found_whole_row)
1320  ereport(ERROR,
1321  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1322  errmsg("cannot convert whole-row table reference"),
1323  errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
1324  ccname,
1325  RelationGetRelationName(relation))));
1326 
1327  n = makeNode(Constraint);
1328  n->contype = CONSTR_CHECK;
1329  n->conname = pstrdup(ccname);
1330  n->location = -1;
1331  n->is_no_inherit = ccnoinherit;
1332  n->raw_expr = NULL;
1333  n->cooked_expr = nodeToString(ccbin_node);
1334 
1335  /* We can skip validation, since the new table should be empty. */
1336  n->skip_validation = true;
1337  n->initially_valid = true;
1338 
1339  atsubcmd = makeNode(AlterTableCmd);
1340  atsubcmd->subtype = AT_AddConstraint;
1341  atsubcmd->def = (Node *) n;
1342  atsubcmds = lappend(atsubcmds, atsubcmd);
1343 
1344  /* Copy comment on constraint */
1345  if ((table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS) &&
1347  n->conname, false),
1348  ConstraintRelationId,
1349  0)) != NULL)
1350  {
1351  CommentStmt *stmt = makeNode(CommentStmt);
1352 
1353  stmt->objtype = OBJECT_TABCONSTRAINT;
1354  stmt->object = (Node *) list_make3(makeString(heapRel->schemaname),
1355  makeString(heapRel->relname),
1356  makeString(n->conname));
1357  stmt->comment = comment;
1358 
1359  result = lappend(result, stmt);
1360  }
1361  }
1362  }
1363 
1364  /*
1365  * If we generated any ALTER TABLE actions above, wrap them into a single
1366  * ALTER TABLE command. Stick it at the front of the result, so it runs
1367  * before any CommentStmts we made above.
1368  */
1369  if (atsubcmds)
1370  {
1372 
1373  atcmd->relation = copyObject(heapRel);
1374  atcmd->cmds = atsubcmds;
1375  atcmd->objtype = OBJECT_TABLE;
1376  atcmd->missing_ok = false;
1377  result = lcons(atcmd, result);
1378  }
1379 
1380  /*
1381  * Process indexes if required.
1382  */
1383  if ((table_like_clause->options & CREATE_TABLE_LIKE_INDEXES) &&
1384  relation->rd_rel->relhasindex)
1385  {
1386  List *parent_indexes;
1387  ListCell *l;
1388 
1389  parent_indexes = RelationGetIndexList(relation);
1390 
1391  foreach(l, parent_indexes)
1392  {
1393  Oid parent_index_oid = lfirst_oid(l);
1394  Relation parent_index;
1395  IndexStmt *index_stmt;
1396 
1397  parent_index = index_open(parent_index_oid, AccessShareLock);
1398 
1399  /* Build CREATE INDEX statement to recreate the parent_index */
1400  index_stmt = generateClonedIndexStmt(heapRel,
1401  parent_index,
1402  attmap,
1403  NULL);
1404 
1405  /* Copy comment on index, if requested */
1406  if (table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS)
1407  {
1408  comment = GetComment(parent_index_oid, RelationRelationId, 0);
1409 
1410  /*
1411  * We make use of IndexStmt's idxcomment option, so as not to
1412  * need to know now what name the index will have.
1413  */
1414  index_stmt->idxcomment = comment;
1415  }
1416 
1417  result = lappend(result, index_stmt);
1418 
1419  index_close(parent_index, AccessShareLock);
1420  }
1421  }
1422 
1423  /* Done with child rel */
1424  table_close(childrel, NoLock);
1425 
1426  /*
1427  * Close the parent rel, but keep our AccessShareLock on it until xact
1428  * commit. That will prevent someone else from deleting or ALTERing the
1429  * parent before the child is committed.
1430  */
1431  table_close(relation, NoLock);
1432 
1433  return result;
1434 }
ObjectType objtype
Definition: parsenodes.h:2697
Value * makeString(char *str)
Definition: value.c:53
#define list_make3(x1, x2, x3)
Definition: pg_list.h:210
#define NIL
Definition: pg_list.h:65
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
#define RelationGetDescr(relation)
Definition: rel.h:483
Oid get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok)
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
char * pstrdup(const char *in)
Definition: mcxt.c:1187
Node * raw_expr
Definition: parsenodes.h:2178
#define AccessShareLock
Definition: lockdefs.h:36
Definition: nodes.h:528
int errcode(int sqlerrcode)
Definition: elog.c:704
void * stringToNode(const char *str)
Definition: read.c:89
AlterTableType subtype
Definition: parsenodes.h:1889
char * comment
Definition: parsenodes.h:2699
char * conname
Definition: parsenodes.h:2171
Node * object
Definition: parsenodes.h:2698
Node * map_variable_attnos(Node *node, int target_varno, int sublevels_up, const AttrMap *attno_map, Oid to_rowtype, bool *found_whole_row)
Form_pg_class rd_rel
Definition: rel.h:110
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:698
Definition: attmap.h:34
char * schemaname
Definition: primnodes.h:67
AttrDefault * defval
Definition: tupdesc.h:39
char * relname
Definition: primnodes.h:68
#define ERROR
Definition: elog.h:45
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:48
#define NoLock
Definition: lockdefs.h:34
TupleConstr * constr
Definition: tupdesc.h:85
int errdetail(const char *fmt,...)
Definition: elog.c:1048
char * ccname
Definition: tupdesc.h:30
#define RelationGetRelationName(relation)
Definition: rel.h:491
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:193
char * GetComment(Oid oid, Oid classoid, int32 subid)
Definition: comment.c:409
ObjectType objtype
Definition: parsenodes.h:1803
List * lappend(List *list, void *datum)
Definition: list.c:336
Relation relation_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition: relation.c:138
uint16 num_check
Definition: tupdesc.h:43
#define InvalidOid
Definition: postgres_ext.h:36
bool is_no_inherit
Definition: parsenodes.h:2177
bool initially_valid
Definition: parsenodes.h:2214
#define ereport(elevel,...)
Definition: elog.h:155
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc)
Definition: attmap.c:174
List * lcons(void *datum, List *list)
Definition: list.c:468
#define makeNode(_type_)
Definition: nodes.h:576
#define Assert(condition)
Definition: c.h:792
IndexStmt * generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx, const AttrMap *attmap, Oid *constraintOid)
AttrNumber * attnums
Definition: attmap.h:36
char * idxcomment
Definition: parsenodes.h:2802
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4526
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:158
uint16 num_defval
Definition: tupdesc.h:42
int errmsg(const char *fmt,...)
Definition: elog.c:915
RangeVar * relation
Definition: parsenodes.h:1801
#define elog(elevel,...)
Definition: elog.h:228
int i
#define NameStr(name)
Definition: c.h:669
char * nodeToString(const void *obj)
Definition: outfuncs.c:4390
ConstrType contype
Definition: parsenodes.h:2168
ConstrCheck * check
Definition: tupdesc.h:40
char * cooked_expr
Definition: parsenodes.h:2179
#define copyObject(obj)
Definition: nodes.h:644
Definition: pg_list.h:50
char * ccbin
Definition: tupdesc.h:31
int16 AttrNumber
Definition: attnum.h:21
bool skip_validation
Definition: parsenodes.h:2213
#define RelationGetRelid(relation)
Definition: rel.h:457
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:132
#define lfirst_oid(lc)
Definition: pg_list.h:171
bool ccnoinherit
Definition: tupdesc.h:33

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

References AccessShareLock, addNSItemToQuery(), addRangeTableEntryForRelation(), CreateStmtContext::alist, AT_AddColumn, AT_AddColumnRecurse, AT_AddConstraint, AT_AddConstraintRecurse, AT_AddIdentity, AT_AddIndex, AT_AddIndexConstraint, AT_AlterColumnType, AT_AttachPartition, AT_DetachPartition, AT_SetIdentity, attnum, CreateStmtContext::blist, PartitionCmd::bound, castNode, CreateStmtContext::ckconstraints, AlterTableStmt::cmds, ColumnDef::colname, CreateStmtContext::columns, CONSTR_FOREIGN, ColumnDef::constraints, ColumnDef::cooked_default, copyObject, AlterTableCmd::def, DefElem::defname, elog, ereport, errcode(), errmsg(), ERROR, EXPR_KIND_ALTER_COL_TRANSFORM, CreateStmtContext::extstats, 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, 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, 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, AlterTableStmt::relation, relation_close(), relation_open(), RelationGetDescr, RelationGetRelationName, AlterSeqStmt::sequence, CreateStmtContext::stmtType, AlterTableCmd::subtype, transformCheckConstraints(), transformColumnDefinition(), transformExpr(), transformExtendedStatistics(), transformFKConstraints(), transformIndexConstraints(), transformIndexStmt(), transformPartitionCmd(), transformTableConstraint(), TupleDescAttr, ColumnDef::typeName, and typenameTypeId().

Referenced by ATParseTransformCmd(), and ATPostAlterTypeParse().

3178 {
3179  Relation rel;
3180  TupleDesc tupdesc;
3181  ParseState *pstate;
3182  CreateStmtContext cxt;
3183  List *save_alist;
3184  ListCell *lcmd,
3185  *l;
3186  List *newcmds = NIL;
3187  bool skipValidation = true;
3188  AlterTableCmd *newcmd;
3189  ParseNamespaceItem *nsitem;
3190 
3191  /*
3192  * We must not scribble on the passed-in AlterTableStmt, so copy it. (This
3193  * is overkill, but easy.)
3194  */
3195  stmt = copyObject(stmt);
3196 
3197  /* Caller is responsible for locking the relation */
3198  rel = relation_open(relid, NoLock);
3199  tupdesc = RelationGetDescr(rel);
3200 
3201  /* Set up pstate */
3202  pstate = make_parsestate(NULL);
3203  pstate->p_sourcetext = queryString;
3204  nsitem = addRangeTableEntryForRelation(pstate,
3205  rel,
3207  NULL,
3208  false,
3209  true);
3210  addNSItemToQuery(pstate, nsitem, false, true, true);
3211 
3212  /* Set up CreateStmtContext */
3213  cxt.pstate = pstate;
3214  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
3215  {
3216  cxt.stmtType = "ALTER FOREIGN TABLE";
3217  cxt.isforeign = true;
3218  }
3219  else
3220  {
3221  cxt.stmtType = "ALTER TABLE";
3222  cxt.isforeign = false;
3223  }
3224  cxt.relation = stmt->relation;
3225  cxt.rel = rel;
3226  cxt.inhRelations = NIL;
3227  cxt.isalter = true;
3228  cxt.columns = NIL;
3229  cxt.ckconstraints = NIL;
3230  cxt.fkconstraints = NIL;
3231  cxt.ixconstraints = NIL;
3232  cxt.likeclauses = NIL;
3233  cxt.extstats = NIL;
3234  cxt.blist = NIL;
3235  cxt.alist = NIL;
3236  cxt.pkey = NULL;
3237  cxt.ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
3238  cxt.partbound = NULL;
3239  cxt.ofType = false;
3240 
3241  /*
3242  * Transform ALTER subcommands that need it (most don't). These largely
3243  * re-use code from CREATE TABLE.
3244  */
3245  foreach(lcmd, stmt->cmds)
3246  {
3247  AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
3248 
3249  switch (cmd->subtype)
3250  {
3251  case AT_AddColumn:
3252  case AT_AddColumnRecurse:
3253  {
3254  ColumnDef *def = castNode(ColumnDef, cmd->def);
3255 
3256  transformColumnDefinition(&cxt, def);
3257 
3258  /*
3259  * If the column has a non-null default, we can't skip
3260  * validation of foreign keys.
3261  */
3262  if (def->raw_default != NULL)
3263  skipValidation = false;
3264 
3265  /*
3266  * All constraints are processed in other ways. Remove the
3267  * original list
3268  */
3269  def->constraints = NIL;
3270 
3271  newcmds = lappend(newcmds, cmd);
3272  break;
3273  }
3274 
3275  case AT_AddConstraint:
3277 
3278  /*
3279  * The original AddConstraint cmd node doesn't go to newcmds
3280  */
3281  if (IsA(cmd->def, Constraint))
3282  {
3283  transformTableConstraint(&cxt, (Constraint *) cmd->def);
3284  if (((Constraint *) cmd->def)->contype == CONSTR_FOREIGN)
3285  skipValidation = false;
3286  }
3287  else
3288  elog(ERROR, "unrecognized node type: %d",
3289  (int) nodeTag(cmd->def));
3290  break;
3291 
3292  case AT_AlterColumnType:
3293  {
3294  ColumnDef *def = castNode(ColumnDef, cmd->def);
3296 
3297  /*
3298  * For ALTER COLUMN TYPE, transform the USING clause if
3299  * one was specified.
3300  */
3301  if (def->raw_default)
3302  {
3303  def->cooked_default =
3304  transformExpr(pstate, def->raw_default,
3306  }
3307 
3308  /*
3309  * For identity column, create ALTER SEQUENCE command to
3310  * change the data type of the sequence.
3311  */
3312  attnum = get_attnum(relid, cmd->name);
3313  if (attnum == InvalidAttrNumber)
3314  ereport(ERROR,
3315  (errcode(ERRCODE_UNDEFINED_COLUMN),
3316  errmsg("column \"%s\" of relation \"%s\" does not exist",
3317  cmd->name, RelationGetRelationName(rel))));
3318 
3319  if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
3320  {
3321  Oid seq_relid = getIdentitySequence(relid, attnum, false);
3322  Oid typeOid = typenameTypeId(pstate, def->typeName);
3323  AlterSeqStmt *altseqstmt = makeNode(AlterSeqStmt);
3324 
3325  altseqstmt->sequence = makeRangeVar(get_namespace_name(get_rel_namespace(seq_relid)),
3326  get_rel_name(seq_relid),
3327  -1);
3328  altseqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(typeOid, -1), -1));
3329  altseqstmt->for_identity = true;
3330  cxt.blist = lappend(cxt.blist, altseqstmt);
3331  }
3332 
3333  newcmds = lappend(newcmds, cmd);
3334  break;
3335  }
3336 
3337  case AT_AddIdentity:
3338  {
3339  Constraint *def = castNode(Constraint, cmd->def);
3340  ColumnDef *newdef = makeNode(ColumnDef);
3342 
3343  newdef->colname = cmd->name;
3344  newdef->identity = def->generated_when;
3345  cmd->def = (Node *) newdef;
3346 
3347  attnum = get_attnum(relid, cmd->name);
3348  if (attnum == InvalidAttrNumber)
3349  ereport(ERROR,
3350  (errcode(ERRCODE_UNDEFINED_COLUMN),
3351  errmsg("column \"%s\" of relation \"%s\" does not exist",
3352  cmd->name, RelationGetRelationName(rel))));
3353 
3354  generateSerialExtraStmts(&cxt, newdef,
3355  get_atttype(relid, attnum),
3356  def->options, true, true,
3357  NULL, NULL);
3358 
3359  newcmds = lappend(newcmds, cmd);
3360  break;
3361  }
3362 
3363  case AT_SetIdentity:
3364  {
3365  /*
3366  * Create an ALTER SEQUENCE statement for the internal
3367  * sequence of the identity column.
3368  */
3369  ListCell *lc;
3370  List *newseqopts = NIL;
3371  List *newdef = NIL;
3373  Oid seq_relid;
3374 
3375  /*
3376  * Split options into those handled by ALTER SEQUENCE and
3377  * those for ALTER TABLE proper.
3378  */
3379  foreach(lc, castNode(List, cmd->def))
3380  {
3381  DefElem *def = lfirst_node(DefElem, lc);
3382 
3383  if (strcmp(def->defname, "generated") == 0)
3384  newdef = lappend(newdef, def);
3385  else
3386  newseqopts = lappend(newseqopts, def);
3387  }
3388 
3389  attnum = get_attnum(relid, cmd->name);
3390  if (attnum == InvalidAttrNumber)
3391  ereport(ERROR,
3392  (errcode(ERRCODE_UNDEFINED_COLUMN),
3393  errmsg("column \"%s\" of relation \"%s\" does not exist",
3394  cmd->name, RelationGetRelationName(rel))));
3395 
3396  seq_relid = getIdentitySequence(relid, attnum, true);
3397 
3398  if (seq_relid)
3399  {
3400  AlterSeqStmt *seqstmt;
3401 
3402  seqstmt = makeNode(AlterSeqStmt);
3404  get_rel_name(seq_relid), -1);
3405  seqstmt->options = newseqopts;
3406  seqstmt->for_identity = true;
3407  seqstmt->missing_ok = false;
3408 
3409  cxt.blist = lappend(cxt.blist, seqstmt);
3410  }
3411 
3412  /*
3413  * If column was not an identity column, we just let the
3414  * ALTER TABLE command error out later. (There are cases
3415  * this fails to cover, but we'll need to restructure
3416  * where creation of the sequence dependency linkage
3417  * happens before we can fix it.)
3418  */
3419 
3420  cmd->def = (Node *) newdef;
3421  newcmds = lappend(newcmds, cmd);
3422  break;
3423  }
3424 
3425  case AT_AttachPartition:
3426  case AT_DetachPartition:
3427  {
3428  PartitionCmd *partcmd = (PartitionCmd *) cmd->def;
3429 
3430  transformPartitionCmd(&cxt, partcmd);
3431  /* assign transformed value of the partition bound */
3432  partcmd->bound = cxt.partbound;
3433  }
3434 
3435  newcmds = lappend(newcmds, cmd);
3436  break;
3437 
3438  default:
3439 
3440  /*
3441  * Currently, we shouldn't actually get here for subcommand
3442  * types that don't require transformation; but if we do, just
3443  * emit them unchanged.
3444  */
3445  newcmds = lappend(newcmds, cmd);
3446  break;
3447  }
3448  }
3449 
3450  /*
3451  * Transfer anything we already have in cxt.alist into save_alist, to keep
3452  * it separate from the output of transformIndexConstraints.
3453  */
3454  save_alist = cxt.alist;
3455  cxt.alist = NIL;
3456 
3457  /* Postprocess constraints */
3459  transformFKConstraints(&cxt, skipValidation, true);
3460  transformCheckConstraints(&cxt, false);
3461 
3462  /*
3463  * Push any index-creation commands into the ALTER, so that they can be
3464  * scheduled nicely by tablecmds.c. Note that tablecmds.c assumes that
3465  * the IndexStmt attached to an AT_AddIndex or AT_AddIndexConstraint
3466  * subcommand has already been through transformIndexStmt.
3467  */
3468  foreach(l, cxt.alist)
3469  {
3470  Node *istmt = (Node *) lfirst(l);
3471 
3472  /*
3473  * We assume here that cxt.alist contains only IndexStmts and possibly
3474  * ALTER TABLE SET NOT NULL statements generated from primary key
3475  * constraints. We absorb the subcommands of the latter directly.
3476  */
3477  if (IsA(istmt, IndexStmt))
3478  {
3479  IndexStmt *idxstmt = (IndexStmt *) istmt;
3480 
3481  idxstmt = transformIndexStmt(relid, idxstmt, queryString);
3482  newcmd = makeNode(AlterTableCmd);
3484  newcmd->def = (Node *) idxstmt;
3485  newcmds = lappend(newcmds, newcmd);
3486  }
3487  else if (IsA(istmt, AlterTableStmt))
3488  {
3489  AlterTableStmt *alterstmt = (AlterTableStmt *) istmt;
3490 
3491  newcmds = list_concat(newcmds, alterstmt->cmds);
3492  }
3493  else
3494  elog(ERROR, "unexpected stmt type %d", (int) nodeTag(istmt));
3495  }
3496  cxt.alist = NIL;
3497 
3498  /* Append any CHECK or FK constraints to the commands list */
3499  foreach(l, cxt.ckconstraints)
3500  {
3501  newcmd = makeNode(AlterTableCmd);
3502  newcmd->subtype = AT_AddConstraint;
3503  newcmd->def = (Node *) lfirst(l);
3504  newcmds = lappend(newcmds, newcmd);
3505  }
3506  foreach(l, cxt.fkconstraints)
3507  {
3508  newcmd = makeNode(AlterTableCmd);
3509  newcmd->subtype = AT_AddConstraint;
3510  newcmd->def = (Node *) lfirst(l);
3511  newcmds = lappend(newcmds, newcmd);
3512  }
3513 
3514  /* Append extended statistic objects */
3516 
3517  /* Close rel */
3518  relation_close(rel, NoLock);
3519 
3520  /*
3521  * Output results.
3522  */
3523  stmt->cmds = newcmds;
3524 
3525  *beforeStmts = cxt.blist;
3526  *afterStmts = list_concat(cxt.alist, save_alist);
3527 
3528  return stmt;
3529 }
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:579
char generated_when
Definition: parsenodes.h:2180
static void generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column, Oid seqtypid, List *seqoptions, bool for_identity, bool col_exists, char **snamespace_p, char **sname_p)
#define RelationGetDescr(relation)
Definition: rel.h:483
#define castNode(_type_, nodeptr)
Definition: nodes.h:597
char identity
Definition: parsenodes.h:656
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
List * constraints
Definition: parsenodes.h:662
Oid get_rel_namespace(Oid relid)
Definition: lsyscache.c:1896
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:94
#define AccessShareLock
Definition: lockdefs.h:36
void addNSItemToQuery(ParseState *pstate, ParseNamespaceItem *nsitem, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
Definition: nodes.h:528
List * list_concat(List *list1, const List *list2)
Definition: list.c:530
int errcode(int sqlerrcode)
Definition: elog.c:704
AlterTableType subtype
Definition: parsenodes.h:1889
Form_pg_class rd_rel
Definition: rel.h:110
unsigned int Oid
Definition: postgres_ext.h:31
Oid getIdentitySequence(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: pg_depend.c:954
#define OidIsValid(objectId)
Definition: c.h:698
DefElem * makeDefElem(char *name, Node *arg, int location)
Definition: makefuncs.c:546
List * options
Definition: parsenodes.h:2573
List * options
Definition: parsenodes.h:2192
ParseNamespaceItem * addRangeTableEntryForRelation(ParseState *pstate, Relation rel, int lockmode, Alias *alias, bool inh, bool inFromCl)
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:44
#define list_make1(x1)
Definition: pg_list.h:206
ParseState * pstate
Definition: parse_utilcmd.c:78
Node * cooked_default
Definition: parsenodes.h:655
Oid indexOid
Definition: parsenodes.h:2803
RangeVar * relation
Definition: parse_utilcmd.c:80
IndexStmt * pkey
Definition: parse_utilcmd.c:95
#define ERROR
Definition: elog.h:45
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:48
#define lfirst_node(type, lc)
Definition: pg_list.h:172
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3289
#define NoLock
Definition: lockdefs.h:34
AttrNumber get_attnum(Oid relid, const char *attname)
Definition: lsyscache.c:856
#define RelationGetRelationName(relation)
Definition: rel.h:491
const char * p_sourcetext
Definition: parse_node.h:179
Oid get_atttype(Oid relid, AttrNumber attnum)
Definition: lsyscache.c:911
PartitionBoundSpec * partbound
Definition: parse_utilcmd.c:97
Node * raw_default
Definition: parsenodes.h:654
List * lappend(List *list, void *datum)
Definition: list.c:336
TypeName * makeTypeNameFromOid(Oid typeOid, int32 typmod)
Definition: makefuncs.c:472
RangeVar * sequence
Definition: parsenodes.h:2572
int16 attnum
Definition: pg_attribute.h:79
#define ereport(elevel,...)
Definition: elog.h:155
#define makeNode(_type_)
Definition: nodes.h:576
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
#define lfirst(lc)
Definition: pg_list.h:169
static void transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
PartitionBoundSpec * bound
Definition: parsenodes.h:862
TypeName * typeName
Definition: parsenodes.h:648
static void transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd)
static void transformFKConstraints(CreateStmtContext *cxt, bool skipValidation, bool isAddConstraint)
#define InvalidAttrNumber
Definition: attnum.h:23
#define nodeTag(nodeptr)
Definition: nodes.h:533
const char * stmtType
Definition: parse_utilcmd.c:79
static void transformExtendedStatistics(CreateStmtContext *cxt)
int errmsg(const char *fmt,...)
Definition: elog.c:915
RangeVar * relation
Definition: parsenodes.h:1801
#define elog(elevel,...)
Definition: elog.h:228
bool for_identity
Definition: parsenodes.h:2574
char * defname
Definition: parsenodes.h:733
char * colname
Definition: parsenodes.h:647
#define copyObject(obj)
Definition: nodes.h:644
static void transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
static void transformIndexConstraints(CreateStmtContext *cxt)
IndexStmt * transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
Definition: pg_list.h:50
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1872
int16 AttrNumber
Definition: attnum.h:21
RangeVar * makeRangeVar(char *schemaname, char *relname, int location)
Definition: makefuncs.c:422
static void transformCheckConstraints(CreateStmtContext *cxt, bool skipValidation)
Oid typenameTypeId(ParseState *pstate, const TypeName *typeName)
Definition: parse_type.c:291

◆ transformCreateSchemaStmt()

List* transformCreateSchemaStmt ( CreateSchemaStmt stmt)

Definition at line 3710 of file parse_utilcmd.c.

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

Referenced by CreateSchemaCommand().

3711 {
3713  List *result;
3714  ListCell *elements;
3715 
3716  cxt.stmtType = "CREATE SCHEMA";
3717  cxt.schemaname = stmt->schemaname;
3718  cxt.authrole = (RoleSpec *) stmt->authrole;
3719  cxt.sequences = NIL;
3720  cxt.tables = NIL;
3721  cxt.views = NIL;
3722  cxt.indexes = NIL;
3723  cxt.triggers = NIL;
3724  cxt.grants = NIL;
3725 
3726  /*
3727  * Run through each schema element in the schema element list. Separate
3728  * statements by type, and do preliminary analysis.
3729  */
3730  foreach(elements, stmt->schemaElts)
3731  {
3732  Node *element = lfirst(elements);
3733 
3734  switch (nodeTag(element))
3735  {
3736  case T_CreateSeqStmt:
3737  {
3738  CreateSeqStmt *elp = (CreateSeqStmt *) element;
3739 
3741  cxt.sequences = lappend(cxt.sequences, element);
3742  }
3743  break;
3744 
3745  case T_CreateStmt:
3746  {
3747  CreateStmt *elp = (CreateStmt *) element;
3748 
3750 
3751  /*
3752  * XXX todo: deal with constraints
3753  */
3754  cxt.tables = lappend(cxt.tables, element);
3755  }
3756  break;
3757 
3758  case T_ViewStmt:
3759  {
3760  ViewStmt *elp = (ViewStmt *) element;
3761 
3762  setSchemaName(cxt.schemaname, &elp->view->schemaname);
3763 
3764  /*
3765  * XXX todo: deal with references between views
3766  */
3767  cxt.views = lappend(cxt.views, element);
3768  }
3769  break;
3770 
3771  case T_IndexStmt:
3772  {
3773  IndexStmt *elp = (IndexStmt *) element;
3774 
3776  cxt.indexes = lappend(cxt.indexes, element);
3777  }
3778  break;
3779 
3780  case T_CreateTrigStmt:
3781  {
3782  CreateTrigStmt *elp = (CreateTrigStmt *) element;
3783 
3785  cxt.triggers = lappend(cxt.triggers, element);
3786  }
3787  break;
3788 
3789  case T_GrantStmt:
3790  cxt.grants = lappend(cxt.grants, element);
3791  break;
3792 
3793  default:
3794  elog(ERROR, "unrecognized node type: %d",
3795  (int) nodeTag(element));
3796  }
3797  }
3798 
3799  result = NIL;
3800  result = list_concat(result, cxt.sequences);
3801  result = list_concat(result, cxt.tables);
3802  result = list_concat(result, cxt.views);
3803  result = list_concat(result, cxt.indexes);
3804  result = list_concat(result, cxt.triggers);
3805  result = list_concat(result, cxt.grants);
3806 
3807  return result;
3808 }
RangeVar * relation
Definition: parsenodes.h:2088
#define NIL
Definition: pg_list.h:65
Definition: nodes.h:528
List * list_concat(List *list1, const List *list2)
Definition: list.c:530
RangeVar * view
Definition: parsenodes.h:3145
char * schemaname
Definition: primnodes.h:67
RangeVar * relation
Definition: parsenodes.h:2793
RoleSpec * authrole
Definition: parsenodes.h:1783
#define ERROR
Definition: elog.h:45
List * lappend(List *list, void *datum)
Definition: list.c:336
static chr element(struct vars *v, const chr *startp, const chr *endp)
Definition: regc_locale.c:380
RangeVar * sequence
Definition: parsenodes.h:2562
#define lfirst(lc)
Definition: pg_list.h:169
static void setSchemaName(char *context_schema, char **stmt_schema_name)
#define nodeTag(nodeptr)
Definition: nodes.h:533
#define elog(elevel,...)
Definition: elog.h:228
RangeVar * relation
Definition: parsenodes.h:2449
Definition: pg_list.h:50

◆ transformCreateStmt()

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

Definition at line 168 of file parse_utilcmd.c.

References CreateStmtContext::alist, Assert, CreateStmtContext::blist, cancel_parser_errposition_callback(), CreateStmtContext::ckconstraints, CreateStmtContext::columns, CreateStmt::constraints, copyObject, element(), elog, ereport, errcode(), errmsg(), ERROR, CreateStmtContext::extstats, CreateStmtContext::fkconstraints, get_namespace_name(), CreateStmt::if_not_exists, CreateStmtContext::inhRelations, CreateStmt::inhRelations, IsA, CreateStmtContext::isalter, CreateStmtContext::isforeign, CreateStmtContext::ispartitioned, CreateStmtContext::ixconstraints, lappend(), lfirst, CreateStmtContext::likeclauses, list_concat(), RangeVar::location, make_parsestate(), NIL, nodeTag, NoLock, NOTICE, CreateStmtContext::ofType, CreateStmt::ofTypename, OidIsValid, ParseState::p_sourcetext, CreateStmtContext::partbound, CreateStmt::partbound, CreateStmt::partspec, CreateStmtContext::pkey, CreateStmtContext::pstate, RangeVarGetAndCheckCreationNamespace(), CreateStmtContext::rel, CreateStmtContext::relation, CreateStmt::relation, RangeVar::relname, RangeVar::relpersistence, RangeVar::schemaname, setup_parser_errposition_callback(), CreateStmtContext::stmtType, T_ColumnDef, T_Constraint, T_TableLikeClause, CreateStmt::tableElts, transformCheckConstraints(), transformColumnDefinition(), transformExtendedStatistics(), transformFKConstraints(), transformIndexConstraints(), transformOfType(), transformTableConstraint(), and transformTableLikeClause().

Referenced by ProcessUtilitySlow().

169 {
170  ParseState *pstate;
171  CreateStmtContext cxt;
172  List *result;
173  List *save_alist;
174  ListCell *elements;
175  Oid namespaceid;
176  Oid existing_relid;
177  ParseCallbackState pcbstate;
178  bool is_foreign_table = IsA(stmt, CreateForeignTableStmt);
179 
180  /*
181  * We must not scribble on the passed-in CreateStmt, so copy it. (This is
182  * overkill, but easy.)
183  */
184  stmt = copyObject(stmt);
185 
186  /* Set up pstate */
187  pstate = make_parsestate(NULL);
188  pstate->p_sourcetext = queryString;
189 
190  /*
191  * Look up the creation namespace. This also checks permissions on the
192  * target namespace, locks it against concurrent drops, checks for a
193  * preexisting relation in that namespace with the same name, and updates
194  * stmt->relation->relpersistence if the selected namespace is temporary.
195  */
196  setup_parser_errposition_callback(&pcbstate, pstate,
197  stmt->relation->location);
198  namespaceid =
200  &existing_relid);
202 
203  /*
204  * If the relation already exists and the user specified "IF NOT EXISTS",
205  * bail out with a NOTICE.
206  */
207  if (stmt->if_not_exists && OidIsValid(existing_relid))
208  {
209  ereport(NOTICE,
210  (errcode(ERRCODE_DUPLICATE_TABLE),
211  errmsg("relation \"%s\" already exists, skipping",
212  stmt->relation->relname)));
213  return NIL;
214  }
215 
216  /*
217  * If the target relation name isn't schema-qualified, make it so. This
218  * prevents some corner cases in which added-on rewritten commands might
219  * think they should apply to other relations that have the same name and
220  * are earlier in the search path. But a local temp table is effectively
221  * specified to be in pg_temp, so no need for anything extra in that case.
222  */
223  if (stmt->relation->schemaname == NULL
224  && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
225  stmt->relation->schemaname = get_namespace_name(namespaceid);
226 
227  /* Set up CreateStmtContext */
228  cxt.pstate = pstate;
229  if (IsA(stmt, CreateForeignTableStmt))
230  {
231  cxt.stmtType = "CREATE FOREIGN TABLE";
232  cxt.isforeign = true;
233  }
234  else
235  {
236  cxt.stmtType = "CREATE TABLE";
237  cxt.isforeign = false;
238  }
239  cxt.relation = stmt->relation;
240  cxt.rel = NULL;
241  cxt.inhRelations = stmt->inhRelations;
242  cxt.isalter = false;
243  cxt.columns = NIL;
244  cxt.ckconstraints = NIL;
245  cxt.fkconstraints = NIL;
246  cxt.ixconstraints = NIL;
247  cxt.likeclauses = NIL;
248  cxt.extstats = 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:
280  transformColumnDefinition(&cxt, (ColumnDef *) element);
281  break;
282 
283  case T_Constraint:
284  transformTableConstraint(&cxt, (Constraint *) element);
285  break;
286 
287  case T_TableLikeClause:
288  transformTableLikeClause(&cxt, (TableLikeClause *) element);
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  transformCheckConstraints(&cxt, !is_foreign_table ? true : false);
337 
338  /*
339  * Postprocess extended statistics.
340  */
342 
343  /*
344  * Output results.
345  */
346  stmt->tableElts = cxt.columns;
347  stmt->constraints = cxt.ckconstraints;
348 
349  result = lappend(cxt.blist, stmt);
350  result = list_concat(result, cxt.alist);
351  result = list_concat(result, save_alist);
352 
353  return result;
354 }
RangeVar * relation
Definition: parsenodes.h:2088
#define NIL
Definition: pg_list.h:65
List * inhRelations
Definition: parsenodes.h:2090
#define IsA(nodeptr, _type_)
Definition: nodes.h:579
Definition: nodes.h:528
List * list_concat(List *list1, const List *list2)
Definition: list.c:530
int errcode(int sqlerrcode)
Definition: elog.c:704
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:698
char * schemaname
Definition: primnodes.h:67
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:44
List * constraints
Definition: parsenodes.h:2095
bool if_not_exists
Definition: parsenodes.h:2100
int location
Definition: primnodes.h:73
ParseState * pstate
Definition: parse_utilcmd.c:78
PartitionBoundSpec * partbound
Definition: parsenodes.h:2092
char * relname
Definition: primnodes.h:68
RangeVar * relation
Definition: parse_utilcmd.c:80
IndexStmt * pkey
Definition: parse_utilcmd.c:95
void cancel_parser_errposition_callback(ParseCallbackState *pcbstate)
Definition: parse_node.c:161
#define ERROR
Definition: elog.h:45
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3289
#define NoLock
Definition: lockdefs.h:34
const char * p_sourcetext
Definition: parse_node.h:179
void setup_parser_errposition_callback(ParseCallbackState *pcbstate, ParseState *pstate, int location)
Definition: parse_node.c:145
PartitionBoundSpec * partbound
Definition: parse_utilcmd.c:97
List * lappend(List *list, void *datum)
Definition: list.c:336
static chr element(struct vars *v, const chr *startp, const chr *endp)
Definition: regc_locale.c:380
Oid RangeVarGetAndCheckCreationNamespace(RangeVar *relation, LOCKMODE lockmode, Oid *existing_relation_id)
Definition: namespace.c:534
#define ereport(elevel,...)
Definition: elog.h:155
#define NOTICE
Definition: elog.h:37
List * tableElts
Definition: parsenodes.h:2089
static void transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
#define Assert(condition)
Definition: c.h:792
#define lfirst(lc)
Definition: pg_list.h:169
static void transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
static void transformFKConstraints(CreateStmtContext *cxt, bool skipValidation, bool isAddConstraint)
#define nodeTag(nodeptr)
Definition: nodes.h:533
const char * stmtType
Definition: parse_utilcmd.c:79
static void transformExtendedStatistics(CreateStmtContext *cxt)
char relpersistence
Definition: primnodes.h:71
int errmsg(const char *fmt,...)
Definition: elog.c:915
#define elog(elevel,...)
Definition: elog.h:228
#define copyObject(obj)
Definition: nodes.h:644
static void transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
static void transformIndexConstraints(CreateStmtContext *cxt)
PartitionSpec * partspec
Definition: parsenodes.h:2093
Definition: pg_list.h:50
static void transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_clause)
TypeName * ofTypename
Definition: parsenodes.h:2094
static void transformCheckConstraints(CreateStmtContext *cxt, bool skipValidation)

◆ transformIndexStmt()

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

Definition at line 2757 of file parse_utilcmd.c.

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

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

2758 {
2759  ParseState *pstate;
2760  ParseNamespaceItem *nsitem;
2761  ListCell *l;
2762  Relation rel;
2763 
2764  /* Nothing to do if statement already transformed. */
2765  if (stmt->transformed)
2766  return stmt;
2767 
2768  /*
2769  * We must not scribble on the passed-in IndexStmt, so copy it. (This is
2770  * overkill, but easy.)
2771  */
2772  stmt = copyObject(stmt);
2773 
2774  /* Set up pstate */
2775  pstate = make_parsestate(NULL);
2776  pstate->p_sourcetext = queryString;
2777 
2778  /*
2779  * Put the parent table into the rtable so that the expressions can refer
2780  * to its fields without qualification. Caller is responsible for locking
2781  * relation, but we still need to open it.
2782  */
2783  rel = relation_open(relid, NoLock);
2784  nsitem = addRangeTableEntryForRelation(pstate, rel,
2786  NULL, false, true);
2787 
2788  /* no to join list, yes to namespaces */
2789  addNSItemToQuery(pstate, nsitem, false, true, true);
2790 
2791  /* take care of the where clause */
2792  if (stmt->whereClause)
2793  {
2794  stmt->whereClause = transformWhereClause(pstate,
2795  stmt->whereClause,
2797  "WHERE");
2798  /* we have to fix its collations too */
2799  assign_expr_collations(pstate, stmt->whereClause);
2800  }
2801 
2802  /* take care of any index expressions */
2803  foreach(l, stmt->indexParams)
2804  {
2805  IndexElem *ielem = (IndexElem *) lfirst(l);
2806 
2807  if (ielem->expr)
2808  {
2809  /* Extract preliminary index col name before transforming expr */
2810  if (ielem->indexcolname == NULL)
2811  ielem->indexcolname = FigureIndexColname(ielem->expr);
2812 
2813  /* Now do parse transformation of the expression */
2814  ielem->expr = transformExpr(pstate, ielem->expr,
2816 
2817  /* We have to fix its collations too */
2818  assign_expr_collations(pstate, ielem->expr);
2819 
2820  /*
2821  * transformExpr() should have already rejected subqueries,
2822  * aggregates, window functions, and SRFs, based on the EXPR_KIND_
2823  * for an index expression.
2824  *
2825  * DefineIndex() will make more checks.
2826  */
2827  }
2828  }
2829 
2830  /*
2831  * Check that only the base rel is mentioned. (This should be dead code
2832  * now that add_missing_from is history.)
2833  */
2834  if (list_length(pstate->p_rtable) != 1)
2835  ereport(ERROR,
2836  (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
2837  errmsg("index expressions and predicates can refer only to the table being indexed")));
2838 
2839  free_parsestate(pstate);
2840 
2841  /* Close relation */
2842  table_close(rel, NoLock);
2843 
2844  /* Mark statement as successfully transformed */
2845  stmt->transformed = true;
2846 
2847  return stmt;
2848 }
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:94
Node * whereClause
Definition: parsenodes.h:2800
#define AccessShareLock
Definition: lockdefs.h:36
void addNSItemToQuery(ParseState *pstate, ParseNamespaceItem *nsitem, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
int errcode(int sqlerrcode)
Definition: elog.c:704
char * FigureIndexColname(Node *node)
ParseNamespaceItem * addRangeTableEntryForRelation(ParseState *pstate, Relation rel, int lockmode, Alias *alias, bool inh, bool inFromCl)
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:44
void assign_expr_collations(ParseState *pstate, Node *expr)
Node * expr
Definition: parsenodes.h:702
#define ERROR
Definition: elog.h:45
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:48
#define NoLock
Definition: lockdefs.h:34
bool transformed
Definition: parsenodes.h:2813
char * indexcolname
Definition: parsenodes.h:703
const char * p_sourcetext
Definition: parse_node.h:179
#define ereport(elevel,...)
Definition: elog.h:155
#define lfirst(lc)
Definition: pg_list.h:169
Node * transformWhereClause(ParseState *pstate, Node *clause, ParseExprKind exprKind, const char *constructName)
List * indexParams
Definition: parsenodes.h:2796
static int list_length(const List *l)
Definition: pg_list.h:149
int errmsg(const char *fmt,...)
Definition: elog.c:915
#define copyObject(obj)
Definition: nodes.h:644
void free_parsestate(ParseState *pstate)
Definition: parse_node.c:77
List * p_rtable
Definition: parse_node.h:180

◆ transformPartitionBound()

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

Definition at line 3888 of file parse_utilcmd.c.

References Assert, castNode, 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, linitial, list_length(), PartitionBoundSpec::listdatums, PartitionBoundSpec::lowerdatums, NIL, parser_errposition(), PartitionKeyData::partattrs, 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().

3890 {
3891  PartitionBoundSpec *result_spec;
3893  char strategy = get_partition_strategy(key);
3894  int partnatts = get_partition_natts(key);
3895  List *partexprs = get_partition_exprs(key);
3896 
3897  /* Avoid scribbling on input */
3898  result_spec = copyObject(spec);
3899 
3900  if (spec->is_default)
3901  {
3902  /*
3903  * Hash partitioning does not support a default partition; there's no
3904  * use case for it (since the set of partitions to create is perfectly
3905  * defined), and if users do get into it accidentally, it's hard to
3906  * back out from it afterwards.
3907  */
3908  if (strategy == PARTITION_STRATEGY_HASH)
3909  ereport(ERROR,
3910  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3911  errmsg("a hash-partitioned table may not have a default partition")));
3912 
3913  /*
3914  * In case of the default partition, parser had no way to identify the
3915  * partition strategy. Assign the parent's strategy to the default
3916  * partition bound spec.
3917  */
3918  result_spec->strategy = strategy;
3919 
3920  return result_spec;
3921  }
3922 
3923  if (strategy == PARTITION_STRATEGY_HASH)
3924  {
3925  if (spec->strategy != PARTITION_STRATEGY_HASH)
3926  ereport(ERROR,
3927  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3928  errmsg("invalid bound specification for a hash partition"),
3929  parser_errposition(pstate, exprLocation((Node *) spec))));
3930 
3931  if (spec->modulus <= 0)
3932  ereport(ERROR,
3933  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3934  errmsg("modulus for hash partition must be a positive integer")));
3935 
3936  Assert(spec->remainder >= 0);
3937 
3938  if (spec->remainder >= spec->modulus)
3939  ereport(ERROR,
3940  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3941  errmsg("remainder for hash partition must be less than modulus")));
3942  }
3943  else if (strategy == PARTITION_STRATEGY_LIST)
3944  {
3945  ListCell *cell;
3946  char *colname;
3947  Oid coltype;
3948  int32 coltypmod;
3949  Oid partcollation;
3950 
3951  if (spec->strategy != PARTITION_STRATEGY_LIST)
3952  ereport(ERROR,
3953  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3954  errmsg("invalid bound specification for a list partition"),
3955  parser_errposition(pstate, exprLocation((Node *) spec))));
3956 
3957  /* Get the only column's name in case we need to output an error */
3958  if (key->partattrs[0] != 0)
3959  colname = get_attname(RelationGetRelid(parent),
3960  key->partattrs[0], false);
3961  else
3962  colname = deparse_expression((Node *) linitial(partexprs),
3964  RelationGetRelid(parent)),
3965  false, false);
3966  /* Need its type data too */
3967  coltype = get_partition_col_typid(key, 0);
3968  coltypmod = get_partition_col_typmod(key, 0);
3969  partcollation = get_partition_col_collation(key, 0);
3970 
3971  result_spec->listdatums = NIL;
3972  foreach(cell, spec->listdatums)
3973  {
3974  Node *expr = lfirst(cell);
3975  Const *value;
3976  ListCell *cell2;
3977  bool duplicate;
3978 
3979  value = transformPartitionBoundValue(pstate, expr,
3980  colname, coltype, coltypmod,
3981  partcollation);
3982 
3983  /* Don't add to the result if the value is a duplicate */
3984  duplicate = false;
3985  foreach(cell2, result_spec->listdatums)
3986  {
3987  Const *value2 = castNode(Const, lfirst(cell2));
3988 
3989  if (equal(value, value2))
3990  {
3991  duplicate = true;
3992  break;
3993  }
3994  }
3995  if (duplicate)
3996  continue;
3997 
3998  result_spec->listdatums = lappend(result_spec->listdatums,
3999  value);
4000  }
4001  }
4002  else if (strategy == PARTITION_STRATEGY_RANGE)
4003  {
4004  if (spec->strategy != PARTITION_STRATEGY_RANGE)
4005  ereport(ERROR,
4006  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4007  errmsg("invalid bound specification for a range partition"),
4008  parser_errposition(pstate, exprLocation((Node *) spec))));
4009 
4010  if (list_length(spec->lowerdatums) != partnatts)
4011  ereport(ERROR,
4012  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4013  errmsg("FROM must specify exactly one value per partitioning column")));
4014  if (list_length(spec->upperdatums) != partnatts)
4015  ereport(ERROR,
4016  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4017  errmsg("TO must specify exactly one value per partitioning column")));
4018 
4019  /*
4020  * Convert raw parse nodes into PartitionRangeDatum nodes and perform
4021  * any necessary validation.
4022  */
4023  result_spec->lowerdatums =
4025  parent);
4026  result_spec->upperdatums =
4028  parent);
4029  }
4030  else
4031  elog(ERROR, "unexpected partition strategy: %d", (int) strategy);
4032 
4033  return result_spec;
4034 }
#define NIL
Definition: pg_list.h:65
int exprLocation(const Node *expr)
Definition: nodeFuncs.c:1231
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3042
#define castNode(_type_, nodeptr)
Definition: nodes.h:597
static int32 get_partition_col_typmod(PartitionKey key, int col)
Definition: partcache.h:91
static Oid get_partition_col_typid(PartitionKey key, int col)
Definition: partcache.h:85
static struct @144 value
Definition: nodes.h:528
static int get_partition_natts(PartitionKey key)
Definition: partcache.h:64
int errcode(int sqlerrcode)
Definition: elog.c:704
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:54
unsigned int Oid
Definition: postgres_ext.h:31
signed int int32
Definition: c.h:417
static List * get_partition_exprs(PartitionKey key)
Definition: partcache.h:70
#define linitial(l)
Definition: pg_list.h:174
#define ERROR
Definition: elog.h:45
static Const * transformPartitionBoundValue(ParseState *pstate, Node *con, const char *colName, Oid colType, int32 colTypmod, Oid partCollation)
static List * transformPartitionRangeBounds(ParseState *pstate, List *blist, Relation parent)
List * deparse_context_for(const char *aliasname, Oid relid)
Definition: ruleutils.c:3282
#define RelationGetRelationName(relation)
Definition: rel.h:491
List * lappend(List *list, void *datum)
Definition: list.c:336
AttrNumber * partattrs
Definition: partcache.h:28
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:801
#define ereport(elevel,...)
Definition: elog.h:155
#define Assert(condition)
Definition: c.h:792
#define lfirst(lc)
Definition: pg_list.h:169
char * deparse_expression(Node *expr, List *dpcontext, bool forceprefix, bool showimplicit)
Definition: ruleutils.c:3222
static int list_length(const List *l)
Definition: pg_list.h:149
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:111
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:802
static Oid get_partition_col_collation(PartitionKey key, int col)
Definition: partcache.h:97
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:803
int errmsg(const char *fmt,...)
Definition: elog.c:915
#define elog(elevel,...)
Definition: elog.h:228
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:825
#define copyObject(obj)
Definition: nodes.h:644
static int get_partition_strategy(PartitionKey key)
Definition: partcache.h:58
Definition: pg_list.h:50
#define RelationGetRelid(relation)
Definition: rel.h:457

◆ transformRuleStmt()

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

Definition at line 2864 of file parse_utilcmd.c.

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

Referenced by DefineRule().

2866 {
2867  Relation rel;
2868  ParseState *pstate;
2869  ParseNamespaceItem *oldnsitem;
2870  ParseNamespaceItem *newnsitem;
2871 
2872  /*
2873  * To avoid deadlock, make sure the first thing we do is grab
2874  * AccessExclusiveLock on the target relation. This will be needed by
2875  * DefineQueryRewrite(), and we don't want to grab a lesser lock
2876  * beforehand.
2877  */
2879 
2880  if (rel->rd_rel->relkind == RELKIND_MATVIEW)
2881  ereport(ERROR,
2882  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2883  errmsg("rules on materialized views are not supported")));
2884 
2885  /* Set up pstate */
2886  pstate = make_parsestate(NULL);
2887  pstate->p_sourcetext = queryString;
2888 
2889  /*
2890  * NOTE: 'OLD' must always have a varno equal to 1 and 'NEW' equal to 2.
2891  * Set up their ParseNamespaceItems in the main pstate for use in parsing
2892  * the rule qualification.
2893  */
2894  oldnsitem = addRangeTableEntryForRelation(pstate, rel,
2896  makeAlias("old", NIL),
2897  false, false);
2898  newnsitem = addRangeTableEntryForRelation(pstate, rel,
2900  makeAlias("new", NIL),
2901  false, false);
2902  /* Must override addRangeTableEntry's default access-check flags */
2903  oldnsitem->p_rte->requiredPerms = 0;
2904  newnsitem->p_rte->requiredPerms = 0;
2905 
2906  /*
2907  * They must be in the namespace too for lookup purposes, but only add the
2908  * one(s) that are relevant for the current kind of rule. In an UPDATE
2909  * rule, quals must refer to OLD.field or NEW.field to be unambiguous, but
2910  * there's no need to be so picky for INSERT & DELETE. We do not add them
2911  * to the joinlist.
2912  */
2913  switch (stmt->event)
2914  {
2915  case CMD_SELECT:
2916  addNSItemToQuery(pstate, oldnsitem, false, true, true);
2917  break;
2918  case CMD_UPDATE:
2919  addNSItemToQuery(pstate, oldnsitem, false, true, true);
2920  addNSItemToQuery(pstate, newnsitem, false, true, true);
2921  break;
2922  case CMD_INSERT:
2923  addNSItemToQuery(pstate, newnsitem, false, true, true);
2924  break;
2925  case CMD_DELETE:
2926  addNSItemToQuery(pstate, oldnsitem, false, true, true);
2927  break;
2928  default:
2929  elog(ERROR, "unrecognized event type: %d",
2930  (int) stmt->event);
2931  break;
2932  }
2933 
2934  /* take care of the where clause */
2935  *whereClause = transformWhereClause(pstate,
2936  (Node *) copyObject(stmt->whereClause),
2938  "WHERE");
2939  /* we have to fix its collations too */
2940  assign_expr_collations(pstate, *whereClause);
2941 
2942  /* this is probably dead code without add_missing_from: */
2943  if (list_length(pstate->p_rtable) != 2) /* naughty, naughty... */
2944  ereport(ERROR,
2945  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2946  errmsg("rule WHERE condition cannot contain references to other relations")));
2947 
2948  /*
2949  * 'instead nothing' rules with a qualification need a query rangetable so
2950  * the rewrite handler can add the negated rule qualification to the
2951  * original query. We create a query with the new command type CMD_NOTHING
2952  * here that is treated specially by the rewrite system.
2953  */
2954  if (stmt->actions == NIL)
2955  {
2956  Query *nothing_qry = makeNode(Query);
2957 
2958  nothing_qry->commandType = CMD_NOTHING;
2959  nothing_qry->rtable = pstate->p_rtable;
2960  nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */
2961 
2962  *actions = list_make1(nothing_qry);
2963  }
2964  else
2965  {
2966  ListCell *l;
2967  List *newactions = NIL;
2968 
2969  /*
2970  * transform each statement, like parse_sub_analyze()
2971  */
2972  foreach(l, stmt->actions)
2973  {
2974  Node *action = (Node *) lfirst(l);
2975  ParseState *sub_pstate = make_parsestate(NULL);
2976  Query *sub_qry,
2977  *top_subqry;
2978  bool has_old,
2979  has_new;
2980 
2981  /*
2982  * Since outer ParseState isn't parent of inner, have to pass down
2983  * the query text by hand.
2984  */
2985  sub_pstate->p_sourcetext = queryString;
2986 
2987  /*
2988  * Set up OLD/NEW in the rtable for this statement. The entries
2989  * are added only to relnamespace, not varnamespace, because we
2990  * don't want them to be referred to by unqualified field names
2991  * nor "*" in the rule actions. We decide later whether to put
2992  * them in the joinlist.
2993  */
2994  oldnsitem = addRangeTableEntryForRelation(sub_pstate, rel,
2996  makeAlias("old", NIL),
2997  false, false);
2998  newnsitem = addRangeTableEntryForRelation(sub_pstate, rel,
3000  makeAlias("new", NIL),
3001  false, false);
3002  oldnsitem->p_rte->requiredPerms = 0;
3003  newnsitem->p_rte->requiredPerms = 0;
3004  addNSItemToQuery(sub_pstate, oldnsitem, false, true, false);
3005  addNSItemToQuery(sub_pstate, newnsitem, false, true, false);
3006 
3007  /* Transform the rule action statement */
3008  top_subqry = transformStmt(sub_pstate,
3009  (Node *) copyObject(action));
3010 
3011  /*
3012  * We cannot support utility-statement actions (eg NOTIFY) with
3013  * nonempty rule WHERE conditions, because there's no way to make
3014  * the utility action execute conditionally.
3015  */
3016  if (top_subqry->commandType == CMD_UTILITY &&
3017  *whereClause != NULL)
3018  ereport(ERROR,
3019  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3020  errmsg("rules with WHERE conditions can only have SELECT, INSERT, UPDATE, or DELETE actions")));
3021 
3022  /*
3023  * If the action is INSERT...SELECT, OLD/NEW have been pushed down
3024  * into the SELECT, and that's what we need to look at. (Ugly
3025  * kluge ... try to fix this when we redesign querytrees.)
3026  */
3027  sub_qry = getInsertSelectQuery(top_subqry, NULL);
3028 
3029  /*
3030  * If the sub_qry is a setop, we cannot attach any qualifications
3031  * to it, because the planner won't notice them. This could
3032  * perhaps be relaxed someday, but for now, we may as well reject
3033  * such a rule immediately.
3034  */
3035  if (sub_qry->setOperations != NULL && *whereClause != NULL)
3036  ereport(ERROR,
3037  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3038  errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
3039 
3040  /*
3041  * Validate action's use of OLD/NEW, qual too
3042  */
3043  has_old =
3044  rangeTableEntry_used((Node *) sub_qry, PRS2_OLD_VARNO, 0) ||
3045  rangeTableEntry_used(*whereClause, PRS2_OLD_VARNO, 0);
3046  has_new =
3047  rangeTableEntry_used((Node *) sub_qry, PRS2_NEW_VARNO, 0) ||
3048  rangeTableEntry_used(*whereClause, PRS2_NEW_VARNO, 0);
3049 
3050  switch (stmt->event)
3051  {
3052  case CMD_SELECT:
3053  if (has_old)
3054  ereport(ERROR,
3055  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3056  errmsg("ON SELECT rule cannot use OLD")));
3057  if (has_new)
3058  ereport(ERROR,
3059  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3060  errmsg("ON SELECT rule cannot use NEW")));
3061  break;
3062  case CMD_UPDATE:
3063  /* both are OK */
3064  break;
3065  case CMD_INSERT:
3066  if (has_old)
3067  ereport(ERROR,
3068  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3069  errmsg("ON INSERT rule cannot use OLD")));
3070  break;
3071  case CMD_DELETE:
3072  if (has_new)
3073  ereport(ERROR,
3074  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3075  errmsg("ON DELETE rule cannot use NEW")));
3076  break;
3077  default:
3078  elog(ERROR, "unrecognized event type: %d",
3079  (int) stmt->event);
3080  break;
3081  }
3082 
3083  /*
3084  * OLD/NEW are not allowed in WITH queries, because they would
3085  * amount to outer references for the WITH, which we disallow.
3086  * However, they were already in the outer rangetable when we
3087  * analyzed the query, so we have to check.
3088  *
3089  * Note that in the INSERT...SELECT case, we need to examine the
3090  * CTE lists of both top_subqry and sub_qry.
3091  *
3092  * Note that we aren't digging into the body of the query looking
3093  * for WITHs in nested sub-SELECTs. A WITH down there can
3094  * legitimately refer to OLD/NEW, because it'd be an
3095  * indirect-correlated outer reference.
3096  */
3097  if (rangeTableEntry_used((Node *) top_subqry->cteList,
3098  PRS2_OLD_VARNO, 0) ||
3099  rangeTableEntry_used((Node *) sub_qry->cteList,
3100  PRS2_OLD_VARNO, 0))
3101  ereport(ERROR,
3102  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3103  errmsg("cannot refer to OLD within WITH query")));
3104  if (rangeTableEntry_used((Node *) top_subqry->cteList,
3105  PRS2_NEW_VARNO, 0) ||
3106  rangeTableEntry_used((Node *) sub_qry->cteList,
3107  PRS2_NEW_VARNO, 0))
3108  ereport(ERROR,
3109  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3110  errmsg("cannot refer to NEW within WITH query")));
3111 
3112  /*
3113  * For efficiency's sake, add OLD to the rule action's jointree
3114  * only if it was actually referenced in the statement or qual.
3115  *
3116  * For INSERT, NEW is not really a relation (only a reference to
3117  * the to-be-inserted tuple) and should never be added to the
3118  * jointree.
3119  *
3120  * For UPDATE, we treat NEW as being another kind of reference to
3121  * OLD, because it represents references to *transformed* tuples
3122  * of the existing relation. It would be wrong to enter NEW
3123  * separately in the jointree, since that would cause a double
3124  * join of the updated relation. It's also wrong to fail to make
3125  * a jointree entry if only NEW and not OLD is mentioned.
3126  */
3127  if (has_old || (has_new && stmt->event == CMD_UPDATE))
3128  {
3129  RangeTblRef *rtr;
3130 
3131  /*
3132  * If sub_qry is a setop, manipulating its jointree will do no
3133  * good at all, because the jointree is dummy. (This should be
3134  * a can't-happen case because of prior tests.)
3135  */
3136  if (sub_qry->setOperations != NULL)
3137  ereport(ERROR,
3138  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3139  errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
3140  /* hackishly add OLD to the already-built FROM clause */
3141  rtr = makeNode(RangeTblRef);
3142  rtr->rtindex = oldnsitem->p_rtindex;
3143  sub_qry->jointree->fromlist =
3144  lappend(sub_qry->jointree->fromlist, rtr);
3145  }
3146 
3147  newactions = lappend(newactions, top_subqry);
3148 
3149  free_parsestate(sub_pstate);
3150  }
3151 
3152  *actions = newactions;
3153  }
3154 
3155  free_parsestate(pstate);
3156 
3157  /* Close relation, but keep the exclusive lock */
3158  table_close(rel, NoLock);
3159 }
#define NIL
Definition: pg_list.h:65
Query * getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr)
Definition: rewriteManip.c:924
FromExpr * makeFromExpr(List *fromlist, Node *quals)
Definition: makefuncs.c:285
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
FromExpr * jointree
Definition: parsenodes.h:138
#define AccessShareLock
Definition: lockdefs.h:36
void addNSItemToQuery(ParseState *pstate, ParseNamespaceItem *nsitem, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
Definition: nodes.h:528
int errcode(int sqlerrcode)
Definition: elog.c:704
List * actions
Definition: parsenodes.h:3020
AclMode requiredPerms
Definition: parsenodes.h:1124
List * fromlist
Definition: primnodes.h:1534
Form_pg_class rd_rel
Definition: rel.h:110
ParseNamespaceItem * addRangeTableEntryForRelation(ParseState *pstate, Relation rel, int lockmode, Alias *alias, bool inh, bool inFromCl)
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:44
#define list_make1(x1)
Definition: pg_list.h:206
void assign_expr_collations(ParseState *pstate, Node *expr)
Alias * makeAlias(const char *aliasname, List *colnames)
Definition: makefuncs.c:387
RangeTblEntry * p_rte
Definition: parse_node.h:257
List * rtable
Definition: parsenodes.h:137
#define ERROR
Definition: elog.h:45
#define NoLock
Definition: lockdefs.h:34
const char * p_sourcetext
Definition: parse_node.h:179
Query * transformStmt(ParseState *pstate, Node *parseTree)
Definition: analyze.c:258
List * lappend(List *list, void *datum)
Definition: list.c:336
#define PRS2_OLD_VARNO
Definition: primnodes.h:178
Relation table_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition: table.c:102
#define ereport(elevel,...)
Definition: elog.h:155
CmdType commandType
Definition: parsenodes.h:112
#define makeNode(_type_)
Definition: nodes.h:576
CmdType event
Definition: parsenodes.h:3018
#define lfirst(lc)
Definition: pg_list.h:169
Node * transformWhereClause(ParseState *pstate, Node *clause, ParseExprKind exprKind, const char *constructName)
Node * whereClause
Definition: parsenodes.h:3017
static int list_length(const List *l)
Definition: pg_list.h:149
#define AccessExclusiveLock
Definition: lockdefs.h:45
List * cteList
Definition: parsenodes.h:135
Node * setOperations
Definition: parsenodes.h:166
int errmsg(const char *fmt,...)
Definition: elog.c:915
#define elog(elevel,...)
Definition: elog.h:228
bool rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
Definition: rewriteManip.c:892
#define copyObject(obj)
Definition: nodes.h:644
RangeVar * relation
Definition: parsenodes.h:3015
void free_parsestate(ParseState *pstate)
Definition: parse_node.c:77
Definition: pg_list.h:50
#define PRS2_NEW_VARNO
Definition: primnodes.h:179
List * p_rtable
Definition: parse_node.h:180