Add current_schema() and current_schemas() inquiry functions.

Update has_table_privilege functions to cope with schema-qualified
names in the same way as nextval() and others.
This commit is contained in:
Tom Lane 2002-04-26 01:24:08 +00:00
parent d9375ad564
commit a309032d2f
10 changed files with 259 additions and 180 deletions

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.95 2002/04/18 20:01:08 tgl Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.96 2002/04/26 01:24:08 tgl Exp $
PostgreSQL documentation
-->
@ -3928,6 +3928,12 @@ SELECT TIMESTAMP 'now';
nextval('foo') <lineannotation>operates on sequence </><literal>foo</>
nextval('FOO') <lineannotation>operates on sequence </><literal>foo</>
nextval('"Foo"') <lineannotation>operates on sequence </><literal>Foo</>
</programlisting>
The sequence name can be schema-qualified if necessary:
<programlisting>
nextval('myschema.foo') <lineannotation>operates on </><literal>myschema.foo</>
nextval('"myschema".foo') <lineannotation>same as above</>
nextval('foo') <lineannotation>searches search path for </><literal>foo</>
</programlisting>
Of course, the text argument can be the result of an expression,
not only a simple literal, which is occasionally useful.
@ -4212,17 +4218,27 @@ SELECT NULLIF(value, '(none)') ...
<row>
<entry><function>current_user</></entry>
<entry><type>name</></entry>
<entry>user name of current execution context</>
<entry>user name of current execution context</entry>
</row>
<row>
<entry><function>session_user</></entry>
<entry><type>name</></entry>
<entry>session user name</>
<entry>session user name</entry>
</row>
<row>
<entry><function>user</></entry>
<entry><type>name</></entry>
<entry>equivalent to <function>current_user</></>
<entry>equivalent to <function>current_user</></entry>
</row>
<row>
<entry><function>current_schema()</></entry>
<entry><type>name</></entry>
<entry>name of current schema</entry>
</row>
<row>
<entry><function>current_schemas()</></entry>
<entry><type>name[]</></entry>
<entry>names of schemas in search path</entry>
</row>
</tbody>
</tgroup>
@ -4233,6 +4249,16 @@ SELECT NULLIF(value, '(none)') ...
<secondary>current</secondary>
</indexterm>
<indexterm zone="functions-misc">
<primary>schema</primary>
<secondary>current</secondary>
</indexterm>
<indexterm zone="functions-misc">
<primary>search path</primary>
<secondary>current</secondary>
</indexterm>
<para>
The <function>session_user</> is the user that initiated a database
connection; it is fixed for the duration of that connection. The
@ -4244,10 +4270,13 @@ SELECT NULLIF(value, '(none)') ...
and the current user is the <quote>effective user</>.
</para>
<para>
Note that these functions have special syntactic status in <acronym>SQL</>:
they must be called without trailing parentheses.
</para>
<note>
<para>
<function>current_user</>, <function>session_user</>, and
<function>user</> have special syntactic status in <acronym>SQL</>:
they must be called without trailing parentheses.
</para>
</note>
<note>
<title>Deprecated</>
@ -4257,6 +4286,17 @@ SELECT NULLIF(value, '(none)') ...
</para>
</note>
<para>
<function>current_schema</> returns the name of the schema that is
at the front of the search path (or NULL if the search path is
empty). This is the schema that will be used for any tables or
other named objects that are created without specifying a target schema.
<function>current_schemas</> returns an array of the names of all
schemas presently in the search path. Note that these functions show
only schemas that are explicitly part of the path; when a system schema
is being searched implicitly, it is not listed.
</para>
<table>
<title>System Information Functions</>
<tgroup cols="3">
@ -4323,11 +4363,17 @@ SELECT NULLIF(value, '(none)') ...
<function>current_user</> is assumed. The table can be specified
by name or by OID. (Thus, there are actually six variants of
<function>has_table_privilege</>, which can be distinguished by
the number and types of their arguments.) The desired access type
the number and types of their arguments.) When specifying by name,
the name can be schema-qualified if necessary.
The desired access type
is specified by a text string, which must evaluate to one of the
values <literal>SELECT</>, <literal>INSERT</>, <literal>UPDATE</>,
<literal>DELETE</>, <literal>RULE</>, <literal>REFERENCES</>, or
<literal>TRIGGER</>. (Case of the string is not significant, however.)
An example is:
<programlisting>
SELECT has_table_privilege('myschema.mytable', 'select');
</programlisting>
</para>
<table>

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.113 2002/04/15 22:33:20 tgl Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.114 2002/04/26 01:24:08 tgl Exp $
-->
<Chapter Id="runtime">
@ -1252,6 +1252,15 @@ dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir'
The administrator may choose to restrict permissions on
<literal>public</> or even remove it, if that suits his purposes.
</para>
<para>
The current effective value of the search path can be examined
via the SQL function <function>current_schemas()</>. This is not
quite the same as examining the value of
<varname>search_path</varname>, since <function>current_schemas()</>
shows how the requests appearing in <varname>search_path</varname>
were resolved.
</para>
</listitem>
</varlistentry>

View File

@ -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.12 2002/04/25 02:56:55 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.13 2002/04/26 01:24:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1470,3 +1470,14 @@ InitializeSearchPath(void)
assign_search_path(namespace_search_path);
}
}
/*
* Fetch the active search path, expressed as a List of OIDs.
*
* NB: caller must treat the list as read-only!
*/
List *
fetch_search_path(void)
{
return namespaceSearchPath;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.71 2002/04/21 00:26:43 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.72 2002/04/26 01:24:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -35,12 +35,7 @@ static bool aclitemeq(const AclItem *a1, const AclItem *a2);
static bool aclitemgt(const AclItem *a1, const AclItem *a2);
static AclMode convert_priv_string(text *priv_type_text);
static bool has_table_privilege_cname_cname(char *username, char *relname,
text *priv_type_text);
static bool has_table_privilege_cname_id(char *username, Oid reloid,
text *priv_type_text);
static bool has_table_privilege_id_cname(int32 usesysid, char *relname,
text *priv_type_text);
static Oid convert_rel_name(text *relname);
/*
@ -653,7 +648,7 @@ aclcontains(PG_FUNCTION_ARGS)
/*
* has_table_privilege_name_name
* Check user privileges on a relation given
* name username, name relname, and text priv name.
* name username, text relname, and text priv name.
*
* RETURNS
* a boolean value
@ -664,22 +659,41 @@ Datum
has_table_privilege_name_name(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Name relname = PG_GETARG_NAME(1);
text *relname = PG_GETARG_TEXT_P(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
bool result;
int32 usesysid;
Oid reloid;
AclMode mode;
int32 aclresult;
result = has_table_privilege_cname_cname(NameStr(*username),
NameStr(*relname),
priv_type_text);
/*
* Lookup userid based on username
*/
usesysid = get_usesysid(NameStr(*username));
PG_RETURN_BOOL(result);
/*
* Lookup rel OID based on relname
*/
reloid = convert_rel_name(relname);
/*
* Convert priv_type_text to an AclMode
*/
mode = convert_priv_string(priv_type_text);
/*
* Check for the privilege
*/
aclresult = pg_class_aclcheck(reloid, usesysid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* has_table_privilege_name
* Check user privileges on a relation given
* name relname and text priv name.
* text relname and text priv name.
* current_user is assumed
*
* RETURNS
@ -690,18 +704,31 @@ has_table_privilege_name_name(PG_FUNCTION_ARGS)
Datum
has_table_privilege_name(PG_FUNCTION_ARGS)
{
Name relname = PG_GETARG_NAME(0);
text *relname = PG_GETARG_TEXT_P(0);
text *priv_type_text = PG_GETARG_TEXT_P(1);
int32 usesysid;
bool result;
Oid reloid;
AclMode mode;
int32 aclresult;
usesysid = GetUserId();
result = has_table_privilege_id_cname(usesysid,
NameStr(*relname),
priv_type_text);
/*
* Lookup rel OID based on relname
*/
reloid = convert_rel_name(relname);
PG_RETURN_BOOL(result);
/*
* Convert priv_type_text to an AclMode
*/
mode = convert_priv_string(priv_type_text);
/*
* Check for the privilege
*/
aclresult = pg_class_aclcheck(reloid, usesysid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -721,13 +748,26 @@ has_table_privilege_name_id(PG_FUNCTION_ARGS)
Name username = PG_GETARG_NAME(0);
Oid reloid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
bool result;
int32 usesysid;
AclMode mode;
int32 aclresult;
result = has_table_privilege_cname_id(NameStr(*username),
reloid,
priv_type_text);
/*
* Lookup userid based on username
*/
usesysid = get_usesysid(NameStr(*username));
PG_RETURN_BOOL(result);
/*
* Convert priv_type_text to an AclMode
*/
mode = convert_priv_string(priv_type_text);
/*
* Check for the privilege
*/
aclresult = pg_class_aclcheck(reloid, usesysid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -749,7 +789,7 @@ has_table_privilege_id(PG_FUNCTION_ARGS)
text *priv_type_text = PG_GETARG_TEXT_P(1);
int32 usesysid;
AclMode mode;
int32 result;
int32 aclresult;
usesysid = GetUserId();
@ -761,19 +801,16 @@ has_table_privilege_id(PG_FUNCTION_ARGS)
/*
* Check for the privilege
*/
result = pg_class_aclcheck(reloid, usesysid, mode);
aclresult = pg_class_aclcheck(reloid, usesysid, mode);
if (result == ACLCHECK_OK)
PG_RETURN_BOOL(true);
else
PG_RETURN_BOOL(false);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* has_table_privilege_id_name
* Check user privileges on a relation given
* usesysid, name relname, and priv name.
* usesysid, text relname, and priv name.
*
* RETURNS
* a boolean value
@ -784,15 +821,28 @@ Datum
has_table_privilege_id_name(PG_FUNCTION_ARGS)
{
int32 usesysid = PG_GETARG_INT32(0);
Name relname = PG_GETARG_NAME(1);
text *relname = PG_GETARG_TEXT_P(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
bool result;
Oid reloid;
AclMode mode;
int32 aclresult;
result = has_table_privilege_id_cname(usesysid,
NameStr(*relname),
priv_type_text);
/*
* Lookup rel OID based on relname
*/
reloid = convert_rel_name(relname);
PG_RETURN_BOOL(result);
/*
* Convert priv_type_text to an AclMode
*/
mode = convert_priv_string(priv_type_text);
/*
* Check for the privilege
*/
aclresult = pg_class_aclcheck(reloid, usesysid, mode);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -813,7 +863,7 @@ has_table_privilege_id_id(PG_FUNCTION_ARGS)
Oid reloid = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_P(2);
AclMode mode;
int32 result;
int32 aclresult;
/*
* Convert priv_type_text to an AclMode
@ -823,18 +873,29 @@ has_table_privilege_id_id(PG_FUNCTION_ARGS)
/*
* Check for the privilege
*/
result = pg_class_aclcheck(reloid, usesysid, mode);
aclresult = pg_class_aclcheck(reloid, usesysid, mode);
if (result == ACLCHECK_OK)
PG_RETURN_BOOL(true);
else
PG_RETURN_BOOL(false);
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
/*
* Internal functions.
*/
/*
* Given a relation name expressed as a string, look it up and return Oid
*/
static Oid
convert_rel_name(text *relname)
{
RangeVar *relrv;
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname,
"has_table_privilege"));
return RangeVarGetRelid(relrv, false);
}
/*
* convert_priv_string
* Internal function.
@ -843,11 +904,13 @@ has_table_privilege_id_id(PG_FUNCTION_ARGS)
* RETURNS
* AclMode
*/
static AclMode
convert_priv_string(text *priv_type_text)
{
char *priv_type = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(priv_type_text)));
char *priv_type;
priv_type = DatumGetCString(DirectFunctionCall1(textout,
PointerGetDatum(priv_type_text)));
/*
* Return mode from priv_type string
@ -880,114 +943,3 @@ convert_priv_string(text *priv_type_text)
*/
return ACL_NO_RIGHTS;
}
/*
* has_table_privilege_cname_cname
* Check user privileges on a relation given
* char *usename, char *relname, and text priv name.
*
* RETURNS
* a boolean value
* 't' indicating user has the privilege
* 'f' indicating user does not have the privilege
*/
static bool
has_table_privilege_cname_cname(char *username, char *relname,
text *priv_type_text)
{
int32 usesysid;
/*
* Lookup userid based on username
*/
usesysid = get_usesysid(username);
/*
* Make use of has_table_privilege_id_cname. It accepts the arguments
* we now have.
*/
return has_table_privilege_id_cname(usesysid, relname, priv_type_text);
}
/*
* has_table_privilege_cname_id
* Check user privileges on a relation given
* char *usename, rel oid, and text priv name.
*
* RETURNS
* a boolean value
* 't' indicating user has the privilege
* 'f' indicating user does not have the privilege
*/
static bool
has_table_privilege_cname_id(char *username, Oid reloid,
text *priv_type_text)
{
int32 usesysid;
AclMode mode;
int32 result;
/*
* Lookup userid based on username
*/
usesysid = get_usesysid(username);
/*
* Convert priv_type_text to an AclMode
*/
mode = convert_priv_string(priv_type_text);
/*
* Finally, check for the privilege
*/
result = pg_class_aclcheck(reloid, usesysid, mode);
if (result == ACLCHECK_OK)
return true;
else
return false;
}
/*
* has_table_privilege_id_cname
* Check user privileges on a relation given
* usesysid, char *relname, and text priv name.
*
* RETURNS
* a boolean value
* 't' indicating user has the privilege
* 'f' indicating user does not have the privilege
*/
static bool
has_table_privilege_id_cname(int32 usesysid, char *relname,
text *priv_type_text)
{
Oid reloid;
AclMode mode;
int32 result;
/*
* Convert relname to rel OID.
*/
reloid = RelnameGetRelid(relname);
if (!OidIsValid(reloid))
elog(ERROR, "has_table_privilege: relation \"%s\" does not exist",
relname);
/*
* Convert priv_type_text to an AclMode
*/
mode = convert_priv_string(priv_type_text);
/*
* Finally, check for the privilege
*/
result = pg_class_aclcheck(reloid, usesysid, mode);
if (result == ACLCHECK_OK)
return true;
else
return false;
}

View File

@ -12,14 +12,18 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/name.c,v 1.33 2001/10/28 06:25:52 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/name.c,v 1.34 2002/04/26 01:24:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "catalog/namespace.h"
#include "miscadmin.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
/*****************************************************************************
* USER I/O ROUTINES (none) *
@ -209,7 +213,9 @@ namestrcmp(Name name, const char *str)
}
/* SQL-functions CURRENT_USER and SESSION_USER */
/*
* SQL-functions CURRENT_USER, SESSION_USER
*/
Datum
current_user(PG_FUNCTION_ARGS)
{
@ -223,6 +229,52 @@ session_user(PG_FUNCTION_ARGS)
}
/*
* SQL-functions CURRENT_SCHEMA, CURRENT_SCHEMAS
*/
Datum
current_schema(PG_FUNCTION_ARGS)
{
List *search_path = fetch_search_path();
char *nspname;
if (search_path == NIL)
PG_RETURN_NULL();
nspname = get_namespace_name((Oid) lfirsti(search_path));
PG_RETURN_DATUM(DirectFunctionCall1(namein, CStringGetDatum(nspname)));
}
Datum
current_schemas(PG_FUNCTION_ARGS)
{
List *search_path = fetch_search_path();
int nnames = length(search_path);
Datum *names;
int i;
ArrayType *array;
/* +1 here is just to avoid palloc(0) error */
names = (Datum *) palloc((nnames + 1) * sizeof(Datum));
i = 0;
while (search_path)
{
char *nspname;
nspname = get_namespace_name((Oid) lfirsti(search_path));
names[i] = DirectFunctionCall1(namein, CStringGetDatum(nspname));
i++;
search_path = lnext(search_path);
}
array = construct_array(names, nnames,
false, /* Name is not by-val */
NAMEDATALEN, /* sizeof(Name) */
'i'); /* alignment of Name */
PG_RETURN_POINTER(array);
}
/*****************************************************************************
* PRIVATE ROUTINES *
*****************************************************************************/

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: catversion.h,v 1.126 2002/04/25 02:56:56 tgl Exp $
* $Id: catversion.h,v 1.127 2002/04/26 01:24:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200204242
#define CATALOG_VERSION_NO 200204251
#endif

View File

@ -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.10 2002/04/25 02:56:56 tgl Exp $
* $Id: namespace.h,v 1.11 2002/04/26 01:24:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -79,4 +79,6 @@ extern bool check_search_path(const char *proposed);
extern void assign_search_path(const char *newval);
extern void InitializeSearchPath(void);
extern List *fetch_search_path(void);
#endif /* NAMESPACE_H */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_proc.h,v 1.233 2002/04/25 02:56:56 tgl Exp $
* $Id: pg_proc.h,v 1.234 2002/04/26 01:24:08 tgl Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
@ -1751,6 +1751,11 @@ DESCR("convert varchar to name");
DATA(insert OID = 1401 ( varchar PGNSP PGUID 12 f t t t f i 1 1043 "19" 100 0 0 100 name_text - _null_ ));
DESCR("convert name to varchar");
DATA(insert OID = 1402 ( current_schema PGNSP PGUID 12 f t f t f s 0 19 "0" 100 0 0 100 current_schema - _null_ ));
DESCR("current schema name");
DATA(insert OID = 1403 ( current_schemas PGNSP PGUID 12 f t f t f s 0 1003 "0" 100 0 0 100 current_schemas - _null_ ));
DESCR("current schema search list");
DATA(insert OID = 1406 ( isvertical PGNSP PGUID 12 f t f t f i 2 16 "600 600" 100 0 0 100 point_vert - _null_ ));
DESCR("vertically aligned?");
DATA(insert OID = 1407 ( ishorizontal PGNSP PGUID 12 f t f t f i 2 16 "600 600" 100 0 0 100 point_horiz - _null_ ));
@ -2663,15 +2668,15 @@ DESCR("unary plus");
DATA(insert OID = 1915 ( numeric_uplus PGNSP PGUID 12 f t f t f i 1 1700 "1700" 100 0 0 100 numeric_uplus - _null_ ));
DESCR("unary plus");
DATA(insert OID = 1922 ( has_table_privilege PGNSP PGUID 12 f t f t f s 3 16 "19 19 25" 100 0 0 100 has_table_privilege_name_name - _null_ ));
DATA(insert OID = 1922 ( has_table_privilege PGNSP PGUID 12 f t f t f s 3 16 "19 25 25" 100 0 0 100 has_table_privilege_name_name - _null_ ));
DESCR("user privilege on relation by username, relname");
DATA(insert OID = 1923 ( has_table_privilege PGNSP PGUID 12 f t f t f s 3 16 "19 26 25" 100 0 0 100 has_table_privilege_name_id - _null_ ));
DESCR("user privilege on relation by username, rel oid");
DATA(insert OID = 1924 ( has_table_privilege PGNSP PGUID 12 f t f t f s 3 16 "23 19 25" 100 0 0 100 has_table_privilege_id_name - _null_ ));
DATA(insert OID = 1924 ( has_table_privilege PGNSP PGUID 12 f t f t f s 3 16 "23 25 25" 100 0 0 100 has_table_privilege_id_name - _null_ ));
DESCR("user privilege on relation by usesysid, relname");
DATA(insert OID = 1925 ( has_table_privilege PGNSP PGUID 12 f t f t f s 3 16 "23 26 25" 100 0 0 100 has_table_privilege_id_id - _null_ ));
DESCR("user privilege on relation by usesysid, rel oid");
DATA(insert OID = 1926 ( has_table_privilege PGNSP PGUID 12 f t f t f s 2 16 "19 25" 100 0 0 100 has_table_privilege_name - _null_ ));
DATA(insert OID = 1926 ( has_table_privilege PGNSP PGUID 12 f t f t f s 2 16 "25 25" 100 0 0 100 has_table_privilege_name - _null_ ));
DESCR("current user privilege on relation by relname");
DATA(insert OID = 1927 ( has_table_privilege PGNSP PGUID 12 f t f t f s 2 16 "26 25" 100 0 0 100 has_table_privilege_id - _null_ ));
DESCR("current user privilege on relation by rel oid");

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: builtins.h,v 1.179 2002/04/25 02:56:56 tgl Exp $
* $Id: builtins.h,v 1.180 2002/04/26 01:24:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -164,6 +164,8 @@ extern int namestrcpy(Name name, const char *str);
extern int namestrcmp(Name name, const char *str);
extern Datum current_user(PG_FUNCTION_ARGS);
extern Datum session_user(PG_FUNCTION_ARGS);
extern Datum current_schema(PG_FUNCTION_ARGS);
extern Datum current_schemas(PG_FUNCTION_ARGS);
/* numutils.c */
extern int32 pg_atoi(char *s, int size, int c);

View File

@ -240,7 +240,7 @@ select has_table_privilege(NULL,'pg_shadow','select');
(1 row)
select has_table_privilege('pg_shad','select');
ERROR: has_table_privilege: relation "pg_shad" does not exist
ERROR: Relation "pg_shad" does not exist
select has_table_privilege('nosuchuser','pg_shadow','select');
ERROR: user "nosuchuser" does not exist
select has_table_privilege('pg_shadow','sel');