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:2315
@ CONSTR_UNIQUE
Definition: parsenodes.h:2313
@ CONSTR_EXCLUSION
Definition: parsenodes.h:2314
@ CONSTR_PRIMARY
Definition: parsenodes.h:2312

Function Documentation

◆ expandTableLikeClause()

List* expandTableLikeClause ( RangeVar heapRel,
TableLikeClause table_like_clause 
)

Definition at line 1197 of file parse_utilcmd.c.

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

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

Referenced by transformTableLikeClause().

◆ generateClonedIndexStmt()

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

Definition at line 1522 of file parse_utilcmd.c.

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

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

1999 {
2000  List *result;
2001  HeapTuple ht_coll;
2002  Form_pg_collation coll_rec;
2003  char *nsp_name;
2004  char *coll_name;
2005 
2006  if (!OidIsValid(collation))
2007  return NIL; /* easy case */
2008  if (collation == get_typcollation(actual_datatype))
2009  return NIL; /* just let it default */
2010 
2011  ht_coll = SearchSysCache1(COLLOID, ObjectIdGetDatum(collation));
2012  if (!HeapTupleIsValid(ht_coll))
2013  elog(ERROR, "cache lookup failed for collation %u", collation);
2014  coll_rec = (Form_pg_collation) GETSTRUCT(ht_coll);
2015 
2016  /* For simplicity, we always schema-qualify the name */
2017  nsp_name = get_namespace_name(coll_rec->collnamespace);
2018  coll_name = pstrdup(NameStr(coll_rec->collname));
2019  result = list_make2(makeString(nsp_name), makeString(coll_name));
2020 
2021  ReleaseSysCache(ht_coll);
2022  return result;
2023 }
Oid get_typcollation(Oid typid)
Definition: lsyscache.c:3014
FormData_pg_collation * Form_pg_collation
Definition: pg_collation.h:57
@ 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 2032 of file parse_utilcmd.c.

2033 {
2034  List *result = NIL;
2035  HeapTuple ht_opc;
2036  Form_pg_opclass opc_rec;
2037 
2038  ht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
2039  if (!HeapTupleIsValid(ht_opc))
2040  elog(ERROR, "cache lookup failed for opclass %u", opclass);
2041  opc_rec = (Form_pg_opclass) GETSTRUCT(ht_opc);
2042 
2043  if (GetDefaultOpClass(actual_datatype, opc_rec->opcmethod) != opclass)
2044  {
2045  /* For simplicity, we always schema-qualify the name */
2046  char *nsp_name = get_namespace_name(opc_rec->opcnamespace);
2047  char *opc_name = pstrdup(NameStr(opc_rec->opcname));
2048 
2049  result = list_make2(makeString(nsp_name), makeString(opc_name));
2050  }
2051 
2052  ReleaseSysCache(ht_opc);
2053  return result;
2054 }
Oid GetDefaultOpClass(Oid type_id, Oid am_id)
Definition: indexcmds.c:2221
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 3932 of file parse_utilcmd.c.

3933 {
3934  if (*stmt_schema_name == NULL)
3935  *stmt_schema_name = context_schema;
3936  else if (strcmp(context_schema, *stmt_schema_name) != 0)
3937  ereport(ERROR,
3938  (errcode(ERRCODE_INVALID_SCHEMA_DEFINITION),
3939  errmsg("CREATE specifies a schema (%s) "
3940  "different from the one being created (%s)",
3941  *stmt_schema_name, context_schema)));
3942 }

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

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

References AccessShareLock, addNSItemToQuery(), addRangeTableEntryForRelation(), CreateStmtContext::alist, AT_AddColumn, AT_AddColumnRecurse, AT_AddConstraint, AT_AddConstraintRecurse, 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, AlterTableStmt::relation, relation_close(), relation_open(), RelationGetDescr, RelationGetRelationName, AlterSeqStmt::sequence, 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 2717 of file parse_utilcmd.c.

2718 {
2719  ListCell *ckclist;
2720 
2721  if (cxt->ckconstraints == NIL)
2722  return;
2723 
2724  /*
2725  * If creating a new table (but not a foreign table), we can safely skip
2726  * validation of check constraints, and nonetheless mark them valid. (This
2727  * will override any user-supplied NOT VALID flag.)
2728  */
2729  if (skipValidation)
2730  {
2731  foreach(ckclist, cxt->ckconstraints)
2732  {
2733  Constraint *constraint = (Constraint *) lfirst(ckclist);
2734 
2735  constraint->skip_validation = true;
2736  constraint->initially_valid = true;
2737  }
2738  }
2739 }

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 (cxt->partbound)
744  ereport(ERROR,
745  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
746  errmsg("generated columns are not supported on partitions")));
747 
748  if (saw_generated)
749  ereport(ERROR,
750  (errcode(ERRCODE_SYNTAX_ERROR),
751  errmsg("multiple generation clauses specified for column \"%s\" of table \"%s\"",
752  column->colname, cxt->relation->relname),
754  constraint->location)));
755  column->generated = ATTRIBUTE_GENERATED_STORED;
756  column->raw_default = constraint->raw_expr;
757  Assert(constraint->cooked_expr == NULL);
758  saw_generated = true;
759  break;
760 
761  case CONSTR_CHECK:
762  cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
763  break;
764 
765  case CONSTR_PRIMARY:
766  if (cxt->isforeign)
767  ereport(ERROR,
768  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
769  errmsg("primary key constraints are not supported on foreign tables"),
771  constraint->location)));
772  /* FALL THRU */
773 
774  case CONSTR_UNIQUE:
775  if (cxt->isforeign)
776  ereport(ERROR,
777  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
778  errmsg("unique constraints are not supported on foreign tables"),
780  constraint->location)));
781  if (constraint->keys == NIL)
782  constraint->keys = list_make1(makeString(column->colname));
783  cxt->ixconstraints = lappend(cxt->ixconstraints, constraint);
784  break;
785 
786  case CONSTR_EXCLUSION:
787  /* grammar does not allow EXCLUDE as a column constraint */
788  elog(ERROR, "column exclusion constraints are not supported");
789  break;
790 
791  case CONSTR_FOREIGN:
792  if (cxt->isforeign)
793  ereport(ERROR,
794  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
795  errmsg("foreign key constraints are not supported on foreign tables"),
797  constraint->location)));
798 
799  /*
800  * Fill in the current attribute's name and throw it into the
801  * list of FK constraints to be processed later.
802  */
803  constraint->fk_attrs = list_make1(makeString(column->colname));
804  cxt->fkconstraints = lappend(cxt->fkconstraints, constraint);
805  break;
806 
811  /* transformConstraintAttrs took care of these */
812  break;
813 
814  default:
815  elog(ERROR, "unrecognized constraint type: %d",
816  constraint->contype);
817  break;
818  }
819 
820  if (saw_default && saw_identity)
821  ereport(ERROR,
822  (errcode(ERRCODE_SYNTAX_ERROR),
823  errmsg("both default and identity specified for column \"%s\" of table \"%s\"",
824  column->colname, cxt->relation->relname),
826  constraint->location)));
827 
828  if (saw_default && saw_generated)
829  ereport(ERROR,
830  (errcode(ERRCODE_SYNTAX_ERROR),
831  errmsg("both default and generation expression specified for column \"%s\" of table \"%s\"",
832  column->colname, cxt->relation->relname),
834  constraint->location)));
835 
836  if (saw_identity && saw_generated)
837  ereport(ERROR,
838  (errcode(ERRCODE_SYNTAX_ERROR),
839  errmsg("both identity and generation expression specified for column \"%s\" of table \"%s\"",
840  column->colname, cxt->relation->relname),
842  constraint->location)));
843  }
844 
845  /*
846  * If needed, generate ALTER FOREIGN TABLE ALTER COLUMN statement to add
847  * per-column foreign data wrapper options to this column after creation.
848  */
849  if (column->fdwoptions != NIL)
850  {
851  AlterTableStmt *stmt;
852  AlterTableCmd *cmd;
853 
854  cmd = makeNode(AlterTableCmd);
856  cmd->name = column->colname;
857  cmd->def = (Node *) column->fdwoptions;
858  cmd->behavior = DROP_RESTRICT;
859  cmd->missing_ok = false;
860 
861  stmt = makeNode(AlterTableStmt);
862  stmt->relation = cxt->relation;
863  stmt->cmds = NIL;
865  stmt->cmds = lappend(stmt->cmds, cmd);
866 
867  cxt->alist = lappend(cxt->alist, stmt);
868  }
869 }
FuncCall * makeFuncCall(List *name, List *args, CoercionForm funcformat, int location)
Definition: makefuncs.c:585
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:110
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:2318
@ CONSTR_IDENTITY
Definition: parsenodes.h:2309
@ CONSTR_ATTR_NOT_DEFERRABLE
Definition: parsenodes.h:2317
@ CONSTR_DEFAULT
Definition: parsenodes.h:2308
@ CONSTR_NOTNULL
Definition: parsenodes.h:2307
@ CONSTR_ATTR_IMMEDIATE
Definition: parsenodes.h:2319
@ CONSTR_NULL
Definition: parsenodes.h:2305
@ CONSTR_GENERATED
Definition: parsenodes.h:2310
@ CONSTR_ATTR_DEFERRABLE
Definition: parsenodes.h:2316
@ DROP_RESTRICT
Definition: parsenodes.h:1933
@ OBJECT_FOREIGN_TABLE
Definition: parsenodes.h:1878
@ AT_AlterColumnGenericOptions
Definition: parsenodes.h:1982
TypeName * SystemTypeName(char *name)
List * SystemFuncName(char *name)
static int list_length(const List *l)
Definition: pg_list.h:150
#define linitial(l)
Definition: pg_list.h:176
FormData_pg_type * Form_pg_type
Definition: pg_type.h:261
NameData typname
Definition: pg_type.h:41
@ COERCE_EXPLICIT_CALL
Definition: primnodes.h:586
char * quote_qualified_identifier(const char *qualifier, const char *ident)
Definition: ruleutils.c:11613
int location
Definition: parsenodes.h:327
union ValUnion val
Definition: parsenodes.h:325
DropBehavior behavior
Definition: parsenodes.h:2043
bool is_not_null
Definition: parsenodes.h:691
char generated
Definition: parsenodes.h:700
List * fdwoptions
Definition: parsenodes.h:704
List * keys
Definition: parsenodes.h:2355
List * fk_attrs
Definition: parsenodes.h:2375
NodeTag type
Definition: nodes.h:113
char * sval
Definition: value.h:68
TypeName * typeName
Definition: parsenodes.h:337
int location
Definition: parsenodes.h:338
Node * arg
Definition: parsenodes.h:336
Oid typeOid
Definition: parsenodes.h:232
bool pct_type
Definition: parsenodes.h:234
List * names
Definition: parsenodes.h:231
List * arrayBounds
Definition: parsenodes.h:237
int location
Definition: parsenodes.h:238
Node node
Definition: parsenodes.h:312
String sval
Definition: parsenodes.h:316
#define strVal(v)
Definition: value.h:82

References CreateStmtContext::alist, TypeCast::arg, TypeName::arrayBounds, Assert(), AT_AlterColumnGenericOptions, AlterTableCmd::behavior, CreateStmtContext::ckconstraints, AlterTableStmt::cmds, 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, AlterTableStmt::objtype, CreateStmtContext::ofType, Constraint::options, parser_errposition(), CreateStmtContext::partbound, TypeName::pct_type, CreateStmtContext::pstate, quote_qualified_identifier(), ColumnDef::raw_default, Constraint::raw_expr, CreateStmtContext::relation, AlterTableStmt::relation, ReleaseSysCache(), RangeVar::relname, 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 3774 of file parse_utilcmd.c.

3775 {
3776  /*
3777  * All we really need to do here is verify that the type is valid,
3778  * including any collation spec that might be present.
3779  */
3780  Type ctype = typenameType(cxt->pstate, column->typeName, NULL);
3781 
3782  if (column->collClause)
3783  {
3784  Form_pg_type typtup = (Form_pg_type) GETSTRUCT(ctype);
3785 
3786  LookupCollation(cxt->pstate,
3787  column->collClause->collname,
3788  column->collClause->location);
3789  /* Complain if COLLATE is applied to an uncollatable type */
3790  if (!OidIsValid(typtup->typcollation))
3791  ereport(ERROR,
3792  (errcode(ERRCODE_DATATYPE_MISMATCH),
3793  errmsg("collations are not supported by type %s",
3794  format_type_be(typtup->oid)),
3796  column->collClause->location)));
3797  }
3798 
3799  ReleaseSysCache(ctype);
3800 }
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:348
CollateClause * collClause
Definition: parsenodes.h:701

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

3660 {
3661  Constraint *lastprimarycon = NULL;
3662  bool saw_deferrability = false;
3663  bool saw_initially = false;
3664  ListCell *clist;
3665 
3666 #define SUPPORTS_ATTRS(node) \
3667  ((node) != NULL && \
3668  ((node)->contype == CONSTR_PRIMARY || \
3669  (node)->contype == CONSTR_UNIQUE || \
3670  (node)->contype == CONSTR_EXCLUSION || \
3671  (node)->contype == CONSTR_FOREIGN))
3672 
3673  foreach(clist, constraintList)
3674  {
3675  Constraint *con = (Constraint *) lfirst(clist);
3676 
3677  if (!IsA(con, Constraint))
3678  elog(ERROR, "unrecognized node type: %d",
3679  (int) nodeTag(con));
3680  switch (con->contype)
3681  {
3683  if (!SUPPORTS_ATTRS(lastprimarycon))
3684  ereport(ERROR,
3685  (errcode(ERRCODE_SYNTAX_ERROR),
3686  errmsg("misplaced DEFERRABLE clause"),
3687  parser_errposition(cxt->pstate, con->location)));
3688  if (saw_deferrability)
3689  ereport(ERROR,
3690  (errcode(ERRCODE_SYNTAX_ERROR),
3691  errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed"),
3692  parser_errposition(cxt->pstate, con->location)));
3693  saw_deferrability = true;
3694  lastprimarycon->deferrable = true;
3695  break;
3696 
3698  if (!SUPPORTS_ATTRS(lastprimarycon))
3699  ereport(ERROR,
3700  (errcode(ERRCODE_SYNTAX_ERROR),
3701  errmsg("misplaced NOT DEFERRABLE clause"),
3702  parser_errposition(cxt->pstate, con->location)));
3703  if (saw_deferrability)
3704  ereport(ERROR,
3705  (errcode(ERRCODE_SYNTAX_ERROR),
3706  errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed"),
3707  parser_errposition(cxt->pstate, con->location)));
3708  saw_deferrability = true;
3709  lastprimarycon->deferrable = false;
3710  if (saw_initially &&
3711  lastprimarycon->initdeferred)
3712  ereport(ERROR,
3713  (errcode(ERRCODE_SYNTAX_ERROR),
3714  errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"),
3715  parser_errposition(cxt->pstate, con->location)));
3716  break;
3717 
3718  case CONSTR_ATTR_DEFERRED:
3719  if (!SUPPORTS_ATTRS(lastprimarycon))
3720  ereport(ERROR,
3721  (errcode(ERRCODE_SYNTAX_ERROR),
3722  errmsg("misplaced INITIALLY DEFERRED clause"),
3723  parser_errposition(cxt->pstate, con->location)));
3724  if (saw_initially)
3725  ereport(ERROR,
3726  (errcode(ERRCODE_SYNTAX_ERROR),
3727  errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed"),
3728  parser_errposition(cxt->pstate, con->location)));
3729  saw_initially = true;
3730  lastprimarycon->initdeferred = true;
3731 
3732  /*
3733  * If only INITIALLY DEFERRED appears, assume DEFERRABLE
3734  */
3735  if (!saw_deferrability)
3736  lastprimarycon->deferrable = true;
3737  else if (!lastprimarycon->deferrable)
3738  ereport(ERROR,
3739  (errcode(ERRCODE_SYNTAX_ERROR),
3740  errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"),
3741  parser_errposition(cxt->pstate, con->location)));
3742  break;
3743 
3744  case CONSTR_ATTR_IMMEDIATE:
3745  if (!SUPPORTS_ATTRS(lastprimarycon))
3746  ereport(ERROR,
3747  (errcode(ERRCODE_SYNTAX_ERROR),
3748  errmsg("misplaced INITIALLY IMMEDIATE clause"),
3749  parser_errposition(cxt->pstate, con->location)));
3750  if (saw_initially)
3751  ereport(ERROR,
3752  (errcode(ERRCODE_SYNTAX_ERROR),
3753  errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed"),
3754  parser_errposition(cxt->pstate, con->location)));
3755  saw_initially = true;
3756  lastprimarycon->initdeferred = false;
3757  break;
3758 
3759  default:
3760  /* Otherwise it's not an attribute */
3761  lastprimarycon = con;
3762  /* reset flags for new primary node */
3763  saw_deferrability = false;
3764  saw_initially = false;
3765  break;
3766  }
3767  }
3768 }
#define SUPPORTS_ATTRS(node)
bool initdeferred
Definition: parsenodes.h:2344
bool deferrable
Definition: parsenodes.h:2343

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

3828 {
3830  List *result;
3831  ListCell *elements;
3832 
3833  cxt.stmtType = "CREATE SCHEMA";
3834  cxt.schemaname = stmt->schemaname;
3835  cxt.authrole = (RoleSpec *) stmt->authrole;
3836  cxt.sequences = NIL;
3837  cxt.tables = NIL;
3838  cxt.views = NIL;
3839  cxt.indexes = NIL;
3840  cxt.triggers = NIL;
3841  cxt.grants = NIL;
3842 
3843  /*
3844  * Run through each schema element in the schema element list. Separate
3845  * statements by type, and do preliminary analysis.
3846  */
3847  foreach(elements, stmt->schemaElts)
3848  {
3849  Node *element = lfirst(elements);
3850 
3851  switch (nodeTag(element))
3852  {
3853  case T_CreateSeqStmt:
3854  {
3855  CreateSeqStmt *elp = (CreateSeqStmt *) element;
3856 
3858  cxt.sequences = lappend(cxt.sequences, element);
3859  }
3860  break;
3861 
3862  case T_CreateStmt:
3863  {
3864  CreateStmt *elp = (CreateStmt *) element;
3865 
3867 
3868  /*
3869  * XXX todo: deal with constraints
3870  */
3871  cxt.tables = lappend(cxt.tables, element);
3872  }
3873  break;
3874 
3875  case T_ViewStmt:
3876  {
3877  ViewStmt *elp = (ViewStmt *) element;
3878 
3879  setSchemaName(cxt.schemaname, &elp->view->schemaname);
3880 
3881  /*
3882  * XXX todo: deal with references between views
3883  */
3884  cxt.views = lappend(cxt.views, element);
3885  }
3886  break;
3887 
3888  case T_IndexStmt:
3889  {
3890  IndexStmt *elp = (IndexStmt *) element;
3891 
3893  cxt.indexes = lappend(cxt.indexes, element);
3894  }
3895  break;
3896 
3897  case T_CreateTrigStmt:
3898  {
3900 
3902  cxt.triggers = lappend(cxt.triggers, element);
3903  }
3904  break;
3905 
3906  case T_GrantStmt:
3907  cxt.grants = lappend(cxt.grants, element);
3908  break;
3909 
3910  default:
3911  elog(ERROR, "unrecognized node type: %d",
3912  (int) nodeTag(element));
3913  }
3914  }
3915 
3916  result = NIL;
3917  result = list_concat(result, cxt.sequences);
3918  result = list_concat(result, cxt.tables);
3919  result = list_concat(result, cxt.views);
3920  result = list_concat(result, cxt.indexes);
3921  result = list_concat(result, cxt.triggers);
3922  result = list_concat(result, cxt.grants);
3923 
3924  return result;
3925 }
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
RoleSpec * authrole
Definition: parsenodes.h:1926
RangeVar * relation
Definition: parsenodes.h:2257
RangeVar * relation
Definition: parsenodes.h:2622
RangeVar * relation
Definition: parsenodes.h:2967
RangeVar * view
Definition: parsenodes.h:3349

References CreateSchemaStmtContext::authrole, CreateSchemaStmt::authrole, element(), elog(), ERROR, CreateSchemaStmtContext::grants, CreateSchemaStmtContext::indexes, lappend(), lfirst, list_concat(), NIL, nodeTag, CreateStmt::relation, CreateTrigStmt::relation, IndexStmt::relation, CreateSchemaStmt::schemaElts, CreateSchemaStmtContext::schemaname, CreateSchemaStmt::schemaname, RangeVar::schemaname, CreateSeqStmt::sequence, CreateSchemaStmtContext::sequences, setSchemaName(), 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;
229  if (IsA(stmt, CreateForeignTableStmt))
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:31
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:160
void setup_parser_errposition_callback(ParseCallbackState *pcbstate, ParseState *pstate, int location)
Definition: parse_node.c:144
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
List * tableElts
Definition: parsenodes.h:2258
TypeName * ofTypename
Definition: parsenodes.h:2263
bool if_not_exists
Definition: parsenodes.h:2269
List * inhRelations
Definition: parsenodes.h:2259
PartitionSpec * partspec
Definition: parsenodes.h:2262
PartitionBoundSpec * partbound
Definition: parsenodes.h:2261
List * constraints
Definition: parsenodes.h:2264
int location
Definition: primnodes.h:89

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

Referenced by ProcessUtilitySlow().

◆ transformExtendedStatistics()

static void transformExtendedStatistics ( CreateStmtContext cxt)
static

Definition at line 2702 of file parse_utilcmd.c.

2703 {
2704  cxt->alist = list_concat(cxt->alist, cxt->extstats);
2705 }

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

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

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

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

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

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

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

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, IndexStmt::indexParams, lfirst, list_length(), make_parsestate(), NoLock, ParseState::p_rtable, ParseState::p_sourcetext, relation_open(), table_close(), IndexStmt::transformed, transformExpr(), transformWhereClause(), and IndexStmt::whereClause.

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

◆ transformOfType()

static void transformOfType ( CreateStmtContext cxt,
TypeName ofTypename 
)
static

Definition at line 1459 of file parse_utilcmd.c.

1460 {
1461  HeapTuple tuple;
1462  TupleDesc tupdesc;
1463  int i;
1464  Oid ofTypeId;
1465 
1466  Assert(ofTypename);
1467 
1468  tuple = typenameType(NULL, ofTypename, NULL);
1469  check_of_type(tuple);
1470  ofTypeId = ((Form_pg_type) GETSTRUCT(tuple))->oid;
1471  ofTypename->typeOid = ofTypeId; /* cached for later */
1472 
1473  tupdesc = lookup_rowtype_tupdesc(ofTypeId, -1);
1474  for (i = 0; i < tupdesc->natts; i++)
1475  {
1476  Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
1477  ColumnDef *n;
1478 
1479  if (attr->attisdropped)
1480  continue;
1481 
1482  n = makeNode(ColumnDef);
1483  n->colname = pstrdup(NameStr(attr->attname));
1484  n->typeName = makeTypeNameFromOid(attr->atttypid, attr->atttypmod);
1485  n->inhcount = 0;
1486  n->is_local = true;
1487  n->is_not_null = false;
1488  n->is_from_type = true;
1489  n->storage = 0;
1490  n->raw_default = NULL;
1491  n->cooked_default = NULL;
1492  n->collClause = NULL;
1493  n->collOid = attr->attcollation;
1494  n->constraints = NIL;
1495  n->location = -1;
1496  cxt->columns = lappend(cxt->columns, n);
1497  }
1498  ReleaseTupleDesc(tupdesc);
1499 
1500  ReleaseSysCache(tuple);
1501 }
int location
Definition: parsenodes.h:705
int inhcount
Definition: parsenodes.h:689
char storage
Definition: parsenodes.h:693
Oid collOid
Definition: parsenodes.h:702
bool is_local
Definition: parsenodes.h:690
void check_of_type(HeapTuple typetuple)
Definition: tablecmds.c:6617
#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 4005 of file parse_utilcmd.c.

4007 {
4008  PartitionBoundSpec *result_spec;
4010  char strategy = get_partition_strategy(key);
4011  int partnatts = get_partition_natts(key);
4012  List *partexprs = get_partition_exprs(key);
4013 
4014  /* Avoid scribbling on input */
4015  result_spec = copyObject(spec);
4016 
4017  if (spec->is_default)
4018  {
4019  /*
4020  * Hash partitioning does not support a default partition; there's no
4021  * use case for it (since the set of partitions to create is perfectly
4022  * defined), and if users do get into it accidentally, it's hard to
4023  * back out from it afterwards.
4024  */
4025  if (strategy == PARTITION_STRATEGY_HASH)
4026  ereport(ERROR,
4027  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4028  errmsg("a hash-partitioned table may not have a default partition")));
4029 
4030  /*
4031  * In case of the default partition, parser had no way to identify the
4032  * partition strategy. Assign the parent's strategy to the default
4033  * partition bound spec.
4034  */
4035  result_spec->strategy = strategy;
4036 
4037  return result_spec;
4038  }
4039 
4040  if (strategy == PARTITION_STRATEGY_HASH)
4041  {
4042  if (spec->strategy != PARTITION_STRATEGY_HASH)
4043  ereport(ERROR,
4044  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4045  errmsg("invalid bound specification for a hash partition"),
4046  parser_errposition(pstate, exprLocation((Node *) spec))));
4047 
4048  if (spec->modulus <= 0)
4049  ereport(ERROR,
4050  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4051  errmsg("modulus for hash partition must be an integer value greater than zero")));
4052 
4053  Assert(spec->remainder >= 0);
4054 
4055  if (spec->remainder >= spec->modulus)
4056  ereport(ERROR,
4057  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4058  errmsg("remainder for hash partition must be less than modulus")));
4059  }
4060  else if (strategy == PARTITION_STRATEGY_LIST)
4061  {
4062  ListCell *cell;
4063  char *colname;
4064  Oid coltype;
4065  int32 coltypmod;
4066  Oid partcollation;
4067 
4068  if (spec->strategy != PARTITION_STRATEGY_LIST)
4069  ereport(ERROR,
4070  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4071  errmsg("invalid bound specification for a list partition"),
4072  parser_errposition(pstate, exprLocation((Node *) spec))));
4073 
4074  /* Get the only column's name in case we need to output an error */
4075  if (key->partattrs[0] != 0)
4076  colname = get_attname(RelationGetRelid(parent),
4077  key->partattrs[0], false);
4078  else
4079  colname = deparse_expression((Node *) linitial(partexprs),
4081  RelationGetRelid(parent)),
4082  false, false);
4083  /* Need its type data too */
4084  coltype = get_partition_col_typid(key, 0);
4085  coltypmod = get_partition_col_typmod(key, 0);
4086  partcollation = get_partition_col_collation(key, 0);
4087 
4088  result_spec->listdatums = NIL;
4089  foreach(cell, spec->listdatums)
4090  {
4091  Node *expr = lfirst(cell);
4092  Const *value;
4093  ListCell *cell2;
4094  bool duplicate;
4095 
4096  value = transformPartitionBoundValue(pstate, expr,
4097  colname, coltype, coltypmod,
4098  partcollation);
4099 
4100  /* Don't add to the result if the value is a duplicate */
4101  duplicate = false;
4102  foreach(cell2, result_spec->listdatums)
4103  {
4104  Const *value2 = lfirst_node(Const, cell2);
4105 
4106  if (equal(value, value2))
4107  {
4108  duplicate = true;
4109  break;
4110  }
4111  }
4112  if (duplicate)
4113  continue;
4114 
4115  result_spec->listdatums = lappend(result_spec->listdatums,
4116  value);
4117  }
4118  }
4119  else if (strategy == PARTITION_STRATEGY_RANGE)
4120  {
4121  if (spec->strategy != PARTITION_STRATEGY_RANGE)
4122  ereport(ERROR,
4123  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4124  errmsg("invalid bound specification for a range partition"),
4125  parser_errposition(pstate, exprLocation((Node *) spec))));
4126 
4127  if (list_length(spec->lowerdatums) != partnatts)
4128  ereport(ERROR,
4129  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4130  errmsg("FROM must specify exactly one value per partitioning column")));
4131  if (list_length(spec->upperdatums) != partnatts)
4132  ereport(ERROR,
4133  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4134  errmsg("TO must specify exactly one value per partitioning column")));
4135 
4136  /*
4137  * Convert raw parse nodes into PartitionRangeDatum nodes and perform
4138  * any necessary validation.
4139  */
4140  result_spec->lowerdatums =
4142  parent);
4143  result_spec->upperdatums =
4145  parent);
4146  }
4147  else
4148  elog(ERROR, "unexpected partition strategy: %d", (int) strategy);
4149 
4150  return result_spec;
4151 }
signed int int32
Definition: c.h:430
static struct @143 value
int exprLocation(const Node *expr)
Definition: nodeFuncs.c:1243
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:833
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:831
@ PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:832
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:3596
List * deparse_context_for(const char *aliasname, Oid relid)
Definition: ruleutils.c:3656

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

4318 {
4319  Node *value;
4320 
4321  /* Transform raw parsetree */
4323 
4324  /*
4325  * transformExpr() should have already rejected column references,
4326  * subqueries, aggregates, window functions, and SRFs, based on the
4327  * EXPR_KIND_ of a partition bound expression.
4328  */
4330 
4331  /*
4332  * Coerce to the correct type. This might cause an explicit coercion step
4333  * to be added on top of the expression, which must be evaluated before
4334  * returning the result to the caller.
4335  */
4336  value = coerce_to_target_type(pstate,
4337  value, exprType(value),
4338  colType,
4339  colTypmod,
4342  -1);
4343 
4344  if (value == NULL)
4345  ereport(ERROR,
4346  (errcode(ERRCODE_DATATYPE_MISMATCH),
4347  errmsg("specified value cannot be cast to type %s for column \"%s\"",
4348  format_type_be(colType), colName),
4349  parser_errposition(pstate, exprLocation(val))));
4350 
4351  /*
4352  * Evaluate the expression, if needed, assigning the partition key's data
4353  * type and collation to the resulting Const node.
4354  */
4355  if (!IsA(value, Const))
4356  {
4357  assign_expr_collations(pstate, value);
4358  value = (Node *) expression_planner((Expr *) value);
4359  value = (Node *) evaluate_expr((Expr *) value, colType, colTypmod,
4360  partCollation);
4361  if (!IsA(value, Const))
4362  elog(ERROR, "could not evaluate partition bound expression");
4363  }
4364  else
4365  {
4366  /*
4367  * If the expression is already a Const, as is often the case, we can
4368  * skip the rather expensive steps above. But we still have to insert
4369  * the right collation, since coerce_to_target_type doesn't handle
4370  * that.
4371  */
4372  ((Const *) value)->constcollid = partCollation;
4373  }
4374 
4375  /*
4376  * Attach original expression's parse location to the Const, so that
4377  * that's what will be reported for any later errors related to this
4378  * partition bound.
4379  */
4380  ((Const *) value)->location = exprLocation(val);
4381 
4382  return (Const *) value;
4383 }
Expr * evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod, Oid result_collation)
Definition: clauses.c:4807
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:6146
@ COERCE_IMPLICIT_CAST
Definition: primnodes.h:588
@ COERCION_ASSIGNMENT
Definition: primnodes.h:567
bool contain_var_clause(Node *node)
Definition: var.c:393

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

3953 {
3954  Relation parentRel = cxt->rel;
3955 
3956  switch (parentRel->rd_rel->relkind)
3957  {
3958  case RELKIND_PARTITIONED_TABLE:
3959  /* transform the partition bound, if any */
3960  Assert(RelationGetPartitionKey(parentRel) != NULL);
3961  if (cmd->bound != NULL)
3962  cxt->partbound = transformPartitionBound(cxt->pstate, parentRel,
3963  cmd->bound);
3964  break;
3965  case RELKIND_PARTITIONED_INDEX:
3966 
3967  /*
3968  * A partitioned index cannot have a partition bound set. ALTER
3969  * INDEX prevents that with its grammar, but not ALTER TABLE.
3970  */
3971  if (cmd->bound != NULL)
3972  ereport(ERROR,
3973  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3974  errmsg("\"%s\" is not a partitioned table",
3975  RelationGetRelationName(parentRel))));
3976  break;
3977  case RELKIND_RELATION:
3978  /* the table must be partitioned */
3979  ereport(ERROR,
3980  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3981  errmsg("table \"%s\" is not partitioned",
3982  RelationGetRelationName(parentRel))));
3983  break;
3984  case RELKIND_INDEX:
3985  /* the index must be partitioned */
3986  ereport(ERROR,
3987  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3988  errmsg("index \"%s\" is not partitioned",
3989  RelationGetRelationName(parentRel))));
3990  break;
3991  default:
3992  /* parser shouldn't let this case through */
3993  elog(ERROR, "\"%s\" is not a partitioned table or index",
3994  RelationGetRelationName(parentRel));
3995  break;
3996  }
3997 }
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 4159 of file parse_utilcmd.c.

4161 {
4162  List *result = NIL;
4164  List *partexprs = get_partition_exprs(key);
4165  ListCell *lc;
4166  int i,
4167  j;
4168 
4169  i = j = 0;
4170  foreach(lc, blist)
4171  {
4172  Node *expr = lfirst(lc);
4173  PartitionRangeDatum *prd = NULL;
4174 
4175  /*
4176  * Infinite range bounds -- "minvalue" and "maxvalue" -- get passed in
4177  * as ColumnRefs.
4178  */
4179  if (IsA(expr, ColumnRef))
4180  {
4181  ColumnRef *cref = (ColumnRef *) expr;
4182  char *cname = NULL;
4183 
4184  /*
4185  * There should be a single field named either "minvalue" or
4186  * "maxvalue".
4187  */
4188  if (list_length(cref->fields) == 1 &&
4189  IsA(linitial(cref->fields), String))
4190  cname = strVal(linitial(cref->fields));
4191 
4192  if (cname == NULL)
4193  {
4194  /*
4195  * ColumnRef is not in the desired single-field-name form. For
4196  * consistency between all partition strategies, let the
4197  * expression transformation report any errors rather than
4198  * doing it ourselves.
4199  */
4200  }
4201  else if (strcmp("minvalue", cname) == 0)
4202  {
4205  prd->value = NULL;
4206  }
4207  else if (strcmp("maxvalue", cname) == 0)
4208  {
4211  prd->value = NULL;
4212  }
4213  }
4214 
4215  if (prd == NULL)
4216  {
4217  char *colname;
4218  Oid coltype;
4219  int32 coltypmod;
4220  Oid partcollation;
4221  Const *value;
4222 
4223  /* Get the column's name in case we need to output an error */
4224  if (key->partattrs[i] != 0)
4225  colname = get_attname(RelationGetRelid(parent),
4226  key->partattrs[i], false);
4227  else
4228  {
4229  colname = deparse_expression((Node *) list_nth(partexprs, j),
4231  RelationGetRelid(parent)),
4232  false, false);
4233  ++j;
4234  }
4235 
4236  /* Need its type data too */
4237  coltype = get_partition_col_typid(key, i);
4238  coltypmod = get_partition_col_typmod(key, i);
4239  partcollation = get_partition_col_collation(key, i);
4240 
4241  value = transformPartitionBoundValue(pstate, expr,
4242  colname,
4243  coltype, coltypmod,
4244  partcollation);
4245  if (value->constisnull)
4246  ereport(ERROR,
4247  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
4248  errmsg("cannot specify NULL in range bound")));
4251  prd->value = (Node *) value;
4252  ++i;
4253  }
4254 
4255  prd->location = exprLocation(expr);
4256 
4257  result = lappend(result, prd);
4258  }
4259 
4260  /*
4261  * Once we see MINVALUE or MAXVALUE for one column, the remaining columns
4262  * must be the same.
4263  */
4264  validateInfiniteBounds(pstate, result);
4265 
4266  return result;
4267 }
int j
Definition: isn.c:74
static void validateInfiniteBounds(ParseState *pstate, List *blist)
@ PARTITION_RANGE_DATUM_MAXVALUE
Definition: parsenodes.h:885
@ PARTITION_RANGE_DATUM_VALUE
Definition: parsenodes.h:884
@ PARTITION_RANGE_DATUM_MINVALUE
Definition: parsenodes.h:883
static void * list_nth(const List *list, int n)
Definition: pg_list.h:297
List * fields
Definition: parsenodes.h:257
PartitionRangeDatumKind kind
Definition: parsenodes.h:892
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 2987 of file parse_utilcmd.c.

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

References AccessExclusiveLock, AccessShareLock, generate_unaccent_rules::action, RuleStmt::actions, 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, RuleStmt::event, 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, ParseNamespaceItem::p_rte, ParseNamespaceItem::p_rtindex, ParseState::p_sourcetext, PRS2_NEW_VARNO, PRS2_OLD_VARNO, rangeTableEntry_used(), RelationData::rd_rel, RuleStmt::relation, RangeTblEntry::requiredPerms, Query::rtable, RangeTblRef::rtindex, Query::setOperations, table_close(), table_openrv(), transformStmt(), transformWhereClause(), and RuleStmt::whereClause.

Referenced by DefineRule().

◆ transformStatsStmt()

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

Definition at line 2912 of file parse_utilcmd.c.

2913 {
2914  ParseState *pstate;
2915  ParseNamespaceItem *nsitem;
2916  ListCell *l;
2917  Relation rel;
2918 
2919  /* Nothing to do if statement already transformed. */
2920  if (stmt->transformed)
2921  return stmt;
2922 
2923  /* Set up pstate */
2924  pstate = make_parsestate(NULL);
2925  pstate->p_sourcetext = queryString;
2926 
2927  /*
2928  * Put the parent table into the rtable so that the expressions can refer
2929  * to its fields without qualification. Caller is responsible for locking
2930  * relation, but we still need to open it.
2931  */
2932  rel = relation_open(relid, NoLock);