#include "postgres.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/parsetree.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/typcache.h"
Go to the source code of this file.
Functions | |
static void | markTargetListOrigin (ParseState *pstate, TargetEntry *tle, Var *var, int levelsup) |
static Node * | transformAssignmentIndirection (ParseState *pstate, Node *basenode, const char *targetName, bool targetIsArray, Oid targetTypeId, int32 targetTypMod, Oid targetCollation, ListCell *indirection, Node *rhs, int location) |
static Node * | transformAssignmentSubscripts (ParseState *pstate, Node *basenode, const char *targetName, Oid targetTypeId, int32 targetTypMod, Oid targetCollation, List *subscripts, bool isSlice, ListCell *next_indirection, Node *rhs, int location) |
static List * | ExpandColumnRefStar (ParseState *pstate, ColumnRef *cref, bool make_target_entry) |
static List * | ExpandAllTables (ParseState *pstate, int location) |
static List * | ExpandIndirectionStar (ParseState *pstate, A_Indirection *ind, bool make_target_entry, ParseExprKind exprKind) |
static List * | ExpandSingleTable (ParseState *pstate, RangeTblEntry *rte, int location, bool make_target_entry) |
static List * | ExpandRowReference (ParseState *pstate, Node *expr, bool make_target_entry) |
static int | FigureColnameInternal (Node *node, char **name) |
TargetEntry * | transformTargetEntry (ParseState *pstate, Node *node, Node *expr, ParseExprKind exprKind, char *colname, bool resjunk) |
List * | transformTargetList (ParseState *pstate, List *targetlist, ParseExprKind exprKind) |
List * | transformExpressionList (ParseState *pstate, List *exprlist, ParseExprKind exprKind) |
void | markTargetListOrigins (ParseState *pstate, List *targetlist) |
Expr * | transformAssignedExpr (ParseState *pstate, Expr *expr, ParseExprKind exprKind, char *colname, int attrno, List *indirection, int location) |
void | updateTargetListEntry (ParseState *pstate, TargetEntry *tle, char *colname, int attrno, List *indirection, int location) |
List * | checkInsertTargets (ParseState *pstate, List *cols, List **attrnos) |
TupleDesc | expandRecordVariable (ParseState *pstate, Var *var, int levelsup) |
char * | FigureColname (Node *node) |
char * | FigureIndexColname (Node *node) |
List* checkInsertTargets | ( | ParseState * | pstate, | |
List * | cols, | |||
List ** | attrnos | |||
) |
Definition at line 868 of file parse_target.c.
References attnameAttNum(), tupleDesc::attrs, bms_add_member(), bms_is_member(), ereport, errcode(), errmsg(), ERROR, i, ResTarget::indirection, InvalidAttrNumber, lappend(), lappend_int(), lfirst, ResTarget::location, makeNode, name, ResTarget::name, NameStr, NIL, ParseState::p_target_relation, parser_errposition(), pstrdup(), RelationData::rd_att, RelationData::rd_rel, RelationGetRelationName, and ResTarget::val.
Referenced by transformInsertStmt().
{ *attrnos = NIL; if (cols == NIL) { /* * Generate default column list for INSERT. */ Form_pg_attribute *attr = pstate->p_target_relation->rd_att->attrs; int numcol = pstate->p_target_relation->rd_rel->relnatts; int i; for (i = 0; i < numcol; i++) { ResTarget *col; if (attr[i]->attisdropped) continue; col = makeNode(ResTarget); col->name = pstrdup(NameStr(attr[i]->attname)); col->indirection = NIL; col->val = NULL; col->location = -1; cols = lappend(cols, col); *attrnos = lappend_int(*attrnos, i + 1); } } else { /* * Do initial validation of user-supplied INSERT column list. */ Bitmapset *wholecols = NULL; Bitmapset *partialcols = NULL; ListCell *tl; foreach(tl, cols) { ResTarget *col = (ResTarget *) lfirst(tl); char *name = col->name; int attrno; /* 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))); /* * Check for duplicates, but only of whole columns --- we allow * INSERT INTO foo (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; }
static List * ExpandAllTables | ( | ParseState * | pstate, | |
int | location | |||
) | [static] |
Definition at line 1140 of file parse_target.c.
References Assert, ereport, errcode(), errmsg(), ERROR, expandRelAttrs(), lfirst, list_concat(), NULL, ParseNamespaceItem::p_cols_visible, ParseNamespaceItem::p_lateral_only, ParseState::p_namespace, ParseNamespaceItem::p_rte, parser_errposition(), and RTERangeTablePosn().
Referenced by ExpandColumnRefStar().
{ List *target = NIL; bool found_table = false; ListCell *l; foreach(l, pstate->p_namespace) { ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l); RangeTblEntry *rte = nsitem->p_rte; /* 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; target = list_concat(target, expandRelAttrs(pstate, rte, RTERangeTablePosn(pstate, rte, NULL), 0, location)); } /* * Check for "SELECT *;". We do it this way, rather than checking for * target == NIL, because we want to allow SELECT * FROM a zero_column * table. */ if (!found_table) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("SELECT * with no tables specified is not valid"), parser_errposition(pstate, location))); return target; }
static List * ExpandColumnRefStar | ( | ParseState * | pstate, | |
ColumnRef * | cref, | |||
bool | make_target_entry | |||
) | [static] |
Definition at line 970 of file parse_target.c.
References Assert, ereport, errcode(), errmsg(), ERROR, errorMissingRTE(), ExpandAllTables(), ExpandRowReference(), ExpandSingleTable(), ColumnRef::fields, get_database_name(), linitial, list_length(), ColumnRef::location, lsecond, lthird, makeRangeVar(), MyDatabaseId, NameListToString(), NULL, ParseState::p_post_columnref_hook, ParseState::p_pre_columnref_hook, parser_errposition(), refnameRangeTblEntry(), and strVal.
Referenced by transformExpressionList(), and transformTargetList().
{ List *fields = cref->fields; int numnames = list_length(fields); if (numnames == 1) { /* * Target item is a bare '*', expand all tables * * (e.g., SELECT * FROM emp, dept) * * Since the grammar only accepts bare '*' at top level of SELECT, we * need not handle the make_target_entry==false case here. */ Assert(make_target_entry); return ExpandAllTables(pstate, cref->location); } else { /* * Target item is relation.*, expand that table * * (e.g., SELECT emp.*, dname FROM emp, dept) * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with * a list of Vars. However, that would leave us with the RTE's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select * permissions). We could try to remove the whole-row permission bit * after the fact, but duplicating code is less messy. */ char *nspname = NULL; char *relname = NULL; RangeTblEntry *rte = NULL; int levels_up; enum { CRSERR_NO_RTE, CRSERR_WRONG_DB, CRSERR_TOO_MANY } crserr = CRSERR_NO_RTE; /* * Give the PreParseColumnRefHook, if any, first shot. If it returns * non-null then we should use that expression. */ if (pstate->p_pre_columnref_hook != NULL) { Node *node; node = (*pstate->p_pre_columnref_hook) (pstate, cref); if (node != NULL) return ExpandRowReference(pstate, node, make_target_entry); } switch (numnames) { case 2: relname = strVal(linitial(fields)); rte = refnameRangeTblEntry(pstate, nspname, relname, cref->location, &levels_up); break; case 3: nspname = strVal(linitial(fields)); relname = strVal(lsecond(fields)); rte = refnameRangeTblEntry(pstate, nspname, relname, cref->location, &levels_up); break; case 4: { char *catname = strVal(linitial(fields)); /* * We check the catalog name and then ignore it. */ if (strcmp(catname, get_database_name(MyDatabaseId)) != 0) { crserr = CRSERR_WRONG_DB; break; } nspname = strVal(lsecond(fields)); relname = strVal(lthird(fields)); rte = refnameRangeTblEntry(pstate, nspname, relname, cref->location, &levels_up); break; } default: crserr = CRSERR_TOO_MANY; break; } /* * Now give the PostParseColumnRefHook, if any, a chance. We cheat a * bit by passing the RangeTblEntry, not a Var, as the planned * translation. (A single Var wouldn't be strictly correct anyway. * This convention allows hooks that really care to know what is * happening.) */ if (pstate->p_post_columnref_hook != NULL) { Node *node; node = (*pstate->p_post_columnref_hook) (pstate, cref, (Node *) rte); if (node != NULL) { if (rte != NULL) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_COLUMN), errmsg("column reference \"%s\" is ambiguous", NameListToString(cref->fields)), parser_errposition(pstate, cref->location))); return ExpandRowReference(pstate, node, make_target_entry); } } /* * Throw error if no translation found. */ if (rte == NULL) { switch (crserr) { case CRSERR_NO_RTE: errorMissingRTE(pstate, makeRangeVar(nspname, relname, cref->location)); break; case CRSERR_WRONG_DB: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cross-database references are not implemented: %s", NameListToString(cref->fields)), parser_errposition(pstate, cref->location))); break; case CRSERR_TOO_MANY: ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("improper qualified name (too many dotted names): %s", NameListToString(cref->fields)), parser_errposition(pstate, cref->location))); break; } } /* * OK, expand the RTE into fields. */ return ExpandSingleTable(pstate, rte, cref->location, make_target_entry); } }
static List * ExpandIndirectionStar | ( | ParseState * | pstate, | |
A_Indirection * | ind, | |||
bool | make_target_entry, | |||
ParseExprKind | exprKind | |||
) | [static] |
Definition at line 1194 of file parse_target.c.
References copyObject(), ExpandRowReference(), A_Indirection::indirection, list_length(), list_truncate(), and transformExpr().
Referenced by transformExpressionList(), and transformTargetList().
{ Node *expr; /* 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); }
TupleDesc expandRecordVariable | ( | ParseState * | pstate, | |
Var * | var, | |||
int | levelsup | |||
) |
Definition at line 1369 of file parse_target.c.
References Alias::aliasname, Assert, CreateTemplateTupleDesc(), RangeTblEntry::ctelevelsup, CommonTableExpr::ctequery, elog, RangeTblEntry::eref, ERROR, expandRecordVariable(), expandRTE(), TargetEntry::expr, exprCollation(), exprType(), exprTypmod(), forboth, get_expr_result_type(), get_tle_by_resno(), GetCTEForRTE(), GetCTETargetList, GetRTEByRangeTablePosn(), i, InvalidAttrNumber, IsA, RangeTblEntry::joinaliasvars, label, lfirst, list_length(), list_nth(), Var::location, lookup_rowtype_tupdesc_copy(), MemSet, NULL, ParseState::p_rtable, ParseState::parentParseState, RECORDOID, TargetEntry::resjunk, Query::rtable, RTE_CTE, RTE_FUNCTION, RTE_JOIN, RTE_RELATION, RTE_SUBQUERY, RTE_VALUES, RangeTblEntry::rtekind, RangeTblEntry::self_reference, strVal, RangeTblEntry::subquery, Query::targetList, TupleDescInitEntry(), TupleDescInitEntryCollation(), TYPEFUNC_COMPOSITE, Var::varattno, Var::varlevelsup, Var::varno, and Var::vartype.
Referenced by expandRecordVariable(), ExpandRowReference(), and ParseComplexProjection().
{ 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); netlevelsup = var->varlevelsup + levelsup; rte = GetRTEByRangeTablePosn(pstate, var->varno, netlevelsup); attnum = var->varattno; if (attnum == InvalidAttrNumber) { /* Whole-row reference to an RTE, so expand the known fields */ List *names, *vars; ListCell *lname, *lvar; int i; expandRTE(rte, var->varno, 0, var->location, false, &names, &vars); tupleDesc = CreateTemplateTupleDesc(list_length(vars), false); i = 1; forboth(lname, names, lvar, vars) { char *label = strVal(lfirst(lname)); Node *varnode = (Node *) lfirst(lvar); TupleDescInitEntry(tupleDesc, i, label, exprType(varnode), exprTypmod(varnode), 0); TupleDescInitEntryCollation(tupleDesc, i, exprCollation(varnode)); i++; } Assert(lname == NULL && lvar == NULL); /* lists same length? */ return tupleDesc; } expr = (Node *) var; /* default if we can't drill down */ switch (rte->rtekind) { case RTE_RELATION: case RTE_VALUES: /* * This case should not occur: a column of a table or values list * shouldn't have type RECORD. Fall through and fail (most * likely) at the bottom. */ break; case RTE_SUBQUERY: { /* Subselect-in-FROM: examine sub-select's output expr */ 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); expr = (Node *) ste->expr; if (IsA(expr, Var)) { /* * Recurse into the sub-select to see what its Var refers * to. We have to build an additional level of ParseState * to keep in step with varlevelsup in the subselect. */ ParseState mypstate; MemSet(&mypstate, 0, sizeof(mypstate)); 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); if (IsA(expr, Var)) return expandRecordVariable(pstate, (Var *) expr, netlevelsup); /* else fall through to inspect the expression */ break; case RTE_FUNCTION: /* * We couldn't get here unless a function is declared with one of * its result columns as RECORD, which is not allowed. */ break; case RTE_CTE: /* CTE reference: examine subquery's output expr */ if (!rte->self_reference) { CommonTableExpr *cte = GetCTEForRTE(pstate, rte, netlevelsup); TargetEntry *ste; ste = get_tle_by_resno(GetCTETargetList(cte), attnum); 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)) { /* * Recurse into the CTE to see what its Var refers to. We * have to build an additional level of ParseState to keep * in step with varlevelsup in the CTE; furthermore it * could be an outer CTE. */ ParseState mypstate; Index levelsup; MemSet(&mypstate, 0, sizeof(mypstate)); /* this loop must work, since GetCTEForRTE did */ for (levelsup = 0; levelsup < rte->ctelevelsup + netlevelsup; levelsup++) pstate = pstate->parentParseState; mypstate.parentParseState = pstate; mypstate.p_rtable = ((Query *) cte->ctequery)->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; } /* * We now have an expression we can't expand any more, so see if * get_expr_result_type() can do anything with it. If not, pass to * lookup_rowtype_tupdesc() which will probably fail, but will give an * appropriate error message while failing. */ if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) tupleDesc = lookup_rowtype_tupdesc_copy(exprType(expr), exprTypmod(expr)); return tupleDesc; }
static List * ExpandRowReference | ( | ParseState * | pstate, | |
Node * | expr, | |||
bool | make_target_entry | |||
) | [static] |
Definition at line 1270 of file parse_target.c.
References FieldSelect::arg, Assert, tupleDesc::attrs, copyObject(), expandRecordVariable(), ExpandSingleTable(), exprType(), exprTypmod(), FieldSelect::fieldnum, get_expr_result_type(), GetRTEByRangeTablePosn(), i, InvalidAttrNumber, IsA, lappend(), Var::location, lookup_rowtype_tupdesc_copy(), makeNode, makeTargetEntry(), NameStr, tupleDesc::natts, NULL, ParseState::p_next_resno, pstrdup(), RECORDOID, FieldSelect::resultcollid, FieldSelect::resulttype, FieldSelect::resulttypmod, TYPEFUNC_COMPOSITE, Var::varlevelsup, and Var::varno.
Referenced by ExpandColumnRefStar(), and ExpandIndirectionStar().
{ List *result = NIL; TupleDesc tupleDesc; int numAttrs; int i; /* * If the rowtype expression is a whole-row Var, we can expand the fields * as simple Vars. Note: if the RTE is a relation, this case leaves us * with the RTE's selectedCols bitmap showing the whole row as needing * select permission, as well as the individual columns. However, we can * only get here for weird notations like (table.*).*, so it's not worth * trying to clean up --- arguably, the permissions marking is correct * anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) { Var *var = (Var *) expr; RangeTblEntry *rte; rte = GetRTEByRangeTablePosn(pstate, var->varno, var->varlevelsup); return ExpandSingleTable(pstate, rte, var->location, make_target_entry); } /* * Otherwise we have to do it the hard way. Our current implementation is * to generate multiple copies of the expression and do FieldSelects. * (This can be pretty inefficient if the expression involves nontrivial * computation :-(.) * * Verify it's a composite type, and get the tupdesc. We use * get_expr_result_type() because that can handle references to functions * returning anonymous record types. If that fails, use * lookup_rowtype_tupdesc(), which will almost certainly fail as well, but * it will give an appropriate error message. * * If it's a Var of type RECORD, we have to work even harder: we have to * find what the Var refers to, and pass that to get_expr_result_type. * That task is handled by expandRecordVariable(). */ if (IsA(expr, Var) && ((Var *) expr)->vartype == RECORDOID) tupleDesc = expandRecordVariable(pstate, (Var *) expr, 0); else if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) tupleDesc = lookup_rowtype_tupdesc_copy(exprType(expr), exprTypmod(expr)); Assert(tupleDesc); /* Generate a list of references to the individual fields */ numAttrs = tupleDesc->natts; for (i = 0; i < numAttrs; i++) { Form_pg_attribute att = tupleDesc->attrs[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; }
static List * ExpandSingleTable | ( | ParseState * | pstate, | |
RangeTblEntry * | rte, | |||
int | location, | |||
bool | make_target_entry | |||
) | [static] |
Definition at line 1221 of file parse_target.c.
References expandRelAttrs(), expandRTE(), lfirst, markVarForSelectPriv(), NULL, RangeTblEntry::requiredPerms, and RTERangeTablePosn().
Referenced by ExpandColumnRefStar(), and ExpandRowReference().
{ int sublevels_up; int rtindex; rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up); if (make_target_entry) { /* expandRelAttrs handles permissions marking */ return expandRelAttrs(pstate, rte, rtindex, sublevels_up, location); } else { List *vars; ListCell *l; expandRTE(rte, rtindex, sublevels_up, location, false, NULL, &vars); /* * Require read access to the table. This is normally redundant with * the markVarForSelectPriv calls below, but not if the table has zero * columns. */ rte->requiredPerms |= ACL_SELECT; /* Require read access to each column */ foreach(l, vars) { Var *var = (Var *) lfirst(l); markVarForSelectPriv(pstate, var, rte); } return vars; } }
char* FigureColname | ( | Node * | node | ) |
Definition at line 1539 of file parse_target.c.
References FigureColnameInternal(), name, and NULL.
Referenced by transformRangeFunction(), transformTargetEntry(), and transformXmlExpr().
{ char *name = NULL; (void) FigureColnameInternal(node, &name); if (name != NULL) return name; /* default result if we can't guess anything */ return "?column?"; }
static int FigureColnameInternal | ( | Node * | node, | |
char ** | name | |||
) | [static] |
Definition at line 1578 of file parse_target.c.
References AEXPR_NULLIF, ALL_SUBLINK, ANY_SUBLINK, arg, A_Indirection::arg, ARRAY_SUBLINK, CTE_SUBLINK, EXISTS_SUBLINK, EXPR_SUBLINK, i, A_Indirection::indirection, IS_DOCUMENT, IS_GREATEST, IS_LEAST, IS_XMLCONCAT, IS_XMLELEMENT, IS_XMLFOREST, IS_XMLPARSE, IS_XMLPI, IS_XMLROOT, IS_XMLSERIALIZE, IsA, lfirst, linitial, llast, nodeTag, NULL, TargetEntry::resname, ROWCOMPARE_SUBLINK, strVal, SubLink::subselect, T_A_ArrayExpr, T_A_Expr, T_A_Indirection, T_CaseExpr, T_CoalesceExpr, T_CollateClause, T_ColumnRef, T_FuncCall, T_MinMaxExpr, T_RowExpr, T_SubLink, T_TypeCast, T_XmlExpr, T_XmlSerialize, and Query::targetList.
Referenced by FigureColname(), and FigureIndexColname().
{ int strength = 0; if (node == NULL) return strength; switch (nodeTag(node)) { case T_ColumnRef: { char *fname = NULL; ListCell *l; /* find last field name, if any, ignoring "*" */ foreach(l, ((ColumnRef *) node)->fields) { Node *i = lfirst(l); if (IsA(i, String)) fname = strVal(i); } if (fname) { *name = fname; return 2; } } break; case T_A_Indirection: { A_Indirection *ind = (A_Indirection *) node; char *fname = NULL; ListCell *l; /* find last field name, if any, ignoring "*" and subscripts */ foreach(l, ind->indirection) { Node *i = lfirst(l); if (IsA(i, String)) fname = strVal(i); } if (fname) { *name = fname; return 2; } return FigureColnameInternal(ind->arg, name); } break; case T_FuncCall: *name = strVal(llast(((FuncCall *) node)->funcname)); return 2; case T_A_Expr: /* make nullif() act like a regular function */ if (((A_Expr *) node)->kind == AEXPR_NULLIF) { *name = "nullif"; return 2; } break; case T_TypeCast: strength = FigureColnameInternal(((TypeCast *) node)->arg, name); if (strength <= 1) { if (((TypeCast *) node)->typeName != NULL) { *name = strVal(llast(((TypeCast *) node)->typeName->names)); return 1; } } break; case T_CollateClause: return FigureColnameInternal(((CollateClause *) node)->arg, name); case T_SubLink: switch (((SubLink *) node)->subLinkType) { case EXISTS_SUBLINK: *name = "exists"; return 2; case ARRAY_SUBLINK: *name = "array"; return 2; case EXPR_SUBLINK: { /* Get column name of the subquery's single target */ SubLink *sublink = (SubLink *) node; Query *query = (Query *) sublink->subselect; /* * The subquery has probably already been transformed, * but let's be careful and check that. (The reason * we can see a transformed subquery here is that * transformSubLink is lazy and modifies the SubLink * node in-place.) */ if (IsA(query, Query)) { TargetEntry *te = (TargetEntry *) linitial(query->targetList); if (te->resname) { *name = te->resname; return 2; } } } break; /* As with other operator-like nodes, these have no names */ 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"; return 1; } break; case T_A_ArrayExpr: /* make ARRAY[] act like a function */ *name = "array"; return 2; case T_RowExpr: /* make ROW() act like a function */ *name = "row"; return 2; case T_CoalesceExpr: /* make coalesce() act like a regular function */ *name = "coalesce"; return 2; case T_MinMaxExpr: /* make greatest/least act like a regular function */ switch (((MinMaxExpr *) node)->op) { case IS_GREATEST: *name = "greatest"; return 2; case IS_LEAST: *name = "least"; return 2; } break; case T_XmlExpr: /* make SQL/XML functions act like a regular function */ switch (((XmlExpr *) node)->op) { case IS_XMLCONCAT: *name = "xmlconcat"; return 2; case IS_XMLELEMENT: *name = "xmlelement"; return 2; case IS_XMLFOREST: *name = "xmlforest"; return 2; case IS_XMLPARSE: *name = "xmlparse"; return 2; case IS_XMLPI: *name = "xmlpi"; return 2; case IS_XMLROOT: *name = "xmlroot"; return 2; case IS_XMLSERIALIZE: *name = "xmlserialize"; return 2; case IS_DOCUMENT: /* nothing */ break; } break; case T_XmlSerialize: *name = "xmlserialize"; return 2; default: break; } return strength; }
char* FigureIndexColname | ( | Node * | node | ) |
Definition at line 1558 of file parse_target.c.
References FigureColnameInternal(), and name.
Referenced by transformIndexStmt().
{ char *name = NULL; (void) FigureColnameInternal(node, &name); return name; }
static void markTargetListOrigin | ( | ParseState * | pstate, | |
TargetEntry * | tle, | |||
Var * | var, | |||
int | levelsup | |||
) | [static] |
Definition at line 272 of file parse_target.c.
References Alias::aliasname, Assert, elog, RangeTblEntry::eref, ERROR, get_tle_by_resno(), GetCTEForRTE(), GetCTETargetList, GetRTEByRangeTablePosn(), InvalidAttrNumber, IsA, RangeTblEntry::joinaliasvars, list_length(), list_nth(), NULL, RangeTblEntry::relid, TargetEntry::resjunk, TargetEntry::resorigcol, TargetEntry::resorigtbl, RTE_CTE, RTE_FUNCTION, RTE_JOIN, RTE_RELATION, RTE_SUBQUERY, RTE_VALUES, RangeTblEntry::rtekind, RangeTblEntry::self_reference, RangeTblEntry::subquery, Query::targetList, Var::varattno, Var::varlevelsup, and Var::varno.
Referenced by markTargetListOrigins().
{ int netlevelsup; RangeTblEntry *rte; AttrNumber attnum; if (var == NULL || !IsA(var, Var)) return; netlevelsup = var->varlevelsup + levelsup; rte = GetRTEByRangeTablePosn(pstate, var->varno, netlevelsup); attnum = var->varattno; 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: /* Join RTE --- recursively inspect the alias variable */ if (attnum != InvalidAttrNumber) { Var *aliasvar; Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars)); aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1); markTargetListOrigin(pstate, tle, aliasvar, netlevelsup); } break; case RTE_FUNCTION: case RTE_VALUES: /* not a simple relation, leave it unmarked */ break; case RTE_CTE: /* * CTE reference: copy up from the subquery, if possible. If the * RTE is a recursive self-reference then we can't do anything * because we haven't finished analyzing it yet. However, it's no * big loss because we must be down inside the recursive term of a * recursive CTE, and so any markings on the current targetlist * are not going to affect the results anyway. */ if (attnum != InvalidAttrNumber && !rte->self_reference) { CommonTableExpr *cte = GetCTEForRTE(pstate, rte, netlevelsup); TargetEntry *ste; ste = get_tle_by_resno(GetCTETargetList(cte), 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; } }
void markTargetListOrigins | ( | ParseState * | pstate, | |
List * | targetlist | |||
) |
Definition at line 250 of file parse_target.c.
References TargetEntry::expr, lfirst, and markTargetListOrigin().
Referenced by transformReturningList(), and transformSelectStmt().
{ ListCell *l; foreach(l, targetlist) { TargetEntry *tle = (TargetEntry *) lfirst(l); markTargetListOrigin(pstate, tle, (Var *) tle->expr, 0); } }
Expr* transformAssignedExpr | ( | ParseState * | pstate, | |
Expr * | expr, | |||
ParseExprKind | exprKind, | |||
char * | colname, | |||
int | attrno, | |||
List * | indirection, | |||
int | location | |||
) |
Definition at line 373 of file parse_target.c.
References Assert, attnumTypeId(), tupleDesc::attrs, COERCE_IMPLICIT_CAST, coerce_to_target_type(), COERCION_ASSIGNMENT, SetToDefault::collation, ereport, errcode(), errhint(), errmsg(), ERROR, EXPR_KIND_NONE, exprLocation(), exprType(), format_type_be(), IsA, linitial, list_head(), make_var(), makeNullConst(), NULL, ParseState::p_expr_kind, ParseState::p_is_insert, ParseState::p_target_rangetblentry, ParseState::p_target_relation, parser_errposition(), RelationData::rd_att, transformAssignmentIndirection(), SetToDefault::typeId, and SetToDefault::typeMod.
Referenced by transformInsertRow(), and updateTargetListEntry().
{ 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; /* * Save and restore identity of expression type we're parsing. We must * set p_expr_kind here because we can parse subscripts without going * through transformExpr(). */ Assert(exprKind != EXPR_KIND_NONE); sv_expr_kind = pstate->p_expr_kind; pstate->p_expr_kind = exprKind; Assert(rd != NULL); if (attrno <= 0) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot assign to system column \"%s\"", colname), parser_errposition(pstate, location))); attrtype = attnumTypeId(rd, attrno); attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; attrcollation = rd->rd_att->attrs[attrno - 1]->attcollation; /* * If the expression is a DEFAULT placeholder, insert the attribute's * type/typmod/collation into it so that exprType etc will report the * right things. (We expect that the eventually substituted default * expression will in fact have this type and typmod. The collation * likely doesn't matter, but let's set it correctly anyway.) Also, * reject trying to update a subfield or array element with DEFAULT, since * there can't be any default for portions of a column. */ if (expr && IsA(expr, SetToDefault)) { SetToDefault *def = (SetToDefault *) expr; 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); /* * If there is indirection on the target column, prepare an array or * subfield assignment expression. This will generate a new column value * that the source value has been inserted into, which can then be placed * in the new tuple constructed by INSERT or UPDATE. */ if (indirection) { Node *colVar; if (pstate->p_is_insert) { /* * The command is INSERT INTO table (col.something) ... so there * is not really a source value to work with. Insert a NULL * constant as the source value. */ colVar = (Node *) makeNullConst(attrtype, attrtypmod, attrcollation); } else { /* * Build a Var for the column to be updated. */ colVar = (Node *) make_var(pstate, pstate->p_target_rangetblentry, attrno, location); } expr = (Expr *) transformAssignmentIndirection(pstate, colVar, colname, false, attrtype, attrtypmod, attrcollation, list_head(indirection), (Node *) expr, location); } else { /* * For normal non-qualified target column, do type checking and * coercion. */ Node *orig_expr = (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; }
static Node * transformAssignmentIndirection | ( | ParseState * | pstate, | |
Node * | basenode, | |||
const char * | targetName, | |||
bool | targetIsArray, | |||
Oid | targetTypeId, | |||
int32 | targetTypMod, | |||
Oid | targetCollation, | |||
ListCell * | indirection, | |||
Node * | rhs, | |||
int | location | |||
) | [static] |
Definition at line 593 of file parse_target.c.
References FieldStore::arg, Assert, COERCE_IMPLICIT_CAST, coerce_to_target_type(), COERCION_ASSIGNMENT, CaseTestExpr::collation, ereport, errcode(), errhint(), errmsg(), ERROR, exprType(), FieldStore::fieldnums, for_each_cell, format_type_be(), get_attnum(), get_atttypetypmodcoll(), i, InvalidAttrNumber, IsA, lappend(), lfirst, list_make1, list_make1_int, lnext, makeNode, FieldStore::newvals, NULL, parser_errposition(), FieldStore::resulttype, strVal, transformAssignmentSubscripts(), CaseTestExpr::typeId, typeidTypeRelid(), and CaseTestExpr::typeMod.
Referenced by transformAssignedExpr(), and transformAssignmentSubscripts().
{ Node *result; List *subscripts = NIL; bool isSlice = false; ListCell *i; if (indirection && !basenode) { /* Set up a substitution. We reuse CaseTestExpr for this. */ CaseTestExpr *ctest = makeNode(CaseTestExpr); ctest->typeId = targetTypeId; ctest->typeMod = targetTypMod; ctest->collation = targetCollation; basenode = (Node *) ctest; } /* * We have to split any field-selection operations apart from * subscripting. Adjacent A_Indices nodes have to be treated as a single * multidimensional subscript operation. */ for_each_cell(i, indirection) { Node *n = lfirst(i); if (IsA(n, A_Indices)) { subscripts = lappend(subscripts, n); if (((A_Indices *) n)->lidx != NULL) isSlice = true; } else if (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 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, isSlice, i, rhs, location); } /* No subscripts, so can process field selection here */ typrelid = typeidTypeRelid(targetTypeId); 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))); get_atttypetypmodcoll(typrelid, attnum, &fieldTypeId, &fieldTypMod, &fieldCollation); /* recurse to create appropriate RHS for field assign */ rhs = transformAssignmentIndirection(pstate, NULL, strVal(n), false, fieldTypeId, fieldTypMod, fieldCollation, lnext(i), rhs, location); /* and build a FieldStore node */ fstore = makeNode(FieldStore); fstore->arg = (Expr *) basenode; fstore->newvals = list_make1(rhs); fstore->fieldnums = list_make1_int(attnum); fstore->resulttype = targetTypeId; return (Node *) fstore; } } /* process trailing subscripts, if any */ if (subscripts) { /* recurse, and then return because we're done */ return transformAssignmentSubscripts(pstate, basenode, targetName, targetTypeId, targetTypMod, targetCollation, subscripts, isSlice, NULL, rhs, location); } /* base case: just coerce RHS to match target type ID */ result = coerce_to_target_type(pstate, rhs, exprType(rhs), targetTypeId, targetTypMod, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST, -1); if (result == NULL) { if (targetIsArray) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("array 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; }
static Node * transformAssignmentSubscripts | ( | ParseState * | pstate, | |
Node * | basenode, | |||
const char * | targetName, | |||
Oid | targetTypeId, | |||
int32 | targetTypMod, | |||
Oid | targetCollation, | |||
List * | subscripts, | |||
bool | isSlice, | |||
ListCell * | next_indirection, | |||
Node * | rhs, | |||
int | location | |||
) | [static] |
Definition at line 778 of file parse_target.c.
References Assert, COERCE_IMPLICIT_CAST, coerce_to_target_type(), COERCION_ASSIGNMENT, ereport, errcode(), errmsg(), ERROR, exprType(), format_type_be(), get_typcollation(), NIL, NULL, parser_errposition(), transformArraySubscripts(), transformArrayType(), and transformAssignmentIndirection().
Referenced by transformAssignmentIndirection().
{ Node *result; Oid arrayType; int32 arrayTypMod; Oid elementTypeId; Oid typeNeeded; Oid collationNeeded; Assert(subscripts != NIL); /* Identify the actual array type and element type involved */ arrayType = targetTypeId; arrayTypMod = targetTypMod; elementTypeId = transformArrayType(&arrayType, &arrayTypMod); /* Identify type that RHS must provide */ typeNeeded = isSlice ? arrayType : elementTypeId; /* * Array normally has same collation as elements, but there's an * exception: we might be subscripting a domain over an array type. In * that case use collation of the base type. */ if (arrayType == targetTypeId) collationNeeded = targetCollation; else collationNeeded = get_typcollation(arrayType); /* recurse to create appropriate RHS for array assign */ rhs = transformAssignmentIndirection(pstate, NULL, targetName, true, typeNeeded, arrayTypMod, collationNeeded, next_indirection, rhs, location); /* process subscripts */ result = (Node *) transformArraySubscripts(pstate, basenode, arrayType, elementTypeId, arrayTypMod, subscripts, rhs); /* If target was a domain over array, need to coerce up to the domain */ if (arrayType != targetTypeId) { result = coerce_to_target_type(pstate, result, exprType(result), targetTypeId, targetTypMod, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST, -1); /* probably shouldn't fail, but check */ if (result == NULL) ereport(ERROR, (errcode(ERRCODE_CANNOT_COERCE), errmsg("cannot cast type %s to %s", format_type_be(exprType(result)), format_type_be(targetTypeId)), parser_errposition(pstate, location))); } return result; }
List* transformExpressionList | ( | ParseState * | pstate, | |
List * | exprlist, | |||
ParseExprKind | exprKind | |||
) |
Definition at line 188 of file parse_target.c.
References ExpandColumnRefStar(), ExpandIndirectionStar(), ColumnRef::fields, A_Indirection::indirection, IsA, lappend(), lfirst, list_concat(), llast, and transformExpr().
Referenced by transformRowExpr(), and transformValuesClause().
{ List *result = NIL; ListCell *lc; foreach(lc, exprlist) { Node *e = (Node *) lfirst(lc); /* * Check for "something.*". Depending on the complexity of the * "something", the star could appear as the last field in ColumnRef, * or as the last indirection item in A_Indirection. */ if (IsA(e, ColumnRef)) { ColumnRef *cref = (ColumnRef *) e; if (IsA(llast(cref->fields), A_Star)) { /* It is something.*, expand into multiple items */ result = list_concat(result, ExpandColumnRefStar(pstate, cref, false)); continue; } } else if (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.*", so transform as a single expression */ result = lappend(result, transformExpr(pstate, e, exprKind)); } return result; }
TargetEntry* transformTargetEntry | ( | ParseState * | pstate, | |
Node * | node, | |||
Node * | expr, | |||
ParseExprKind | exprKind, | |||
char * | colname, | |||
bool | resjunk | |||
) |
Definition at line 85 of file parse_target.c.
References FigureColname(), makeTargetEntry(), NULL, ParseState::p_next_resno, and transformExpr().
Referenced by findTargetlistEntrySQL99(), and transformTargetList().
{ /* Transform the node if caller didn't do it already */ if (expr == NULL) expr = transformExpr(pstate, node, exprKind); if (colname == NULL && !resjunk) { /* * Generate a suitable column name for a column without any explicit * 'AS ColumnName' clause. */ colname = FigureColname(node); } return makeTargetEntry((Expr *) expr, (AttrNumber) pstate->p_next_resno++, colname, resjunk); }
List* transformTargetList | ( | ParseState * | pstate, | |
List * | targetlist, | |||
ParseExprKind | exprKind | |||
) |
Definition at line 121 of file parse_target.c.
References ExpandColumnRefStar(), ExpandIndirectionStar(), ColumnRef::fields, A_Indirection::indirection, IsA, lappend(), lfirst, list_concat(), llast, ResTarget::name, NULL, transformTargetEntry(), and ResTarget::val.
Referenced by transformReturningList(), transformSelectStmt(), and transformUpdateStmt().
{ List *p_target = NIL; ListCell *o_target; foreach(o_target, targetlist) { ResTarget *res = (ResTarget *) lfirst(o_target); /* * Check for "something.*". Depending on the complexity of the * "something", the star could appear as the last field in ColumnRef, * or as the last indirection item in A_Indirection. */ if (IsA(res->val, ColumnRef)) { ColumnRef *cref = (ColumnRef *) res->val; if (IsA(llast(cref->fields), A_Star)) { /* It is something.*, expand into multiple items */ p_target = list_concat(p_target, ExpandColumnRefStar(pstate, cref, true)); continue; } } else if (IsA(res->val, A_Indirection)) { A_Indirection *ind = (A_Indirection *) res->val; if (IsA(llast(ind->indirection), A_Star)) { /* It is something.*, expand into multiple items */ p_target = list_concat(p_target, ExpandIndirectionStar(pstate, ind, true, exprKind)); continue; } } /* * Not "something.*", so transform as a single expression */ p_target = lappend(p_target, transformTargetEntry(pstate, res->val, NULL, exprKind, res->name, false)); } return p_target; }
void updateTargetListEntry | ( | ParseState * | pstate, | |
TargetEntry * | tle, | |||
char * | colname, | |||
int | attrno, | |||
List * | indirection, | |||
int | location | |||
) |
Definition at line 534 of file parse_target.c.
References TargetEntry::expr, EXPR_KIND_UPDATE_TARGET, TargetEntry::resname, TargetEntry::resno, and transformAssignedExpr().
Referenced by transformUpdateStmt().
{ /* Fix up expression as needed */ tle->expr = transformAssignedExpr(pstate, tle->expr, EXPR_KIND_UPDATE_TARGET, colname, attrno, indirection, location); /* * Set the resno to identify the target column --- the rewriter and * planner depend on this. We also set the resname to identify the target * column, but this is only for debugging purposes; it should not be * relied on. (In particular, it might be out of date in a stored rule.) */ tle->resno = (AttrNumber) attrno; tle->resname = colname; }