📄 rewritehandler.c
字号:
sub_action->commandType != CMD_UTILITY) { sub_action = (Query *) ResolveNew((Node *) sub_action, new_varno, 0, rt_fetch(new_varno, sub_action->rtable), parsetree->targetList, event, current_varno); if (sub_action_ptr) *sub_action_ptr = sub_action; else rule_action = sub_action; } return rule_action;}/* * Copy the query's jointree list, and optionally attempt to remove any * occurrence of the given rt_index as a top-level join item (we do not look * for it within join items; this is OK because we are only expecting to find * it as an UPDATE or DELETE target relation, which will be at the top level * of the join). Returns modified jointree list --- this is a separate copy * sharing no nodes with the original. */static List *adjustJoinTreeList(Query *parsetree, bool removert, int rt_index){ List *newjointree = copyObject(parsetree->jointree->fromlist); ListCell *l; if (removert) { foreach(l, newjointree) { RangeTblRef *rtr = lfirst(l); if (IsA(rtr, RangeTblRef) && rtr->rtindex == rt_index) { newjointree = list_delete_ptr(newjointree, rtr); /* * foreach is safe because we exit loop after list_delete... */ break; } } } return newjointree;}/* * rewriteTargetList - rewrite INSERT/UPDATE targetlist into standard form * * This has the following responsibilities: * * 1. For an INSERT, add tlist entries to compute default values for any * attributes that have defaults and are not assigned to in the given tlist. * (We do not insert anything for default-less attributes, however. The * planner will later insert NULLs for them, but there's no reason to slow * down rewriter processing with extra tlist nodes.) Also, for both INSERT * and UPDATE, replace explicit DEFAULT specifications with column default * expressions. * * 2. Merge multiple entries for the same target attribute, or declare error * if we can't. Multiple entries are only allowed for INSERT/UPDATE of * portions of an array or record field, for example * UPDATE table SET foo[2] = 42, foo[4] = 43; * We can merge such operations into a single assignment op. Essentially, * the expression we want to produce in this case is like * foo = array_set(array_set(foo, 2, 42), 4, 43) * * 3. Sort the tlist into standard order: non-junk fields in order by resno, * then junk fields (these in no particular order). * * We must do items 1 and 2 before firing rewrite rules, else rewritten * references to NEW.foo will produce wrong or incomplete results. Item 3 * is not needed for rewriting, but will be needed by the planner, and we * can do it essentially for free while handling items 1 and 2. */static voidrewriteTargetList(Query *parsetree, Relation target_relation){ CmdType commandType = parsetree->commandType; TargetEntry **new_tles; List *new_tlist = NIL; List *junk_tlist = NIL; Form_pg_attribute att_tup; int attrno, next_junk_attrno, numattrs; ListCell *temp; /* * We process the normal (non-junk) attributes by scanning the input tlist * once and transferring TLEs into an array, then scanning the array to * build an output tlist. This avoids O(N^2) behavior for large numbers * of attributes. * * Junk attributes are tossed into a separate list during the same tlist * scan, then appended to the reconstructed tlist. */ numattrs = RelationGetNumberOfAttributes(target_relation); new_tles = (TargetEntry **) palloc0(numattrs * sizeof(TargetEntry *)); next_junk_attrno = numattrs + 1; foreach(temp, parsetree->targetList) { TargetEntry *old_tle = (TargetEntry *) lfirst(temp); if (!old_tle->resjunk) { /* Normal attr: stash it into new_tles[] */ attrno = old_tle->resno; if (attrno < 1 || attrno > numattrs) elog(ERROR, "bogus resno %d in targetlist", attrno); att_tup = target_relation->rd_att->attrs[attrno - 1]; /* We can (and must) ignore deleted attributes */ if (att_tup->attisdropped) continue; /* Merge with any prior assignment to same attribute */ new_tles[attrno - 1] = process_matched_tle(old_tle, new_tles[attrno - 1], NameStr(att_tup->attname)); } else { /* * Copy all resjunk tlist entries to junk_tlist, and assign them * resnos above the last real resno. * * Typical junk entries include ORDER BY or GROUP BY expressions * (are these actually possible in an INSERT or UPDATE?), system * attribute references, etc. */ /* Get the resno right, but don't copy unnecessarily */ if (old_tle->resno != next_junk_attrno) { old_tle = flatCopyTargetEntry(old_tle); old_tle->resno = next_junk_attrno; } junk_tlist = lappend(junk_tlist, old_tle); next_junk_attrno++; } } for (attrno = 1; attrno <= numattrs; attrno++) { TargetEntry *new_tle = new_tles[attrno - 1]; att_tup = target_relation->rd_att->attrs[attrno - 1]; /* We can (and must) ignore deleted attributes */ if (att_tup->attisdropped) continue; /* * Handle the two cases where we need to insert a default expression: * it's an INSERT and there's no tlist entry for the column, or the * tlist entry is a DEFAULT placeholder node. */ if ((new_tle == NULL && commandType == CMD_INSERT) || (new_tle && new_tle->expr && IsA(new_tle->expr, SetToDefault))) { Node *new_expr; new_expr = build_column_default(target_relation, attrno); /* * If there is no default (ie, default is effectively NULL), we * can omit the tlist entry in the INSERT case, since the planner * can insert a NULL for itself, and there's no point in spending * any more rewriter cycles on the entry. But in the UPDATE case * we've got to explicitly set the column to NULL. */ if (!new_expr) { if (commandType == CMD_INSERT) new_tle = NULL; else { new_expr = (Node *) makeConst(att_tup->atttypid, att_tup->attlen, (Datum) 0, true, /* isnull */ att_tup->attbyval); /* this is to catch a NOT NULL domain constraint */ new_expr = coerce_to_domain(new_expr, InvalidOid, att_tup->atttypid, COERCE_IMPLICIT_CAST, false, false); } } if (new_expr) new_tle = makeTargetEntry((Expr *) new_expr, attrno, pstrdup(NameStr(att_tup->attname)), false); } if (new_tle) new_tlist = lappend(new_tlist, new_tle); } pfree(new_tles); parsetree->targetList = list_concat(new_tlist, junk_tlist);}/* * Convert a matched TLE from the original tlist into a correct new TLE. * * This routine detects and handles multiple assignments to the same target * attribute. (The attribute name is needed only for error messages.) */static TargetEntry *process_matched_tle(TargetEntry *src_tle, TargetEntry *prior_tle, const char *attrName){ TargetEntry *result; Node *src_expr; Node *prior_expr; Node *src_input; Node *prior_input; Node *priorbottom; Node *newexpr; if (prior_tle == NULL) { /* * Normal case where this is the first assignment to the attribute. */ return src_tle; } /*---------- * Multiple assignments to same attribute. Allow only if all are * FieldStore or ArrayRef assignment operations. This is a bit * tricky because what we may actually be looking at is a nest of * such nodes; consider * UPDATE tab SET col.fld1.subfld1 = x, col.fld2.subfld2 = y * The two expressions produced by the parser will look like * FieldStore(col, fld1, FieldStore(placeholder, subfld1, x)) * FieldStore(col, fld2, FieldStore(placeholder, subfld2, x)) * However, we can ignore the substructure and just consider the top * FieldStore or ArrayRef from each assignment, because it works to * combine these as * FieldStore(FieldStore(col, fld1, * FieldStore(placeholder, subfld1, x)), * fld2, FieldStore(placeholder, subfld2, x)) * Note the leftmost expression goes on the inside so that the * assignments appear to occur left-to-right. * * For FieldStore, instead of nesting we can generate a single * FieldStore with multiple target fields. We must nest when * ArrayRefs are involved though. *---------- */ src_expr = (Node *) src_tle->expr; prior_expr = (Node *) prior_tle->expr; src_input = get_assignment_input(src_expr); prior_input = get_assignment_input(prior_expr); if (src_input == NULL || prior_input == NULL || exprType(src_expr) != exprType(prior_expr)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("multiple assignments to same column \"%s\"", attrName))); /* * Prior TLE could be a nest of assignments if we do this more than once. */ priorbottom = prior_input; for (;;) { Node *newbottom = get_assignment_input(priorbottom); if (newbottom == NULL) break; /* found the original Var reference */ priorbottom = newbottom; } if (!equal(priorbottom, src_input)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("multiple assignments to same column \"%s\"", attrName))); /* * Looks OK to nest 'em. */ if (IsA(src_expr, FieldStore)) { FieldStore *fstore = makeNode(FieldStore); if (IsA(prior_expr, FieldStore)) { /* combine the two */ memcpy(fstore, prior_expr, sizeof(FieldStore)); fstore->newvals = list_concat(list_copy(((FieldStore *) prior_expr)->newvals), list_copy(((FieldStore *) src_expr)->newvals)); fstore->fieldnums = list_concat(list_copy(((FieldStore *) prior_expr)->fieldnums), list_copy(((FieldStore *) src_expr)->fieldnums)); } else { /* general case, just nest 'em */ memcpy(fstore, src_expr, sizeof(FieldStore)); fstore->arg = (Expr *) prior_expr; } newexpr = (Node *) fstore; } else if (IsA(src_expr, ArrayRef)) { ArrayRef *aref = makeNode(ArrayRef); memcpy(aref, src_expr, sizeof(ArrayRef)); aref->refexpr = (Expr *) prior_expr; newexpr = (Node *) aref; } else { elog(ERROR, "can't happen"); newexpr = NULL; } result = flatCopyTargetEntry(src_tle); result->expr = (Expr *) newexpr; return result;}/* * If node is an assignment node, return its input; else return NULL */static Node *get_assignment_input(Node *node){ if (node == NULL) return NULL; if (IsA(node, FieldStore)) { FieldStore *fstore = (FieldStore *) node; return (Node *) fstore->arg; } else if (IsA(node, ArrayRef)) { ArrayRef *aref = (ArrayRef *) node; if (aref->refassgnexpr == NULL) return NULL; return (Node *) aref->refexpr; } return NULL;}/* * Make an expression tree for the default value for a column. * * If there is no default, return a NULL instead. */Node *build_column_default(Relation rel, int attrno){ TupleDesc rd_att = rel->rd_att; Form_pg_attribute att_tup = rd_att->attrs[attrno - 1]; Oid atttype = att_tup->atttypid; int32 atttypmod = att_tup->atttypmod; Node *expr = NULL; Oid exprtype; /* * Scan to see if relation has a default for this column. */ if (rd_att->constr && rd_att->constr->num_defval > 0) { AttrDefault *defval = rd_att->constr->defval; int ndef = rd_att->constr->num_defval; while (--ndef >= 0) { if (attrno == defval[ndef].adnum) { /* * Found it, convert string representation to node tree. */ expr = stringToNode(defval[ndef].adbin); break; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -