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

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

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

Referenced by ProcessUtilitySlow().

◆ generateClonedIndexStmt()

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

◆ transformAlterTableStmt()

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

Definition at line 3515 of file parse_utilcmd.c.

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

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

Referenced by ATParseTransformCmd(), and ATPostAlterTypeParse().

◆ transformCreateSchemaStmtElements()

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

Definition at line 4090 of file parse_utilcmd.c.

4091{
4093 List *result;
4094 ListCell *elements;
4095
4096 cxt.schemaname = schemaName;
4097 cxt.sequences = NIL;
4098 cxt.tables = NIL;
4099 cxt.views = NIL;
4100 cxt.indexes = NIL;
4101 cxt.triggers = NIL;
4102 cxt.grants = NIL;
4103
4104 /*
4105 * Run through each schema element in the schema element list. Separate
4106 * statements by type, and do preliminary analysis.
4107 */
4108 foreach(elements, schemaElts)
4109 {
4110 Node *element = lfirst(elements);
4111
4112 switch (nodeTag(element))
4113 {
4114 case T_CreateSeqStmt:
4115 {
4117
4119 cxt.sequences = lappend(cxt.sequences, element);
4120 }
4121 break;
4122
4123 case T_CreateStmt:
4124 {
4125 CreateStmt *elp = (CreateStmt *) element;
4126
4128
4129 /*
4130 * XXX todo: deal with constraints
4131 */
4132 cxt.tables = lappend(cxt.tables, element);
4133 }
4134 break;
4135
4136 case T_ViewStmt:
4137 {
4138 ViewStmt *elp = (ViewStmt *) element;
4139
4141
4142 /*
4143 * XXX todo: deal with references between views
4144 */
4145 cxt.views = lappend(cxt.views, element);
4146 }
4147 break;
4148
4149 case T_IndexStmt:
4150 {
4151 IndexStmt *elp = (IndexStmt *) element;
4152
4154 cxt.indexes = lappend(cxt.indexes, element);
4155 }
4156 break;
4157
4158 case T_CreateTrigStmt:
4159 {
4161
4163 cxt.triggers = lappend(cxt.triggers, element);
4164 }
4165 break;
4166
4167 case T_GrantStmt:
4168 cxt.grants = lappend(cxt.grants, element);
4169 break;
4170
4171 default:
4172 elog(ERROR, "unrecognized node type: %d",
4173 (int) nodeTag(element));
4174 }
4175 }
4176
4177 result = NIL;
4178 result = list_concat(result, cxt.sequences);
4179 result = list_concat(result, cxt.tables);
4180 result = list_concat(result, cxt.views);
4181 result = list_concat(result, cxt.indexes);
4182 result = list_concat(result, cxt.triggers);
4183 result = list_concat(result, cxt.grants);
4184
4185 return result;
4186}
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:3194
RangeVar * relation
Definition: parsenodes.h:2719
RangeVar * relation
Definition: parsenodes.h:3081
RangeVar * relation
Definition: parsenodes.h:3426
RangeVar * view
Definition: parsenodes.h:3819

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

Referenced by CreateSchemaCommand().

◆ transformCreateStmt()

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

Definition at line 164 of file parse_utilcmd.c.

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

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

Referenced by ProcessUtilitySlow().

◆ transformIndexStmt()

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

Definition at line 3039 of file parse_utilcmd.c.

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

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

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

◆ transformPartitionBound()

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

Definition at line 4266 of file parse_utilcmd.c.

4268{
4269 PartitionBoundSpec *result_spec;
4271 char strategy = get_partition_strategy(key);
4272 int partnatts = get_partition_natts(key);
4273 List *partexprs = get_partition_exprs(key);
4274
4275 /* Avoid scribbling on input */
4276 result_spec = copyObject(spec);
4277
4278 if (spec->is_default)
4279 {
4280 /*
4281 * Hash partitioning does not support a default partition; there's no
4282 * use case for it (since the set of partitions to create is perfectly
4283 * defined), and if users do get into it accidentally, it's hard to
4284 * back out from it afterwards.
4285 */
4286 if (strategy == PARTITION_STRATEGY_HASH)
4287 ereport(ERROR,
4288 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4289 errmsg("a hash-partitioned table may not have a default partition")));
4290
4291 /*
4292 * In case of the default partition, parser had no way to identify the
4293 * partition strategy. Assign the parent's strategy to the default
4294 * partition bound spec.
4295 */
4296 result_spec->strategy = strategy;
4297
4298 return result_spec;
4299 }
4300
4301 if (strategy == PARTITION_STRATEGY_HASH)
4302 {
4303 if (spec->strategy != PARTITION_STRATEGY_HASH)
4304 ereport(ERROR,
4305 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4306 errmsg("invalid bound specification for a hash partition"),
4307 parser_errposition(pstate, exprLocation((Node *) spec))));
4308
4309 if (spec->modulus <= 0)
4310 ereport(ERROR,
4311 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4312 errmsg("modulus for hash partition must be an integer value greater than zero")));
4313
4314 Assert(spec->remainder >= 0);
4315
4316 if (spec->remainder >= spec->modulus)
4317 ereport(ERROR,
4318 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4319 errmsg("remainder for hash partition must be less than modulus")));
4320 }
4321 else if (strategy == PARTITION_STRATEGY_LIST)
4322 {
4323 ListCell *cell;
4324 char *colname;
4325 Oid coltype;
4326 int32 coltypmod;
4327 Oid partcollation;
4328
4329 if (spec->strategy != PARTITION_STRATEGY_LIST)
4330 ereport(ERROR,
4331 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4332 errmsg("invalid bound specification for a list partition"),
4333 parser_errposition(pstate, exprLocation((Node *) spec))));
4334
4335 /* Get the only column's name in case we need to output an error */
4336 if (key->partattrs[0] != 0)
4337 colname = get_attname(RelationGetRelid(parent),
4338 key->partattrs[0], false);
4339 else
4340 colname = deparse_expression((Node *) linitial(partexprs),
4342 RelationGetRelid(parent)),
4343 false, false);
4344 /* Need its type data too */
4345 coltype = get_partition_col_typid(key, 0);
4346 coltypmod = get_partition_col_typmod(key, 0);
4347 partcollation = get_partition_col_collation(key, 0);
4348
4349 result_spec->listdatums = NIL;
4350 foreach(cell, spec->listdatums)
4351 {
4352 Node *expr = lfirst(cell);
4353 Const *value;
4354 ListCell *cell2;
4355 bool duplicate;
4356
4357 value = transformPartitionBoundValue(pstate, expr,
4358 colname, coltype, coltypmod,
4359 partcollation);
4360
4361 /* Don't add to the result if the value is a duplicate */
4362 duplicate = false;
4363 foreach(cell2, result_spec->listdatums)
4364 {
4365 Const *value2 = lfirst_node(Const, cell2);
4366
4367 if (equal(value, value2))
4368 {
4369 duplicate = true;
4370 break;
4371 }
4372 }
4373 if (duplicate)
4374 continue;
4375
4376 result_spec->listdatums = lappend(result_spec->listdatums,
4377 value);
4378 }
4379 }
4380 else if (strategy == PARTITION_STRATEGY_RANGE)
4381 {
4383 ereport(ERROR,
4384 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4385 errmsg("invalid bound specification for a range partition"),
4386 parser_errposition(pstate, exprLocation((Node *) spec))));
4387
4388 if (list_length(spec->lowerdatums) != partnatts)
4389 ereport(ERROR,
4390 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4391 errmsg("FROM must specify exactly one value per partitioning column")));
4392 if (list_length(spec->upperdatums) != partnatts)
4393 ereport(ERROR,
4394 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4395 errmsg("TO must specify exactly one value per partitioning column")));
4396
4397 /*
4398 * Convert raw parse nodes into PartitionRangeDatum nodes and perform
4399 * any necessary validation.
4400 */
4401 result_spec->lowerdatums =
4403 parent);
4404 result_spec->upperdatums =
4406 parent);
4407 }
4408 else
4409 elog(ERROR, "unexpected partition strategy: %d", (int) strategy);
4410
4411 return result_spec;
4412}
int32_t int32
Definition: c.h:484
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
static struct @162 value
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:828
int exprLocation(const Node *expr)
Definition: nodeFuncs.c:1388
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:106
static List * transformPartitionRangeBounds(ParseState *pstate, List *blist, Relation parent)
static Const * transformPartitionBoundValue(ParseState *pstate, Node *val, const char *colName, Oid colType, int32 colTypmod, Oid partCollation)
@ PARTITION_STRATEGY_HASH
Definition: parsenodes.h:885
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:883
@ PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:884
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:51
static int get_partition_strategy(PartitionKey key)
Definition: partcache.h:59
static int32 get_partition_col_typmod(PartitionKey key, int col)
Definition: partcache.h:92
static int get_partition_natts(PartitionKey key)
Definition: partcache.h:65
static Oid get_partition_col_typid(PartitionKey key, int col)
Definition: partcache.h:86
static List * get_partition_exprs(PartitionKey key)
Definition: partcache.h:71
static Oid get_partition_col_collation(PartitionKey key, int col)
Definition: partcache.h:98
List * deparse_context_for(const char *aliasname, Oid relid)
Definition: ruleutils.c:3705
char * deparse_expression(Node *expr, List *dpcontext, bool forceprefix, bool showimplicit)
Definition: ruleutils.c:3642

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

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

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

Referenced by DefineRule().

◆ transformStatsStmt()

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

Definition at line 3134 of file parse_utilcmd.c.

3135{
3136 ParseState *pstate;
3137 ParseNamespaceItem *nsitem;
3138 ListCell *l;
3139 Relation rel;
3140
3141 /* Nothing to do if statement already transformed. */
3142 if (stmt->transformed)
3143 return stmt;
3144
3145 /* Set up pstate */
3146 pstate = make_parsestate(NULL);
3147 pstate->p_sourcetext = queryString;
3148
3149 /*
3150 * Put the parent table into the rtable so that the expressions can refer
3151 * to its fields without qualification. Caller is responsible for locking
3152 * relation, but we still need to open it.
3153 */
3154 rel = relation_open(relid, NoLock);
3155 nsitem = addRangeTableEntryForRelation(pstate, rel,
3157 NULL, false, true);
3158
3159 /* no to join list, yes to namespaces */
3160 addNSItemToQuery(pstate, nsitem, false, true, true);
3161
3162 /* take care of any expressions */
3163 foreach(l, stmt->exprs)
3164 {
3165 StatsElem *selem = (StatsElem *) lfirst(l);
3166
3167 if (selem->expr)
3168 {
3169 /* Now do parse transformation of the expression */
3170 selem->expr = transformExpr(pstate, selem->expr,
3172
3173 /* We have to fix its collations too */
3174 assign_expr_collations(pstate, selem->expr);
3175 }
3176 }
3177
3178 /*
3179 * Check that only the base rel is mentioned. (This should be dead code
3180 * now that add_missing_from is history.)
3181 */
3182 if (list_length(pstate->p_rtable) != 1)
3183 ereport(ERROR,
3184 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
3185 errmsg("statistics expressions can refer only to the table being referenced")));
3186
3187 free_parsestate(pstate);
3188
3189 /* Close relation */
3190 table_close(rel, NoLock);
3191
3192 /* Mark statement as successfully transformed */
3193 stmt->transformed = true;
3194
3195 return stmt;
3196}
@ EXPR_KIND_STATS_EXPRESSION
Definition: parse_node.h:74
Node * expr
Definition: parsenodes.h:3482

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