ruleutils.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 2,475 行 · 第 1/5 页
C
2,475 行
{ appendContextKeyword(context, " HAVING ", -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); get_rule_expr(query->havingQual, context, false); }}static voidget_setop_query(Node *setOp, Query *query, deparse_context *context, TupleDesc resultDesc){ StringInfo buf = context->buf; bool need_paren; if (IsA(setOp, RangeTblRef)) { RangeTblRef *rtr = (RangeTblRef *) setOp; RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable); Query *subquery = rte->subquery; Assert(subquery != NULL); Assert(subquery->setOperations == NULL); /* Need parens if ORDER BY, FOR UPDATE, or LIMIT; see gram.y */ need_paren = (subquery->sortClause || subquery->rowMarks || subquery->limitOffset || subquery->limitCount); if (need_paren) appendStringInfoChar(buf, '('); get_query_def(subquery, buf, context->namespaces, resultDesc, context->prettyFlags, context->indentLevel); if (need_paren) appendStringInfoChar(buf, ')'); } else if (IsA(setOp, SetOperationStmt)) { SetOperationStmt *op = (SetOperationStmt *) setOp; /* * We force parens whenever nesting two SetOperationStmts. * There are some cases in which parens are needed around a leaf * query too, but those are more easily handled at the next level * down (see code above). */ need_paren = !IsA(op->larg, RangeTblRef); if (need_paren) appendStringInfoChar(buf, '('); get_setop_query(op->larg, query, context, resultDesc); if (need_paren) appendStringInfoChar(buf, ')'); if (!PRETTY_INDENT(context)) appendStringInfoChar(buf, ' '); switch (op->op) { case SETOP_UNION: appendContextKeyword(context, "UNION ", -PRETTYINDENT_STD, 0, 0); break; case SETOP_INTERSECT: appendContextKeyword(context, "INTERSECT ", -PRETTYINDENT_STD, 0, 0); break; case SETOP_EXCEPT: appendContextKeyword(context, "EXCEPT ", -PRETTYINDENT_STD, 0, 0); break; default: elog(ERROR, "unrecognized set op: %d", (int) op->op); } if (op->all) appendStringInfo(buf, "ALL "); if (PRETTY_INDENT(context)) appendStringInfoChar(buf, '\n'); need_paren = !IsA(op->rarg, RangeTblRef); if (need_paren) appendStringInfoChar(buf, '('); get_setop_query(op->rarg, query, context, resultDesc); if (need_paren) appendStringInfoChar(buf, ')'); } else { elog(ERROR, "unrecognized node type: %d", (int) nodeTag(setOp)); }}/* * Display a sort/group clause. * * Also returns the expression tree, so caller need not find it again. */static Node *get_rule_sortgroupclause(SortClause *srt, List *tlist, bool force_colno, deparse_context *context){ StringInfo buf = context->buf; TargetEntry *tle; Node *expr; tle = get_sortgroupclause_tle(srt, tlist); expr = (Node *) tle->expr; /* * Use column-number form if requested by caller or if expression is a * constant --- a constant is ambiguous (and will be misinterpreted by * findTargetlistEntry()) if we dump it explicitly. */ if (force_colno || (expr && IsA(expr, Const))) { Assert(!tle->resdom->resjunk); appendStringInfo(buf, "%d", tle->resdom->resno); } else get_rule_expr(expr, context, true); return expr;}/* ---------- * get_insert_query_def - Parse back an INSERT parsetree * ---------- */static voidget_insert_query_def(Query *query, deparse_context *context){ StringInfo buf = context->buf; RangeTblEntry *select_rte = NULL; RangeTblEntry *rte; char *sep; List *l; /* * If it's an INSERT ... SELECT there will be a single subquery RTE * for the SELECT. */ foreach(l, query->rtable) { rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind != RTE_SUBQUERY) continue; if (select_rte) elog(ERROR, "too many RTEs in INSERT"); select_rte = rte; } /* * Start the query with INSERT INTO relname */ rte = rt_fetch(query->resultRelation, query->rtable); Assert(rte->rtekind == RTE_RELATION); if (PRETTY_INDENT(context)) { context->indentLevel += PRETTYINDENT_STD; appendStringInfoChar(buf, ' '); } appendStringInfo(buf, "INSERT INTO %s", generate_relation_name(rte->relid)); /* Add the insert-column-names list */ sep = " ("; foreach(l, query->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(l); if (tle->resdom->resjunk) continue; /* ignore junk entries */ appendStringInfo(buf, sep); sep = ", "; appendStringInfo(buf, "%s", quote_identifier(get_relid_attribute_name(rte->relid, tle->resdom->resno))); } appendStringInfo(buf, ") "); /* Add the VALUES or the SELECT */ if (select_rte == NULL) { appendContextKeyword(context, "VALUES (", -PRETTYINDENT_STD, PRETTYINDENT_STD, 2); sep = ""; foreach(l, query->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(l); if (tle->resdom->resjunk) continue; /* ignore junk entries */ appendStringInfo(buf, sep); sep = ", "; get_rule_expr((Node *) tle->expr, context, false); } appendStringInfoChar(buf, ')'); } else get_query_def(select_rte->subquery, buf, NIL, NULL, context->prettyFlags, context->indentLevel);}/* ---------- * get_update_query_def - Parse back an UPDATE parsetree * ---------- */static voidget_update_query_def(Query *query, deparse_context *context){ StringInfo buf = context->buf; char *sep; RangeTblEntry *rte; List *l; /* * Start the query with UPDATE relname SET */ rte = rt_fetch(query->resultRelation, query->rtable); Assert(rte->rtekind == RTE_RELATION); if (PRETTY_INDENT(context)) { appendStringInfoChar(buf, ' '); context->indentLevel += PRETTYINDENT_STD; } appendStringInfo(buf, "UPDATE %s%s SET ", only_marker(rte), generate_relation_name(rte->relid)); /* Add the comma separated list of 'attname = value' */ sep = ""; foreach(l, query->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(l); if (tle->resdom->resjunk) continue; /* ignore junk entries */ appendStringInfo(buf, sep); sep = ", "; /* * If the update expression is an array assignment, we mustn't put * out "attname =" here; it will come out of the display of the * ArrayRef node instead. */ if (!tleIsArrayAssign(tle)) appendStringInfo(buf, "%s = ", quote_identifier(get_relid_attribute_name(rte->relid, tle->resdom->resno))); get_rule_expr((Node *) tle->expr, context, false); } /* Add the FROM clause if needed */ get_from_clause(query, context); /* Finally add a WHERE clause if given */ if (query->jointree->quals != NULL) { appendContextKeyword(context, " WHERE ", -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); get_rule_expr(query->jointree->quals, context, false); }}/* ---------- * get_delete_query_def - Parse back a DELETE parsetree * ---------- */static voidget_delete_query_def(Query *query, deparse_context *context){ StringInfo buf = context->buf; RangeTblEntry *rte; /* * Start the query with DELETE FROM relname */ rte = rt_fetch(query->resultRelation, query->rtable); Assert(rte->rtekind == RTE_RELATION); if (PRETTY_INDENT(context)) { context->indentLevel += PRETTYINDENT_STD; appendStringInfoChar(buf, ' '); } appendStringInfo(buf, "DELETE FROM %s%s", only_marker(rte), generate_relation_name(rte->relid)); /* Add a WHERE clause if given */ if (query->jointree->quals != NULL) { appendContextKeyword(context, " WHERE ", -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); get_rule_expr(query->jointree->quals, context, false); }}/* ---------- * get_utility_query_def - Parse back a UTILITY parsetree * ---------- */static voidget_utility_query_def(Query *query, deparse_context *context){ StringInfo buf = context->buf; if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt)) { NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt; appendContextKeyword(context, "", 0, PRETTYINDENT_STD, 1); appendStringInfo(buf, "NOTIFY %s", quote_qualified_identifier(stmt->relation->schemaname, stmt->relation->relname)); } else { /* Currently only NOTIFY utility commands can appear in rules */ elog(ERROR, "unexpected utility statement type"); }}/* * Get the schemaname, refname and attname for a (possibly nonlocal) Var. * * schemaname is usually returned as NULL. It will be non-null only if * use of the unqualified refname would find the wrong RTE. * * refname will be returned as NULL if the Var references an unnamed join. * In this case the Var *must* be displayed without any qualification. * * attname will be returned as NULL if the Var represents a whole tuple * of the relation. (Typically we'd want to display the Var as "foo.*", * but it's convenient to return NULL to make it easier for callers to * distinguish this case.) */static voidget_names_for_var(Var *var, deparse_context *context, char **schemaname, char **refname, char **attname){ List *nslist = context->namespaces; int sup = var->varlevelsup; deparse_namespace *dpns; RangeTblEntry *rte; /* Find appropriate nesting depth */ while (sup-- > 0 && nslist != NIL) nslist = lnext(nslist); if (nslist == NIL) elog(ERROR, "bogus varlevelsup: %d", var->varlevelsup); dpns = (deparse_namespace *) lfirst(nslist); /* Find the relevant RTE */ if (var->varno >= 1 && var->varno <= length(dpns->rtable)) rte = rt_fetch(var->varno, dpns->rtable); else if (var->varno == dpns->outer_varno) rte = dpns->outer_rte; else if (var->varno == dpns->inner_varno) rte = dpns->inner_rte; else rte = NULL; if (rte == NULL) elog(ERROR, "bogus varno: %d", var->varno); /* Emit results */ *schemaname = NULL; /* default assumptions */ *refname = rte->eref->aliasname; /* Exceptions occur only if the RTE is alias-less */ if (rte->alias == NULL) { if (rte->rtekind == RTE_RELATION) { /* * It's possible that use of the bare refname would find * another more-closely-nested RTE, or be ambiguous, in which * case we need to specify the schemaname to avoid these * errors. */ if (find_rte_by_refname(rte->eref->aliasname, context) != rte) *schemaname = get_namespace_name(get_rel_namespace(rte->relid)); } else if (rte->rtekind == RTE_JOIN) { /* Unnamed join has neither schemaname nor refname */ *refname = NULL; } } if (var->varattno == InvalidAttrNumber) *attname = NULL; else *attname = get_rte_attribute_name(rte, var->varattno);}/* * find_rte_by_refname - look up an RTE by refname in a deparse context * * Returns NULL if there is no matching RTE or the refname is ambiguous. * * NOTE: this code is not really correct since it does not take account of * the fact that not all the RTEs in a rangetable may be visible from the * point where a Var reference appears. For the purposes we need, however, * the only consequence of a false match is that we might stick a schema * qualifier on a Var that doesn't really need it. So it seems close * enough. */static RangeTblEntry *find_rte_by_refname(const char *refname, deparse_context *context){ RangeTblEntry *result = NULL; List *nslist; foreach(nslist, context->namespaces) { deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist); List *rtlist; foreach(rtlist, dpns->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(rtlist); if (strcmp(rte->eref->aliasname, refname) == 0) { if (result) return NULL; /* it's ambiguous */ result = rte; } } if (dpns->outer_rte && strcmp(dpns->outer_rte->eref->aliasname, refname) == 0) { if (result) return NULL; /* it's ambiguous */ result = dpns->outer_rte; } if (dpns->inner_rte && strcmp(dpns->inner_rte->eref->aliasname, refname) == 0) { if (result) return NULL; /* it's ambiguous */ result = dpns->inner_rte; } if (result) break; } return result;}/* * get_simple_binary_op_name * * helper function for isSimpleNode * will return single char binary operator name, or NULL if it's not */static const char *get_simple_binary_op_name(OpExpr *expr){ List *args = expr->args; if (length(args) == 2) { /* binary operator */ Node *arg1 = (Node *) lfirst(args); Node *arg2 = (Node *) lsecond(args); const char *op; op = generate_operator_name(expr->opno, exprType(arg1), exprType(arg2)); if (strlen(op) == 1) return op; } return NULL;}/* * isSimpleNode - check if given node is simple (doesn't need parenthesizing) * * true : simple in the context of parent node's type * false : not simple */static boolisSimpleNode(Node *n
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?