Commit 90739d46 authored by Tom Lane's avatar Tom Lane

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.
parent 1a69a37d
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -686,6 +686,11 @@ func_select_candidate(int nargs,
* b) if the answer is one, we have our function * b) if the answer is one, we have our function
* c) if the answer is more than one, attempt to resolve the conflict * 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 * 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 FuncDetailCode
func_get_detail(List *funcname, func_get_detail(List *funcname,
...@@ -740,7 +745,7 @@ 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 * that result for something coerce_type can't handle, we'll cause
* infinite recursion between this module and coerce_type! * infinite recursion between this module and coerce_type!
*/ */
if (nargs == 1) if (nargs == 1 && fargs != NIL)
{ {
Oid targetType; Oid targetType;
TypeName *tn = makeNode(TypeName); TypeName *tn = makeNode(TypeName);
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* back to source text * back to source text
* *
* IDENTIFICATION * 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. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -54,6 +54,8 @@ ...@@ -54,6 +54,8 @@
#include "optimizer/tlist.h" #include "optimizer/tlist.h"
#include "parser/keywords.h" #include "parser/keywords.h"
#include "parser/parse_expr.h" #include "parser/parse_expr.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
#include "parser/parsetree.h" #include "parser/parsetree.h"
#include "rewrite/rewriteManip.h" #include "rewrite/rewriteManip.h"
#include "rewrite/rewriteSupport.h" #include "rewrite/rewriteSupport.h"
...@@ -124,12 +126,13 @@ static void get_utility_query_def(Query *query, deparse_context *context); ...@@ -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_basic_select_query(Query *query, deparse_context *context);
static void get_setop_query(Node *setOp, Query *query, static void get_setop_query(Node *setOp, Query *query,
deparse_context *context); deparse_context *context);
static void get_rule_sortgroupclause(SortClause *srt, List *tlist, static Node *get_rule_sortgroupclause(SortClause *srt, List *tlist,
bool force_colno, bool force_colno,
deparse_context *context); deparse_context *context);
static void get_names_for_var(Var *var, deparse_context *context, static void get_names_for_var(Var *var, deparse_context *context,
char **refname, char **attname); char **refname, char **attname);
static void get_rule_expr(Node *node, deparse_context *context); 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_func_expr(Expr *expr, deparse_context *context);
static void get_agg_expr(Aggref *aggref, deparse_context *context); static void get_agg_expr(Aggref *aggref, deparse_context *context);
static Node *strip_type_coercion(Node *expr, Oid resultType); static Node *strip_type_coercion(Node *expr, Oid resultType);
...@@ -142,6 +145,9 @@ static void get_from_clause_item(Node *jtnode, Query *query, ...@@ -142,6 +145,9 @@ static void get_from_clause_item(Node *jtnode, Query *query,
static void get_opclass_name(Oid opclass, Oid actual_datatype, static void get_opclass_name(Oid opclass, Oid actual_datatype,
StringInfo buf); StringInfo buf);
static bool tleIsArrayAssign(TargetEntry *tle); 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); static char *get_relid_attribute_name(Oid relid, AttrNumber attnum);
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ") #define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
...@@ -281,7 +287,6 @@ pg_do_getviewdef(Oid viewoid) ...@@ -281,7 +287,6 @@ pg_do_getviewdef(Oid viewoid)
TupleDesc rulettc; TupleDesc rulettc;
StringInfoData buf; StringInfoData buf;
int len; int len;
char *viewname;
/* /*
* Connect to SPI manager * Connect to SPI manager
...@@ -310,14 +315,13 @@ pg_do_getviewdef(Oid viewoid) ...@@ -310,14 +315,13 @@ pg_do_getviewdef(Oid viewoid)
/* /*
* Get the pg_rewrite tuple for the view's SELECT rule * Get the pg_rewrite tuple for the view's SELECT rule
*/ */
viewname = get_rel_name(viewoid);
args[0] = ObjectIdGetDatum(viewoid); args[0] = ObjectIdGetDatum(viewoid);
args[1] = PointerGetDatum(ViewSelectRuleName); args[1] = PointerGetDatum(ViewSelectRuleName);
nulls[0] = ' '; nulls[0] = ' ';
nulls[1] = ' '; nulls[1] = ' ';
spirc = SPI_execp(plan_getviewrule, args, nulls, 2); spirc = SPI_execp(plan_getviewrule, args, nulls, 2);
if (spirc != SPI_OK_SELECT) 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); initStringInfo(&buf);
if (SPI_processed != 1) if (SPI_processed != 1)
appendStringInfo(&buf, "Not a view"); appendStringInfo(&buf, "Not a view");
...@@ -357,14 +361,14 @@ pg_get_indexdef(PG_FUNCTION_ARGS) ...@@ -357,14 +361,14 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
text *indexdef; text *indexdef;
HeapTuple ht_idx; HeapTuple ht_idx;
HeapTuple ht_idxrel; HeapTuple ht_idxrel;
HeapTuple ht_indrel;
HeapTuple ht_am; HeapTuple ht_am;
Form_pg_index idxrec; Form_pg_index idxrec;
Form_pg_class idxrelrec; Form_pg_class idxrelrec;
Form_pg_class indrelrec;
Form_pg_am amrec; Form_pg_am amrec;
Oid indrelid;
int len; int len;
int keyno; int keyno;
Oid keycoltypes[INDEX_MAX_KEYS];
StringInfoData buf; StringInfoData buf;
StringInfoData keybuf; StringInfoData keybuf;
char *sep; char *sep;
...@@ -379,26 +383,19 @@ pg_get_indexdef(PG_FUNCTION_ARGS) ...@@ -379,26 +383,19 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
elog(ERROR, "syscache lookup for index %u failed", indexrelid); elog(ERROR, "syscache lookup for index %u failed", indexrelid);
idxrec = (Form_pg_index) GETSTRUCT(ht_idx); idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
indrelid = idxrec->indrelid;
Assert(indexrelid == idxrec->indexrelid);
/* /*
* Fetch the pg_class tuple of the index relation * Fetch the pg_class tuple of the index relation
*/ */
ht_idxrel = SearchSysCache(RELOID, ht_idxrel = SearchSysCache(RELOID,
ObjectIdGetDatum(idxrec->indexrelid), ObjectIdGetDatum(indexrelid),
0, 0, 0); 0, 0, 0);
if (!HeapTupleIsValid(ht_idxrel)) 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); 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 * Fetch the pg_am tuple of the index' access method
*/ */
...@@ -410,13 +407,14 @@ pg_get_indexdef(PG_FUNCTION_ARGS) ...@@ -410,13 +407,14 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
amrec = (Form_pg_am) GETSTRUCT(ht_am); 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); initStringInfo(&buf);
appendStringInfo(&buf, "CREATE %sINDEX %s ON %s USING %s (", appendStringInfo(&buf, "CREATE %sINDEX %s ON %s USING %s (",
idxrec->indisunique ? "UNIQUE " : "", idxrec->indisunique ? "UNIQUE " : "",
quote_identifier(NameStr(idxrelrec->relname)), quote_identifier(NameStr(idxrelrec->relname)),
quote_identifier(NameStr(indrelrec->relname)), generate_relation_name(indrelid),
quote_identifier(NameStr(amrec->amname))); quote_identifier(NameStr(amrec->amname)));
/* /*
...@@ -427,26 +425,28 @@ pg_get_indexdef(PG_FUNCTION_ARGS) ...@@ -427,26 +425,28 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
for (keyno = 0; keyno < INDEX_MAX_KEYS; keyno++) for (keyno = 0; keyno < INDEX_MAX_KEYS; keyno++)
{ {
AttrNumber attnum = idxrec->indkey[keyno]; AttrNumber attnum = idxrec->indkey[keyno];
char *attname;
if (attnum == InvalidAttrNumber) if (attnum == InvalidAttrNumber)
break; break;
attname = get_relid_attribute_name(indrelid, attnum);
keycoltypes[keyno] = get_atttype(indrelid, attnum);
appendStringInfo(&keybuf, sep); appendStringInfo(&keybuf, sep);
sep = ", "; sep = ", ";
/* /*
* Add the indexed field name * Add the indexed field name
*/ */
appendStringInfo(&keybuf, "%s", appendStringInfo(&keybuf, "%s", quote_identifier(attname));
quote_identifier(get_relid_attribute_name(idxrec->indrelid,
attnum)));
/* /*
* If not a functional index, add the operator class name * If not a functional index, add the operator class name
*/ */
if (idxrec->indproc == InvalidOid) if (idxrec->indproc == InvalidOid)
get_opclass_name(idxrec->indclass[keyno], get_opclass_name(idxrec->indclass[keyno],
get_atttype(idxrec->indrelid, attnum), keycoltypes[keyno],
&keybuf); &keybuf);
} }
...@@ -455,22 +455,13 @@ pg_get_indexdef(PG_FUNCTION_ARGS) ...@@ -455,22 +455,13 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
/* /*
* For functional index say 'func (attrs) opclass' * 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)", appendStringInfo(&buf, "%s(%s)",
quote_identifier(NameStr(procStruct->proname)), generate_function_name(idxrec->indproc,
keyno, keycoltypes),
keybuf.data); keybuf.data);
get_opclass_name(idxrec->indclass[0], procStruct->prorettype, &buf); get_opclass_name(idxrec->indclass[0],
get_func_rettype(idxrec->indproc),
ReleaseSysCache(proctup); &buf);
} }
else else
{ {
...@@ -480,7 +471,7 @@ pg_get_indexdef(PG_FUNCTION_ARGS) ...@@ -480,7 +471,7 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
appendStringInfo(&buf, "%s", keybuf.data); appendStringInfo(&buf, "%s", keybuf.data);
} }
appendStringInfo(&buf, ")"); appendStringInfoChar(&buf, ')');
/* /*
* If it's a partial index, decompile and append the predicate * If it's a partial index, decompile and append the predicate
...@@ -506,8 +497,7 @@ pg_get_indexdef(PG_FUNCTION_ARGS) ...@@ -506,8 +497,7 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
if (node && IsA(node, List)) if (node && IsA(node, List))
node = (Node *) make_ands_explicit((List *) node); node = (Node *) make_ands_explicit((List *) node);
/* Deparse */ /* Deparse */
context = deparse_context_for(NameStr(indrelrec->relname), context = deparse_context_for(get_rel_name(indrelid), indrelid);
idxrec->indrelid);
str = deparse_expression(node, context, false); str = deparse_expression(node, context, false);
appendStringInfo(&buf, " WHERE %s", str); appendStringInfo(&buf, " WHERE %s", str);
} }
...@@ -525,7 +515,6 @@ pg_get_indexdef(PG_FUNCTION_ARGS) ...@@ -525,7 +515,6 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
ReleaseSysCache(ht_idx); ReleaseSysCache(ht_idx);
ReleaseSysCache(ht_idxrel); ReleaseSysCache(ht_idxrel);
ReleaseSysCache(ht_indrel);
ReleaseSysCache(ht_am); ReleaseSysCache(ht_am);
PG_RETURN_TEXT_P(indexdef); PG_RETURN_TEXT_P(indexdef);
...@@ -856,7 +845,6 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc) ...@@ -856,7 +845,6 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
if (ev_action != NULL) if (ev_action != NULL)
actions = (List *) stringToNode(ev_action); actions = (List *) stringToNode(ev_action);
/* /*
* Build the rules definition text * Build the rules definition text
*/ */
...@@ -889,8 +877,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc) ...@@ -889,8 +877,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
} }
/* The relation the rule is fired on */ /* The relation the rule is fired on */
appendStringInfo(buf, " TO %s", appendStringInfo(buf, " TO %s", generate_relation_name(ev_class));
quote_identifier(get_rel_name(ev_class)));
if (ev_attr > 0) if (ev_attr > 0)
appendStringInfo(buf, ".%s", appendStringInfo(buf, ".%s",
quote_identifier(get_relid_attribute_name(ev_class, quote_identifier(get_relid_attribute_name(ev_class,
...@@ -1126,12 +1113,16 @@ get_select_query_def(Query *query, deparse_context *context) ...@@ -1126,12 +1113,16 @@ get_select_query_def(Query *query, deparse_context *context)
foreach(l, query->sortClause) foreach(l, query->sortClause)
{ {
SortClause *srt = (SortClause *) lfirst(l); SortClause *srt = (SortClause *) lfirst(l);
Node *sortexpr;
Oid sortcoltype;
char *opname; char *opname;
appendStringInfo(buf, sep); appendStringInfo(buf, sep);
get_rule_sortgroupclause(srt, query->targetList, sortexpr = get_rule_sortgroupclause(srt, query->targetList,
force_colno, context); force_colno, context);
opname = get_opname(srt->sortop); sortcoltype = exprType(sortexpr);
opname = generate_operator_name(srt->sortop,
sortcoltype, sortcoltype);
if (strcmp(opname, "<") != 0) if (strcmp(opname, "<") != 0)
{ {
if (strcmp(opname, ">") == 0) if (strcmp(opname, ">") == 0)
...@@ -1315,8 +1306,10 @@ get_setop_query(Node *setOp, Query *query, deparse_context *context) ...@@ -1315,8 +1306,10 @@ get_setop_query(Node *setOp, Query *query, deparse_context *context)
/* /*
* Display a sort/group clause. * 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, get_rule_sortgroupclause(SortClause *srt, List *tlist, bool force_colno,
deparse_context *context) deparse_context *context)
{ {
...@@ -1339,6 +1332,8 @@ get_rule_sortgroupclause(SortClause *srt, List *tlist, bool force_colno, ...@@ -1339,6 +1332,8 @@ get_rule_sortgroupclause(SortClause *srt, List *tlist, bool force_colno,
} }
else else
get_rule_expr(expr, context); get_rule_expr(expr, context);
return expr;
} }
/* ---------- /* ----------
...@@ -1361,7 +1356,7 @@ get_insert_query_def(Query *query, deparse_context *context) ...@@ -1361,7 +1356,7 @@ get_insert_query_def(Query *query, deparse_context *context)
foreach(l, query->rtable) foreach(l, query->rtable)
{ {
rte = (RangeTblEntry *) lfirst(l); rte = (RangeTblEntry *) lfirst(l);
if (rte->subquery == NULL) if (rte->rtekind != RTE_SUBQUERY)
continue; continue;
if (select_rte) if (select_rte)
elog(ERROR, "get_insert_query_def: too many RTEs in INSERT!"); elog(ERROR, "get_insert_query_def: too many RTEs in INSERT!");
...@@ -1372,8 +1367,9 @@ get_insert_query_def(Query *query, deparse_context *context) ...@@ -1372,8 +1367,9 @@ get_insert_query_def(Query *query, deparse_context *context)
* Start the query with INSERT INTO relname * Start the query with INSERT INTO relname
*/ */
rte = rt_fetch(query->resultRelation, query->rtable); rte = rt_fetch(query->resultRelation, query->rtable);
Assert(rte->rtekind == RTE_RELATION);
appendStringInfo(buf, "INSERT INTO %s", appendStringInfo(buf, "INSERT INTO %s",
quote_identifier(rte->eref->aliasname)); generate_relation_name(rte->relid));
/* Add the insert-column-names list */ /* Add the insert-column-names list */
sep = " ("; sep = " (";
...@@ -1429,9 +1425,10 @@ get_update_query_def(Query *query, deparse_context *context) ...@@ -1429,9 +1425,10 @@ get_update_query_def(Query *query, deparse_context *context)
* Start the query with UPDATE relname SET * Start the query with UPDATE relname SET
*/ */
rte = rt_fetch(query->resultRelation, query->rtable); rte = rt_fetch(query->resultRelation, query->rtable);
Assert(rte->rtekind == RTE_RELATION);
appendStringInfo(buf, "UPDATE %s%s SET ", appendStringInfo(buf, "UPDATE %s%s SET ",
only_marker(rte), only_marker(rte),
quote_identifier(rte->eref->aliasname)); generate_relation_name(rte->relid));
/* Add the comma separated list of 'attname = value' */ /* Add the comma separated list of 'attname = value' */
sep = ""; sep = "";
...@@ -1482,9 +1479,10 @@ get_delete_query_def(Query *query, deparse_context *context) ...@@ -1482,9 +1479,10 @@ get_delete_query_def(Query *query, deparse_context *context)
* Start the query with DELETE FROM relname * Start the query with DELETE FROM relname
*/ */
rte = rt_fetch(query->resultRelation, query->rtable); rte = rt_fetch(query->resultRelation, query->rtable);
Assert(rte->rtekind == RTE_RELATION);
appendStringInfo(buf, "DELETE FROM %s%s", appendStringInfo(buf, "DELETE FROM %s%s",
only_marker(rte), only_marker(rte),
quote_identifier(rte->eref->aliasname)); generate_relation_name(rte->relid));
/* Add a WHERE clause if given */ /* Add a WHERE clause if given */
if (query->jointree->quals != NULL) if (query->jointree->quals != NULL)
...@@ -1509,7 +1507,8 @@ get_utility_query_def(Query *query, deparse_context *context) ...@@ -1509,7 +1507,8 @@ get_utility_query_def(Query *query, deparse_context *context)
NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt; NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt;
appendStringInfo(buf, "NOTIFY %s", appendStringInfo(buf, "NOTIFY %s",
quote_identifier(stmt->relation->relname)); quote_qualified_identifier(stmt->relation->schemaname,
stmt->relation->relname));
} }
else else
elog(ERROR, "get_utility_query_def: unexpected statement type"); elog(ERROR, "get_utility_query_def: unexpected statement type");
...@@ -1628,52 +1627,11 @@ get_rule_expr(Node *node, deparse_context *context) ...@@ -1628,52 +1627,11 @@ get_rule_expr(Node *node, deparse_context *context)
switch (expr->opType) switch (expr->opType)
{ {
case OP_EXPR: case OP_EXPR:
appendStringInfoChar(buf, '('); get_oper_expr(expr, context);
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, ')');
break; break;
case FUNC_EXPR: case FUNC_EXPR:
get_func_expr((Expr *) node, context); get_func_expr(expr, context);
break; break;
case OR_EXPR: case OR_EXPR:
...@@ -1922,9 +1880,69 @@ get_rule_expr(Node *node, deparse_context *context) ...@@ -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 * get_func_expr - Parse back a Func node
* ----------
*/ */
static void static void
get_func_expr(Expr *expr, deparse_context *context) get_func_expr(Expr *expr, deparse_context *context)
...@@ -1932,25 +1950,12 @@ get_func_expr(Expr *expr, deparse_context *context) ...@@ -1932,25 +1950,12 @@ get_func_expr(Expr *expr, deparse_context *context)
StringInfo buf = context->buf; StringInfo buf = context->buf;
Func *func = (Func *) (expr->oper); Func *func = (Func *) (expr->oper);
Oid funcoid = func->funcid; Oid funcoid = func->funcid;
HeapTuple proctup;
Form_pg_proc procStruct;
char *proname;
int32 coercedTypmod; int32 coercedTypmod;
Oid argtypes[FUNC_MAX_ARGS];
int nargs;
List *l; List *l;
char *sep; 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 * Check to see if function is a length-coercion function for some
* datatype. If so, display the operation as a type cast. * datatype. If so, display the operation as a type cast.
...@@ -1958,6 +1963,7 @@ get_func_expr(Expr *expr, deparse_context *context) ...@@ -1958,6 +1963,7 @@ get_func_expr(Expr *expr, deparse_context *context)
if (exprIsLengthCoercion((Node *) expr, &coercedTypmod)) if (exprIsLengthCoercion((Node *) expr, &coercedTypmod))
{ {
Node *arg = lfirst(expr->args); Node *arg = lfirst(expr->args);
Oid rettype = get_func_rettype(funcoid);
char *typdesc; char *typdesc;
/* /*
...@@ -1966,7 +1972,7 @@ get_func_expr(Expr *expr, deparse_context *context) ...@@ -1966,7 +1972,7 @@ get_func_expr(Expr *expr, deparse_context *context)
* *
* XXX Are there any cases where this is a bad idea? * 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, '('); appendStringInfoChar(buf, '(');
get_rule_expr(arg, context); get_rule_expr(arg, context);
...@@ -1978,19 +1984,28 @@ get_func_expr(Expr *expr, deparse_context *context) ...@@ -1978,19 +1984,28 @@ get_func_expr(Expr *expr, deparse_context *context)
* to quote the result of format_type_with_typemod: it takes * to quote the result of format_type_with_typemod: it takes
* care of double-quoting any identifier that needs it. * care of double-quoting any identifier that needs it.
*/ */
typdesc = format_type_with_typemod(procStruct->prorettype, typdesc = format_type_with_typemod(rettype, coercedTypmod);
coercedTypmod);
appendStringInfo(buf, ")::%s", typdesc); appendStringInfo(buf, ")::%s", typdesc);
pfree(typdesc); pfree(typdesc);
ReleaseSysCache(proctup);
return; 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 = ""; sep = "";
foreach(l, expr->args) foreach(l, expr->args)
{ {
...@@ -1999,47 +2014,25 @@ get_func_expr(Expr *expr, deparse_context *context) ...@@ -1999,47 +2014,25 @@ get_func_expr(Expr *expr, deparse_context *context)
get_rule_expr((Node *) lfirst(l), context); get_rule_expr((Node *) lfirst(l), context);
} }
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
ReleaseSysCache(proctup);
} }
/* ---------- /*
* get_agg_expr - Parse back an Aggref node * get_agg_expr - Parse back an Aggref node
* ----------
*/ */
static void static void
get_agg_expr(Aggref *aggref, deparse_context *context) get_agg_expr(Aggref *aggref, deparse_context *context)
{ {
StringInfo buf = context->buf; StringInfo buf = context->buf;
HeapTuple proctup; Oid argtype = exprType(aggref->target);
Form_pg_proc procStruct;
char *proname;
/*
* 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", appendStringInfo(buf, "%s(%s",
quote_identifier(proname), generate_function_name(aggref->aggfnoid, 1, &argtype),
aggref->aggdistinct ? "DISTINCT " : ""); aggref->aggdistinct ? "DISTINCT " : "");
if (aggref->aggstar) if (aggref->aggstar)
appendStringInfo(buf, "*"); appendStringInfo(buf, "*");
else else
get_rule_expr(aggref->target, context); get_rule_expr(aggref->target, context);
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
ReleaseSysCache(proctup);
} }
...@@ -2064,7 +2057,8 @@ strip_type_coercion(Node *expr, Oid resultType) ...@@ -2064,7 +2057,8 @@ strip_type_coercion(Node *expr, Oid resultType)
if (IsA(expr, RelabelType)) if (IsA(expr, RelabelType))
return strip_type_coercion(((RelabelType *) expr)->arg, resultType); 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; Func *func;
HeapTuple procTuple; HeapTuple procTuple;
...@@ -2173,9 +2167,8 @@ get_const_expr(Const *constval, deparse_context *context) ...@@ -2173,9 +2167,8 @@ get_const_expr(Const *constval, deparse_context *context)
if (constval->constisnull) if (constval->constisnull)
{ {
/* /*
* Always label the type of a NULL constant. This not only * Always label the type of a NULL constant to prevent misdecisions
* prevents misdecisions about the type, but it ensures that our * about type when reparsing.
* output is a valid b_expr.
*/ */
appendStringInfo(buf, "NULL::%s", appendStringInfo(buf, "NULL::%s",
format_type_with_typemod(constval->consttype, -1)); format_type_with_typemod(constval->consttype, -1));
...@@ -2289,6 +2282,12 @@ get_sublink_expr(Node *node, deparse_context *context) ...@@ -2289,6 +2282,12 @@ get_sublink_expr(Node *node, deparse_context *context)
need_paren = true; 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) switch (sublink->subLinkType)
{ {
case EXISTS_SUBLINK: case EXISTS_SUBLINK:
...@@ -2391,7 +2390,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) ...@@ -2391,7 +2390,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
/* Normal relation RTE */ /* Normal relation RTE */
appendStringInfo(buf, "%s%s", appendStringInfo(buf, "%s%s",
only_marker(rte), only_marker(rte),
quote_identifier(get_rel_name(rte->relid))); generate_relation_name(rte->relid));
break; break;
case RTE_SUBQUERY: case RTE_SUBQUERY:
/* Subquery RTE */ /* Subquery RTE */
...@@ -2506,7 +2505,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) ...@@ -2506,7 +2505,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
nodeTag(jtnode)); nodeTag(jtnode));
} }
/* ---------- /*
* get_opclass_name - fetch name of an index operator class * get_opclass_name - fetch name of an index operator class
* *
* The opclass name is appended (after a space) to buf. * 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) ...@@ -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 * Output is suppressed if the opclass is the default for the given
* actual_datatype. (If you don't want this behavior, just pass * actual_datatype. (If you don't want this behavior, just pass
* InvalidOid for actual_datatype.) * InvalidOid for actual_datatype.)
* ----------
*/ */
static void static void
get_opclass_name(Oid opclass, Oid actual_datatype, get_opclass_name(Oid opclass, Oid actual_datatype,
...@@ -2522,6 +2520,8 @@ get_opclass_name(Oid opclass, Oid actual_datatype, ...@@ -2522,6 +2520,8 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
{ {
HeapTuple ht_opc; HeapTuple ht_opc;
Form_pg_opclass opcrec; Form_pg_opclass opcrec;
char *opcname;
char *nspname;
ht_opc = SearchSysCache(CLAOID, ht_opc = SearchSysCache(CLAOID,
ObjectIdGetDatum(opclass), ObjectIdGetDatum(opclass),
...@@ -2530,14 +2530,24 @@ get_opclass_name(Oid opclass, Oid actual_datatype, ...@@ -2530,14 +2530,24 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
elog(ERROR, "cache lookup failed for opclass %u", opclass); elog(ERROR, "cache lookup failed for opclass %u", opclass);
opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc); opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);
if (actual_datatype != opcrec->opcintype || !opcrec->opcdefault) 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); ReleaseSysCache(ht_opc);
} }
/* ---------- /*
* tleIsArrayAssign - check for array assignment * tleIsArrayAssign - check for array assignment
* ----------
*/ */
static bool static bool
tleIsArrayAssign(TargetEntry *tle) tleIsArrayAssign(TargetEntry *tle)
...@@ -2561,12 +2571,11 @@ tleIsArrayAssign(TargetEntry *tle) ...@@ -2561,12 +2571,11 @@ tleIsArrayAssign(TargetEntry *tle)
return true; return true;
} }
/* ---------- /*
* quote_identifier - Quote an identifier only if needed * quote_identifier - Quote an identifier only if needed
* *
* When quotes are needed, we palloc the required space; slightly * When quotes are needed, we palloc the required space; slightly
* space-wasteful but well worth it for notational simplicity. * space-wasteful but well worth it for notational simplicity.
* ----------
*/ */
const char * const char *
quote_identifier(const char *ident) quote_identifier(const char *ident)
...@@ -2623,12 +2632,11 @@ quote_identifier(const char *ident) ...@@ -2623,12 +2632,11 @@ quote_identifier(const char *ident)
return result; return result;
} }
/* ---------- /*
* quote_qualified_identifier - Quote a possibly-qualified identifier * quote_qualified_identifier - Quote a possibly-qualified identifier
* *
* Return a name of the form namespace.ident, or just ident if namespace * 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. * is NULL, quoting each component if necessary. The result is palloc'd.
* ----------
*/ */
char * char *
quote_qualified_identifier(const char *namespace, quote_qualified_identifier(const char *namespace,
...@@ -2643,13 +2651,173 @@ quote_qualified_identifier(const char *namespace, ...@@ -2643,13 +2651,173 @@ quote_qualified_identifier(const char *namespace,
return buf.data; 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_relid_attribute_name
* Get an attribute name by its relations Oid and its attnum * Get an attribute name by its relations Oid and its attnum
* *
* Same as underlying syscache routine get_attname(), except that error * Same as underlying syscache routine get_attname(), except that error
* is handled by elog() instead of returning NULL. * is handled by elog() instead of returning NULL.
* ----------
*/ */
static char * static char *
get_relid_attribute_name(Oid relid, AttrNumber attnum) get_relid_attribute_name(Oid relid, AttrNumber attnum)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment