diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index cb7a5a00d981115911b43758da245f5dee336c6a..998f693c79b7eabe279deabd02aad5e3ae661a97 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.213 2004/07/02 22:49:45 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.214 2004/07/12 20:23:47 momjian Exp $
 PostgreSQL documentation
 -->
 
@@ -6980,6 +6980,21 @@ SELECT set_config('log_statement_stats', 'off', false);
        <entry><type>boolean</type></entry>
        <entry>does current user have privilege for schema</entry>
       </row>
+      <row>
+       <entry><literal><function>has_tablespace_privilege</function>(<parameter>user</parameter>,
+                                  <parameter>tablespace</parameter>,
+                                  <parameter>privilege</parameter>)</literal>
+       </entry>
+       <entry><type>boolean</type></entry>
+       <entry>does user have privilege for tablespace</entry>
+      </row>
+      <row>
+       <entry><literal><function>has_tablespace_privilege</function>(<parameter>tablespace</parameter>,
+                                  <parameter>privilege</parameter>)</literal>
+       </entry>
+       <entry><type>boolean</type></entry>
+       <entry>does current user have privilege for tablespace</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
@@ -6999,6 +7014,9 @@ SELECT set_config('log_statement_stats', 'off', false);
    <indexterm zone="functions-misc">
     <primary>has_schema_privilege</primary>
    </indexterm>
+   <indexterm zone="functions-misc">
+    <primary>has_tablespace_privilege</primary>
+   </indexterm>
 
    <para>
     <function>has_table_privilege</function> checks whether a user
@@ -7064,6 +7082,14 @@ SELECT has_function_privilege('joeuser', 'myfunc(int, text)', 'execute');
     <literal>USAGE</literal>.
    </para>
 
+   <para>
+    <function>has_tablespace_privilege</function> checks whether a user
+    can access a tablespace in a particular way.  The possibilities for its
+    arguments are analogous to <function>has_table_privilege</function>.
+    The desired access privilege type must evaluate to
+    <literal>CREATE</literal>.
+   </para>
+
   <para>
    To evaluate whether a user holds a grant option on the privilege,
    append <literal> WITH GRANT OPTION</literal> to the privilege key
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 4b13e318be6c69c8025356f0e9f7f6e8f2d1a91d..4b45ca7c4d479f2345af028de5758b3958e9439a 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.106 2004/06/18 06:13:49 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.107 2004/07/12 20:23:50 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,6 +21,7 @@
 #include "catalog/pg_shadow.h"
 #include "catalog/pg_type.h"
 #include "commands/dbcommands.h"
+#include "commands/tablespace.h"
 #include "miscadmin.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
@@ -54,6 +55,8 @@ static Oid	convert_language_name(text *languagename);
 static AclMode convert_language_priv_string(text *priv_type_text);
 static Oid	convert_schema_name(text *schemaname);
 static AclMode convert_schema_priv_string(text *priv_type_text);
+static Oid convert_tablespace_name(text *tablespacename);
+static AclMode convert_tablespace_priv_string(text *priv_type_text);
 
 
 /*
@@ -2207,3 +2210,204 @@ convert_schema_priv_string(text *priv_type_text)
 			 errmsg("unrecognized privilege type: \"%s\"", priv_type)));
 	return ACL_NO_RIGHTS;		/* keep compiler quiet */
 }
+
+/*
+ * has_tablespace_privilege variants
+ *		These are all named "has_tablespace_privilege" at the SQL level.
+ *		They take various combinations of tablespace name, tablespace OID,
+ *		user name, user sysid, or implicit user = current_user.
+ *
+ *		The result is a boolean value: true if user has the indicated
+ *		privilege, false if not.
+ */
+
+/*
+ * has_tablespace_privilege_name_name
+ *		Check user privileges on a tablespace given
+ *		name username, text tablespacename, and text priv name.
+ */
+Datum
+has_tablespace_privilege_name_name(PG_FUNCTION_ARGS)
+{
+	Name		username = PG_GETARG_NAME(0);
+	text	   *tablespacename = PG_GETARG_TEXT_P(1);
+	text	   *priv_type_text = PG_GETARG_TEXT_P(2);
+	int32		usesysid;
+	Oid			tablespaceoid;
+	AclMode		mode;
+	AclResult	aclresult;
+
+	usesysid = get_usesysid(NameStr(*username));
+	tablespaceoid = convert_tablespace_name(tablespacename);
+	mode = convert_tablespace_priv_string(priv_type_text);
+
+	aclresult = pg_tablespace_aclcheck(tablespaceoid, usesysid, mode);
+
+	PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_tablespace_privilege_name
+ *		Check user privileges on a tablespace given
+ *		text tablespacename and text priv name.
+ *		current_user is assumed
+ */
+Datum
+has_tablespace_privilege_name(PG_FUNCTION_ARGS)
+{
+	text	   *tablespacename = PG_GETARG_TEXT_P(0);
+	text	   *priv_type_text = PG_GETARG_TEXT_P(1);
+	AclId		usesysid;
+	Oid			tablespaceoid;
+	AclMode		mode;
+	AclResult	aclresult;
+
+	usesysid = GetUserId();
+	tablespaceoid = convert_tablespace_name(tablespacename);
+	mode = convert_tablespace_priv_string(priv_type_text);
+
+	aclresult = pg_tablespace_aclcheck(tablespaceoid, usesysid, mode);
+
+	PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_tablespace_privilege_name_id
+ *		Check user privileges on a tablespace given
+ *		name usename, tablespace oid, and text priv name.
+ */
+Datum
+has_tablespace_privilege_name_id(PG_FUNCTION_ARGS)
+{
+	Name		username = PG_GETARG_NAME(0);
+	Oid			tablespaceoid = PG_GETARG_OID(1);
+	text	   *priv_type_text = PG_GETARG_TEXT_P(2);
+	int32		usesysid;
+	AclMode		mode;
+	AclResult	aclresult;
+
+	usesysid = get_usesysid(NameStr(*username));
+	mode = convert_tablespace_priv_string(priv_type_text);
+
+	aclresult = pg_tablespace_aclcheck(tablespaceoid, usesysid, mode);
+
+	PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_tablespace_privilege_id
+ *		Check user privileges on a tablespace given
+ *		tablespace oid, and text priv name.
+ *		current_user is assumed
+ */
+Datum
+has_tablespace_privilege_id(PG_FUNCTION_ARGS)
+{
+	Oid			tablespaceoid = PG_GETARG_OID(0);
+	text	   *priv_type_text = PG_GETARG_TEXT_P(1);
+	AclId		usesysid;
+	AclMode		mode;
+	AclResult	aclresult;
+
+	usesysid = GetUserId();
+	mode = convert_tablespace_priv_string(priv_type_text);
+
+	aclresult = pg_tablespace_aclcheck(tablespaceoid, usesysid, mode);
+
+	PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_tablespace_privilege_id_name
+ *		Check user privileges on a tablespace given
+ *		usesysid, text tablespacename, and text priv name.
+ */
+Datum
+has_tablespace_privilege_id_name(PG_FUNCTION_ARGS)
+{
+	int32		usesysid = PG_GETARG_INT32(0);
+	text	   *tablespacename = PG_GETARG_TEXT_P(1);
+	text	   *priv_type_text = PG_GETARG_TEXT_P(2);
+	Oid			tablespaceoid;
+	AclMode		mode;
+	AclResult	aclresult;
+
+	tablespaceoid = convert_tablespace_name(tablespacename);
+	mode = convert_tablespace_priv_string(priv_type_text);
+
+	aclresult = pg_tablespace_aclcheck(tablespaceoid, usesysid, mode);
+
+	PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_tablespace_privilege_id_id
+ *		Check user privileges on a tablespace given
+ *		usesysid, tablespace oid, and text priv name.
+ */
+Datum
+has_tablespace_privilege_id_id(PG_FUNCTION_ARGS)
+{
+	int32		usesysid = PG_GETARG_INT32(0);
+	Oid			tablespaceoid = PG_GETARG_OID(1);
+	text	   *priv_type_text = PG_GETARG_TEXT_P(2);
+	AclMode		mode;
+	AclResult	aclresult;
+
+	mode = convert_tablespace_priv_string(priv_type_text);
+
+	aclresult = pg_tablespace_aclcheck(tablespaceoid, usesysid, mode);
+
+	PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ *		Support routines for has_tablespace_privilege family.
+ */
+
+/*
+ * Given a tablespace name expressed as a string, look it up and return Oid
+ */
+static Oid
+convert_tablespace_name(text *tablespacename)
+{
+	char			*spcname;
+	Oid			oid;
+	
+	spcname = DatumGetCString(DirectFunctionCall1(textout,
+                                                           PointerGetDatum(tablespacename)));
+	oid = get_tablespace_oid(spcname);
+
+        if (!OidIsValid(oid))
+                ereport(ERROR,
+                                (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                 errmsg("tablespace \"%s\" does not exist", spcname)));
+
+	return oid;
+}
+
+/*
+ * convert_tablespace_priv_string
+ *		Convert text string to AclMode value.
+ */
+static AclMode
+convert_tablespace_priv_string(text *priv_type_text)
+{
+	char	   *priv_type;
+
+	priv_type = DatumGetCString(DirectFunctionCall1(textout,
+									   PointerGetDatum(priv_type_text)));
+
+	/*
+	 * Return mode from priv_type string
+	 */
+	if (pg_strcasecmp(priv_type, "CREATE") == 0)
+		return ACL_CREATE;
+	if (pg_strcasecmp(priv_type, "CREATE WITH GRANT OPTION") == 0)
+		return ACL_GRANT_OPTION_FOR(ACL_CREATE);
+
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			 errmsg("unrecognized privilege type: \"%s\"", priv_type)));
+	return ACL_NO_RIGHTS;		/* keep compiler quiet */
+}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 4550e67057f33b7a0e868f0d50f83b968cae315b..067d7347866b42da61572e9b672a963a83a8a1fe 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.243 2004/07/02 22:49:48 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.244 2004/07/12 20:23:51 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200407022
+#define CATALOG_VERSION_NO	200407121
 
 #endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 263a3492db5440613125d4c7286abba52f718164..646fa73dd15aae4eaff44153d0ddb32b598c40fd 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.341 2004/07/02 22:49:48 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.342 2004/07/12 20:23:53 momjian Exp $
  *
  * NOTES
  *	  The script catalog/genbki.sh reads this file and generates .bki
@@ -3181,6 +3181,18 @@ DESCR("current user privilege on schema by schema name");
 DATA(insert OID = 2273 (  has_schema_privilege		   PGNSP PGUID 12 f f t f s 2 16 "26 25" _null_	has_schema_privilege_id - _null_ ));
 DESCR("current user privilege on schema by schema oid");
 
+DATA(insert OID = 2390 (  has_tablespace_privilege		   PGNSP PGUID 12 f f t f s 3 16 "19 25 25" _null_  has_tablespace_privilege_name_name - _null_ ));
+DESCR("user privilege on tablespace by username, tablespace name");
+DATA(insert OID = 2391 (  has_tablespace_privilege		   PGNSP PGUID 12 f f t f s 3 16 "19 26 25" _null_  has_tablespace_privilege_name_id - _null_ ));
+DESCR("user privilege on tablespace by username, tablespace oid");
+DATA(insert OID = 2392 (  has_tablespace_privilege		   PGNSP PGUID 12 f f t f s 3 16 "23 25 25" _null_  has_tablespace_privilege_id_name - _null_ ));
+DESCR("user privilege on tablespace by usesysid, tablespace name");
+DATA(insert OID = 2393 (  has_tablespace_privilege		   PGNSP PGUID 12 f f t f s 3 16 "23 26 25" _null_  has_tablespace_privilege_id_id - _null_ ));
+DESCR("user privilege on tablespace by usesysid, tablespace oid");
+DATA(insert OID = 2394 (  has_tablespace_privilege		   PGNSP PGUID 12 f f t f s 2 16 "25 25" _null_	has_tablespace_privilege_name - _null_ ));
+DESCR("current user privilege on tablespace by tablespace name");
+DATA(insert OID = 2395 (  has_tablespace_privilege		   PGNSP PGUID 12 f f t f s 2 16 "26 25" _null_	has_tablespace_privilege_id - _null_ ));
+DESCR("current user privilege on tablespace by tablespace oid");
 
 DATA(insert OID = 2290 (  record_in			PGNSP PGUID 12 f f t f v 2 2249 "2275 26" _null_	record_in - _null_ ));
 DESCR("I/O");
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index c18ad551e485a988f883aa52f583196a6b586493..3e9f58e7f26970702078dc6ec44348fc63cbbd84 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.245 2004/07/02 18:59:25 joe Exp $
+ * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.246 2004/07/12 20:23:59 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -52,6 +52,12 @@ extern Datum has_schema_privilege_id_name(PG_FUNCTION_ARGS);
 extern Datum has_schema_privilege_id_id(PG_FUNCTION_ARGS);
 extern Datum has_schema_privilege_name(PG_FUNCTION_ARGS);
 extern Datum has_schema_privilege_id(PG_FUNCTION_ARGS);
+extern Datum has_tablespace_privilege_name_name(PG_FUNCTION_ARGS);
+extern Datum has_tablespace_privilege_name_id(PG_FUNCTION_ARGS);
+extern Datum has_tablespace_privilege_id_name(PG_FUNCTION_ARGS);
+extern Datum has_tablespace_privilege_id_id(PG_FUNCTION_ARGS);
+extern Datum has_tablespace_privilege_name(PG_FUNCTION_ARGS);
+extern Datum has_tablespace_privilege_id(PG_FUNCTION_ARGS);
 
 /* bool.c */
 extern Datum boolin(PG_FUNCTION_ARGS);