From 90739d4621c654719dd8d214f9a8c160d407ac4c Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 3 May 2002 20:15:02 +0000 Subject: [PATCH] Make ruleutils.c schema-aware. Displayed names are schema-qualified only if they would not be found without qualification given the current search path, as per idea from Peter Eisentraut. --- src/backend/parser/parse_func.c | 9 +- src/backend/utils/adt/ruleutils.c | 496 ++++++++++++++++++++---------- 2 files changed, 339 insertions(+), 166 deletions(-) diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 2ab9c9a689..f74f5be2f7 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.126 2002/04/11 20:00:01 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.127 2002/05/03 20:15:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -686,6 +686,11 @@ func_select_candidate(int nargs, * b) if the answer is one, we have our function * c) if the answer is more than one, attempt to resolve the conflict * d) if the answer is zero, try the next array from vector #1 + * + * Note: we rely primarily on nargs/argtypes as the argument description. + * The actual expression node list is passed in fargs so that we can check + * for type coercion of a constant. Some callers pass fargs == NIL + * indicating they don't want that check made. */ FuncDetailCode func_get_detail(List *funcname, @@ -740,7 +745,7 @@ func_get_detail(List *funcname, * that result for something coerce_type can't handle, we'll cause * infinite recursion between this module and coerce_type! */ - if (nargs == 1) + if (nargs == 1 && fargs != NIL) { Oid targetType; TypeName *tn = makeNode(TypeName); diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 1e4b571351..b1f012d4d9 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3,7 +3,7 @@ * back to source text * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.101 2002/05/02 18:44:11 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.102 2002/05/03 20:15:02 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -54,6 +54,8 @@ #include "optimizer/tlist.h" #include "parser/keywords.h" #include "parser/parse_expr.h" +#include "parser/parse_func.h" +#include "parser/parse_oper.h" #include "parser/parsetree.h" #include "rewrite/rewriteManip.h" #include "rewrite/rewriteSupport.h" @@ -124,12 +126,13 @@ static void get_utility_query_def(Query *query, deparse_context *context); static void get_basic_select_query(Query *query, deparse_context *context); static void get_setop_query(Node *setOp, Query *query, deparse_context *context); -static void get_rule_sortgroupclause(SortClause *srt, List *tlist, +static Node *get_rule_sortgroupclause(SortClause *srt, List *tlist, bool force_colno, deparse_context *context); static void get_names_for_var(Var *var, deparse_context *context, char **refname, char **attname); static void get_rule_expr(Node *node, deparse_context *context); +static void get_oper_expr(Expr *expr, deparse_context *context); static void get_func_expr(Expr *expr, deparse_context *context); static void get_agg_expr(Aggref *aggref, deparse_context *context); static Node *strip_type_coercion(Node *expr, Oid resultType); @@ -142,6 +145,9 @@ static void get_from_clause_item(Node *jtnode, Query *query, static void get_opclass_name(Oid opclass, Oid actual_datatype, StringInfo buf); static bool tleIsArrayAssign(TargetEntry *tle); +static char *generate_relation_name(Oid relid); +static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes); +static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2); static char *get_relid_attribute_name(Oid relid, AttrNumber attnum); #define only_marker(rte) ((rte)->inh ? "" : "ONLY ") @@ -281,7 +287,6 @@ pg_do_getviewdef(Oid viewoid) TupleDesc rulettc; StringInfoData buf; int len; - char *viewname; /* * Connect to SPI manager @@ -310,14 +315,13 @@ pg_do_getviewdef(Oid viewoid) /* * Get the pg_rewrite tuple for the view's SELECT rule */ - viewname = get_rel_name(viewoid); args[0] = ObjectIdGetDatum(viewoid); args[1] = PointerGetDatum(ViewSelectRuleName); nulls[0] = ' '; nulls[1] = ' '; spirc = SPI_execp(plan_getviewrule, args, nulls, 2); if (spirc != SPI_OK_SELECT) - elog(ERROR, "failed to get pg_rewrite tuple for view %s", viewname); + elog(ERROR, "failed to get pg_rewrite tuple for view %u", viewoid); initStringInfo(&buf); if (SPI_processed != 1) appendStringInfo(&buf, "Not a view"); @@ -357,14 +361,14 @@ pg_get_indexdef(PG_FUNCTION_ARGS) text *indexdef; HeapTuple ht_idx; HeapTuple ht_idxrel; - HeapTuple ht_indrel; HeapTuple ht_am; Form_pg_index idxrec; Form_pg_class idxrelrec; - Form_pg_class indrelrec; Form_pg_am amrec; + Oid indrelid; int len; int keyno; + Oid keycoltypes[INDEX_MAX_KEYS]; StringInfoData buf; StringInfoData keybuf; char *sep; @@ -379,26 +383,19 @@ pg_get_indexdef(PG_FUNCTION_ARGS) elog(ERROR, "syscache lookup for index %u failed", indexrelid); idxrec = (Form_pg_index) GETSTRUCT(ht_idx); + indrelid = idxrec->indrelid; + Assert(indexrelid == idxrec->indexrelid); + /* * Fetch the pg_class tuple of the index relation */ ht_idxrel = SearchSysCache(RELOID, - ObjectIdGetDatum(idxrec->indexrelid), + ObjectIdGetDatum(indexrelid), 0, 0, 0); if (!HeapTupleIsValid(ht_idxrel)) - elog(ERROR, "syscache lookup for relid %u failed", idxrec->indexrelid); + elog(ERROR, "syscache lookup for relid %u failed", indexrelid); idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel); - /* - * Fetch the pg_class tuple of the indexed relation - */ - ht_indrel = SearchSysCache(RELOID, - ObjectIdGetDatum(idxrec->indrelid), - 0, 0, 0); - if (!HeapTupleIsValid(ht_indrel)) - elog(ERROR, "syscache lookup for relid %u failed", idxrec->indrelid); - indrelrec = (Form_pg_class) GETSTRUCT(ht_indrel); - /* * Fetch the pg_am tuple of the index' access method */ @@ -410,13 +407,14 @@ pg_get_indexdef(PG_FUNCTION_ARGS) amrec = (Form_pg_am) GETSTRUCT(ht_am); /* - * Start the index definition + * Start the index definition. Note that the index's name should never + * be schema-qualified, but the indexed rel's name may be. */ initStringInfo(&buf); appendStringInfo(&buf, "CREATE %sINDEX %s ON %s USING %s (", idxrec->indisunique ? "UNIQUE " : "", quote_identifier(NameStr(idxrelrec->relname)), - quote_identifier(NameStr(indrelrec->relname)), + generate_relation_name(indrelid), quote_identifier(NameStr(amrec->amname))); /* @@ -427,26 +425,28 @@ pg_get_indexdef(PG_FUNCTION_ARGS) for (keyno = 0; keyno < INDEX_MAX_KEYS; keyno++) { AttrNumber attnum = idxrec->indkey[keyno]; + char *attname; if (attnum == InvalidAttrNumber) break; + attname = get_relid_attribute_name(indrelid, attnum); + keycoltypes[keyno] = get_atttype(indrelid, attnum); + appendStringInfo(&keybuf, sep); sep = ", "; /* * Add the indexed field name */ - appendStringInfo(&keybuf, "%s", - quote_identifier(get_relid_attribute_name(idxrec->indrelid, - attnum))); + appendStringInfo(&keybuf, "%s", quote_identifier(attname)); /* * If not a functional index, add the operator class name */ if (idxrec->indproc == InvalidOid) get_opclass_name(idxrec->indclass[keyno], - get_atttype(idxrec->indrelid, attnum), + keycoltypes[keyno], &keybuf); } @@ -455,22 +455,13 @@ pg_get_indexdef(PG_FUNCTION_ARGS) /* * For functional index say 'func (attrs) opclass' */ - HeapTuple proctup; - Form_pg_proc procStruct; - - proctup = SearchSysCache(PROCOID, - ObjectIdGetDatum(idxrec->indproc), - 0, 0, 0); - if (!HeapTupleIsValid(proctup)) - elog(ERROR, "cache lookup for proc %u failed", idxrec->indproc); - procStruct = (Form_pg_proc) GETSTRUCT(proctup); - appendStringInfo(&buf, "%s(%s)", - quote_identifier(NameStr(procStruct->proname)), + generate_function_name(idxrec->indproc, + keyno, keycoltypes), keybuf.data); - get_opclass_name(idxrec->indclass[0], procStruct->prorettype, &buf); - - ReleaseSysCache(proctup); + get_opclass_name(idxrec->indclass[0], + get_func_rettype(idxrec->indproc), + &buf); } else { @@ -480,7 +471,7 @@ pg_get_indexdef(PG_FUNCTION_ARGS) appendStringInfo(&buf, "%s", keybuf.data); } - appendStringInfo(&buf, ")"); + appendStringInfoChar(&buf, ')'); /* * If it's a partial index, decompile and append the predicate @@ -506,8 +497,7 @@ pg_get_indexdef(PG_FUNCTION_ARGS) if (node && IsA(node, List)) node = (Node *) make_ands_explicit((List *) node); /* Deparse */ - context = deparse_context_for(NameStr(indrelrec->relname), - idxrec->indrelid); + context = deparse_context_for(get_rel_name(indrelid), indrelid); str = deparse_expression(node, context, false); appendStringInfo(&buf, " WHERE %s", str); } @@ -525,7 +515,6 @@ pg_get_indexdef(PG_FUNCTION_ARGS) ReleaseSysCache(ht_idx); ReleaseSysCache(ht_idxrel); - ReleaseSysCache(ht_indrel); ReleaseSysCache(ht_am); PG_RETURN_TEXT_P(indexdef); @@ -856,7 +845,6 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc) if (ev_action != NULL) actions = (List *) stringToNode(ev_action); - /* * Build the rules definition text */ @@ -889,8 +877,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc) } /* The relation the rule is fired on */ - appendStringInfo(buf, " TO %s", - quote_identifier(get_rel_name(ev_class))); + appendStringInfo(buf, " TO %s", generate_relation_name(ev_class)); if (ev_attr > 0) appendStringInfo(buf, ".%s", quote_identifier(get_relid_attribute_name(ev_class, @@ -1126,12 +1113,16 @@ get_select_query_def(Query *query, deparse_context *context) foreach(l, query->sortClause) { SortClause *srt = (SortClause *) lfirst(l); + Node *sortexpr; + Oid sortcoltype; char *opname; appendStringInfo(buf, sep); - get_rule_sortgroupclause(srt, query->targetList, - force_colno, context); - opname = get_opname(srt->sortop); + sortexpr = get_rule_sortgroupclause(srt, query->targetList, + force_colno, context); + sortcoltype = exprType(sortexpr); + opname = generate_operator_name(srt->sortop, + sortcoltype, sortcoltype); if (strcmp(opname, "<") != 0) { if (strcmp(opname, ">") == 0) @@ -1315,8 +1306,10 @@ get_setop_query(Node *setOp, Query *query, deparse_context *context) /* * Display a sort/group clause. + * + * Also returns the expression tree, so caller need not find it again. */ -static void +static Node * get_rule_sortgroupclause(SortClause *srt, List *tlist, bool force_colno, deparse_context *context) { @@ -1339,6 +1332,8 @@ get_rule_sortgroupclause(SortClause *srt, List *tlist, bool force_colno, } else get_rule_expr(expr, context); + + return expr; } /* ---------- @@ -1361,7 +1356,7 @@ get_insert_query_def(Query *query, deparse_context *context) foreach(l, query->rtable) { rte = (RangeTblEntry *) lfirst(l); - if (rte->subquery == NULL) + if (rte->rtekind != RTE_SUBQUERY) continue; if (select_rte) elog(ERROR, "get_insert_query_def: too many RTEs in INSERT!"); @@ -1372,8 +1367,9 @@ get_insert_query_def(Query *query, deparse_context *context) * Start the query with INSERT INTO relname */ rte = rt_fetch(query->resultRelation, query->rtable); + Assert(rte->rtekind == RTE_RELATION); appendStringInfo(buf, "INSERT INTO %s", - quote_identifier(rte->eref->aliasname)); + generate_relation_name(rte->relid)); /* Add the insert-column-names list */ sep = " ("; @@ -1429,9 +1425,10 @@ get_update_query_def(Query *query, deparse_context *context) * Start the query with UPDATE relname SET */ rte = rt_fetch(query->resultRelation, query->rtable); + Assert(rte->rtekind == RTE_RELATION); appendStringInfo(buf, "UPDATE %s%s SET ", only_marker(rte), - quote_identifier(rte->eref->aliasname)); + generate_relation_name(rte->relid)); /* Add the comma separated list of 'attname = value' */ sep = ""; @@ -1482,9 +1479,10 @@ get_delete_query_def(Query *query, deparse_context *context) * Start the query with DELETE FROM relname */ rte = rt_fetch(query->resultRelation, query->rtable); + Assert(rte->rtekind == RTE_RELATION); appendStringInfo(buf, "DELETE FROM %s%s", only_marker(rte), - quote_identifier(rte->eref->aliasname)); + generate_relation_name(rte->relid)); /* Add a WHERE clause if given */ if (query->jointree->quals != NULL) @@ -1509,7 +1507,8 @@ get_utility_query_def(Query *query, deparse_context *context) NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt; appendStringInfo(buf, "NOTIFY %s", - quote_identifier(stmt->relation->relname)); + quote_qualified_identifier(stmt->relation->schemaname, + stmt->relation->relname)); } else elog(ERROR, "get_utility_query_def: unexpected statement type"); @@ -1628,52 +1627,11 @@ get_rule_expr(Node *node, deparse_context *context) switch (expr->opType) { case OP_EXPR: - appendStringInfoChar(buf, '('); - if (length(args) == 2) - { - /* binary operator */ - get_rule_expr((Node *) lfirst(args), context); - appendStringInfo(buf, " %s ", - get_opname(((Oper *) expr->oper)->opno)); - get_rule_expr((Node *) lsecond(args), context); - } - else - { - /* unary operator --- but which side? */ - Oid opno = ((Oper *) expr->oper)->opno; - HeapTuple tp; - Form_pg_operator optup; - - tp = SearchSysCache(OPEROID, - ObjectIdGetDatum(opno), - 0, 0, 0); - if (!HeapTupleIsValid(tp)) - elog(ERROR, "cache lookup for operator %u failed", opno); - optup = (Form_pg_operator) GETSTRUCT(tp); - switch (optup->oprkind) - { - case 'l': - appendStringInfo(buf, "%s ", - get_opname(opno)); - get_rule_expr((Node *) lfirst(args), - context); - break; - case 'r': - get_rule_expr((Node *) lfirst(args), - context); - appendStringInfo(buf, " %s", - get_opname(opno)); - break; - default: - elog(ERROR, "get_rule_expr: bogus oprkind"); - } - ReleaseSysCache(tp); - } - appendStringInfoChar(buf, ')'); + get_oper_expr(expr, context); break; case FUNC_EXPR: - get_func_expr((Expr *) node, context); + get_func_expr(expr, context); break; case OR_EXPR: @@ -1922,9 +1880,69 @@ get_rule_expr(Node *node, deparse_context *context) } -/* ---------- +/* + * get_oper_expr - Parse back an Oper node + */ +static void +get_oper_expr(Expr *expr, deparse_context *context) +{ + StringInfo buf = context->buf; + Oid opno = ((Oper *) expr->oper)->opno; + List *args = expr->args; + + appendStringInfoChar(buf, '('); + if (length(args) == 2) + { + /* binary operator */ + Node *arg1 = (Node *) lfirst(args); + Node *arg2 = (Node *) lsecond(args); + + get_rule_expr(arg1, context); + appendStringInfo(buf, " %s ", + generate_operator_name(opno, + exprType(arg1), + exprType(arg2))); + get_rule_expr(arg2, context); + } + else + { + /* unary operator --- but which side? */ + Node *arg = (Node *) lfirst(args); + HeapTuple tp; + Form_pg_operator optup; + + tp = SearchSysCache(OPEROID, + ObjectIdGetDatum(opno), + 0, 0, 0); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup for operator %u failed", opno); + optup = (Form_pg_operator) GETSTRUCT(tp); + switch (optup->oprkind) + { + case 'l': + appendStringInfo(buf, "%s ", + generate_operator_name(opno, + InvalidOid, + exprType(arg))); + get_rule_expr(arg, context); + break; + case 'r': + get_rule_expr(arg, context); + appendStringInfo(buf, " %s", + generate_operator_name(opno, + exprType(arg), + InvalidOid)); + break; + default: + elog(ERROR, "get_rule_expr: bogus oprkind"); + } + ReleaseSysCache(tp); + } + appendStringInfoChar(buf, ')'); +} + +/* * get_func_expr - Parse back a Func node - * ---------- */ static void get_func_expr(Expr *expr, deparse_context *context) @@ -1932,25 +1950,12 @@ get_func_expr(Expr *expr, deparse_context *context) StringInfo buf = context->buf; Func *func = (Func *) (expr->oper); Oid funcoid = func->funcid; - HeapTuple proctup; - Form_pg_proc procStruct; - char *proname; int32 coercedTypmod; + Oid argtypes[FUNC_MAX_ARGS]; + int nargs; List *l; char *sep; - /* - * Get the functions pg_proc tuple - */ - proctup = SearchSysCache(PROCOID, - ObjectIdGetDatum(funcoid), - 0, 0, 0); - if (!HeapTupleIsValid(proctup)) - elog(ERROR, "cache lookup for proc %u failed", funcoid); - - procStruct = (Form_pg_proc) GETSTRUCT(proctup); - proname = NameStr(procStruct->proname); - /* * Check to see if function is a length-coercion function for some * datatype. If so, display the operation as a type cast. @@ -1958,6 +1963,7 @@ get_func_expr(Expr *expr, deparse_context *context) if (exprIsLengthCoercion((Node *) expr, &coercedTypmod)) { Node *arg = lfirst(expr->args); + Oid rettype = get_func_rettype(funcoid); char *typdesc; /* @@ -1966,7 +1972,7 @@ get_func_expr(Expr *expr, deparse_context *context) * * XXX Are there any cases where this is a bad idea? */ - arg = strip_type_coercion(arg, procStruct->prorettype); + arg = strip_type_coercion(arg, rettype); appendStringInfoChar(buf, '('); get_rule_expr(arg, context); @@ -1978,19 +1984,28 @@ get_func_expr(Expr *expr, deparse_context *context) * to quote the result of format_type_with_typemod: it takes * care of double-quoting any identifier that needs it. */ - typdesc = format_type_with_typemod(procStruct->prorettype, - coercedTypmod); + typdesc = format_type_with_typemod(rettype, coercedTypmod); appendStringInfo(buf, ")::%s", typdesc); pfree(typdesc); - ReleaseSysCache(proctup); return; } /* - * Normal function: display as proname(args) + * Normal function: display as proname(args). First we need to extract + * the argument datatypes. */ - appendStringInfo(buf, "%s(", quote_identifier(proname)); + nargs = 0; + foreach(l, expr->args) + { + Assert(nargs < FUNC_MAX_ARGS); + argtypes[nargs] = exprType((Node *) lfirst(l)); + nargs++; + } + + appendStringInfo(buf, "%s(", + generate_function_name(funcoid, nargs, argtypes)); + sep = ""; foreach(l, expr->args) { @@ -1999,47 +2014,25 @@ get_func_expr(Expr *expr, deparse_context *context) get_rule_expr((Node *) lfirst(l), context); } appendStringInfoChar(buf, ')'); - - ReleaseSysCache(proctup); } -/* ---------- +/* * get_agg_expr - Parse back an Aggref node - * ---------- */ static void get_agg_expr(Aggref *aggref, deparse_context *context) { StringInfo buf = context->buf; - HeapTuple proctup; - Form_pg_proc procStruct; - char *proname; + Oid argtype = exprType(aggref->target); - /* - * Get the aggregate's pg_proc tuple - */ - proctup = SearchSysCache(PROCOID, - ObjectIdGetDatum(aggref->aggfnoid), - 0, 0, 0); - if (!HeapTupleIsValid(proctup)) - elog(ERROR, "cache lookup for proc %u failed", aggref->aggfnoid); - - procStruct = (Form_pg_proc) GETSTRUCT(proctup); - proname = NameStr(procStruct->proname); - - /* - * Display it - */ appendStringInfo(buf, "%s(%s", - quote_identifier(proname), + generate_function_name(aggref->aggfnoid, 1, &argtype), aggref->aggdistinct ? "DISTINCT " : ""); if (aggref->aggstar) appendStringInfo(buf, "*"); else get_rule_expr(aggref->target, context); appendStringInfoChar(buf, ')'); - - ReleaseSysCache(proctup); } @@ -2064,7 +2057,8 @@ strip_type_coercion(Node *expr, Oid resultType) if (IsA(expr, RelabelType)) return strip_type_coercion(((RelabelType *) expr)->arg, resultType); - if (IsA(expr, Expr) &&((Expr *) expr)->opType == FUNC_EXPR) + if (IsA(expr, Expr) && + ((Expr *) expr)->opType == FUNC_EXPR) { Func *func; HeapTuple procTuple; @@ -2173,9 +2167,8 @@ get_const_expr(Const *constval, deparse_context *context) if (constval->constisnull) { /* - * Always label the type of a NULL constant. This not only - * prevents misdecisions about the type, but it ensures that our - * output is a valid b_expr. + * Always label the type of a NULL constant to prevent misdecisions + * about type when reparsing. */ appendStringInfo(buf, "NULL::%s", format_type_with_typemod(constval->consttype, -1)); @@ -2201,7 +2194,7 @@ get_const_expr(Const *constval, deparse_context *context) case INT4OID: case OIDOID: /* int types */ case FLOAT4OID: - case FLOAT8OID: /* float types */ + case FLOAT8OID: /* float types */ /* These types are printed without quotes */ appendStringInfo(buf, extval); break; @@ -2289,6 +2282,12 @@ get_sublink_expr(Node *node, deparse_context *context) need_paren = true; + /* + * XXX we assume here that we can get away without qualifying the + * operator name. Since the name may imply multiple physical operators + * it's rather difficult to do otherwise --- in fact, if the operators + * are in different namespaces any attempt to qualify would surely fail. + */ switch (sublink->subLinkType) { case EXISTS_SUBLINK: @@ -2391,7 +2390,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) /* Normal relation RTE */ appendStringInfo(buf, "%s%s", only_marker(rte), - quote_identifier(get_rel_name(rte->relid))); + generate_relation_name(rte->relid)); break; case RTE_SUBQUERY: /* Subquery RTE */ @@ -2506,7 +2505,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) nodeTag(jtnode)); } -/* ---------- +/* * get_opclass_name - fetch name of an index operator class * * The opclass name is appended (after a space) to buf. @@ -2514,7 +2513,6 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) * Output is suppressed if the opclass is the default for the given * actual_datatype. (If you don't want this behavior, just pass * InvalidOid for actual_datatype.) - * ---------- */ static void get_opclass_name(Oid opclass, Oid actual_datatype, @@ -2522,6 +2520,8 @@ get_opclass_name(Oid opclass, Oid actual_datatype, { HeapTuple ht_opc; Form_pg_opclass opcrec; + char *opcname; + char *nspname; ht_opc = SearchSysCache(CLAOID, ObjectIdGetDatum(opclass), @@ -2530,14 +2530,24 @@ get_opclass_name(Oid opclass, Oid actual_datatype, elog(ERROR, "cache lookup failed for opclass %u", opclass); opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc); if (actual_datatype != opcrec->opcintype || !opcrec->opcdefault) - appendStringInfo(buf, " %s", - quote_identifier(NameStr(opcrec->opcname))); + { + /* Okay, we need the opclass name. Do we need to qualify it? */ + opcname = NameStr(opcrec->opcname); + if (OpclassIsVisible(opclass)) + appendStringInfo(buf, " %s", quote_identifier(opcname)); + else + { + nspname = get_namespace_name(opcrec->opcnamespace); + appendStringInfo(buf, " %s.%s", + quote_identifier(nspname), + quote_identifier(opcname)); + } + } ReleaseSysCache(ht_opc); } -/* ---------- +/* * tleIsArrayAssign - check for array assignment - * ---------- */ static bool tleIsArrayAssign(TargetEntry *tle) @@ -2561,12 +2571,11 @@ tleIsArrayAssign(TargetEntry *tle) return true; } -/* ---------- +/* * quote_identifier - Quote an identifier only if needed * * When quotes are needed, we palloc the required space; slightly * space-wasteful but well worth it for notational simplicity. - * ---------- */ const char * quote_identifier(const char *ident) @@ -2623,12 +2632,11 @@ quote_identifier(const char *ident) return result; } -/* ---------- +/* * quote_qualified_identifier - Quote a possibly-qualified identifier * * Return a name of the form namespace.ident, or just ident if namespace * is NULL, quoting each component if necessary. The result is palloc'd. - * ---------- */ char * quote_qualified_identifier(const char *namespace, @@ -2643,13 +2651,173 @@ quote_qualified_identifier(const char *namespace, return buf.data; } -/* ---------- +/* + * generate_relation_name + * Compute the name to display for a relation specified by OID + * + * The result includes all necessary quoting and schema-prefixing. + */ +static char * +generate_relation_name(Oid relid) +{ + HeapTuple tp; + Form_pg_class reltup; + char *nspname; + char *result; + + tp = SearchSysCache(RELOID, + ObjectIdGetDatum(relid), + 0, 0, 0); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup of relation %u failed", relid); + reltup = (Form_pg_class) GETSTRUCT(tp); + + /* Qualify the name if not visible in search path */ + if (RelationIsVisible(relid)) + nspname = NULL; + else + nspname = get_namespace_name(reltup->relnamespace); + + result = quote_qualified_identifier(nspname, NameStr(reltup->relname)); + + ReleaseSysCache(tp); + + return result; +} + +/* + * generate_function_name + * Compute the name to display for a function specified by OID, + * given that it is being called with the specified actual arg types. + * (Arg types matter because of ambiguous-function resolution rules.) + * + * The result includes all necessary quoting and schema-prefixing. + */ +static char * +generate_function_name(Oid funcid, int nargs, Oid *argtypes) +{ + HeapTuple proctup; + Form_pg_proc procform; + char *proname; + char *nspname; + char *result; + FuncDetailCode p_result; + Oid p_funcid; + Oid p_rettype; + bool p_retset; + Oid *p_true_typeids; + + proctup = SearchSysCache(PROCOID, + ObjectIdGetDatum(funcid), + 0, 0, 0); + if (!HeapTupleIsValid(proctup)) + elog(ERROR, "cache lookup of function %u failed", funcid); + procform = (Form_pg_proc) GETSTRUCT(proctup); + proname = NameStr(procform->proname); + Assert(nargs == procform->pronargs); + + /* + * The idea here is to schema-qualify only if the parser would fail to + * resolve the correct function given the unqualified func name + * with the specified argtypes. + */ + p_result = func_get_detail(makeList1(makeString(proname)), + NIL, nargs, argtypes, + &p_funcid, &p_rettype, + &p_retset, &p_true_typeids); + if (p_result != FUNCDETAIL_NOTFOUND && p_funcid == funcid) + nspname = NULL; + else + nspname = get_namespace_name(procform->pronamespace); + + result = quote_qualified_identifier(nspname, proname); + + ReleaseSysCache(proctup); + + return result; +} + +/* + * generate_operator_name + * Compute the name to display for an operator specified by OID, + * given that it is being called with the specified actual arg types. + * (Arg types matter because of ambiguous-operator resolution rules. + * Pass InvalidOid for unused arg of a unary operator.) + * + * The result includes all necessary quoting and schema-prefixing, + * plus the OPERATOR() decoration needed to use a qualified operator name + * in an expression. + */ +static char * +generate_operator_name(Oid operid, Oid arg1, Oid arg2) +{ + StringInfoData buf; + HeapTuple opertup; + Form_pg_operator operform; + char *oprname; + char *nspname; + Operator p_result; + + initStringInfo(&buf); + + opertup = SearchSysCache(OPEROID, + ObjectIdGetDatum(operid), + 0, 0, 0); + if (!HeapTupleIsValid(opertup)) + elog(ERROR, "cache lookup of operator %u failed", operid); + operform = (Form_pg_operator) GETSTRUCT(opertup); + oprname = NameStr(operform->oprname); + + /* + * The idea here is to schema-qualify only if the parser would fail to + * resolve the correct operator given the unqualified op name + * with the specified argtypes. + */ + switch (operform->oprkind) + { + case 'b': + p_result = oper(makeList1(makeString(oprname)), arg1, arg2, true); + break; + case 'l': + p_result = left_oper(makeList1(makeString(oprname)), arg2, true); + break; + case 'r': + p_result = right_oper(makeList1(makeString(oprname)), arg1, true); + break; + default: + elog(ERROR, "unexpected oprkind %c for operator %u", + operform->oprkind, operid); + p_result = NULL; /* keep compiler quiet */ + break; + } + + if (p_result != NULL && oprid(p_result) == operid) + nspname = NULL; + else + { + nspname = get_namespace_name(operform->oprnamespace); + appendStringInfo(&buf, "OPERATOR(%s.", quote_identifier(nspname)); + } + + appendStringInfo(&buf, "%s", oprname); + + if (nspname) + appendStringInfoChar(&buf, ')'); + + if (p_result != NULL) + ReleaseSysCache(p_result); + + ReleaseSysCache(opertup); + + return buf.data; +} + +/* * get_relid_attribute_name * Get an attribute name by its relations Oid and its attnum * * Same as underlying syscache routine get_attname(), except that error * is handled by elog() instead of returning NULL. - * ---------- */ static char * get_relid_attribute_name(Oid relid, AttrNumber attnum) -- 2.40.0