PostgreSQL Source Code  git master
parse_utilcmd.h File Reference
Include dependency graph for parse_utilcmd.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

ListtransformCreateStmt (CreateStmt *stmt, const char *queryString)
 
ListtransformAlterTableStmt (Oid relid, AlterTableStmt *stmt, const char *queryString)
 
IndexStmttransformIndexStmt (Oid relid, IndexStmt *stmt, const char *queryString)
 
void transformRuleStmt (RuleStmt *stmt, const char *queryString, List **actions, Node **whereClause)
 
ListtransformCreateSchemaStmt (CreateSchemaStmt *stmt)
 
PartitionBoundSpectransformPartitionBound (ParseState *pstate, Relation parent, PartitionBoundSpec *spec)
 

Function Documentation

◆ transformAlterTableStmt()

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

Definition at line 2639 of file parse_utilcmd.c.

References addRangeTableEntryForRelation(), addRTEtoQuery(), CreateStmtContext::alist, AT_AddColumn, AT_AddColumnToView, AT_AddConstraint, AT_AddIdentity, AT_AddIndex, AT_AddIndexConstraint, AT_AlterColumnType, AT_AttachPartition, AT_DetachPartition, AT_ProcessedConstraint, AT_SetIdentity, CreateStmtContext::blist, PartitionCmd::bound, castNode, CreateStmtContext::ckconstraints, AlterTableStmt::cmds, ColumnDef::colname, CreateStmtContext::columns, CONSTR_FOREIGN, ColumnDef::constraints, ColumnDef::cooked_default, copyObject, AlterTableCmd::def, DefElem::defname, elog, ERROR, EXPR_KIND_ALTER_COL_TRANSFORM, CreateStmtContext::fkconstraints, AlterSeqStmt::for_identity, Constraint::generated_when, generateSerialExtraStmts(), get_attidentity(), get_attnum(), get_atttype(), get_namespace_name(), get_rel_name(), get_rel_namespace(), getOwnedSequence(), getOwnedSequences(), CreateStmtContext::hasoids, ColumnDef::identity, IndexStmt::indexOid, CreateStmtContext::inh_indexes, CreateStmtContext::inhRelations, InvalidAttrNumber, IsA, CreateStmtContext::isalter, CreateStmtContext::isforeign, CreateStmtContext::ispartitioned, CreateStmtContext::ixconstraints, lappend(), lfirst, lfirst_node, linitial_oid, list_concat(), list_make1, make_parsestate(), makeDefElem(), makeNode, makeRangeVar(), makeTypeNameFromOid(), AlterSeqStmt::missing_ok, AlterTableCmd::name, NIL, nodeTag, NoLock, OBJECT_FOREIGN_TABLE, 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(), AlterTableStmt::relkind, RELKIND_PARTITIONED_TABLE, AlterSeqStmt::sequence, CreateStmtContext::stmtType, AlterTableCmd::subtype, transformCheckConstraints(), transformColumnDefinition(), transformExpr(), transformFKConstraints(), transformIndexConstraints(), transformIndexStmt(), transformPartitionCmd(), transformTableConstraint(), ColumnDef::typeName, and typenameTypeId().

Referenced by ATPostAlterTypeParse(), and ProcessUtilitySlow().

2641 {
2642  Relation rel;
2643  ParseState *pstate;
2644  CreateStmtContext cxt;
2645  List *result;
2646  List *save_alist;
2647  ListCell *lcmd,
2648  *l;
2649  List *newcmds = NIL;
2650  bool skipValidation = true;
2651  AlterTableCmd *newcmd;
2652  RangeTblEntry *rte;
2653 
2654  /*
2655  * We must not scribble on the passed-in AlterTableStmt, so copy it. (This
2656  * is overkill, but easy.)
2657  */
2658  stmt = copyObject(stmt);
2659 
2660  /* Caller is responsible for locking the relation */
2661  rel = relation_open(relid, NoLock);
2662 
2663  /* Set up pstate */
2664  pstate = make_parsestate(NULL);
2665  pstate->p_sourcetext = queryString;
2666  rte = addRangeTableEntryForRelation(pstate,
2667  rel,
2668  NULL,
2669  false,
2670  true);
2671  addRTEtoQuery(pstate, rte, false, true, true);
2672 
2673  /* Set up CreateStmtContext */
2674  cxt.pstate = pstate;
2675  if (stmt->relkind == OBJECT_FOREIGN_TABLE)
2676  {
2677  cxt.stmtType = "ALTER FOREIGN TABLE";
2678  cxt.isforeign = true;
2679  }
2680  else
2681  {
2682  cxt.stmtType = "ALTER TABLE";
2683  cxt.isforeign = false;
2684  }
2685  cxt.relation = stmt->relation;
2686  cxt.rel = rel;
2687  cxt.inhRelations = NIL;
2688  cxt.isalter = true;
2689  cxt.hasoids = false; /* need not be right */
2690  cxt.columns = NIL;
2691  cxt.ckconstraints = NIL;
2692  cxt.fkconstraints = NIL;
2693  cxt.ixconstraints = NIL;
2694  cxt.inh_indexes = NIL;
2695  cxt.blist = NIL;
2696  cxt.alist = NIL;
2697  cxt.pkey = NULL;
2698  cxt.ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
2699  cxt.partbound = NULL;
2700 
2701  /*
2702  * The only subtypes that currently require parse transformation handling
2703  * are ADD COLUMN, ADD CONSTRAINT and SET DATA TYPE. These largely re-use
2704  * code from CREATE TABLE.
2705  */
2706  foreach(lcmd, stmt->cmds)
2707  {
2708  AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
2709 
2710  switch (cmd->subtype)
2711  {
2712  case AT_AddColumn:
2713  case AT_AddColumnToView:
2714  {
2715  ColumnDef *def = castNode(ColumnDef, cmd->def);
2716 
2717  transformColumnDefinition(&cxt, def);
2718 
2719  /*
2720  * If the column has a non-null default, we can't skip
2721  * validation of foreign keys.
2722  */
2723  if (def->raw_default != NULL)
2724  skipValidation = false;
2725 
2726  /*
2727  * All constraints are processed in other ways. Remove the
2728  * original list
2729  */
2730  def->constraints = NIL;
2731 
2732  newcmds = lappend(newcmds, cmd);
2733  break;
2734  }
2735 
2736  case AT_AddConstraint:
2737 
2738  /*
2739  * The original AddConstraint cmd node doesn't go to newcmds
2740  */
2741  if (IsA(cmd->def, Constraint))
2742  {
2743  transformTableConstraint(&cxt, (Constraint *) cmd->def);
2744  if (((Constraint *) cmd->def)->contype == CONSTR_FOREIGN)
2745  skipValidation = false;
2746  }
2747  else
2748  elog(ERROR, "unrecognized node type: %d",
2749  (int) nodeTag(cmd->def));
2750  break;
2751 
2753 
2754  /*
2755  * Already-transformed ADD CONSTRAINT, so just make it look
2756  * like the standard case.
2757  */
2758  cmd->subtype = AT_AddConstraint;
2759  newcmds = lappend(newcmds, cmd);
2760  break;
2761 
2762  case AT_AlterColumnType:
2763  {
2764  ColumnDef *def = (ColumnDef *) cmd->def;
2765  AttrNumber attnum;
2766 
2767  /*
2768  * For ALTER COLUMN TYPE, transform the USING clause if
2769  * one was specified.
2770  */
2771  if (def->raw_default)
2772  {
2773  def->cooked_default =
2774  transformExpr(pstate, def->raw_default,
2776  }
2777 
2778  /*
2779  * For identity column, create ALTER SEQUENCE command to
2780  * change the data type of the sequence.
2781  */
2782  attnum = get_attnum(relid, cmd->name);
2783 
2784  /*
2785  * if attribute not found, something will error about it
2786  * later
2787  */
2788  if (attnum != InvalidAttrNumber && get_attidentity(relid, attnum))
2789  {
2790  Oid seq_relid = getOwnedSequence(relid, attnum);
2791  Oid typeOid = typenameTypeId(pstate, def->typeName);
2792  AlterSeqStmt *altseqstmt = makeNode(AlterSeqStmt);
2793 
2794  altseqstmt->sequence = makeRangeVar(get_namespace_name(get_rel_namespace(seq_relid)),
2795  get_rel_name(seq_relid),
2796  -1);
2797  altseqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(typeOid, -1), -1));
2798  altseqstmt->for_identity = true;
2799  cxt.blist = lappend(cxt.blist, altseqstmt);
2800  }
2801 
2802  newcmds = lappend(newcmds, cmd);
2803  break;
2804  }
2805 
2806  case AT_AddIdentity:
2807  {
2808  Constraint *def = castNode(Constraint, cmd->def);
2809  ColumnDef *newdef = makeNode(ColumnDef);
2810  AttrNumber attnum;
2811 
2812  newdef->colname = cmd->name;
2813  newdef->identity = def->generated_when;
2814  cmd->def = (Node *) newdef;
2815 
2816  attnum = get_attnum(relid, cmd->name);
2817 
2818  /*
2819  * if attribute not found, something will error about it
2820  * later
2821  */
2822  if (attnum != InvalidAttrNumber)
2823  generateSerialExtraStmts(&cxt, newdef,
2824  get_atttype(relid, attnum),
2825  def->options, true,
2826  NULL, NULL);
2827 
2828  newcmds = lappend(newcmds, cmd);
2829  break;
2830  }
2831 
2832  case AT_SetIdentity:
2833  {
2834  /*
2835  * Create an ALTER SEQUENCE statement for the internal
2836  * sequence of the identity column.
2837  */
2838  ListCell *lc;
2839  List *newseqopts = NIL;
2840  List *newdef = NIL;
2841  List *seqlist;
2842  AttrNumber attnum;
2843 
2844  /*
2845  * Split options into those handled by ALTER SEQUENCE and
2846  * those for ALTER TABLE proper.
2847  */
2848  foreach(lc, castNode(List, cmd->def))
2849  {
2850  DefElem *def = lfirst_node(DefElem, lc);
2851 
2852  if (strcmp(def->defname, "generated") == 0)
2853  newdef = lappend(newdef, def);
2854  else
2855  newseqopts = lappend(newseqopts, def);
2856  }
2857 
2858  attnum = get_attnum(relid, cmd->name);
2859 
2860  if (attnum)
2861  {
2862  seqlist = getOwnedSequences(relid, attnum);
2863  if (seqlist)
2864  {
2865  AlterSeqStmt *seqstmt;
2866  Oid seq_relid;
2867 
2868  seqstmt = makeNode(AlterSeqStmt);
2869  seq_relid = linitial_oid(seqlist);
2871  get_rel_name(seq_relid), -1);
2872  seqstmt->options = newseqopts;
2873  seqstmt->for_identity = true;
2874  seqstmt->missing_ok = false;
2875 
2876  cxt.alist = lappend(cxt.alist, seqstmt);
2877  }
2878  }
2879 
2880  /*
2881  * If column was not found or was not an identity column,
2882  * we just let the ALTER TABLE command error out later.
2883  */
2884 
2885  cmd->def = (Node *) newdef;
2886  newcmds = lappend(newcmds, cmd);
2887  break;
2888  }
2889 
2890  case AT_AttachPartition:
2891  case AT_DetachPartition:
2892  {
2893  PartitionCmd *partcmd = (PartitionCmd *) cmd->def;
2894 
2895  transformPartitionCmd(&cxt, partcmd);
2896  /* assign transformed value of the partition bound */
2897  partcmd->bound = cxt.partbound;
2898  }
2899 
2900  newcmds = lappend(newcmds, cmd);
2901  break;
2902 
2903  default:
2904  newcmds = lappend(newcmds, cmd);
2905  break;
2906  }
2907  }
2908 
2909  /*
2910  * transformIndexConstraints wants cxt.alist to contain only index
2911  * statements, so transfer anything we already have into save_alist
2912  * immediately.
2913  */
2914  save_alist = cxt.alist;
2915  cxt.alist = NIL;
2916 
2917  /* Postprocess constraints */
2919  transformFKConstraints(&cxt, skipValidation, true);
2920  transformCheckConstraints(&cxt, false);
2921 
2922  /*
2923  * Push any index-creation commands into the ALTER, so that they can be
2924  * scheduled nicely by tablecmds.c. Note that tablecmds.c assumes that
2925  * the IndexStmt attached to an AT_AddIndex or AT_AddIndexConstraint
2926  * subcommand has already been through transformIndexStmt.
2927  */
2928  foreach(l, cxt.alist)
2929  {
2930  IndexStmt *idxstmt = lfirst_node(IndexStmt, l);
2931 
2932  idxstmt = transformIndexStmt(relid, idxstmt, queryString);
2933  newcmd = makeNode(AlterTableCmd);
2935  newcmd->def = (Node *) idxstmt;
2936  newcmds = lappend(newcmds, newcmd);
2937  }
2938  cxt.alist = NIL;
2939 
2940  /* Append any CHECK or FK constraints to the commands list */
2941  foreach(l, cxt.ckconstraints)
2942  {
2943  newcmd = makeNode(AlterTableCmd);
2944  newcmd->subtype = AT_AddConstraint;
2945  newcmd->def = (Node *) lfirst(l);
2946  newcmds = lappend(newcmds, newcmd);
2947  }
2948  foreach(l, cxt.fkconstraints)
2949  {
2950  newcmd = makeNode(AlterTableCmd);
2951  newcmd->subtype = AT_AddConstraint;
2952  newcmd->def = (Node *) lfirst(l);
2953  newcmds = lappend(newcmds, newcmd);
2954  }
2955 
2956  /* Close rel */
2957  relation_close(rel, NoLock);
2958 
2959  /*
2960  * Output results.
2961  */
2962  stmt->cmds = newcmds;
2963 
2964  result = lappend(cxt.blist, stmt);
2965  result = list_concat(result, cxt.alist);
2966  result = list_concat(result, save_alist);
2967 
2968  return result;
2969 }
#define NIL
Definition: pg_list.h:69
Oid getOwnedSequence(Oid relid, AttrNumber attnum)
Definition: pg_depend.c:605
#define IsA(nodeptr, _type_)
Definition: nodes.h:561
char get_attidentity(Oid relid, AttrNumber attnum)
Definition: lsyscache.c:851
static void generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column, Oid seqtypid, List *seqoptions, bool for_identity, char **snamespace_p, char **sname_p)
char generated_when
Definition: parsenodes.h:2100
#define castNode(_type_, nodeptr)
Definition: nodes.h:579
char identity
Definition: parsenodes.h:649
List * constraints
Definition: parsenodes.h:652
Oid get_rel_namespace(Oid relid)
Definition: lsyscache.c:1750
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:146
Definition: nodes.h:510
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: heapam.c:1266
AlterTableType subtype
Definition: parsenodes.h:1785
List * list_concat(List *list1, List *list2)
Definition: list.c:321
Form_pg_class rd_rel
Definition: rel.h:114
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:576
DefElem * makeDefElem(char *name, Node *arg, int location)
Definition: makefuncs.c:546
List * options
Definition: parsenodes.h:2488
List * options
Definition: parsenodes.h:2109
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:44
#define list_make1(x1)
Definition: pg_list.h:139
ParseState * pstate
Definition: parse_utilcmd.c:75
Node * cooked_default
Definition: parsenodes.h:648
Oid indexOid
Definition: parsenodes.h:2715
RangeVar * relation
Definition: parse_utilcmd.c:77
IndexStmt * pkey
Definition: parse_utilcmd.c:92
#define ERROR
Definition: elog.h:43
#define lfirst_node(type, lc)
Definition: pg_list.h:109
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3047
#define NoLock
Definition: lockdefs.h:34
AttrNumber get_attnum(Oid relid, const char *attname)
Definition: lsyscache.c:821
const char * p_sourcetext
Definition: parse_node.h:171
Oid get_atttype(Oid relid, AttrNumber attnum)
Definition: lsyscache.c:878
ObjectType relkind
Definition: parsenodes.h:1699
void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
PartitionBoundSpec * partbound
Definition: parse_utilcmd.c:94
Node * raw_default
Definition: parsenodes.h:647
List * lappend(List *list, void *datum)
Definition: list.c:128
#define RELKIND_PARTITIONED_TABLE
Definition: pg_class.h:168
List * getOwnedSequences(Oid relid, AttrNumber attnum)
Definition: pg_depend.c:548
TypeName * makeTypeNameFromOid(Oid typeOid, int32 typmod)
Definition: makefuncs.c:471
RangeVar * sequence
Definition: parsenodes.h:2487
#define makeNode(_type_)
Definition: nodes.h:558
#define lfirst(lc)
Definition: pg_list.h:106
static void transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
PartitionBoundSpec * bound
Definition: parsenodes.h:848
#define linitial_oid(l)
Definition: pg_list.h:113
RangeTblEntry * addRangeTableEntryForRelation(ParseState *pstate, Relation rel, Alias *alias, bool inh, bool inFromCl)
TypeName * typeName
Definition: parsenodes.h:640
static void transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd)
static void transformFKConstraints(CreateStmtContext *cxt, bool skipValidation, bool isAddConstraint)
#define InvalidAttrNumber
Definition: attnum.h:23
#define nodeTag(nodeptr)
Definition: nodes.h:515
const char * stmtType
Definition: parse_utilcmd.c:76
RangeVar * relation
Definition: parsenodes.h:1697
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1120
bool for_identity
Definition: parsenodes.h:2489
char * defname
Definition: parsenodes.h:719
char * colname
Definition: parsenodes.h:639
#define elog
Definition: elog.h:219
#define copyObject(obj)
Definition: nodes.h:623
static void transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
static void transformIndexConstraints(CreateStmtContext *cxt)
IndexStmt * transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
Definition: pg_list.h:45
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1726
int16 AttrNumber
Definition: attnum.h:21
RangeVar * makeRangeVar(char *schemaname, char *relname, int location)
Definition: makefuncs.c:421
static void transformCheckConstraints(CreateStmtContext *cxt, bool skipValidation)
Oid typenameTypeId(ParseState *pstate, const TypeName *typeName)
Definition: parse_type.c:274

◆ transformCreateSchemaStmt()

List* transformCreateSchemaStmt ( CreateSchemaStmt stmt)

Definition at line 3150 of file parse_utilcmd.c.

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, RangeVar::schemaname, CreateSchemaStmtContext::schemaname, CreateSchemaStmt::schemaname, CreateSeqStmt::sequence, CreateSchemaStmtContext::sequences, setSchemaName(), CreateSchemaStmtContext::stmtType, T_CreateSeqStmt, T_CreateStmt, T_CreateTrigStmt, T_GrantStmt, T_IndexStmt, T_ViewStmt, CreateSchemaStmtContext::tables, CreateSchemaStmtContext::triggers, ViewStmt::view, and CreateSchemaStmtContext::views.

Referenced by CreateSchemaCommand().

3151 {
3153  List *result;
3154  ListCell *elements;
3155 
3156  cxt.stmtType = "CREATE SCHEMA";
3157  cxt.schemaname = stmt->schemaname;
3158  cxt.authrole = (RoleSpec *) stmt->authrole;
3159  cxt.sequences = NIL;
3160  cxt.tables = NIL;
3161  cxt.views = NIL;
3162  cxt.indexes = NIL;
3163  cxt.triggers = NIL;
3164  cxt.grants = NIL;
3165 
3166  /*
3167  * Run through each schema element in the schema element list. Separate
3168  * statements by type, and do preliminary analysis.
3169  */
3170  foreach(elements, stmt->schemaElts)
3171  {
3172  Node *element = lfirst(elements);
3173 
3174  switch (nodeTag(element))
3175  {
3176  case T_CreateSeqStmt:
3177  {
3178  CreateSeqStmt *elp = (CreateSeqStmt *) element;
3179 
3181  cxt.sequences = lappend(cxt.sequences, element);
3182  }
3183  break;
3184 
3185  case T_CreateStmt:
3186  {
3187  CreateStmt *elp = (CreateStmt *) element;
3188 
3190 
3191  /*
3192  * XXX todo: deal with constraints
3193  */
3194  cxt.tables = lappend(cxt.tables, element);
3195  }
3196  break;
3197 
3198  case T_ViewStmt:
3199  {
3200  ViewStmt *elp = (ViewStmt *) element;
3201 
3202  setSchemaName(cxt.schemaname, &elp->view->schemaname);
3203 
3204  /*
3205  * XXX todo: deal with references between views
3206  */
3207  cxt.views = lappend(cxt.views, element);
3208  }
3209  break;
3210 
3211  case T_IndexStmt:
3212  {
3213  IndexStmt *elp = (IndexStmt *) element;
3214 
3216  cxt.indexes = lappend(cxt.indexes, element);
3217  }
3218  break;
3219 
3220  case T_CreateTrigStmt:
3221  {
3222  CreateTrigStmt *elp = (CreateTrigStmt *) element;
3223 
3225  cxt.triggers = lappend(cxt.triggers, element);
3226  }
3227  break;
3228 
3229  case T_GrantStmt:
3230  cxt.grants = lappend(cxt.grants, element);
3231  break;
3232 
3233  default:
3234  elog(ERROR, "unrecognized node type: %d",
3235  (int) nodeTag(element));
3236  }
3237  }
3238 
3239  result = NIL;
3240  result = list_concat(result, cxt.sequences);
3241  result = list_concat(result, cxt.tables);
3242  result = list_concat(result, cxt.views);
3243  result = list_concat(result, cxt.indexes);
3244  result = list_concat(result, cxt.triggers);
3245  result = list_concat(result, cxt.grants);
3246 
3247  return result;
3248 }
RangeVar * relation
Definition: parsenodes.h:2010
#define NIL
Definition: pg_list.h:69
Definition: nodes.h:510
List * list_concat(List *list1, List *list2)
Definition: list.c:321
RangeVar * view
Definition: parsenodes.h:3008
char * schemaname
Definition: primnodes.h:67
RangeVar * relation
Definition: parsenodes.h:2707
RoleSpec * authrole
Definition: parsenodes.h:1679
#define ERROR
Definition: elog.h:43
List * lappend(List *list, void *datum)
Definition: list.c:128
static chr element(struct vars *v, const chr *startp, const chr *endp)
Definition: regc_locale.c:380
RangeVar * sequence
Definition: parsenodes.h:2477
#define lfirst(lc)
Definition: pg_list.h:106
static void setSchemaName(char *context_schema, char **stmt_schema_name)
#define nodeTag(nodeptr)
Definition: nodes.h:515
RangeVar * relation
Definition: parsenodes.h:2362
#define elog
Definition: elog.h:219
Definition: pg_list.h:45

◆ transformCreateStmt()

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

Definition at line 158 of file parse_utilcmd.c.

References CreateStmtContext::alist, Assert, CreateStmtContext::blist, cancel_parser_errposition_callback(), CreateStmtContext::ckconstraints, CreateStmtContext::columns, CreateStmt::constraints, copyObject, element(), elog, ereport, errcode(), errmsg(), ERROR, CreateStmtContext::fkconstraints, get_namespace_name(), CreateStmtContext::hasoids, CreateStmt::if_not_exists, CreateStmtContext::inh_indexes, CreateStmtContext::inhRelations, CreateStmt::inhRelations, interpretOidsOption(), IsA, CreateStmtContext::isalter, CreateStmtContext::isforeign, CreateStmtContext::ispartitioned, CreateStmtContext::ixconstraints, lappend(), lcons(), lfirst, list_concat(), RangeVar::location, make_parsestate(), makeDefElem(), makeInteger(), NIL, nodeTag, NoLock, NOTICE, CreateStmt::ofTypename, OidIsValid, CreateStmt::options, ParseState::p_sourcetext, CreateStmt::partbound, CreateStmt::partspec, CreateStmtContext::pkey, CreateStmtContext::pstate, RangeVarGetAndCheckCreationNamespace(), CreateStmtContext::rel, CreateStmtContext::relation, CreateStmt::relation, RangeVar::relname, RangeVar::relpersistence, RELPERSISTENCE_TEMP, RangeVar::schemaname, setup_parser_errposition_callback(), CreateStmtContext::stmtType, T_ColumnDef, T_Constraint, T_TableLikeClause, CreateStmt::tableElts, transformCheckConstraints(), transformColumnDefinition(), transformFKConstraints(), transformIndexConstraints(), transformOfType(), transformTableConstraint(), and transformTableLikeClause().

Referenced by ProcessUtilitySlow().

159 {
160  ParseState *pstate;
161  CreateStmtContext cxt;
162  List *result;
163  List *save_alist;
164  ListCell *elements;
165  Oid namespaceid;
166  Oid existing_relid;
167  ParseCallbackState pcbstate;
168  bool like_found = false;
169  bool is_foreign_table = IsA(stmt, CreateForeignTableStmt);
170 
171  /*
172  * We must not scribble on the passed-in CreateStmt, so copy it. (This is
173  * overkill, but easy.)
174  */
175  stmt = copyObject(stmt);
176 
177  /* Set up pstate */
178  pstate = make_parsestate(NULL);
179  pstate->p_sourcetext = queryString;
180 
181  /*
182  * Look up the creation namespace. This also checks permissions on the
183  * target namespace, locks it against concurrent drops, checks for a
184  * preexisting relation in that namespace with the same name, and updates
185  * stmt->relation->relpersistence if the selected namespace is temporary.
186  */
187  setup_parser_errposition_callback(&pcbstate, pstate,
188  stmt->relation->location);
189  namespaceid =
191  &existing_relid);
193 
194  /*
195  * If the relation already exists and the user specified "IF NOT EXISTS",
196  * bail out with a NOTICE.
197  */
198  if (stmt->if_not_exists && OidIsValid(existing_relid))
199  {
200  ereport(NOTICE,
201  (errcode(ERRCODE_DUPLICATE_TABLE),
202  errmsg("relation \"%s\" already exists, skipping",
203  stmt->relation->relname)));
204  return NIL;
205  }
206 
207  /*
208  * If the target relation name isn't schema-qualified, make it so. This
209  * prevents some corner cases in which added-on rewritten commands might
210  * think they should apply to other relations that have the same name and
211  * are earlier in the search path. But a local temp table is effectively
212  * specified to be in pg_temp, so no need for anything extra in that case.
213  */
214  if (stmt->relation->schemaname == NULL
216  stmt->relation->schemaname = get_namespace_name(namespaceid);
217 
218  /* Set up CreateStmtContext */
219  cxt.pstate = pstate;
220  if (IsA(stmt, CreateForeignTableStmt))
221  {
222  cxt.stmtType = "CREATE FOREIGN TABLE";
223  cxt.isforeign = true;
224  }
225  else
226  {
227  cxt.stmtType = "CREATE TABLE";
228  cxt.isforeign = false;
229  }
230  cxt.relation = stmt->relation;
231  cxt.rel = NULL;
232  cxt.inhRelations = stmt->inhRelations;
233  cxt.isalter = false;
234  cxt.columns = NIL;
235  cxt.ckconstraints = NIL;
236  cxt.fkconstraints = NIL;
237  cxt.ixconstraints = NIL;
238  cxt.inh_indexes = NIL;
239  cxt.blist = NIL;
240  cxt.alist = NIL;
241  cxt.pkey = NULL;
242  cxt.ispartitioned = stmt->partspec != NULL;
243 
244  /*
245  * Notice that we allow OIDs here only for plain tables, even though
246  * foreign tables also support them. This is necessary because the
247  * default_with_oids GUC must apply only to plain tables and not any other
248  * relkind; doing otherwise would break existing pg_dump files. We could
249  * allow explicit "WITH OIDS" while not allowing default_with_oids to
250  * affect other relkinds, but it would complicate interpretOidsOption(),
251  * and right now there's no WITH OIDS option in CREATE FOREIGN TABLE
252  * anyway.
253  */
254  cxt.hasoids = interpretOidsOption(stmt->options, !cxt.isforeign);
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:
280  transformColumnDefinition(&cxt, (ColumnDef *) element);
281  break;
282 
283  case T_Constraint:
284  transformTableConstraint(&cxt, (Constraint *) element);
285  break;
286 
287  case T_TableLikeClause:
288  like_found = true;
289  transformTableLikeClause(&cxt, (TableLikeClause *) element);
290  break;
291 
292  default:
293  elog(ERROR, "unrecognized node type: %d",
294  (int) nodeTag(element));
295  break;
296  }
297  }
298 
299  /*
300  * If we had any LIKE tables, they may require creation of an OID column
301  * even though the command's own WITH clause didn't ask for one (or,
302  * perhaps, even specifically rejected having one). Insert a WITH option
303  * to ensure that happens. We prepend to the list because the first oid
304  * option will be honored, and we want to override anything already there.
305  * (But note that DefineRelation will override this again to add an OID
306  * column if one appears in an inheritance parent table.)
307  */
308  if (like_found && cxt.hasoids)
309  stmt->options = lcons(makeDefElem("oids",
310  (Node *) makeInteger(true), -1),
311  stmt->options);
312 
313  /*
314  * transformIndexConstraints wants cxt.alist to contain only index
315  * statements, so transfer anything we already have into save_alist.
316  */
317  save_alist = cxt.alist;
318  cxt.alist = NIL;
319 
320  Assert(stmt->constraints == NIL);
321 
322  /*
323  * Postprocess constraints that give rise to index definitions.
324  */
326 
327  /*
328  * Postprocess foreign-key constraints.
329  */
330  transformFKConstraints(&cxt, true, false);
331 
332  /*
333  * Postprocess check constraints.
334  */
335  transformCheckConstraints(&cxt, !is_foreign_table ? true : false);
336 
337  /*
338  * Output results.
339  */
340  stmt->tableElts = cxt.columns;
341  stmt->constraints = cxt.ckconstraints;
342 
343  result = lappend(cxt.blist, stmt);
344  result = list_concat(result, cxt.alist);
345  result = list_concat(result, save_alist);
346 
347  return result;
348 }
RangeVar * relation
Definition: parsenodes.h:2010
#define NIL
Definition: pg_list.h:69
List * inhRelations
Definition: parsenodes.h:2012
#define IsA(nodeptr, _type_)
Definition: nodes.h:561
Definition: nodes.h:510
int errcode(int sqlerrcode)
Definition: elog.c:575
List * list_concat(List *list1, List *list2)
Definition: list.c:321
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:576
DefElem * makeDefElem(char *name, Node *arg, int location)
Definition: makefuncs.c:546
char * schemaname
Definition: primnodes.h:67
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:44
List * constraints
Definition: parsenodes.h:2017
bool if_not_exists
Definition: parsenodes.h:2021
int location
Definition: primnodes.h:73
ParseState * pstate
Definition: parse_utilcmd.c:75
PartitionBoundSpec * partbound
Definition: parsenodes.h:2014
char * relname
Definition: primnodes.h:68
Value * makeInteger(long i)
Definition: value.c:23
RangeVar * relation
Definition: parse_utilcmd.c:77
IndexStmt * pkey
Definition: parse_utilcmd.c:92
void cancel_parser_errposition_callback(ParseCallbackState *pcbstate)
Definition: parse_node.c:161
#define ERROR
Definition: elog.h:43
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3047
#define NoLock
Definition: lockdefs.h:34
List * options
Definition: parsenodes.h:2018
const char * p_sourcetext
Definition: parse_node.h:171
#define ereport(elevel, rest)
Definition: elog.h:122
void setup_parser_errposition_callback(ParseCallbackState *pcbstate, ParseState *pstate, int location)
Definition: parse_node.c:145
List * lappend(List *list, void *datum)
Definition: list.c:128
static chr element(struct vars *v, const chr *startp, const chr *endp)
Definition: regc_locale.c:380
Oid RangeVarGetAndCheckCreationNamespace(RangeVar *relation, LOCKMODE lockmode, Oid *existing_relation_id)
Definition: namespace.c:508
#define NOTICE
Definition: elog.h:37
List * tableElts
Definition: parsenodes.h:2011
List * lcons(void *datum, List *list)
Definition: list.c:259
static void transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
#define Assert(condition)
Definition: c.h:670
#define lfirst(lc)
Definition: pg_list.h:106
static void transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
static void transformFKConstraints(CreateStmtContext *cxt, bool skipValidation, bool isAddConstraint)
#define nodeTag(nodeptr)
Definition: nodes.h:515
const char * stmtType
Definition: parse_utilcmd.c:76
char relpersistence
Definition: primnodes.h:71
bool interpretOidsOption(List *defList, bool allowOids)
Definition: parse_clause.c:255
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define elog
Definition: elog.h:219
#define copyObject(obj)
Definition: nodes.h:623
#define RELPERSISTENCE_TEMP
Definition: pg_class.h:172
static void transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
static void transformIndexConstraints(CreateStmtContext *cxt)
PartitionSpec * partspec
Definition: parsenodes.h:2015
Definition: pg_list.h:45
static void transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_clause)
TypeName * ofTypename
Definition: parsenodes.h:2016
static void transformCheckConstraints(CreateStmtContext *cxt, bool skipValidation)

◆ transformIndexStmt()

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

Definition at line 2229 of file parse_utilcmd.c.

References addRangeTableEntryForRelation(), addRTEtoQuery(), assign_expr_collations(), copyObject, ereport, errcode(), errmsg(), ERROR, IndexElem::expr, EXPR_KIND_INDEX_EXPRESSION, EXPR_KIND_INDEX_PREDICATE, FigureIndexColname(), free_parsestate(), heap_close, IndexElem::indexcolname, IndexStmt::indexParams, lfirst, list_length(), make_parsestate(), NoLock, ParseState::p_rtable, ParseState::p_sourcetext, relation_open(), IndexStmt::transformed, transformExpr(), transformWhereClause(), and IndexStmt::whereClause.

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

2230 {
2231  ParseState *pstate;
2232  RangeTblEntry *rte;
2233  ListCell *l;
2234  Relation rel;
2235 
2236  /* Nothing to do if statement already transformed. */
2237  if (stmt->transformed)
2238  return stmt;
2239 
2240  /*
2241  * We must not scribble on the passed-in IndexStmt, so copy it. (This is
2242  * overkill, but easy.)
2243  */
2244  stmt = copyObject(stmt);
2245 
2246  /* Set up pstate */
2247  pstate = make_parsestate(NULL);
2248  pstate->p_sourcetext = queryString;
2249 
2250  /*
2251  * Put the parent table into the rtable so that the expressions can refer
2252  * to its fields without qualification. Caller is responsible for locking
2253  * relation, but we still need to open it.
2254  */
2255  rel = relation_open(relid, NoLock);
2256  rte = addRangeTableEntryForRelation(pstate, rel, NULL, false, true);
2257 
2258  /* no to join list, yes to namespaces */
2259  addRTEtoQuery(pstate, rte, false, true, true);
2260 
2261  /* take care of the where clause */
2262  if (stmt->whereClause)
2263  {
2264  stmt->whereClause = transformWhereClause(pstate,
2265  stmt->whereClause,
2267  "WHERE");
2268  /* we have to fix its collations too */
2269  assign_expr_collations(pstate, stmt->whereClause);
2270  }
2271 
2272  /* take care of any index expressions */
2273  foreach(l, stmt->indexParams)
2274  {
2275  IndexElem *ielem = (IndexElem *) lfirst(l);
2276 
2277  if (ielem->expr)
2278  {
2279  /* Extract preliminary index col name before transforming expr */
2280  if (ielem->indexcolname == NULL)
2281  ielem->indexcolname = FigureIndexColname(ielem->expr);
2282 
2283  /* Now do parse transformation of the expression */
2284  ielem->expr = transformExpr(pstate, ielem->expr,
2286 
2287  /* We have to fix its collations too */
2288  assign_expr_collations(pstate, ielem->expr);
2289 
2290  /*
2291  * transformExpr() should have already rejected subqueries,
2292  * aggregates, window functions, and SRFs, based on the EXPR_KIND_
2293  * for an index expression.
2294  *
2295  * DefineIndex() will make more checks.
2296  */
2297  }
2298  }
2299 
2300  /*
2301  * Check that only the base rel is mentioned. (This should be dead code
2302  * now that add_missing_from is history.)
2303  */
2304  if (list_length(pstate->p_rtable) != 1)
2305  ereport(ERROR,
2306  (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
2307  errmsg("index expressions and predicates can refer only to the table being indexed")));
2308 
2309  free_parsestate(pstate);
2310 
2311  /* Close relation */
2312  heap_close(rel, NoLock);
2313 
2314  /* Mark statement as successfully transformed */
2315  stmt->transformed = true;
2316 
2317  return stmt;
2318 }
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:146
Node * whereClause
Definition: parsenodes.h:2712
int errcode(int sqlerrcode)
Definition: elog.c:575
char * FigureIndexColname(Node *node)
#define heap_close(r, l)
Definition: heapam.h:97
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:44
void assign_expr_collations(ParseState *pstate, Node *expr)
Node * expr
Definition: parsenodes.h:689
#define ERROR
Definition: elog.h:43
#define NoLock
Definition: lockdefs.h:34
bool transformed
Definition: parsenodes.h:2722
char * indexcolname
Definition: parsenodes.h:690
const char * p_sourcetext
Definition: parse_node.h:171
#define ereport(elevel, rest)
Definition: elog.h:122
void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
#define lfirst(lc)
Definition: pg_list.h:106
Node * transformWhereClause(ParseState *pstate, Node *clause, ParseExprKind exprKind, const char *constructName)
List * indexParams
Definition: parsenodes.h:2710
static int list_length(const List *l)
Definition: pg_list.h:89
RangeTblEntry * addRangeTableEntryForRelation(ParseState *pstate, Relation rel, Alias *alias, bool inh, bool inFromCl)
int errmsg(const char *fmt,...)
Definition: elog.c:797
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1120
#define copyObject(obj)
Definition: nodes.h:623
void free_parsestate(ParseState *pstate)
Definition: parse_node.c:77
List * p_rtable
Definition: parse_node.h:172

◆ transformPartitionBound()

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

Definition at line 3299 of file parse_utilcmd.c.

References Assert, castNode, Const::constisnull, copyObject, deparse_context_for(), deparse_expression(), elog, equal(), ereport, errcode(), errmsg(), ERROR, exprLocation(), forboth, get_partition_col_typid(), get_partition_col_typmod(), get_partition_exprs(), get_partition_natts(), get_partition_strategy(), get_relid_attribute_name(), i, PartitionBoundSpec::is_default, lappend(), lfirst, linitial, list_length(), list_nth(), PartitionBoundSpec::listdatums, PartitionBoundSpec::lowerdatums, NIL, parser_errposition(), PartitionKeyData::partattrs, PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, RelationGetPartitionKey, RelationGetRelationName, RelationGetRelid, PartitionBoundSpec::strategy, transformPartitionBoundValue(), PartitionBoundSpec::upperdatums, validateInfiniteBounds(), value, and PartitionRangeDatum::value.

Referenced by DefineRelation(), and transformPartitionCmd().

3301 {
3302  PartitionBoundSpec *result_spec;
3303  PartitionKey key = RelationGetPartitionKey(parent);
3304  char strategy = get_partition_strategy(key);
3305  int partnatts = get_partition_natts(key);
3306  List *partexprs = get_partition_exprs(key);
3307 
3308  /* Avoid scribbling on input */
3309  result_spec = copyObject(spec);
3310 
3311  if (spec->is_default)
3312  {
3313  if (strategy == PARTITION_STRATEGY_HASH)
3314  ereport(ERROR,
3315  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3316  errmsg("a hash-partitioned table may not have a default partition")));
3317 
3318  /*
3319  * In case of the default partition, parser had no way to identify the
3320  * partition strategy. Assign the parent's strategy to the default
3321  * partition bound spec.
3322  */
3323  result_spec->strategy = strategy;
3324 
3325  return result_spec;
3326  }
3327 
3328  if (strategy == PARTITION_STRATEGY_HASH)
3329  {
3330  if (spec->strategy != PARTITION_STRATEGY_HASH)
3331  ereport(ERROR,
3332  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3333  errmsg("invalid bound specification for a hash partition"),
3334  parser_errposition(pstate, exprLocation((Node *) spec))));
3335 
3336  if (spec->modulus <= 0)
3337  ereport(ERROR,
3338  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3339  errmsg("modulus for hash partition must be a positive integer")));
3340 
3341  Assert(spec->remainder >= 0);
3342 
3343  if (spec->remainder >= spec->modulus)
3344  ereport(ERROR,
3345  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3346  errmsg("remainder for hash partition must be less than modulus")));
3347  }
3348  else if (strategy == PARTITION_STRATEGY_LIST)
3349  {
3350  ListCell *cell;
3351  char *colname;
3352  Oid coltype;
3353  int32 coltypmod;
3354 
3355  if (spec->strategy != PARTITION_STRATEGY_LIST)
3356  ereport(ERROR,
3357  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3358  errmsg("invalid bound specification for a list partition"),
3359  parser_errposition(pstate, exprLocation((Node *) spec))));
3360 
3361  /* Get the only column's name in case we need to output an error */
3362  if (key->partattrs[0] != 0)
3363  colname = get_relid_attribute_name(RelationGetRelid(parent),
3364  key->partattrs[0]);
3365  else
3366  colname = deparse_expression((Node *) linitial(partexprs),
3368  RelationGetRelid(parent)),
3369  false, false);
3370  /* Need its type data too */
3371  coltype = get_partition_col_typid(key, 0);
3372  coltypmod = get_partition_col_typmod(key, 0);
3373 
3374  result_spec->listdatums = NIL;
3375  foreach(cell, spec->listdatums)
3376  {
3377  A_Const *con = castNode(A_Const, lfirst(cell));
3378  Const *value;
3379  ListCell *cell2;
3380  bool duplicate;
3381 
3382  value = transformPartitionBoundValue(pstate, con,
3383  colname, coltype, coltypmod);
3384 
3385  /* Don't add to the result if the value is a duplicate */
3386  duplicate = false;
3387  foreach(cell2, result_spec->listdatums)
3388  {
3389  Const *value2 = castNode(Const, lfirst(cell2));
3390 
3391  if (equal(value, value2))
3392  {
3393  duplicate = true;
3394  break;
3395  }
3396  }
3397  if (duplicate)
3398  continue;
3399 
3400  result_spec->listdatums = lappend(result_spec->listdatums,
3401  value);
3402  }
3403  }
3404  else if (strategy == PARTITION_STRATEGY_RANGE)
3405  {
3406  ListCell *cell1,
3407  *cell2;
3408  int i,
3409  j;
3410 
3411  if (spec->strategy != PARTITION_STRATEGY_RANGE)
3412  ereport(ERROR,
3413  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3414  errmsg("invalid bound specification for a range partition"),
3415  parser_errposition(pstate, exprLocation((Node *) spec))));
3416 
3417  if (list_length(spec->lowerdatums) != partnatts)
3418  ereport(ERROR,
3419  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3420  errmsg("FROM must specify exactly one value per partitioning column")));
3421  if (list_length(spec->upperdatums) != partnatts)
3422  ereport(ERROR,
3423  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3424  errmsg("TO must specify exactly one value per partitioning column")));
3425 
3426  /*
3427  * Once we see MINVALUE or MAXVALUE for one column, the remaining
3428  * columns must be the same.
3429  */
3430  validateInfiniteBounds(pstate, spec->lowerdatums);
3431  validateInfiniteBounds(pstate, spec->upperdatums);
3432 
3433  /* Transform all the constants */
3434  i = j = 0;
3435  result_spec->lowerdatums = result_spec->upperdatums = NIL;
3436  forboth(cell1, spec->lowerdatums, cell2, spec->upperdatums)
3437  {
3438  PartitionRangeDatum *ldatum = (PartitionRangeDatum *) lfirst(cell1);
3439  PartitionRangeDatum *rdatum = (PartitionRangeDatum *) lfirst(cell2);
3440  char *colname;
3441  Oid coltype;
3442  int32 coltypmod;
3443  A_Const *con;
3444  Const *value;
3445 
3446  /* Get the column's name in case we need to output an error */
3447  if (key->partattrs[i] != 0)
3448  colname = get_relid_attribute_name(RelationGetRelid(parent),
3449  key->partattrs[i]);
3450  else
3451  {
3452  colname = deparse_expression((Node *) list_nth(partexprs, j),
3454  RelationGetRelid(parent)),
3455  false, false);
3456  ++j;
3457  }
3458  /* Need its type data too */
3459  coltype = get_partition_col_typid(key, i);
3460  coltypmod = get_partition_col_typmod(key, i);
3461 
3462  if (ldatum->value)
3463  {
3464  con = castNode(A_Const, ldatum->value);
3465  value = transformPartitionBoundValue(pstate, con,
3466  colname,
3467  coltype, coltypmod);
3468  if (value->constisnull)
3469  ereport(ERROR,
3470  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3471  errmsg("cannot specify NULL in range bound")));
3472  ldatum = copyObject(ldatum); /* don't scribble on input */
3473  ldatum->value = (Node *) value;
3474  }
3475 
3476  if (rdatum->value)
3477  {
3478  con = castNode(A_Const, rdatum->value);
3479  value = transformPartitionBoundValue(pstate, con,
3480  colname,
3481  coltype, coltypmod);
3482  if (value->constisnull)
3483  ereport(ERROR,
3484  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3485  errmsg("cannot specify NULL in range bound")));
3486  rdatum = copyObject(rdatum); /* don't scribble on input */
3487  rdatum->value = (Node *) value;
3488  }
3489 
3490  result_spec->lowerdatums = lappend(result_spec->lowerdatums,
3491  ldatum);
3492  result_spec->upperdatums = lappend(result_spec->upperdatums,
3493  rdatum);
3494 
3495  ++i;
3496  }
3497  }
3498  else
3499  elog(ERROR, "unexpected partition strategy: %d", (int) strategy);
3500 
3501  return result_spec;
3502 }
#define NIL
Definition: pg_list.h:69
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:180
int exprLocation(const Node *expr)
Definition: nodeFuncs.c:1186
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:2974
#define castNode(_type_, nodeptr)
Definition: nodes.h:579
Definition: nodes.h:510
int errcode(int sqlerrcode)
Definition: elog.c:575
static Const * transformPartitionBoundValue(ParseState *pstate, A_Const *con, const char *colName, Oid colType, int32 colTypmod)
unsigned int Oid
Definition: postgres_ext.h:31
signed int int32
Definition: c.h:284
static int get_partition_natts(PartitionKey key)
Definition: rel.h:605
#define linitial(l)
Definition: pg_list.h:111
#define ERROR
Definition: elog.h:43
static struct @121 value
static void validateInfiniteBounds(ParseState *pstate, List *blist)
void * list_nth(const List *list, int n)
Definition: list.c:410
static List * get_partition_exprs(PartitionKey key)
Definition: rel.h:611
List * deparse_context_for(const char *aliasname, Oid relid)
Definition: ruleutils.c:3061
#define RelationGetRelationName(relation)
Definition: rel.h:445
#define ereport(elevel, rest)
Definition: elog.h:122
List * lappend(List *list, void *datum)
Definition: list.c:128
static int32 get_partition_col_typmod(PartitionKey key, int col)
Definition: rel.h:632
char * get_relid_attribute_name(Oid relid, AttrNumber attnum)
Definition: lsyscache.c:801
AttrNumber * partattrs
Definition: rel.h:56
static Oid get_partition_col_typid(PartitionKey key, int col)
Definition: rel.h:626
static int get_partition_strategy(PartitionKey key)
Definition: rel.h:599
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:787
#define Assert(condition)
Definition: c.h:670
#define lfirst(lc)
Definition: pg_list.h:106
char * deparse_expression(Node *expr, List *dpcontext, bool forceprefix, bool showimplicit)
Definition: ruleutils.c:3002
static int list_length(const List *l)
Definition: pg_list.h:89
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:111
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:788
#define RelationGetPartitionKey(relation)
Definition: rel.h:593
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:789
int errmsg(const char *fmt,...)
Definition: elog.c:797
int i
#define elog
Definition: elog.h:219
#define copyObject(obj)
Definition: nodes.h:623
Definition: pg_list.h:45
#define RelationGetRelid(relation)
Definition: rel.h:425
bool constisnull
Definition: primnodes.h:197

◆ transformRuleStmt()

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

Definition at line 2334 of file parse_utilcmd.c.

References AccessExclusiveLock, generate_unaccent_rules::action, RuleStmt::actions, addRangeTableEntryForRelation(), addRTEtoQuery(), assign_expr_collations(), CMD_DELETE, CMD_INSERT, CMD_NOTHING, CMD_SELECT, CMD_UPDATE, CMD_UTILITY, Query::commandType, copyObject, Query::cteList, elog, ereport, errcode(), errmsg(), ERROR, RuleStmt::event, EXPR_KIND_WHERE, free_parsestate(), getInsertSelectQuery(), heap_close, heap_openrv(), Query::jointree, lappend(), lfirst, list_length(), list_make1, make_parsestate(), makeAlias(), makeFromExpr(), makeNode, NIL, NoLock, ParseState::p_joinlist, ParseState::p_rtable, ParseState::p_sourcetext, PRS2_NEW_VARNO, PRS2_OLD_VARNO, rangeTableEntry_used(), RelationData::rd_rel, RuleStmt::relation, RELKIND_MATVIEW, RangeTblEntry::requiredPerms, Query::rtable, Query::setOperations, transformStmt(), transformWhereClause(), and RuleStmt::whereClause.

Referenced by DefineRule().

2336 {
2337  Relation rel;
2338  ParseState *pstate;
2339  RangeTblEntry *oldrte;
2340  RangeTblEntry *newrte;
2341 
2342  /*
2343  * To avoid deadlock, make sure the first thing we do is grab
2344  * AccessExclusiveLock on the target relation. This will be needed by
2345  * DefineQueryRewrite(), and we don't want to grab a lesser lock
2346  * beforehand.
2347  */
2349 
2350  if (rel->rd_rel->relkind == RELKIND_MATVIEW)
2351  ereport(ERROR,
2352  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2353  errmsg("rules on materialized views are not supported")));
2354 
2355  /* Set up pstate */
2356  pstate = make_parsestate(NULL);
2357  pstate->p_sourcetext = queryString;
2358 
2359  /*
2360  * NOTE: 'OLD' must always have a varno equal to 1 and 'NEW' equal to 2.
2361  * Set up their RTEs in the main pstate for use in parsing the rule
2362  * qualification.
2363  */
2364  oldrte = addRangeTableEntryForRelation(pstate, rel,
2365  makeAlias("old", NIL),
2366  false, false);
2367  newrte = addRangeTableEntryForRelation(pstate, rel,
2368  makeAlias("new", NIL),
2369  false, false);
2370  /* Must override addRangeTableEntry's default access-check flags */
2371  oldrte->requiredPerms = 0;
2372  newrte->requiredPerms = 0;
2373 
2374  /*
2375  * They must be in the namespace too for lookup purposes, but only add the
2376  * one(s) that are relevant for the current kind of rule. In an UPDATE
2377  * rule, quals must refer to OLD.field or NEW.field to be unambiguous, but
2378  * there's no need to be so picky for INSERT & DELETE. We do not add them
2379  * to the joinlist.
2380  */
2381  switch (stmt->event)
2382  {
2383  case CMD_SELECT:
2384  addRTEtoQuery(pstate, oldrte, false, true, true);
2385  break;
2386  case CMD_UPDATE:
2387  addRTEtoQuery(pstate, oldrte, false, true, true);
2388  addRTEtoQuery(pstate, newrte, false, true, true);
2389  break;
2390  case CMD_INSERT:
2391  addRTEtoQuery(pstate, newrte, false, true, true);
2392  break;
2393  case CMD_DELETE:
2394  addRTEtoQuery(pstate, oldrte, false, true, true);
2395  break;
2396  default:
2397  elog(ERROR, "unrecognized event type: %d",
2398  (int) stmt->event);
2399  break;
2400  }
2401 
2402  /* take care of the where clause */
2403  *whereClause = transformWhereClause(pstate,
2404  (Node *) copyObject(stmt->whereClause),
2406  "WHERE");
2407  /* we have to fix its collations too */
2408  assign_expr_collations(pstate, *whereClause);
2409 
2410  /* this is probably dead code without add_missing_from: */
2411  if (list_length(pstate->p_rtable) != 2) /* naughty, naughty... */
2412  ereport(ERROR,
2413  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2414  errmsg("rule WHERE condition cannot contain references to other relations")));
2415 
2416  /*
2417  * 'instead nothing' rules with a qualification need a query rangetable so
2418  * the rewrite handler can add the negated rule qualification to the
2419  * original query. We create a query with the new command type CMD_NOTHING
2420  * here that is treated specially by the rewrite system.
2421  */
2422  if (stmt->actions == NIL)
2423  {
2424  Query *nothing_qry = makeNode(Query);
2425 
2426  nothing_qry->commandType = CMD_NOTHING;
2427  nothing_qry->rtable = pstate->p_rtable;
2428  nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */
2429 
2430  *actions = list_make1(nothing_qry);
2431  }
2432  else
2433  {
2434  ListCell *l;
2435  List *newactions = NIL;
2436 
2437  /*
2438  * transform each statement, like parse_sub_analyze()
2439  */
2440  foreach(l, stmt->actions)
2441  {
2442  Node *action = (Node *) lfirst(l);
2443  ParseState *sub_pstate = make_parsestate(NULL);
2444  Query *sub_qry,
2445  *top_subqry;
2446  bool has_old,
2447  has_new;
2448 
2449  /*
2450  * Since outer ParseState isn't parent of inner, have to pass down
2451  * the query text by hand.
2452  */
2453  sub_pstate->p_sourcetext = queryString;
2454 
2455  /*
2456  * Set up OLD/NEW in the rtable for this statement. The entries
2457  * are added only to relnamespace, not varnamespace, because we
2458  * don't want them to be referred to by unqualified field names
2459  * nor "*" in the rule actions. We decide later whether to put
2460  * them in the joinlist.
2461  */
2462  oldrte = addRangeTableEntryForRelation(sub_pstate, rel,
2463  makeAlias("old", NIL),
2464  false, false);
2465  newrte = addRangeTableEntryForRelation(sub_pstate, rel,
2466  makeAlias("new", NIL),
2467  false, false);
2468  oldrte->requiredPerms = 0;
2469  newrte->requiredPerms = 0;
2470  addRTEtoQuery(sub_pstate, oldrte, false, true, false);
2471  addRTEtoQuery(sub_pstate, newrte, false, true, false);
2472 
2473  /* Transform the rule action statement */
2474  top_subqry = transformStmt(sub_pstate,
2475  (Node *) copyObject(action));
2476 
2477  /*
2478  * We cannot support utility-statement actions (eg NOTIFY) with
2479  * nonempty rule WHERE conditions, because there's no way to make
2480  * the utility action execute conditionally.
2481  */
2482  if (top_subqry->commandType == CMD_UTILITY &&
2483  *whereClause != NULL)
2484  ereport(ERROR,
2485  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2486  errmsg("rules with WHERE conditions can only have SELECT, INSERT, UPDATE, or DELETE actions")));
2487 
2488  /*
2489  * If the action is INSERT...SELECT, OLD/NEW have been pushed down
2490  * into the SELECT, and that's what we need to look at. (Ugly
2491  * kluge ... try to fix this when we redesign querytrees.)
2492  */
2493  sub_qry = getInsertSelectQuery(top_subqry, NULL);
2494 
2495  /*
2496  * If the sub_qry is a setop, we cannot attach any qualifications
2497  * to it, because the planner won't notice them. This could
2498  * perhaps be relaxed someday, but for now, we may as well reject
2499  * such a rule immediately.
2500  */
2501  if (sub_qry->setOperations != NULL && *whereClause != NULL)
2502  ereport(ERROR,
2503  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2504  errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
2505 
2506  /*
2507  * Validate action's use of OLD/NEW, qual too
2508  */
2509  has_old =
2510  rangeTableEntry_used((Node *) sub_qry, PRS2_OLD_VARNO, 0) ||
2511  rangeTableEntry_used(*whereClause, PRS2_OLD_VARNO, 0);
2512  has_new =
2513  rangeTableEntry_used((Node *) sub_qry, PRS2_NEW_VARNO, 0) ||
2514  rangeTableEntry_used(*whereClause, PRS2_NEW_VARNO, 0);
2515 
2516  switch (stmt->event)
2517  {
2518  case CMD_SELECT:
2519  if (has_old)
2520  ereport(ERROR,
2521  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2522  errmsg("ON SELECT rule cannot use OLD")));
2523  if (has_new)
2524  ereport(ERROR,
2525  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2526  errmsg("ON SELECT rule cannot use NEW")));
2527  break;
2528  case CMD_UPDATE:
2529  /* both are OK */
2530  break;
2531  case CMD_INSERT:
2532  if (has_old)
2533  ereport(ERROR,
2534  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2535  errmsg("ON INSERT rule cannot use OLD")));
2536  break;
2537  case CMD_DELETE:
2538  if (has_new)
2539  ereport(ERROR,
2540  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2541  errmsg("ON DELETE rule cannot use NEW")));
2542  break;
2543  default:
2544  elog(ERROR, "unrecognized event type: %d",
2545  (int) stmt->event);
2546  break;
2547  }
2548 
2549  /*
2550  * OLD/NEW are not allowed in WITH queries, because they would
2551  * amount to outer references for the WITH, which we disallow.
2552  * However, they were already in the outer rangetable when we
2553  * analyzed the query, so we have to check.
2554  *
2555  * Note that in the INSERT...SELECT case, we need to examine the
2556  * CTE lists of both top_subqry and sub_qry.
2557  *
2558  * Note that we aren't digging into the body of the query looking
2559  * for WITHs in nested sub-SELECTs. A WITH down there can
2560  * legitimately refer to OLD/NEW, because it'd be an
2561  * indirect-correlated outer reference.
2562  */
2563  if (rangeTableEntry_used((Node *) top_subqry->cteList,
2564  PRS2_OLD_VARNO, 0) ||
2565  rangeTableEntry_used((Node *) sub_qry->cteList,
2566  PRS2_OLD_VARNO, 0))
2567  ereport(ERROR,
2568  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2569  errmsg("cannot refer to OLD within WITH query")));
2570  if (rangeTableEntry_used((Node *) top_subqry->cteList,
2571  PRS2_NEW_VARNO, 0) ||
2572  rangeTableEntry_used((Node *) sub_qry->cteList,
2573  PRS2_NEW_VARNO, 0))
2574  ereport(ERROR,
2575  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2576  errmsg("cannot refer to NEW within WITH query")));
2577 
2578  /*
2579  * For efficiency's sake, add OLD to the rule action's jointree
2580  * only if it was actually referenced in the statement or qual.
2581  *
2582  * For INSERT, NEW is not really a relation (only a reference to
2583  * the to-be-inserted tuple) and should never be added to the
2584  * jointree.
2585  *
2586  * For UPDATE, we treat NEW as being another kind of reference to
2587  * OLD, because it represents references to *transformed* tuples
2588  * of the existing relation. It would be wrong to enter NEW
2589  * separately in the jointree, since that would cause a double
2590  * join of the updated relation. It's also wrong to fail to make
2591  * a jointree entry if only NEW and not OLD is mentioned.
2592  */
2593  if (has_old || (has_new && stmt->event == CMD_UPDATE))
2594  {
2595  /*
2596  * If sub_qry is a setop, manipulating its jointree will do no
2597  * good at all, because the jointree is dummy. (This should be
2598  * a can't-happen case because of prior tests.)
2599  */
2600  if (sub_qry->setOperations != NULL)
2601  ereport(ERROR,
2602  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2603  errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
2604  /* hack so we can use addRTEtoQuery() */
2605  sub_pstate->p_rtable = sub_qry->rtable;
2606  sub_pstate->p_joinlist = sub_qry->jointree->fromlist;
2607  addRTEtoQuery(sub_pstate, oldrte, true, false, false);
2608  sub_qry->jointree->fromlist = sub_pstate->p_joinlist;
2609  }
2610 
2611  newactions = lappend(newactions, top_subqry);
2612 
2613  free_parsestate(sub_pstate);
2614  }
2615 
2616  *actions = newactions;
2617  }
2618 
2619  free_parsestate(pstate);
2620 
2621  /* Close relation, but keep the exclusive lock */
2622  heap_close(rel, NoLock);
2623 }
#define NIL
Definition: pg_list.h:69
Query * getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr)
Definition: rewriteManip.c:921
FromExpr * makeFromExpr(List *fromlist, Node *quals)
Definition: makefuncs.c:284
FromExpr * jointree
Definition: parsenodes.h:136
#define RELKIND_MATVIEW
Definition: pg_class.h:165
Definition: nodes.h:510
int errcode(int sqlerrcode)
Definition: elog.c:575
List * actions
Definition: parsenodes.h:2885
AclMode requiredPerms
Definition: parsenodes.h:1059
List * fromlist
Definition: primnodes.h:1478
#define heap_close(r, l)
Definition: heapam.h:97
Form_pg_class rd_rel
Definition: rel.h:114
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:44
#define list_make1(x1)
Definition: pg_list.h:139
void assign_expr_collations(ParseState *pstate, Node *expr)
Alias * makeAlias(const char *aliasname, List *colnames)
Definition: makefuncs.c:386
List * rtable
Definition: parsenodes.h:135
#define ERROR
Definition: elog.h:43
#define NoLock
Definition: lockdefs.h:34
const char * p_sourcetext
Definition: parse_node.h:171
#define ereport(elevel, rest)
Definition: elog.h:122
void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
Query * transformStmt(ParseState *pstate, Node *parseTree)
Definition: analyze.c:250
List * lappend(List *list, void *datum)
Definition: list.c:128
#define PRS2_OLD_VARNO
Definition: primnodes.h:160
Relation heap_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition: heapam.c:1318
CmdType commandType
Definition: parsenodes.h:110
#define makeNode(_type_)
Definition: nodes.h:558
CmdType event
Definition: parsenodes.h:2883
#define lfirst(lc)
Definition: pg_list.h:106
Node * transformWhereClause(ParseState *pstate, Node *clause, ParseExprKind exprKind, const char *constructName)
Node * whereClause
Definition: parsenodes.h:2882
static int list_length(const List *l)
Definition: pg_list.h:89
RangeTblEntry * addRangeTableEntryForRelation(ParseState *pstate, Relation rel, Alias *alias, bool inh, bool inFromCl)
#define AccessExclusiveLock
Definition: lockdefs.h:45
List * cteList
Definition: parsenodes.h:133
Node * setOperations
Definition: parsenodes.h:163
int errmsg(const char *fmt,...)
Definition: elog.c:797
List * p_joinlist
Definition: parse_node.h:174
bool rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
Definition: rewriteManip.c:889
#define elog
Definition: elog.h:219
#define copyObject(obj)
Definition: nodes.h:623
RangeVar * relation
Definition: parsenodes.h:2880
void free_parsestate(ParseState *pstate)
Definition: parse_node.c:77
Definition: pg_list.h:45
#define PRS2_NEW_VARNO
Definition: primnodes.h:161
List * p_rtable
Definition: parse_node.h:172