if (IsA(llast(cref->fields), A_Star))
{ /* It is something.*, expand into multiple items */
result = list_concat(result,
ExpandColumnRefStar(pstate, cref, false)); continue;
}
} elseif (IsA(e, A_Indirection))
{
A_Indirection *ind = (A_Indirection *) e;
if (IsA(llast(ind->indirection), A_Star))
{ /* It is something.*, expand into multiple items */
result = list_concat(result,
ExpandIndirectionStar(pstate, ind, false, exprKind)); continue;
}
}
/* *Not"something.*",sotransformasasingleexpression.Ifit'sa *SetToDefaultnodeandweshouldallowthat,passitthrough *unmodified.(transformExprwillthrowtheappropriateerrorif *we'redisallowingit.)
*/ if (allowDefault && IsA(e, SetToDefault)) /* do nothing */ ; else
e = transformExpr(pstate, e, exprKind);
switch (rte->rtekind)
{ case RTE_RELATION: /* It's a table or view, report it */
tle->resorigtbl = rte->relid;
tle->resorigcol = attnum; break; case RTE_SUBQUERY: /* Subselect-in-FROM: copy up from the subselect */ if (attnum != InvalidAttrNumber)
{
TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
attnum);
if (ste == NULL || ste->resjunk)
elog(ERROR, "subquery %s does not have attribute %d",
rte->eref->aliasname, attnum);
tle->resorigtbl = ste->resorigtbl;
tle->resorigcol = ste->resorigcol;
} break; case RTE_JOIN: case RTE_FUNCTION: case RTE_VALUES: case RTE_TABLEFUNC: case RTE_NAMEDTUPLESTORE: case RTE_RESULT: /* not a simple relation, leave it unmarked */ break; case RTE_CTE:
/* *RTEforCTEwillalreadyhavethesearchandcyclecolumns *added,butthesubquerywon't,soskiplookingthoseup.
*/ if (cte->search_clause)
extra_cols += 1; if (cte->cycle_clause)
extra_cols += 2; if (extra_cols &&
attnum > list_length(tl) &&
attnum <= list_length(tl) + extra_cols) break;
ste = get_tle_by_resno(tl, attnum); if (ste == NULL || ste->resjunk)
elog(ERROR, "CTE %s does not have attribute %d",
rte->eref->aliasname, attnum);
tle->resorigtbl = ste->resorigtbl;
tle->resorigcol = ste->resorigcol;
} break; case RTE_GROUP: /* We couldn't get here: the RTE_GROUP RTE has not been added */ break;
}
}
/* *transformAssignedExpr() *ThisisusedinINSERTandUPDATEstatementsonly.Itpreparesan *expressionforassignmenttoacolumnofthetargettable. *Thisincludescoercingthegivenvaluetothetargetcolumn'stype *(ifnecessary),anddealingwithanysubfieldnamesorsubscripts *attachedtothetargetcolumnitself.Theinputexpressionhas *alreadybeenthroughtransformExpr(). * *pstateparsestate *exprexpressiontobemodified *exprKindindicateswhichtypeofstatementwe'redealingwith *colnametargetcolumnname(ie,nameofattributetobeassignedto) *attrnotargetattributenumber *indirectionsubscripts/fieldnamesfortargetcolumn,ifany *locationerrorcursorpositionforthetargetcolumn,or-1 * *Returnsthemodifiedexpression. * *Note:locationpointsatthetargetcolumnname(SETtargetorINSERT *columnnamelistentry),andmustthereforebe-1inanINSERTthat *omitsthecolumnnamelist.Soweshouldusuallyprefertouse *exprLocation(expr)forerrorsthatcanhappeninadefaultINSERT.
*/
Expr *
transformAssignedExpr(ParseState *pstate,
Expr *expr,
ParseExprKind exprKind, constchar *colname, int attrno,
List *indirection, int location)
{
Relation rd = pstate->p_target_relation;
Oid type_id; /* type of value provided */
Oid attrtype; /* type of target column */
int32 attrtypmod;
Oid attrcollation; /* collation of target column */
ParseExprKind sv_expr_kind;
def->typeId = attrtype;
def->typeMod = attrtypmod;
def->collation = attrcollation; if (indirection)
{ if (IsA(linitial(indirection), A_Indices))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot set an array element to DEFAULT"),
parser_errposition(pstate, location))); else
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot set a subfield to DEFAULT"),
parser_errposition(pstate, location)));
}
}
/* Now we can use exprType() safely. */
type_id = exprType((Node *) expr);
expr = (Expr *)
coerce_to_target_type(pstate,
orig_expr, type_id,
attrtype, attrtypmod,
COERCION_ASSIGNMENT,
COERCE_IMPLICIT_CAST,
-1); if (expr == NULL)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("column \"%s\" is of type %s" " but expression is of type %s",
colname,
format_type_be(attrtype),
format_type_be(type_id)),
errhint("You will need to rewrite or cast the expression."),
parser_errposition(pstate, exprLocation(orig_expr))));
}
pstate->p_expr_kind = sv_expr_kind;
return expr;
}
/* *updateTargetListEntry() *ThisisusedinUPDATEstatements(andONCONFLICTDOUPDATE) *only.ItpreparesanUPDATETargetEntryforassignmenttoa *columnofthetargettable.Thisincludescoercingthegiven *valuetothetargetcolumn'stype(ifnecessary),anddealingwith *anysubfieldnamesorsubscriptsattachedtothetargetcolumn *itself. * *pstateparsestate *tletargetlistentrytobemodified *colnametargetcolumnname(ie,nameofattributetobeassignedto) *attrnotargetattributenumber *indirectionsubscripts/fieldnamesfortargetcolumn,ifany *locationerrorcursorposition(shouldpointatcolumnname),or-1
*/ void
updateTargetListEntry(ParseState *pstate,
TargetEntry *tle, char *colname, int attrno,
List *indirection, int location)
{ /* Fix up expression as needed */
tle->expr = transformAssignedExpr(pstate,
tle->expr,
EXPR_KIND_UPDATE_TARGET,
colname,
attrno,
indirection,
location);
if (IsA(n, A_Indices))
subscripts = lappend(subscripts, n); elseif (IsA(n, A_Star))
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("row expansion via \"*\" is not supported here"),
parser_errposition(pstate, location)));
} else
{
FieldStore *fstore;
Oid baseTypeId;
int32 baseTypeMod;
Oid typrelid;
AttrNumber attnum;
Oid fieldTypeId;
int32 fieldTypMod;
Oid fieldCollation;
Assert(IsA(n, String));
/* process subscripts before this field selection */ if (subscripts)
{ /* recurse, and then return because we're done */ return transformAssignmentSubscripts(pstate,
basenode,
targetName,
targetTypeId,
targetTypMod,
targetCollation,
subscripts,
indirection,
i,
rhs,
ccontext,
location);
}
/* No subscripts, so can process field selection here */
typrelid = typeidTypeRelid(baseTypeId); if (!typrelid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot assign to field \"%s\" of column \"%s\" because its type %s is not a composite type",
strVal(n), targetName,
format_type_be(targetTypeId)),
parser_errposition(pstate, location)));
attnum = get_attnum(typrelid, strVal(n)); if (attnum == InvalidAttrNumber)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("cannot assign to field \"%s\" of column \"%s\" because there is no such column in data type %s",
strVal(n), targetName,
format_type_be(targetTypeId)),
parser_errposition(pstate, location))); if (attnum < 0)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("cannot assign to system column \"%s\"",
strVal(n)),
parser_errposition(pstate, location)));
/* process trailing subscripts, if any */ if (subscripts)
{ /* recurse, and then return because we're done */ return transformAssignmentSubscripts(pstate,
basenode,
targetName,
targetTypeId,
targetTypMod,
targetCollation,
subscripts,
indirection,
NULL,
rhs,
ccontext,
location);
}
/* base case: just coerce RHS to match target type ID */
result = coerce_to_target_type(pstate,
rhs, exprType(rhs),
targetTypeId, targetTypMod,
ccontext,
COERCE_IMPLICIT_CAST,
-1); if (result == NULL)
{ if (targetIsSubscripting)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("subscripted assignment to \"%s\" requires type %s" " but expression is of type %s",
targetName,
format_type_be(targetTypeId),
format_type_be(exprType(rhs))),
errhint("You will need to rewrite or cast the expression."),
parser_errposition(pstate, location))); else
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("subfield \"%s\" is of type %s" " but expression is of type %s",
targetName,
format_type_be(targetTypeId),
format_type_be(exprType(rhs))),
errhint("You will need to rewrite or cast the expression."),
parser_errposition(pstate, location)));
}
return result;
}
/* *helperfortransformAssignmentIndirection:processcontainerassignment
*/ static Node *
transformAssignmentSubscripts(ParseState *pstate,
Node *basenode, constchar *targetName,
Oid targetTypeId,
int32 targetTypMod,
Oid targetCollation,
List *subscripts,
List *indirection,
ListCell *next_indirection,
Node *rhs,
CoercionContext ccontext, int location)
{
Node *result;
SubscriptingRef *sbsref;
Oid containerType;
int32 containerTypMod;
Oid typeNeeded;
int32 typmodNeeded;
Oid collationNeeded;
Assert(subscripts != NIL);
/* Identify the actual container type involved */
containerType = targetTypeId;
containerTypMod = targetTypMod;
transformContainerType(&containerType, &containerTypMod);
/* Process subscripts and identify required type for RHS */
sbsref = transformContainerSubscripts(pstate,
basenode,
containerType,
containerTypMod,
subscripts, true);
/* *Iftargetwasadomainovercontainer,needtocoerceuptothedomain. *AsintransformAssignmentIndirection,thiscoercionisprematureifthe *queryassignstomultipleelementsofthecontainer;butwe'llfixthat *duringqueryrewrite.
*/ if (containerType != targetTypeId)
{
Oid resulttype = exprType(result);
result = coerce_to_target_type(pstate,
result, resulttype,
targetTypeId, targetTypMod,
ccontext,
COERCE_IMPLICIT_CAST,
-1); /* can fail if we had int2vector/oidvector, but not for true domains */ if (result == NULL)
ereport(ERROR,
(errcode(ERRCODE_CANNOT_COERCE),
errmsg("cannot cast type %s to %s",
format_type_be(resulttype),
format_type_be(targetTypeId)),
parser_errposition(pstate, location)));
}
return result;
}
/* *checkInsertTargets- *generatealistofINSERTcolumntargetsifnotsupplied,or *testsuppliedcolumnnamestomakesuretheyareintargettable. *Alsoreturnanintegerlistofthecolumns'attributenumbers.
*/
List *
checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
{
*attrnos = NIL;
if (cols == NIL)
{ /* *GeneratedefaultcolumnlistforINSERT.
*/ int numcol = RelationGetNumberOfAttributes(pstate->p_target_relation);
int i;
for (i = 0; i < numcol; i++)
{
ResTarget *col;
Form_pg_attribute attr;
/* Lookup column name, ereport on failure */
attrno = attnameAttNum(pstate->p_target_relation, name, false); if (attrno == InvalidAttrNumber)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" of relation \"%s\" does not exist",
name,
RelationGetRelationName(pstate->p_target_relation)),
parser_errposition(pstate, col->location)));
/* *Checkforduplicates,butonlyofwholecolumns---weallow *INSERTINTOfoo(col.subcol1,col.subcol2)
*/ if (col->indirection == NIL)
{ /* whole column; must not have any other assignment */ if (bms_is_member(attrno, wholecols) ||
bms_is_member(attrno, partialcols))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_COLUMN),
errmsg("column \"%s\" specified more than once",
name),
parser_errposition(pstate, col->location)));
wholecols = bms_add_member(wholecols, attrno);
} else
{ /* partial column; must not have any whole assignment */ if (bms_is_member(attrno, wholecols))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_COLUMN),
errmsg("column \"%s\" specified more than once",
name),
parser_errposition(pstate, col->location)));
partialcols = bms_add_member(partialcols, attrno);
}
*attrnos = lappend_int(*attrnos, attrno);
}
}
return cols;
}
/* *ExpandColumnRefStar() *Transformsfoo.*intoalistofexpressionsortargetlistentries. * *Thishandlesthecasewhere'*'appearsasthelastoronlyitemina *ColumnRef.Thecodeissharedbetweenthecaseoffoo.*atthetoplevel *inaSELECTtargetlist(wherewewantTargetEntrynodesintheresult) *andfoo.*inaROW()orVALUES()construct(wherewewantjustbare *expressions). * *ThereferencedcolumnsaremarkedasrequiringSELECTaccess.
*/ static List *
ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, bool make_target_entry)
{
List *fields = cref->fields; int numnames = list_length(fields);
/* Ignore table-only items */ if (!nsitem->p_cols_visible) continue; /* Should not have any lateral-only items when parsing targetlist */
Assert(!nsitem->p_lateral_only); /* Remember we found a p_cols_visible item */
found_table = true;
/* *Checkfor"SELECT*;".Wedoitthisway,ratherthancheckingfor *target==NIL,becausewewanttoallowSELECT*FROMazero_column *table.
*/ if (!found_table)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("SELECT * with no tables specified is not valid"),
parser_errposition(pstate, location)));
/* Strip off the '*' to create a reference to the rowtype object */
ind = copyObject(ind);
ind->indirection = list_truncate(ind->indirection,
list_length(ind->indirection) - 1);
/* And transform that */
expr = transformExpr(pstate, (Node *) ind, exprKind);
/* Expand the rowtype expression into individual fields */ return ExpandRowReference(pstate, expr, make_target_entry);
}
/* Generate a list of references to the individual fields */
numAttrs = tupleDesc->natts; for (i = 0; i < numAttrs; i++)
{
Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
FieldSelect *fselect;
if (att->attisdropped) continue;
fselect = makeNode(FieldSelect);
fselect->arg = (Expr *) copyObject(expr);
fselect->fieldnum = i + 1;
fselect->resulttype = att->atttypid;
fselect->resulttypmod = att->atttypmod; /* save attribute's collation for parse_collate.c */
fselect->resultcollid = att->attcollation;
if (make_target_entry)
{ /* add TargetEntry decoration */
TargetEntry *te;
te = makeTargetEntry((Expr *) fselect,
(AttrNumber) pstate->p_next_resno++,
pstrdup(NameStr(att->attname)), false);
result = lappend(result, te);
} else
result = lappend(result, fselect);
}
return result;
}
/* *expandRecordVariable *GetthetupledescriptorforaVaroftypeRECORD,ifpossible. * *SincenoactualtableorviewcolumnisallowedtohavetypeRECORD,such *aVarmustrefertoaJOINorFUNCTIONRTEortoasubqueryoutput.We *drilldowntofindtheultimatedefiningexpressionandattempttoinfer *thetupdescfromit.Weereportifwecan'tdeterminethetupdesc. * *levelsupisanextraoffsettointerprettheVar'svarlevelsupcorrectly *whenrecursing.Outsidecallersshouldpasszero.
*/
TupleDesc
expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
{
TupleDesc tupleDesc; int netlevelsup;
RangeTblEntry *rte;
AttrNumber attnum;
Node *expr;
/* Check my caller didn't mess up */
Assert(IsA(var, Var));
Assert(var->vartype == RECORDOID);
if (ste == NULL || ste->resjunk)
elog(ERROR, "subquery %s does not have attribute %d",
rte->eref->aliasname, attnum);
expr = (Node *) ste->expr; if (IsA(expr, Var))
{ /* *Recurseintothesub-selecttoseewhatitsVarrefers *to.WehavetobuildanadditionallevelofParseState *tokeepinstepwithvarlevelsupinthesubselect; *furthermore,thesubqueryRTEmightbefromanouter *querylevel,inwhichcasetheParseStateforthe *subselectmusthavethatouterlevelasparent.
*/
ParseState mypstate = {0};
Index levelsup;
/* this loop must work, since GetRTEByRangeTablePosn did */ for (levelsup = 0; levelsup < netlevelsup; levelsup++)
pstate = pstate->parentParseState;
mypstate.parentParseState = pstate;
mypstate.p_rtable = rte->subquery->rtable; /* don't bother filling the rest of the fake pstate */
return expandRecordVariable(&mypstate, (Var *) expr, 0);
} /* else fall through to inspect the expression */
} break; case RTE_JOIN: /* Join RTE --- recursively inspect the alias variable */
Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
Assert(expr != NULL); /* We intentionally don't strip implicit coercions here */ if (IsA(expr, Var)) return expandRecordVariable(pstate, (Var *) expr, netlevelsup); /* else fall through to inspect the expression */ break; case RTE_FUNCTION:
/* *Wecouldn'tgethereunlessafunctionisdeclaredwithoneof *itsresultcolumnsasRECORD,whichisnotallowed.
*/ break; case RTE_TABLEFUNC:
if (te->resname)
{
*name = te->resname; return2;
}
}
} break; /* As with other operator-like nodes, these have no names */ case MULTIEXPR_SUBLINK: case ALL_SUBLINK: case ANY_SUBLINK: case ROWCOMPARE_SUBLINK: case CTE_SUBLINK: break;
} break; case T_CaseExpr:
strength = FigureColnameInternal((Node *) ((CaseExpr *) node)->defresult,
name); if (strength <= 1)
{
*name = "case"; return1;
} break; case T_A_ArrayExpr: /* make ARRAY[] act like a function */
*name = "array"; return2; case T_RowExpr: /* make ROW() act like a function */
*name = "row"; return2; case T_CoalesceExpr: /* make coalesce() act like a regular function */
*name = "coalesce"; return2; case T_MinMaxExpr: /* make greatest/least act like a regular function */ switch (((MinMaxExpr *) node)->op)
{ case IS_GREATEST:
*name = "greatest"; return2; case IS_LEAST:
*name = "least"; return2;
} break; case T_SQLValueFunction: /* make these act like a function or variable */ switch (((SQLValueFunction *) node)->op)
{ case SVFOP_CURRENT_DATE:
*name = "current_date"; return2; case SVFOP_CURRENT_TIME: case SVFOP_CURRENT_TIME_N:
*name = "current_time"; return2; case SVFOP_CURRENT_TIMESTAMP: case SVFOP_CURRENT_TIMESTAMP_N:
*name = "current_timestamp"; return2; case SVFOP_LOCALTIME: case SVFOP_LOCALTIME_N:
*name = "localtime"; return2; case SVFOP_LOCALTIMESTAMP: case SVFOP_LOCALTIMESTAMP_N:
*name = "localtimestamp"; return2; case SVFOP_CURRENT_ROLE:
*name = "current_role"; return2; case SVFOP_CURRENT_USER:
*name = "current_user"; return2; case SVFOP_USER:
*name = "user"; return2; case SVFOP_SESSION_USER:
*name = "session_user"; return2; case SVFOP_CURRENT_CATALOG:
*name = "current_catalog"; return2; case SVFOP_CURRENT_SCHEMA:
*name = "current_schema"; return2;
} break; case T_XmlExpr: /* make SQL/XML functions act like a regular function */ switch (((XmlExpr *) node)->op)
{ case IS_XMLCONCAT:
*name = "xmlconcat"; return2; case IS_XMLELEMENT:
*name = "xmlelement"; return2; case IS_XMLFOREST:
*name = "xmlforest"; return2; case IS_XMLPARSE:
*name = "xmlparse"; return2; case IS_XMLPI:
*name = "xmlpi"; return2; case IS_XMLROOT:
*name = "xmlroot"; return2; case IS_XMLSERIALIZE:
*name = "xmlserialize"; return2; case IS_DOCUMENT: /* nothing */ break;
} break; case T_XmlSerialize: /* make XMLSERIALIZE act like a regular function */
*name = "xmlserialize"; return2; case T_JsonParseExpr: /* make JSON act like a regular function */
*name = "json"; return2; case T_JsonScalarExpr: /* make JSON_SCALAR act like a regular function */
*name = "json_scalar"; return2; case T_JsonSerializeExpr: /* make JSON_SERIALIZE act like a regular function */
*name = "json_serialize"; return2; case T_JsonObjectConstructor: /* make JSON_OBJECT act like a regular function */
*name = "json_object"; return2; case T_JsonArrayConstructor: case T_JsonArrayQueryConstructor: /* make JSON_ARRAY act like a regular function */
*name = "json_array"; return2; case T_JsonObjectAgg: /* make JSON_OBJECTAGG act like a regular function */
*name = "json_objectagg"; return2; case T_JsonArrayAgg: /* make JSON_ARRAYAGG act like a regular function */
*name = "json_arrayagg"; return2; case T_JsonFuncExpr: /* make SQL/JSON functions act like a regular function */ switch (((JsonFuncExpr *) node)->op)
{ case JSON_EXISTS_OP:
*name = "json_exists"; return2; case JSON_QUERY_OP:
*name = "json_query"; return2; case JSON_VALUE_OP:
*name = "json_value"; return2; /* JSON_TABLE_OP can't happen here. */ default:
elog(ERROR, "unrecognized JsonExpr op: %d",
(int) ((JsonFuncExpr *) node)->op);
} break; default: break;
}