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

1349{
1350 List *result = NIL;
1351 List *atsubcmds = NIL;
1353 Relation relation;
1356 TupleConstr *constr;
1357 AttrMap *attmap;
1358 char *comment;
1359
1360 /*
1361 * Open the relation referenced by the LIKE clause. We should still have
1362 * the table lock obtained by transformTableLikeClause (and this'll throw
1363 * an assertion failure if not). Hence, no need to recheck privileges
1364 * etc. We must open the rel by OID not name, to be sure we get the same
1365 * table.
1366 */
1367 if (!OidIsValid(table_like_clause->relationOid))
1368 elog(ERROR, "expandTableLikeClause called on untransformed LIKE clause");
1369
1370 relation = relation_open(table_like_clause->relationOid, NoLock);
1371
1372 tupleDesc = RelationGetDescr(relation);
1373 constr = tupleDesc->constr;
1374
1375 /*
1376 * Open the newly-created child relation; we have lock on that too.
1377 */
1378 childrel = relation_openrv(heapRel, NoLock);
1379
1380 /*
1381 * Construct a map from the LIKE relation's attnos to the child rel's.
1382 * This re-checks type match etc, although it shouldn't be possible to
1383 * have a failure since both tables are locked.
1384 */
1386 tupleDesc,
1387 false);
1388
1389 /*
1390 * Process defaults, if required.
1391 */
1392 if ((table_like_clause->options &
1394 constr != NULL)
1395 {
1396 for (parent_attno = 1; parent_attno <= tupleDesc->natts;
1397 parent_attno++)
1398 {
1400 parent_attno - 1);
1401
1402 /*
1403 * Ignore dropped columns in the parent.
1404 */
1405 if (attribute->attisdropped)
1406 continue;
1407
1408 /*
1409 * Copy default, if present and it should be copied. We have
1410 * separate options for plain default expressions and GENERATED
1411 * defaults.
1412 */
1413 if (attribute->atthasdef &&
1414 (attribute->attgenerated ?
1417 {
1420 bool found_whole_row;
1421
1423 if (this_default == NULL)
1424 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
1426
1429 atsubcmd->num = attmap->attnums[parent_attno - 1];
1431 1, 0,
1432 attmap,
1433 InvalidOid,
1434 &found_whole_row);
1435
1436 /*
1437 * Prevent this for the same reason as for constraints below.
1438 * Note that defaults cannot contain any vars, so it's OK that
1439 * the error message refers to generated columns.
1440 */
1441 if (found_whole_row)
1442 ereport(ERROR,
1444 errmsg("cannot convert whole-row table reference"),
1445 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
1446 NameStr(attribute->attname),
1447 RelationGetRelationName(relation))));
1448
1450 }
1451 }
1452 }
1453
1454 /*
1455 * Copy CHECK constraints if requested, being careful to adjust attribute
1456 * numbers so they match the child.
1457 */
1459 constr != NULL)
1460 {
1461 int ccnum;
1462
1463 for (ccnum = 0; ccnum < constr->num_check; ccnum++)
1464 {
1465 char *ccname = constr->check[ccnum].ccname;
1466 char *ccbin = constr->check[ccnum].ccbin;
1467 bool ccenforced = constr->check[ccnum].ccenforced;
1468 bool ccnoinherit = constr->check[ccnum].ccnoinherit;
1470 bool found_whole_row;
1471 Constraint *n;
1473
1475 1, 0,
1476 attmap,
1477 InvalidOid, &found_whole_row);
1478
1479 /*
1480 * We reject whole-row variables because the whole point of LIKE
1481 * is that the new table's rowtype might later diverge from the
1482 * parent's. So, while translation might be possible right now,
1483 * it wouldn't be possible to guarantee it would work in future.
1484 */
1485 if (found_whole_row)
1486 ereport(ERROR,
1488 errmsg("cannot convert whole-row table reference"),
1489 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
1490 ccname,
1491 RelationGetRelationName(relation))));
1492
1493 n = makeNode(Constraint);
1494 n->contype = CONSTR_CHECK;
1495 n->conname = pstrdup(ccname);
1496 n->location = -1;
1497 n->is_enforced = ccenforced;
1498 n->initially_valid = ccenforced; /* sic */
1499 n->is_no_inherit = ccnoinherit;
1500 n->raw_expr = NULL;
1502
1503 /* We can skip validation, since the new table should be empty. */
1504 n->skip_validation = true;
1505
1507 atsubcmd->subtype = AT_AddConstraint;
1508 atsubcmd->def = (Node *) n;
1510
1511 /* Copy comment on constraint */
1514 n->conname, false),
1516 0)) != NULL)
1517 {
1519
1520 stmt->objtype = OBJECT_TABCONSTRAINT;
1521 stmt->object = (Node *) list_make3(makeString(heapRel->schemaname),
1522 makeString(heapRel->relname),
1523 makeString(n->conname));
1524 stmt->comment = comment;
1525
1526 result = lappend(result, stmt);
1527 }
1528 }
1529 }
1530
1531 /*
1532 * If we generated any ALTER TABLE actions above, wrap them into a single
1533 * ALTER TABLE command. Stick it at the front of the result, so it runs
1534 * before any CommentStmts we made above.
1535 */
1536 if (atsubcmds)
1537 {
1539
1540 atcmd->relation = copyObject(heapRel);
1541 atcmd->cmds = atsubcmds;
1542 atcmd->objtype = OBJECT_TABLE;
1543 atcmd->missing_ok = false;
1544 result = lcons(atcmd, result);
1545 }
1546
1547 /*
1548 * Process indexes if required.
1549 */
1551 relation->rd_rel->relhasindex &&
1552 childrel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
1553 {
1555 ListCell *l;
1556
1558
1559 foreach(l, parent_indexes)
1560 {
1564
1566
1567 /* Build CREATE INDEX statement to recreate the parent_index */
1570 attmap,
1571 NULL);
1572
1573 /* Copy comment on index, if requested */
1575 {
1577
1578 /*
1579 * We make use of IndexStmt's idxcomment option, so as not to
1580 * need to know now what name the index will have.
1581 */
1582 index_stmt->idxcomment = comment;
1583 }
1584
1585 result = lappend(result, index_stmt);
1586
1588 }
1589 }
1590
1591 /*
1592 * Process extended statistics if required.
1593 */
1595 {
1597 ListCell *l;
1598
1600
1601 foreach(l, parent_extstats)
1602 {
1605
1609 attmap);
1610
1611 /* Copy comment on statistics object, if requested */
1613 {
1615
1616 /*
1617 * We make use of CreateStatsStmt's stxcomment option, so as
1618 * not to need to know now what name the statistics will have.
1619 */
1620 stats_stmt->stxcomment = comment;
1621 }
1622
1623 result = lappend(result, stats_stmt);
1624 }
1625
1627 }
1628
1629 /* Done with child rel */
1631
1632 /*
1633 * Close the parent rel, but keep our AccessShareLock on it until xact
1634 * commit. That will prevent someone else from deleting or ALTERing the
1635 * parent before the child is committed.
1636 */
1637 table_close(relation, NoLock);
1638
1639 return result;
1640}
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:837
#define OidIsValid(objectId)
Definition c.h:860
char * GetComment(Oid oid, Oid classoid, int32 subid)
Definition comment.c:420
int errcode(int sqlerrcode)
Definition elog.c:874
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
#define ereport(elevel,...)
Definition elog.h:150
#define stmt
#define comment
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:1781
#define copyObject(obj)
Definition nodes.h:232
#define makeNode(_type_)
Definition nodes.h:161
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:802
@ CREATE_TABLE_LIKE_GENERATED
Definition parsenodes.h:806
@ CREATE_TABLE_LIKE_INDEXES
Definition parsenodes.h:808
@ CREATE_TABLE_LIKE_DEFAULTS
Definition parsenodes.h:805
@ CREATE_TABLE_LIKE_STATISTICS
Definition parsenodes.h:809
@ CREATE_TABLE_LIKE_CONSTRAINTS
Definition parsenodes.h:804
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:216
#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:514
#define RelationGetDescr(relation)
Definition rel.h:540
#define RelationGetRelationName(relation)
Definition rel.h:548
List * RelationGetIndexList(Relation relation)
Definition relcache.c:4826
List * RelationGetStatExtList(Relation relation)
Definition relcache.c:4967
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
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:135
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:1149
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, 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 1696 of file parse_utilcmd.c.

1699{
1710 List *indexprs;
1712 Oid indrelid;
1713 int keyno;
1715 Datum datum;
1716 bool isnull;
1717
1718 if (constraintOid)
1720
1721 /*
1722 * Fetch pg_class tuple of source index. We can't use the copy in the
1723 * relcache entry because it doesn't include optional fields.
1724 */
1727 elog(ERROR, "cache lookup failed for relation %u", source_relid);
1729
1730 /* Fetch pg_index tuple for source index from relcache entry */
1731 ht_idx = source_idx->rd_indextuple;
1733 indrelid = idxrec->indrelid;
1734
1735 /* Fetch the pg_am tuple of the index' access method */
1737 if (!HeapTupleIsValid(ht_am))
1738 elog(ERROR, "cache lookup failed for access method %u",
1739 idxrelrec->relam);
1741
1742 /* Extract indcollation from the pg_index tuple */
1746
1747 /* Extract indclass from the pg_index tuple */
1749 indclass = (oidvector *) DatumGetPointer(datum);
1750
1751 /* Begin building the IndexStmt */
1753 index->relation = heapRel;
1754 index->accessMethod = pstrdup(NameStr(amrec->amname));
1755 if (OidIsValid(idxrelrec->reltablespace))
1756 index->tableSpace = get_tablespace_name(idxrelrec->reltablespace);
1757 else
1758 index->tableSpace = NULL;
1759 index->excludeOpNames = NIL;
1760 index->idxcomment = NULL;
1761 index->indexOid = InvalidOid;
1762 index->oldNumber = InvalidRelFileNumber;
1763 index->oldCreateSubid = InvalidSubTransactionId;
1764 index->oldFirstRelfilelocatorSubid = InvalidSubTransactionId;
1765 index->unique = idxrec->indisunique;
1766 index->nulls_not_distinct = idxrec->indnullsnotdistinct;
1767 index->primary = idxrec->indisprimary;
1768 index->iswithoutoverlaps = (idxrec->indisprimary || idxrec->indisunique) && idxrec->indisexclusion;
1769 index->transformed = true; /* don't need transformIndexStmt */
1770 index->concurrent = false;
1771 index->if_not_exists = false;
1772 index->reset_default_tblspc = false;
1773
1774 /*
1775 * We don't try to preserve the name of the source index; instead, just
1776 * let DefineIndex() choose a reasonable name. (If we tried to preserve
1777 * the name, we'd get duplicate-relation-name failures unless the source
1778 * table was in a different schema.)
1779 */
1780 index->idxname = NULL;
1781
1782 /*
1783 * If the index is marked PRIMARY or has an exclusion condition, it's
1784 * certainly from a constraint; else, if it's not marked UNIQUE, it
1785 * certainly isn't. If it is or might be from a constraint, we have to
1786 * fetch the pg_constraint record.
1787 */
1788 if (index->primary || index->unique || idxrec->indisexclusion)
1789 {
1791
1793 {
1796
1797 if (constraintOid)
1799
1803 elog(ERROR, "cache lookup failed for constraint %u",
1804 constraintId);
1806
1807 index->isconstraint = true;
1808 index->deferrable = conrec->condeferrable;
1809 index->initdeferred = conrec->condeferred;
1810
1811 /* If it's an exclusion constraint, we need the operator names */
1812 if (idxrec->indisexclusion)
1813 {
1814 Datum *elems;
1815 int nElems;
1816 int i;
1817
1818 Assert(conrec->contype == CONSTRAINT_EXCLUSION ||
1819 (index->iswithoutoverlaps &&
1820 (conrec->contype == CONSTRAINT_PRIMARY || conrec->contype == CONSTRAINT_UNIQUE)));
1821 /* Extract operator OIDs from the pg_constraint tuple */
1824 deconstruct_array_builtin(DatumGetArrayTypeP(datum), OIDOID, &elems, NULL, &nElems);
1825
1826 for (i = 0; i < nElems; i++)
1827 {
1828 Oid operid = DatumGetObjectId(elems[i]);
1831 char *oprname;
1832 char *nspname;
1833 List *namelist;
1834
1838 elog(ERROR, "cache lookup failed for operator %u",
1839 operid);
1841 oprname = pstrdup(NameStr(operform->oprname));
1842 /* For simplicity we always schema-qualify the op name */
1843 nspname = get_namespace_name(operform->oprnamespace);
1844 namelist = list_make2(makeString(nspname),
1845 makeString(oprname));
1846 index->excludeOpNames = lappend(index->excludeOpNames,
1847 namelist);
1849 }
1850 }
1851
1853 }
1854 else
1855 index->isconstraint = false;
1856 }
1857 else
1858 index->isconstraint = false;
1859
1860 /* Get the index expressions, if any */
1862 Anum_pg_index_indexprs, &isnull);
1863 if (!isnull)
1864 {
1865 char *exprsString;
1866
1869 }
1870 else
1871 indexprs = NIL;
1872
1873 /* Build the list of IndexElem */
1874 index->indexParams = NIL;
1875 index->indexIncludingParams = NIL;
1876
1878 for (keyno = 0; keyno < idxrec->indnkeyatts; keyno++)
1879 {
1881 AttrNumber attnum = idxrec->indkey.values[keyno];
1883 keyno);
1884 int16 opt = source_idx->rd_indoption[keyno];
1885
1887
1889 {
1890 /* Simple index column */
1891 char *attname;
1892
1895
1896 iparam->name = attname;
1897 iparam->expr = NULL;
1898 }
1899 else
1900 {
1901 /* Expressional index */
1902 Node *indexkey;
1903 bool found_whole_row;
1904
1905 if (indexpr_item == NULL)
1906 elog(ERROR, "too few entries in indexprs list");
1909
1910 /* Adjust Vars to match new table's column numbering */
1912 1, 0,
1913 attmap,
1914 InvalidOid, &found_whole_row);
1915
1916 /* As in expandTableLikeClause, reject whole-row variables */
1917 if (found_whole_row)
1918 ereport(ERROR,
1920 errmsg("cannot convert whole-row table reference"),
1921 errdetail("Index \"%s\" contains a whole-row table reference.",
1923
1924 iparam->name = NULL;
1925 iparam->expr = indexkey;
1926
1928 }
1929
1930 /* Copy the original index column name */
1931 iparam->indexcolname = pstrdup(NameStr(attr->attname));
1932
1933 /* Add the collation name, if non-default */
1934 iparam->collation = get_collation(indcollation->values[keyno], keycoltype);
1935
1936 /* Add the operator class name, if non-default */
1937 iparam->opclass = get_opclass(indclass->values[keyno], keycoltype);
1938 iparam->opclassopts =
1940
1941 iparam->ordering = SORTBY_DEFAULT;
1942 iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
1943
1944 /* Adjust options if necessary */
1945 if (source_idx->rd_indam->amcanorder)
1946 {
1947 /*
1948 * If it supports sort ordering, copy DESC and NULLS opts. Don't
1949 * set non-default settings unnecessarily, though, so as to
1950 * improve the chance of recognizing equivalence to constraint
1951 * indexes.
1952 */
1953 if (opt & INDOPTION_DESC)
1954 {
1955 iparam->ordering = SORTBY_DESC;
1956 if ((opt & INDOPTION_NULLS_FIRST) == 0)
1957 iparam->nulls_ordering = SORTBY_NULLS_LAST;
1958 }
1959 else
1960 {
1961 if (opt & INDOPTION_NULLS_FIRST)
1962 iparam->nulls_ordering = SORTBY_NULLS_FIRST;
1963 }
1964 }
1965
1966 iparam->location = -1;
1967
1968 index->indexParams = lappend(index->indexParams, iparam);
1969 }
1970
1971 /* Handle included columns separately */
1972 for (keyno = idxrec->indnkeyatts; keyno < idxrec->indnatts; keyno++)
1973 {
1975 AttrNumber attnum = idxrec->indkey.values[keyno];
1977 keyno);
1978
1980
1982 {
1983 /* Simple index column */
1984 char *attname;
1985
1987
1988 iparam->name = attname;
1989 iparam->expr = NULL;
1990 }
1991 else
1992 ereport(ERROR,
1994 errmsg("expressions are not supported in included columns")));
1995
1996 /* Copy the original index column name */
1997 iparam->indexcolname = pstrdup(NameStr(attr->attname));
1998
1999 iparam->location = -1;
2000
2001 index->indexIncludingParams = lappend(index->indexIncludingParams, iparam);
2002 }
2003 /* Copy reloptions if any */
2005 Anum_pg_class_reloptions, &isnull);
2006 if (!isnull)
2007 index->options = untransformRelOptions(datum);
2008
2009 /* If it's a partial index, decompile and append the predicate */
2011 Anum_pg_index_indpred, &isnull);
2012 if (!isnull)
2013 {
2014 char *pred_str;
2015 Node *pred_tree;
2016 bool found_whole_row;
2017
2018 /* Convert text string to node tree */
2021
2022 /* Adjust Vars to match new table's column numbering */
2024 1, 0,
2025 attmap,
2026 InvalidOid, &found_whole_row);
2027
2028 /* As in expandTableLikeClause, reject whole-row variables */
2029 if (found_whole_row)
2030 ereport(ERROR,
2032 errmsg("cannot convert whole-row table reference"),
2033 errdetail("Index \"%s\" contains a whole-row table reference.",
2035
2036 index->whereClause = pred_tree;
2037 }
2038
2039 /* Clean up */
2042
2043 return index;
2044}
#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:744
#define Assert(condition)
Definition c.h:945
int16_t int16
Definition c.h:613
#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:1089
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition lsyscache.c:946
char * get_namespace_name(Oid nspid)
Definition lsyscache.c:3588
Oid get_atttype(Oid relid, AttrNumber attnum)
Definition lsyscache.c:1032
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:1061
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:343
#define list_make2(x1, x2)
Definition pg_list.h:214
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:96
Definition c.h:817
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:264
Datum SysCacheGetAttrNotNull(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition syscache.c:625
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:220
Datum SysCacheGetAttr(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition syscache.c:595

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

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

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 ( List schemaElts,
const char schemaName 
)
extern

Definition at line 4422 of file parse_utilcmd.c.

4423{
4425 List *result;
4426 ListCell *elements;
4427
4428 cxt.schemaname = schemaName;
4429 cxt.sequences = NIL;
4430 cxt.tables = NIL;
4431 cxt.views = NIL;
4432 cxt.indexes = NIL;
4433 cxt.triggers = NIL;
4434 cxt.grants = NIL;
4435
4436 /*
4437 * Run through each schema element in the schema element list. Separate
4438 * statements by type, and do preliminary analysis.
4439 */
4440 foreach(elements, schemaElts)
4441 {
4442 Node *element = lfirst(elements);
4443
4444 switch (nodeTag(element))
4445 {
4446 case T_CreateSeqStmt:
4447 {
4449
4450 setSchemaName(cxt.schemaname, &elp->sequence->schemaname);
4451 cxt.sequences = lappend(cxt.sequences, element);
4452 }
4453 break;
4454
4455 case T_CreateStmt:
4456 {
4458
4459 setSchemaName(cxt.schemaname, &elp->relation->schemaname);
4460
4461 /*
4462 * XXX todo: deal with constraints
4463 */
4464 cxt.tables = lappend(cxt.tables, element);
4465 }
4466 break;
4467
4468 case T_ViewStmt:
4469 {
4471
4472 setSchemaName(cxt.schemaname, &elp->view->schemaname);
4473
4474 /*
4475 * XXX todo: deal with references between views
4476 */
4477 cxt.views = lappend(cxt.views, element);
4478 }
4479 break;
4480
4481 case T_IndexStmt:
4482 {
4484
4485 setSchemaName(cxt.schemaname, &elp->relation->schemaname);
4486 cxt.indexes = lappend(cxt.indexes, element);
4487 }
4488 break;
4489
4490 case T_CreateTrigStmt:
4491 {
4493
4494 setSchemaName(cxt.schemaname, &elp->relation->schemaname);
4495 cxt.triggers = lappend(cxt.triggers, element);
4496 }
4497 break;
4498
4499 case T_GrantStmt:
4500 cxt.grants = lappend(cxt.grants, element);
4501 break;
4502
4503 default:
4504 elog(ERROR, "unrecognized node type: %d",
4505 (int) nodeTag(element));
4506 }
4507 }
4508
4509 result = NIL;
4510 result = list_concat(result, cxt.sequences);
4511 result = list_concat(result, cxt.tables);
4512 result = list_concat(result, cxt.views);
4513 result = list_concat(result, cxt.indexes);
4514 result = list_concat(result, cxt.triggers);
4515 result = list_concat(result, cxt.grants);
4516
4517 return result;
4518}
static void setSchemaName(const char *context_schema, char **stmt_schema_name)
static chr element(struct vars *v, const chr *startp, const chr *endp)

References element(), elog, ERROR, fb(), CreateSchemaStmtContext::grants, CreateSchemaStmtContext::indexes, lappend(), lfirst, list_concat(), NIL, nodeTag, CreateSchemaStmtContext::schemaname, CreateSchemaStmtContext::sequences, setSchemaName(), CreateSchemaStmtContext::tables, CreateSchemaStmtContext::triggers, and CreateSchemaStmtContext::views.

Referenced by CreateSchemaCommand().

◆ transformCreateStmt()

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

Definition at line 168 of file parse_utilcmd.c.

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

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

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

Definition at line 4598 of file parse_utilcmd.c.

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

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

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

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