PostgreSQL Source Code git master
Loading...
Searching...
No Matches
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 (ParseState *pstate, 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

Definition at line 19 of file parse_utilcmd.h.

Function Documentation

◆ expandTableLikeClause()

List * expandTableLikeClause ( RangeVar heapRel,
TableLikeClause table_like_clause 
)
extern

Definition at line 1342 of file parse_utilcmd.c.

1343{
1344 List *result = NIL;
1345 List *atsubcmds = NIL;
1347 Relation relation;
1350 TupleConstr *constr;
1351 AttrMap *attmap;
1352 char *comment;
1353
1354 /*
1355 * Open the relation referenced by the LIKE clause. We should still have
1356 * the table lock obtained by transformTableLikeClause (and this'll throw
1357 * an assertion failure if not). Hence, no need to recheck privileges
1358 * etc. We must open the rel by OID not name, to be sure we get the same
1359 * table.
1360 */
1361 if (!OidIsValid(table_like_clause->relationOid))
1362 elog(ERROR, "expandTableLikeClause called on untransformed LIKE clause");
1363
1364 relation = relation_open(table_like_clause->relationOid, NoLock);
1365
1366 tupleDesc = RelationGetDescr(relation);
1367 constr = tupleDesc->constr;
1368
1369 /*
1370 * Open the newly-created child relation; we have lock on that too.
1371 */
1372 childrel = relation_openrv(heapRel, NoLock);
1373
1374 /*
1375 * Construct a map from the LIKE relation's attnos to the child rel's.
1376 * This re-checks type match etc, although it shouldn't be possible to
1377 * have a failure since both tables are locked.
1378 */
1380 tupleDesc,
1381 false);
1382
1383 /*
1384 * Process defaults, if required.
1385 */
1386 if ((table_like_clause->options &
1388 constr != NULL)
1389 {
1390 for (parent_attno = 1; parent_attno <= tupleDesc->natts;
1391 parent_attno++)
1392 {
1394 parent_attno - 1);
1395
1396 /*
1397 * Ignore dropped columns in the parent.
1398 */
1399 if (attribute->attisdropped)
1400 continue;
1401
1402 /*
1403 * Copy default, if present and it should be copied. We have
1404 * separate options for plain default expressions and GENERATED
1405 * defaults.
1406 */
1407 if (attribute->atthasdef &&
1408 (attribute->attgenerated ?
1411 {
1414 bool found_whole_row;
1415
1417 if (this_default == NULL)
1418 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
1420
1423 atsubcmd->num = attmap->attnums[parent_attno - 1];
1425 1, 0,
1426 attmap,
1427 InvalidOid,
1428 &found_whole_row);
1429
1430 /*
1431 * Prevent this for the same reason as for constraints below.
1432 * Note that defaults cannot contain any vars, so it's OK that
1433 * the error message refers to generated columns.
1434 */
1435 if (found_whole_row)
1436 ereport(ERROR,
1438 errmsg("cannot convert whole-row table reference"),
1439 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
1440 NameStr(attribute->attname),
1441 RelationGetRelationName(relation))));
1442
1444 }
1445 }
1446 }
1447
1448 /*
1449 * Copy CHECK constraints if requested, being careful to adjust attribute
1450 * numbers so they match the child.
1451 */
1453 constr != NULL)
1454 {
1455 int ccnum;
1456
1457 for (ccnum = 0; ccnum < constr->num_check; ccnum++)
1458 {
1459 char *ccname = constr->check[ccnum].ccname;
1460 char *ccbin = constr->check[ccnum].ccbin;
1461 bool ccenforced = constr->check[ccnum].ccenforced;
1462 bool ccnoinherit = constr->check[ccnum].ccnoinherit;
1464 bool found_whole_row;
1465 Constraint *n;
1467
1469 1, 0,
1470 attmap,
1471 InvalidOid, &found_whole_row);
1472
1473 /*
1474 * We reject whole-row variables because the whole point of LIKE
1475 * is that the new table's rowtype might later diverge from the
1476 * parent's. So, while translation might be possible right now,
1477 * it wouldn't be possible to guarantee it would work in future.
1478 */
1479 if (found_whole_row)
1480 ereport(ERROR,
1482 errmsg("cannot convert whole-row table reference"),
1483 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
1484 ccname,
1485 RelationGetRelationName(relation))));
1486
1487 n = makeNode(Constraint);
1488 n->contype = CONSTR_CHECK;
1489 n->conname = pstrdup(ccname);
1490 n->location = -1;
1491 n->is_enforced = ccenforced;
1492 n->initially_valid = ccenforced; /* sic */
1493 n->is_no_inherit = ccnoinherit;
1494 n->raw_expr = NULL;
1496
1497 /* We can skip validation, since the new table should be empty. */
1498 n->skip_validation = true;
1499
1501 atsubcmd->subtype = AT_AddConstraint;
1502 atsubcmd->def = (Node *) n;
1504
1505 /* Copy comment on constraint */
1508 n->conname, false),
1510 0)) != NULL)
1511 {
1513
1514 stmt->objtype = OBJECT_TABCONSTRAINT;
1515 stmt->object = (Node *) list_make3(makeString(heapRel->schemaname),
1516 makeString(heapRel->relname),
1517 makeString(n->conname));
1518 stmt->comment = comment;
1519
1521 }
1522 }
1523 }
1524
1525 /*
1526 * If we generated any ALTER TABLE actions above, wrap them into a single
1527 * ALTER TABLE command. Stick it at the front of the result, so it runs
1528 * before any CommentStmts we made above.
1529 */
1530 if (atsubcmds)
1531 {
1533
1534 atcmd->relation = copyObject(heapRel);
1535 atcmd->cmds = atsubcmds;
1536 atcmd->objtype = OBJECT_TABLE;
1537 atcmd->missing_ok = false;
1539 }
1540
1541 /*
1542 * Process indexes if required.
1543 */
1545 relation->rd_rel->relhasindex &&
1546 childrel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
1547 {
1549 ListCell *l;
1550
1552
1553 foreach(l, parent_indexes)
1554 {
1558
1560
1561 /* Build CREATE INDEX statement to recreate the parent_index */
1564 attmap,
1565 NULL);
1566
1567 /* Copy comment on index, if requested */
1569 {
1571
1572 /*
1573 * We make use of IndexStmt's idxcomment option, so as not to
1574 * need to know now what name the index will have.
1575 */
1576 index_stmt->idxcomment = comment;
1577 }
1578
1580
1582 }
1583 }
1584
1585 /*
1586 * Process extended statistics if required.
1587 */
1589 {
1591 ListCell *l;
1592
1594
1595 foreach(l, parent_extstats)
1596 {
1599
1603 attmap);
1604
1605 /* Copy comment on statistics object, if requested */
1607 {
1609
1610 /*
1611 * We make use of CreateStatsStmt's stxcomment option, so as
1612 * not to need to know now what name the statistics will have.
1613 */
1614 stats_stmt->stxcomment = comment;
1615 }
1616
1618 }
1619
1621 }
1622
1623 /* Done with child rel */
1625
1626 /*
1627 * Close the parent rel, but keep our AccessShareLock on it until xact
1628 * commit. That will prevent someone else from deleting or ALTERing the
1629 * parent before the child is committed.
1630 */
1631 table_close(relation, NoLock);
1632
1633 return result;
1634}
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:894
#define OidIsValid(objectId)
Definition c.h:917
uint32 result
char * GetComment(Oid oid, Oid classoid, int32 subid)
Definition comment.c:420
int errcode(int sqlerrcode)
Definition elog.c:875
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
#define stmt
#define comment
void index_close(Relation relation, LOCKMODE lockmode)
Definition indexam.c:178
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition indexam.c:134
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:1910
#define copyObject(obj)
Definition nodes.h:230
#define makeNode(_type_)
Definition nodes.h:159
static char * errmsg
char * nodeToString(const void *obj)
Definition outfuncs.c:811
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
@ OBJECT_TABLE
@ OBJECT_TABCONSTRAINT
@ AT_AddConstraint
@ AT_CookedColumnDefault
@ CREATE_TABLE_LIKE_COMMENTS
Definition parsenodes.h:806
@ CREATE_TABLE_LIKE_GENERATED
Definition parsenodes.h:810
@ CREATE_TABLE_LIKE_INDEXES
Definition parsenodes.h:812
@ CREATE_TABLE_LIKE_DEFAULTS
Definition parsenodes.h:809
@ CREATE_TABLE_LIKE_STATISTICS
Definition parsenodes.h:813
@ CREATE_TABLE_LIKE_CONSTRAINTS
Definition parsenodes.h:808
FormData_pg_attribute * Form_pg_attribute
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:248
#define lfirst_oid(lc)
Definition pg_list.h:174
#define InvalidOid
unsigned int Oid
static int fb(int x)
void * stringToNode(const char *str)
Definition read.c:90
#define RelationGetRelid(relation)
Definition rel.h:516
#define RelationGetDescr(relation)
Definition rel.h:542
#define RelationGetRelationName(relation)
Definition rel.h:550
List * RelationGetIndexList(Relation relation)
Definition relcache.c:4848
List * RelationGetStatExtList(Relation relation)
Definition relcache.c:4989
Node * map_variable_attnos(Node *node, int target_varno, int sublevels_up, const AttrMap *attno_map, Oid to_rowtype, bool *found_whole_row)
Relation relation_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition relation.c:138
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition relation.c:48
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
ConstrType contype
bool is_no_inherit
bool is_enforced
char * cooked_expr
bool initially_valid
bool skip_validation
Node * raw_expr
char * conname
Definition pg_list.h:54
Definition nodes.h:133
char * relname
Definition primnodes.h:84
char * schemaname
Definition primnodes.h:81
Form_pg_class rd_rel
Definition rel.h:111
ConstrCheck * check
Definition tupdesc.h:41
uint16 num_check
Definition tupdesc.h:44
void table_close(Relation relation, LOCKMODE lockmode)
Definition table.c:126
Node * TupleDescGetDefault(TupleDesc tupdesc, AttrNumber attnum)
Definition tupdesc.c:1161
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:178
String * makeString(char *str)
Definition value.c:63

References AccessShareLock, AT_AddConstraint, AT_CookedColumnDefault, build_attrmap_by_name(), ConstrCheck::ccbin, ConstrCheck::ccenforced, ConstrCheck::ccname, ConstrCheck::ccnoinherit, TupleConstr::check, comment, Constraint::conname, 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, elog, ereport, errcode(), errdetail(), errmsg, ERROR, fb(), generateClonedExtStatsStmt(), generateClonedIndexStmt(), get_relation_constraint_oid(), GetComment(), 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(), NameStr, NIL, nodeToString(), NoLock, TupleConstr::num_check, OBJECT_TABCONSTRAINT, OBJECT_TABLE, OidIsValid, pstrdup(), Constraint::raw_expr, RelationData::rd_rel, relation_open(), relation_openrv(), RelationGetDescr, RelationGetIndexList(), RelationGetRelationName, RelationGetRelid, RelationGetStatExtList(), RangeVar::relname, result, RangeVar::schemaname, Constraint::skip_validation, stmt, stringToNode(), table_close(), TupleDescAttr(), and TupleDescGetDefault().

Referenced by ProcessUtilitySlow().

◆ generateClonedIndexStmt()

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

Definition at line 1690 of file parse_utilcmd.c.

1693{
1704 List *indexprs;
1706 Oid indrelid;
1707 int keyno;
1709 Datum datum;
1710 bool isnull;
1711
1712 if (constraintOid)
1714
1715 /*
1716 * Fetch pg_class tuple of source index. We can't use the copy in the
1717 * relcache entry because it doesn't include optional fields.
1718 */
1721 elog(ERROR, "cache lookup failed for relation %u", source_relid);
1723
1724 /* Fetch pg_index tuple for source index from relcache entry */
1725 ht_idx = source_idx->rd_indextuple;
1727 indrelid = idxrec->indrelid;
1728
1729 /* Fetch the pg_am tuple of the index' access method */
1731 if (!HeapTupleIsValid(ht_am))
1732 elog(ERROR, "cache lookup failed for access method %u",
1733 idxrelrec->relam);
1735
1736 /* Extract indcollation from the pg_index tuple */
1740
1741 /* Extract indclass from the pg_index tuple */
1743 indclass = (oidvector *) DatumGetPointer(datum);
1744
1745 /* Begin building the IndexStmt */
1747 index->relation = heapRel;
1748 index->accessMethod = pstrdup(NameStr(amrec->amname));
1749 if (OidIsValid(idxrelrec->reltablespace))
1750 index->tableSpace = get_tablespace_name(idxrelrec->reltablespace);
1751 else
1752 index->tableSpace = NULL;
1753 index->excludeOpNames = NIL;
1754 index->idxcomment = NULL;
1755 index->indexOid = InvalidOid;
1756 index->oldNumber = InvalidRelFileNumber;
1757 index->oldCreateSubid = InvalidSubTransactionId;
1758 index->oldFirstRelfilelocatorSubid = InvalidSubTransactionId;
1759 index->unique = idxrec->indisunique;
1760 index->nulls_not_distinct = idxrec->indnullsnotdistinct;
1761 index->primary = idxrec->indisprimary;
1762 index->iswithoutoverlaps = (idxrec->indisprimary || idxrec->indisunique) && idxrec->indisexclusion;
1763 index->transformed = true; /* don't need transformIndexStmt */
1764 index->concurrent = false;
1765 index->if_not_exists = false;
1766 index->reset_default_tblspc = false;
1767
1768 /*
1769 * We don't try to preserve the name of the source index; instead, just
1770 * let DefineIndex() choose a reasonable name. (If we tried to preserve
1771 * the name, we'd get duplicate-relation-name failures unless the source
1772 * table was in a different schema.)
1773 */
1774 index->idxname = NULL;
1775
1776 /*
1777 * If the index is marked PRIMARY or has an exclusion condition, it's
1778 * certainly from a constraint; else, if it's not marked UNIQUE, it
1779 * certainly isn't. If it is or might be from a constraint, we have to
1780 * fetch the pg_constraint record.
1781 */
1782 if (index->primary || index->unique || idxrec->indisexclusion)
1783 {
1785
1787 {
1790
1791 if (constraintOid)
1793
1797 elog(ERROR, "cache lookup failed for constraint %u",
1798 constraintId);
1800
1801 index->isconstraint = true;
1802 index->deferrable = conrec->condeferrable;
1803 index->initdeferred = conrec->condeferred;
1804
1805 /* If it's an exclusion constraint, we need the operator names */
1806 if (idxrec->indisexclusion)
1807 {
1808 Datum *elems;
1809 int nElems;
1810 int i;
1811
1812 Assert(conrec->contype == CONSTRAINT_EXCLUSION ||
1813 (index->iswithoutoverlaps &&
1814 (conrec->contype == CONSTRAINT_PRIMARY || conrec->contype == CONSTRAINT_UNIQUE)));
1815 /* Extract operator OIDs from the pg_constraint tuple */
1818 deconstruct_array_builtin(DatumGetArrayTypeP(datum), OIDOID, &elems, NULL, &nElems);
1819
1820 for (i = 0; i < nElems; i++)
1821 {
1822 Oid operid = DatumGetObjectId(elems[i]);
1825 char *oprname;
1826 char *nspname;
1827 List *namelist;
1828
1832 elog(ERROR, "cache lookup failed for operator %u",
1833 operid);
1835 oprname = pstrdup(NameStr(operform->oprname));
1836 /* For simplicity we always schema-qualify the op name */
1837 nspname = get_namespace_name(operform->oprnamespace);
1838 namelist = list_make2(makeString(nspname),
1839 makeString(oprname));
1840 index->excludeOpNames = lappend(index->excludeOpNames,
1841 namelist);
1843 }
1844 }
1845
1847 }
1848 else
1849 index->isconstraint = false;
1850 }
1851 else
1852 index->isconstraint = false;
1853
1854 /* Get the index expressions, if any */
1856 Anum_pg_index_indexprs, &isnull);
1857 if (!isnull)
1858 {
1859 char *exprsString;
1860
1863 }
1864 else
1865 indexprs = NIL;
1866
1867 /* Build the list of IndexElem */
1868 index->indexParams = NIL;
1869 index->indexIncludingParams = NIL;
1870
1872 for (keyno = 0; keyno < idxrec->indnkeyatts; keyno++)
1873 {
1875 AttrNumber attnum = idxrec->indkey.values[keyno];
1877 keyno);
1878 int16 opt = source_idx->rd_indoption[keyno];
1879
1881
1883 {
1884 /* Simple index column */
1885 char *attname;
1886
1889
1890 iparam->name = attname;
1891 iparam->expr = NULL;
1892 }
1893 else
1894 {
1895 /* Expressional index */
1896 Node *indexkey;
1897 bool found_whole_row;
1898
1899 if (indexpr_item == NULL)
1900 elog(ERROR, "too few entries in indexprs list");
1903
1904 /* Adjust Vars to match new table's column numbering */
1906 1, 0,
1907 attmap,
1908 InvalidOid, &found_whole_row);
1909
1910 /* As in expandTableLikeClause, reject whole-row variables */
1911 if (found_whole_row)
1912 ereport(ERROR,
1914 errmsg("cannot convert whole-row table reference"),
1915 errdetail("Index \"%s\" contains a whole-row table reference.",
1917
1918 iparam->name = NULL;
1919 iparam->expr = indexkey;
1920
1922 }
1923
1924 /* Copy the original index column name */
1925 iparam->indexcolname = pstrdup(NameStr(attr->attname));
1926
1927 /* Add the collation name, if non-default */
1928 iparam->collation = get_collation(indcollation->values[keyno], keycoltype);
1929
1930 /* Add the operator class name, if non-default */
1931 iparam->opclass = get_opclass(indclass->values[keyno], keycoltype);
1932 iparam->opclassopts =
1934
1935 iparam->ordering = SORTBY_DEFAULT;
1936 iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
1937
1938 /* Adjust options if necessary */
1939 if (source_idx->rd_indam->amcanorder)
1940 {
1941 /*
1942 * If it supports sort ordering, copy DESC and NULLS opts. Don't
1943 * set non-default settings unnecessarily, though, so as to
1944 * improve the chance of recognizing equivalence to constraint
1945 * indexes.
1946 */
1947 if (opt & INDOPTION_DESC)
1948 {
1949 iparam->ordering = SORTBY_DESC;
1950 if ((opt & INDOPTION_NULLS_FIRST) == 0)
1951 iparam->nulls_ordering = SORTBY_NULLS_LAST;
1952 }
1953 else
1954 {
1955 if (opt & INDOPTION_NULLS_FIRST)
1956 iparam->nulls_ordering = SORTBY_NULLS_FIRST;
1957 }
1958 }
1959
1960 iparam->location = -1;
1961
1962 index->indexParams = lappend(index->indexParams, iparam);
1963 }
1964
1965 /* Handle included columns separately */
1966 for (keyno = idxrec->indnkeyatts; keyno < idxrec->indnatts; keyno++)
1967 {
1969 AttrNumber attnum = idxrec->indkey.values[keyno];
1971 keyno);
1972
1974
1976 {
1977 /* Simple index column */
1978 char *attname;
1979
1981
1982 iparam->name = attname;
1983 iparam->expr = NULL;
1984 }
1985 else
1986 ereport(ERROR,
1988 errmsg("expressions are not supported in included columns")));
1989
1990 /* Copy the original index column name */
1991 iparam->indexcolname = pstrdup(NameStr(attr->attname));
1992
1993 iparam->location = -1;
1994
1995 index->indexIncludingParams = lappend(index->indexIncludingParams, iparam);
1996 }
1997 /* Copy reloptions if any */
1999 Anum_pg_class_reloptions, &isnull);
2000 if (!isnull)
2001 index->options = untransformRelOptions(datum);
2002
2003 /* If it's a partial index, decompile and append the predicate */
2005 Anum_pg_index_indpred, &isnull);
2006 if (!isnull)
2007 {
2008 char *pred_str;
2009 Node *pred_tree;
2010 bool found_whole_row;
2011
2012 /* Convert text string to node tree */
2015
2016 /* Adjust Vars to match new table's column numbering */
2018 1, 0,
2019 attmap,
2020 InvalidOid, &found_whole_row);
2021
2022 /* As in expandTableLikeClause, reject whole-row variables */
2023 if (found_whole_row)
2024 ereport(ERROR,
2026 errmsg("cannot convert whole-row table reference"),
2027 errdetail("Index \"%s\" contains a whole-row table reference.",
2029
2030 index->whereClause = pred_tree;
2031 }
2032
2033 /* Clean up */
2036
2037 return index;
2038}
#define DatumGetArrayTypeP(X)
Definition array.h:261
void deconstruct_array_builtin(const ArrayType *array, Oid elmtype, Datum **elemsp, bool **nullsp, int *nelemsp)
#define AttributeNumberIsValid(attributeNumber)
Definition attnum.h:34
char * get_tablespace_name(Oid spc_oid)
#define TextDatumGetCString(d)
Definition builtins.h:99
#define InvalidSubTransactionId
Definition c.h:801
#define Assert(condition)
Definition c.h:1002
int16_t int16
Definition c.h:678
#define HeapTupleIsValid(tuple)
Definition htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
int i
Definition isn.c:77
Datum get_attoptions(Oid relid, int16 attnum)
Definition lsyscache.c:1196
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition lsyscache.c:1053
char * get_namespace_name(Oid nspid)
Definition lsyscache.c:3682
Oid get_atttype(Oid relid, AttrNumber attnum)
Definition lsyscache.c:1139
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
END_CATALOG_STRUCT typedef FormData_pg_am * Form_pg_am
Definition pg_am.h:52
NameData attname
int16 attnum
FormData_pg_class * Form_pg_class
Definition pg_class.h:160
END_CATALOG_STRUCT typedef FormData_pg_constraint * Form_pg_constraint
Oid get_index_constraint(Oid indexId)
Definition pg_depend.c:1192
END_CATALOG_STRUCT typedef FormData_pg_index * Form_pg_index
Definition pg_index.h:74
#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:375
#define list_make2(x1, x2)
Definition pg_list.h:246
END_CATALOG_STRUCT typedef FormData_pg_operator * Form_pg_operator
Definition pg_operator.h:87
static Oid DatumGetObjectId(Datum X)
Definition postgres.h:242
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
uint64_t Datum
Definition postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
List * untransformRelOptions(Datum options)
#define InvalidRelFileNumber
Definition relpath.h:26
Definition type.h:97
Definition c.h:874
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:265
Datum SysCacheGetAttrNotNull(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition syscache.c:626
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:221
Datum SysCacheGetAttr(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition syscache.c:596

References Assert, attname, attnum, AttributeNumberIsValid, DatumGetArrayTypeP, DatumGetObjectId(), DatumGetPointer(), deconstruct_array_builtin(), elog, ereport, errcode(), errdetail(), errmsg, ERROR, exprType(), fb(), Form_pg_am, Form_pg_constraint, Form_pg_index, Form_pg_operator, get_attname(), get_attoptions(), get_atttype(), get_collation(), get_index_constraint(), get_namespace_name(), get_opclass(), get_tablespace_name(), GETSTRUCT(), HeapTupleIsValid, i, InvalidOid, InvalidRelFileNumber, InvalidSubTransactionId, lappend(), lfirst, list_head(), list_make2, lnext(), makeNode, makeString(), map_variable_attnos(), NameStr, NIL, ObjectIdGetDatum(), OidIsValid, pstrdup(), RelationGetDescr, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), SearchSysCache1(), SORTBY_DEFAULT, SORTBY_DESC, SORTBY_NULLS_DEFAULT, SORTBY_NULLS_FIRST, SORTBY_NULLS_LAST, stringToNode(), SysCacheGetAttr(), SysCacheGetAttrNotNull(), TextDatumGetCString, TupleDescAttr(), and untransformRelOptions().

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

◆ transformAlterTableStmt()

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

Definition at line 3835 of file parse_utilcmd.c.

3838{
3839 Relation rel;
3840 TupleDesc tupdesc;
3841 ParseState *pstate;
3844 ListCell *lcmd,
3845 *l;
3846 List *newcmds = NIL;
3847 bool skipValidation = true;
3850
3851 /* Caller is responsible for locking the relation */
3852 rel = relation_open(relid, NoLock);
3853 tupdesc = RelationGetDescr(rel);
3854
3855 /* Set up pstate */
3856 pstate = make_parsestate(NULL);
3857 pstate->p_sourcetext = queryString;
3859 rel,
3861 NULL,
3862 false,
3863 true);
3864 addNSItemToQuery(pstate, nsitem, false, true, true);
3865
3866 /* Set up CreateStmtContext */
3867 cxt.pstate = pstate;
3868 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
3869 {
3870 cxt.stmtType = "ALTER FOREIGN TABLE";
3871 cxt.isforeign = true;
3872 }
3873 else
3874 {
3875 cxt.stmtType = "ALTER TABLE";
3876 cxt.isforeign = false;
3877 }
3878 cxt.relation = stmt->relation;
3879 cxt.rel = rel;
3880 cxt.inhRelations = NIL;
3881 cxt.isalter = true;
3882 cxt.columns = NIL;
3883 cxt.ckconstraints = NIL;
3884 cxt.nnconstraints = NIL;
3885 cxt.fkconstraints = NIL;
3886 cxt.ixconstraints = NIL;
3887 cxt.likeclauses = NIL;
3888 cxt.blist = NIL;
3889 cxt.alist = NIL;
3890 cxt.pkey = NULL;
3891 cxt.ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
3892 cxt.partbound = NULL;
3893 cxt.ofType = false;
3894
3895 /*
3896 * Transform ALTER subcommands that need it (most don't). These largely
3897 * re-use code from CREATE TABLE.
3898 */
3899 foreach(lcmd, stmt->cmds)
3900 {
3902
3903 switch (cmd->subtype)
3904 {
3905 case AT_AddColumn:
3906 {
3907 ColumnDef *def = castNode(ColumnDef, cmd->def);
3908
3909 transformColumnDefinition(&cxt, def);
3910
3911 /*
3912 * If the column has a non-null default, we can't skip
3913 * validation of foreign keys.
3914 */
3915 if (def->raw_default != NULL)
3916 skipValidation = false;
3917
3918 /*
3919 * All constraints are processed in other ways. Remove the
3920 * original list
3921 */
3922 def->constraints = NIL;
3923
3924 newcmds = lappend(newcmds, cmd);
3925 break;
3926 }
3927
3928 case AT_AddConstraint:
3929
3930 /*
3931 * The original AddConstraint cmd node doesn't go to newcmds
3932 */
3933 if (IsA(cmd->def, Constraint))
3934 {
3935 transformTableConstraint(&cxt, (Constraint *) cmd->def);
3936 if (((Constraint *) cmd->def)->contype == CONSTR_FOREIGN)
3937 skipValidation = false;
3938 }
3939 else
3940 elog(ERROR, "unrecognized node type: %d",
3941 (int) nodeTag(cmd->def));
3942 break;
3943
3944 case AT_AlterColumnType:
3945 {
3946 ColumnDef *def = castNode(ColumnDef, cmd->def);
3948
3949 /*
3950 * For ALTER COLUMN TYPE, transform the USING clause if
3951 * one was specified.
3952 */
3953 if (def->raw_default)
3954 {
3955 def->cooked_default =
3956 transformExpr(pstate, def->raw_default,
3958 }
3959
3960 /*
3961 * For identity column, create ALTER SEQUENCE command to
3962 * change the data type of the sequence. Identity sequence
3963 * is associated with the top level partitioned table.
3964 * Hence ignore partitions.
3965 */
3967 {
3968 attnum = get_attnum(relid, cmd->name);
3970 ereport(ERROR,
3972 errmsg("column \"%s\" of relation \"%s\" does not exist",
3973 cmd->name, RelationGetRelationName(rel))));
3974
3975 if (attnum > 0 &&
3976 TupleDescAttr(tupdesc, attnum - 1)->attidentity)
3977 {
3978 Oid seq_relid = getIdentitySequence(rel, attnum, false);
3979 Oid typeOid = typenameTypeId(pstate, def->typeName);
3981
3982 altseqstmt->sequence
3985 -1);
3986 altseqstmt->options = list_make1(makeDefElem("as",
3987 (Node *) makeTypeNameFromOid(typeOid, -1),
3988 -1));
3989 altseqstmt->for_identity = true;
3990 cxt.blist = lappend(cxt.blist, altseqstmt);
3991 }
3992 }
3993
3994 newcmds = lappend(newcmds, cmd);
3995 break;
3996 }
3997
3998 case AT_AddIdentity:
3999 {
4000 Constraint *def = castNode(Constraint, cmd->def);
4003
4004 newdef->colname = cmd->name;
4005 newdef->identity = def->generated_when;
4006 cmd->def = (Node *) newdef;
4007
4008 attnum = get_attnum(relid, cmd->name);
4010 ereport(ERROR,
4012 errmsg("column \"%s\" of relation \"%s\" does not exist",
4013 cmd->name, RelationGetRelationName(rel))));
4014
4016 get_atttype(relid, attnum),
4017 def->options, true, true,
4018 NULL, NULL);
4019
4020 newcmds = lappend(newcmds, cmd);
4021 break;
4022 }
4023
4024 case AT_SetIdentity:
4025 {
4026 /*
4027 * Create an ALTER SEQUENCE statement for the internal
4028 * sequence of the identity column.
4029 */
4030 ListCell *lc;
4031 List *newseqopts = NIL;
4032 List *newdef = NIL;
4034 Oid seq_relid;
4035
4036 /*
4037 * Split options into those handled by ALTER SEQUENCE and
4038 * those for ALTER TABLE proper.
4039 */
4040 foreach(lc, castNode(List, cmd->def))
4041 {
4042 DefElem *def = lfirst_node(DefElem, lc);
4043
4044 if (strcmp(def->defname, "generated") == 0)
4045 newdef = lappend(newdef, def);
4046 else
4048 }
4049
4050 attnum = get_attnum(relid, cmd->name);
4052 ereport(ERROR,
4054 errmsg("column \"%s\" of relation \"%s\" does not exist",
4055 cmd->name, RelationGetRelationName(rel))));
4056
4057 seq_relid = getIdentitySequence(rel, attnum, true);
4058
4059 if (seq_relid)
4060 {
4062
4065 get_rel_name(seq_relid), -1);
4066 seqstmt->options = newseqopts;
4067 seqstmt->for_identity = true;
4068 seqstmt->missing_ok = false;
4069
4070 cxt.blist = lappend(cxt.blist, seqstmt);
4071 }
4072
4073 /*
4074 * If column was not an identity column, we just let the
4075 * ALTER TABLE command error out later. (There are cases
4076 * this fails to cover, but we'll need to restructure
4077 * where creation of the sequence dependency linkage
4078 * happens before we can fix it.)
4079 */
4080
4081 cmd->def = (Node *) newdef;
4082 newcmds = lappend(newcmds, cmd);
4083 break;
4084 }
4085
4086 case AT_AttachPartition:
4087 case AT_DetachPartition:
4088 {
4090
4091 transformPartitionCmd(&cxt, partcmd->bound);
4092 /* assign the transformed value of the partition bound */
4093 partcmd->bound = cxt.partbound;
4094 }
4095
4096 newcmds = lappend(newcmds, cmd);
4097 break;
4098
4099 case AT_MergePartitions:
4100 {
4102
4103 if (list_length(partcmd->partlist) < 2)
4104 ereport(ERROR,
4106 errmsg("list of partitions to be merged should include at least two partitions"));
4107
4109 newcmds = lappend(newcmds, cmd);
4110 break;
4111 }
4112
4113 case AT_SplitPartition:
4114 {
4116
4117 if (list_length(partcmd->partlist) < 2)
4118 ereport(ERROR,
4120 errmsg("list of new partitions should contain at least two partitions"));
4121
4123 newcmds = lappend(newcmds, cmd);
4124 break;
4125 }
4126
4127 default:
4128
4129 /*
4130 * Currently, we shouldn't actually get here for the
4131 * subcommand types that don't require transformation; but if
4132 * we do, just emit them unchanged.
4133 */
4134 newcmds = lappend(newcmds, cmd);
4135 break;
4136 }
4137 }
4138
4139 /*
4140 * Transfer anything we already have in cxt.alist into save_alist, to keep
4141 * it separate from the output of transformIndexConstraints.
4142 */
4143 save_alist = cxt.alist;
4144 cxt.alist = NIL;
4145
4146 /* Postprocess constraints */
4149 transformCheckConstraints(&cxt, false);
4150
4151 /*
4152 * Push any index-creation commands into the ALTER, so that they can be
4153 * scheduled nicely by tablecmds.c. Note that tablecmds.c assumes that
4154 * the IndexStmt attached to an AT_AddIndex or AT_AddIndexConstraint
4155 * subcommand has already been through transformIndexStmt.
4156 */
4157 foreach(l, cxt.alist)
4158 {
4159 Node *istmt = (Node *) lfirst(l);
4160
4161 /*
4162 * We assume here that cxt.alist contains only IndexStmts generated
4163 * from primary key constraints.
4164 */
4165 if (IsA(istmt, IndexStmt))
4166 {
4167 IndexStmt *idxstmt = (IndexStmt *) istmt;
4168
4169 idxstmt = transformIndexStmt(relid, idxstmt, queryString);
4171 newcmd->subtype = OidIsValid(idxstmt->indexOid) ? AT_AddIndexConstraint : AT_AddIndex;
4172 newcmd->def = (Node *) idxstmt;
4174 }
4175 else
4176 elog(ERROR, "unexpected stmt type %d", (int) nodeTag(istmt));
4177 }
4178 cxt.alist = NIL;
4179
4180 /* Append any CHECK, NOT NULL or FK constraints to the commands list */
4182 {
4184 newcmd->subtype = AT_AddConstraint;
4185 newcmd->def = (Node *) def;
4187 }
4189 {
4191 newcmd->subtype = AT_AddConstraint;
4192 newcmd->def = (Node *) def;
4194 }
4196 {
4198 newcmd->subtype = AT_AddConstraint;
4199 newcmd->def = (Node *) def;
4201 }
4202
4203 /* Close rel */
4204 relation_close(rel, NoLock);
4205
4206 /*
4207 * Output results.
4208 */
4209 stmt->cmds = newcmds;
4210
4211 *beforeStmts = cxt.blist;
4212 *afterStmts = list_concat(cxt.alist, save_alist);
4213
4214 return stmt;
4215}
#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:2242
AttrNumber get_attnum(Oid relid, const char *attname)
Definition lsyscache.c:1084
Oid get_rel_namespace(Oid relid)
Definition lsyscache.c:2266
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:162
#define nodeTag(nodeptr)
Definition nodes.h:137
#define castNode(_type_, nodeptr)
Definition nodes.h:180
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition parse_expr.c:121
ParseState * make_parsestate(ParseState *parentParseState)
Definition parse_node.c:39
@ EXPR_KIND_ALTER_COL_TRANSFORM
Definition parse_node.h:76
void addNSItemToQuery(ParseState *pstate, ParseNamespaceItem *nsitem, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
ParseNamespaceItem * addRangeTableEntryForRelation(ParseState *pstate, Relation rel, LOCKMODE lockmode, Alias *alias, bool inh, bool inFromCl)
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
@ AT_AddIndexConstraint
@ AT_MergePartitions
@ AT_SetIdentity
@ AT_AddIndex
@ AT_AddIdentity
@ AT_AlterColumnType
@ AT_DetachPartition
@ AT_AttachPartition
@ AT_SplitPartition
@ AT_AddColumn
Oid getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
Definition pg_depend.c:1149
#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:244
#define foreach_node(type, var, lst)
Definition pg_list.h:528
#define RelationGetForm(relation)
Definition rel.h:510
void relation_close(Relation relation, LOCKMODE lockmode)
Definition relation.c:206
AlterTableType subtype
List * constraints
Definition parsenodes.h:788
Node * cooked_default
Definition parsenodes.h:781
TypeName * typeName
Definition parsenodes.h:772
Node * raw_default
Definition parsenodes.h:780
List * options
char generated_when
const char * stmtType
RangeVar * relation
ParseState * pstate
PartitionBoundSpec * partbound
char * defname
Definition parsenodes.h:862
const char * p_sourcetext
Definition parse_node.h:214

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, castNode, CreateStmtContext::ckconstraints, CreateStmtContext::columns, CONSTR_FOREIGN, ColumnDef::constraints, ColumnDef::cooked_default, AlterTableCmd::def, DefElem::defname, elog, ereport, errcode(), errmsg, ERROR, EXPR_KIND_ALTER_COL_TRANSFORM, fb(), CreateStmtContext::fkconstraints, foreach_node, Constraint::generated_when, generateSerialExtraStmts(), get_attnum(), get_atttype(), get_namespace_name(), get_rel_name(), get_rel_namespace(), getIdentitySequence(), 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(), AlterTableCmd::name, NIL, CreateStmtContext::nnconstraints, nodeTag, NoLock, CreateStmtContext::ofType, OidIsValid, Constraint::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, 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 ( ParseState pstate,
List schemaElts,
const char schemaName 
)
extern

Definition at line 4444 of file parse_utilcmd.c.

4446{
4447 List *elements = NIL;
4448 List *fk_elements = NIL;
4449 ListCell *lc;
4450
4451 /*
4452 * Run through each schema element in the schema element list. Check
4453 * target schema names, and collect the list of actions to be done.
4454 */
4455 foreach(lc, schemaElts)
4456 {
4457 Node *element = lfirst(lc);
4458
4459 switch (nodeTag(element))
4460 {
4461 case T_CreateSeqStmt:
4462 {
4464
4465 checkSchemaNameRV(pstate, schemaName, elp->sequence);
4466 elements = lappend(elements, element);
4467 }
4468 break;
4469
4470 case T_CreateStmt:
4471 {
4473
4474 checkSchemaNameRV(pstate, schemaName, elp->relation);
4475 /* Pull out any foreign key clauses, add to fk_elements */
4477 elp,
4478 &fk_elements);
4479 elements = lappend(elements, elp);
4480 }
4481 break;
4482
4483 case T_ViewStmt:
4484 {
4486
4487 checkSchemaNameRV(pstate, schemaName, elp->view);
4488 elements = lappend(elements, element);
4489 }
4490 break;
4491
4492 case T_IndexStmt:
4493 {
4495
4496 checkSchemaNameRV(pstate, schemaName, elp->relation);
4497 elements = lappend(elements, element);
4498 }
4499 break;
4500
4501 case T_CreateTrigStmt:
4502 {
4504
4505 checkSchemaNameRV(pstate, schemaName, elp->relation);
4506 elements = lappend(elements, element);
4507 }
4508 break;
4509
4510 case T_CreateDomainStmt:
4511 {
4513
4514 checkSchemaNameList(schemaName, elp->domainname);
4515 elements = lappend(elements, element);
4516 }
4517 break;
4518
4520 {
4522
4524 elements = lappend(elements, element);
4525 }
4526 break;
4527
4528 /*
4529 * CREATE TYPE can produce a DefineStmt, but also
4530 * CreateEnumStmt, CreateRangeStmt, and CompositeTypeStmt.
4531 * Allowing DefineStmt also provides support for several other
4532 * commands: currently, CREATE AGGREGATE, CREATE COLLATION,
4533 * CREATE OPERATOR, and text search objects.
4534 */
4535
4536 case T_DefineStmt:
4537 {
4539
4541 elements = lappend(elements, element);
4542 }
4543 break;
4544
4545 case T_CreateEnumStmt:
4546 {
4548
4550 elements = lappend(elements, element);
4551 }
4552 break;
4553
4554 case T_CreateRangeStmt:
4555 {
4557
4559 elements = lappend(elements, element);
4560 }
4561 break;
4562
4564 {
4566
4567 checkSchemaNameRV(pstate, schemaName, elp->typevar);
4568 elements = lappend(elements, element);
4569 }
4570 break;
4571
4572 case T_GrantStmt:
4573 elements = lappend(elements, element);
4574 break;
4575
4576 default:
4577 elog(ERROR, "unrecognized node type: %d",
4578 (int) nodeTag(element));
4579 }
4580 }
4581
4582 return list_concat(elements, fk_elements);
4583}
static CreateStmt * transformCreateSchemaCreateTable(ParseState *pstate, CreateStmt *stmt, List **fk_elements)
static void checkSchemaNameRV(ParseState *pstate, const char *context_schema, RangeVar *relation)
static void checkSchemaNameList(const char *context_schema, List *qualified_name)
static chr element(struct vars *v, const chr *startp, const chr *endp)

References checkSchemaNameList(), checkSchemaNameRV(), element(), elog, ERROR, fb(), lappend(), lfirst, list_concat(), NIL, nodeTag, and transformCreateSchemaCreateTable().

Referenced by CreateSchemaCommand().

◆ transformCreateStmt()

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

Definition at line 162 of file parse_utilcmd.c.

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

Definition at line 3062 of file parse_utilcmd.c.

3063{
3064 ParseState *pstate;
3066 ListCell *l;
3067 Relation rel;
3068
3069 /* Nothing to do if statement already transformed. */
3070 if (stmt->transformed)
3071 return stmt;
3072
3073 /* Set up pstate */
3074 pstate = make_parsestate(NULL);
3075 pstate->p_sourcetext = queryString;
3076
3077 /*
3078 * Put the parent table into the rtable so that the expressions can refer
3079 * to its fields without qualification. Caller is responsible for locking
3080 * relation, but we still need to open it.
3081 */
3082 rel = relation_open(relid, NoLock);
3085 NULL, false, true);
3086
3087 /* no to join list, yes to namespaces */
3088 addNSItemToQuery(pstate, nsitem, false, true, true);
3089
3090 /* take care of the where clause */
3091 if (stmt->whereClause)
3092 {
3093 stmt->whereClause = transformWhereClause(pstate,
3094 stmt->whereClause,
3096 "WHERE");
3097 /* we have to fix its collations too */
3098 assign_expr_collations(pstate, stmt->whereClause);
3099 }
3100
3101 /* take care of any index expressions */
3102 foreach(l, stmt->indexParams)
3103 {
3104 IndexElem *ielem = (IndexElem *) lfirst(l);
3105
3106 if (ielem->expr)
3107 {
3108 /* Do parse transformation of the expression */
3109 ielem->expr = transformExpr(pstate, ielem->expr,
3111
3112 /* We have to fix its collations too */
3113 assign_expr_collations(pstate, ielem->expr);
3114
3115 /*
3116 * transformExpr() should have already rejected subqueries,
3117 * aggregates, window functions, and SRFs, based on the EXPR_KIND_
3118 * for an index expression.
3119 *
3120 * DefineIndex() will make more checks.
3121 */
3122 }
3123 }
3124
3125 /*
3126 * Likewise take care of any expressions in INCLUDING. (At this writing,
3127 * those will be rejected later on, but probably someday we'll wish to
3128 * support them.)
3129 */
3130 foreach(l, stmt->indexIncludingParams)
3131 {
3132 IndexElem *ielem = (IndexElem *) lfirst(l);
3133
3134 if (ielem->expr)
3135 {
3136 /* Do parse transformation of the expression */
3137 ielem->expr = transformExpr(pstate, ielem->expr,
3139
3140 /* We have to fix its collations too */
3141 assign_expr_collations(pstate, ielem->expr);
3142 }
3143 }
3144
3145 /*
3146 * Check that only the base rel is mentioned. (This should be dead code
3147 * now that add_missing_from is history.)
3148 */
3149 if (list_length(pstate->p_rtable) != 1)
3150 ereport(ERROR,
3152 errmsg("index expressions and predicates can refer only to the table being indexed")));
3153
3154 free_parsestate(pstate);
3155
3156 /* Close relation */
3157 table_close(rel, NoLock);
3158
3159 /* Mark statement as successfully transformed */
3160 stmt->transformed = true;
3161
3162 return stmt;
3163}
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:73
@ EXPR_KIND_INDEX_PREDICATE
Definition parse_node.h:74
Node * expr
Definition parsenodes.h:830
List * p_rtable
Definition parse_node.h:215

References AccessShareLock, addNSItemToQuery(), addRangeTableEntryForRelation(), assign_expr_collations(), ereport, errcode(), errmsg, ERROR, IndexElem::expr, EXPR_KIND_INDEX_EXPRESSION, EXPR_KIND_INDEX_PREDICATE, fb(), free_parsestate(), 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 
)
extern

Definition at line 4859 of file parse_utilcmd.c.

4861{
4864 char strategy = get_partition_strategy(key);
4865 int partnatts = get_partition_natts(key);
4866 List *partexprs = get_partition_exprs(key);
4867
4868 /* Avoid scribbling on input */
4870
4871 if (spec->is_default)
4872 {
4873 /*
4874 * Hash partitioning does not support a default partition; there's no
4875 * use case for it (since the set of partitions to create is perfectly
4876 * defined), and if users do get into it accidentally, it's hard to
4877 * back out from it afterwards.
4878 */
4879 if (strategy == PARTITION_STRATEGY_HASH)
4880 ereport(ERROR,
4882 errmsg("a hash-partitioned table may not have a default partition")));
4883
4884 /*
4885 * In case of the default partition, parser had no way to identify the
4886 * partition strategy. Assign the parent's strategy to the default
4887 * partition bound spec.
4888 */
4889 result_spec->strategy = strategy;
4890
4891 return result_spec;
4892 }
4893
4894 if (strategy == PARTITION_STRATEGY_HASH)
4895 {
4896 if (spec->strategy != PARTITION_STRATEGY_HASH)
4897 ereport(ERROR,
4899 errmsg("invalid bound specification for a hash partition"),
4900 parser_errposition(pstate, exprLocation((Node *) spec))));
4901
4902 if (spec->modulus <= 0)
4903 ereport(ERROR,
4905 errmsg("modulus for hash partition must be an integer value greater than zero")));
4906
4907 Assert(spec->remainder >= 0);
4908
4909 if (spec->remainder >= spec->modulus)
4910 ereport(ERROR,
4912 errmsg("remainder for hash partition must be less than modulus")));
4913 }
4914 else if (strategy == PARTITION_STRATEGY_LIST)
4915 {
4916 ListCell *cell;
4917 char *colname;
4918 Oid coltype;
4920 Oid partcollation;
4921
4922 if (spec->strategy != PARTITION_STRATEGY_LIST)
4923 ereport(ERROR,
4925 errmsg("invalid bound specification for a list partition"),
4926 parser_errposition(pstate, exprLocation((Node *) spec))));
4927
4928 /* Get the only column's name in case we need to output an error */
4929 if (key->partattrs[0] != 0)
4930 colname = get_attname(RelationGetRelid(parent),
4931 key->partattrs[0], false);
4932 else
4933 colname = deparse_expression((Node *) linitial(partexprs),
4935 RelationGetRelid(parent)),
4936 false, false);
4937 /* Need its type data too */
4938 coltype = get_partition_col_typid(key, 0);
4940 partcollation = get_partition_col_collation(key, 0);
4941
4942 result_spec->listdatums = NIL;
4943 foreach(cell, spec->listdatums)
4944 {
4945 Node *expr = lfirst(cell);
4946 Const *value;
4947 ListCell *cell2;
4948 bool duplicate;
4949
4950 value = transformPartitionBoundValue(pstate, expr,
4951 colname, coltype, coltypmod,
4952 partcollation);
4953
4954 /* Don't add to the result if the value is a duplicate */
4955 duplicate = false;
4956 foreach(cell2, result_spec->listdatums)
4957 {
4959
4960 if (equal(value, value2))
4961 {
4962 duplicate = true;
4963 break;
4964 }
4965 }
4966 if (duplicate)
4967 continue;
4968
4969 result_spec->listdatums = lappend(result_spec->listdatums,
4970 value);
4971 }
4972 }
4973 else if (strategy == PARTITION_STRATEGY_RANGE)
4974 {
4975 if (spec->strategy != PARTITION_STRATEGY_RANGE)
4976 ereport(ERROR,
4978 errmsg("invalid bound specification for a range partition"),
4979 parser_errposition(pstate, exprLocation((Node *) spec))));
4980
4981 if (list_length(spec->lowerdatums) != partnatts)
4982 ereport(ERROR,
4984 errmsg("FROM must specify exactly one value per partitioning column")));
4985 if (list_length(spec->upperdatums) != partnatts)
4986 ereport(ERROR,
4988 errmsg("TO must specify exactly one value per partitioning column")));
4989
4990 /*
4991 * Convert raw parse nodes into PartitionRangeDatum nodes and perform
4992 * any necessary validation.
4993 */
4994 result_spec->lowerdatums =
4995 transformPartitionRangeBounds(pstate, spec->lowerdatums,
4996 parent);
4997 result_spec->upperdatums =
4998 transformPartitionRangeBounds(pstate, spec->upperdatums,
4999 parent);
5000 }
5001 else
5002 elog(ERROR, "unexpected partition strategy: %d", (int) strategy);
5003
5004 return result_spec;
5005}
int32_t int32
Definition c.h:679
bool equal(const void *a, const void *b)
Definition equalfuncs.c:223
static struct @175 value
int exprLocation(const Node *expr)
Definition nodeFuncs.c:1403
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:921
@ PARTITION_STRATEGY_LIST
Definition parsenodes.h:919
@ PARTITION_STRATEGY_RANGE
Definition parsenodes.h:920
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:4072
char * deparse_expression(Node *expr, List *dpcontext, bool forceprefix, bool showimplicit)
Definition ruleutils.c:4009

References Assert, copyObject, deparse_context_for(), deparse_expression(), elog, equal(), ereport, errcode(), errmsg, ERROR, exprLocation(), fb(), get_attname(), get_partition_col_collation(), get_partition_col_typid(), get_partition_col_typmod(), get_partition_exprs(), get_partition_natts(), get_partition_strategy(), lappend(), lfirst, lfirst_node, linitial, list_length(), NIL, parser_errposition(), PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, RelationGetPartitionKey(), RelationGetRelationName, RelationGetRelid, transformPartitionBoundValue(), transformPartitionRangeBounds(), and value.

Referenced by DefineRelation(), and transformPartitionCmd().

◆ transformRuleStmt()

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

Definition at line 3248 of file parse_utilcmd.c.

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

References AccessExclusiveLock, AccessShareLock, addNSItemToQuery(), addRangeTableEntryForRelation(), assign_expr_collations(), CMD_DELETE, CMD_INSERT, CMD_NOTHING, CMD_SELECT, CMD_UPDATE, CMD_UTILITY, elog, ereport, errcode(), errmsg, ERROR, EXPR_KIND_WHERE, fb(), free_parsestate(), getInsertSelectQuery(), lappend(), lfirst, list_length(), list_make1, make_parsestate(), makeAlias(), makeFromExpr(), makeNode, NIL, NoLock, ParseState::p_rtable, ParseState::p_rteperminfos, ParseState::p_sourcetext, PRS2_NEW_VARNO, PRS2_OLD_VARNO, rangeTableEntry_used(), RelationData::rd_rel, stmt, table_close(), table_openrv(), transformStmt(), and transformWhereClause().

Referenced by DefineRule().

◆ transformStatsStmt()

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

Definition at line 3173 of file parse_utilcmd.c.

3174{
3175 ParseState *pstate;
3177 ListCell *l;
3178 Relation rel;
3179
3180 /* Nothing to do if statement already transformed. */
3181 if (stmt->transformed)
3182 return stmt;
3183
3184 /* Set up pstate */
3185 pstate = make_parsestate(NULL);
3186 pstate->p_sourcetext = queryString;
3187
3188 /*
3189 * Put the parent table into the rtable so that the expressions can refer
3190 * to its fields without qualification. Caller is responsible for locking
3191 * relation, but we still need to open it.
3192 */
3193 rel = relation_open(relid, NoLock);
3196 NULL, false, true);
3197
3198 /* no to join list, yes to namespaces */
3199 addNSItemToQuery(pstate, nsitem, false, true, true);
3200
3201 /* take care of any expressions */
3202 foreach(l, stmt->exprs)
3203 {
3204 StatsElem *selem = (StatsElem *) lfirst(l);
3205
3206 if (selem->expr)
3207 {
3208 /* Now do parse transformation of the expression */
3209 selem->expr = transformExpr(pstate, selem->expr,
3211
3212 /* We have to fix its collations too */
3213 assign_expr_collations(pstate, selem->expr);
3214 }
3215 }
3216
3217 /*
3218 * Check that only the base rel is mentioned. (This should be dead code
3219 * now that add_missing_from is history.)
3220 */
3221 if (list_length(pstate->p_rtable) != 1)
3222 ereport(ERROR,
3224 errmsg("statistics expressions can refer only to the table being referenced")));
3225
3226 free_parsestate(pstate);
3227
3228 /* Close relation */
3229 table_close(rel, NoLock);
3230
3231 /* Mark statement as successfully transformed */
3232 stmt->transformed = true;
3233
3234 return stmt;
3235}
@ EXPR_KIND_STATS_EXPRESSION
Definition parse_node.h:75
Node * expr

References AccessShareLock, addNSItemToQuery(), addRangeTableEntryForRelation(), assign_expr_collations(), ereport, errcode(), errmsg, ERROR, StatsElem::expr, EXPR_KIND_STATS_EXPRESSION, fb(), 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().