PostgreSQL Source Code  git master
parse_utilcmd.c File Reference
Include dependency graph for parse_utilcmd.c:

Go to the source code of this file.

Data Structures

struct  CreateStmtContext
 
struct  CreateSchemaStmtContext
 

Macros

#define SUPPORTS_ATTRS(node)
 

Functions

static void transformColumnDefinition (CreateStmtContext *cxt, ColumnDef *column)
 
static void transformTableConstraint (CreateStmtContext *cxt, Constraint *constraint)
 
static void transformTableLikeClause (CreateStmtContext *cxt, TableLikeClause *table_like_clause)
 
static void transformOfType (CreateStmtContext *cxt, TypeName *ofTypename)
 
static CreateStatsStmtgenerateClonedExtStatsStmt (RangeVar *heapRel, Oid heapRelid, Oid source_statsid)
 
static Listget_collation (Oid collation, Oid actual_datatype)
 
static Listget_opclass (Oid opclass, Oid actual_datatype)
 
static void transformIndexConstraints (CreateStmtContext *cxt)
 
static IndexStmttransformIndexConstraint (Constraint *constraint, CreateStmtContext *cxt)
 
static void transformExtendedStatistics (CreateStmtContext *cxt)
 
static void transformFKConstraints (CreateStmtContext *cxt, bool skipValidation, bool isAddConstraint)
 
static void transformCheckConstraints (CreateStmtContext *cxt, bool skipValidation)
 
static void transformConstraintAttrs (CreateStmtContext *cxt, List *constraintList)
 
static void transformColumnType (CreateStmtContext *cxt, ColumnDef *column)
 
static void setSchemaName (char *context_schema, char **stmt_schema_name)
 
static void transformPartitionCmd (CreateStmtContext *cxt, PartitionCmd *cmd)
 
static ListtransformPartitionRangeBounds (ParseState *pstate, List *blist, Relation parent)
 
static void validateInfiniteBounds (ParseState *pstate, List *blist)
 
static ConsttransformPartitionBoundValue (ParseState *pstate, Node *val, const char *colName, Oid colType, int32 colTypmod, Oid partCollation)
 
ListtransformCreateStmt (CreateStmt *stmt, const char *queryString)
 
static void generateSerialExtraStmts (CreateStmtContext *cxt, ColumnDef *column, Oid seqtypid, List *seqoptions, bool for_identity, bool col_exists, char **snamespace_p, char **sname_p)
 
ListexpandTableLikeClause (RangeVar *heapRel, TableLikeClause *table_like_clause)
 
IndexStmtgenerateClonedIndexStmt (RangeVar *heapRel, Relation source_idx, const AttrMap *attmap, Oid *constraintOid)
 
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)
 
AlterTableStmttransformAlterTableStmt (Oid relid, AlterTableStmt *stmt, const char *queryString, List **beforeStmts, List **afterStmts)
 
ListtransformCreateSchemaStmt (CreateSchemaStmt *stmt)
 
PartitionBoundSpectransformPartitionBound (ParseState *pstate, Relation parent, PartitionBoundSpec *spec)
 

Macro Definition Documentation

◆ SUPPORTS_ATTRS

#define SUPPORTS_ATTRS (   node)
Value:
((node) != NULL && \
((node)->contype == CONSTR_PRIMARY || \
(node)->contype == CONSTR_UNIQUE || \
(node)->contype == CONSTR_EXCLUSION || \
(node)->contype == CONSTR_FOREIGN))
@ CONSTR_FOREIGN
Definition: parsenodes.h:2533
@ CONSTR_UNIQUE
Definition: parsenodes.h:2531
@ CONSTR_EXCLUSION
Definition: parsenodes.h:2532
@ CONSTR_PRIMARY
Definition: parsenodes.h:2530

Function Documentation

◆ expandTableLikeClause()

List* expandTableLikeClause ( RangeVar heapRel,
TableLikeClause table_like_clause 
)

Definition at line 1192 of file parse_utilcmd.c.

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

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

Referenced by ProcessUtilitySlow().

◆ generateClonedExtStatsStmt()

static CreateStatsStmt * generateClonedExtStatsStmt ( RangeVar heapRel,
Oid  heapRelid,
Oid  source_statsid 
)
static

Definition at line 1867 of file parse_utilcmd.c.

1869 {
1870  HeapTuple ht_stats;
1871  Form_pg_statistic_ext statsrec;
1872  CreateStatsStmt *stats;
1873  List *stat_types = NIL;
1874  List *def_names = NIL;
1875  bool isnull;
1876  Datum datum;
1877  ArrayType *arr;
1878  char *enabled;
1879  int i;
1880 
1881  Assert(OidIsValid(heapRelid));
1882  Assert(heapRel != NULL);
1883 
1884  /*
1885  * Fetch pg_statistic_ext tuple of source statistics object.
1886  */
1887  ht_stats = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(source_statsid));
1888  if (!HeapTupleIsValid(ht_stats))
1889  elog(ERROR, "cache lookup failed for statistics object %u", source_statsid);
1890  statsrec = (Form_pg_statistic_ext) GETSTRUCT(ht_stats);
1891 
1892  /* Determine which statistics types exist */
1893  datum = SysCacheGetAttrNotNull(STATEXTOID, ht_stats,
1894  Anum_pg_statistic_ext_stxkind);
1895  arr = DatumGetArrayTypeP(datum);
1896  if (ARR_NDIM(arr) != 1 ||
1897  ARR_HASNULL(arr) ||
1898  ARR_ELEMTYPE(arr) != CHAROID)
1899  elog(ERROR, "stxkind is not a 1-D char array");
1900  enabled = (char *) ARR_DATA_PTR(arr);
1901  for (i = 0; i < ARR_DIMS(arr)[0]; i++)
1902  {
1903  if (enabled[i] == STATS_EXT_NDISTINCT)
1904  stat_types = lappend(stat_types, makeString("ndistinct"));
1905  else if (enabled[i] == STATS_EXT_DEPENDENCIES)
1906  stat_types = lappend(stat_types, makeString("dependencies"));
1907  else if (enabled[i] == STATS_EXT_MCV)
1908  stat_types = lappend(stat_types, makeString("mcv"));
1909  else if (enabled[i] == STATS_EXT_EXPRESSIONS)
1910  /* expression stats are not exposed to users */
1911  continue;
1912  else
1913  elog(ERROR, "unrecognized statistics kind %c", enabled[i]);
1914  }
1915 
1916  /* Determine which columns the statistics are on */
1917  for (i = 0; i < statsrec->stxkeys.dim1; i++)
1918  {
1919  StatsElem *selem = makeNode(StatsElem);
1920  AttrNumber attnum = statsrec->stxkeys.values[i];
1921 
1922  selem->name = get_attname(heapRelid, attnum, false);
1923  selem->expr = NULL;
1924 
1925  def_names = lappend(def_names, selem);
1926  }
1927 
1928  /*
1929  * Now handle expressions, if there are any. The order (with respect to
1930  * regular attributes) does not really matter for extended stats, so we
1931  * simply append them after simple column references.
1932  *
1933  * XXX Some places during build/estimation treat expressions as if they
1934  * are before attributes, but for the CREATE command that's entirely
1935  * irrelevant.
1936  */
1937  datum = SysCacheGetAttr(STATEXTOID, ht_stats,
1938  Anum_pg_statistic_ext_stxexprs, &isnull);
1939 
1940  if (!isnull)
1941  {
1942  ListCell *lc;
1943  List *exprs = NIL;
1944  char *exprsString;
1945 
1946  exprsString = TextDatumGetCString(datum);
1947  exprs = (List *) stringToNode(exprsString);
1948 
1949  foreach(lc, exprs)
1950  {
1951  StatsElem *selem = makeNode(StatsElem);
1952 
1953  selem->name = NULL;
1954  selem->expr = (Node *) lfirst(lc);
1955 
1956  def_names = lappend(def_names, selem);
1957  }
1958 
1959  pfree(exprsString);
1960  }
1961 
1962  /* finally, build the output node */
1963  stats = makeNode(CreateStatsStmt);
1964  stats->defnames = NULL;
1965  stats->stat_types = stat_types;
1966  stats->exprs = def_names;
1967  stats->relations = list_make1(heapRel);
1968  stats->stxcomment = NULL;
1969  stats->transformed = true; /* don't need transformStatsStmt again */
1970  stats->if_not_exists = false;
1971 
1972  /* Clean up */
1973  ReleaseSysCache(ht_stats);
1974 
1975  return stats;
1976 }
#define ARR_NDIM(a)
Definition: array.h:283
#define ARR_DATA_PTR(a)
Definition: array.h:315
#define DatumGetArrayTypeP(X)
Definition: array.h:254
#define ARR_ELEMTYPE(a)
Definition: array.h:285
#define ARR_DIMS(a)
Definition: array.h:287
#define ARR_HASNULL(a)
Definition: array.h:284
#define TextDatumGetCString(d)
Definition: builtins.h:95
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
Assert(fmt[strlen(fmt) - 1] !='\n')
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:826
void pfree(void *pointer)
Definition: mcxt.c:1436
int16 attnum
Definition: pg_attribute.h:74
#define lfirst(lc)
Definition: pg_list.h:172
#define list_make1(x1)
Definition: pg_list.h:212
FormData_pg_statistic_ext * Form_pg_statistic_ext
uintptr_t Datum
Definition: postgres.h:64
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
char * name
Definition: parsenodes.h:3239
Node * expr
Definition: parsenodes.h:3240
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:866
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:818
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1079
Datum SysCacheGetAttrNotNull(int cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition: syscache.c:1110
@ STATEXTOID
Definition: syscache.h:96

References ARR_DATA_PTR, ARR_DIMS, ARR_ELEMTYPE, ARR_HASNULL, ARR_NDIM, Assert(), attnum, DatumGetArrayTypeP, CreateStatsStmt::defnames, elog(), ERROR, StatsElem::expr, CreateStatsStmt::exprs, get_attname(), GETSTRUCT, HeapTupleIsValid, i, CreateStatsStmt::if_not_exists, lappend(), lfirst, list_make1, makeNode, makeString(), StatsElem::name, NIL, ObjectIdGetDatum(), OidIsValid, pfree(), CreateStatsStmt::relations, ReleaseSysCache(), SearchSysCache1(), CreateStatsStmt::stat_types, STATEXTOID, stringToNode(), CreateStatsStmt::stxcomment, SysCacheGetAttr(), SysCacheGetAttrNotNull(), TextDatumGetCString, and CreateStatsStmt::transformed.

Referenced by transformTableLikeClause().

◆ generateClonedIndexStmt()

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

Definition at line 1518 of file parse_utilcmd.c.

1521 {
1522  Oid source_relid = RelationGetRelid(source_idx);
1523  HeapTuple ht_idxrel;
1524  HeapTuple ht_idx;
1525  HeapTuple ht_am;
1526  Form_pg_class idxrelrec;
1527  Form_pg_index idxrec;
1528  Form_pg_am amrec;
1529  oidvector *indcollation;
1530  oidvector *indclass;
1531  IndexStmt *index;
1532  List *indexprs;
1533  ListCell *indexpr_item;
1534  Oid indrelid;
1535  int keyno;
1536  Oid keycoltype;
1537  Datum datum;
1538  bool isnull;
1539 
1540  if (constraintOid)
1541  *constraintOid = InvalidOid;
1542 
1543  /*
1544  * Fetch pg_class tuple of source index. We can't use the copy in the
1545  * relcache entry because it doesn't include optional fields.
1546  */
1547  ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(source_relid));
1548  if (!HeapTupleIsValid(ht_idxrel))
1549  elog(ERROR, "cache lookup failed for relation %u", source_relid);
1550  idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
1551 
1552  /* Fetch pg_index tuple for source index from relcache entry */
1553  ht_idx = source_idx->rd_indextuple;
1554  idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
1555  indrelid = idxrec->indrelid;
1556 
1557  /* Fetch the pg_am tuple of the index' access method */
1558  ht_am = SearchSysCache1(AMOID, ObjectIdGetDatum(idxrelrec->relam));
1559  if (!HeapTupleIsValid(ht_am))
1560  elog(ERROR, "cache lookup failed for access method %u",
1561  idxrelrec->relam);
1562  amrec = (Form_pg_am) GETSTRUCT(ht_am);
1563 
1564  /* Extract indcollation from the pg_index tuple */
1565  datum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
1566  Anum_pg_index_indcollation);
1567  indcollation = (oidvector *) DatumGetPointer(datum);
1568 
1569  /* Extract indclass from the pg_index tuple */
1570  datum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx, Anum_pg_index_indclass);
1571  indclass = (oidvector *) DatumGetPointer(datum);
1572 
1573  /* Begin building the IndexStmt */
1575  index->relation = heapRel;
1576  index->accessMethod = pstrdup(NameStr(amrec->amname));
1577  if (OidIsValid(idxrelrec->reltablespace))
1578  index->tableSpace = get_tablespace_name(idxrelrec->reltablespace);
1579  else
1580  index->tableSpace = NULL;
1581  index->excludeOpNames = NIL;
1582  index->idxcomment = NULL;
1583  index->indexOid = InvalidOid;
1584  index->oldNumber = InvalidRelFileNumber;
1585  index->oldCreateSubid = InvalidSubTransactionId;
1586  index->oldFirstRelfilelocatorSubid = InvalidSubTransactionId;
1587  index->unique = idxrec->indisunique;
1588  index->nulls_not_distinct = idxrec->indnullsnotdistinct;
1589  index->primary = idxrec->indisprimary;
1590  index->transformed = true; /* don't need transformIndexStmt */
1591  index->concurrent = false;
1592  index->if_not_exists = false;
1593  index->reset_default_tblspc = false;
1594 
1595  /*
1596  * We don't try to preserve the name of the source index; instead, just
1597  * let DefineIndex() choose a reasonable name. (If we tried to preserve
1598  * the name, we'd get duplicate-relation-name failures unless the source
1599  * table was in a different schema.)
1600  */
1601  index->idxname = NULL;
1602 
1603  /*
1604  * If the index is marked PRIMARY or has an exclusion condition, it's
1605  * certainly from a constraint; else, if it's not marked UNIQUE, it
1606  * certainly isn't. If it is or might be from a constraint, we have to
1607  * fetch the pg_constraint record.
1608  */
1609  if (index->primary || index->unique || idxrec->indisexclusion)
1610  {
1611  Oid constraintId = get_index_constraint(source_relid);
1612 
1613  if (OidIsValid(constraintId))
1614  {
1615  HeapTuple ht_constr;
1616  Form_pg_constraint conrec;
1617 
1618  if (constraintOid)
1619  *constraintOid = constraintId;
1620 
1621  ht_constr = SearchSysCache1(CONSTROID,
1622  ObjectIdGetDatum(constraintId));
1623  if (!HeapTupleIsValid(ht_constr))
1624  elog(ERROR, "cache lookup failed for constraint %u",
1625  constraintId);
1626  conrec = (Form_pg_constraint) GETSTRUCT(ht_constr);
1627 
1628  index->isconstraint = true;
1629  index->deferrable = conrec->condeferrable;
1630  index->initdeferred = conrec->condeferred;
1631 
1632  /* If it's an exclusion constraint, we need the operator names */
1633  if (idxrec->indisexclusion)
1634  {
1635  Datum *elems;
1636  int nElems;
1637  int i;
1638 
1639  Assert(conrec->contype == CONSTRAINT_EXCLUSION);
1640  /* Extract operator OIDs from the pg_constraint tuple */
1641  datum = SysCacheGetAttrNotNull(CONSTROID, ht_constr,
1642  Anum_pg_constraint_conexclop);
1643  deconstruct_array_builtin(DatumGetArrayTypeP(datum), OIDOID, &elems, NULL, &nElems);
1644 
1645  for (i = 0; i < nElems; i++)
1646  {
1647  Oid operid = DatumGetObjectId(elems[i]);
1648  HeapTuple opertup;
1649  Form_pg_operator operform;
1650  char *oprname;
1651  char *nspname;
1652  List *namelist;
1653 
1654  opertup = SearchSysCache1(OPEROID,
1655  ObjectIdGetDatum(operid));
1656  if (!HeapTupleIsValid(opertup))
1657  elog(ERROR, "cache lookup failed for operator %u",
1658  operid);
1659  operform = (Form_pg_operator) GETSTRUCT(opertup);
1660  oprname = pstrdup(NameStr(operform->oprname));
1661  /* For simplicity we always schema-qualify the op name */
1662  nspname = get_namespace_name(operform->oprnamespace);
1663  namelist = list_make2(makeString(nspname),
1664  makeString(oprname));
1665  index->excludeOpNames = lappend(index->excludeOpNames,
1666  namelist);
1667  ReleaseSysCache(opertup);
1668  }
1669  }
1670 
1671  ReleaseSysCache(ht_constr);
1672  }
1673  else
1674  index->isconstraint = false;
1675  }
1676  else
1677  index->isconstraint = false;
1678 
1679  /* Get the index expressions, if any */
1680  datum = SysCacheGetAttr(INDEXRELID, ht_idx,
1681  Anum_pg_index_indexprs, &isnull);
1682  if (!isnull)
1683  {
1684  char *exprsString;
1685 
1686  exprsString = TextDatumGetCString(datum);
1687  indexprs = (List *) stringToNode(exprsString);
1688  }
1689  else
1690  indexprs = NIL;
1691 
1692  /* Build the list of IndexElem */
1693  index->indexParams = NIL;
1694  index->indexIncludingParams = NIL;
1695 
1696  indexpr_item = list_head(indexprs);
1697  for (keyno = 0; keyno < idxrec->indnkeyatts; keyno++)
1698  {
1699  IndexElem *iparam;
1700  AttrNumber attnum = idxrec->indkey.values[keyno];
1702  keyno);
1703  int16 opt = source_idx->rd_indoption[keyno];
1704 
1705  iparam = makeNode(IndexElem);
1706 
1708  {
1709  /* Simple index column */
1710  char *attname;
1711 
1712  attname = get_attname(indrelid, attnum, false);
1713  keycoltype = get_atttype(indrelid, attnum);
1714 
1715  iparam->name = attname;
1716  iparam->expr = NULL;
1717  }
1718  else
1719  {
1720  /* Expressional index */
1721  Node *indexkey;
1722  bool found_whole_row;
1723 
1724  if (indexpr_item == NULL)
1725  elog(ERROR, "too few entries in indexprs list");
1726  indexkey = (Node *) lfirst(indexpr_item);
1727  indexpr_item = lnext(indexprs, indexpr_item);
1728 
1729  /* Adjust Vars to match new table's column numbering */
1730  indexkey = map_variable_attnos(indexkey,
1731  1, 0,
1732  attmap,
1733  InvalidOid, &found_whole_row);
1734 
1735  /* As in expandTableLikeClause, reject whole-row variables */
1736  if (found_whole_row)
1737  ereport(ERROR,
1738  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1739  errmsg("cannot convert whole-row table reference"),
1740  errdetail("Index \"%s\" contains a whole-row table reference.",
1741  RelationGetRelationName(source_idx))));
1742 
1743  iparam->name = NULL;
1744  iparam->expr = indexkey;
1745 
1746  keycoltype = exprType(indexkey);
1747  }
1748 
1749  /* Copy the original index column name */
1750  iparam->indexcolname = pstrdup(NameStr(attr->attname));
1751 
1752  /* Add the collation name, if non-default */
1753  iparam->collation = get_collation(indcollation->values[keyno], keycoltype);
1754 
1755  /* Add the operator class name, if non-default */
1756  iparam->opclass = get_opclass(indclass->values[keyno], keycoltype);
1757  iparam->opclassopts =
1758  untransformRelOptions(get_attoptions(source_relid, keyno + 1));
1759 
1760  iparam->ordering = SORTBY_DEFAULT;
1762 
1763  /* Adjust options if necessary */
1764  if (source_idx->rd_indam->amcanorder)
1765  {
1766  /*
1767  * If it supports sort ordering, copy DESC and NULLS opts. Don't
1768  * set non-default settings unnecessarily, though, so as to
1769  * improve the chance of recognizing equivalence to constraint
1770  * indexes.
1771  */
1772  if (opt & INDOPTION_DESC)
1773  {
1774  iparam->ordering = SORTBY_DESC;
1775  if ((opt & INDOPTION_NULLS_FIRST) == 0)
1777  }
1778  else
1779  {
1780  if (opt & INDOPTION_NULLS_FIRST)
1782  }
1783  }
1784 
1785  index->indexParams = lappend(index->indexParams, iparam);
1786  }
1787 
1788  /* Handle included columns separately */
1789  for (keyno = idxrec->indnkeyatts; keyno < idxrec->indnatts; keyno++)
1790  {
1791  IndexElem *iparam;
1792  AttrNumber attnum = idxrec->indkey.values[keyno];
1794  keyno);
1795 
1796  iparam = makeNode(IndexElem);
1797 
1799  {
1800  /* Simple index column */
1801  char *attname;
1802 
1803  attname = get_attname(indrelid, attnum, false);
1804 
1805  iparam->name = attname;
1806  iparam->expr = NULL;
1807  }
1808  else
1809  ereport(ERROR,
1810  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1811  errmsg("expressions are not supported in included columns")));
1812 
1813  /* Copy the original index column name */
1814  iparam->indexcolname = pstrdup(NameStr(attr->attname));
1815 
1816  index->indexIncludingParams = lappend(index->indexIncludingParams, iparam);
1817  }
1818  /* Copy reloptions if any */
1819  datum = SysCacheGetAttr(RELOID, ht_idxrel,
1820  Anum_pg_class_reloptions, &isnull);
1821  if (!isnull)
1822  index->options = untransformRelOptions(datum);
1823 
1824  /* If it's a partial index, decompile and append the predicate */
1825  datum = SysCacheGetAttr(INDEXRELID, ht_idx,
1826  Anum_pg_index_indpred, &isnull);
1827  if (!isnull)
1828  {
1829  char *pred_str;
1830  Node *pred_tree;
1831  bool found_whole_row;
1832 
1833  /* Convert text string to node tree */
1834  pred_str = TextDatumGetCString(datum);
1835  pred_tree = (Node *) stringToNode(pred_str);
1836 
1837  /* Adjust Vars to match new table's column numbering */
1838  pred_tree = map_variable_attnos(pred_tree,
1839  1, 0,
1840  attmap,
1841  InvalidOid, &found_whole_row);
1842 
1843  /* As in expandTableLikeClause, reject whole-row variables */
1844  if (found_whole_row)
1845  ereport(ERROR,
1846  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1847  errmsg("cannot convert whole-row table reference"),
1848  errdetail("Index \"%s\" contains a whole-row table reference.",
1849  RelationGetRelationName(source_idx))));
1850 
1851  index->whereClause = pred_tree;
1852  }
1853 
1854  /* Clean up */
1855  ReleaseSysCache(ht_idxrel);
1856  ReleaseSysCache(ht_am);
1857 
1858  return index;
1859 }
void deconstruct_array_builtin(ArrayType *array, Oid elmtype, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3666
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
char * get_tablespace_name(Oid spc_oid)
Definition: tablespace.c:1478
signed short int16
Definition: c.h:477
#define InvalidSubTransactionId
Definition: c.h:642
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3324
Datum get_attoptions(Oid relid, int16 attnum)
Definition: lsyscache.c:996
Oid get_atttype(Oid relid, AttrNumber attnum)
Definition: lsyscache.c:939
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:43
static List * get_collation(Oid collation, Oid actual_datatype)
static List * get_opclass(Oid opclass, Oid actual_datatype)
@ SORTBY_NULLS_DEFAULT
Definition: parsenodes.h:61
@ SORTBY_NULLS_LAST
Definition: parsenodes.h:63
@ SORTBY_NULLS_FIRST
Definition: parsenodes.h:62
@ SORTBY_DESC
Definition: parsenodes.h:55
@ SORTBY_DEFAULT
Definition: parsenodes.h:53
FormData_pg_am * Form_pg_am
Definition: pg_am.h:48
NameData attname
Definition: pg_attribute.h:41
FormData_pg_class * Form_pg_class
Definition: pg_class.h:153
FormData_pg_constraint * Form_pg_constraint
Oid get_index_constraint(Oid indexId)
Definition: pg_depend.c:968
FormData_pg_index * Form_pg_index
Definition: pg_index.h:70
static ListCell * list_head(const List *l)
Definition: pg_list.h:128
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:343
#define list_make2(x1, x2)
Definition: pg_list.h:214
FormData_pg_operator * Form_pg_operator
Definition: pg_operator.h:83
static Oid DatumGetObjectId(Datum X)
Definition: postgres.h:242
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
List * untransformRelOptions(Datum options)
Definition: reloptions.c:1333
#define InvalidRelFileNumber
Definition: relpath.h:26
bool amcanorder
Definition: amapi.h:220
Node * expr
Definition: parsenodes.h:779
SortByDir ordering
Definition: parsenodes.h:784
List * opclassopts
Definition: parsenodes.h:783
char * indexcolname
Definition: parsenodes.h:780
SortByNulls nulls_ordering
Definition: parsenodes.h:785
List * opclass
Definition: parsenodes.h:782
char * name
Definition: parsenodes.h:778
List * collation
Definition: parsenodes.h:781
struct IndexAmRoutine * rd_indam
Definition: rel.h:204
struct HeapTupleData * rd_indextuple
Definition: rel.h:192
int16 * rd_indoption
Definition: rel.h:209
Definition: type.h:95
Definition: c.h:710
Oid values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:717
@ AMOID
Definition: syscache.h:36
@ OPEROID
Definition: syscache.h:72
@ INDEXRELID
Definition: syscache.h:66
@ RELOID
Definition: syscache.h:89
@ CONSTROID
Definition: syscache.h:53

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

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

◆ generateSerialExtraStmts()

static void generateSerialExtraStmts ( CreateStmtContext cxt,
ColumnDef column,
Oid  seqtypid,
List seqoptions,
bool  for_identity,
bool  col_exists,
char **  snamespace_p,
char **  sname_p 
)
static

Definition at line 369 of file parse_utilcmd.c.

373 {
374  ListCell *option;
375  DefElem *nameEl = NULL;
376  Oid snamespaceid;
377  char *snamespace;
378  char *sname;
379  CreateSeqStmt *seqstmt;
380  AlterSeqStmt *altseqstmt;
381  List *attnamelist;
382  int nameEl_idx = -1;
383 
384  /*
385  * Determine namespace and name to use for the sequence.
386  *
387  * First, check if a sequence name was passed in as an option. This is
388  * used by pg_dump. Else, generate a name.
389  *
390  * Although we use ChooseRelationName, it's not guaranteed that the
391  * selected sequence name won't conflict; given sufficiently long field
392  * names, two different serial columns in the same table could be assigned
393  * the same sequence name, and we'd not notice since we aren't creating
394  * the sequence quite yet. In practice this seems quite unlikely to be a
395  * problem, especially since few people would need two serial columns in
396  * one table.
397  */
398  foreach(option, seqoptions)
399  {
400  DefElem *defel = lfirst_node(DefElem, option);
401 
402  if (strcmp(defel->defname, "sequence_name") == 0)
403  {
404  if (nameEl)
405  errorConflictingDefElem(defel, cxt->pstate);
406  nameEl = defel;
407  nameEl_idx = foreach_current_index(option);
408  }
409  }
410 
411  if (nameEl)
412  {
414 
415  snamespace = rv->schemaname;
416  if (!snamespace)
417  {
418  /* Given unqualified SEQUENCE NAME, select namespace */
419  if (cxt->rel)
420  snamespaceid = RelationGetNamespace(cxt->rel);
421  else
422  snamespaceid = RangeVarGetCreationNamespace(cxt->relation);
423  snamespace = get_namespace_name(snamespaceid);
424  }
425  sname = rv->relname;
426  /* Remove the SEQUENCE NAME item from seqoptions */
427  seqoptions = list_delete_nth_cell(seqoptions, nameEl_idx);
428  }
429  else
430  {
431  if (cxt->rel)
432  snamespaceid = RelationGetNamespace(cxt->rel);
433  else
434  {
435  snamespaceid = RangeVarGetCreationNamespace(cxt->relation);
436  RangeVarAdjustRelationPersistence(cxt->relation, snamespaceid);
437  }
438  snamespace = get_namespace_name(snamespaceid);
439  sname = ChooseRelationName(cxt->relation->relname,
440  column->colname,
441  "seq",
442  snamespaceid,
443  false);
444  }
445 
446  ereport(DEBUG1,
447  (errmsg_internal("%s will create implicit sequence \"%s\" for serial column \"%s.%s\"",
448  cxt->stmtType, sname,
449  cxt->relation->relname, column->colname)));
450 
451  /*
452  * Build a CREATE SEQUENCE command to create the sequence object, and add
453  * it to the list of things to be done before this CREATE/ALTER TABLE.
454  */
455  seqstmt = makeNode(CreateSeqStmt);
456  seqstmt->for_identity = for_identity;
457  seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
459  seqstmt->options = seqoptions;
460 
461  /*
462  * If a sequence data type was specified, add it to the options. Prepend
463  * to the list rather than append; in case a user supplied their own AS
464  * clause, the "redundant options" error will point to their occurrence,
465  * not our synthetic one.
466  */
467  if (seqtypid)
468  seqstmt->options = lcons(makeDefElem("as",
469  (Node *) makeTypeNameFromOid(seqtypid, -1),
470  -1),
471  seqstmt->options);
472 
473  /*
474  * If this is ALTER ADD COLUMN, make sure the sequence will be owned by
475  * the table's owner. The current user might be someone else (perhaps a
476  * superuser, or someone who's only a member of the owning role), but the
477  * SEQUENCE OWNED BY mechanisms will bleat unless table and sequence have
478  * exactly the same owning role.
479  */
480  if (cxt->rel)
481  seqstmt->ownerId = cxt->rel->rd_rel->relowner;
482  else
483  seqstmt->ownerId = InvalidOid;
484 
485  cxt->blist = lappend(cxt->blist, seqstmt);
486 
487  /*
488  * Store the identity sequence name that we decided on. ALTER TABLE ...
489  * ADD COLUMN ... IDENTITY needs this so that it can fill the new column
490  * with values from the sequence, while the association of the sequence
491  * with the table is not set until after the ALTER TABLE.
492  */
493  column->identitySequence = seqstmt->sequence;
494 
495  /*
496  * Build an ALTER SEQUENCE ... OWNED BY command to mark the sequence as
497  * owned by this column, and add it to the appropriate list of things to
498  * be done along with this CREATE/ALTER TABLE. In a CREATE or ALTER ADD
499  * COLUMN, it must be done after the statement because we don't know the
500  * column's attnum yet. But if we do have the attnum (in AT_AddIdentity),
501  * we can do the marking immediately, which improves some ALTER TABLE
502  * behaviors.
503  */
504  altseqstmt = makeNode(AlterSeqStmt);
505  altseqstmt->sequence = makeRangeVar(snamespace, sname, -1);
506  attnamelist = list_make3(makeString(snamespace),
507  makeString(cxt->relation->relname),
508  makeString(column->colname));
509  altseqstmt->options = list_make1(makeDefElem("owned_by",
510  (Node *) attnamelist, -1));
511  altseqstmt->for_identity = for_identity;
512 
513  if (col_exists)
514  cxt->blist = lappend(cxt->blist, altseqstmt);
515  else
516  cxt->alist = lappend(cxt->alist, altseqstmt);
517 
518  if (snamespace_p)
519  *snamespace_p = snamespace;
520  if (sname_p)
521  *sname_p = sname;
522 }
void errorConflictingDefElem(DefElem *defel, ParseState *pstate)
Definition: define.c:385
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1156
#define DEBUG1
Definition: elog.h:30
char * ChooseRelationName(const char *name1, const char *name2, const char *label, Oid namespaceid, bool isconstraint)
Definition: indexcmds.c:2475
List * list_delete_nth_cell(List *list, int n)
Definition: list.c:766
RangeVar * makeRangeVar(char *schemaname, char *relname, int location)
Definition: makefuncs.c:425
DefElem * makeDefElem(char *name, Node *arg, int location)
Definition: makefuncs.c:549
TypeName * makeTypeNameFromOid(Oid typeOid, int32 typmod)
Definition: makefuncs.c:475
RangeVar * makeRangeVarFromNameList(List *names)
Definition: namespace.c:3105
void RangeVarAdjustRelationPersistence(RangeVar *newRelation, Oid nspid)
Definition: namespace.c:644
Oid RangeVarGetCreationNamespace(const RangeVar *newRelation)
Definition: namespace.c:452
#define castNode(_type_, nodeptr)
Definition: nodes.h:197
#define lfirst_node(type, lc)
Definition: pg_list.h:176
#define foreach_current_index(cell)
Definition: pg_list.h:403
#define RelationGetNamespace(relation)
Definition: rel.h:544
List * options
Definition: parsenodes.h:2964
RangeVar * sequence
Definition: parsenodes.h:2963
bool for_identity
Definition: parsenodes.h:2965
RangeVar * identitySequence
Definition: parsenodes.h:733
char * colname
Definition: parsenodes.h:721
List * options
Definition: parsenodes.h:2954
RangeVar * sequence
Definition: parsenodes.h:2953
const char * stmtType
Definition: parse_utilcmd.c:76
RangeVar * relation
Definition: parse_utilcmd.c:77
ParseState * pstate
Definition: parse_utilcmd.c:75
char * defname
Definition: parsenodes.h:810
Node * arg
Definition: parsenodes.h:811
char relpersistence
Definition: primnodes.h:80

References CreateStmtContext::alist, DefElem::arg, CreateStmtContext::blist, castNode, ChooseRelationName(), ColumnDef::colname, DEBUG1, DefElem::defname, ereport, errmsg_internal(), errorConflictingDefElem(), CreateSeqStmt::for_identity, AlterSeqStmt::for_identity, foreach_current_index, get_namespace_name(), ColumnDef::identitySequence, InvalidOid, lappend(), lcons(), lfirst_node, list_delete_nth_cell(), list_make1, list_make3, makeDefElem(), makeNode, makeRangeVar(), makeRangeVarFromNameList(), makeString(), makeTypeNameFromOid(), CreateSeqStmt::options, AlterSeqStmt::options, CreateSeqStmt::ownerId, CreateStmtContext::pstate, RangeVarAdjustRelationPersistence(), RangeVarGetCreationNamespace(), RelationData::rd_rel, CreateStmtContext::rel, CreateStmtContext::relation, RelationGetNamespace, RangeVar::relname, RangeVar::relpersistence, RangeVar::schemaname, CreateSeqStmt::sequence, AlterSeqStmt::sequence, and CreateStmtContext::stmtType.

Referenced by transformAlterTableStmt(), transformColumnDefinition(), and transformTableLikeClause().

◆ get_collation()

static List * get_collation ( Oid  collation,
Oid  actual_datatype 
)
static

Definition at line 1985 of file parse_utilcmd.c.

1986 {
1987  List *result;
1988  HeapTuple ht_coll;
1989  Form_pg_collation coll_rec;
1990  char *nsp_name;
1991  char *coll_name;
1992 
1993  if (!OidIsValid(collation))
1994  return NIL; /* easy case */
1995  if (collation == get_typcollation(actual_datatype))
1996  return NIL; /* just let it default */
1997 
1998  ht_coll = SearchSysCache1(COLLOID, ObjectIdGetDatum(collation));
1999  if (!HeapTupleIsValid(ht_coll))
2000  elog(ERROR, "cache lookup failed for collation %u", collation);
2001  coll_rec = (Form_pg_collation) GETSTRUCT(ht_coll);
2002 
2003  /* For simplicity, we always schema-qualify the name */
2004  nsp_name = get_namespace_name(coll_rec->collnamespace);
2005  coll_name = pstrdup(NameStr(coll_rec->collname));
2006  result = list_make2(makeString(nsp_name), makeString(coll_name));
2007 
2008  ReleaseSysCache(ht_coll);
2009  return result;
2010 }
Oid get_typcollation(Oid typid)
Definition: lsyscache.c:3014
FormData_pg_collation * Form_pg_collation
Definition: pg_collation.h:58
@ COLLOID
Definition: syscache.h:50

References COLLOID, elog(), ERROR, get_namespace_name(), get_typcollation(), GETSTRUCT, HeapTupleIsValid, list_make2, makeString(), NameStr, NIL, ObjectIdGetDatum(), OidIsValid, pstrdup(), ReleaseSysCache(), and SearchSysCache1().

Referenced by generateClonedIndexStmt().

◆ get_opclass()

static List * get_opclass ( Oid  opclass,
Oid  actual_datatype 
)
static

Definition at line 2019 of file parse_utilcmd.c.

2020 {
2021  List *result = NIL;
2022  HeapTuple ht_opc;
2023  Form_pg_opclass opc_rec;
2024 
2025  ht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
2026  if (!HeapTupleIsValid(ht_opc))
2027  elog(ERROR, "cache lookup failed for opclass %u", opclass);
2028  opc_rec = (Form_pg_opclass) GETSTRUCT(ht_opc);
2029 
2030  if (GetDefaultOpClass(actual_datatype, opc_rec->opcmethod) != opclass)
2031  {
2032  /* For simplicity, we always schema-qualify the name */
2033  char *nsp_name = get_namespace_name(opc_rec->opcnamespace);
2034  char *opc_name = pstrdup(NameStr(opc_rec->opcname));
2035 
2036  result = list_make2(makeString(nsp_name), makeString(opc_name));
2037  }
2038 
2039  ReleaseSysCache(ht_opc);
2040  return result;
2041 }
Oid GetDefaultOpClass(Oid type_id, Oid am_id)
Definition: indexcmds.c:2280
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:83
@ CLAOID
Definition: syscache.h:48

References CLAOID, elog(), ERROR, get_namespace_name(), GetDefaultOpClass(), GETSTRUCT, HeapTupleIsValid, list_make2, makeString(), NameStr, NIL, ObjectIdGetDatum(), pstrdup(), ReleaseSysCache(), and SearchSysCache1().

Referenced by generateClonedIndexStmt().

◆ setSchemaName()

static void setSchemaName ( char *  context_schema,
char **  stmt_schema_name 
)
static

Definition at line 3912 of file parse_utilcmd.c.

3913 {
3914  if (*stmt_schema_name == NULL)
3915  *stmt_schema_name = context_schema;
3916  else if (strcmp(context_schema, *stmt_schema_name) != 0)
3917  ereport(ERROR,
3918  (errcode(ERRCODE_INVALID_SCHEMA_DEFINITION),
3919  errmsg("CREATE specifies a schema (%s) "
3920  "different from the one being created (%s)",
3921  *stmt_schema_name, context_schema)));
3922 }

References ereport, errcode(), errmsg(), and ERROR.

Referenced by transformCreateSchemaStmt().

◆ transformAlterTableStmt()

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

Definition at line 3279 of file parse_utilcmd.c.

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

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

Referenced by ATParseTransformCmd(), and ATPostAlterTypeParse().

◆ transformCheckConstraints()

static void transformCheckConstraints ( CreateStmtContext cxt,
bool  skipValidation 
)
static

Definition at line 2703 of file parse_utilcmd.c.

2704 {
2705  ListCell *ckclist;
2706 
2707  if (cxt->ckconstraints == NIL)
2708  return;
2709 
2710  /*
2711  * If creating a new table (but not a foreign table), we can safely skip
2712  * validation of check constraints, and nonetheless mark them valid. (This
2713  * will override any user-supplied NOT VALID flag.)
2714  */
2715  if (skipValidation)
2716  {
2717  foreach(ckclist, cxt->ckconstraints)
2718  {
2719  Constraint *constraint = (Constraint *) lfirst(ckclist);
2720 
2721  constraint->skip_validation = true;
2722  constraint->initially_valid = true;
2723  }
2724  }
2725 }

References CreateStmtContext::ckconstraints, Constraint::initially_valid, lfirst, NIL, and Constraint::skip_validation.

Referenced by transformAlterTableStmt(), and transformCreateStmt().

◆ transformColumnDefinition()

static void transformColumnDefinition ( CreateStmtContext cxt,
ColumnDef column 
)
static

Definition at line 530 of file parse_utilcmd.c.

531 {
532  bool is_serial;
533  bool saw_nullable;
534  bool saw_default;
535  bool saw_identity;
536  bool saw_generated;
537  ListCell *clist;
538 
539  cxt->columns = lappend(cxt->columns, column);
540 
541  /* Check for SERIAL pseudo-types */
542  is_serial = false;
543  if (column->typeName
544  && list_length(column->typeName->names) == 1
545  && !column->typeName->pct_type)
546  {
547  char *typname = strVal(linitial(column->typeName->names));
548 
549  if (strcmp(typname, "smallserial") == 0 ||
550  strcmp(typname, "serial2") == 0)
551  {
552  is_serial = true;
553  column->typeName->names = NIL;
554  column->typeName->typeOid = INT2OID;
555  }
556  else if (strcmp(typname, "serial") == 0 ||
557  strcmp(typname, "serial4") == 0)
558  {
559  is_serial = true;
560  column->typeName->names = NIL;
561  column->typeName->typeOid = INT4OID;
562  }
563  else if (strcmp(typname, "bigserial") == 0 ||
564  strcmp(typname, "serial8") == 0)
565  {
566  is_serial = true;
567  column->typeName->names = NIL;
568  column->typeName->typeOid = INT8OID;
569  }
570 
571  /*
572  * We have to reject "serial[]" explicitly, because once we've set
573  * typeid, LookupTypeName won't notice arrayBounds. We don't need any
574  * special coding for serial(typmod) though.
575  */
576  if (is_serial && column->typeName->arrayBounds != NIL)
577  ereport(ERROR,
578  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
579  errmsg("array of serial is not implemented"),
581  column->typeName->location)));
582  }
583 
584  /* Do necessary work on the column type declaration */
585  if (column->typeName)
586  transformColumnType(cxt, column);
587 
588  /* Special actions for SERIAL pseudo-types */
589  if (is_serial)
590  {
591  char *snamespace;
592  char *sname;
593  char *qstring;
594  A_Const *snamenode;
595  TypeCast *castnode;
596  FuncCall *funccallnode;
597  Constraint *constraint;
598 
599  generateSerialExtraStmts(cxt, column,
600  column->typeName->typeOid, NIL,
601  false, false,
602  &snamespace, &sname);
603 
604  /*
605  * Create appropriate constraints for SERIAL. We do this in full,
606  * rather than shortcutting, so that we will detect any conflicting
607  * constraints the user wrote (like a different DEFAULT).
608  *
609  * Create an expression tree representing the function call
610  * nextval('sequencename'). We cannot reduce the raw tree to cooked
611  * form until after the sequence is created, but there's no need to do
612  * so.
613  */
614  qstring = quote_qualified_identifier(snamespace, sname);
615  snamenode = makeNode(A_Const);
616  snamenode->val.node.type = T_String;
617  snamenode->val.sval.sval = qstring;
618  snamenode->location = -1;
619  castnode = makeNode(TypeCast);
620  castnode->typeName = SystemTypeName("regclass");
621  castnode->arg = (Node *) snamenode;
622  castnode->location = -1;
623  funccallnode = makeFuncCall(SystemFuncName("nextval"),
624  list_make1(castnode),
626  -1);
627  constraint = makeNode(Constraint);
628  constraint->contype = CONSTR_DEFAULT;
629  constraint->location = -1;
630  constraint->raw_expr = (Node *) funccallnode;
631  constraint->cooked_expr = NULL;
632  column->constraints = lappend(column->constraints, constraint);
633 
634  constraint = makeNode(Constraint);
635  constraint->contype = CONSTR_NOTNULL;
636  constraint->location = -1;
637  column->constraints = lappend(column->constraints, constraint);
638  }
639 
640  /* Process column constraints, if any... */
642 
643  saw_nullable = false;
644  saw_default = false;
645  saw_identity = false;
646  saw_generated = false;
647 
648  foreach(clist, column->constraints)
649  {
650  Constraint *constraint = lfirst_node(Constraint, clist);
651 
652  switch (constraint->contype)
653  {
654  case CONSTR_NULL:
655  if (saw_nullable && column->is_not_null)
656  ereport(ERROR,
657  (errcode(ERRCODE_SYNTAX_ERROR),
658  errmsg("conflicting NULL/NOT NULL declarations for column \"%s\" of table \"%s\"",
659  column->colname, cxt->relation->relname),
661  constraint->location)));
662  column->is_not_null = false;
663  saw_nullable = true;
664  break;
665 
666  case CONSTR_NOTNULL:
667  if (saw_nullable && !column->is_not_null)
668  ereport(ERROR,
669  (errcode(ERRCODE_SYNTAX_ERROR),
670  errmsg("conflicting NULL/NOT NULL declarations for column \"%s\" of table \"%s\"",
671  column->colname, cxt->relation->relname),
673  constraint->location)));
674  column->is_not_null = true;
675  saw_nullable = true;
676  break;
677 
678  case CONSTR_DEFAULT:
679  if (saw_default)
680  ereport(ERROR,
681  (errcode(ERRCODE_SYNTAX_ERROR),
682  errmsg("multiple default values specified for column \"%s\" of table \"%s\"",
683  column->colname, cxt->relation->relname),
685  constraint->location)));
686  column->raw_default = constraint->raw_expr;
687  Assert(constraint->cooked_expr == NULL);
688  saw_default = true;
689  break;
690 
691  case CONSTR_IDENTITY:
692  {
693  Type ctype;
694  Oid typeOid;
695 
696  if (cxt->ofType)
697  ereport(ERROR,
698  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
699  errmsg("identity columns are not supported on typed tables")));
700  if (cxt->partbound)
701  ereport(ERROR,
702  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
703  errmsg("identity columns are not supported on partitions")));
704 
705  ctype = typenameType(cxt->pstate, column->typeName, NULL);
706  typeOid = ((Form_pg_type) GETSTRUCT(ctype))->oid;
707  ReleaseSysCache(ctype);
708 
709  if (saw_identity)
710  ereport(ERROR,
711  (errcode(ERRCODE_SYNTAX_ERROR),
712  errmsg("multiple identity specifications for column \"%s\" of table \"%s\"",
713  column->colname, cxt->relation->relname),
715  constraint->location)));
716 
717  generateSerialExtraStmts(cxt, column,
718  typeOid, constraint->options,
719  true, false,
720  NULL, NULL);
721 
722  column->identity = constraint->generated_when;
723  saw_identity = true;
724 
725  /* An identity column is implicitly NOT NULL */
726  if (saw_nullable && !column->is_not_null)
727  ereport(ERROR,
728  (errcode(ERRCODE_SYNTAX_ERROR),
729  errmsg("conflicting NULL/NOT NULL declarations for column \"%s\" of table \"%s\"",
730  column->colname, cxt->relation->relname),
732  constraint->location)));
733  column->is_not_null = true;
734  saw_nullable = true;
735  break;
736  }
737 
738  case CONSTR_GENERATED:
739  if (cxt->ofType)
740  ereport(ERROR,
741  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
742  errmsg("generated columns are not supported on typed tables")));
743  if (saw_generated)
744  ereport(ERROR,
745  (errcode(ERRCODE_SYNTAX_ERROR),
746  errmsg("multiple generation clauses specified for column \"%s\" of table \"%s\"",
747  column->colname, cxt->relation->relname),
749  constraint->location)));
750  column->generated = ATTRIBUTE_GENERATED_STORED;
751  column->raw_default = constraint->raw_expr;
752  Assert(constraint->cooked_expr == NULL);
753  saw_generated = true;
754  break;
755 
756  case CONSTR_CHECK:
757  cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
758  break;
759 
760  case CONSTR_PRIMARY:
761  if (cxt->isforeign)
762  ereport(ERROR,
763  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
764  errmsg("primary key constraints are not supported on foreign tables"),
766  constraint->location)));
767  /* FALL THRU */
768 
769  case CONSTR_UNIQUE:
770  if (cxt->isforeign)
771  ereport(ERROR,
772  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
773  errmsg("unique constraints are not supported on foreign tables"),
775  constraint->location)));
776  if (constraint->keys == NIL)
777  constraint->keys = list_make1(makeString(column->colname));
778  cxt->ixconstraints = lappend(cxt->ixconstraints, constraint);
779  break;
780 
781  case CONSTR_EXCLUSION:
782  /* grammar does not allow EXCLUDE as a column constraint */
783  elog(ERROR, "column exclusion constraints are not supported");
784  break;
785 
786  case CONSTR_FOREIGN:
787  if (cxt->isforeign)
788  ereport(ERROR,
789  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
790  errmsg("foreign key constraints are not supported on foreign tables"),
792  constraint->location)));
793 
794  /*
795  * Fill in the current attribute's name and throw it into the
796  * list of FK constraints to be processed later.
797  */
798  constraint->fk_attrs = list_make1(makeString(column->colname));
799  cxt->fkconstraints = lappend(cxt->fkconstraints, constraint);
800  break;
801 
806  /* transformConstraintAttrs took care of these */
807  break;
808 
809  default:
810  elog(ERROR, "unrecognized constraint type: %d",
811  constraint->contype);
812  break;
813  }
814 
815  if (saw_default && saw_identity)
816  ereport(ERROR,
817  (errcode(ERRCODE_SYNTAX_ERROR),
818  errmsg("both default and identity specified for column \"%s\" of table \"%s\"",
819  column->colname, cxt->relation->relname),
821  constraint->location)));
822 
823  if (saw_default && saw_generated)
824  ereport(ERROR,
825  (errcode(ERRCODE_SYNTAX_ERROR),
826  errmsg("both default and generation expression specified for column \"%s\" of table \"%s\"",
827  column->colname, cxt->relation->relname),
829  constraint->location)));
830 
831  if (saw_identity && saw_generated)
832  ereport(ERROR,
833  (errcode(ERRCODE_SYNTAX_ERROR),
834  errmsg("both identity and generation expression specified for column \"%s\" of table \"%s\"",
835  column->colname, cxt->relation->relname),
837  constraint->location)));
838  }
839 
840  /*
841  * If needed, generate ALTER FOREIGN TABLE ALTER COLUMN statement to add
842  * per-column foreign data wrapper options to this column after creation.
843  */
844  if (column->fdwoptions != NIL)
845  {
847  AlterTableCmd *cmd;
848 
849  cmd = makeNode(AlterTableCmd);
851  cmd->name = column->colname;
852  cmd->def = (Node *) column->fdwoptions;
853  cmd->behavior = DROP_RESTRICT;
854  cmd->missing_ok = false;
855 
857  stmt->relation = cxt->relation;
858  stmt->cmds = NIL;
859  stmt->objtype = OBJECT_FOREIGN_TABLE;
860  stmt->cmds = lappend(stmt->cmds, cmd);
861 
862  cxt->alist = lappend(cxt->alist, stmt);
863  }
864 }
FuncCall * makeFuncCall(List *name, List *args, CoercionForm funcformat, int location)
Definition: makefuncs.c:588
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:111
Type typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p)
Definition: parse_type.c:264
static void transformColumnType(CreateStmtContext *cxt, ColumnDef *column)
static void transformConstraintAttrs(CreateStmtContext *cxt, List *constraintList)
@ CONSTR_ATTR_DEFERRED
Definition: parsenodes.h:2536
@ CONSTR_IDENTITY
Definition: parsenodes.h:2527
@ CONSTR_ATTR_NOT_DEFERRABLE
Definition: parsenodes.h:2535
@ CONSTR_DEFAULT
Definition: parsenodes.h:2526
@ CONSTR_NOTNULL
Definition: parsenodes.h:2525
@ CONSTR_ATTR_IMMEDIATE
Definition: parsenodes.h:2537
@ CONSTR_NULL
Definition: parsenodes.h:2523
@ CONSTR_GENERATED
Definition: parsenodes.h:2528
@ CONSTR_ATTR_DEFERRABLE
Definition: parsenodes.h:2534
@ DROP_RESTRICT
Definition: parsenodes.h:2155
@ OBJECT_FOREIGN_TABLE
Definition: parsenodes.h:2100
@ AT_AlterColumnGenericOptions
Definition: parsenodes.h:2199
TypeName * SystemTypeName(char *name)
List * SystemFuncName(char *name)
static int list_length(const List *l)
Definition: pg_list.h:152
#define linitial(l)
Definition: pg_list.h:178
FormData_pg_type * Form_pg_type
Definition: pg_type.h:261
NameData typname
Definition: pg_type.h:41
@ COERCE_EXPLICIT_CALL
Definition: primnodes.h:661
char * quote_qualified_identifier(const char *qualifier, const char *ident)
Definition: ruleutils.c:11835
int location
Definition: parsenodes.h:362
union ValUnion val
Definition: parsenodes.h:360
DropBehavior behavior
Definition: parsenodes.h:2260
bool is_not_null
Definition: parsenodes.h:726
char generated
Definition: parsenodes.h:735
List * fdwoptions
Definition: parsenodes.h:739
List * keys
Definition: parsenodes.h:2573
List * fk_attrs
Definition: parsenodes.h:2593
NodeTag type
Definition: nodes.h:130
char * sval
Definition: value.h:68
TypeName * typeName
Definition: parsenodes.h:372
int location
Definition: parsenodes.h:373
Node * arg
Definition: parsenodes.h:371
Oid typeOid
Definition: parsenodes.h:267
bool pct_type
Definition: parsenodes.h:269
List * names
Definition: parsenodes.h:266
List * arrayBounds
Definition: parsenodes.h:272
int location
Definition: parsenodes.h:273
Node node
Definition: parsenodes.h:347
String sval
Definition: parsenodes.h:351
#define strVal(v)
Definition: value.h:82

References CreateStmtContext::alist, TypeCast::arg, TypeName::arrayBounds, Assert(), AT_AlterColumnGenericOptions, AlterTableCmd::behavior, CreateStmtContext::ckconstraints, COERCE_EXPLICIT_CALL, ColumnDef::colname, CreateStmtContext::columns, CONSTR_ATTR_DEFERRABLE, CONSTR_ATTR_DEFERRED, CONSTR_ATTR_IMMEDIATE, CONSTR_ATTR_NOT_DEFERRABLE, CONSTR_CHECK, CONSTR_DEFAULT, CONSTR_EXCLUSION, CONSTR_FOREIGN, CONSTR_GENERATED, CONSTR_IDENTITY, CONSTR_NOTNULL, CONSTR_NULL, CONSTR_PRIMARY, CONSTR_UNIQUE, ColumnDef::constraints, Constraint::contype, Constraint::cooked_expr, AlterTableCmd::def, DROP_RESTRICT, elog(), ereport, errcode(), errmsg(), ERROR, ColumnDef::fdwoptions, Constraint::fk_attrs, CreateStmtContext::fkconstraints, ColumnDef::generated, Constraint::generated_when, generateSerialExtraStmts(), GETSTRUCT, ColumnDef::identity, ColumnDef::is_not_null, CreateStmtContext::isforeign, CreateStmtContext::ixconstraints, Constraint::keys, lappend(), lfirst_node, linitial, list_length(), list_make1, TypeName::location, A_Const::location, TypeCast::location, Constraint::location, makeFuncCall(), makeNode, makeString(), AlterTableCmd::missing_ok, AlterTableCmd::name, TypeName::names, NIL, ValUnion::node, OBJECT_FOREIGN_TABLE, CreateStmtContext::ofType, Constraint::options, parser_errposition(), CreateStmtContext::partbound, TypeName::pct_type, CreateStmtContext::pstate, quote_qualified_identifier(), ColumnDef::raw_default, Constraint::raw_expr, CreateStmtContext::relation, ReleaseSysCache(), RangeVar::relname, stmt, strVal, AlterTableCmd::subtype, ValUnion::sval, String::sval, SystemFuncName(), SystemTypeName(), transformColumnType(), transformConstraintAttrs(), Node::type, TypeCast::typeName, ColumnDef::typeName, typenameType(), TypeName::typeOid, typname, and A_Const::val.

Referenced by transformAlterTableStmt(), and transformCreateStmt().

◆ transformColumnType()

static void transformColumnType ( CreateStmtContext cxt,
ColumnDef column 
)
static

Definition at line 3754 of file parse_utilcmd.c.

3755 {
3756  /*
3757  * All we really need to do here is verify that the type is valid,
3758  * including any collation spec that might be present.
3759  */
3760  Type ctype = typenameType(cxt->pstate, column->typeName, NULL);
3761 
3762  if (column->collClause)
3763  {
3764  Form_pg_type typtup = (Form_pg_type) GETSTRUCT(ctype);
3765 
3766  LookupCollation(cxt->pstate,
3767  column->collClause->collname,
3768  column->collClause->location);
3769  /* Complain if COLLATE is applied to an uncollatable type */
3770  if (!OidIsValid(typtup->typcollation))
3771  ereport(ERROR,
3772  (errcode(ERRCODE_DATATYPE_MISMATCH),
3773  errmsg("collations are not supported by type %s",
3774  format_type_be(typtup->oid)),
3776  column->collClause->location)));
3777  }
3778 
3779  ReleaseSysCache(ctype);
3780 }
char * format_type_be(Oid type_oid)
Definition: format_type.c:339
Oid LookupCollation(ParseState *pstate, List *collnames, int location)
Definition: parse_type.c:515
List * collname
Definition: parsenodes.h:383
CollateClause * collClause
Definition: parsenodes.h:736

References ColumnDef::collClause, CollateClause::collname, ereport, errcode(), errmsg(), ERROR, format_type_be(), GETSTRUCT, CollateClause::location, LookupCollation(), OidIsValid, parser_errposition(), CreateStmtContext::pstate, ReleaseSysCache(), ColumnDef::typeName, and typenameType().

Referenced by transformColumnDefinition().

◆ transformConstraintAttrs()

static void transformConstraintAttrs ( CreateStmtContext cxt,
List constraintList 
)
static

Definition at line 3639 of file parse_utilcmd.c.

3640 {
3641  Constraint *lastprimarycon = NULL;
3642  bool saw_deferrability = false;
3643  bool saw_initially = false;
3644  ListCell *clist;
3645 
3646 #define SUPPORTS_ATTRS(node) \
3647  ((node) != NULL && \
3648  ((node)->contype == CONSTR_PRIMARY || \
3649  (node)->contype == CONSTR_UNIQUE || \
3650  (node)->contype == CONSTR_EXCLUSION || \
3651  (node)->contype == CONSTR_FOREIGN))
3652 
3653  foreach(clist, constraintList)
3654  {
3655  Constraint *con = (Constraint *) lfirst(clist);
3656 
3657  if (!IsA(con, Constraint))
3658  elog(ERROR, "unrecognized node type: %d",
3659  (int) nodeTag(con));
3660  switch (con->contype)
3661  {
3663  if (!SUPPORTS_ATTRS(lastprimarycon))
3664  ereport(ERROR,
3665  (errcode(ERRCODE_SYNTAX_ERROR),
3666  errmsg("misplaced DEFERRABLE clause"),
3667  parser_errposition(cxt->pstate, con->location)));
3668  if (saw_deferrability)
3669  ereport(ERROR,
3670  (errcode(ERRCODE_SYNTAX_ERROR),
3671  errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed"),
3672  parser_errposition(cxt->pstate, con->location)));
3673  saw_deferrability = true;
3674  lastprimarycon->deferrable = true;
3675  break;
3676 
3678  if (!SUPPORTS_ATTRS(lastprimarycon))
3679  ereport(ERROR,
3680  (errcode(ERRCODE_SYNTAX_ERROR),
3681  errmsg("misplaced NOT DEFERRABLE clause"),
3682  parser_errposition(cxt->pstate, con->location)));
3683  if (saw_deferrability)
3684  ereport(ERROR,
3685  (errcode(ERRCODE_SYNTAX_ERROR),
3686  errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed"),
3687  parser_errposition(cxt->pstate, con->location)));
3688  saw_deferrability = true;
3689  lastprimarycon->deferrable = false;
3690  if (saw_initially &&
3691  lastprimarycon->initdeferred)
3692  ereport(ERROR,
3693  (errcode(ERRCODE_SYNTAX_ERROR),
3694  errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"),
3695  parser_errposition(cxt->pstate, con->location)));
3696  break;
3697 
3698  case CONSTR_ATTR_DEFERRED:
3699  if (!SUPPORTS_ATTRS(lastprimarycon))
3700  ereport(ERROR,
3701  (errcode(ERRCODE_SYNTAX_ERROR),
3702  errmsg("misplaced INITIALLY DEFERRED clause"),
3703  parser_errposition(cxt->pstate, con->location)));
3704  if (saw_initially)
3705  ereport(ERROR,
3706  (errcode(ERRCODE_SYNTAX_ERROR),
3707  errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed"),
3708  parser_errposition(cxt->pstate, con->location)));
3709  saw_initially = true;
3710  lastprimarycon->initdeferred = true;
3711 
3712  /*
3713  * If only INITIALLY DEFERRED appears, assume DEFERRABLE
3714  */
3715  if (!saw_deferrability)
3716  lastprimarycon->deferrable = true;
3717  else if (!lastprimarycon->deferrable)
3718  ereport(ERROR,
3719  (errcode(ERRCODE_SYNTAX_ERROR),
3720  errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"),
3721  parser_errposition(cxt->pstate, con->location)));
3722  break;
3723 
3724  case CONSTR_ATTR_IMMEDIATE:
3725  if (!SUPPORTS_ATTRS(lastprimarycon))
3726  ereport(ERROR,
3727  (errcode(ERRCODE_SYNTAX_ERROR),
3728  errmsg("misplaced INITIALLY IMMEDIATE clause"),
3729  parser_errposition(cxt->pstate, con->location)));
3730  if (saw_initially)
3731  ereport(ERROR,
3732  (errcode(ERRCODE_SYNTAX_ERROR),
3733  errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed"),
3734  parser_errposition(cxt->pstate, con->location)));
3735  saw_initially = true;
3736  lastprimarycon->initdeferred = false;
3737  break;
3738 
3739  default:
3740  /* Otherwise it's not an attribute */
3741  lastprimarycon = con;
3742  /* reset flags for new primary node */
3743  saw_deferrability = false;
3744  saw_initially = false;
3745  break;
3746  }
3747  }
3748 }
#define SUPPORTS_ATTRS(node)
bool initdeferred
Definition: parsenodes.h:2562
bool deferrable
Definition: parsenodes.h:2561

References CONSTR_ATTR_DEFERRABLE, CONSTR_ATTR_DEFERRED, CONSTR_ATTR_IMMEDIATE, CONSTR_ATTR_NOT_DEFERRABLE, Constraint::contype, Constraint::deferrable, elog(), ereport, errcode(), errmsg(), ERROR, Constraint::initdeferred, IsA, lfirst, Constraint::location, nodeTag, parser_errposition(), CreateStmtContext::pstate, and SUPPORTS_ATTRS.

Referenced by transformColumnDefinition().

◆ transformCreateSchemaStmt()

List* transformCreateSchemaStmt ( CreateSchemaStmt stmt)

Definition at line 3807 of file parse_utilcmd.c.

3808 {
3810  List *result;
3811  ListCell *elements;
3812 
3813  cxt.stmtType = "CREATE SCHEMA";
3814  cxt.schemaname = stmt->schemaname;
3815  cxt.authrole = (RoleSpec *) stmt->authrole;
3816  cxt.sequences = NIL;
3817  cxt.tables = NIL;
3818  cxt.views = NIL;
3819  cxt.indexes = NIL;
3820  cxt.triggers = NIL;
3821  cxt.grants = NIL;
3822 
3823  /*
3824  * Run through each schema element in the schema element list. Separate
3825  * statements by type, and do preliminary analysis.
3826  */
3827  foreach(elements, stmt->schemaElts)
3828  {
3829  Node *element = lfirst(elements);
3830 
3831  switch (nodeTag(element))
3832  {
3833  case T_CreateSeqStmt:
3834  {
3835  CreateSeqStmt *elp = (CreateSeqStmt *) element;
3836 
3838  cxt.sequences = lappend(cxt.sequences, element);
3839  }
3840  break;
3841 
3842  case T_CreateStmt:
3843  {
3844  CreateStmt *elp = (CreateStmt *) element;
3845 
3847 
3848  /*
3849  * XXX todo: deal with constraints
3850  */
3851  cxt.tables = lappend(cxt.tables, element);
3852  }
3853  break;
3854 
3855  case T_ViewStmt:
3856  {
3857  ViewStmt *elp = (ViewStmt *) element;
3858 
3859  setSchemaName(cxt.schemaname, &elp->view->schemaname);
3860 
3861  /*
3862  * XXX todo: deal with references between views
3863  */
3864  cxt.views = lappend(cxt.views, element);
3865  }
3866  break;
3867 
3868  case T_IndexStmt:
3869  {
3870  IndexStmt *elp = (IndexStmt *) element;
3871 
3873  cxt.indexes = lappend(cxt.indexes, element);
3874  }
3875  break;
3876 
3877  case T_CreateTrigStmt:
3878  {
3880 
3882  cxt.triggers = lappend(cxt.triggers, element);
3883  }
3884  break;
3885 
3886  case T_GrantStmt:
3887  cxt.grants = lappend(cxt.grants, element);
3888  break;
3889 
3890  default:
3891  elog(ERROR, "unrecognized node type: %d",
3892  (int) nodeTag(element));
3893  }
3894  }
3895 
3896  result = NIL;
3897  result = list_concat(result, cxt.sequences);
3898  result = list_concat(result, cxt.tables);
3899  result = list_concat(result, cxt.views);
3900  result = list_concat(result, cxt.indexes);
3901  result = list_concat(result, cxt.triggers);
3902  result = list_concat(result, cxt.grants);
3903 
3904  return result;
3905 }
static void setSchemaName(char *context_schema, char **stmt_schema_name)
static chr element(struct vars *v, const chr *startp, const chr *endp)
Definition: regc_locale.c:376
RangeVar * relation
Definition: parsenodes.h:2475
RangeVar * relation
Definition: parsenodes.h:2840
RangeVar * relation
Definition: parsenodes.h:3185
RangeVar * view
Definition: parsenodes.h:3571

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

Referenced by CreateSchemaCommand().

◆ transformCreateStmt()

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

Definition at line 165 of file parse_utilcmd.c.

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

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

Referenced by ProcessUtilitySlow().

◆ transformExtendedStatistics()

static void transformExtendedStatistics ( CreateStmtContext cxt)
static

Definition at line 2688 of file parse_utilcmd.c.

2689 {
2690  cxt->alist = list_concat(cxt->alist, cxt->extstats);
2691 }

References CreateStmtContext::alist, CreateStmtContext::extstats, and list_concat().

Referenced by transformAlterTableStmt(), and transformCreateStmt().

◆ transformFKConstraints()

static void transformFKConstraints ( CreateStmtContext cxt,
bool  skipValidation,
bool  isAddConstraint 
)
static

Definition at line 2732 of file parse_utilcmd.c.

2734 {
2735  ListCell *fkclist;
2736 
2737  if (cxt->fkconstraints == NIL)
2738  return;
2739 
2740  /*
2741  * If CREATE TABLE or adding a column with NULL default, we can safely
2742  * skip validation of FK constraints, and nonetheless mark them valid.
2743  * (This will override any user-supplied NOT VALID flag.)
2744  */
2745  if (skipValidation)
2746  {
2747  foreach(fkclist, cxt->fkconstraints)
2748  {
2749  Constraint *constraint = (Constraint *) lfirst(fkclist);
2750 
2751  constraint->skip_validation = true;
2752  constraint->initially_valid = true;
2753  }
2754  }
2755 
2756  /*
2757  * For CREATE TABLE or ALTER TABLE ADD COLUMN, gin up an ALTER TABLE ADD
2758  * CONSTRAINT command to execute after the basic command is complete. (If
2759  * called from ADD CONSTRAINT, that routine will add the FK constraints to
2760  * its own subcommand list.)
2761  *
2762  * Note: the ADD CONSTRAINT command must also execute after any index
2763  * creation commands. Thus, this should run after
2764  * transformIndexConstraints, so that the CREATE INDEX commands are
2765  * already in cxt->alist. See also the handling of cxt->likeclauses.
2766  */
2767  if (!isAddConstraint)
2768  {
2769  AlterTableStmt *alterstmt = makeNode(AlterTableStmt);
2770 
2771  alterstmt->relation = cxt->relation;
2772  alterstmt->cmds = NIL;
2773  alterstmt->objtype = OBJECT_TABLE;
2774 
2775  foreach(fkclist, cxt->fkconstraints)
2776  {
2777  Constraint *constraint = (Constraint *) lfirst(fkclist);
2778  AlterTableCmd *altercmd = makeNode(AlterTableCmd);
2779 
2780  altercmd->subtype = AT_AddConstraint;
2781  altercmd->name = NULL;
2782  altercmd->def = (Node *) constraint;
2783  alterstmt->cmds = lappend(alterstmt->cmds, altercmd);
2784  }
2785 
2786  cxt->alist = lappend(cxt->alist, alterstmt);
2787  }
2788 }

References CreateStmtContext::alist, AT_AddConstraint, AlterTableStmt::cmds, AlterTableCmd::def, CreateStmtContext::fkconstraints, Constraint::initially_valid, lappend(), lfirst, makeNode, AlterTableCmd::name, NIL, OBJECT_TABLE, AlterTableStmt::objtype, CreateStmtContext::relation, AlterTableStmt::relation, Constraint::skip_validation, and AlterTableCmd::subtype.

Referenced by transformAlterTableStmt(), and transformCreateStmt().

◆ transformIndexConstraint()

static IndexStmt * transformIndexConstraint ( Constraint constraint,
CreateStmtContext cxt 
)
static

Definition at line 2154 of file parse_utilcmd.c.

2155 {
2156  IndexStmt *index;
2157  List *notnullcmds = NIL;
2158  ListCell *lc;
2159 
2161 
2162  index->unique = (constraint->contype != CONSTR_EXCLUSION);
2163  index->primary = (constraint->contype == CONSTR_PRIMARY);
2164  if (index->primary)
2165  {
2166  if (cxt->pkey != NULL)
2167  ereport(ERROR,
2168  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
2169  errmsg("multiple primary keys for table \"%s\" are not allowed",
2170  cxt->relation->relname),
2171  parser_errposition(cxt->pstate, constraint->location)));
2172  cxt->pkey = index;
2173 
2174  /*
2175  * In ALTER TABLE case, a primary index might already exist, but
2176  * DefineIndex will check for it.
2177  */
2178  }
2179  index->nulls_not_distinct = constraint->nulls_not_distinct;
2180  index->isconstraint = true;
2181  index->deferrable = constraint->deferrable;
2182  index->initdeferred = constraint->initdeferred;
2183 
2184  if (constraint->conname != NULL)
2185  index->idxname = pstrdup(constraint->conname);
2186  else
2187  index->idxname = NULL; /* DefineIndex will choose name */
2188 
2189  index->relation = cxt->relation;
2190  index->accessMethod = constraint->access_method ? constraint->access_method : DEFAULT_INDEX_TYPE;
2191  index->options = constraint->options;
2192  index->tableSpace = constraint->indexspace;
2193  index->whereClause = constraint->where_clause;
2194  index->indexParams = NIL;
2195  index->indexIncludingParams = NIL;
2196  index->excludeOpNames = NIL;
2197  index->idxcomment = NULL;
2198  index->indexOid = InvalidOid;
2199  index->oldNumber = InvalidRelFileNumber;
2200  index->oldCreateSubid = InvalidSubTransactionId;
2201  index->oldFirstRelfilelocatorSubid = InvalidSubTransactionId;
2202  index->transformed = false;
2203  index->concurrent = false;
2204  index->if_not_exists = false;
2205  index->reset_default_tblspc = constraint->reset_default_tblspc;
2206 
2207  /*
2208  * If it's ALTER TABLE ADD CONSTRAINT USING INDEX, look up the index and
2209  * verify it's usable, then extract the implied column name list. (We
2210  * will not actually need the column name list at runtime, but we need it
2211  * now to check for duplicate column entries below.)
2212  */
2213  if (constraint->indexname != NULL)
2214  {
2215  char *index_name = constraint->indexname;
2216  Relation heap_rel = cxt->rel;
2217  Oid index_oid;
2218  Relation index_rel;
2219  Form_pg_index index_form;
2220  oidvector *indclass;
2221  Datum indclassDatum;
2222  int i;
2223 
2224  /* Grammar should not allow this with explicit column list */
2225  Assert(constraint->keys == NIL);
2226 
2227  /* Grammar should only allow PRIMARY and UNIQUE constraints */
2228  Assert(constraint->contype == CONSTR_PRIMARY ||
2229  constraint->contype == CONSTR_UNIQUE);
2230 
2231  /* Must be ALTER, not CREATE, but grammar doesn't enforce that */
2232  if (!cxt->isalter)
2233  ereport(ERROR,
2234  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2235  errmsg("cannot use an existing index in CREATE TABLE"),
2236  parser_errposition(cxt->pstate, constraint->location)));
2237 
2238  /* Look for the index in the same schema as the table */
2239  index_oid = get_relname_relid(index_name, RelationGetNamespace(heap_rel));
2240 
2241  if (!OidIsValid(index_oid))
2242  ereport(ERROR,
2243  (errcode(ERRCODE_UNDEFINED_OBJECT),
2244  errmsg("index \"%s\" does not exist", index_name),
2245  parser_errposition(cxt->pstate, constraint->location)));
2246 
2247  /* Open the index (this will throw an error if it is not an index) */
2248  index_rel = index_open(index_oid, AccessShareLock);
2249  index_form = index_rel->rd_index;
2250 
2251  /* Check that it does not have an associated constraint already */
2252  if (OidIsValid(get_index_constraint(index_oid)))
2253  ereport(ERROR,
2254  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2255  errmsg("index \"%s\" is already associated with a constraint",
2256  index_name),
2257  parser_errposition(cxt->pstate, constraint->location)));
2258 
2259  /* Perform validity checks on the index */
2260  if (index_form->indrelid != RelationGetRelid(heap_rel))
2261  ereport(ERROR,
2262  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2263  errmsg("index \"%s\" does not belong to table \"%s\"",
2264  index_name, RelationGetRelationName(heap_rel)),
2265  parser_errposition(cxt->pstate, constraint->location)));
2266 
2267  if (!index_form->indisvalid)
2268  ereport(ERROR,
2269  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2270  errmsg("index \"%s\" is not valid", index_name),
2271  parser_errposition(cxt->pstate, constraint->location)));
2272 
2273  if (!index_form->indisunique)
2274  ereport(ERROR,
2275  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2276  errmsg("\"%s\" is not a unique index", index_name),
2277  errdetail("Cannot create a primary key or unique constraint using such an index."),
2278  parser_errposition(cxt->pstate, constraint->location)));
2279 
2280  if (RelationGetIndexExpressions(index_rel) != NIL)
2281  ereport(ERROR,
2282  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2283  errmsg("index \"%s\" contains expressions", index_name),
2284  errdetail("Cannot create a primary key or unique constraint using such an index."),
2285  parser_errposition(cxt->pstate, constraint->location)));
2286 
2287  if (RelationGetIndexPredicate(index_rel) != NIL)
2288  ereport(ERROR,
2289  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2290  errmsg("\"%s\" is a partial index", index_name),
2291  errdetail("Cannot create a primary key or unique constraint using such an index."),
2292  parser_errposition(cxt->pstate, constraint->location)));
2293 
2294  /*
2295  * It's probably unsafe to change a deferred index to non-deferred. (A
2296  * non-constraint index couldn't be deferred anyway, so this case
2297  * should never occur; no need to sweat, but let's check it.)
2298  */
2299  if (!index_form->indimmediate && !constraint->deferrable)
2300  ereport(ERROR,
2301  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2302  errmsg("\"%s\" is a deferrable index", index_name),
2303  errdetail("Cannot create a non-deferrable constraint using a deferrable index."),
2304  parser_errposition(cxt->pstate, constraint->location)));
2305 
2306  /*
2307  * Insist on it being a btree. That's the only kind that supports
2308  * uniqueness at the moment anyway; but we must have an index that
2309  * exactly matches what you'd get from plain ADD CONSTRAINT syntax,
2310  * else dump and reload will produce a different index (breaking
2311  * pg_upgrade in particular).
2312  */
2313  if (index_rel->rd_rel->relam != get_index_am_oid(DEFAULT_INDEX_TYPE, false))
2314  ereport(ERROR,
2315  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2316  errmsg("index \"%s\" is not a btree", index_name),
2317  parser_errposition(cxt->pstate, constraint->location)));
2318 
2319  /* Must get indclass the hard way */
2320  indclassDatum = SysCacheGetAttrNotNull(INDEXRELID,
2321  index_rel->rd_indextuple,
2322  Anum_pg_index_indclass);
2323  indclass = (oidvector *) DatumGetPointer(indclassDatum);
2324 
2325  for (i = 0; i < index_form->indnatts; i++)
2326  {
2327  int16 attnum = index_form->indkey.values[i];
2328  const FormData_pg_attribute *attform;
2329  char *attname;
2330  Oid defopclass;
2331 
2332  /*
2333  * We shouldn't see attnum == 0 here, since we already rejected
2334  * expression indexes. If we do, SystemAttributeDefinition will
2335  * throw an error.
2336  */
2337  if (attnum > 0)
2338  {
2339  Assert(attnum <= heap_rel->rd_att->natts);
2340  attform = TupleDescAttr(heap_rel->rd_att, attnum - 1);
2341  }
2342  else
2343  attform = SystemAttributeDefinition(attnum);
2344  attname = pstrdup(NameStr(attform->attname));
2345 
2346  if (i < index_form->indnkeyatts)
2347  {
2348  /*
2349  * Insist on default opclass, collation, and sort options.
2350  * While the index would still work as a constraint with
2351  * non-default settings, it might not provide exactly the same
2352  * uniqueness semantics as you'd get from a normally-created
2353  * constraint; and there's also the dump/reload problem
2354  * mentioned above.
2355  */
2356  Datum attoptions =
2357  get_attoptions(RelationGetRelid(index_rel), i + 1);
2358 
2359  defopclass = GetDefaultOpClass(attform->atttypid,
2360  index_rel->rd_rel->relam);
2361  if (indclass->values[i] != defopclass ||
2362  attform->attcollation != index_rel->rd_indcollation[i] ||
2363  attoptions != (Datum) 0 ||
2364  index_rel->rd_indoption[i] != 0)
2365  ereport(ERROR,
2366  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2367  errmsg("index \"%s\" column number %d does not have default sorting behavior", index_name, i + 1),
2368  errdetail("Cannot create a primary key or unique constraint using such an index."),
2369  parser_errposition(cxt->pstate, constraint->location)));
2370 
2371  constraint->keys = lappend(constraint->keys, makeString(attname));
2372  }
2373  else
2374  constraint->including = lappend(constraint->including, makeString(attname));
2375  }
2376 
2377  /* Close the index relation but keep the lock */
2378  relation_close(index_rel, NoLock);
2379 
2380  index->indexOid = index_oid;
2381  }
2382 
2383  /*
2384  * If it's an EXCLUDE constraint, the grammar returns a list of pairs of
2385  * IndexElems and operator names. We have to break that apart into
2386  * separate lists.
2387  */
2388  if (constraint->contype == CONSTR_EXCLUSION)
2389  {
2390  foreach(lc, constraint->exclusions)
2391  {
2392  List *pair = (List *) lfirst(lc);
2393  IndexElem *elem;
2394  List *opname;
2395 
2396  Assert(list_length(pair) == 2);
2397  elem = linitial_node(IndexElem, pair);
2398  opname = lsecond_node(List, pair);
2399 
2400  index->indexParams = lappend(index->indexParams, elem);
2401  index->excludeOpNames = lappend(index->excludeOpNames, opname);
2402  }
2403  }
2404 
2405  /*
2406  * For UNIQUE and PRIMARY KEY, we just have a list of column names.
2407  *
2408  * Make sure referenced keys exist. If we are making a PRIMARY KEY index,
2409  * also make sure they are NOT NULL.
2410  */
2411  else
2412  {
2413  foreach(lc, constraint->keys)
2414  {
2415  char *key = strVal(lfirst(lc));
2416  bool found = false;
2417  bool forced_not_null = false;
2418  ColumnDef *column = NULL;
2419  ListCell *columns;
2420  IndexElem *iparam;
2421 
2422  /* Make sure referenced column exists. */
2423  foreach(columns, cxt->columns)
2424  {
2425  column = lfirst_node(ColumnDef, columns);
2426  if (strcmp(column->colname, key) == 0)
2427  {
2428  found = true;
2429  break;
2430  }
2431  }
2432  if (found)
2433  {
2434  /*
2435  * column is defined in the new table. For PRIMARY KEY, we
2436  * can apply the NOT NULL constraint cheaply here ... unless
2437  * the column is marked is_from_type, in which case marking it
2438  * here would be ineffective (see MergeAttributes).
2439  */
2440  if (constraint->contype == CONSTR_PRIMARY &&
2441  !column->is_from_type)
2442  {
2443  column->is_not_null = true;
2444  forced_not_null = true;
2445  }
2446  }
2447  else if (SystemAttributeByName(key) != NULL)
2448  {
2449  /*
2450  * column will be a system column in the new table, so accept
2451  * it. System columns can't ever be null, so no need to worry
2452  * about PRIMARY/NOT NULL constraint.
2453  */
2454  found = true;
2455  }
2456  else if (cxt->inhRelations)
2457  {
2458  /* try inherited tables */
2459  ListCell *inher;
2460 
2461  foreach(inher, cxt->inhRelations)
2462  {
2463  RangeVar *inh = lfirst_node(RangeVar, inher);
2464  Relation rel;
2465  int count;
2466 
2467  rel = table_openrv(inh, AccessShareLock);
2468  /* check user requested inheritance from valid relkind */
2469  if (rel->rd_rel->relkind != RELKIND_RELATION &&
2470  rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2471  rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2472  ereport(ERROR,
2473  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2474  errmsg("inherited relation \"%s\" is not a table or foreign table",
2475  inh->relname)));
2476  for (count = 0; count < rel->rd_att->natts; count++)
2477  {
2478  Form_pg_attribute inhattr = TupleDescAttr(rel->rd_att,
2479  count);
2480  char *inhname = NameStr(inhattr->attname);
2481 
2482  if (inhattr->attisdropped)
2483  continue;
2484  if (strcmp(key, inhname) == 0)
2485  {
2486  found = true;
2487 
2488  /*
2489  * It's tempting to set forced_not_null if the
2490  * parent column is already NOT NULL, but that
2491  * seems unsafe because the column's NOT NULL
2492  * marking might disappear between now and
2493  * execution. Do the runtime check to be safe.
2494  */
2495  break;
2496  }
2497  }
2498  table_close(rel, NoLock);
2499  if (found)
2500  break;
2501  }
2502  }
2503 
2504  /*
2505  * In the ALTER TABLE case, don't complain about index keys not
2506  * created in the command; they may well exist already.
2507  * DefineIndex will complain about them if not.
2508  */
2509  if (!found && !cxt->isalter)
2510  ereport(ERROR,
2511  (errcode(ERRCODE_UNDEFINED_COLUMN),
2512  errmsg("column \"%s\" named in key does not exist", key),
2513  parser_errposition(cxt->pstate, constraint->location)));
2514 
2515  /* Check for PRIMARY KEY(foo, foo) */
2516  foreach(columns, index->indexParams)
2517  {
2518  iparam = (IndexElem *) lfirst(columns);
2519  if (iparam->name && strcmp(key, iparam->name) == 0)
2520  {
2521  if (index->primary)
2522  ereport(ERROR,
2523  (errcode(ERRCODE_DUPLICATE_COLUMN),
2524  errmsg("column \"%s\" appears twice in primary key constraint",
2525  key),
2526  parser_errposition(cxt->pstate, constraint->location)));
2527  else
2528  ereport(ERROR,
2529  (errcode(ERRCODE_DUPLICATE_COLUMN),
2530  errmsg("column \"%s\" appears twice in unique constraint",
2531  key),
2532  parser_errposition(cxt->pstate, constraint->location)));
2533  }
2534  }
2535 
2536  /* OK, add it to the index definition */
2537  iparam = makeNode(IndexElem);
2538  iparam->name = pstrdup(key);
2539  iparam->expr = NULL;
2540  iparam->indexcolname = NULL;
2541  iparam->collation = NIL;
2542  iparam->opclass = NIL;
2543  iparam->opclassopts = NIL;
2544  iparam->ordering = SORTBY_DEFAULT;
2546  index->indexParams = lappend(index->indexParams, iparam);
2547 
2548  /*
2549  * For a primary-key column, also create an item for ALTER TABLE
2550  * SET NOT NULL if we couldn't ensure it via is_not_null above.
2551  */
2552  if (constraint->contype == CONSTR_PRIMARY && !forced_not_null)
2553  {
2554  AlterTableCmd *notnullcmd = makeNode(AlterTableCmd);
2555 
2556  notnullcmd->subtype = AT_SetNotNull;
2557  notnullcmd->name = pstrdup(key);
2558  notnullcmds = lappend(notnullcmds, notnullcmd);
2559  }
2560  }
2561  }
2562 
2563  /*
2564  * Add included columns to index definition. This is much like the
2565  * simple-column-name-list code above, except that we don't worry about
2566  * NOT NULL marking; included columns in a primary key should not be
2567  * forced NOT NULL. We don't complain about duplicate columns, either,
2568  * though maybe we should?
2569  */
2570  foreach(lc, constraint->including)
2571  {
2572  char *key = strVal(lfirst(lc));
2573  bool found = false;
2574  ColumnDef *column = NULL;
2575  ListCell *columns;
2576  IndexElem *iparam;
2577 
2578  foreach(columns, cxt->columns)
2579  {
2580  column = lfirst_node(ColumnDef, columns);
2581  if (strcmp(column->colname, key) == 0)
2582  {
2583  found = true;
2584  break;
2585  }
2586  }
2587 
2588  if (!found)
2589  {
2590  if (SystemAttributeByName(key) != NULL)
2591  {
2592  /*
2593  * column will be a system column in the new table, so accept
2594  * it.
2595  */
2596  found = true;
2597  }
2598  else if (cxt->inhRelations)
2599  {
2600  /* try inherited tables */
2601  ListCell *inher;
2602 
2603  foreach(inher, cxt->inhRelations)
2604  {
2605  RangeVar *inh = lfirst_node(RangeVar, inher);
2606  Relation rel;
2607  int count;
2608 
2609  rel = table_openrv(inh, AccessShareLock);
2610  /* check user requested inheritance from valid relkind */
2611  if (rel->rd_rel->relkind != RELKIND_RELATION &&
2612  rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2613  rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2614  ereport(ERROR,
2615  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2616  errmsg("inherited relation \"%s\" is not a table or foreign table",
2617  inh->relname)));
2618  for (count = 0; count < rel->rd_att->natts; count++)
2619  {
2620  Form_pg_attribute inhattr = TupleDescAttr(rel->rd_att,
2621  count);
2622  char *inhname = NameStr(inhattr->attname);
2623 
2624  if (inhattr->attisdropped)
2625  continue;
2626  if (strcmp(key, inhname) == 0)
2627  {
2628  found = true;
2629  break;
2630  }
2631  }
2632  table_close(rel, NoLock);
2633  if (found)
2634  break;
2635  }
2636  }
2637  }
2638 
2639  /*
2640  * In the ALTER TABLE case, don't complain about index keys not
2641  * created in the command; they may well exist already. DefineIndex
2642  * will complain about them if not.
2643  */
2644  if (!found && !cxt->isalter)
2645  ereport(ERROR,
2646  (errcode(ERRCODE_UNDEFINED_COLUMN),
2647  errmsg("column \"%s\" named in key does not exist", key),
2648  parser_errposition(cxt->pstate, constraint->location)));
2649 
2650  /* OK, add it to the index definition */
2651  iparam = makeNode(IndexElem);
2652  iparam->name = pstrdup(key);
2653  iparam->expr = NULL;
2654  iparam->indexcolname = NULL;
2655  iparam->collation = NIL;
2656  iparam->opclass = NIL;
2657  iparam->opclassopts = NIL;
2658  index->indexIncludingParams = lappend(index->indexIncludingParams, iparam);
2659  }
2660 
2661  /*
2662  * If we found anything that requires run-time SET NOT NULL, build a full
2663  * ALTER TABLE command for that and add it to cxt->alist.
2664  */
2665  if (notnullcmds)
2666  {
2667  AlterTableStmt *alterstmt = makeNode(AlterTableStmt);
2668 
2669  alterstmt->relation = copyObject(cxt->relation);
2670  alterstmt->cmds = notnullcmds;
2671  alterstmt->objtype = OBJECT_TABLE;
2672  alterstmt->missing_ok = false;
2673 
2674  cxt->alist = lappend(cxt->alist, alterstmt);
2675  }
2676 
2677  return index;
2678 }
Oid get_index_am_oid(const char *amname, bool missing_ok)
Definition: amcmds.c:163
const FormData_pg_attribute * SystemAttributeDefinition(AttrNumber attno)
Definition: heap.c:239
const FormData_pg_attribute * SystemAttributeByName(const char *attname)
Definition: heap.c:251
#define DEFAULT_INDEX_TYPE
Definition: index.h:21
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition: lsyscache.c:1867
@ AT_SetNotNull
Definition: parsenodes.h:2179
FormData_pg_attribute
Definition: pg_attribute.h:193
#define linitial_node(type, l)
Definition: pg_list.h:181
#define lsecond_node(type, l)
Definition: pg_list.h:186
List * RelationGetIndexPredicate(Relation relation)
Definition: relcache.c:5086
List * RelationGetIndexExpressions(Relation relation)
Definition: relcache.c:4973
bool is_from_type
Definition: parsenodes.h:727
List * exclusions
Definition: parsenodes.h:2579
bool reset_default_tblspc
Definition: parsenodes.h:2585
Node * where_clause
Definition: parsenodes.h:2589
char * indexname
Definition: parsenodes.h:2583
char * indexspace
Definition: parsenodes.h:2584
char * access_method
Definition: parsenodes.h:2588
bool nulls_not_distinct
Definition: parsenodes.h:2572
List * including
Definition: parsenodes.h:2575
TupleDesc rd_att
Definition: rel.h:111
Form_pg_index rd_index
Definition: rel.h:190
Oid * rd_indcollation
Definition: rel.h:215
Relation table_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition: table.c:83

References Constraint::access_method, AccessShareLock, CreateStmtContext::alist, Assert(), AT_SetNotNull, attname, attnum, AlterTableStmt::cmds, IndexElem::collation, ColumnDef::colname, CreateStmtContext::columns, Constraint::conname, CONSTR_EXCLUSION, CONSTR_PRIMARY, CONSTR_UNIQUE, Constraint::contype, copyObject, DatumGetPointer(), DEFAULT_INDEX_TYPE, Constraint::deferrable, ereport, errcode(), errdetail(), errmsg(), ERROR, Constraint::exclusions, IndexElem::expr, FormData_pg_attribute, get_attoptions(), get_index_am_oid(), get_index_constraint(), get_relname_relid(), GetDefaultOpClass(), i, Constraint::including, index_open(), IndexElem::indexcolname, Constraint::indexname, INDEXRELID, Constraint::indexspace, CreateStmtContext::inhRelations, Constraint::initdeferred, InvalidOid, InvalidRelFileNumber, InvalidSubTransactionId, ColumnDef::is_from_type, ColumnDef::is_not_null, CreateStmtContext::isalter, sort-test::key, Constraint::keys, lappend(), lfirst, lfirst_node, linitial_node, list_length(), Constraint::location, lsecond_node, makeNode, makeString(), AlterTableStmt::missing_ok, IndexElem::name, AlterTableCmd::name, NameStr, TupleDescData::natts, NIL, NoLock, Constraint::nulls_not_distinct, IndexElem::nulls_ordering, OBJECT_TABLE, AlterTableStmt::objtype, OidIsValid, IndexElem::opclass, IndexElem::opclassopts, Constraint::options, IndexElem::ordering, parser_errposition(), CreateStmtContext::pkey, CreateStmtContext::pstate, pstrdup(), RelationData::rd_att, RelationData::rd_indcollation, RelationData::rd_index, RelationData::rd_indextuple, RelationData::rd_indoption, RelationData::rd_rel, CreateStmtContext::rel, CreateStmtContext::relation, AlterTableStmt::relation, relation_close(), RelationGetIndexExpressions(), RelationGetIndexPredicate(), RelationGetNamespace, RelationGetRelationName, RelationGetRelid, RangeVar::relname, Constraint::reset_default_tblspc, SORTBY_DEFAULT, SORTBY_NULLS_DEFAULT, strVal, AlterTableCmd::subtype, SysCacheGetAttrNotNull(), SystemAttributeByName(), SystemAttributeDefinition(), table_close(), table_openrv(), TupleDescAttr, oidvector::values, and Constraint::where_clause.

Referenced by transformIndexConstraints().

◆ transformIndexConstraints()

static void transformIndexConstraints ( CreateStmtContext cxt)
static

Definition at line 2051 of file parse_utilcmd.c.

2052 {
2053  IndexStmt *index;
2054  List *indexlist = NIL;
2055  List *finalindexlist = NIL;
2056  ListCell *lc;
2057 
2058  /*
2059  * Run through the constraints that need to generate an index. For PRIMARY
2060  * KEY, mark each column as NOT NULL and create an index. For UNIQUE or
2061  * EXCLUDE, create an index as for PRIMARY KEY, but do not insist on NOT
2062  * NULL.
2063  */
2064  foreach(lc, cxt->ixconstraints)
2065  {
2066  Constraint *constraint = lfirst_node(Constraint, lc);
2067 
2068  Assert(constraint->contype == CONSTR_PRIMARY ||
2069  constraint->contype == CONSTR_UNIQUE ||
2070  constraint->contype == CONSTR_EXCLUSION);
2071 
2072  index = transformIndexConstraint(constraint, cxt);
2073 
2074  indexlist = lappend(indexlist, index);
2075  }
2076 
2077  /*
2078  * Scan the index list and remove any redundant index specifications. This
2079  * can happen if, for instance, the user writes UNIQUE PRIMARY KEY. A
2080  * strict reading of SQL would suggest raising an error instead, but that
2081  * strikes me as too anal-retentive. - tgl 2001-02-14
2082  *
2083  * XXX in ALTER TABLE case, it'd be nice to look for duplicate
2084  * pre-existing indexes, too.
2085  */
2086  if (cxt->pkey != NULL)
2087  {
2088  /* Make sure we keep the PKEY index in preference to others... */
2089  finalindexlist = list_make1(cxt->pkey);
2090  }
2091 
2092  foreach(lc, indexlist)
2093  {
2094  bool keep = true;
2095  ListCell *k;
2096 
2097  index = lfirst(lc);
2098 
2099  /* if it's pkey, it's already in finalindexlist */
2100  if (index == cxt->pkey)
2101  continue;
2102 
2103  foreach(k, finalindexlist)
2104  {
2105  IndexStmt *priorindex = lfirst(k);
2106 
2107  if (equal(index->indexParams, priorindex->indexParams) &&
2108  equal(index->indexIncludingParams, priorindex->indexIncludingParams) &&
2109  equal(index->whereClause, priorindex->whereClause) &&
2110  equal(index->excludeOpNames, priorindex->excludeOpNames) &&
2111  strcmp(index->accessMethod, priorindex->accessMethod) == 0 &&
2112  index->nulls_not_distinct == priorindex->nulls_not_distinct &&
2113  index->deferrable == priorindex->deferrable &&
2114  index->initdeferred == priorindex->initdeferred)
2115  {
2116  priorindex->unique |= index->unique;
2117 
2118  /*
2119  * If the prior index is as yet unnamed, and this one is
2120  * named, then transfer the name to the prior index. This
2121  * ensures that if we have named and unnamed constraints,
2122  * we'll use (at least one of) the names for the index.
2123  */
2124  if (priorindex->idxname == NULL)
2125  priorindex->idxname = index->idxname;
2126  keep = false;
2127  break;
2128  }
2129  }
2130 
2131  if (keep)
2132  finalindexlist = lappend(finalindexlist, index);
2133  }
2134 
2135  /*
2136  * Now append all the IndexStmts to cxt->alist. If we generated an ALTER
2137  * TABLE SET NOT NULL statement to support a primary key, it's already in
2138  * cxt->alist.
2139  */
2140  cxt->alist = list_concat(cxt->alist, finalindexlist);
2141 }
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
static IndexStmt * transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
bool unique
Definition: parsenodes.h:3200
bool deferrable
Definition: parsenodes.h:3204
List * indexParams
Definition: parsenodes.h:3188
bool initdeferred
Definition: parsenodes.h:3205
List * excludeOpNames
Definition: parsenodes.h:3193
bool nulls_not_distinct
Definition: parsenodes.h:3201
char * idxname
Definition: parsenodes.h:3184
Node * whereClause
Definition: parsenodes.h:3192
char * accessMethod
Definition: parsenodes.h:3186
List * indexIncludingParams
Definition: parsenodes.h:3189

References IndexStmt::accessMethod, CreateStmtContext::alist, Assert(), CONSTR_EXCLUSION, CONSTR_PRIMARY, CONSTR_UNIQUE, Constraint::contype, IndexStmt::deferrable, equal(), IndexStmt::excludeOpNames, IndexStmt::idxname, IndexStmt::indexIncludingParams, IndexStmt::indexParams, IndexStmt::initdeferred, CreateStmtContext::ixconstraints, lappend(), lfirst, lfirst_node, list_concat(), list_make1, NIL, IndexStmt::nulls_not_distinct, CreateStmtContext::pkey, transformIndexConstraint(), IndexStmt::unique, and IndexStmt::whereClause.

Referenced by transformAlterTableStmt(), and transformCreateStmt().

◆ transformIndexStmt()

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

Definition at line 2803 of file parse_utilcmd.c.

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

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

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

◆ transformOfType()

static void transformOfType ( CreateStmtContext cxt,
TypeName ofTypename 
)
static

Definition at line 1455 of file parse_utilcmd.c.

1456 {
1457  HeapTuple tuple;
1458  TupleDesc tupdesc;
1459  int i;
1460  Oid ofTypeId;
1461 
1462  Assert(ofTypename);
1463 
1464  tuple = typenameType(NULL, ofTypename, NULL);
1465  check_of_type(tuple);
1466  ofTypeId = ((Form_pg_type) GETSTRUCT(tuple))->oid;
1467  ofTypename->typeOid = ofTypeId; /* cached for later */
1468 
1469  tupdesc = lookup_rowtype_tupdesc(ofTypeId, -1);
1470  for (i = 0; i < tupdesc->natts; i++)
1471  {
1472  Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
1473  ColumnDef *n;
1474 
1475  if (attr->attisdropped)
1476  continue;
1477 
1478  n = makeNode(ColumnDef);
1479  n->colname = pstrdup(NameStr(attr->attname));
1480  n->typeName = makeTypeNameFromOid(attr->atttypid, attr->atttypmod);
1481  n->inhcount = 0;
1482  n->is_local = true;
1483  n->is_not_null = false;
1484  n->is_from_type = true;
1485  n->storage = 0;
1486  n->raw_default = NULL;
1487  n->cooked_default = NULL;
1488  n->collClause = NULL;
1489  n->collOid = attr->attcollation;
1490  n->constraints = NIL;
1491  n->location = -1;
1492  cxt->columns = lappend(cxt->columns, n);
1493  }
1494  ReleaseTupleDesc(tupdesc);
1495 
1496  ReleaseSysCache(tuple);
1497 }
int location
Definition: parsenodes.h:740
int inhcount
Definition: parsenodes.h:724
char storage
Definition: parsenodes.h:728
Oid collOid
Definition: parsenodes.h:737
bool is_local
Definition: parsenodes.h:725
void check_of_type(HeapTuple typetuple)
Definition: tablecmds.c:6693
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:122
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1824

References Assert(), check_of_type(), ColumnDef::collClause, ColumnDef::collOid, ColumnDef::colname, CreateStmtContext::columns, ColumnDef::constraints, ColumnDef::cooked_default, GETSTRUCT, i, ColumnDef::inhcount, ColumnDef::is_from_type, ColumnDef::is_local, ColumnDef::is_not_null, lappend(), ColumnDef::location, lookup_rowtype_tupdesc(), makeNode, makeTypeNameFromOid(), NameStr, TupleDescData::natts, NIL, pstrdup(), ColumnDef::raw_default, ReleaseSysCache(), ReleaseTupleDesc, ColumnDef::storage, TupleDescAttr, ColumnDef::typeName, typenameType(), and TypeName::typeOid.

Referenced by transformCreateStmt().

◆ transformPartitionBound()

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

Definition at line 3985 of file parse_utilcmd.c.

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

References Assert(), copyObject, deparse_context_for(), deparse_expression(), elog(), equal(), ereport, errcode(), errmsg(), ERROR, exprLocation(), get_attname(), get_partition_col_collation(), get_partition_col_typid(), get_partition_col_typmod(), get_partition_exprs(), get_partition_natts(), get_partition_strategy(), PartitionBoundSpec::is_default, sort-test::key, lappend(), lfirst, lfirst_node, linitial, list_length(), PartitionBoundSpec::listdatums, PartitionBoundSpec::lowerdatums, NIL, parser_errposition(), PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, RelationGetPartitionKey(), RelationGetRelationName, RelationGetRelid, PartitionBoundSpec::strategy, transformPartitionBoundValue(), transformPartitionRangeBounds(), PartitionBoundSpec::upperdatums, and value.

Referenced by DefineRelation(), and transformPartitionCmd().

◆ transformPartitionBoundValue()

static Const * transformPartitionBoundValue ( ParseState pstate,
Node val,
const char *  colName,
Oid  colType,
int32  colTypmod,
Oid  partCollation 
)
static

Definition at line 4295 of file parse_utilcmd.c.

4298 {
4299  Node *value;
4300 
4301  /* Transform raw parsetree */
4303 
4304  /*
4305  * transformExpr() should have already rejected column references,
4306  * subqueries, aggregates, window functions, and SRFs, based on the
4307  * EXPR_KIND_ of a partition bound expression.
4308  */
4310 
4311  /*
4312  * Coerce to the correct type. This might cause an explicit coercion step
4313  * to be added on top of the expression, which must be evaluated before
4314  * returning the result to the caller.
4315  */
4316  value = coerce_to_target_type(pstate,
4317  value, exprType(value),
4318  colType,
4319  colTypmod,
4322  -1);
4323 
4324  if (value == NULL)
4325  ereport(ERROR,
4326  (errcode(ERRCODE_DATATYPE_MISMATCH),
4327  errmsg("specified value cannot be cast to type %s for column \"%s\"",
4328  format_type_be(colType), colName),
4329  parser_errposition(pstate, exprLocation(val))));
4330 
4331  /*
4332  * Evaluate the expression, if needed, assigning the partition key's data
4333  * type and collation to the resulting Const node.
4334  */
4335  if (!IsA(value, Const))
4336  {
4337  assign_expr_collations(pstate, value);
4338  value = (Node *) expression_planner((Expr *) value);
4339  value = (Node *) evaluate_expr((Expr *) value, colType, colTypmod,
4340  partCollation);
4341  if (!IsA(value, Const))
4342  elog(ERROR, "could not evaluate partition bound expression");
4343  }
4344  else
4345  {
4346  /*
4347  * If the expression is already a Const, as is often the case, we can
4348  * skip the rather expensive steps above. But we still have to insert
4349  * the right collation, since coerce_to_target_type doesn't handle
4350  * that.
4351  */
4352  ((Const *) value)->constcollid = partCollation;
4353  }
4354 
4355  /*
4356  * Attach original expression's parse location to the Const, so that
4357  * that's what will be reported for any later errors related to this
4358  * partition bound.
4359  */
4360  ((Const *) value)->location = exprLocation(val);
4361 
4362  return (Const *) value;
4363 }
Expr * evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod, Oid result_collation)
Definition: clauses.c:4854
long val
Definition: informix.c:664
Node * coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, Oid targettype, int32 targettypmod, CoercionContext ccontext, CoercionForm cformat, int location)
Definition: parse_coerce.c:78
@ EXPR_KIND_PARTITION_BOUND
Definition: parse_node.h:78
Expr * expression_planner(Expr *expr)
Definition: planner.c:6434
@ COERCE_IMPLICIT_CAST
Definition: primnodes.h:663
@ COERCION_ASSIGNMENT
Definition: primnodes.h:642
bool contain_var_clause(Node *node)
Definition: var.c:403

References Assert(), assign_expr_collations(), COERCE_IMPLICIT_CAST, coerce_to_target_type(), COERCION_ASSIGNMENT, contain_var_clause(), elog(), ereport, errcode(), errmsg(), ERROR, evaluate_expr(), EXPR_KIND_PARTITION_BOUND, expression_planner(), exprLocation(), exprType(), format_type_be(), IsA, parser_errposition(), transformExpr(), val, and value.

Referenced by transformPartitionBound(), and transformPartitionRangeBounds().

◆ transformPartitionCmd()

static void transformPartitionCmd ( CreateStmtContext cxt,
PartitionCmd cmd 
)
static

Definition at line 3932 of file parse_utilcmd.c.

3933 {
3934  Relation parentRel = cxt->rel;
3935 
3936  switch (parentRel->rd_rel->relkind)
3937  {
3938  case RELKIND_PARTITIONED_TABLE:
3939  /* transform the partition bound, if any */
3940  Assert(RelationGetPartitionKey(parentRel) != NULL);
3941  if (cmd->bound != NULL)
3942  cxt->partbound = transformPartitionBound(cxt->pstate, parentRel,
3943  cmd->bound);
3944  break;
3945  case RELKIND_PARTITIONED_INDEX:
3946 
3947  /*
3948  * A partitioned index cannot have a partition bound set. ALTER
3949  * INDEX prevents that with its grammar, but not ALTER TABLE.
3950  */
3951  if (cmd->bound != NULL)
3952  ereport(ERROR,
3953  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3954  errmsg("\"%s\" is not a partitioned table",
3955  RelationGetRelationName(parentRel))));
3956  break;
3957  case RELKIND_RELATION:
3958  /* the table must be partitioned */
3959  ereport(ERROR,
3960  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3961  errmsg("table \"%s\" is not partitioned",
3962  RelationGetRelationName(parentRel))));
3963  break;
3964  case RELKIND_INDEX:
3965  /* the index must be partitioned */
3966  ereport(ERROR,
3967  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3968  errmsg("index \"%s\" is not partitioned",
3969  RelationGetRelationName(parentRel))));
3970  break;
3971  default:
3972  /* parser shouldn't let this case through */
3973  elog(ERROR, "\"%s\" is not a partitioned table or index",
3974  RelationGetRelationName(parentRel));
3975  break;
3976  }
3977 }
PartitionBoundSpec * transformPartitionBound(ParseState *pstate, Relation parent, PartitionBoundSpec *spec)

References Assert(), PartitionCmd::bound, elog(), ereport, errcode(), errmsg(), ERROR, CreateStmtContext::partbound, CreateStmtContext::pstate, RelationData::rd_rel, CreateStmtContext::rel, RelationGetPartitionKey(), RelationGetRelationName, and transformPartitionBound().

Referenced by transformAlterTableStmt().

◆ transformPartitionRangeBounds()

static List * transformPartitionRangeBounds ( ParseState pstate,
List blist,
Relation  parent 
)
static

Definition at line 4139 of file parse_utilcmd.c.

4141 {
4142  List *result = NIL;
4144  List *partexprs = get_partition_exprs(key);
4145  ListCell *lc;
4146  int i,
4147  j;
4148 
4149  i = j = 0;
4150  foreach(lc, blist)
4151  {
4152  Node *expr = lfirst(lc);
4153  PartitionRangeDatum *prd = NULL;
4154 
4155  /*
4156  * Infinite range bounds -- "minvalue" and "maxvalue" -- get passed in
4157  * as ColumnRefs.
4158  */
4159  if (IsA(expr, ColumnRef))
4160  {
4161  ColumnRef *cref = (ColumnRef *) expr;
4162  char *cname = NULL;
4163 
4164  /*
4165  * There should be a single field named either "minvalue" or
4166  * "maxvalue".
4167  */
4168  if (list_length(cref->fields) == 1 &&
4169  IsA(linitial(cref->fields), String))
4170  cname = strVal(linitial(cref->fields));
4171 
4172  if (cname == NULL)
4173  {
4174  /*
4175  * ColumnRef is not in the desired single-field-name form. For
4176  * consistency between all partition strategies, let the
4177  * expression transformation report any errors rather than
4178  * doing it ourselves.
4179  */
4180  }
4181  else if (strcmp("minvalue", cname) == 0)
4182  {
4185  prd->value = NULL;
4186  }
4187  else if (strcmp("maxvalue", cname) == 0)
4188  {
4191  prd->value = NULL;
4192  }
4193  }
4194 
4195  if (prd == NULL)
4196  {
4197  char *colname;
4198  Oid coltype;
4199  int32 coltypmod;
4200  Oid partcollation;
4201  Const *value;
4202 
4203  /* Get the column's name in case we need to output an error */
4204  if (key->partattrs[i] != 0)
4205  colname = get_attname(RelationGetRelid(parent),
4206  key->partattrs[i], false);
4207  else
4208  {
4209  colname = deparse_expression((Node *) list_nth(partexprs, j),
4211  RelationGetRelid(parent)),
4212  false, false);
4213  ++j;
4214  }
4215 
4216  /* Need its type data too */
4217  coltype = get_partition_col_typid(key, i);
4218  coltypmod = get_partition_col_typmod(key, i);
4219  partcollation = get_partition_col_collation(key, i);
4220 
4221  value = transformPartitionBoundValue(pstate, expr,
4222  colname,
4223  coltype, coltypmod,
4224  partcollation);
4225  if (value->constisnull)
4226  ereport(ERROR,
4227  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
4228  errmsg("cannot specify NULL in range bound")));
4231  prd->value = (Node *) value;
4232  ++i;
4233  }
4234 
4235  prd->location = exprLocation(expr);
4236 
4237  result = lappend(result, prd);
4238  }
4239 
4240  /*
4241  * Once we see MINVALUE or MAXVALUE for one column, the remaining columns
4242  * must be the same.
4243  */
4244  validateInfiniteBounds(pstate, result);
4245 
4246  return result;
4247 }
int j
Definition: isn.c:74
static void validateInfiniteBounds(ParseState *pstate, List *blist)
@ PARTITION_RANGE_DATUM_MAXVALUE
Definition: parsenodes.h:921
@ PARTITION_RANGE_DATUM_VALUE
Definition: parsenodes.h:920
@ PARTITION_RANGE_DATUM_MINVALUE
Definition: parsenodes.h:919
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
List * fields
Definition: parsenodes.h:292
PartitionRangeDatumKind kind
Definition: parsenodes.h:928
Definition: value.h:64

References deparse_context_for(), deparse_expression(), ereport, errcode(), errmsg(), ERROR, exprLocation(), ColumnRef::fields, get_attname(), get_partition_col_collation(), get_partition_col_typid(), get_partition_col_typmod(), get_partition_exprs(), i, IsA, j, sort-test::key, PartitionRangeDatum::kind, lappend(), lfirst, linitial, list_length(), list_nth(), PartitionRangeDatum::location, makeNode, NIL, PARTITION_RANGE_DATUM_MAXVALUE, PARTITION_RANGE_DATUM_MINVALUE, PARTITION_RANGE_DATUM_VALUE, RelationGetPartitionKey(), RelationGetRelationName, RelationGetRelid, strVal, transformPartitionBoundValue(), validateInfiniteBounds(), PartitionRangeDatum::value, and value.

Referenced by transformPartitionBound().

◆ transformRuleStmt()

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

Definition at line 2973 of file parse_utilcmd.c.

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

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

Referenced by DefineRule().

◆ transformStatsStmt()

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

Definition at line 2898 of file parse_utilcmd.c.

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

References AccessShareLock, addNSItemToQuery(), addRangeTableEntryForRelation(), assign_expr_collations(), ereport, errcode(), errmsg(), ERROR, StatsElem::expr, EXPR_KIND_STATS_EXPRESSION, free_parsestate(), lfirst, list_length(), make_parsestate(), NoLock, ParseState::p_rtable, ParseState::p_sourcetext, relation_open(), stmt, table_close(), and transformExpr().

Referenced by ATPostAlterTypeParse(), and ProcessUtilitySlow().

◆ transformTableConstraint()

static void transformTableConstraint ( CreateStmtContext cxt,
Constraint constraint 
)
static

Definition at line 871 of file parse_utilcmd.c.

872 {
873  switch (constraint->contype)
874  {
875  case CONSTR_PRIMARY: