📄 parse_target.c
字号:
/*------------------------------------------------------------------------- * * parse_target.c * handle target lists * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /usr/local/cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.42 1999/06/17 22:21:41 tgl Exp $ * *------------------------------------------------------------------------- */#include <stdio.h>#include <stdlib.h>#include <string.h>#include "postgres.h"#include "catalog/pg_type.h"#include "nodes/makefuncs.h"#include "nodes/primnodes.h"#include "nodes/print.h"#include "parser/parse_expr.h"#include "parser/parse_func.h"#include "parser/parse_node.h"#include "parser/parse_relation.h"#include "parser/parse_target.h"#include "parser/parse_coerce.h"#include "utils/builtins.h"#include "utils/lsyscache.h"#include "utils/syscache.h"static List *ExpandAllTables(ParseState *pstate);static char *FigureColname(Node *expr, Node *resval);static Node *SizeTargetExpr(ParseState *pstate, Node *expr, Oid attrtype, int32 attrtypmod);/* MakeTargetEntryIdent() * Transforms an Ident Node to a Target Entry * Created this function to allow the ORDER/GROUP BY clause to be able * to construct a TargetEntry from an Ident. * * resjunk = TRUE will hide the target entry in the final result tuple. * daveh@insightdist.com 5/20/98 * * Added more conversion logic to match up types from source to target. * - thomas 1998-06-02 */TargetEntry *MakeTargetEntryIdent(ParseState *pstate, Node *node, char **resname, char *refname, char *colname, bool resjunk){ Node *expr = NULL; Oid attrtype_target; TargetEntry *tent = makeNode(TargetEntry); if (pstate->p_is_insert && !resjunk) { /* * Assign column name of destination column to the new TLE. XXX * this is probably WRONG in INSERT ... SELECT case, since * handling of GROUP BY and so forth probably should use the * source table's names not the destination's names. */ if (pstate->p_insert_columns != NIL) { Ident *id = lfirst(pstate->p_insert_columns); *resname = id->name; pstate->p_insert_columns = lnext(pstate->p_insert_columns); } else elog(ERROR, "INSERT has more expressions than target columns"); } if ((pstate->p_is_insert || pstate->p_is_update) && !resjunk) { Oid attrtype_id; int resdomno_id, resdomno_target; RangeTblEntry *rte; char *target_colname; int32 attrtypmod, attrtypmod_target; target_colname = *resname; /* * this looks strange to me, returning an empty TargetEntry bjm * 1998/08/24 */ if (target_colname == NULL || colname == NULL) return tent; if (refname != NULL) rte = refnameRangeTableEntry(pstate, refname); else { rte = colnameRangeTableEntry(pstate, colname); if (rte == (RangeTblEntry *) NULL) elog(ERROR, "Attribute %s not found", colname); refname = rte->refname; } resdomno_id = get_attnum(rte->relid, colname); attrtype_id = get_atttype(rte->relid, resdomno_id); attrtypmod = get_atttypmod(rte->relid, resdomno_id); resdomno_target = attnameAttNum(pstate->p_target_relation, target_colname); attrtype_target = attnumTypeId(pstate->p_target_relation, resdomno_target); attrtypmod_target = get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target); if ((attrtype_id != attrtype_target) || ((attrtypmod_target >= 0) && (attrtypmod_target != attrtypmod))) { if (can_coerce_type(1, &attrtype_id, &attrtype_target)) { expr = coerce_type(pstate, node, attrtype_id, attrtype_target, get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target)); expr = transformExpr(pstate, expr, EXPR_COLUMN_FIRST); tent = MakeTargetEntryExpr(pstate, *resname, expr, false, false); expr = tent->expr; } else { elog(ERROR, "Unable to convert %s to %s for column %s", typeidTypeName(attrtype_id), typeidTypeName(attrtype_target), target_colname); } } } /* * here we want to look for column names only, not relation names * (even though they can be stored in Ident nodes, too) */ if (expr == NULL) { char *name; int32 type_mod; name = ((*resname != NULL) ? *resname : colname); expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST); attrtype_target = exprType(expr); if (nodeTag(expr) == T_Var) type_mod = ((Var *) expr)->vartypmod; else type_mod = -1; tent->resdom = makeResdom((AttrNumber) pstate->p_last_resno++, (Oid) attrtype_target, type_mod, name, (Index) 0, (Oid) 0, resjunk); tent->expr = expr; } return tent;} /* MakeTargetEntryIdent() *//* MakeTargetEntryExpr() * Make a TargetEntry from an expression. * arrayRef is a list of transformed A_Indices. * * For type mismatches between expressions and targets, use the same * techniques as for function and operator type coersion. * - thomas 1998-05-08 * * Added resjunk flag and made extern so that it can be use by GROUP/ * ORDER BY a function or expression not in the target_list * - daveh@insightdist.com 1998-07-31 */TargetEntry *MakeTargetEntryExpr(ParseState *pstate, char *colname, Node *expr, List *arrayRef, bool resjunk){ Oid type_id, attrtype; int32 type_mod, attrtypmod; int resdomno; Relation rd; bool attrisset; Resdom *resnode; if (expr == NULL) elog(ERROR, "Invalid use of NULL expression (internal error)"); type_id = exprType(expr); if (nodeTag(expr) == T_Var) type_mod = ((Var *) expr)->vartypmod; else type_mod = -1; /* Process target columns that will be receiving results */ if ((pstate->p_is_insert || pstate->p_is_update) && !resjunk) { /* * insert or update query -- insert, update work only on one * relation, so multiple occurence of same resdomno is bogus */ rd = pstate->p_target_relation; Assert(rd != NULL); resdomno = attnameAttNum(rd, colname); if (resdomno <= 0) elog(ERROR, "Cannot assign to system attribute '%s'", colname); attrisset = attnameIsSet(rd, colname); attrtype = attnumTypeId(rd, resdomno); if ((arrayRef != NIL) && (lfirst(arrayRef) == NIL)) attrtype = GetArrayElementType(attrtype); attrtypmod = rd->rd_att->attrs[resdomno - 1]->atttypmod; /* * Check for InvalidOid since that seems to indicate a NULL * constant... */ if (type_id != InvalidOid) { /* Mismatch on types? then try to coerce to target... */ if (attrtype != type_id) { Oid typelem; if (arrayRef && !(((A_Indices *) lfirst(arrayRef))->lidx)) typelem = typeTypElem(typeidType(attrtype)); else typelem = attrtype; expr = CoerceTargetExpr(pstate, expr, type_id, typelem); if (!HeapTupleIsValid(expr)) elog(ERROR, "Attribute '%s' is of type '%s'" " but expression is of type '%s'" "\n\tYou will need to rewrite or cast the expression", colname, typeidTypeName(attrtype), typeidTypeName(type_id)); } /* * Apparently going to a fixed-length string? Then explicitly * size for storage... */ if (attrtypmod > 0) expr = SizeTargetExpr(pstate, expr, attrtype, attrtypmod); } if (arrayRef != NIL) { Expr *target_expr; Attr *att = makeNode(Attr); List *ar = arrayRef; List *upperIndexpr = NIL; List *lowerIndexpr = NIL; att->relname = pstrdup(RelationGetRelationName(rd)->data); att->attrs = lcons(makeString(colname), NIL); target_expr = (Expr *) ParseNestedFuncOrColumn(pstate, att, &pstate->p_last_resno, EXPR_COLUMN_FIRST); while (ar != NIL) { A_Indices *ind = lfirst(ar); if (lowerIndexpr || (!upperIndexpr && ind->lidx)) { /* * XXX assume all lowerIndexpr is non-null in this * case */ lowerIndexpr = lappend(lowerIndexpr, ind->lidx); } upperIndexpr = lappend(upperIndexpr, ind->uidx); ar = lnext(ar); } expr = (Node *) make_array_set(target_expr, upperIndexpr, lowerIndexpr, (Expr *) expr); attrtype = attnumTypeId(rd, resdomno); attrtypmod = get_atttypmod(RelationGetRelid(rd), resdomno); } } else { resdomno = pstate->p_last_resno++; attrtype = type_id; attrtypmod = type_mod; } resnode = makeResdom((AttrNumber) resdomno, (Oid) attrtype, attrtypmod, colname, (Index) 0, (Oid) 0, resjunk); return makeTargetEntry(resnode, expr);} /* MakeTargetEntryExpr() *//* * MakeTargetEntryCase() * Make a TargetEntry from a case node. */static TargetEntry *MakeTargetEntryCase(ParseState *pstate, ResTarget *res){ TargetEntry *tent; CaseExpr *expr; Resdom *resnode; int resdomno; Oid type_id; int32 type_mod; expr = (CaseExpr *) transformExpr(pstate, (Node *) res->val, EXPR_COLUMN_FIRST); type_id = expr->casetype; type_mod = -1; handleTargetColname(pstate, &res->name, NULL, NULL); if (res->name == NULL) res->name = FigureColname((Node *) expr, res->val); resdomno = pstate->p_last_resno++; resnode = makeResdom((AttrNumber) resdomno, (Oid) type_id, type_mod, res->name, (Index) 0, (Oid) 0, false); tent = makeNode(TargetEntry); tent->resdom = resnode; tent->expr = (Node *) expr; return tent;} /* MakeTargetEntryCase() *//* * MakeTargetEntryComplex() * Make a TargetEntry from a complex node. */static TargetEntry *MakeTargetEntryComplex(ParseState *pstate, ResTarget *res){ Node *expr = transformExpr(pstate, (Node *) res->val, EXPR_COLUMN_FIRST); handleTargetColname(pstate, &res->name, NULL, NULL); /* note indirection has not been transformed */ if (pstate->p_is_insert && res->indirection != NIL) { /* this is an array assignment */ char *val; char *str, *save_str; List *elt; int i = 0, ndims; int lindx[MAXDIM], uindx[MAXDIM]; int resdomno; Relation rd; Value *constval; if (exprType(expr) != UNKNOWNOID || !IsA(expr, Const)) elog(ERROR, "String constant expected (internal error)"); val = (char *) textout((struct varlena *) ((Const *) expr)->constvalue); str = save_str = (char *) palloc(strlen(val) + MAXDIM * 25 + 2); foreach(elt, res->indirection) { A_Indices *aind = (A_Indices *) lfirst(elt); aind->uidx = transformExpr(pstate, aind->uidx, EXPR_COLUMN_FIRST); if (!IsA(aind->uidx, Const)) elog(ERROR, "Array Index for Append should be a constant"); uindx[i] = ((Const *) aind->uidx)->constvalue; if (aind->lidx != NULL) { aind->lidx = transformExpr(pstate, aind->lidx, EXPR_COLUMN_FIRST); if (!IsA(aind->lidx, Const)) elog(ERROR, "Array Index for Append should be a constant"); lindx[i] = ((Const *) aind->lidx)->constvalue; } else lindx[i] = 1; if (lindx[i] > uindx[i]) elog(ERROR, "Lower index cannot be greater than upper index"); sprintf(str, "[%d:%d]", lindx[i], uindx[i]); str += strlen(str); i++; } sprintf(str, "=%s", val); rd = pstate->p_target_relation; Assert(rd != NULL); resdomno = attnameAttNum(rd, res->name); ndims = attnumAttNelems(rd, resdomno); if (i != ndims) elog(ERROR, "Array dimensions do not match"); constval = makeNode(Value); constval->type = T_String; constval->val.str = save_str; return MakeTargetEntryExpr(pstate, res->name, (Node *) make_const(constval), NULL, false); pfree(save_str); } else { /* this is not an array assignment */ char *colname = res->name; if (colname == NULL) { /* * if you're wondering why this is here, look at the yacc * grammar for why a name can be missing. -ay */ colname = FigureColname(expr, res->val); } if (res->indirection) { List *ilist = res->indirection;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -