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.

Typedefs

typedef struct AttrMap AttrMap
 

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 AttrMap *attmap, Oid *constraintOid)
 

Typedef Documentation

◆ AttrMap

typedef struct AttrMap AttrMap

Definition at line 19 of file parse_utilcmd.h.

Function Documentation

◆ expandTableLikeClause()

List * expandTableLikeClause ( RangeVar heapRel,
TableLikeClause table_like_clause 
)

Definition at line 1347 of file parse_utilcmd.c.

1348{
1349 List *result = NIL;
1350 List *atsubcmds = NIL;
1351 AttrNumber parent_attno;
1352 Relation relation;
1353 Relation childrel;
1354 TupleDesc tupleDesc;
1355 TupleConstr *constr;
1356 AttrMap *attmap;
1357 char *comment;
1358
1359 /*
1360 * Open the relation referenced by the LIKE clause. We should still have
1361 * the table lock obtained by transformTableLikeClause (and this'll throw
1362 * an assertion failure if not). Hence, no need to recheck privileges
1363 * etc. We must open the rel by OID not name, to be sure we get the same
1364 * table.
1365 */
1366 if (!OidIsValid(table_like_clause->relationOid))
1367 elog(ERROR, "expandTableLikeClause called on untransformed LIKE clause");
1368
1369 relation = relation_open(table_like_clause->relationOid, NoLock);
1370
1371 tupleDesc = RelationGetDescr(relation);
1372 constr = tupleDesc->constr;
1373
1374 /*
1375 * Open the newly-created child relation; we have lock on that too.
1376 */
1377 childrel = relation_openrv(heapRel, NoLock);
1378
1379 /*
1380 * Construct a map from the LIKE relation's attnos to the child rel's.
1381 * This re-checks type match etc, although it shouldn't be possible to
1382 * have a failure since both tables are locked.
1383 */
1384 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
1385 tupleDesc,
1386 false);
1387
1388 /*
1389 * Process defaults, if required.
1390 */
1391 if ((table_like_clause->options &
1393 constr != NULL)
1394 {
1395 for (parent_attno = 1; parent_attno <= tupleDesc->natts;
1396 parent_attno++)
1397 {
1398 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
1399 parent_attno - 1);
1400
1401 /*
1402 * Ignore dropped columns in the parent.
1403 */
1404 if (attribute->attisdropped)
1405 continue;
1406
1407 /*
1408 * Copy default, if present and it should be copied. We have
1409 * separate options for plain default expressions and GENERATED
1410 * defaults.
1411 */
1412 if (attribute->atthasdef &&
1413 (attribute->attgenerated ?
1414 (table_like_clause->options & CREATE_TABLE_LIKE_GENERATED) :
1415 (table_like_clause->options & CREATE_TABLE_LIKE_DEFAULTS)))
1416 {
1417 Node *this_default;
1418 AlterTableCmd *atsubcmd;
1419 bool found_whole_row;
1420
1421 this_default = TupleDescGetDefault(tupleDesc, parent_attno);
1422 if (this_default == NULL)
1423 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
1424 parent_attno, RelationGetRelationName(relation));
1425
1426 atsubcmd = makeNode(AlterTableCmd);
1427 atsubcmd->subtype = AT_CookedColumnDefault;
1428 atsubcmd->num = attmap->attnums[parent_attno - 1];
1429 atsubcmd->def = map_variable_attnos(this_default,
1430 1, 0,
1431 attmap,
1432 InvalidOid,
1433 &found_whole_row);
1434
1435 /*
1436 * Prevent this for the same reason as for constraints below.
1437 * Note that defaults cannot contain any vars, so it's OK that
1438 * the error message refers to generated columns.
1439 */
1440 if (found_whole_row)
1441 ereport(ERROR,
1442 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1443 errmsg("cannot convert whole-row table reference"),
1444 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
1445 NameStr(attribute->attname),
1446 RelationGetRelationName(relation))));
1447
1448 atsubcmds = lappend(atsubcmds, atsubcmd);
1449 }
1450 }
1451 }
1452
1453 /*
1454 * Copy CHECK constraints if requested, being careful to adjust attribute
1455 * numbers so they match the child.
1456 */
1457 if ((table_like_clause->options & CREATE_TABLE_LIKE_CONSTRAINTS) &&
1458 constr != NULL)
1459 {
1460 int ccnum;
1461
1462 for (ccnum = 0; ccnum < constr->num_check; ccnum++)
1463 {
1464 char *ccname = constr->check[ccnum].ccname;
1465 char *ccbin = constr->check[ccnum].ccbin;
1466 bool ccenforced = constr->check[ccnum].ccenforced;
1467 bool ccnoinherit = constr->check[ccnum].ccnoinherit;
1468 Node *ccbin_node;
1469 bool found_whole_row;
1470 Constraint *n;
1471 AlterTableCmd *atsubcmd;
1472
1473 ccbin_node = map_variable_attnos(stringToNode(ccbin),
1474 1, 0,
1475 attmap,
1476 InvalidOid, &found_whole_row);
1477
1478 /*
1479 * We reject whole-row variables because the whole point of LIKE
1480 * is that the new table's rowtype might later diverge from the
1481 * parent's. So, while translation might be possible right now,
1482 * it wouldn't be possible to guarantee it would work in future.
1483 */
1484 if (found_whole_row)
1485 ereport(ERROR,
1486 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1487 errmsg("cannot convert whole-row table reference"),
1488 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
1489 ccname,
1490 RelationGetRelationName(relation))));
1491
1492 n = makeNode(Constraint);
1493 n->contype = CONSTR_CHECK;
1494 n->conname = pstrdup(ccname);
1495 n->location = -1;
1496 n->is_enforced = ccenforced;
1497 n->initially_valid = ccenforced; /* sic */
1498 n->is_no_inherit = ccnoinherit;
1499 n->raw_expr = NULL;
1500 n->cooked_expr = nodeToString(ccbin_node);
1501
1502 /* We can skip validation, since the new table should be empty. */
1503 n->skip_validation = true;
1504
1505 atsubcmd = makeNode(AlterTableCmd);
1506 atsubcmd->subtype = AT_AddConstraint;
1507 atsubcmd->def = (Node *) n;
1508 atsubcmds = lappend(atsubcmds, atsubcmd);
1509
1510 /* Copy comment on constraint */
1511 if ((table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS) &&
1513 n->conname, false),
1514 ConstraintRelationId,
1515 0)) != NULL)
1516 {
1518
1519 stmt->objtype = OBJECT_TABCONSTRAINT;
1520 stmt->object = (Node *) list_make3(makeString(heapRel->schemaname),
1521 makeString(heapRel->relname),
1522 makeString(n->conname));
1523 stmt->comment = comment;
1524
1525 result = lappend(result, stmt);
1526 }
1527 }
1528 }
1529
1530 /*
1531 * If we generated any ALTER TABLE actions above, wrap them into a single
1532 * ALTER TABLE command. Stick it at the front of the result, so it runs
1533 * before any CommentStmts we made above.
1534 */
1535 if (atsubcmds)
1536 {
1538
1539 atcmd->relation = copyObject(heapRel);
1540 atcmd->cmds = atsubcmds;
1541 atcmd->objtype = OBJECT_TABLE;
1542 atcmd->missing_ok = false;
1543 result = lcons(atcmd, result);
1544 }
1545
1546 /*
1547 * Process indexes if required.
1548 */
1549 if ((table_like_clause->options & CREATE_TABLE_LIKE_INDEXES) &&
1550 relation->rd_rel->relhasindex &&
1551 childrel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
1552 {
1553 List *parent_indexes;
1554 ListCell *l;
1555
1556 parent_indexes = RelationGetIndexList(relation);
1557
1558 foreach(l, parent_indexes)
1559 {
1560 Oid parent_index_oid = lfirst_oid(l);
1561 Relation parent_index;
1562 IndexStmt *index_stmt;
1563
1564 parent_index = index_open(parent_index_oid, AccessShareLock);
1565
1566 /* Build CREATE INDEX statement to recreate the parent_index */
1567 index_stmt = generateClonedIndexStmt(heapRel,
1568 parent_index,
1569 attmap,
1570 NULL);
1571
1572 /* Copy comment on index, if requested */
1573 if (table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS)
1574 {
1575 comment = GetComment(parent_index_oid, RelationRelationId, 0);
1576
1577 /*
1578 * We make use of IndexStmt's idxcomment option, so as not to
1579 * need to know now what name the index will have.
1580 */
1581 index_stmt->idxcomment = comment;
1582 }
1583
1584 result = lappend(result, index_stmt);
1585
1586 index_close(parent_index, AccessShareLock);
1587 }
1588 }
1589
1590 /*
1591 * Process extended statistics if required.
1592 */
1593 if (table_like_clause->options & CREATE_TABLE_LIKE_STATISTICS)
1594 {
1595 List *parent_extstats;
1596 ListCell *l;
1597
1598 parent_extstats = RelationGetStatExtList(relation);
1599
1600 foreach(l, parent_extstats)
1601 {
1602 Oid parent_stat_oid = lfirst_oid(l);
1603 CreateStatsStmt *stats_stmt;
1604
1605 stats_stmt = generateClonedExtStatsStmt(heapRel,
1606 RelationGetRelid(childrel),
1607 parent_stat_oid,
1608 attmap);
1609
1610 /* Copy comment on statistics object, if requested */
1611 if (table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS)
1612 {
1613 comment = GetComment(parent_stat_oid, StatisticExtRelationId, 0);
1614
1615 /*
1616 * We make use of CreateStatsStmt's stxcomment option, so as
1617 * not to need to know now what name the statistics will have.
1618 */
1619 stats_stmt->stxcomment = comment;
1620 }
1621
1622 result = lappend(result, stats_stmt);
1623 }
1624
1625 list_free(parent_extstats);
1626 }
1627
1628 /* Done with child rel */
1629 table_close(childrel, NoLock);
1630
1631 /*
1632 * Close the parent rel, but keep our AccessShareLock on it until xact
1633 * commit. That will prevent someone else from deleting or ALTERing the
1634 * parent before the child is committed.
1635 */
1636 table_close(relation, NoLock);
1637
1638 return result;
1639}
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:175
int16 AttrNumber
Definition: attnum.h:21
#define NameStr(name)
Definition: c.h:765
#define OidIsValid(objectId)
Definition: c.h:788
char * GetComment(Oid oid, Oid classoid, int32 subid)
Definition: comment.c:410
int errdetail(const char *fmt,...)
Definition: elog.c:1216
int errcode(int sqlerrcode)
Definition: elog.c:863
int errmsg(const char *fmt,...)
Definition: elog.c:1080
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
#define ereport(elevel,...)
Definition: elog.h:150
#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:1759
#define copyObject(obj)
Definition: nodes.h:232
#define makeNode(_type_)
Definition: nodes.h:161
char * nodeToString(const void *obj)
Definition: outfuncs.c:802
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:2833
@ OBJECT_TABLE
Definition: parsenodes.h:2392
@ OBJECT_TABCONSTRAINT
Definition: parsenodes.h:2391
@ AT_AddConstraint
Definition: parsenodes.h:2459
@ AT_CookedColumnDefault
Definition: parsenodes.h:2446
@ CREATE_TABLE_LIKE_COMMENTS
Definition: parsenodes.h:789
@ CREATE_TABLE_LIKE_GENERATED
Definition: parsenodes.h:793
@ CREATE_TABLE_LIKE_INDEXES
Definition: parsenodes.h:795
@ CREATE_TABLE_LIKE_DEFAULTS
Definition: parsenodes.h:792
@ CREATE_TABLE_LIKE_STATISTICS
Definition: parsenodes.h:796
@ CREATE_TABLE_LIKE_CONSTRAINTS
Definition: parsenodes.h:791
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:202
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:515
#define RelationGetDescr(relation)
Definition: rel.h:541
#define RelationGetRelationName(relation)
Definition: rel.h:549
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4836
List * RelationGetStatExtList(Relation relation)
Definition: relcache.c:4977
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:2516
RangeVar * relation
Definition: parsenodes.h:2435
ObjectType objtype
Definition: parsenodes.h:2437
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
char * ccbin
Definition: tupdesc.h:31
ParseLoc location
Definition: parsenodes.h:2905
ConstrType contype
Definition: parsenodes.h:2861
bool is_no_inherit
Definition: parsenodes.h:2868
bool is_enforced
Definition: parsenodes.h:2865
char * cooked_expr
Definition: parsenodes.h:2871
bool initially_valid
Definition: parsenodes.h:2867
bool skip_validation
Definition: parsenodes.h:2866
Node * raw_expr
Definition: parsenodes.h:2869
char * conname
Definition: parsenodes.h:2862
char * idxcomment
Definition: parsenodes.h:3523
Definition: pg_list.h:54
Definition: nodes.h:135
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:141
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Node * TupleDescGetDefault(TupleDesc tupdesc, AttrNumber attnum)
Definition: tupdesc.c:1092
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:160
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, 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 AttrMap attmap,
Oid constraintOid 
)

Definition at line 1695 of file parse_utilcmd.c.

1698{
1699 Oid source_relid = RelationGetRelid(source_idx);
1700 HeapTuple ht_idxrel;
1701 HeapTuple ht_idx;
1702 HeapTuple ht_am;
1703 Form_pg_class idxrelrec;
1704 Form_pg_index idxrec;
1705 Form_pg_am amrec;
1706 oidvector *indcollation;
1707 oidvector *indclass;
1709 List *indexprs;
1710 ListCell *indexpr_item;
1711 Oid indrelid;
1712 int keyno;
1713 Oid keycoltype;
1714 Datum datum;
1715 bool isnull;
1716
1717 if (constraintOid)
1718 *constraintOid = InvalidOid;
1719
1720 /*
1721 * Fetch pg_class tuple of source index. We can't use the copy in the
1722 * relcache entry because it doesn't include optional fields.
1723 */
1724 ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(source_relid));
1725 if (!HeapTupleIsValid(ht_idxrel))
1726 elog(ERROR, "cache lookup failed for relation %u", source_relid);
1727 idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
1728
1729 /* Fetch pg_index tuple for source index from relcache entry */
1730 ht_idx = source_idx->rd_indextuple;
1731 idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
1732 indrelid = idxrec->indrelid;
1733
1734 /* Fetch the pg_am tuple of the index' access method */
1735 ht_am = SearchSysCache1(AMOID, ObjectIdGetDatum(idxrelrec->relam));
1736 if (!HeapTupleIsValid(ht_am))
1737 elog(ERROR, "cache lookup failed for access method %u",
1738 idxrelrec->relam);
1739 amrec = (Form_pg_am) GETSTRUCT(ht_am);
1740
1741 /* Extract indcollation from the pg_index tuple */
1742 datum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1743 Anum_pg_index_indcollation);
1744 indcollation = (oidvector *) DatumGetPointer(datum);
1745
1746 /* Extract indclass from the pg_index tuple */
1747 datum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx, Anum_pg_index_indclass);
1748 indclass = (oidvector *) DatumGetPointer(datum);
1749
1750 /* Begin building the IndexStmt */
1752 index->relation = heapRel;
1753 index->accessMethod = pstrdup(NameStr(amrec->amname));
1754 if (OidIsValid(idxrelrec->reltablespace))
1755 index->tableSpace = get_tablespace_name(idxrelrec->reltablespace);
1756 else
1757 index->tableSpace = NULL;
1758 index->excludeOpNames = NIL;
1759 index->idxcomment = NULL;
1760 index->indexOid = InvalidOid;
1761 index->oldNumber = InvalidRelFileNumber;
1762 index->oldCreateSubid = InvalidSubTransactionId;
1763 index->oldFirstRelfilelocatorSubid = InvalidSubTransactionId;
1764 index->unique = idxrec->indisunique;
1765 index->nulls_not_distinct = idxrec->indnullsnotdistinct;
1766 index->primary = idxrec->indisprimary;
1767 index->iswithoutoverlaps = (idxrec->indisprimary || idxrec->indisunique) && idxrec->indisexclusion;
1768 index->transformed = true; /* don't need transformIndexStmt */
1769 index->concurrent = false;
1770 index->if_not_exists = false;
1771 index->reset_default_tblspc = false;
1772
1773 /*
1774 * We don't try to preserve the name of the source index; instead, just
1775 * let DefineIndex() choose a reasonable name. (If we tried to preserve
1776 * the name, we'd get duplicate-relation-name failures unless the source
1777 * table was in a different schema.)
1778 */
1779 index->idxname = NULL;
1780
1781 /*
1782 * If the index is marked PRIMARY or has an exclusion condition, it's
1783 * certainly from a constraint; else, if it's not marked UNIQUE, it
1784 * certainly isn't. If it is or might be from a constraint, we have to
1785 * fetch the pg_constraint record.
1786 */
1787 if (index->primary || index->unique || idxrec->indisexclusion)
1788 {
1789 Oid constraintId = get_index_constraint(source_relid);
1790
1791 if (OidIsValid(constraintId))
1792 {
1793 HeapTuple ht_constr;
1794 Form_pg_constraint conrec;
1795
1796 if (constraintOid)
1797 *constraintOid = constraintId;
1798
1799 ht_constr = SearchSysCache1(CONSTROID,
1800 ObjectIdGetDatum(constraintId));
1801 if (!HeapTupleIsValid(ht_constr))
1802 elog(ERROR, "cache lookup failed for constraint %u",
1803 constraintId);
1804 conrec = (Form_pg_constraint) GETSTRUCT(ht_constr);
1805
1806 index->isconstraint = true;
1807 index->deferrable = conrec->condeferrable;
1808 index->initdeferred = conrec->condeferred;
1809
1810 /* If it's an exclusion constraint, we need the operator names */
1811 if (idxrec->indisexclusion)
1812 {
1813 Datum *elems;
1814 int nElems;
1815 int i;
1816
1817 Assert(conrec->contype == CONSTRAINT_EXCLUSION ||
1818 (index->iswithoutoverlaps &&
1819 (conrec->contype == CONSTRAINT_PRIMARY || conrec->contype == CONSTRAINT_UNIQUE)));
1820 /* Extract operator OIDs from the pg_constraint tuple */
1821 datum = SysCacheGetAttrNotNull(CONSTROID, ht_constr,
1822 Anum_pg_constraint_conexclop);
1823 deconstruct_array_builtin(DatumGetArrayTypeP(datum), OIDOID, &elems, NULL, &nElems);
1824
1825 for (i = 0; i < nElems; i++)
1826 {
1827 Oid operid = DatumGetObjectId(elems[i]);
1828 HeapTuple opertup;
1829 Form_pg_operator operform;
1830 char *oprname;
1831 char *nspname;
1832 List *namelist;
1833
1834 opertup = SearchSysCache1(OPEROID,
1835 ObjectIdGetDatum(operid));
1836 if (!HeapTupleIsValid(opertup))
1837 elog(ERROR, "cache lookup failed for operator %u",
1838 operid);
1839 operform = (Form_pg_operator) GETSTRUCT(opertup);
1840 oprname = pstrdup(NameStr(operform->oprname));
1841 /* For simplicity we always schema-qualify the op name */
1842 nspname = get_namespace_name(operform->oprnamespace);
1843 namelist = list_make2(makeString(nspname),
1844 makeString(oprname));
1845 index->excludeOpNames = lappend(index->excludeOpNames,
1846 namelist);
1847 ReleaseSysCache(opertup);
1848 }
1849 }
1850
1851 ReleaseSysCache(ht_constr);
1852 }
1853 else
1854 index->isconstraint = false;
1855 }
1856 else
1857 index->isconstraint = false;
1858
1859 /* Get the index expressions, if any */
1860 datum = SysCacheGetAttr(INDEXRELID, ht_idx,
1861 Anum_pg_index_indexprs, &isnull);
1862 if (!isnull)
1863 {
1864 char *exprsString;
1865
1866 exprsString = TextDatumGetCString(datum);
1867 indexprs = (List *) stringToNode(exprsString);
1868 }
1869 else
1870 indexprs = NIL;
1871
1872 /* Build the list of IndexElem */
1873 index->indexParams = NIL;
1874 index->indexIncludingParams = NIL;
1875
1876 indexpr_item = list_head(indexprs);
1877 for (keyno = 0; keyno < idxrec->indnkeyatts; keyno++)
1878 {
1879 IndexElem *iparam;
1880 AttrNumber attnum = idxrec->indkey.values[keyno];
1882 keyno);
1883 int16 opt = source_idx->rd_indoption[keyno];
1884
1885 iparam = makeNode(IndexElem);
1886
1888 {
1889 /* Simple index column */
1890 char *attname;
1891
1892 attname = get_attname(indrelid, attnum, false);
1893 keycoltype = get_atttype(indrelid, attnum);
1894
1895 iparam->name = attname;
1896 iparam->expr = NULL;
1897 }
1898 else
1899 {
1900 /* Expressional index */
1901 Node *indexkey;
1902 bool found_whole_row;
1903
1904 if (indexpr_item == NULL)
1905 elog(ERROR, "too few entries in indexprs list");
1906 indexkey = (Node *) lfirst(indexpr_item);
1907 indexpr_item = lnext(indexprs, indexpr_item);
1908
1909 /* Adjust Vars to match new table's column numbering */
1910 indexkey = map_variable_attnos(indexkey,
1911 1, 0,
1912 attmap,
1913 InvalidOid, &found_whole_row);
1914
1915 /* As in expandTableLikeClause, reject whole-row variables */
1916 if (found_whole_row)
1917 ereport(ERROR,
1918 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1919 errmsg("cannot convert whole-row table reference"),
1920 errdetail("Index \"%s\" contains a whole-row table reference.",
1921 RelationGetRelationName(source_idx))));
1922
1923 iparam->name = NULL;
1924 iparam->expr = indexkey;
1925
1926 keycoltype = exprType(indexkey);
1927 }
1928
1929 /* Copy the original index column name */
1930 iparam->indexcolname = pstrdup(NameStr(attr->attname));
1931
1932 /* Add the collation name, if non-default */
1933 iparam->collation = get_collation(indcollation->values[keyno], keycoltype);
1934
1935 /* Add the operator class name, if non-default */
1936 iparam->opclass = get_opclass(indclass->values[keyno], keycoltype);
1937 iparam->opclassopts =
1938 untransformRelOptions(get_attoptions(source_relid, keyno + 1));
1939
1940 iparam->ordering = SORTBY_DEFAULT;
1942
1943 /* Adjust options if necessary */
1944 if (source_idx->rd_indam->amcanorder)
1945 {
1946 /*
1947 * If it supports sort ordering, copy DESC and NULLS opts. Don't
1948 * set non-default settings unnecessarily, though, so as to
1949 * improve the chance of recognizing equivalence to constraint
1950 * indexes.
1951 */
1952 if (opt & INDOPTION_DESC)
1953 {
1954 iparam->ordering = SORTBY_DESC;
1955 if ((opt & INDOPTION_NULLS_FIRST) == 0)
1957 }
1958 else
1959 {
1960 if (opt & INDOPTION_NULLS_FIRST)
1962 }
1963 }
1964
1965 index->indexParams = lappend(index->indexParams, iparam);
1966 }
1967
1968 /* Handle included columns separately */
1969 for (keyno = idxrec->indnkeyatts; keyno < idxrec->indnatts; keyno++)
1970 {
1971 IndexElem *iparam;
1972 AttrNumber attnum = idxrec->indkey.values[keyno];
1974 keyno);
1975
1976 iparam = makeNode(IndexElem);
1977
1979 {
1980 /* Simple index column */
1981 char *attname;
1982
1983 attname = get_attname(indrelid, attnum, false);
1984
1985 iparam->name = attname;
1986 iparam->expr = NULL;
1987 }
1988 else
1989 ereport(ERROR,
1990 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1991 errmsg("expressions are not supported in included columns")));
1992
1993 /* Copy the original index column name */
1994 iparam->indexcolname = pstrdup(NameStr(attr->attname));
1995
1996 index->indexIncludingParams = lappend(index->indexIncludingParams, iparam);
1997 }
1998 /* Copy reloptions if any */
1999 datum = SysCacheGetAttr(RELOID, ht_idxrel,
2000 Anum_pg_class_reloptions, &isnull);
2001 if (!isnull)
2002 index->options = untransformRelOptions(datum);
2003
2004 /* If it's a partial index, decompile and append the predicate */
2005 datum = SysCacheGetAttr(INDEXRELID, ht_idx,
2006 Anum_pg_index_indpred, &isnull);
2007 if (!isnull)
2008 {
2009 char *pred_str;
2010 Node *pred_tree;
2011 bool found_whole_row;
2012
2013 /* Convert text string to node tree */
2014 pred_str = TextDatumGetCString(datum);
2015 pred_tree = (Node *) stringToNode(pred_str);
2016
2017 /* Adjust Vars to match new table's column numbering */
2018 pred_tree = map_variable_attnos(pred_tree,
2019 1, 0,
2020 attmap,
2021 InvalidOid, &found_whole_row);
2022
2023 /* As in expandTableLikeClause, reject whole-row variables */
2024 if (found_whole_row)
2025 ereport(ERROR,
2026 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2027 errmsg("cannot convert whole-row table reference"),
2028 errdetail("Index \"%s\" contains a whole-row table reference.",
2029 RelationGetRelationName(source_idx))));
2030
2031 index->whereClause = pred_tree;
2032 }
2033
2034 /* Clean up */
2035 ReleaseSysCache(ht_idxrel);
2036 ReleaseSysCache(ht_am);
2037
2038 return index;
2039}
#define DatumGetArrayTypeP(X)
Definition: array.h:261
void deconstruct_array_builtin(const ArrayType *array, Oid elmtype, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3698
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
char * get_tablespace_name(Oid spc_oid)
Definition: tablespace.c:1472
#define TextDatumGetCString(d)
Definition: builtins.h:98
#define InvalidSubTransactionId
Definition: c.h:677
int16_t int16
Definition: c.h:547
Assert(PointerIsAligned(start, uint64))
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
Definition: htup_details.h:728
int i
Definition: isn.c:77
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
Datum get_attoptions(Oid relid, int16 attnum)
Definition: lsyscache.c:1061
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:918
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3531
Oid get_atttype(Oid relid, AttrNumber attnum)
Definition: lsyscache.c:1004
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
static List * get_collation(Oid collation, Oid actual_datatype)
static List * get_opclass(Oid opclass, Oid actual_datatype)
@ SORTBY_NULLS_DEFAULT
Definition: parsenodes.h:54
@ SORTBY_NULLS_LAST
Definition: parsenodes.h:56
@ SORTBY_NULLS_FIRST
Definition: parsenodes.h:55
@ SORTBY_DESC
Definition: parsenodes.h:48
@ SORTBY_DEFAULT
Definition: parsenodes.h:46
FormData_pg_am * Form_pg_am
Definition: pg_am.h:48
NameData attname
Definition: pg_attribute.h:41
int16 attnum
Definition: pg_attribute.h:74
FormData_pg_class * Form_pg_class
Definition: pg_class.h:156
FormData_pg_constraint * Form_pg_constraint
Oid get_index_constraint(Oid indexId)
Definition: pg_depend.c:988
FormData_pg_index * Form_pg_index
Definition: pg_index.h:70
#define lfirst(lc)
Definition: pg_list.h:172
static ListCell * list_head(const List *l)
Definition: pg_list.h:128
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:343
#define list_make2(x1, x2)
Definition: pg_list.h:214
FormData_pg_operator * Form_pg_operator
Definition: pg_operator.h:83
static Oid DatumGetObjectId(Datum X)
Definition: postgres.h:252
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:262
uint64_t Datum
Definition: postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:322
List * untransformRelOptions(Datum options)
Definition: reloptions.c:1360
#define InvalidRelFileNumber
Definition: relpath.h:26
bool amcanorder
Definition: amapi.h:246
Node * expr
Definition: parsenodes.h:812
SortByDir ordering
Definition: parsenodes.h:817
List * opclassopts
Definition: parsenodes.h:816
char * indexcolname
Definition: parsenodes.h:813
SortByNulls nulls_ordering
Definition: parsenodes.h:818
List * opclass
Definition: parsenodes.h:815
char * name
Definition: parsenodes.h:811
List * collation
Definition: parsenodes.h:814
struct IndexAmRoutine * rd_indam
Definition: rel.h:206
struct HeapTupleData * rd_indextuple
Definition: rel.h:194
int16 * rd_indoption
Definition: rel.h:211
Definition: type.h:96
Definition: c.h:745
Oid values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:752
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:264
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:220
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:595
Datum SysCacheGetAttrNotNull(int cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition: syscache.c:625

References IndexAmRoutine::amcanorder, Assert(), attname, attnum, AttributeNumberIsValid, IndexElem::collation, DatumGetArrayTypeP, DatumGetObjectId(), DatumGetPointer(), deconstruct_array_builtin(), elog, ereport, errcode(), errdetail(), errmsg(), ERROR, IndexElem::expr, exprType(), get_attname(), get_attoptions(), get_atttype(), get_collation(), get_index_constraint(), get_namespace_name(), get_opclass(), get_tablespace_name(), GETSTRUCT(), HeapTupleIsValid, i, if(), IndexElem::indexcolname, InvalidOid, InvalidRelFileNumber, InvalidSubTransactionId, lappend(), lfirst, list_head(), list_make2, lnext(), makeNode, makeString(), map_variable_attnos(), IndexElem::name, NameStr, NIL, IndexElem::nulls_ordering, ObjectIdGetDatum(), OidIsValid, IndexElem::opclass, IndexElem::opclassopts, IndexElem::ordering, pstrdup(), RelationData::rd_indam, RelationData::rd_indextuple, RelationData::rd_indoption, RelationGetDescr, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), SearchSysCache1(), SORTBY_DEFAULT, SORTBY_DESC, SORTBY_NULLS_DEFAULT, SORTBY_NULLS_FIRST, SORTBY_NULLS_LAST, stringToNode(), SysCacheGetAttr(), SysCacheGetAttrNotNull(), TextDatumGetCString, TupleDescAttr(), untransformRelOptions(), and oidvector::values.

Referenced by AttachPartitionEnsureIndexes(), DefineIndex(), DefineRelation(), and expandTableLikeClause().

◆ transformAlterTableStmt()

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

Definition at line 3810 of file parse_utilcmd.c.

3813{
3814 Relation rel;
3815 TupleDesc tupdesc;
3816 ParseState *pstate;
3818 List *save_alist;
3819 ListCell *lcmd,
3820 *l;
3821 List *newcmds = NIL;
3822 bool skipValidation = true;
3823 AlterTableCmd *newcmd;
3824 ParseNamespaceItem *nsitem;
3825
3826 /* Caller is responsible for locking the relation */
3827 rel = relation_open(relid, NoLock);
3828 tupdesc = RelationGetDescr(rel);
3829
3830 /* Set up pstate */
3831 pstate = make_parsestate(NULL);
3832 pstate->p_sourcetext = queryString;
3833 nsitem = addRangeTableEntryForRelation(pstate,
3834 rel,
3836 NULL,
3837 false,
3838 true);
3839 addNSItemToQuery(pstate, nsitem, false, true, true);
3840
3841 /* Set up CreateStmtContext */
3842 cxt.pstate = pstate;
3843 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
3844 {
3845 cxt.stmtType = "ALTER FOREIGN TABLE";
3846 cxt.isforeign = true;
3847 }
3848 else
3849 {
3850 cxt.stmtType = "ALTER TABLE";
3851 cxt.isforeign = false;
3852 }
3853 cxt.relation = stmt->relation;
3854 cxt.rel = rel;
3855 cxt.inhRelations = NIL;
3856 cxt.isalter = true;
3857 cxt.columns = NIL;
3858 cxt.ckconstraints = NIL;
3859 cxt.nnconstraints = NIL;
3860 cxt.fkconstraints = NIL;
3861 cxt.ixconstraints = NIL;
3862 cxt.likeclauses = NIL;
3863 cxt.blist = NIL;
3864 cxt.alist = NIL;
3865 cxt.pkey = NULL;
3866 cxt.ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
3867 cxt.partbound = NULL;
3868 cxt.ofType = false;
3869
3870 /*
3871 * Transform ALTER subcommands that need it (most don't). These largely
3872 * re-use code from CREATE TABLE.
3873 */
3874 foreach(lcmd, stmt->cmds)
3875 {
3876 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
3877
3878 switch (cmd->subtype)
3879 {
3880 case AT_AddColumn:
3881 {
3882 ColumnDef *def = castNode(ColumnDef, cmd->def);
3883
3884 transformColumnDefinition(&cxt, def);
3885
3886 /*
3887 * If the column has a non-null default, we can't skip
3888 * validation of foreign keys.
3889 */
3890 if (def->raw_default != NULL)
3891 skipValidation = false;
3892
3893 /*
3894 * All constraints are processed in other ways. Remove the
3895 * original list
3896 */
3897 def->constraints = NIL;
3898
3899 newcmds = lappend(newcmds, cmd);
3900 break;
3901 }
3902
3903 case AT_AddConstraint:
3904
3905 /*
3906 * The original AddConstraint cmd node doesn't go to newcmds
3907 */
3908 if (IsA(cmd->def, Constraint))
3909 {
3910 transformTableConstraint(&cxt, (Constraint *) cmd->def);
3911 if (((Constraint *) cmd->def)->contype == CONSTR_FOREIGN)
3912 skipValidation = false;
3913 }
3914 else
3915 elog(ERROR, "unrecognized node type: %d",
3916 (int) nodeTag(cmd->def));
3917 break;
3918
3919 case AT_AlterColumnType:
3920 {
3921 ColumnDef *def = castNode(ColumnDef, cmd->def);
3923
3924 /*
3925 * For ALTER COLUMN TYPE, transform the USING clause if
3926 * one was specified.
3927 */
3928 if (def->raw_default)
3929 {
3930 def->cooked_default =
3931 transformExpr(pstate, def->raw_default,
3933 }
3934
3935 /*
3936 * For identity column, create ALTER SEQUENCE command to
3937 * change the data type of the sequence. Identity sequence
3938 * is associated with the top level partitioned table.
3939 * Hence ignore partitions.
3940 */
3941 if (!RelationGetForm(rel)->relispartition)
3942 {
3943 attnum = get_attnum(relid, cmd->name);
3945 ereport(ERROR,
3946 (errcode(ERRCODE_UNDEFINED_COLUMN),
3947 errmsg("column \"%s\" of relation \"%s\" does not exist",
3948 cmd->name, RelationGetRelationName(rel))));
3949
3950 if (attnum > 0 &&
3951 TupleDescAttr(tupdesc, attnum - 1)->attidentity)
3952 {
3953 Oid seq_relid = getIdentitySequence(rel, attnum, false);
3954 Oid typeOid = typenameTypeId(pstate, def->typeName);
3955 AlterSeqStmt *altseqstmt = makeNode(AlterSeqStmt);
3956
3957 altseqstmt->sequence
3959 get_rel_name(seq_relid),
3960 -1);
3961 altseqstmt->options = list_make1(makeDefElem("as",
3962 (Node *) makeTypeNameFromOid(typeOid, -1),
3963 -1));
3964 altseqstmt->for_identity = true;
3965 cxt.blist = lappend(cxt.blist, altseqstmt);
3966 }
3967 }
3968
3969 newcmds = lappend(newcmds, cmd);
3970 break;
3971 }
3972
3973 case AT_AddIdentity:
3974 {
3975 Constraint *def = castNode(Constraint, cmd->def);
3976 ColumnDef *newdef = makeNode(ColumnDef);
3978
3979 newdef->colname = cmd->name;
3980 newdef->identity = def->generated_when;
3981 cmd->def = (Node *) newdef;
3982
3983 attnum = get_attnum(relid, cmd->name);
3985 ereport(ERROR,
3986 (errcode(ERRCODE_UNDEFINED_COLUMN),
3987 errmsg("column \"%s\" of relation \"%s\" does not exist",
3988 cmd->name, RelationGetRelationName(rel))));
3989
3990 generateSerialExtraStmts(&cxt, newdef,
3991 get_atttype(relid, attnum),
3992 def->options, true, true,
3993 NULL, NULL);
3994
3995 newcmds = lappend(newcmds, cmd);
3996 break;
3997 }
3998
3999 case AT_SetIdentity:
4000 {
4001 /*
4002 * Create an ALTER SEQUENCE statement for the internal
4003 * sequence of the identity column.
4004 */
4005 ListCell *lc;
4006 List *newseqopts = NIL;
4007 List *newdef = NIL;
4009 Oid seq_relid;
4010
4011 /*
4012 * Split options into those handled by ALTER SEQUENCE and
4013 * those for ALTER TABLE proper.
4014 */
4015 foreach(lc, castNode(List, cmd->def))
4016 {
4017 DefElem *def = lfirst_node(DefElem, lc);
4018
4019 if (strcmp(def->defname, "generated") == 0)
4020 newdef = lappend(newdef, def);
4021 else
4022 newseqopts = lappend(newseqopts, def);
4023 }
4024
4025 attnum = get_attnum(relid, cmd->name);
4027 ereport(ERROR,
4028 (errcode(ERRCODE_UNDEFINED_COLUMN),
4029 errmsg("column \"%s\" of relation \"%s\" does not exist",
4030 cmd->name, RelationGetRelationName(rel))));
4031
4032 seq_relid = getIdentitySequence(rel, attnum, true);
4033
4034 if (seq_relid)
4035 {
4036 AlterSeqStmt *seqstmt;
4037
4038 seqstmt = makeNode(AlterSeqStmt);
4040 get_rel_name(seq_relid), -1);
4041 seqstmt->options = newseqopts;
4042 seqstmt->for_identity = true;
4043 seqstmt->missing_ok = false;
4044
4045 cxt.blist = lappend(cxt.blist, seqstmt);
4046 }
4047
4048 /*
4049 * If column was not an identity column, we just let the
4050 * ALTER TABLE command error out later. (There are cases
4051 * this fails to cover, but we'll need to restructure
4052 * where creation of the sequence dependency linkage
4053 * happens before we can fix it.)
4054 */
4055
4056 cmd->def = (Node *) newdef;
4057 newcmds = lappend(newcmds, cmd);
4058 break;
4059 }
4060
4061 case AT_AttachPartition:
4062 case AT_DetachPartition:
4063 {
4064 PartitionCmd *partcmd = (PartitionCmd *) cmd->def;
4065
4066 transformPartitionCmd(&cxt, partcmd->bound);
4067 /* assign the transformed value of the partition bound */
4068 partcmd->bound = cxt.partbound;
4069 }
4070
4071 newcmds = lappend(newcmds, cmd);
4072 break;
4073
4074 case AT_MergePartitions:
4075 {
4076 PartitionCmd *partcmd = (PartitionCmd *) cmd->def;
4077
4078 if (list_length(partcmd->partlist) < 2)
4079 ereport(ERROR,
4080 errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
4081 errmsg("list of partitions to be merged should include at least two partitions"));
4082
4083 transformPartitionCmdForMerge(&cxt, partcmd);
4084 newcmds = lappend(newcmds, cmd);
4085 break;
4086 }
4087
4088 case AT_SplitPartition:
4089 {
4090 PartitionCmd *partcmd = (PartitionCmd *) cmd->def;
4091
4092 if (list_length(partcmd->partlist) < 2)
4093 ereport(ERROR,
4094 errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
4095 errmsg("list of new partitions should contain at least two partitions"));
4096
4097 transformPartitionCmdForSplit(&cxt, partcmd);
4098 newcmds = lappend(newcmds, cmd);
4099 break;
4100 }
4101
4102 default:
4103
4104 /*
4105 * Currently, we shouldn't actually get here for the
4106 * subcommand types that don't require transformation; but if
4107 * we do, just emit them unchanged.
4108 */
4109 newcmds = lappend(newcmds, cmd);
4110 break;
4111 }
4112 }
4113
4114 /*
4115 * Transfer anything we already have in cxt.alist into save_alist, to keep
4116 * it separate from the output of transformIndexConstraints.
4117 */
4118 save_alist = cxt.alist;
4119 cxt.alist = NIL;
4120
4121 /* Postprocess constraints */
4123 transformFKConstraints(&cxt, skipValidation, true);
4124 transformCheckConstraints(&cxt, false);
4125
4126 /*
4127 * Push any index-creation commands into the ALTER, so that they can be
4128 * scheduled nicely by tablecmds.c. Note that tablecmds.c assumes that
4129 * the IndexStmt attached to an AT_AddIndex or AT_AddIndexConstraint
4130 * subcommand has already been through transformIndexStmt.
4131 */
4132 foreach(l, cxt.alist)
4133 {
4134 Node *istmt = (Node *) lfirst(l);
4135
4136 /*
4137 * We assume here that cxt.alist contains only IndexStmts generated
4138 * from primary key constraints.
4139 */
4140 if (IsA(istmt, IndexStmt))
4141 {
4142 IndexStmt *idxstmt = (IndexStmt *) istmt;
4143
4144 idxstmt = transformIndexStmt(relid, idxstmt, queryString);
4145 newcmd = makeNode(AlterTableCmd);
4147 newcmd->def = (Node *) idxstmt;
4148 newcmds = lappend(newcmds, newcmd);
4149 }
4150 else
4151 elog(ERROR, "unexpected stmt type %d", (int) nodeTag(istmt));
4152 }
4153 cxt.alist = NIL;
4154
4155 /* Append any CHECK, NOT NULL or FK constraints to the commands list */
4157 {
4158 newcmd = makeNode(AlterTableCmd);
4159 newcmd->subtype = AT_AddConstraint;
4160 newcmd->def = (Node *) def;
4161 newcmds = lappend(newcmds, newcmd);
4162 }
4164 {
4165 newcmd = makeNode(AlterTableCmd);
4166 newcmd->subtype = AT_AddConstraint;
4167 newcmd->def = (Node *) def;
4168 newcmds = lappend(newcmds, newcmd);
4169 }
4171 {
4172 newcmd = makeNode(AlterTableCmd);
4173 newcmd->subtype = AT_AddConstraint;
4174 newcmd->def = (Node *) def;
4175 newcmds = lappend(newcmds, newcmd);
4176 }
4177
4178 /* Close rel */
4179 relation_close(rel, NoLock);
4180
4181 /*
4182 * Output results.
4183 */
4184 stmt->cmds = newcmds;
4185
4186 *beforeStmts = cxt.blist;
4187 *afterStmts = list_concat(cxt.alist, save_alist);
4188
4189 return stmt;
4190}
#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:2093
AttrNumber get_attnum(Oid relid, const char *attname)
Definition: lsyscache.c:949
Oid get_rel_namespace(Oid relid)
Definition: lsyscache.c:2117
DefElem * makeDefElem(char *name, Node *arg, int location)
Definition: makefuncs.c:637
RangeVar * makeRangeVar(char *schemaname, char *relname, int location)
Definition: makefuncs.c:473
TypeName * makeTypeNameFromOid(Oid typeOid, int32 typmod)
Definition: makefuncs.c:547
#define IsA(nodeptr, _type_)
Definition: nodes.h:164
#define nodeTag(nodeptr)
Definition: nodes.h:139
#define castNode(_type_, nodeptr)
Definition: nodes.h:182
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:119
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 transformPartitionCmdForSplit(CreateStmtContext *cxt, PartitionCmd *partcmd)
static void transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
static void transformPartitionCmdForMerge(CreateStmtContext *cxt, PartitionCmd *partcmd)
static void transformPartitionCmd(CreateStmtContext *cxt, PartitionBoundSpec *bound)
static void transformCheckConstraints(CreateStmtContext *cxt, bool skipValidation)
static void transformFKConstraints(CreateStmtContext *cxt, bool skipValidation, bool isAddConstraint)
@ CONSTR_FOREIGN
Definition: parsenodes.h:2837
@ AT_AddIndexConstraint
Definition: parsenodes.h:2464
@ AT_MergePartitions
Definition: parsenodes.h:2506
@ AT_SetIdentity
Definition: parsenodes.h:2508
@ AT_AddIndex
Definition: parsenodes.h:2457
@ AT_AddIdentity
Definition: parsenodes.h:2507
@ AT_AlterColumnType
Definition: parsenodes.h:2467
@ AT_DetachPartition
Definition: parsenodes.h:2503
@ AT_AttachPartition
Definition: parsenodes.h:2502
@ AT_SplitPartition
Definition: parsenodes.h:2505
@ AT_AddColumn
Definition: parsenodes.h:2443
Oid getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
Definition: pg_depend.c:945
#define lfirst_node(type, lc)
Definition: pg_list.h:176
static int list_length(const List *l)
Definition: pg_list.h:152
#define list_make1(x1)
Definition: pg_list.h:212
#define foreach_node(type, var, lst)
Definition: pg_list.h:496
#define RelationGetForm(relation)
Definition: rel.h:509
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:205
List * options
Definition: parsenodes.h:3264
RangeVar * sequence
Definition: parsenodes.h:3263
bool for_identity
Definition: parsenodes.h:3265
char identity
Definition: parsenodes.h:765
List * constraints
Definition: parsenodes.h:771
Node * cooked_default
Definition: parsenodes.h:764
char * colname
Definition: parsenodes.h:754
TypeName * typeName
Definition: parsenodes.h:755
Node * raw_default
Definition: parsenodes.h:763
List * options
Definition: parsenodes.h:2883
char generated_when
Definition: parsenodes.h:2873
IndexStmt * pkey
Definition: parse_utilcmd.c:95
const char * stmtType
Definition: parse_utilcmd.c:79
RangeVar * relation
Definition: parse_utilcmd.c:80
ParseState * pstate
Definition: parse_utilcmd.c:78
PartitionBoundSpec * partbound
Definition: parse_utilcmd.c:97
char * defname
Definition: parsenodes.h:843
Oid indexOid
Definition: parsenodes.h:3524
const char * p_sourcetext
Definition: parse_node.h:195
PartitionBoundSpec * bound
Definition: parsenodes.h:992
List * partlist
Definition: parsenodes.h:1000

References AccessShareLock, addNSItemToQuery(), addRangeTableEntryForRelation(), CreateStmtContext::alist, AT_AddColumn, AT_AddConstraint, AT_AddIdentity, AT_AddIndex, AT_AddIndexConstraint, AT_AlterColumnType, AT_AttachPartition, AT_DetachPartition, AT_MergePartitions, AT_SetIdentity, AT_SplitPartition, attnum, CreateStmtContext::blist, PartitionCmd::bound, castNode, CreateStmtContext::ckconstraints, 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, if(), IndexStmt::indexOid, CreateStmtContext::inhRelations, InvalidAttrNumber, IsA, CreateStmtContext::isalter, CreateStmtContext::isforeign, CreateStmtContext::ispartitioned, CreateStmtContext::ixconstraints, lappend(), lfirst, lfirst_node, CreateStmtContext::likeclauses, list_concat(), list_length(), list_make1, make_parsestate(), makeDefElem(), makeNode, makeRangeVar(), makeTypeNameFromOid(), AlterSeqStmt::missing_ok, AlterTableCmd::name, NIL, CreateStmtContext::nnconstraints, nodeTag, NoLock, CreateStmtContext::ofType, OidIsValid, Constraint::options, AlterSeqStmt::options, ParseState::p_sourcetext, CreateStmtContext::partbound, PartitionCmd::partlist, CreateStmtContext::pkey, CreateStmtContext::pstate, ColumnDef::raw_default, RelationData::rd_rel, CreateStmtContext::rel, CreateStmtContext::relation, relation_close(), relation_open(), RelationGetDescr, RelationGetForm, RelationGetRelationName, AlterSeqStmt::sequence, stmt, CreateStmtContext::stmtType, AlterTableCmd::subtype, transformCheckConstraints(), transformColumnDefinition(), transformExpr(), transformFKConstraints(), transformIndexConstraints(), transformIndexStmt(), transformPartitionCmd(), transformPartitionCmdForMerge(), transformPartitionCmdForSplit(), transformTableConstraint(), TupleDescAttr(), ColumnDef::typeName, and typenameTypeId().

Referenced by ATParseTransformCmd(), and ATPostAlterTypeParse().

◆ transformCreateSchemaStmtElements()

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

Definition at line 4415 of file parse_utilcmd.c.

4416{
4418 List *result;
4419 ListCell *elements;
4420
4421 cxt.schemaname = schemaName;
4422 cxt.sequences = NIL;
4423 cxt.tables = NIL;
4424 cxt.views = NIL;
4425 cxt.indexes = NIL;
4426 cxt.triggers = NIL;
4427 cxt.grants = NIL;
4428
4429 /*
4430 * Run through each schema element in the schema element list. Separate
4431 * statements by type, and do preliminary analysis.
4432 */
4433 foreach(elements, schemaElts)
4434 {
4435 Node *element = lfirst(elements);
4436
4437 switch (nodeTag(element))
4438 {
4439 case T_CreateSeqStmt:
4440 {
4442
4444 cxt.sequences = lappend(cxt.sequences, element);
4445 }
4446 break;
4447
4448 case T_CreateStmt:
4449 {
4450 CreateStmt *elp = (CreateStmt *) element;
4451
4453
4454 /*
4455 * XXX todo: deal with constraints
4456 */
4457 cxt.tables = lappend(cxt.tables, element);
4458 }
4459 break;
4460
4461 case T_ViewStmt:
4462 {
4463 ViewStmt *elp = (ViewStmt *) element;
4464
4466
4467 /*
4468 * XXX todo: deal with references between views
4469 */
4470 cxt.views = lappend(cxt.views, element);
4471 }
4472 break;
4473
4474 case T_IndexStmt:
4475 {
4476 IndexStmt *elp = (IndexStmt *) element;
4477
4479 cxt.indexes = lappend(cxt.indexes, element);
4480 }
4481 break;
4482
4483 case T_CreateTrigStmt:
4484 {
4486
4488 cxt.triggers = lappend(cxt.triggers, element);
4489 }
4490 break;
4491
4492 case T_GrantStmt:
4493 cxt.grants = lappend(cxt.grants, element);
4494 break;
4495
4496 default:
4497 elog(ERROR, "unrecognized node type: %d",
4498 (int) nodeTag(element));
4499 }
4500 }
4501
4502 result = NIL;
4503 result = list_concat(result, cxt.sequences);
4504 result = list_concat(result, cxt.tables);
4505 result = list_concat(result, cxt.views);
4506 result = list_concat(result, cxt.indexes);
4507 result = list_concat(result, cxt.triggers);
4508 result = list_concat(result, cxt.grants);
4509
4510 return result;
4511}
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:3253
RangeVar * relation
Definition: parsenodes.h:2778
RangeVar * relation
Definition: parsenodes.h:3140
RangeVar * relation
Definition: parsenodes.h:3514
RangeVar * view
Definition: parsenodes.h:3907

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

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

3053{
3054 ParseState *pstate;
3055 ParseNamespaceItem *nsitem;
3056 ListCell *l;
3057 Relation rel;
3058
3059 /* Nothing to do if statement already transformed. */
3060 if (stmt->transformed)
3061 return stmt;
3062
3063 /* Set up pstate */
3064 pstate = make_parsestate(NULL);
3065 pstate->p_sourcetext = queryString;
3066
3067 /*
3068 * Put the parent table into the rtable so that the expressions can refer
3069 * to its fields without qualification. Caller is responsible for locking
3070 * relation, but we still need to open it.
3071 */
3072 rel = relation_open(relid, NoLock);
3073 nsitem = addRangeTableEntryForRelation(pstate, rel,
3075 NULL, false, true);
3076
3077 /* no to join list, yes to namespaces */
3078 addNSItemToQuery(pstate, nsitem, false, true, true);
3079
3080 /* take care of the where clause */
3081 if (stmt->whereClause)
3082 {
3083 stmt->whereClause = transformWhereClause(pstate,
3084 stmt->whereClause,
3086 "WHERE");
3087 /* we have to fix its collations too */
3088 assign_expr_collations(pstate, stmt->whereClause);
3089 }
3090
3091 /* take care of any index expressions */
3092 foreach(l, stmt->indexParams)
3093 {
3094 IndexElem *ielem = (IndexElem *) lfirst(l);
3095
3096 if (ielem->expr)
3097 {
3098 /* Extract preliminary index col name before transforming expr */
3099 if (ielem->indexcolname == NULL)
3100 ielem->indexcolname = FigureIndexColname(ielem->expr);
3101
3102 /* Now do parse transformation of the expression */
3103 ielem->expr = transformExpr(pstate, ielem->expr,
3105
3106 /* We have to fix its collations too */
3107 assign_expr_collations(pstate, ielem->expr);
3108
3109 /*
3110 * transformExpr() should have already rejected subqueries,
3111 * aggregates, window functions, and SRFs, based on the EXPR_KIND_
3112 * for an index expression.
3113 *
3114 * DefineIndex() will make more checks.
3115 */
3116 }
3117 }
3118
3119 /*
3120 * Check that only the base rel is mentioned. (This should be dead code
3121 * now that add_missing_from is history.)
3122 */
3123 if (list_length(pstate->p_rtable) != 1)
3124 ereport(ERROR,
3125 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
3126 errmsg("index expressions and predicates can refer only to the table being indexed")));
3127
3128 free_parsestate(pstate);
3129
3130 /* Close relation */
3131 table_close(rel, NoLock);
3132
3133 /* Mark statement as successfully transformed */
3134 stmt->transformed = true;
3135
3136 return stmt;
3137}
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)
List * p_rtable
Definition: parse_node.h:196

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

4593{
4594 PartitionBoundSpec *result_spec;
4596 char strategy = get_partition_strategy(key);
4597 int partnatts = get_partition_natts(key);
4598 List *partexprs = get_partition_exprs(key);
4599
4600 /* Avoid scribbling on input */
4601 result_spec = copyObject(spec);
4602
4603 if (spec->is_default)
4604 {
4605 /*
4606 * Hash partitioning does not support a default partition; there's no
4607 * use case for it (since the set of partitions to create is perfectly
4608 * defined), and if users do get into it accidentally, it's hard to
4609 * back out from it afterwards.
4610 */
4611 if (strategy == PARTITION_STRATEGY_HASH)
4612 ereport(ERROR,
4613 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4614 errmsg("a hash-partitioned table may not have a default partition")));
4615
4616 /*
4617 * In case of the default partition, parser had no way to identify the
4618 * partition strategy. Assign the parent's strategy to the default
4619 * partition bound spec.
4620 */
4621 result_spec->strategy = strategy;
4622
4623 return result_spec;
4624 }
4625
4626 if (strategy == PARTITION_STRATEGY_HASH)
4627 {
4628 if (spec->strategy != PARTITION_STRATEGY_HASH)
4629 ereport(ERROR,
4630 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4631 errmsg("invalid bound specification for a hash partition"),
4632 parser_errposition(pstate, exprLocation((Node *) spec))));
4633
4634 if (spec->modulus <= 0)
4635 ereport(ERROR,
4636 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4637 errmsg("modulus for hash partition must be an integer value greater than zero")));
4638
4639 Assert(spec->remainder >= 0);
4640
4641 if (spec->remainder >= spec->modulus)
4642 ereport(ERROR,
4643 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4644 errmsg("remainder for hash partition must be less than modulus")));
4645 }
4646 else if (strategy == PARTITION_STRATEGY_LIST)
4647 {
4648 ListCell *cell;
4649 char *colname;
4650 Oid coltype;
4651 int32 coltypmod;
4652 Oid partcollation;
4653
4654 if (spec->strategy != PARTITION_STRATEGY_LIST)
4655 ereport(ERROR,
4656 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4657 errmsg("invalid bound specification for a list partition"),
4658 parser_errposition(pstate, exprLocation((Node *) spec))));
4659
4660 /* Get the only column's name in case we need to output an error */
4661 if (key->partattrs[0] != 0)
4662 colname = get_attname(RelationGetRelid(parent),
4663 key->partattrs[0], false);
4664 else
4665 colname = deparse_expression((Node *) linitial(partexprs),
4667 RelationGetRelid(parent)),
4668 false, false);
4669 /* Need its type data too */
4670 coltype = get_partition_col_typid(key, 0);
4671 coltypmod = get_partition_col_typmod(key, 0);
4672 partcollation = get_partition_col_collation(key, 0);
4673
4674 result_spec->listdatums = NIL;
4675 foreach(cell, spec->listdatums)
4676 {
4677 Node *expr = lfirst(cell);
4678 Const *value;
4679 ListCell *cell2;
4680 bool duplicate;
4681
4682 value = transformPartitionBoundValue(pstate, expr,
4683 colname, coltype, coltypmod,
4684 partcollation);
4685
4686 /* Don't add to the result if the value is a duplicate */
4687 duplicate = false;
4688 foreach(cell2, result_spec->listdatums)
4689 {
4690 Const *value2 = lfirst_node(Const, cell2);
4691
4692 if (equal(value, value2))
4693 {
4694 duplicate = true;
4695 break;
4696 }
4697 }
4698 if (duplicate)
4699 continue;
4700
4701 result_spec->listdatums = lappend(result_spec->listdatums,
4702 value);
4703 }
4704 }
4705 else if (strategy == PARTITION_STRATEGY_RANGE)
4706 {
4708 ereport(ERROR,
4709 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4710 errmsg("invalid bound specification for a range partition"),
4711 parser_errposition(pstate, exprLocation((Node *) spec))));
4712
4713 if (list_length(spec->lowerdatums) != partnatts)
4714 ereport(ERROR,
4715 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4716 errmsg("FROM must specify exactly one value per partitioning column")));
4717 if (list_length(spec->upperdatums) != partnatts)
4718 ereport(ERROR,
4719 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4720 errmsg("TO must specify exactly one value per partitioning column")));
4721
4722 /*
4723 * Convert raw parse nodes into PartitionRangeDatum nodes and perform
4724 * any necessary validation.
4725 */
4726 result_spec->lowerdatums =
4728 parent);
4729 result_spec->upperdatums =
4731 parent);
4732 }
4733 else
4734 elog(ERROR, "unexpected partition strategy: %d", (int) strategy);
4735
4736 return result_spec;
4737}
int32_t int32
Definition: c.h:548
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
static struct @171 value
int exprLocation(const Node *expr)
Definition: nodeFuncs.c:1384
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:902
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:900
@ PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:901
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:3711
char * deparse_expression(Node *expr, List *dpcontext, bool forceprefix, bool showimplicit)
Definition: ruleutils.c:3648

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

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

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

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