PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
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

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

Definition at line 2614 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, NULL, 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, result, AlterSeqStmt::sequence, CreateStmtContext::stmtType, AlterTableCmd::subtype, transformCheckConstraints(), transformColumnDefinition(), transformExpr(), transformFKConstraints(), transformIndexConstraints(), transformIndexStmt(), transformPartitionCmd(), transformTableConstraint(), ColumnDef::typeName, and typenameTypeId().

Referenced by ATPostAlterTypeParse(), and ProcessUtilitySlow().

2616 {
2617  Relation rel;
2618  ParseState *pstate;
2619  CreateStmtContext cxt;
2620  List *result;
2621  List *save_alist;
2622  ListCell *lcmd,
2623  *l;
2624  List *newcmds = NIL;
2625  bool skipValidation = true;
2626  AlterTableCmd *newcmd;
2627  RangeTblEntry *rte;
2628 
2629  /*
2630  * We must not scribble on the passed-in AlterTableStmt, so copy it. (This
2631  * is overkill, but easy.)
2632  */
2633  stmt = copyObject(stmt);
2634 
2635  /* Caller is responsible for locking the relation */
2636  rel = relation_open(relid, NoLock);
2637 
2638  /* Set up pstate */
2639  pstate = make_parsestate(NULL);
2640  pstate->p_sourcetext = queryString;
2641  rte = addRangeTableEntryForRelation(pstate,
2642  rel,
2643  NULL,
2644  false,
2645  true);
2646  addRTEtoQuery(pstate, rte, false, true, true);
2647 
2648  /* Set up CreateStmtContext */
2649  cxt.pstate = pstate;
2650  if (stmt->relkind == OBJECT_FOREIGN_TABLE)
2651  {
2652  cxt.stmtType = "ALTER FOREIGN TABLE";
2653  cxt.isforeign = true;
2654  }
2655  else
2656  {
2657  cxt.stmtType = "ALTER TABLE";
2658  cxt.isforeign = false;
2659  }
2660  cxt.relation = stmt->relation;
2661  cxt.rel = rel;
2662  cxt.inhRelations = NIL;
2663  cxt.isalter = true;
2664  cxt.hasoids = false; /* need not be right */
2665  cxt.columns = NIL;
2666  cxt.ckconstraints = NIL;
2667  cxt.fkconstraints = NIL;
2668  cxt.ixconstraints = NIL;
2669  cxt.inh_indexes = NIL;
2670  cxt.blist = NIL;
2671  cxt.alist = NIL;
2672  cxt.pkey = NULL;
2673  cxt.ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
2674  cxt.partbound = NULL;
2675 
2676  /*
2677  * The only subtypes that currently require parse transformation handling
2678  * are ADD COLUMN, ADD CONSTRAINT and SET DATA TYPE. These largely re-use
2679  * code from CREATE TABLE.
2680  */
2681  foreach(lcmd, stmt->cmds)
2682  {
2683  AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
2684 
2685  switch (cmd->subtype)
2686  {
2687  case AT_AddColumn:
2688  case AT_AddColumnToView:
2689  {
2690  ColumnDef *def = castNode(ColumnDef, cmd->def);
2691 
2692  transformColumnDefinition(&cxt, def);
2693 
2694  /*
2695  * If the column has a non-null default, we can't skip
2696  * validation of foreign keys.
2697  */
2698  if (def->raw_default != NULL)
2699  skipValidation = false;
2700 
2701  /*
2702  * All constraints are processed in other ways. Remove the
2703  * original list
2704  */
2705  def->constraints = NIL;
2706 
2707  newcmds = lappend(newcmds, cmd);
2708  break;
2709  }
2710 
2711  case AT_AddConstraint:
2712 
2713  /*
2714  * The original AddConstraint cmd node doesn't go to newcmds
2715  */
2716  if (IsA(cmd->def, Constraint))
2717  {
2718  transformTableConstraint(&cxt, (Constraint *) cmd->def);
2719  if (((Constraint *) cmd->def)->contype == CONSTR_FOREIGN)
2720  skipValidation = false;
2721  }
2722  else
2723  elog(ERROR, "unrecognized node type: %d",
2724  (int) nodeTag(cmd->def));
2725  break;
2726 
2728 
2729  /*
2730  * Already-transformed ADD CONSTRAINT, so just make it look
2731  * like the standard case.
2732  */
2733  cmd->subtype = AT_AddConstraint;
2734  newcmds = lappend(newcmds, cmd);
2735  break;
2736 
2737  case AT_AlterColumnType:
2738  {
2739  ColumnDef *def = (ColumnDef *) cmd->def;
2740  AttrNumber attnum;
2741 
2742  /*
2743  * For ALTER COLUMN TYPE, transform the USING clause if
2744  * one was specified.
2745  */
2746  if (def->raw_default)
2747  {
2748  def->cooked_default =
2749  transformExpr(pstate, def->raw_default,
2751  }
2752 
2753  /*
2754  * For identity column, create ALTER SEQUENCE command to
2755  * change the data type of the sequence.
2756  */
2757  attnum = get_attnum(relid, cmd->name);
2758 
2759  /*
2760  * if attribute not found, something will error about it
2761  * later
2762  */
2763  if (attnum != InvalidAttrNumber && get_attidentity(relid, attnum))
2764  {
2765  Oid seq_relid = getOwnedSequence(relid, attnum);
2766  Oid typeOid = typenameTypeId(pstate, def->typeName);
2767  AlterSeqStmt *altseqstmt = makeNode(AlterSeqStmt);
2768 
2769  altseqstmt->sequence = makeRangeVar(get_namespace_name(get_rel_namespace(seq_relid)),
2770  get_rel_name(seq_relid),
2771  -1);
2772  altseqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(typeOid, -1), -1));
2773  altseqstmt->for_identity = true;
2774  cxt.blist = lappend(cxt.blist, altseqstmt);
2775  }
2776 
2777  newcmds = lappend(newcmds, cmd);
2778  break;
2779  }
2780 
2781  case AT_AddIdentity:
2782  {
2783  Constraint *def = castNode(Constraint, cmd->def);
2784  ColumnDef *newdef = makeNode(ColumnDef);
2785  AttrNumber attnum;
2786 
2787  newdef->colname = cmd->name;
2788  newdef->identity = def->generated_when;
2789  cmd->def = (Node *) newdef;
2790 
2791  attnum = get_attnum(relid, cmd->name);
2792 
2793  /*
2794  * if attribute not found, something will error about it
2795  * later
2796  */
2797  if (attnum != InvalidAttrNumber)
2798  generateSerialExtraStmts(&cxt, newdef,
2799  get_atttype(relid, attnum),
2800  def->options, true,
2801  NULL, NULL);
2802 
2803  newcmds = lappend(newcmds, cmd);
2804  break;
2805  }
2806 
2807  case AT_SetIdentity:
2808  {
2809  /*
2810  * Create an ALTER SEQUENCE statement for the internal
2811  * sequence of the identity column.
2812  */
2813  ListCell *lc;
2814  List *newseqopts = NIL;
2815  List *newdef = NIL;
2816  List *seqlist;
2817  AttrNumber attnum;
2818 
2819  /*
2820  * Split options into those handled by ALTER SEQUENCE and
2821  * those for ALTER TABLE proper.
2822  */
2823  foreach(lc, castNode(List, cmd->def))
2824  {
2825  DefElem *def = lfirst_node(DefElem, lc);
2826 
2827  if (strcmp(def->defname, "generated") == 0)
2828  newdef = lappend(newdef, def);
2829  else
2830  newseqopts = lappend(newseqopts, def);
2831  }
2832 
2833  attnum = get_attnum(relid, cmd->name);
2834 
2835  if (attnum)
2836  {
2837  seqlist = getOwnedSequences(relid, attnum);
2838  if (seqlist)
2839  {
2840  AlterSeqStmt *seqstmt;
2841  Oid seq_relid;
2842 
2843  seqstmt = makeNode(AlterSeqStmt);
2844  seq_relid = linitial_oid(seqlist);
2846  get_rel_name(seq_relid), -1);
2847  seqstmt->options = newseqopts;
2848  seqstmt->for_identity = true;
2849  seqstmt->missing_ok = false;
2850 
2851  cxt.alist = lappend(cxt.alist, seqstmt);
2852  }
2853  }
2854 
2855  /*
2856  * If column was not found or was not an identity column,
2857  * we just let the ALTER TABLE command error out later.
2858  */
2859 
2860  cmd->def = (Node *) newdef;
2861  newcmds = lappend(newcmds, cmd);
2862  break;
2863  }
2864 
2865  case AT_AttachPartition:
2866  case AT_DetachPartition:
2867  {
2868  PartitionCmd *partcmd = (PartitionCmd *) cmd->def;
2869 
2870  transformPartitionCmd(&cxt, partcmd);
2871  /* assign transformed value of the partition bound */
2872  partcmd->bound = cxt.partbound;
2873  }
2874 
2875  newcmds = lappend(newcmds, cmd);
2876  break;
2877 
2878  default:
2879  newcmds = lappend(newcmds, cmd);
2880  break;
2881  }
2882  }
2883 
2884  /*
2885  * transformIndexConstraints wants cxt.alist to contain only index
2886  * statements, so transfer anything we already have into save_alist
2887  * immediately.
2888  */
2889  save_alist = cxt.alist;
2890  cxt.alist = NIL;
2891 
2892  /* Postprocess constraints */
2894  transformFKConstraints(&cxt, skipValidation, true);
2895  transformCheckConstraints(&cxt, false);
2896 
2897  /*
2898  * Push any index-creation commands into the ALTER, so that they can be
2899  * scheduled nicely by tablecmds.c. Note that tablecmds.c assumes that
2900  * the IndexStmt attached to an AT_AddIndex or AT_AddIndexConstraint
2901  * subcommand has already been through transformIndexStmt.
2902  */
2903  foreach(l, cxt.alist)
2904  {
2905  IndexStmt *idxstmt = lfirst_node(IndexStmt, l);
2906 
2907  idxstmt = transformIndexStmt(relid, idxstmt, queryString);
2908  newcmd = makeNode(AlterTableCmd);
2910  newcmd->def = (Node *) idxstmt;
2911  newcmds = lappend(newcmds, newcmd);
2912  }
2913  cxt.alist = NIL;
2914 
2915  /* Append any CHECK or FK constraints to the commands list */
2916  foreach(l, cxt.ckconstraints)
2917  {
2918  newcmd = makeNode(AlterTableCmd);
2919  newcmd->subtype = AT_AddConstraint;
2920  newcmd->def = (Node *) lfirst(l);
2921  newcmds = lappend(newcmds, newcmd);
2922  }
2923  foreach(l, cxt.fkconstraints)
2924  {
2925  newcmd = makeNode(AlterTableCmd);
2926  newcmd->subtype = AT_AddConstraint;
2927  newcmd->def = (Node *) lfirst(l);
2928  newcmds = lappend(newcmds, newcmd);
2929  }
2930 
2931  /* Close rel */
2932  relation_close(rel, NoLock);
2933 
2934  /*
2935  * Output results.
2936  */
2937  stmt->cmds = newcmds;
2938 
2939  result = lappend(cxt.blist, stmt);
2940  result = list_concat(result, cxt.alist);
2941  result = list_concat(result, save_alist);
2942 
2943  return result;
2944 }
#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:560
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:2066
#define castNode(_type_, nodeptr)
Definition: nodes.h:578
char identity
Definition: parsenodes.h:650
List * constraints
Definition: parsenodes.h:653
Oid get_rel_namespace(Oid relid)
Definition: lsyscache.c:1750
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:147
Definition: nodes.h:509
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: heapam.c:1260
AlterTableType subtype
Definition: parsenodes.h:1753
List * list_concat(List *list1, List *list2)
Definition: list.c:321
return result
Definition: formatting.c:1632
Form_pg_class rd_rel
Definition: rel.h:114
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:538
DefElem * makeDefElem(char *name, Node *arg, int location)
Definition: makefuncs.c:544
List * options
Definition: parsenodes.h:2453
List * options
Definition: parsenodes.h:2075
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:649
Oid indexOid
Definition: parsenodes.h:2680
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:3033
#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:168
Oid get_atttype(Oid relid, AttrNumber attnum)
Definition: lsyscache.c:878
ObjectType relkind
Definition: parsenodes.h:1668
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:648
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:469
RangeVar * sequence
Definition: parsenodes.h:2452
#define makeNode(_type_)
Definition: nodes.h:557
#define NULL
Definition: c.h:229
#define lfirst(lc)
Definition: pg_list.h:106
static void transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
PartitionBoundSpec * bound
Definition: parsenodes.h:834
#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:641
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:514
const char * stmtType
Definition: parse_utilcmd.c:76
RangeVar * relation
Definition: parsenodes.h:1666
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1114
bool for_identity
Definition: parsenodes.h:2454
char * defname
Definition: parsenodes.h:720
char * colname
Definition: parsenodes.h:640
#define elog
Definition: elog.h:219
#define copyObject(obj)
Definition: nodes.h:621
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:419
static void transformCheckConstraints(CreateStmtContext *cxt, bool skipValidation)
Oid typenameTypeId(ParseState *pstate, const TypeName *typeName)
Definition: parse_type.c:274
List* transformCreateSchemaStmt ( CreateSchemaStmt stmt)

Definition at line 3125 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, result, 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().

3126 {
3128  List *result;
3129  ListCell *elements;
3130 
3131  cxt.stmtType = "CREATE SCHEMA";
3132  cxt.schemaname = stmt->schemaname;
3133  cxt.authrole = (RoleSpec *) stmt->authrole;
3134  cxt.sequences = NIL;
3135  cxt.tables = NIL;
3136  cxt.views = NIL;
3137  cxt.indexes = NIL;
3138  cxt.triggers = NIL;
3139  cxt.grants = NIL;
3140 
3141  /*
3142  * Run through each schema element in the schema element list. Separate
3143  * statements by type, and do preliminary analysis.
3144  */
3145  foreach(elements, stmt->schemaElts)
3146  {
3147  Node *element = lfirst(elements);
3148 
3149  switch (nodeTag(element))
3150  {
3151  case T_CreateSeqStmt:
3152  {
3153  CreateSeqStmt *elp = (CreateSeqStmt *) element;
3154 
3156  cxt.sequences = lappend(cxt.sequences, element);
3157  }
3158  break;
3159 
3160  case T_CreateStmt:
3161  {
3162  CreateStmt *elp = (CreateStmt *) element;
3163 
3165 
3166  /*
3167  * XXX todo: deal with constraints
3168  */
3169  cxt.tables = lappend(cxt.tables, element);
3170  }
3171  break;
3172 
3173  case T_ViewStmt:
3174  {
3175  ViewStmt *elp = (ViewStmt *) element;
3176 
3177  setSchemaName(cxt.schemaname, &elp->view->schemaname);
3178 
3179  /*
3180  * XXX todo: deal with references between views
3181  */
3182  cxt.views = lappend(cxt.views, element);
3183  }
3184  break;
3185 
3186  case T_IndexStmt:
3187  {
3188  IndexStmt *elp = (IndexStmt *) element;
3189 
3191  cxt.indexes = lappend(cxt.indexes, element);
3192  }
3193  break;
3194 
3195  case T_CreateTrigStmt:
3196  {
3197  CreateTrigStmt *elp = (CreateTrigStmt *) element;
3198 
3200  cxt.triggers = lappend(cxt.triggers, element);
3201  }
3202  break;
3203 
3204  case T_GrantStmt:
3205  cxt.grants = lappend(cxt.grants, element);
3206  break;
3207 
3208  default:
3209  elog(ERROR, "unrecognized node type: %d",
3210  (int) nodeTag(element));
3211  }
3212  }
3213 
3214  result = NIL;
3215  result = list_concat(result, cxt.sequences);
3216  result = list_concat(result, cxt.tables);
3217  result = list_concat(result, cxt.views);
3218  result = list_concat(result, cxt.indexes);
3219  result = list_concat(result, cxt.triggers);
3220  result = list_concat(result, cxt.grants);
3221 
3222  return result;
3223 }
RangeVar * relation
Definition: parsenodes.h:1976
#define NIL
Definition: pg_list.h:69
Definition: nodes.h:509
List * list_concat(List *list1, List *list2)
Definition: list.c:321
return result
Definition: formatting.c:1632
RangeVar * view
Definition: parsenodes.h:2973
char * schemaname
Definition: primnodes.h:67
RangeVar * relation
Definition: parsenodes.h:2672
RoleSpec * authrole
Definition: parsenodes.h:1648
#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:2442
#define lfirst(lc)
Definition: pg_list.h:106
static void setSchemaName(char *context_schema, char **stmt_schema_name)
#define nodeTag(nodeptr)
Definition: nodes.h:514
RangeVar * relation
Definition: parsenodes.h:2327
#define elog
Definition: elog.h:219
Definition: pg_list.h:45
List* transformCreateStmt ( CreateStmt stmt,
const char *  queryString 
)

Definition at line 157 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, NULL, 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, result, 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().

158 {
159  ParseState *pstate;
160  CreateStmtContext cxt;
161  List *result;
162  List *save_alist;
163  ListCell *elements;
164  Oid namespaceid;
165  Oid existing_relid;
166  ParseCallbackState pcbstate;
167  bool like_found = false;
168 
169  /*
170  * We must not scribble on the passed-in CreateStmt, so copy it. (This is
171  * overkill, but easy.)
172  */
173  stmt = copyObject(stmt);
174 
175  /* Set up pstate */
176  pstate = make_parsestate(NULL);
177  pstate->p_sourcetext = queryString;
178 
179  /*
180  * Look up the creation namespace. This also checks permissions on the
181  * target namespace, locks it against concurrent drops, checks for a
182  * preexisting relation in that namespace with the same name, and updates
183  * stmt->relation->relpersistence if the selected namespace is temporary.
184  */
185  setup_parser_errposition_callback(&pcbstate, pstate,
186  stmt->relation->location);
187  namespaceid =
189  &existing_relid);
191 
192  /*
193  * If the relation already exists and the user specified "IF NOT EXISTS",
194  * bail out with a NOTICE.
195  */
196  if (stmt->if_not_exists && OidIsValid(existing_relid))
197  {
198  ereport(NOTICE,
199  (errcode(ERRCODE_DUPLICATE_TABLE),
200  errmsg("relation \"%s\" already exists, skipping",
201  stmt->relation->relname)));
202  return NIL;
203  }
204 
205  /*
206  * If the target relation name isn't schema-qualified, make it so. This
207  * prevents some corner cases in which added-on rewritten commands might
208  * think they should apply to other relations that have the same name and
209  * are earlier in the search path. But a local temp table is effectively
210  * specified to be in pg_temp, so no need for anything extra in that case.
211  */
212  if (stmt->relation->schemaname == NULL
214  stmt->relation->schemaname = get_namespace_name(namespaceid);
215 
216  /* Set up CreateStmtContext */
217  cxt.pstate = pstate;
218  if (IsA(stmt, CreateForeignTableStmt))
219  {
220  cxt.stmtType = "CREATE FOREIGN TABLE";
221  cxt.isforeign = true;
222  }
223  else
224  {
225  cxt.stmtType = "CREATE TABLE";
226  cxt.isforeign = false;
227  }
228  cxt.relation = stmt->relation;
229  cxt.rel = NULL;
230  cxt.inhRelations = stmt->inhRelations;
231  cxt.isalter = false;
232  cxt.columns = NIL;
233  cxt.ckconstraints = NIL;
234  cxt.fkconstraints = NIL;
235  cxt.ixconstraints = NIL;
236  cxt.inh_indexes = NIL;
237  cxt.blist = NIL;
238  cxt.alist = NIL;
239  cxt.pkey = NULL;
240  cxt.ispartitioned = stmt->partspec != NULL;
241 
242  /*
243  * Notice that we allow OIDs here only for plain tables, even though
244  * foreign tables also support them. This is necessary because the
245  * default_with_oids GUC must apply only to plain tables and not any other
246  * relkind; doing otherwise would break existing pg_dump files. We could
247  * allow explicit "WITH OIDS" while not allowing default_with_oids to
248  * affect other relkinds, but it would complicate interpretOidsOption(),
249  * and right now there's no WITH OIDS option in CREATE FOREIGN TABLE
250  * anyway.
251  */
252  cxt.hasoids = interpretOidsOption(stmt->options, !cxt.isforeign);
253 
254  Assert(!stmt->ofTypename || !stmt->inhRelations); /* grammar enforces */
255 
256  if (stmt->ofTypename)
257  transformOfType(&cxt, stmt->ofTypename);
258 
259  if (stmt->partspec)
260  {
261  if (stmt->inhRelations && !stmt->partbound)
262  ereport(ERROR,
263  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
264  errmsg("cannot create partitioned table as inheritance child")));
265  }
266 
267  /*
268  * Run through each primary element in the table creation clause. Separate
269  * column defs from constraints, and do preliminary analysis.
270  */
271  foreach(elements, stmt->tableElts)
272  {
273  Node *element = lfirst(elements);
274 
275  switch (nodeTag(element))
276  {
277  case T_ColumnDef:
278  transformColumnDefinition(&cxt, (ColumnDef *) element);
279  break;
280 
281  case T_Constraint:
282  transformTableConstraint(&cxt, (Constraint *) element);
283  break;
284 
285  case T_TableLikeClause:
286  like_found = true;
287  transformTableLikeClause(&cxt, (TableLikeClause *) element);
288  break;
289 
290  default:
291  elog(ERROR, "unrecognized node type: %d",
292  (int) nodeTag(element));
293  break;
294  }
295  }
296 
297  /*
298  * If we had any LIKE tables, they may require creation of an OID column
299  * even though the command's own WITH clause didn't ask for one (or,
300  * perhaps, even specifically rejected having one). Insert a WITH option
301  * to ensure that happens. We prepend to the list because the first oid
302  * option will be honored, and we want to override anything already there.
303  * (But note that DefineRelation will override this again to add an OID
304  * column if one appears in an inheritance parent table.)
305  */
306  if (like_found && cxt.hasoids)
307  stmt->options = lcons(makeDefElem("oids",
308  (Node *) makeInteger(true), -1),
309  stmt->options);
310 
311  /*
312  * transformIndexConstraints wants cxt.alist to contain only index
313  * statements, so transfer anything we already have into save_alist.
314  */
315  save_alist = cxt.alist;
316  cxt.alist = NIL;
317 
318  Assert(stmt->constraints == NIL);
319 
320  /*
321  * Postprocess constraints that give rise to index definitions.
322  */
324 
325  /*
326  * Postprocess foreign-key constraints.
327  */
328  transformFKConstraints(&cxt, true, false);
329 
330  /*
331  * Postprocess check constraints.
332  */
333  transformCheckConstraints(&cxt, true);
334 
335  /*
336  * Output results.
337  */
338  stmt->tableElts = cxt.columns;
339  stmt->constraints = cxt.ckconstraints;
340 
341  result = lappend(cxt.blist, stmt);
342  result = list_concat(result, cxt.alist);
343  result = list_concat(result, save_alist);
344 
345  return result;
346 }
RangeVar * relation
Definition: parsenodes.h:1976
#define NIL
Definition: pg_list.h:69
List * inhRelations
Definition: parsenodes.h:1978
#define IsA(nodeptr, _type_)
Definition: nodes.h:560
Definition: nodes.h:509
int errcode(int sqlerrcode)
Definition: elog.c:575
List * list_concat(List *list1, List *list2)
Definition: list.c:321
return result
Definition: formatting.c:1632
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:538
DefElem * makeDefElem(char *name, Node *arg, int location)
Definition: makefuncs.c:544
char * schemaname
Definition: primnodes.h:67
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:44
List * constraints
Definition: parsenodes.h:1983
bool if_not_exists
Definition: parsenodes.h:1987
int location
Definition: primnodes.h:73
ParseState * pstate
Definition: parse_utilcmd.c:75
PartitionBoundSpec * partbound
Definition: parsenodes.h:1980
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:3033
#define NoLock
Definition: lockdefs.h:34
List * options
Definition: parsenodes.h:1984
const char * p_sourcetext
Definition: parse_node.h:168
#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:1977
List * lcons(void *datum, List *list)
Definition: list.c:259
static void transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
#define NULL
Definition: c.h:229
#define Assert(condition)
Definition: c.h:675
#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:514
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:621
#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:1981
Definition: pg_list.h:45
static void transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_clause)
TypeName * ofTypename
Definition: parsenodes.h:1982
static void transformCheckConstraints(CreateStmtContext *cxt, bool skipValidation)
IndexStmt* transformIndexStmt ( Oid  relid,
IndexStmt stmt,
const char *  queryString 
)

Definition at line 2204 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, NULL, ParseState::p_rtable, ParseState::p_sourcetext, relation_open(), IndexStmt::transformed, transformExpr(), transformWhereClause(), and IndexStmt::whereClause.

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

2205 {
2206  ParseState *pstate;
2207  RangeTblEntry *rte;
2208  ListCell *l;
2209  Relation rel;
2210 
2211  /* Nothing to do if statement already transformed. */
2212  if (stmt->transformed)
2213  return stmt;
2214 
2215  /*
2216  * We must not scribble on the passed-in IndexStmt, so copy it. (This is
2217  * overkill, but easy.)
2218  */
2219  stmt = copyObject(stmt);
2220 
2221  /* Set up pstate */
2222  pstate = make_parsestate(NULL);
2223  pstate->p_sourcetext = queryString;
2224 
2225  /*
2226  * Put the parent table into the rtable so that the expressions can refer
2227  * to its fields without qualification. Caller is responsible for locking
2228  * relation, but we still need to open it.
2229  */
2230  rel = relation_open(relid, NoLock);
2231  rte = addRangeTableEntryForRelation(pstate, rel, NULL, false, true);
2232 
2233  /* no to join list, yes to namespaces */
2234  addRTEtoQuery(pstate, rte, false, true, true);
2235 
2236  /* take care of the where clause */
2237  if (stmt->whereClause)
2238  {
2239  stmt->whereClause = transformWhereClause(pstate,
2240  stmt->whereClause,
2242  "WHERE");
2243  /* we have to fix its collations too */
2244  assign_expr_collations(pstate, stmt->whereClause);
2245  }
2246 
2247  /* take care of any index expressions */
2248  foreach(l, stmt->indexParams)
2249  {
2250  IndexElem *ielem = (IndexElem *) lfirst(l);
2251 
2252  if (ielem->expr)
2253  {
2254  /* Extract preliminary index col name before transforming expr */
2255  if (ielem->indexcolname == NULL)
2256  ielem->indexcolname = FigureIndexColname(ielem->expr);
2257 
2258  /* Now do parse transformation of the expression */
2259  ielem->expr = transformExpr(pstate, ielem->expr,
2261 
2262  /* We have to fix its collations too */
2263  assign_expr_collations(pstate, ielem->expr);
2264 
2265  /*
2266  * transformExpr() should have already rejected subqueries,
2267  * aggregates, window functions, and SRFs, based on the EXPR_KIND_
2268  * for an index expression.
2269  *
2270  * DefineIndex() will make more checks.
2271  */
2272  }
2273  }
2274 
2275  /*
2276  * Check that only the base rel is mentioned. (This should be dead code
2277  * now that add_missing_from is history.)
2278  */
2279  if (list_length(pstate->p_rtable) != 1)
2280  ereport(ERROR,
2281  (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
2282  errmsg("index expressions and predicates can refer only to the table being indexed")));
2283 
2284  free_parsestate(pstate);
2285 
2286  /* Close relation */
2287  heap_close(rel, NoLock);
2288 
2289  /* Mark statement as successfully transformed */
2290  stmt->transformed = true;
2291 
2292  return stmt;
2293 }
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:147
Node * whereClause
Definition: parsenodes.h:2677
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:690
#define ERROR
Definition: elog.h:43
#define NoLock
Definition: lockdefs.h:34
bool transformed
Definition: parsenodes.h:2687
char * indexcolname
Definition: parsenodes.h:691
const char * p_sourcetext
Definition: parse_node.h:168
#define ereport(elevel, rest)
Definition: elog.h:122
void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
#define NULL
Definition: c.h:229
#define lfirst(lc)
Definition: pg_list.h:106
Node * transformWhereClause(ParseState *pstate, Node *clause, ParseExprKind exprKind, const char *constructName)
List * indexParams
Definition: parsenodes.h:2675
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:1114
#define copyObject(obj)
Definition: nodes.h:621
void free_parsestate(ParseState *pstate)
Definition: parse_node.c:77
List * p_rtable
Definition: parse_node.h:169
PartitionBoundSpec* transformPartitionBound ( ParseState pstate,
Relation  parent,
PartitionBoundSpec spec 
)

Definition at line 3274 of file parse_utilcmd.c.

References 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, PartitionRangeDatum::infinite, lappend(), lfirst, linitial, list_length(), list_nth(), PartitionBoundSpec::listdatums, PartitionBoundSpec::lowerdatums, NIL, parser_errposition(), PartitionKeyData::partattrs, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, RelationGetPartitionKey, RelationGetRelationName, RelationGetRelid, PartitionBoundSpec::strategy, transformPartitionBoundValue(), PartitionBoundSpec::upperdatums, value, and PartitionRangeDatum::value.

Referenced by DefineRelation(), and transformPartitionCmd().

3276 {
3277  PartitionBoundSpec *result_spec;
3278  PartitionKey key = RelationGetPartitionKey(parent);
3279  char strategy = get_partition_strategy(key);
3280  int partnatts = get_partition_natts(key);
3281  List *partexprs = get_partition_exprs(key);
3282 
3283  /* Avoid scribbling on input */
3284  result_spec = copyObject(spec);
3285 
3286  if (strategy == PARTITION_STRATEGY_LIST)
3287  {
3288  ListCell *cell;
3289  char *colname;
3290  Oid coltype;
3291  int32 coltypmod;
3292 
3293  if (spec->strategy != PARTITION_STRATEGY_LIST)
3294  ereport(ERROR,
3295  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3296  errmsg("invalid bound specification for a list partition"),
3297  parser_errposition(pstate, exprLocation((Node *) spec))));
3298 
3299  /* Get the only column's name in case we need to output an error */
3300  if (key->partattrs[0] != 0)
3301  colname = get_relid_attribute_name(RelationGetRelid(parent),
3302  key->partattrs[0]);
3303  else
3304  colname = deparse_expression((Node *) linitial(partexprs),
3306  RelationGetRelid(parent)),
3307  false, false);
3308  /* Need its type data too */
3309  coltype = get_partition_col_typid(key, 0);
3310  coltypmod = get_partition_col_typmod(key, 0);
3311 
3312  result_spec->listdatums = NIL;
3313  foreach(cell, spec->listdatums)
3314  {
3315  A_Const *con = castNode(A_Const, lfirst(cell));
3316  Const *value;
3317  ListCell *cell2;
3318  bool duplicate;
3319 
3320  value = transformPartitionBoundValue(pstate, con,
3321  colname, coltype, coltypmod);
3322 
3323  /* Don't add to the result if the value is a duplicate */
3324  duplicate = false;
3325  foreach(cell2, result_spec->listdatums)
3326  {
3327  Const *value2 = castNode(Const, lfirst(cell2));
3328 
3329  if (equal(value, value2))
3330  {
3331  duplicate = true;
3332  break;
3333  }
3334  }
3335  if (duplicate)
3336  continue;
3337 
3338  result_spec->listdatums = lappend(result_spec->listdatums,
3339  value);
3340  }
3341  }
3342  else if (strategy == PARTITION_STRATEGY_RANGE)
3343  {
3344  ListCell *cell1,
3345  *cell2;
3346  int i,
3347  j;
3348  bool seen_unbounded;
3349 
3350  if (spec->strategy != PARTITION_STRATEGY_RANGE)
3351  ereport(ERROR,
3352  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3353  errmsg("invalid bound specification for a range partition"),
3354  parser_errposition(pstate, exprLocation((Node *) spec))));
3355 
3356  if (list_length(spec->lowerdatums) != partnatts)
3357  ereport(ERROR,
3358  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3359  errmsg("FROM must specify exactly one value per partitioning column")));
3360  if (list_length(spec->upperdatums) != partnatts)
3361  ereport(ERROR,
3362  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3363  errmsg("TO must specify exactly one value per partitioning column")));
3364 
3365  /*
3366  * Check that no finite value follows an UNBOUNDED item in either of
3367  * lower and upper bound lists.
3368  */
3369  seen_unbounded = false;
3370  foreach(cell1, spec->lowerdatums)
3371  {
3373  lfirst(cell1));
3374 
3375  if (ldatum->infinite)
3376  seen_unbounded = true;
3377  else if (seen_unbounded)
3378  ereport(ERROR,
3379  (errcode(ERRCODE_DATATYPE_MISMATCH),
3380  errmsg("cannot specify finite value after UNBOUNDED"),
3381  parser_errposition(pstate, exprLocation((Node *) ldatum))));
3382  }
3383  seen_unbounded = false;
3384  foreach(cell1, spec->upperdatums)
3385  {
3387  lfirst(cell1));
3388 
3389  if (rdatum->infinite)
3390  seen_unbounded = true;
3391  else if (seen_unbounded)
3392  ereport(ERROR,
3393  (errcode(ERRCODE_DATATYPE_MISMATCH),
3394  errmsg("cannot specify finite value after UNBOUNDED"),
3395  parser_errposition(pstate, exprLocation((Node *) rdatum))));
3396  }
3397 
3398  /* Transform all the constants */
3399  i = j = 0;
3400  result_spec->lowerdatums = result_spec->upperdatums = NIL;
3401  forboth(cell1, spec->lowerdatums, cell2, spec->upperdatums)
3402  {
3403  PartitionRangeDatum *ldatum = (PartitionRangeDatum *) lfirst(cell1);
3404  PartitionRangeDatum *rdatum = (PartitionRangeDatum *) lfirst(cell2);
3405  char *colname;
3406  Oid coltype;
3407  int32 coltypmod;
3408  A_Const *con;
3409  Const *value;
3410 
3411  /* Get the column's name in case we need to output an error */
3412  if (key->partattrs[i] != 0)
3413  colname = get_relid_attribute_name(RelationGetRelid(parent),
3414  key->partattrs[i]);
3415  else
3416  {
3417  colname = deparse_expression((Node *) list_nth(partexprs, j),
3419  RelationGetRelid(parent)),
3420  false, false);
3421  ++j;
3422  }
3423  /* Need its type data too */
3424  coltype = get_partition_col_typid(key, i);
3425  coltypmod = get_partition_col_typmod(key, i);
3426 
3427  if (ldatum->value)
3428  {
3429  con = castNode(A_Const, ldatum->value);
3430  value = transformPartitionBoundValue(pstate, con,
3431  colname,
3432  coltype, coltypmod);
3433  if (value->constisnull)
3434  ereport(ERROR,
3435  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3436  errmsg("cannot specify NULL in range bound")));
3437  ldatum = copyObject(ldatum); /* don't scribble on input */
3438  ldatum->value = (Node *) value;
3439  }
3440 
3441  if (rdatum->value)
3442  {
3443  con = castNode(A_Const, rdatum->value);
3444  value = transformPartitionBoundValue(pstate, con,
3445  colname,
3446  coltype, coltypmod);
3447  if (value->constisnull)
3448  ereport(ERROR,
3449  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3450  errmsg("cannot specify NULL in range bound")));
3451  rdatum = copyObject(rdatum); /* don't scribble on input */
3452  rdatum->value = (Node *) value;
3453  }
3454 
3455  result_spec->lowerdatums = lappend(result_spec->lowerdatums,
3456  ldatum);
3457  result_spec->upperdatums = lappend(result_spec->upperdatums,
3458  rdatum);
3459 
3460  ++i;
3461  }
3462  }
3463  else
3464  elog(ERROR, "unexpected partition strategy: %d", (int) strategy);
3465 
3466  return result_spec;
3467 }
#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:1214
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:2962
#define castNode(_type_, nodeptr)
Definition: nodes.h:578
Definition: nodes.h:509
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:256
static int get_partition_natts(PartitionKey key)
Definition: rel.h:597
#define linitial(l)
Definition: pg_list.h:111
#define ERROR
Definition: elog.h:43
void * list_nth(const List *list, int n)
Definition: list.c:410
static List * get_partition_exprs(PartitionKey key)
Definition: rel.h:603
List * deparse_context_for(const char *aliasname, Oid relid)
Definition: ruleutils.c:3036
#define RelationGetRelationName(relation)
Definition: rel.h:437
#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:624
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:618
static int get_partition_strategy(PartitionKey key)
Definition: rel.h:591
#define lfirst(lc)
Definition: pg_list.h:106
char * deparse_expression(Node *expr, List *dpcontext, bool forceprefix, bool showimplicit)
Definition: ruleutils.c:2977
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:787
#define RelationGetPartitionKey(relation)
Definition: rel.h:585
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:788
int errmsg(const char *fmt,...)
Definition: elog.c:797
int i
#define elog
Definition: elog.h:219
static struct @121 value
#define copyObject(obj)
Definition: nodes.h:621
Definition: pg_list.h:45
#define RelationGetRelid(relation)
Definition: rel.h:417
bool constisnull
Definition: primnodes.h:197
void transformRuleStmt ( RuleStmt stmt,
const char *  queryString,
List **  actions,
Node **  whereClause 
)

Definition at line 2309 of file parse_utilcmd.c.

References AccessExclusiveLock, 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(), FromExpr::fromlist, getInsertSelectQuery(), heap_close, heap_openrv(), Query::jointree, lappend(), lfirst, list_length(), list_make1, make_parsestate(), makeAlias(), makeFromExpr(), makeNode, NIL, NoLock, NULL, 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().

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