Commit 0041a3d7 authored by Tom Lane's avatar Tom Lane

Add routines in namespace.c to determine whether objects are visible

in the search path.  (We might want to make these available as SQL
functions too, but I haven't done that yet.)  Fix format_type to be
schema-aware.
parent a829cbb8
......@@ -13,7 +13,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.16 2002/04/30 01:26:25 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.17 2002/05/01 23:06:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -31,6 +31,7 @@
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_shadow.h"
#include "catalog/pg_type.h"
#include "lib/stringinfo.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
......@@ -296,6 +297,56 @@ RelnameGetRelid(const char *relname)
return InvalidOid;
}
/*
* RelationIsVisible
* Determine whether a relation (identified by OID) is visible in the
* current search path. Visible means "would be found by searching
* for the unqualified relation name".
*/
bool
RelationIsVisible(Oid relid)
{
HeapTuple reltup;
Form_pg_class relform;
Oid relnamespace;
bool visible;
recomputeNamespacePath();
reltup = SearchSysCache(RELOID,
ObjectIdGetDatum(relid),
0, 0, 0);
if (!HeapTupleIsValid(reltup))
elog(ERROR, "Cache lookup failed for relation %u", relid);
relform = (Form_pg_class) GETSTRUCT(reltup);
/*
* Quick check: if it ain't in the path at all, it ain't visible.
*/
relnamespace = relform->relnamespace;
if (relnamespace != myTempNamespace &&
relnamespace != PG_CATALOG_NAMESPACE &&
!intMember(relnamespace, namespaceSearchPath))
visible = false;
else
{
/*
* If it is in the path, it might still not be visible; it could be
* hidden by another relation of the same name earlier in the path.
* So we must do a slow check to see if this rel would be found by
* RelnameGetRelid.
*/
char *relname = NameStr(relform->relname);
visible = (RelnameGetRelid(relname) == relid);
}
ReleaseSysCache(reltup);
return visible;
}
/*
* TypenameGetTypid
* Try to resolve an unqualified datatype name.
......@@ -346,55 +397,54 @@ TypenameGetTypid(const char *typname)
}
/*
* OpclassnameGetOpcid
* Try to resolve an unqualified index opclass name.
* Returns OID if opclass found in search path, else InvalidOid.
*
* This is essentially the same as TypenameGetTypid, but we have to have
* an extra argument for the index AM OID.
* TypeIsVisible
* Determine whether a type (identified by OID) is visible in the
* current search path. Visible means "would be found by searching
* for the unqualified type name".
*/
Oid
OpclassnameGetOpcid(Oid amid, const char *opcname)
bool
TypeIsVisible(Oid typid)
{
Oid opcid;
List *lptr;
HeapTuple typtup;
Form_pg_type typform;
Oid typnamespace;
bool visible;
recomputeNamespacePath();
/*
* If system namespace is not in path, implicitly search it before path
*/
if (!pathContainsSystemNamespace)
{
opcid = GetSysCacheOid(CLAAMNAMENSP,
ObjectIdGetDatum(amid),
PointerGetDatum(opcname),
ObjectIdGetDatum(PG_CATALOG_NAMESPACE),
0);
if (OidIsValid(opcid))
return opcid;
}
typtup = SearchSysCache(TYPEOID,
ObjectIdGetDatum(typid),
0, 0, 0);
if (!HeapTupleIsValid(typtup))
elog(ERROR, "Cache lookup failed for type %u", typid);
typform = (Form_pg_type) GETSTRUCT(typtup);
/*
* Else search the path
* Quick check: if it ain't in the path at all, it ain't visible.
*/
foreach(lptr, namespaceSearchPath)
typnamespace = typform->typnamespace;
if (typnamespace != PG_CATALOG_NAMESPACE &&
!intMember(typnamespace, namespaceSearchPath))
visible = false;
else
{
Oid namespaceId = (Oid) lfirsti(lptr);
/*
* If it is in the path, it might still not be visible; it could be
* hidden by another type of the same name earlier in the path.
* So we must do a slow check to see if this type would be found by
* TypenameGetTypid.
*/
char *typname = NameStr(typform->typname);
opcid = GetSysCacheOid(CLAAMNAMENSP,
ObjectIdGetDatum(amid),
PointerGetDatum(opcname),
ObjectIdGetDatum(namespaceId),
0);
if (OidIsValid(opcid))
return opcid;
visible = (TypenameGetTypid(typname) == typid);
}
/* Not found in path */
return InvalidOid;
ReleaseSysCache(typtup);
return visible;
}
/*
* FuncnameGetCandidates
* Given a possibly-qualified function name and argument count,
......@@ -582,6 +632,70 @@ FuncnameGetCandidates(List *names, int nargs)
return resultList;
}
/*
* FunctionIsVisible
* Determine whether a function (identified by OID) is visible in the
* current search path. Visible means "would be found by searching
* for the unqualified function name with exact argument matches".
*/
bool
FunctionIsVisible(Oid funcid)
{
HeapTuple proctup;
Form_pg_proc procform;
Oid pronamespace;
bool visible;
recomputeNamespacePath();
proctup = SearchSysCache(PROCOID,
ObjectIdGetDatum(funcid),
0, 0, 0);
if (!HeapTupleIsValid(proctup))
elog(ERROR, "Cache lookup failed for procedure %u", funcid);
procform = (Form_pg_proc) GETSTRUCT(proctup);
/*
* Quick check: if it ain't in the path at all, it ain't visible.
*/
pronamespace = procform->pronamespace;
if (pronamespace != PG_CATALOG_NAMESPACE &&
!intMember(pronamespace, namespaceSearchPath))
visible = false;
else
{
/*
* If it is in the path, it might still not be visible; it could be
* hidden by another proc of the same name and arguments earlier
* in the path. So we must do a slow check to see if this is the
* same proc that would be found by FuncnameGetCandidates.
*/
char *proname = NameStr(procform->proname);
int nargs = procform->pronargs;
FuncCandidateList clist;
visible = false;
clist = FuncnameGetCandidates(makeList1(makeString(proname)), nargs);
for (; clist; clist = clist->next)
{
if (memcmp(clist->args, procform->proargtypes,
nargs * sizeof(Oid)) == 0)
{
/* Found the expected entry; is it the right proc? */
visible = (clist->oid == funcid);
break;
}
}
}
ReleaseSysCache(proctup);
return visible;
}
/*
* OpernameGetCandidates
* Given a possibly-qualified operator name and operator kind,
......@@ -765,6 +879,70 @@ OpernameGetCandidates(List *names, char oprkind)
return resultList;
}
/*
* OperatorIsVisible
* Determine whether an operator (identified by OID) is visible in the
* current search path. Visible means "would be found by searching
* for the unqualified operator name with exact argument matches".
*/
bool
OperatorIsVisible(Oid oprid)
{
HeapTuple oprtup;
Form_pg_operator oprform;
Oid oprnamespace;
bool visible;
recomputeNamespacePath();
oprtup = SearchSysCache(OPEROID,
ObjectIdGetDatum(oprid),
0, 0, 0);
if (!HeapTupleIsValid(oprtup))
elog(ERROR, "Cache lookup failed for operator %u", oprid);
oprform = (Form_pg_operator) GETSTRUCT(oprtup);
/*
* Quick check: if it ain't in the path at all, it ain't visible.
*/
oprnamespace = oprform->oprnamespace;
if (oprnamespace != PG_CATALOG_NAMESPACE &&
!intMember(oprnamespace, namespaceSearchPath))
visible = false;
else
{
/*
* If it is in the path, it might still not be visible; it could be
* hidden by another operator of the same name and arguments earlier
* in the path. So we must do a slow check to see if this is the
* same operator that would be found by OpernameGetCandidates.
*/
char *oprname = NameStr(oprform->oprname);
FuncCandidateList clist;
visible = false;
clist = OpernameGetCandidates(makeList1(makeString(oprname)),
oprform->oprkind);
for (; clist; clist = clist->next)
{
if (clist->args[0] == oprform->oprleft &&
clist->args[1] == oprform->oprright)
{
/* Found the expected entry; is it the right op? */
visible = (clist->oid == oprid);
break;
}
}
}
ReleaseSysCache(oprtup);
return visible;
}
/*
* OpclassGetCandidates
* Given an index access method OID, retrieve a list of all the
......@@ -883,6 +1061,104 @@ OpclassGetCandidates(Oid amid)
return resultList;
}
/*
* OpclassnameGetOpcid
* Try to resolve an unqualified index opclass name.
* Returns OID if opclass found in search path, else InvalidOid.
*
* This is essentially the same as TypenameGetTypid, but we have to have
* an extra argument for the index AM OID.
*/
Oid
OpclassnameGetOpcid(Oid amid, const char *opcname)
{
Oid opcid;
List *lptr;
recomputeNamespacePath();
/*
* If system namespace is not in path, implicitly search it before path
*/
if (!pathContainsSystemNamespace)
{
opcid = GetSysCacheOid(CLAAMNAMENSP,
ObjectIdGetDatum(amid),
PointerGetDatum(opcname),
ObjectIdGetDatum(PG_CATALOG_NAMESPACE),
0);
if (OidIsValid(opcid))
return opcid;
}
/*
* Else search the path
*/
foreach(lptr, namespaceSearchPath)
{
Oid namespaceId = (Oid) lfirsti(lptr);
opcid = GetSysCacheOid(CLAAMNAMENSP,
ObjectIdGetDatum(amid),
PointerGetDatum(opcname),
ObjectIdGetDatum(namespaceId),
0);
if (OidIsValid(opcid))
return opcid;
}
/* Not found in path */
return InvalidOid;
}
/*
* OpclassIsVisible
* Determine whether an opclass (identified by OID) is visible in the
* current search path. Visible means "would be found by searching
* for the unqualified opclass name".
*/
bool
OpclassIsVisible(Oid opcid)
{
HeapTuple opctup;
Form_pg_opclass opcform;
Oid opcnamespace;
bool visible;
recomputeNamespacePath();
opctup = SearchSysCache(CLAOID,
ObjectIdGetDatum(opcid),
0, 0, 0);
if (!HeapTupleIsValid(opctup))
elog(ERROR, "Cache lookup failed for opclass %u", opcid);
opcform = (Form_pg_opclass) GETSTRUCT(opctup);
/*
* Quick check: if it ain't in the path at all, it ain't visible.
*/
opcnamespace = opcform->opcnamespace;
if (opcnamespace != PG_CATALOG_NAMESPACE &&
!intMember(opcnamespace, namespaceSearchPath))
visible = false;
else
{
/*
* If it is in the path, it might still not be visible; it could be
* hidden by another opclass of the same name earlier in the path.
* So we must do a slow check to see if this opclass would be found by
* OpclassnameGetOpcid.
*/
char *opcname = NameStr(opcform->opcname);
visible = (OpclassnameGetOpcid(opcform->opcamid, opcname) == opcid);
}
ReleaseSysCache(opctup);
return visible;
}
/*
* QualifiedNameGetCreationNamespace
......@@ -1016,6 +1292,7 @@ isTempNamespace(Oid namespaceId)
return false;
}
/*
* recomputeNamespacePath - recompute path derived variables if needed.
*/
......@@ -1453,6 +1730,7 @@ RemoveTempRelationsCallback(void)
}
}
/*
* Routines for handling the GUC variable 'search_path'.
*/
......
......@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.28 2002/03/20 19:44:40 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.29 2002/05/01 23:06:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -17,11 +17,13 @@
#include <ctype.h>
#include "fmgr.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "fmgr.h"
#include "utils/builtins.h"
#include "utils/datetime.h"
#include "utils/numeric.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#ifdef MULTIBYTE
#include "mb/pg_wchar.h"
......@@ -47,7 +49,7 @@ __attribute__((format(printf, 2, 3)));
* pg_attribute.atttypmod. This function will get the type name and
* format it and the modifier to canonical SQL format, if the type is
* a standard type. Otherwise you just get pg_type.typname back,
* double quoted if it contains funny characters.
* double quoted if it contains funny characters or matches a keyword.
*
* If typemod is NULL then we are formatting a type name in a context where
* no typemod is available, eg a function argument or result type. This
......@@ -121,11 +123,9 @@ format_type_internal(Oid type_oid, int32 typemod,
{
bool with_typemod = typemod_given && (typemod >= 0);
HeapTuple tuple;
Form_pg_type typeform;
Oid array_base_type;
int16 typlen;
char typtype;
bool is_array;
char *name;
char *buf;
if (type_oid == InvalidOid && allow_invalid)
......@@ -142,17 +142,18 @@ format_type_internal(Oid type_oid, int32 typemod,
elog(ERROR, "could not locate data type with oid %u in catalog",
type_oid);
}
typeform = (Form_pg_type) GETSTRUCT(tuple);
/*
* Check if it's an array (and not a domain --- we don't want to show
* the substructure of a domain type). Fixed-length array types such
* as "name" shouldn't get deconstructed either.
*/
array_base_type = ((Form_pg_type) GETSTRUCT(tuple))->typelem;
typlen = ((Form_pg_type) GETSTRUCT(tuple))->typlen;
typtype = ((Form_pg_type) GETSTRUCT(tuple))->typtype;
array_base_type = typeform->typelem;
if (array_base_type != InvalidOid && typlen < 0 && typtype != 'd')
if (array_base_type != InvalidOid &&
typeform->typlen < 0 &&
typeform->typtype != 'd')
{
/* Switch our attention to the array element type */
ReleaseSysCache(tuple);
......@@ -167,12 +168,26 @@ format_type_internal(Oid type_oid, int32 typemod,
elog(ERROR, "could not locate data type with oid %u in catalog",
type_oid);
}
is_array = true;
typeform = (Form_pg_type) GETSTRUCT(tuple);
type_oid = array_base_type;
is_array = true;
}
else
is_array = false;
/*
* See if we want to special-case the output for certain built-in types.
* Note that these special cases should all correspond to special
* productions in gram.y, to ensure that the type name will be taken as
* a system type, not a user type of the same name.
*
* If we do not provide a special-case output here, the type name will
* be handled the same way as a user type name --- in particular, it
* will be double-quoted if it matches any lexer keyword. This behavior
* is essential for some cases, such as types "bit" and "char".
*/
buf = NULL; /* flag for no special case */
switch (type_oid)
{
case BITOID:
......@@ -186,7 +201,6 @@ format_type_internal(Oid type_oid, int32 typemod,
* BIT(1) per SQL spec. Report it as the quoted typename
* so that parser will not assign a bogus typmod.
*/
buf = pstrdup("\"bit\"");
}
else
buf = pstrdup("bit");
......@@ -207,20 +221,11 @@ format_type_internal(Oid type_oid, int32 typemod,
* which means CHARACTER(1) per SQL spec. Report it as
* bpchar so that parser will not assign a bogus typmod.
*/
buf = pstrdup("bpchar");
}
else
buf = pstrdup("character");
break;
case CHAROID:
/*
* This char type is the single-byte version. You have to
* double-quote it to get at it in the parser.
*/
buf = pstrdup("\"char\"");
break;
case FLOAT4OID:
buf = pstrdup("real");
break;
......@@ -365,19 +370,27 @@ format_type_internal(Oid type_oid, int32 typemod,
else
buf = pstrdup("character varying");
break;
}
default:
name = NameStr(((Form_pg_type) GETSTRUCT(tuple))->typname);
/*
* Double-quote the name if it's not a standard identifier.
* Note this is *necessary* for ruleutils.c's use.
*/
if (strspn(name, "abcdefghijklmnopqrstuvwxyz0123456789_") != strlen(name)
|| isdigit((unsigned char) name[0]))
buf = psnprintf(strlen(name) + 3, "\"%s\"", name);
else
buf = pstrdup(name);
break;
if (buf == NULL)
{
/*
* Default handling: report the name as it appears in the catalog.
* Here, we must qualify the name if it is not visible in the search
* path, and we must double-quote it if it's not a standard identifier
* or if it matches any keyword.
*/
char *nspname;
char *typname;
if (TypeIsVisible(type_oid))
nspname = NULL;
else
nspname = get_namespace_name(typeform->typnamespace);
typname = NameStr(typeform->typname);
buf = quote_qualified_identifier(nspname, typname);
}
if (is_array)
......
......@@ -13,7 +13,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/regproc.c,v 1.66 2002/04/25 02:56:55 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/regproc.c,v 1.67 2002/05/01 23:06:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -307,31 +307,21 @@ regprocedureout(PG_FUNCTION_ARGS)
int nargs = procform->pronargs;
int i;
char *nspname;
FuncCandidateList clist;
StringInfoData buf;
/* XXX no support here for bootstrap mode */
initStringInfo(&buf);
/*
* Would this proc be found (given the right args) by regprocedurein?
* If not, we need to qualify it.
*/
clist = FuncnameGetCandidates(makeList1(makeString(proname)), nargs);
for (; clist; clist = clist->next)
{
if (memcmp(clist->args, procform->proargtypes,
nargs * sizeof(Oid)) == 0)
break;
}
if (clist != NULL && clist->oid == proid)
if (FunctionIsVisible(proid))
nspname = NULL;
else
nspname = get_namespace_name(procform->pronamespace);
initStringInfo(&buf);
appendStringInfo(&buf, "%s(",
quote_qualified_identifier(nspname, proname));
for (i = 0; i < nargs; i++)
......@@ -632,28 +622,17 @@ regoperatorout(PG_FUNCTION_ARGS)
Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup);
char *oprname = NameStr(operform->oprname);
char *nspname;
FuncCandidateList clist;
StringInfoData buf;
/* XXX no support here for bootstrap mode */
initStringInfo(&buf);
/*
* Would this oper be found (given the right args) by regoperatorin?
* If not, we need to qualify it.
*/
clist = OpernameGetCandidates(makeList1(makeString(oprname)),
operform->oprkind);
for (; clist; clist = clist->next)
{
if (clist->args[0] == operform->oprleft &&
clist->args[1] == operform->oprright)
break;
}
initStringInfo(&buf);
if (clist == NULL || clist->oid != oprid)
if (!OperatorIsVisible(oprid))
{
nspname = get_namespace_name(operform->oprnamespace);
appendStringInfo(&buf, "%s.",
......@@ -815,7 +794,7 @@ regclassout(PG_FUNCTION_ARGS)
* Would this class be found by regclassin?
* If not, qualify it.
*/
if (RelnameGetRelid(classname) == classid)
if (RelationIsVisible(classid))
nspname = NULL;
else
nspname = get_namespace_name(classform->relnamespace);
......@@ -947,7 +926,6 @@ regtypeout(PG_FUNCTION_ARGS)
if (HeapTupleIsValid(typetup))
{
Form_pg_type typeform = (Form_pg_type) GETSTRUCT(typetup);
char *typname = NameStr(typeform->typname);
/*
* In bootstrap mode, skip the fancy namespace stuff and just
......@@ -956,24 +934,13 @@ regtypeout(PG_FUNCTION_ARGS)
*/
if (IsBootstrapProcessingMode())
{
char *typname = NameStr(typeform->typname);
result = pstrdup(typname);
}
else
{
char *nspname;
/*
* Would this type be found by regtypein?
* If not, qualify it.
*
* XXX shouldn't we use format_type instead?
*/
if (TypenameGetTypid(typname) == typid)
nspname = NULL;
else
nspname = get_namespace_name(typeform->typnamespace);
result = quote_qualified_identifier(nspname, typname);
result = format_type_be(typid);
}
ReleaseSysCache(typetup);
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: namespace.h,v 1.11 2002/04/26 01:24:08 tgl Exp $
* $Id: namespace.h,v 1.12 2002/05/01 23:06:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -49,25 +49,25 @@ typedef struct _OpclassCandidateList
extern Oid RangeVarGetRelid(const RangeVar *relation, bool failOK);
extern Oid RangeVarGetCreationNamespace(const RangeVar *newRelation);
extern Oid RelnameGetRelid(const char *relname);
extern bool RelationIsVisible(Oid relid);
extern Oid TypenameGetTypid(const char *typname);
extern Oid OpclassnameGetOpcid(Oid amid, const char *opcname);
extern bool TypeIsVisible(Oid typid);
extern FuncCandidateList FuncnameGetCandidates(List *names, int nargs);
extern bool FunctionIsVisible(Oid funcid);
extern FuncCandidateList OpernameGetCandidates(List *names, char oprkind);
extern bool OperatorIsVisible(Oid oprid);
extern OpclassCandidateList OpclassGetCandidates(Oid amid);
extern Oid OpclassnameGetOpcid(Oid amid, const char *opcname);
extern bool OpclassIsVisible(Oid opcid);
extern Oid QualifiedNameGetCreationNamespace(List *names, char **objname_p);
extern RangeVar *makeRangeVarFromNameList(List *names);
extern char *NameListToString(List *names);
extern bool isTempNamespace(Oid namespaceId);
......
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