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 2634 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().

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

3146 {
3148  List *result;
3149  ListCell *elements;
3150 
3151  cxt.stmtType = "CREATE SCHEMA";
3152  cxt.schemaname = stmt->schemaname;
3153  cxt.authrole = (RoleSpec *) stmt->authrole;
3154  cxt.sequences = NIL;
3155  cxt.tables = NIL;
3156  cxt.views = NIL;
3157  cxt.indexes = NIL;
3158  cxt.triggers = NIL;
3159  cxt.grants = NIL;
3160 
3161  /*
3162  * Run through each schema element in the schema element list. Separate
3163  * statements by type, and do preliminary analysis.
3164  */
3165  foreach(elements, stmt->schemaElts)
3166  {
3167  Node *element = lfirst(elements);
3168 
3169  switch (nodeTag(element))
3170  {
3171  case T_CreateSeqStmt:
3172  {
3173  CreateSeqStmt *elp = (CreateSeqStmt *) element;
3174 
3176  cxt.sequences = lappend(cxt.sequences, element);
3177  }
3178  break;
3179 
3180  case T_CreateStmt:
3181  {
3182  CreateStmt *elp = (CreateStmt *) element;
3183 
3185 
3186  /*
3187  * XXX todo: deal with constraints
3188  */
3189  cxt.tables = lappend(cxt.tables, element);
3190  }
3191  break;
3192 
3193  case T_ViewStmt:
3194  {
3195  ViewStmt *elp = (ViewStmt *) element;
3196 
3197  setSchemaName(cxt.schemaname, &elp->view->schemaname);
3198 
3199  /*
3200  * XXX todo: deal with references between views
3201  */
3202  cxt.views = lappend(cxt.views, element);
3203  }
3204  break;
3205 
3206  case T_IndexStmt:
3207  {
3208  IndexStmt *elp = (IndexStmt *) element;
3209 
3211  cxt.indexes = lappend(cxt.indexes, element);
3212  }
3213  break;
3214 
3215  case T_CreateTrigStmt:
3216  {
3217  CreateTrigStmt *elp = (CreateTrigStmt *) element;
3218 
3220  cxt.triggers = lappend(cxt.triggers, element);
3221  }
3222  break;
3223 
3224  case T_GrantStmt:
3225  cxt.grants = lappend(cxt.grants, element);
3226  break;
3227 
3228  default:
3229  elog(ERROR, "unrecognized node type: %d",
3230  (int) nodeTag(element));
3231  }
3232  }
3233 
3234  result = NIL;
3235  result = list_concat(result, cxt.sequences);
3236  result = list_concat(result, cxt.tables);
3237  result = list_concat(result, cxt.views);
3238  result = list_concat(result, cxt.indexes);
3239  result = list_concat(result, cxt.triggers);
3240  result = list_concat(result, cxt.grants);
3241 
3242  return result;
3243 }
RangeVar * relation
Definition: parsenodes.h:1987
#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:1633
RangeVar * view
Definition: parsenodes.h:2985
char * schemaname
Definition: primnodes.h:67
RangeVar * relation
Definition: parsenodes.h:2684
RoleSpec * authrole
Definition: parsenodes.h:1659
#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:2454
#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:2339
#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:1987
#define NIL
Definition: pg_list.h:69
List * inhRelations
Definition: parsenodes.h:1989
#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:1633
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:1994
bool if_not_exists
Definition: parsenodes.h:1998
int location
Definition: primnodes.h:73
ParseState * pstate
Definition: parse_utilcmd.c:75
PartitionBoundSpec * partbound
Definition: parsenodes.h:1991
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:1995
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:1988
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:622
#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:1992
Definition: pg_list.h:45
static void transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_clause)
TypeName * ofTypename
Definition: parsenodes.h:1993
static void transformCheckConstraints(CreateStmtContext *cxt, bool skipValidation)
IndexStmt* transformIndexStmt ( Oid  relid,
IndexStmt stmt,
const char *  queryString 
)

Definition at line 2224 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().

2225 {
2226  ParseState *pstate;
2227  RangeTblEntry *rte;
2228  ListCell *l;
2229  Relation rel;
2230 
2231  /* Nothing to do if statement already transformed. */
2232  if (stmt->transformed)
2233  return stmt;
2234 
2235  /*
2236  * We must not scribble on the passed-in IndexStmt, so copy it. (This is
2237  * overkill, but easy.)
2238  */
2239  stmt = copyObject(stmt);
2240 
2241  /* Set up pstate */
2242  pstate = make_parsestate(NULL);
2243  pstate->p_sourcetext = queryString;
2244 
2245  /*
2246  * Put the parent table into the rtable so that the expressions can refer
2247  * to its fields without qualification. Caller is responsible for locking
2248  * relation, but we still need to open it.
2249  */
2250  rel = relation_open(relid, NoLock);
2251  rte = addRangeTableEntryForRelation(pstate, rel, NULL, false, true);
2252 
2253  /* no to join list, yes to namespaces */
2254  addRTEtoQuery(pstate, rte, false, true, true);
2255 
2256  /* take care of the where clause */
2257  if (stmt->whereClause)
2258  {
2259  stmt->whereClause = transformWhereClause(pstate,
2260  stmt->whereClause,
2262  "WHERE");
2263  /* we have to fix its collations too */
2264  assign_expr_collations(pstate, stmt->whereClause);
2265  }
2266 
2267  /* take care of any index expressions */
2268  foreach(l, stmt->indexParams)
2269  {
2270  IndexElem *ielem = (IndexElem *) lfirst(l);
2271 
2272  if (ielem->expr)
2273  {
2274  /* Extract preliminary index col name before transforming expr */
2275  if (ielem->indexcolname == NULL)
2276  ielem->indexcolname = FigureIndexColname(ielem->expr);
2277 
2278  /* Now do parse transformation of the expression */
2279  ielem->expr = transformExpr(pstate, ielem->expr,
2281 
2282  /* We have to fix its collations too */
2283  assign_expr_collations(pstate, ielem->expr);
2284 
2285  /*
2286  * transformExpr() should have already rejected subqueries,
2287  * aggregates, window functions, and SRFs, based on the EXPR_KIND_
2288  * for an index expression.
2289  *
2290  * DefineIndex() will make more checks.
2291  */
2292  }
2293  }
2294 
2295  /*
2296  * Check that only the base rel is mentioned. (This should be dead code
2297  * now that add_missing_from is history.)
2298  */
2299  if (list_length(pstate->p_rtable) != 1)
2300  ereport(ERROR,
2301  (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
2302  errmsg("index expressions and predicates can refer only to the table being indexed")));
2303 
2304  free_parsestate(pstate);
2305 
2306  /* Close relation */
2307  heap_close(rel, NoLock);
2308 
2309  /* Mark statement as successfully transformed */
2310  stmt->transformed = true;
2311 
2312  return stmt;
2313 }
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:146
Node * whereClause
Definition: parsenodes.h:2689
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:2699
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 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:2687
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:622
void free_parsestate(ParseState *pstate)
Definition: parse_node.c:77
List * p_rtable
Definition: parse_node.h:172
PartitionBoundSpec* transformPartitionBound ( ParseState pstate,
Relation  parent,
PartitionBoundSpec spec 
)

Definition at line 3294 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().

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

Definition at line 2329 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().

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