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

1194 {
1195  List *result = NIL;
1196  List *atsubcmds = NIL;
1197  AttrNumber parent_attno;
1198  Relation relation;
1199  Relation childrel;
1200  TupleDesc tupleDesc;
1201  TupleConstr *constr;
1202  AttrMap *attmap;
1203  char *comment;
1204 
1205  /*
1206  * Open the relation referenced by the LIKE clause. We should still have
1207  * the table lock obtained by transformTableLikeClause (and this'll throw
1208  * an assertion failure if not). Hence, no need to recheck privileges
1209  * etc. We must open the rel by OID not name, to be sure we get the same
1210  * table.
1211  */
1212  if (!OidIsValid(table_like_clause->relationOid))
1213  elog(ERROR, "expandTableLikeClause called on untransformed LIKE clause");
1214 
1215  relation = relation_open(table_like_clause->relationOid, NoLock);
1216 
1217  tupleDesc = RelationGetDescr(relation);
1218  constr = tupleDesc->constr;
1219 
1220  /*
1221  * Open the newly-created child relation; we have lock on that too.
1222  */
1223  childrel = relation_openrv(heapRel, NoLock);
1224 
1225  /*
1226  * Construct a map from the LIKE relation's attnos to the child rel's.
1227  * This re-checks type match etc, although it shouldn't be possible to
1228  * have a failure since both tables are locked.
1229  */
1230  attmap = build_attrmap_by_name(RelationGetDescr(childrel),
1231  tupleDesc,
1232  false);
1233 
1234  /*
1235  * Process defaults, if required.
1236  */
1237  if ((table_like_clause->options &
1239  constr != NULL)
1240  {
1241  for (parent_attno = 1; parent_attno <= tupleDesc->natts;
1242  parent_attno++)
1243  {
1244  Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
1245  parent_attno - 1);
1246 
1247  /*
1248  * Ignore dropped columns in the parent.
1249  */
1250  if (attribute->attisdropped)
1251  continue;
1252 
1253  /*
1254  * Copy default, if present and it should be copied. We have
1255  * separate options for plain default expressions and GENERATED
1256  * defaults.
1257  */
1258  if (attribute->atthasdef &&
1259  (attribute->attgenerated ?
1260  (table_like_clause->options & CREATE_TABLE_LIKE_GENERATED) :
1261  (table_like_clause->options & CREATE_TABLE_LIKE_DEFAULTS)))
1262  {
1263  Node *this_default = NULL;
1264  AttrDefault *attrdef = constr->defval;
1265  AlterTableCmd *atsubcmd;
1266  bool found_whole_row;
1267 
1268  /* Find default in constraint structure */
1269  for (int i = 0; i < constr->num_defval; i++)
1270  {
1271  if (attrdef[i].adnum == parent_attno)
1272  {
1273  this_default = stringToNode(attrdef[i].adbin);
1274  break;
1275  }
1276  }
1277  if (this_default == NULL)
1278  elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
1279  parent_attno, RelationGetRelationName(relation));
1280 
1281  atsubcmd = makeNode(AlterTableCmd);
1282  atsubcmd->subtype = AT_CookedColumnDefault;
1283  atsubcmd->num = attmap->attnums[parent_attno - 1];
1284  atsubcmd->def = map_variable_attnos(this_default,
1285  1, 0,
1286  attmap,
1287  InvalidOid,
1288  &found_whole_row);
1289 
1290  /*
1291  * Prevent this for the same reason as for constraints below.
1292  * Note that defaults cannot contain any vars, so it's OK that
1293  * the error message refers to generated columns.
1294  */
1295  if (found_whole_row)
1296  ereport(ERROR,
1297  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1298  errmsg("cannot convert whole-row table reference"),
1299  errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
1300  NameStr(attribute->attname),
1301  RelationGetRelationName(relation))));
1302 
1303  atsubcmds = lappend(atsubcmds, atsubcmd);
1304  }
1305  }
1306  }
1307 
1308  /*
1309  * Copy CHECK constraints if requested, being careful to adjust attribute
1310  * numbers so they match the child.
1311  */
1312  if ((table_like_clause->options & CREATE_TABLE_LIKE_CONSTRAINTS) &&
1313  constr != NULL)
1314  {
1315  int ccnum;
1316 
1317  for (ccnum = 0; ccnum < constr->num_check; ccnum++)
1318  {
1319  char *ccname = constr->check[ccnum].ccname;
1320  char *ccbin = constr->check[ccnum].ccbin;
1321  bool ccnoinherit = constr->check[ccnum].ccnoinherit;
1322  Node *ccbin_node;
1323  bool found_whole_row;
1324  Constraint *n;
1325  AlterTableCmd *atsubcmd;
1326 
1327  ccbin_node = map_variable_attnos(stringToNode(ccbin),
1328  1, 0,
1329  attmap,
1330  InvalidOid, &found_whole_row);
1331 
1332  /*
1333  * We reject whole-row variables because the whole point of LIKE
1334  * is that the new table's rowtype might later diverge from the
1335  * parent's. So, while translation might be possible right now,
1336  * it wouldn't be possible to guarantee it would work in future.
1337  */
1338  if (found_whole_row)
1339  ereport(ERROR,
1340  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1341  errmsg("cannot convert whole-row table reference"),
1342  errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
1343  ccname,
1344  RelationGetRelationName(relation))));
1345 
1346  n = makeNode(Constraint);
1347  n->contype = CONSTR_CHECK;
1348  n->conname = pstrdup(ccname);
1349  n->location = -1;
1350  n->is_no_inherit = ccnoinherit;
1351  n->raw_expr = NULL;
1352  n->cooked_expr = nodeToString(ccbin_node);
1353 
1354  /* We can skip validation, since the new table should be empty. */
1355  n->skip_validation = true;
1356  n->initially_valid = true;
1357 
1358  atsubcmd = makeNode(AlterTableCmd);
1359  atsubcmd->subtype = AT_AddConstraint;
1360  atsubcmd->def = (Node *) n;
1361  atsubcmds = lappend(atsubcmds, atsubcmd);
1362 
1363  /* Copy comment on constraint */
1364  if ((table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS) &&
1366  n->conname, false),
1367  ConstraintRelationId,
1368  0)) != NULL)
1369  {
1371 
1372  stmt->objtype = OBJECT_TABCONSTRAINT;
1373  stmt->object = (Node *) list_make3(makeString(heapRel->schemaname),
1374  makeString(heapRel->relname),
1375  makeString(n->conname));
1376  stmt->comment = comment;
1377 
1378  result = lappend(result, stmt);
1379  }
1380  }
1381  }
1382 
1383  /*
1384  * If we generated any ALTER TABLE actions above, wrap them into a single
1385  * ALTER TABLE command. Stick it at the front of the result, so it runs
1386  * before any CommentStmts we made above.
1387  */
1388  if (atsubcmds)
1389  {
1391 
1392  atcmd->relation = copyObject(heapRel);
1393  atcmd->cmds = atsubcmds;
1394  atcmd->objtype = OBJECT_TABLE;
1395  atcmd->missing_ok = false;
1396  result = lcons(atcmd, result);
1397  }
1398 
1399  /*
1400  * Process indexes if required.
1401  */
1402  if ((table_like_clause->options & CREATE_TABLE_LIKE_INDEXES) &&
1403  relation->rd_rel->relhasindex)
1404  {
1405  List *parent_indexes;
1406  ListCell *l;
1407 
1408  parent_indexes = RelationGetIndexList(relation);
1409 
1410  foreach(l, parent_indexes)
1411  {
1412  Oid parent_index_oid = lfirst_oid(l);
1413  Relation parent_index;
1414  IndexStmt *index_stmt;
1415 
1416  parent_index = index_open(parent_index_oid, AccessShareLock);
1417 
1418  /* Build CREATE INDEX statement to recreate the parent_index */
1419  index_stmt = generateClonedIndexStmt(heapRel,
1420  parent_index,
1421  attmap,
1422  NULL);
1423 
1424  /* Copy comment on index, if requested */
1425  if (table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS)
1426  {
1427  comment = GetComment(parent_index_oid, RelationRelationId, 0);
1428 
1429  /*
1430  * We make use of IndexStmt's idxcomment option, so as not to
1431  * need to know now what name the index will have.
1432  */
1433  index_stmt->idxcomment = comment;
1434  }
1435 
1436  result = lappend(result, index_stmt);
1437 
1438  index_close(parent_index, AccessShareLock);
1439  }
1440  }
1441 
1442  /* Done with child rel */
1443  table_close(childrel, NoLock);
1444 
1445  /*
1446  * Close the parent rel, but keep our AccessShareLock on it until xact
1447  * commit. That will prevent someone else from deleting or ALTERing the
1448  * parent before the child is committed.
1449  */
1450  table_close(relation, NoLock);
1451 
1452  return result;
1453 }
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:178
int16 AttrNumber
Definition: attnum.h:21
#define NameStr(name)
Definition: c.h:730
#define OidIsValid(objectId)
Definition: c.h:759
char * GetComment(Oid oid, Oid classoid, int32 subid)
Definition: comment.c:410
int errdetail(const char *fmt,...)
Definition: elog.c:1202
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define ERROR
Definition: elog.h:39
#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:158
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:132
int i
Definition: isn.c:73
List * lappend(List *list, void *datum)
Definition: list.c:338
List * lcons(void *datum, List *list)
Definition: list.c:494
#define NoLock
Definition: lockdefs.h:34
#define AccessShareLock
Definition: lockdefs.h:36
char * pstrdup(const char *in)
Definition: mcxt.c:1644
#define copyObject(obj)
Definition: nodes.h:244
#define makeNode(_type_)
Definition: nodes.h:176
char * nodeToString(const void *obj)
Definition: outfuncs.c:877
IndexStmt * generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx, const AttrMap *attmap, Oid *constraintOid)
@ CONSTR_CHECK
Definition: parsenodes.h:2528
@ OBJECT_TABLE
Definition: parsenodes.h:2123
@ OBJECT_TABCONSTRAINT
Definition: parsenodes.h:2122
@ AT_AddConstraint
Definition: parsenodes.h:2190
@ AT_CookedColumnDefault
Definition: parsenodes.h:2177
@ CREATE_TABLE_LIKE_COMMENTS
Definition: parsenodes.h:756
@ CREATE_TABLE_LIKE_GENERATED
Definition: parsenodes.h:760
@ CREATE_TABLE_LIKE_INDEXES
Definition: parsenodes.h:762
@ CREATE_TABLE_LIKE_DEFAULTS
Definition: parsenodes.h:759
@ CREATE_TABLE_LIKE_CONSTRAINTS
Definition: parsenodes.h:758
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:504
#define RelationGetDescr(relation)
Definition: rel.h:530
#define RelationGetRelationName(relation)
Definition: rel.h:538
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4739
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:138
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:48
AlterTableType subtype
Definition: parsenodes.h:2252
RangeVar * relation
Definition: parsenodes.h:2166
ObjectType objtype
Definition: parsenodes.h:2168
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
ConstrType contype
Definition: parsenodes.h:2556
bool is_no_inherit
Definition: parsenodes.h:2565
char * cooked_expr
Definition: parsenodes.h:2567
bool initially_valid
Definition: parsenodes.h:2604
bool skip_validation
Definition: parsenodes.h:2603
Node * raw_expr
Definition: parsenodes.h:2566
char * conname
Definition: parsenodes.h:2559
char * idxcomment
Definition: parsenodes.h:3193
Definition: pg_list.h:54
Definition: nodes.h:129
char * relname
Definition: primnodes.h:74
char * schemaname
Definition: primnodes.h:71
Form_pg_class rd_rel
Definition: rel.h:111
AttrDefault * defval
Definition: tupdesc.h:39
ConstrCheck * check
Definition: tupdesc.h:40
uint16 num_defval
Definition: tupdesc.h:42
uint16 num_check
Definition: tupdesc.h:43
TupleConstr * constr
Definition: tupdesc.h:85
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
#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, 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, 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, TableLikeClause::relationOid, RangeVar::relname, RangeVar::schemaname, Constraint::skip_validation, stmt, stringToNode(), AlterTableCmd::subtype, table_close(), and TupleDescAttr.

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

3283 {
3284  Relation rel;
3285  TupleDesc tupdesc;
3286  ParseState *pstate;
3287  CreateStmtContext cxt;
3288  List *save_alist;
3289  ListCell *lcmd,
3290  *l;
3291  List *newcmds = NIL;
3292  bool skipValidation = true;
3293  AlterTableCmd *newcmd;
3294  ParseNamespaceItem *nsitem;
3295 
3296  /* Caller is responsible for locking the relation */
3297  rel = relation_open(relid, NoLock);
3298  tupdesc = RelationGetDescr(rel);
3299 
3300  /* Set up pstate */
3301  pstate = make_parsestate(NULL);
3302  pstate->p_sourcetext = queryString;
3303  nsitem = addRangeTableEntryForRelation(pstate,
3304  rel,
3306  NULL,
3307  false,
3308  true);
3309  addNSItemToQuery(pstate, nsitem, false, true, true);
3310 
3311  /* Set up CreateStmtContext */
3312  cxt.pstate = pstate;
3313  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
3314  {
3315  cxt.stmtType = "ALTER FOREIGN TABLE";
3316  cxt.isforeign = true;
3317  }
3318  else
3319  {
3320  cxt.stmtType = "ALTER TABLE";
3321  cxt.isforeign = false;
3322  }
3323  cxt.relation = stmt->relation;
3324  cxt.rel = rel;
3325  cxt.inhRelations = NIL;
3326  cxt.isalter = true;
3327  cxt.columns = NIL;
3328  cxt.ckconstraints = NIL;
3329  cxt.fkconstraints = NIL;
3330  cxt.ixconstraints = NIL;
3331  cxt.likeclauses = NIL;
3332  cxt.extstats = NIL;
3333  cxt.blist = NIL;
3334  cxt.alist = NIL;
3335  cxt.pkey = NULL;
3336  cxt.ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
3337  cxt.partbound = NULL;
3338  cxt.ofType = false;
3339 
3340  /*
3341  * Transform ALTER subcommands that need it (most don't). These largely
3342  * re-use code from CREATE TABLE.
3343  */
3344  foreach(lcmd, stmt->cmds)
3345  {
3346  AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
3347 
3348  switch (cmd->subtype)
3349  {
3350  case AT_AddColumn:
3351  {
3352  ColumnDef *def = castNode(ColumnDef, cmd->def);
3353 
3354  transformColumnDefinition(&cxt, def);
3355 
3356  /*
3357  * If the column has a non-null default, we can't skip
3358  * validation of foreign keys.
3359  */
3360  if (def->raw_default != NULL)
3361  skipValidation = false;
3362 
3363  /*
3364  * All constraints are processed in other ways. Remove the
3365  * original list
3366  */
3367  def->constraints = NIL;
3368 
3369  newcmds = lappend(newcmds, cmd);
3370  break;
3371  }
3372 
3373  case AT_AddConstraint:
3374 
3375  /*
3376  * The original AddConstraint cmd node doesn't go to newcmds
3377  */
3378  if (IsA(cmd->def, Constraint))
3379  {
3380  transformTableConstraint(&cxt, (Constraint *) cmd->def);
3381  if (((Constraint *) cmd->def)->contype == CONSTR_FOREIGN)
3382  skipValidation = false;
3383  }
3384  else
3385  elog(ERROR, "unrecognized node type: %d",
3386  (int) nodeTag(cmd->def));
3387  break;
3388 
3389  case AT_AlterColumnType:
3390  {
3391  ColumnDef *def = castNode(ColumnDef, cmd->def);
3393 
3394  /*
3395  * For ALTER COLUMN TYPE, transform the USING clause if
3396  * one was specified.
3397  */
3398  if (def->raw_default)
3399  {
3400  def->cooked_default =
3401  transformExpr(pstate, def->raw_default,
3403  }
3404 
3405  /*
3406  * For identity column, create ALTER SEQUENCE command to
3407  * change the data type of the sequence.
3408  */
3409  attnum = get_attnum(relid, cmd->name);
3410  if (attnum == InvalidAttrNumber)
3411  ereport(ERROR,
3412  (errcode(ERRCODE_UNDEFINED_COLUMN),
3413  errmsg("column \"%s\" of relation \"%s\" does not exist",
3414  cmd->name, RelationGetRelationName(rel))));
3415 
3416  if (attnum > 0 &&
3417  TupleDescAttr(tupdesc, attnum - 1)->attidentity)
3418  {
3419  Oid seq_relid = getIdentitySequence(relid, attnum, false);
3420  Oid typeOid = typenameTypeId(pstate, def->typeName);
3421  AlterSeqStmt *altseqstmt = makeNode(AlterSeqStmt);
3422 
3423  altseqstmt->sequence = makeRangeVar(get_namespace_name(get_rel_namespace(seq_relid)),
3424  get_rel_name(seq_relid),
3425  -1);
3426  altseqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(typeOid, -1), -1));
3427  altseqstmt->for_identity = true;
3428  cxt.blist = lappend(cxt.blist, altseqstmt);
3429  }
3430 
3431  newcmds = lappend(newcmds, cmd);
3432  break;
3433  }
3434 
3435  case AT_AddIdentity:
3436  {
3437  Constraint *def = castNode(Constraint, cmd->def);
3438  ColumnDef *newdef = makeNode(ColumnDef);
3440 
3441  newdef->colname = cmd->name;
3442  newdef->identity = def->generated_when;
3443  cmd->def = (Node *) newdef;
3444 
3445  attnum = get_attnum(relid, cmd->name);
3446  if (attnum == InvalidAttrNumber)
3447  ereport(ERROR,
3448  (errcode(ERRCODE_UNDEFINED_COLUMN),
3449  errmsg("column \"%s\" of relation \"%s\" does not exist",
3450  cmd->name, RelationGetRelationName(rel))));
3451 
3452  generateSerialExtraStmts(&cxt, newdef,
3453  get_atttype(relid, attnum),
3454  def->options, true, true,
3455  NULL, NULL);
3456 
3457  newcmds = lappend(newcmds, cmd);
3458  break;
3459  }
3460 
3461  case AT_SetIdentity:
3462  {
3463  /*
3464  * Create an ALTER SEQUENCE statement for the internal
3465  * sequence of the identity column.
3466  */
3467  ListCell *lc;
3468  List *newseqopts = NIL;
3469  List *newdef = NIL;
3471  Oid seq_relid;
3472 
3473  /*
3474  * Split options into those handled by ALTER SEQUENCE and
3475  * those for ALTER TABLE proper.
3476  */
3477  foreach(lc, castNode(List, cmd->def))
3478  {
3479  DefElem *def = lfirst_node(DefElem, lc);
3480 
3481  if (strcmp(def->defname, "generated") == 0)
3482  newdef = lappend(newdef, def);
3483  else
3484  newseqopts = lappend(newseqopts, def);
3485  }
3486 
3487  attnum = get_attnum(relid, cmd->name);
3488  if (attnum == InvalidAttrNumber)
3489  ereport(ERROR,
3490  (errcode(ERRCODE_UNDEFINED_COLUMN),
3491  errmsg("column \"%s\" of relation \"%s\" does not exist",
3492  cmd->name, RelationGetRelationName(rel))));
3493 
3494  seq_relid = getIdentitySequence(relid, attnum, true);
3495 
3496  if (seq_relid)
3497  {
3498  AlterSeqStmt *seqstmt;
3499 
3500  seqstmt = makeNode(AlterSeqStmt);
3502  get_rel_name(seq_relid), -1);
3503  seqstmt->options = newseqopts;
3504  seqstmt->for_identity = true;
3505  seqstmt->missing_ok = false;
3506 
3507  cxt.blist = lappend(cxt.blist, seqstmt);
3508  }
3509 
3510  /*
3511  * If column was not an identity column, we just let the
3512  * ALTER TABLE command error out later. (There are cases
3513  * this fails to cover, but we'll need to restructure
3514  * where creation of the sequence dependency linkage
3515  * happens before we can fix it.)
3516  */
3517 
3518  cmd->def = (Node *) newdef;
3519  newcmds = lappend(newcmds, cmd);
3520  break;
3521  }
3522 
3523  case AT_AttachPartition:
3524  case AT_DetachPartition:
3525  {
3526  PartitionCmd *partcmd = (PartitionCmd *) cmd->def;
3527 
3528  transformPartitionCmd(&cxt, partcmd);
3529  /* assign transformed value of the partition bound */
3530  partcmd->bound = cxt.partbound;
3531  }
3532 
3533  newcmds = lappend(newcmds, cmd);
3534  break;
3535 
3536  default:
3537 
3538  /*
3539  * Currently, we shouldn't actually get here for subcommand
3540  * types that don't require transformation; but if we do, just
3541  * emit them unchanged.
3542  */
3543  newcmds = lappend(newcmds, cmd);
3544  break;
3545  }
3546  }
3547 
3548  /*
3549  * Transfer anything we already have in cxt.alist into save_alist, to keep
3550  * it separate from the output of transformIndexConstraints.
3551  */
3552  save_alist = cxt.alist;
3553  cxt.alist = NIL;
3554 
3555  /* Postprocess constraints */
3557  transformFKConstraints(&cxt, skipValidation, true);
3558  transformCheckConstraints(&cxt, false);
3559 
3560  /*
3561  * Push any index-creation commands into the ALTER, so that they can be
3562  * scheduled nicely by tablecmds.c. Note that tablecmds.c assumes that
3563  * the IndexStmt attached to an AT_AddIndex or AT_AddIndexConstraint
3564  * subcommand has already been through transformIndexStmt.
3565  */
3566  foreach(l, cxt.alist)
3567  {
3568  Node *istmt = (Node *) lfirst(l);
3569 
3570  /*
3571  * We assume here that cxt.alist contains only IndexStmts and possibly
3572  * ALTER TABLE SET NOT NULL statements generated from primary key
3573  * constraints. We absorb the subcommands of the latter directly.
3574  */
3575  if (IsA(istmt, IndexStmt))
3576  {
3577  IndexStmt *idxstmt = (IndexStmt *) istmt;
3578 
3579  idxstmt = transformIndexStmt(relid, idxstmt, queryString);
3580  newcmd = makeNode(AlterTableCmd);
3582  newcmd->def = (Node *) idxstmt;
3583  newcmds = lappend(newcmds, newcmd);
3584  }
3585  else if (IsA(istmt, AlterTableStmt))
3586  {
3587  AlterTableStmt *alterstmt = (AlterTableStmt *) istmt;
3588 
3589  newcmds = list_concat(newcmds, alterstmt->cmds);
3590  }
3591  else
3592  elog(ERROR, "unexpected stmt type %d", (int) nodeTag(istmt));
3593  }
3594  cxt.alist = NIL;
3595 
3596  /* Append any CHECK or FK constraints to the commands list */
3597  foreach(l, cxt.ckconstraints)
3598  {
3599  newcmd = makeNode(AlterTableCmd);
3600  newcmd->subtype = AT_AddConstraint;
3601  newcmd->def = (Node *) lfirst(l);
3602  newcmds = lappend(newcmds, newcmd);
3603  }
3604  foreach(l, cxt.fkconstraints)
3605  {
3606  newcmd = makeNode(AlterTableCmd);
3607  newcmd->subtype = AT_AddConstraint;
3608  newcmd->def = (Node *) lfirst(l);
3609  newcmds = lappend(newcmds, newcmd);
3610  }
3611 
3612  /* Append extended statistics objects */
3614 
3615  /* Close rel */
3616  relation_close(rel, NoLock);
3617 
3618  /*
3619  * Output results.
3620  */
3621  stmt->cmds = newcmds;
3622 
3623  *beforeStmts = cxt.blist;
3624  *afterStmts = list_concat(cxt.alist, save_alist);
3625 
3626  return stmt;
3627 }
#define InvalidAttrNumber
Definition: attnum.h:23
List * list_concat(List *list1, const List *list2)
Definition: list.c:560
AttrNumber get_attnum(Oid relid, const char *attname)
Definition: lsyscache.c:857
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3324
Oid get_rel_namespace(Oid relid)
Definition: lsyscache.c:1934
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1910
Oid get_atttype(Oid relid, AttrNumber attnum)
Definition: lsyscache.c:939
RangeVar * makeRangeVar(char *schemaname, char *relname, int location)
Definition: makefuncs.c:425
DefElem * makeDefElem(char *name, Node *arg, int location)
Definition: makefuncs.c:549
TypeName * makeTypeNameFromOid(Oid typeOid, int32 typmod)
Definition: makefuncs.c:475
#define IsA(nodeptr, _type_)
Definition: nodes.h:179
#define nodeTag(nodeptr)
Definition: nodes.h:133
#define castNode(_type_, nodeptr)
Definition: nodes.h:197
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:106
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:44
@ EXPR_KIND_ALTER_COL_TRANSFORM
Definition: parse_node.h:74
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 transformExtendedStatistics(CreateStmtContext *cxt)
static void transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
IndexStmt * transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
static void transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd)
static void transformCheckConstraints(CreateStmtContext *cxt, bool skipValidation)
static void transformFKConstraints(CreateStmtContext *cxt, bool skipValidation, bool isAddConstraint)
@ CONSTR_FOREIGN
Definition: parsenodes.h:2532
@ AT_AddIndexConstraint
Definition: parsenodes.h:2195
@ AT_SetIdentity
Definition: parsenodes.h:2237
@ AT_AddIndex
Definition: parsenodes.h:2188
@ AT_AddIdentity
Definition: parsenodes.h:2236
@ AT_AlterColumnType
Definition: parsenodes.h:2198
@ AT_DetachPartition
Definition: parsenodes.h:2234
@ AT_AttachPartition
Definition: parsenodes.h:2233
@ AT_AddColumn
Definition: parsenodes.h:2174
int16 attnum
Definition: pg_attribute.h:74
Oid getIdentitySequence(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: pg_depend.c:944
#define lfirst(lc)
Definition: pg_list.h:172
#define lfirst_node(type, lc)
Definition: pg_list.h:176
#define list_make1(x1)
Definition: pg_list.h:212
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
List * options
Definition: parsenodes.h:2963
RangeVar * sequence
Definition: parsenodes.h:2962
bool for_identity
Definition: parsenodes.h:2964
char identity
Definition: parsenodes.h:732
List * constraints
Definition: parsenodes.h:738
Node * cooked_default
Definition: parsenodes.h:731
char * colname
Definition: parsenodes.h:721
TypeName * typeName
Definition: parsenodes.h:722
Node * raw_default
Definition: parsenodes.h:730
List * options
Definition: parsenodes.h:2581
char generated_when
Definition: parsenodes.h:2568
IndexStmt * pkey
Definition: parse_utilcmd.c:92
const char * stmtType
Definition: parse_utilcmd.c:76
RangeVar * relation
Definition: parse_utilcmd.c:77
ParseState * pstate
Definition: parse_utilcmd.c:75
PartitionBoundSpec * partbound
Definition: parse_utilcmd.c:94
char * defname
Definition: parsenodes.h:810
Oid indexOid
Definition: parsenodes.h:3194
const char * p_sourcetext
Definition: parse_node.h:192
PartitionBoundSpec * bound
Definition: parsenodes.h:942

References AccessShareLock, addNSItemToQuery(), addRangeTableEntryForRelation(), CreateStmtContext::alist, AT_AddColumn, AT_AddConstraint, AT_AddIdentity, AT_AddIndex, AT_AddIndexConstraint, AT_AlterColumnType, AT_AttachPartition, AT_DetachPartition, AT_SetIdentity, attnum, CreateStmtContext::blist, PartitionCmd::bound, castNode, CreateStmtContext::ckconstraints, 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::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, relation_close(), relation_open(), RelationGetDescr, RelationGetRelationName, AlterSeqStmt::sequence, stmt, CreateStmtContext::stmtType, AlterTableCmd::subtype, transformCheckConstraints(), transformColumnDefinition(), transformExpr(), transformExtendedStatistics(), transformFKConstraints(), transformIndexConstraints(), transformIndexStmt(), transformPartitionCmd(), transformTableConstraint(), TupleDescAttr, ColumnDef::typeName, and typenameTypeId().

Referenced by ATParseTransformCmd(), and ATPostAlterTypeParse().

◆ transformCreateSchemaStmtElements()

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

Definition at line 3812 of file parse_utilcmd.c.

3813 {
3815  List *result;
3816  ListCell *elements;
3817 
3818  cxt.schemaname = schemaName;
3819  cxt.sequences = NIL;
3820  cxt.tables = NIL;
3821  cxt.views = NIL;
3822  cxt.indexes = NIL;
3823  cxt.triggers = NIL;
3824  cxt.grants = NIL;
3825 
3826  /*
3827  * Run through each schema element in the schema element list. Separate
3828  * statements by type, and do preliminary analysis.
3829  */
3830  foreach(elements, schemaElts)
3831  {
3832  Node *element = lfirst(elements);
3833 
3834  switch (nodeTag(element))
3835  {
3836  case T_CreateSeqStmt:
3837  {
3838  CreateSeqStmt *elp = (CreateSeqStmt *) element;
3839 
3841  cxt.sequences = lappend(cxt.sequences, element);
3842  }
3843  break;
3844 
3845  case T_CreateStmt:
3846  {
3847  CreateStmt *elp = (CreateStmt *) element;
3848 
3850 
3851  /*
3852  * XXX todo: deal with constraints
3853  */
3854  cxt.tables = lappend(cxt.tables, element);
3855  }
3856  break;
3857 
3858  case T_ViewStmt:
3859  {
3860  ViewStmt *elp = (ViewStmt *) element;
3861 
3862  setSchemaName(cxt.schemaname, &elp->view->schemaname);
3863 
3864  /*
3865  * XXX todo: deal with references between views
3866  */
3867  cxt.views = lappend(cxt.views, element);
3868  }
3869  break;
3870 
3871  case T_IndexStmt:
3872  {
3873  IndexStmt *elp = (IndexStmt *) element;
3874 
3876  cxt.indexes = lappend(cxt.indexes, element);
3877  }
3878  break;
3879 
3880  case T_CreateTrigStmt:
3881  {
3883 
3885  cxt.triggers = lappend(cxt.triggers, element);
3886  }
3887  break;
3888 
3889  case T_GrantStmt:
3890  cxt.grants = lappend(cxt.grants, element);
3891  break;
3892 
3893  default:
3894  elog(ERROR, "unrecognized node type: %d",
3895  (int) nodeTag(element));
3896  }
3897  }
3898 
3899  result = NIL;
3900  result = list_concat(result, cxt.sequences);
3901  result = list_concat(result, cxt.tables);
3902  result = list_concat(result, cxt.views);
3903  result = list_concat(result, cxt.indexes);
3904  result = list_concat(result, cxt.triggers);
3905  result = list_concat(result, cxt.grants);
3906 
3907  return result;
3908 }
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:2952
RangeVar * relation
Definition: parsenodes.h:2474
RangeVar * relation
Definition: parsenodes.h:2839
RangeVar * relation
Definition: parsenodes.h:3184
RangeVar * view
Definition: parsenodes.h:3570

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

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

References CreateStmtContext::alist, Assert(), CreateStmtContext::blist, cancel_parser_errposition_callback(), checkMembershipInCurrentExtension(), CreateStmtContext::ckconstraints, CreateStmtContext::columns, element(), elog(), ereport, errcode(), errmsg(), ERROR, CreateStmtContext::extstats, 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(), transformExtendedStatistics(), transformFKConstraints(), transformIndexConstraints(), transformOfType(), transformTableConstraint(), and transformTableLikeClause().

Referenced by ProcessUtilitySlow().

◆ transformIndexStmt()

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

Definition at line 2804 of file parse_utilcmd.c.

2805 {
2806  ParseState *pstate;
2807  ParseNamespaceItem *nsitem;
2808  ListCell *l;
2809  Relation rel;
2810 
2811  /* Nothing to do if statement already transformed. */
2812  if (stmt->transformed)
2813  return stmt;
2814 
2815  /* Set up pstate */
2816  pstate = make_parsestate(NULL);
2817  pstate->p_sourcetext = queryString;
2818 
2819  /*
2820  * Put the parent table into the rtable so that the expressions can refer
2821  * to its fields without qualification. Caller is responsible for locking
2822  * relation, but we still need to open it.
2823  */
2824  rel = relation_open(relid, NoLock);
2825  nsitem = addRangeTableEntryForRelation(pstate, rel,
2827  NULL, false, true);
2828 
2829  /* no to join list, yes to namespaces */
2830  addNSItemToQuery(pstate, nsitem, false, true, true);
2831 
2832  /* take care of the where clause */
2833  if (stmt->whereClause)
2834  {
2835  stmt->whereClause = transformWhereClause(pstate,
2836  stmt->whereClause,
2838  "WHERE");
2839  /* we have to fix its collations too */
2840  assign_expr_collations(pstate, stmt->whereClause);
2841  }
2842 
2843  /* take care of any index expressions */
2844  foreach(l, stmt->indexParams)
2845  {
2846  IndexElem *ielem = (IndexElem *) lfirst(l);
2847 
2848  if (ielem->expr)
2849  {
2850  /* Extract preliminary index col name before transforming expr */
2851  if (ielem->indexcolname == NULL)
2852  ielem->indexcolname = FigureIndexColname(ielem->expr);
2853 
2854  /* Now do parse transformation of the expression */
2855  ielem->expr = transformExpr(pstate, ielem->expr,
2857 
2858  /* We have to fix its collations too */
2859  assign_expr_collations(pstate, ielem->expr);
2860 
2861  /*
2862  * transformExpr() should have already rejected subqueries,
2863  * aggregates, window functions, and SRFs, based on the EXPR_KIND_
2864  * for an index expression.
2865  *
2866  * DefineIndex() will make more checks.
2867  */
2868  }
2869  }
2870 
2871  /*
2872  * Check that only the base rel is mentioned. (This should be dead code
2873  * now that add_missing_from is history.)
2874  */
2875  if (list_length(pstate->p_rtable) != 1)
2876  ereport(ERROR,
2877  (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
2878  errmsg("index expressions and predicates can refer only to the table being indexed")));
2879 
2880  free_parsestate(pstate);
2881 
2882  /* Close relation */
2883  table_close(rel, NoLock);
2884 
2885  /* Mark statement as successfully transformed */
2886  stmt->transformed = true;
2887 
2888  return stmt;
2889 }
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:77
@ EXPR_KIND_INDEX_EXPRESSION
Definition: parse_node.h:71
@ EXPR_KIND_INDEX_PREDICATE
Definition: parse_node.h:72
char * FigureIndexColname(Node *node)
static int list_length(const List *l)
Definition: pg_list.h:152
Node * expr
Definition: parsenodes.h:779
char * indexcolname
Definition: parsenodes.h:780
List * p_rtable
Definition: parse_node.h:193

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

3990 {
3991  PartitionBoundSpec *result_spec;
3993  char strategy = get_partition_strategy(key);
3994  int partnatts = get_partition_natts(key);
3995  List *partexprs = get_partition_exprs(key);
3996 
3997  /* Avoid scribbling on input */
3998  result_spec = copyObject(spec);
3999 
4000  if (spec->is_default)
4001  {
4002  /*
4003  * Hash partitioning does not support a default partition; there's no
4004  * use case for it (since the set of partitions to create is perfectly
4005  * defined), and if users do get into it accidentally, it's hard to
4006  * back out from it afterwards.
4007  */
4008  if (strategy == PARTITION_STRATEGY_HASH)
4009  ereport(ERROR,
4010  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4011  errmsg("a hash-partitioned table may not have a default partition")));
4012 
4013  /*
4014  * In case of the default partition, parser had no way to identify the
4015  * partition strategy. Assign the parent's strategy to the default
4016  * partition bound spec.
4017  */
4018  result_spec->strategy = strategy;
4019 
4020  return result_spec;
4021  }
4022 
4023  if (strategy == PARTITION_STRATEGY_HASH)
4024  {
4025  if (spec->strategy != PARTITION_STRATEGY_HASH)
4026  ereport(ERROR,
4027  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4028  errmsg("invalid bound specification for a hash partition"),
4029  parser_errposition(pstate, exprLocation((Node *) spec))));
4030 
4031  if (spec->modulus <= 0)
4032  ereport(ERROR,
4033  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4034  errmsg("modulus for hash partition must be an integer value greater than zero")));
4035 
4036  Assert(spec->remainder >= 0);
4037 
4038  if (spec->remainder >= spec->modulus)
4039  ereport(ERROR,
4040  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4041  errmsg("remainder for hash partition must be less than modulus")));
4042  }
4043  else if (strategy == PARTITION_STRATEGY_LIST)
4044  {
4045  ListCell *cell;
4046  char *colname;
4047  Oid coltype;
4048  int32 coltypmod;
4049  Oid partcollation;
4050 
4051  if (spec->strategy != PARTITION_STRATEGY_LIST)
4052  ereport(ERROR,
4053  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4054  errmsg("invalid bound specification for a list partition"),
4055  parser_errposition(pstate, exprLocation((Node *) spec))));
4056 
4057  /* Get the only column's name in case we need to output an error */
4058  if (key->partattrs[0] != 0)
4059  colname = get_attname(RelationGetRelid(parent),
4060  key->partattrs[0], false);
4061  else
4062  colname = deparse_expression((Node *) linitial(partexprs),
4064  RelationGetRelid(parent)),
4065  false, false);
4066  /* Need its type data too */
4067  coltype = get_partition_col_typid(key, 0);
4068  coltypmod = get_partition_col_typmod(key, 0);
4069  partcollation = get_partition_col_collation(key, 0);
4070 
4071  result_spec->listdatums = NIL;
4072  foreach(cell, spec->listdatums)
4073  {
4074  Node *expr = lfirst(cell);
4075  Const *value;
4076  ListCell *cell2;
4077  bool duplicate;
4078 
4079  value = transformPartitionBoundValue(pstate, expr,
4080  colname, coltype, coltypmod,
4081  partcollation);
4082 
4083  /* Don't add to the result if the value is a duplicate */
4084  duplicate = false;
4085  foreach(cell2, result_spec->listdatums)
4086  {
4087  Const *value2 = lfirst_node(Const, cell2);
4088 
4089  if (equal(value, value2))
4090  {
4091  duplicate = true;
4092  break;
4093  }
4094  }
4095  if (duplicate)
4096  continue;
4097 
4098  result_spec->listdatums = lappend(result_spec->listdatums,
4099  value);
4100  }
4101  }
4102  else if (strategy == PARTITION_STRATEGY_RANGE)
4103  {
4104  if (spec->strategy != PARTITION_STRATEGY_RANGE)
4105  ereport(ERROR,
4106  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4107  errmsg("invalid bound specification for a range partition"),
4108  parser_errposition(pstate, exprLocation((Node *) spec))));
4109 
4110  if (list_length(spec->lowerdatums) != partnatts)
4111  ereport(ERROR,
4112  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4113  errmsg("FROM must specify exactly one value per partitioning column")));
4114  if (list_length(spec->upperdatums) != partnatts)
4115  ereport(ERROR,
4116  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4117  errmsg("TO must specify exactly one value per partitioning column")));
4118 
4119  /*
4120  * Convert raw parse nodes into PartitionRangeDatum nodes and perform
4121  * any necessary validation.
4122  */
4123  result_spec->lowerdatums =
4125  parent);
4126  result_spec->upperdatums =
4128  parent);
4129  }
4130  else
4131  elog(ERROR, "unexpected partition strategy: %d", (int) strategy);
4132 
4133  return result_spec;
4134 }
signed int int32
Definition: c.h:478
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
static struct @147 value
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:826
int exprLocation(const Node *expr)
Definition: nodeFuncs.c:1314
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:111
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:869
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:867
@ PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:868
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:54
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:3586
List * deparse_context_for(const char *aliasname, Oid relid)
Definition: ruleutils.c:3646

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

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

2900 {
2901  ParseState *pstate;
2902  ParseNamespaceItem *nsitem;
2903  ListCell *l;
2904  Relation rel;
2905 
2906  /* Nothing to do if statement already transformed. */
2907  if (stmt->transformed)
2908  return stmt;
2909 
2910  /* Set up pstate */
2911  pstate = make_parsestate(NULL);
2912  pstate->p_sourcetext = queryString;
2913 
2914  /*
2915  * Put the parent table into the rtable so that the expressions can refer
2916  * to its fields without qualification. Caller is responsible for locking
2917  * relation, but we still need to open it.
2918  */
2919  rel = relation_open(relid, NoLock);
2920  nsitem = addRangeTableEntryForRelation(pstate, rel,
2922  NULL, false, true);
2923 
2924  /* no to join list, yes to namespaces */
2925  addNSItemToQuery(pstate, nsitem, false, true, true);
2926 
2927  /* take care of any expressions */
2928  foreach(l, stmt->exprs)
2929  {
2930  StatsElem *selem = (StatsElem *) lfirst(l);
2931 
2932  if (selem->expr)
2933  {
2934  /* Now do parse transformation of the expression */
2935  selem->expr = transformExpr(pstate, selem->expr,
2937 
2938  /* We have to fix its collations too */
2939  assign_expr_collations(pstate, selem->expr);
2940  }
2941  }
2942 
2943  /*
2944  * Check that only the base rel is mentioned. (This should be dead code
2945  * now that add_missing_from is history.)
2946  */
2947  if (list_length(pstate->p_rtable) != 1)
2948  ereport(ERROR,
2949  (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
2950  errmsg("statistics expressions can refer only to the table being referenced")));
2951 
2952  free_parsestate(pstate);
2953 
2954  /* Close relation */
2955  table_close(rel, NoLock);
2956 
2957  /* Mark statement as successfully transformed */
2958  stmt->transformed = true;
2959 
2960  return stmt;
2961 }
@ EXPR_KIND_STATS_EXPRESSION
Definition: parse_node.h:73
Node * expr
Definition: parsenodes.h:3239

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