ruleutils.c
来自「PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统」· C语言 代码 · 共 2,434 行 · 第 1/5 页
C
2,434 行
* * The returned node is actually the given RangeTblEntry, but we declare it * as just Node to discourage callers from assuming anything. */Node *deparse_context_for_rte(RangeTblEntry *rte){ return (Node *) rte;}/* * deparse_context_for_subplan - Build deparse context for a plan node * * Helper routine to build one of the inputs for deparse_context_for_plan. * Pass the tlist of the subplan node, plus the query rangetable. * * The returned node is actually a RangeTblEntry, but we declare it as just * Node to discourage callers from assuming anything. */Node *deparse_context_for_subplan(const char *name, List *tlist, List *rtable){ RangeTblEntry *rte = makeNode(RangeTblEntry); List *attrs = NIL; int nattrs = 0; int rtablelength = list_length(rtable); ListCell *tl; char buf[32]; foreach(tl, tlist) { TargetEntry *tle = lfirst(tl); nattrs++; Assert(tle->resno == nattrs); if (tle->resname) { attrs = lappend(attrs, makeString(tle->resname)); continue; } if (tle->expr && IsA(tle->expr, Var)) { Var *var = (Var *) tle->expr; /* varno/varattno won't be any good, but varnoold might be */ if (var->varnoold > 0 && var->varnoold <= rtablelength) { RangeTblEntry *varrte = rt_fetch(var->varnoold, rtable); AttrNumber varattnum = var->varoattno; /* need this test in case it's referencing a resjunk col */ if (varattnum <= list_length(varrte->eref->colnames)) { char *varname; varname = get_rte_attribute_name(varrte, varattnum); attrs = lappend(attrs, makeString(varname)); continue; } } } /* Fallback if can't get name */ snprintf(buf, sizeof(buf), "?column%d?", tle->resno); attrs = lappend(attrs, makeString(pstrdup(buf))); } /* * We create an RTE_SPECIAL RangeTblEntry, and store the given tlist * in its coldeflist field. This is a hack to make the tlist available * to get_name_for_var_field(). RTE_SPECIAL nodes shouldn't appear in * deparse contexts otherwise. */ rte->rtekind = RTE_SPECIAL; rte->relid = InvalidOid; rte->coldeflist = tlist; rte->eref = makeAlias(name, attrs); rte->inh = false; rte->inFromCl = true; return (Node *) rte;}/* ---------- * make_ruledef - reconstruct the CREATE RULE command * for a given pg_rewrite tuple * ---------- */static voidmake_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, int prettyFlags){ char *rulename; char ev_type; Oid ev_class; int2 ev_attr; bool is_instead; char *ev_qual; char *ev_action; List *actions = NIL; int fno; Datum dat; bool isnull; /* * Get the attribute values from the rules tuple */ fno = SPI_fnumber(rulettc, "rulename"); dat = SPI_getbinval(ruletup, rulettc, fno, &isnull); Assert(!isnull); rulename = NameStr(*(DatumGetName(dat))); fno = SPI_fnumber(rulettc, "ev_type"); dat = SPI_getbinval(ruletup, rulettc, fno, &isnull); Assert(!isnull); ev_type = DatumGetChar(dat); fno = SPI_fnumber(rulettc, "ev_class"); dat = SPI_getbinval(ruletup, rulettc, fno, &isnull); Assert(!isnull); ev_class = DatumGetObjectId(dat); fno = SPI_fnumber(rulettc, "ev_attr"); dat = SPI_getbinval(ruletup, rulettc, fno, &isnull); Assert(!isnull); ev_attr = DatumGetInt16(dat); fno = SPI_fnumber(rulettc, "is_instead"); dat = SPI_getbinval(ruletup, rulettc, fno, &isnull); Assert(!isnull); is_instead = DatumGetBool(dat); /* these could be nulls */ fno = SPI_fnumber(rulettc, "ev_qual"); ev_qual = SPI_getvalue(ruletup, rulettc, fno); fno = SPI_fnumber(rulettc, "ev_action"); ev_action = SPI_getvalue(ruletup, rulettc, fno); if (ev_action != NULL) actions = (List *) stringToNode(ev_action); /* * Build the rules definition text */ appendStringInfo(buf, "CREATE RULE %s AS", quote_identifier(rulename)); if (prettyFlags & PRETTYFLAG_INDENT) appendStringInfoString(buf, "\n ON "); else appendStringInfoString(buf, " ON "); /* The event the rule is fired for */ switch (ev_type) { case '1': appendStringInfo(buf, "SELECT"); break; case '2': appendStringInfo(buf, "UPDATE"); break; case '3': appendStringInfo(buf, "INSERT"); break; case '4': appendStringInfo(buf, "DELETE"); break; default: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("rule \"%s\" has unsupported event type %d", rulename, ev_type))); break; } /* The relation the rule is fired on */ appendStringInfo(buf, " TO %s", generate_relation_name(ev_class)); if (ev_attr > 0) appendStringInfo(buf, ".%s", quote_identifier(get_relid_attribute_name(ev_class, ev_attr))); /* If the rule has an event qualification, add it */ if (ev_qual == NULL) ev_qual = ""; if (strlen(ev_qual) > 0 && strcmp(ev_qual, "<>") != 0) { Node *qual; Query *query; deparse_context context; deparse_namespace dpns; if (prettyFlags & PRETTYFLAG_INDENT) appendStringInfoString(buf, "\n "); appendStringInfo(buf, " WHERE "); qual = stringToNode(ev_qual); /* * We need to make a context for recognizing any Vars in the qual * (which can only be references to OLD and NEW). Use the rtable of * the first query in the action list for this purpose. */ query = (Query *) linitial(actions); /* * If the action is INSERT...SELECT, OLD/NEW have been pushed down * into the SELECT, and that's what we need to look at. (Ugly kluge * ... try to fix this when we redesign querytrees.) */ query = getInsertSelectQuery(query, NULL); /* Must acquire locks right away; see notes in get_query_def() */ AcquireRewriteLocks(query); context.buf = buf; context.namespaces = list_make1(&dpns); context.varprefix = (list_length(query->rtable) != 1); context.prettyFlags = prettyFlags; context.indentLevel = PRETTYINDENT_STD; dpns.rtable = query->rtable; dpns.outer_varno = dpns.inner_varno = 0; dpns.outer_rte = dpns.inner_rte = NULL; get_rule_expr(qual, &context, false); } appendStringInfo(buf, " DO "); /* The INSTEAD keyword (if so) */ if (is_instead) appendStringInfo(buf, "INSTEAD "); /* Finally the rules actions */ if (list_length(actions) > 1) { ListCell *action; Query *query; appendStringInfo(buf, "("); foreach(action, actions) { query = (Query *) lfirst(action); get_query_def(query, buf, NIL, NULL, prettyFlags, 0); if (prettyFlags) appendStringInfo(buf, ";\n"); else appendStringInfo(buf, "; "); } appendStringInfo(buf, ");"); } else if (list_length(actions) == 0) { appendStringInfo(buf, "NOTHING;"); } else { Query *query; query = (Query *) linitial(actions); get_query_def(query, buf, NIL, NULL, prettyFlags, 0); appendStringInfo(buf, ";"); }}/* ---------- * make_viewdef - reconstruct the SELECT part of a * view rewrite rule * ---------- */static voidmake_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, int prettyFlags){ Query *query; char ev_type; Oid ev_class; int2 ev_attr; bool is_instead; char *ev_qual; char *ev_action; List *actions = NIL; Relation ev_relation; int fno; bool isnull; /* * Get the attribute values from the rules tuple */ fno = SPI_fnumber(rulettc, "ev_type"); ev_type = (char) SPI_getbinval(ruletup, rulettc, fno, &isnull); fno = SPI_fnumber(rulettc, "ev_class"); ev_class = (Oid) SPI_getbinval(ruletup, rulettc, fno, &isnull); fno = SPI_fnumber(rulettc, "ev_attr"); ev_attr = (int2) SPI_getbinval(ruletup, rulettc, fno, &isnull); fno = SPI_fnumber(rulettc, "is_instead"); is_instead = (bool) SPI_getbinval(ruletup, rulettc, fno, &isnull); fno = SPI_fnumber(rulettc, "ev_qual"); ev_qual = SPI_getvalue(ruletup, rulettc, fno); fno = SPI_fnumber(rulettc, "ev_action"); ev_action = SPI_getvalue(ruletup, rulettc, fno); if (ev_action != NULL) actions = (List *) stringToNode(ev_action); if (list_length(actions) != 1) { appendStringInfo(buf, "Not a view"); return; } query = (Query *) linitial(actions); if (ev_type != '1' || ev_attr >= 0 || !is_instead || strcmp(ev_qual, "<>") != 0 || query->commandType != CMD_SELECT) { appendStringInfo(buf, "Not a view"); return; } ev_relation = heap_open(ev_class, AccessShareLock); get_query_def(query, buf, NIL, RelationGetDescr(ev_relation), prettyFlags, 0); appendStringInfo(buf, ";"); heap_close(ev_relation, AccessShareLock);}/* ---------- * get_query_def - Parse back one query parsetree * * If resultDesc is not NULL, then it is the output tuple descriptor for * the view represented by a SELECT query. * ---------- */static voidget_query_def(Query *query, StringInfo buf, List *parentnamespace, TupleDesc resultDesc, int prettyFlags, int startIndent){ deparse_context context; deparse_namespace dpns; /* * Before we begin to examine the query, acquire locks on referenced * relations, and fix up deleted columns in JOIN RTEs. This ensures * consistent results. Note we assume it's OK to scribble on the passed * querytree! */ AcquireRewriteLocks(query); context.buf = buf; context.namespaces = lcons(&dpns, list_copy(parentnamespace)); context.varprefix = (parentnamespace != NIL || list_length(query->rtable) != 1); context.prettyFlags = prettyFlags; context.indentLevel = startIndent; dpns.rtable = query->rtable; dpns.outer_varno = dpns.inner_varno = 0; dpns.outer_rte = dpns.inner_rte = NULL; switch (query->commandType) { case CMD_SELECT: get_select_query_def(query, &context, resultDesc); break; case CMD_UPDATE: get_update_query_def(query, &context); break; case CMD_INSERT: get_insert_query_def(query, &context); break; case CMD_DELETE: get_delete_query_def(query, &context); break; case CMD_NOTHING: appendStringInfo(buf, "NOTHING"); break; case CMD_UTILITY: get_utility_query_def(query, &context); break; default: elog(ERROR, "unrecognized query command type: %d", query->commandType); break; }}/* ---------- * get_select_query_def - Parse back a SELECT parsetree * ---------- */static voidget_select_query_def(Query *query, deparse_context *context, TupleDesc resultDesc){ StringInfo buf = context->buf; bool force_colno; char *sep; ListCell *l; /* * If the Query node has a setOperations tree, then it's the top level of * a UNION/INTERSECT/EXCEPT query; only the ORDER BY and LIMIT fields are * interesting in the top query itself. */ if (query->setOperations) { get_setop_query(query->setOperations, query, context, resultDesc); /* ORDER BY clauses must be simple in this case */ force_colno = true; } else { get_basic_select_query(query, context, resultDesc); force_colno = false; } /* Add the ORDER BY clause if given */ if (query->sortClause != NIL) { appendContextKeyword(context, " ORDER BY ", -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); sep = ""; foreach(l, query->sortClause) { SortClause *srt = (SortClause *) lfirst(l); Node *sortexpr; Oid sortcoltype; TypeCacheEntry *typentry; appendStringInfoString(buf, sep); sortexpr = get_rule_sortgroupclause(srt, query->targetList, force_colno, context); sortcoltype = exprType(sortexpr); /* See whether operator is default < or > for datatype */ typentry = lookup_type_cache(sortcoltype, TYPECACHE_LT_OPR | TYPECACHE_GT_OPR); if (srt->sortop == typentry->lt_opr) /* ASC is default, so emit nothing */ ; else if (srt->sortop == typentry->gt_opr) appendStringInfo(buf, " DESC"); else appendStringInfo(buf, " USING %s", generate_operator_name(srt->sortop, sortcoltype, sortcoltype)); sep = ", "; } } /* Add the LIMIT clause if given */ if (query->limitOffset != NULL) { appendContextKeyword(context, " OFFSET ", -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); get_rule_expr(query->limitOffset, context, false); } if (query->limitCount != NULL) { appendContextKeyword(context, " LIMIT ", -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); if (IsA(query->limitCount, Const) && ((Const *) query->limitCount)->constisnull) appendStringInfo(buf, "ALL"); else get_rule_expr(query->limitCount, context, false); }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?