mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-31 00:03:57 -04:00 
			
		
		
		
	Support explicit placement of the temporary-table schema within search_path.
This is needed to allow a security-definer function to set a truly secure value of search_path. Without it, a malicious user can use temporary objects to execute code with the privileges of the security-definer function. Even pushing the temp schema to the back of the search path is not quite good enough, because a function or operator at the back of the path might still capture control from one nearer the front due to having a more exact datatype match. Hence, disable searching the temp schema altogether for functions and operators. Security: CVE-2007-2138
This commit is contained in:
		
							parent
							
								
									9350056eaa
								
							
						
					
					
						commit
						aa27977fe2
					
				| @ -1,4 +1,4 @@ | |||||||
| <!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.121 2007/04/18 16:44:17 alvherre Exp $ --> | <!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.122 2007/04/20 02:37:37 tgl Exp $ --> | ||||||
| 
 | 
 | ||||||
| <chapter Id="runtime-config"> | <chapter Id="runtime-config"> | ||||||
|   <title>Server Configuration</title> |   <title>Server Configuration</title> | ||||||
| @ -3405,9 +3405,17 @@ SELECT * FROM parent WHERE key = 2400; | |||||||
|         mentioned in the path then it will be searched in the specified |         mentioned in the path then it will be searched in the specified | ||||||
|         order.  If <literal>pg_catalog</> is not in the path then it will |         order.  If <literal>pg_catalog</> is not in the path then it will | ||||||
|         be searched <emphasis>before</> searching any of the path items. |         be searched <emphasis>before</> searching any of the path items. | ||||||
|         It should also be noted that the temporary-table schema, |        </para> | ||||||
|         <literal>pg_temp_<replaceable>nnn</></>, is implicitly searched before any of | 
 | ||||||
|         these. |        <para> | ||||||
|  |         Likewise, the current session's temporary-table schema, | ||||||
|  |         <literal>pg_temp_<replaceable>nnn</></>, is always searched if it | ||||||
|  |         exists.  It can be explicitly listed in the path by using the | ||||||
|  |         alias <literal>pg_temp</>.  If it is not listed in the path then | ||||||
|  |         it is searched first (before even <literal>pg_catalog</>).  However, | ||||||
|  |         the temporary schema is only searched for relation (table, view, | ||||||
|  |         sequence, etc) and data type names.  It will never be searched for | ||||||
|  |         function or operator names. | ||||||
|        </para> |        </para> | ||||||
| 
 | 
 | ||||||
|        <para> |        <para> | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| <!-- | <!-- | ||||||
| $PostgreSQL: pgsql/doc/src/sgml/ref/create_function.sgml,v 1.73 2007/02/01 19:10:24 momjian Exp $ | $PostgreSQL: pgsql/doc/src/sgml/ref/create_function.sgml,v 1.74 2007/04/20 02:37:37 tgl Exp $ | ||||||
| --> | --> | ||||||
| 
 | 
 | ||||||
| <refentry id="SQL-CREATEFUNCTION"> | <refentry id="SQL-CREATEFUNCTION"> | ||||||
| @ -508,6 +508,54 @@ SELECT * FROM dup(42); | |||||||
|   </para> |   </para> | ||||||
|  </refsect1> |  </refsect1> | ||||||
| 
 | 
 | ||||||
|  |  <refsect1 id="sql-createfunction-security"> | ||||||
|  |   <title>Writing <literal>SECURITY DEFINER</literal> Functions Safely</title> | ||||||
|  | 
 | ||||||
|  |    <para> | ||||||
|  |     Because a <literal>SECURITY DEFINER</literal> function is executed | ||||||
|  |     with the privileges of the user that created it, care is needed to | ||||||
|  |     ensure that the function cannot be misused.  For security, | ||||||
|  |     <xref linkend="guc-search-path"> should be set to exclude any schemas | ||||||
|  |     writable by untrusted users.  This prevents | ||||||
|  |     malicious users from creating objects that mask objects used by the | ||||||
|  |     function.  Particularly important is in this regard is the | ||||||
|  |     temporary-table schema, which is searched first by default, and | ||||||
|  |     is normally writable by anyone.  A secure arrangement can be had | ||||||
|  |     by forcing the temporary schema to be searched last.  To do this, | ||||||
|  |     write <literal>pg_temp</> as the last entry in <varname>search_path</>. | ||||||
|  |     This function illustrates safe usage: | ||||||
|  |    </para> | ||||||
|  | 
 | ||||||
|  | <programlisting> | ||||||
|  | CREATE FUNCTION check_password(uname TEXT, pass TEXT) | ||||||
|  | RETURNS BOOLEAN AS $$ | ||||||
|  | DECLARE passed BOOLEAN; | ||||||
|  |         old_path TEXT; | ||||||
|  | BEGIN | ||||||
|  |         -- Save old search_path; notice we must qualify current_setting | ||||||
|  |         -- to ensure we invoke the right function | ||||||
|  |         old_path := pg_catalog.current_setting('search_path'); | ||||||
|  | 
 | ||||||
|  |         -- Set a secure search_path: trusted schemas, then 'pg_temp'. | ||||||
|  |         -- We set is_local = true so that the old value will be restored | ||||||
|  |         -- in event of an error before we reach the function end. | ||||||
|  |         PERFORM pg_catalog.set_config('search_path', 'admin, pg_temp', true); | ||||||
|  | 
 | ||||||
|  |         -- Do whatever secure work we came for. | ||||||
|  |         SELECT  (pwd = $2) INTO passed | ||||||
|  |         FROM    pwds | ||||||
|  |         WHERE   username = $1; | ||||||
|  | 
 | ||||||
|  |         -- Restore caller's search_path | ||||||
|  |         PERFORM pg_catalog.set_config('search_path', old_path, true); | ||||||
|  | 
 | ||||||
|  |         RETURN passed; | ||||||
|  | END; | ||||||
|  | $$ LANGUAGE plpgsql SECURITY DEFINER; | ||||||
|  | </programlisting> | ||||||
|  | 
 | ||||||
|  |  </refsect1> | ||||||
|  | 
 | ||||||
|   |   | ||||||
|  <refsect1 id="sql-createfunction-compat"> |  <refsect1 id="sql-createfunction-compat"> | ||||||
|   <title>Compatibility</title> |   <title>Compatibility</title> | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <!-- $PostgreSQL: pgsql/doc/src/sgml/release.sgml,v 1.500 2007/04/19 13:02:49 momjian Exp $ --> | <!-- $PostgreSQL: pgsql/doc/src/sgml/release.sgml,v 1.501 2007/04/20 02:37:37 tgl Exp $ --> | ||||||
| <!-- | <!-- | ||||||
| 
 | 
 | ||||||
| Typical markup: | Typical markup: | ||||||
| @ -44,7 +44,8 @@ do it for earlier branch release files. | |||||||
|    </note> |    </note> | ||||||
| 
 | 
 | ||||||
|    <para> |    <para> | ||||||
|     This release contains fixes from 8.2.3. |     This release contains a variety of fixes from 8.2.3, | ||||||
|  |     including a security fix. | ||||||
|    </para> |    </para> | ||||||
| 
 | 
 | ||||||
|    <sect2> |    <sect2> | ||||||
| @ -63,8 +64,24 @@ do it for earlier branch release files. | |||||||
| 
 | 
 | ||||||
|      <listitem> |      <listitem> | ||||||
|      <para> |      <para> | ||||||
|       Fix <varname>shared_preload_libraries</> for Win32 by forcing reload in each backend |       Support explicit placement of the temporary-table schema within | ||||||
|       (Korry Douglas) |       <varname>search_path</>, and disable searching it for functions | ||||||
|  |       and operators (Tom) | ||||||
|  |      </para> | ||||||
|  |      <para> | ||||||
|  |       This is needed to allow a security-definer function to set a | ||||||
|  |       truly secure value of <varname>search_path</>.  Without it, | ||||||
|  |       an unprivileged SQL user can use temporary objects to execute code | ||||||
|  |       with the privileges of the security-definer function (CVE-2007-2138). | ||||||
|  |       See <xref linkend="sql-createfunction" | ||||||
|  |       endterm="sql-createfunction-title"> for more information. | ||||||
|  |      </para> | ||||||
|  |      </listitem> | ||||||
|  | 
 | ||||||
|  |      <listitem> | ||||||
|  |      <para> | ||||||
|  |       Fix <varname>shared_preload_libraries</> for Windows | ||||||
|  |       by forcing reload in each backend (Korry Douglas) | ||||||
|      </para> |      </para> | ||||||
|      </listitem> |      </listitem> | ||||||
| 
 | 
 | ||||||
| @ -77,20 +94,21 @@ do it for earlier branch release files. | |||||||
| 
 | 
 | ||||||
|      <listitem> |      <listitem> | ||||||
|      <para> |      <para> | ||||||
|       <filename>/contrib/tsearch2</> fixes (Teodor) |       <filename>/contrib/tsearch2</> crash fixes (Teodor) | ||||||
|      </para> |      </para> | ||||||
|      </listitem> |      </listitem> | ||||||
| 
 | 
 | ||||||
|      <listitem> |      <listitem> | ||||||
|      <para> |      <para> | ||||||
|       Require <command>COMMIT TRANSACTION</> to be executed in the same database as |       Require <command>COMMIT PREPARED</> to be executed in the same | ||||||
|       it was prepared (Heikki) |       database as the transaction was prepared in (Heikki) | ||||||
|      </para> |      </para> | ||||||
|      </listitem> |      </listitem> | ||||||
| 
 | 
 | ||||||
|      <listitem> |      <listitem> | ||||||
|      <para> |      <para> | ||||||
|       Allow Win32 <command>pg_dump</> to do binary backups larger than two gigabytes (Magnus) |       Allow <command>pg_dump</> to do binary backups larger than two gigabytes | ||||||
|  |       on Windows (Magnus) | ||||||
|      </para> |      </para> | ||||||
|      </listitem> |      </listitem> | ||||||
| 
 | 
 | ||||||
| @ -108,13 +126,8 @@ do it for earlier branch release files. | |||||||
| 
 | 
 | ||||||
|      <listitem> |      <listitem> | ||||||
|      <para> |      <para> | ||||||
|       Improve detection of <acronym>POSIX</>-style time zone names (Tom) |       Fix potential-data-corruption bug in how <command>VACUUM FULL</> handles | ||||||
|      </para> |       <command>UPDATE</> chains (Tom, Pavan Deolasee) | ||||||
|      </listitem> |  | ||||||
| 
 |  | ||||||
|      <listitem> |  | ||||||
|      <para> |  | ||||||
|       Fix bug in how <command>VACUUM FULL</> handles <command>UPDATE</> chains (Tom, Pavan Deolasee) |  | ||||||
|      </para> |      </para> | ||||||
|      </listitem> |      </listitem> | ||||||
| 
 | 
 | ||||||
| @ -126,14 +139,36 @@ do it for earlier branch release files. | |||||||
| 
 | 
 | ||||||
|      <listitem> |      <listitem> | ||||||
|      <para> |      <para> | ||||||
|       Fix <command>pg_dump</> so it can dump a sequence using <option>-t</> when not also dumping the owning table |       Fix <command>pg_dump</> so it can dump a serial column's sequence | ||||||
|  |       using <option>-t</> when not also dumping the owning table | ||||||
|       (Tom) |       (Tom) | ||||||
|      </para> |      </para> | ||||||
|      </listitem> |      </listitem> | ||||||
| 
 | 
 | ||||||
|      <listitem> |      <listitem> | ||||||
|      <para> |      <para> | ||||||
|       Improve outer join and bitmap join selection logic (Tom) |       Planner fixes, including improving outer join and bitmap scan | ||||||
|  |       selection logic (Tom) | ||||||
|  |      </para> | ||||||
|  |      </listitem> | ||||||
|  | 
 | ||||||
|  |      <listitem> | ||||||
|  |      <para> | ||||||
|  |       Fix possible wrong answers or crash when a PL/pgSQL function tries | ||||||
|  |       to <literal>RETURN</> from within an <literal>EXCEPTION</> block | ||||||
|  |       (Tom) | ||||||
|  |      </para> | ||||||
|  |      </listitem> | ||||||
|  | 
 | ||||||
|  |      <listitem> | ||||||
|  |      <para> | ||||||
|  |       Fix PANIC during enlargement of a hash index (Tom) | ||||||
|  |      </para> | ||||||
|  |      </listitem> | ||||||
|  | 
 | ||||||
|  |      <listitem> | ||||||
|  |      <para> | ||||||
|  |       Fix POSIX-style timezone specs to follow new USA DST rules (Tom) | ||||||
|      </para> |      </para> | ||||||
|      </listitem> |      </listitem> | ||||||
| 
 | 
 | ||||||
| @ -3040,7 +3075,8 @@ do it for earlier branch release files. | |||||||
|    </note> |    </note> | ||||||
| 
 | 
 | ||||||
|    <para> |    <para> | ||||||
|     This release contains fixes from 8.1.8. |     This release contains a variety of fixes from 8.1.8, | ||||||
|  |     including a security fix. | ||||||
|    </para> |    </para> | ||||||
| 
 | 
 | ||||||
|    <sect2> |    <sect2> | ||||||
| @ -3061,39 +3097,57 @@ do it for earlier branch release files. | |||||||
| 
 | 
 | ||||||
|      <listitem> |      <listitem> | ||||||
|      <para> |      <para> | ||||||
|       Fix <function>to_char()</> so it properly upper/lower cases localized day or month |       Support explicit placement of the temporary-table schema within | ||||||
|       names (Pavel Stehule) |       <varname>search_path</>, and disable searching it for functions | ||||||
|  |       and operators (Tom) | ||||||
|  |      </para> | ||||||
|  |      <para> | ||||||
|  |       This is needed to allow a security-definer function to set a | ||||||
|  |       truly secure value of <varname>search_path</>.  Without it, | ||||||
|  |       an unprivileged SQL user can use temporary objects to execute code | ||||||
|  |       with the privileges of the security-definer function (CVE-2007-2138). | ||||||
|  |       See <xref linkend="sql-createfunction" | ||||||
|  |       endterm="sql-createfunction-title"> for more information. | ||||||
|      </para> |      </para> | ||||||
|      </listitem> |      </listitem> | ||||||
| 
 | 
 | ||||||
|      <listitem> |      <listitem> | ||||||
|      <para> |      <para> | ||||||
|       <filename>/contrib/tsearch2</> fixes (Teodor) |       <filename>/contrib/tsearch2</> crash fixes (Teodor) | ||||||
|      </para> |      </para> | ||||||
|      </listitem> |      </listitem> | ||||||
| 
 | 
 | ||||||
|      <listitem> |      <listitem> | ||||||
|      <para> |      <para> | ||||||
|       Require <command>COMMIT TRANSACTION</> to be executed in the same database as |       Require <command>COMMIT PREPARED</> to be executed in the same | ||||||
|       it was prepared (Heikki) |       database as the transaction was prepared in (Heikki) | ||||||
|      </para> |      </para> | ||||||
|      </listitem> |      </listitem> | ||||||
| 
 | 
 | ||||||
|      <listitem> |      <listitem> | ||||||
|      <para> |      <para> | ||||||
|       Improve detection of <acronym>POSIX</>-style time zone names (Tom) |       Fix potential-data-corruption bug in how <command>VACUUM FULL</> handles | ||||||
|  |       <command>UPDATE</> chains (Tom, Pavan Deolasee) | ||||||
|      </para> |      </para> | ||||||
|      </listitem> |      </listitem> | ||||||
| 
 | 
 | ||||||
|      <listitem> |      <listitem> | ||||||
|      <para> |      <para> | ||||||
|       Fix bug in how <command>VACUUM FULL</> handles <command>UPDATE</> chains (Tom, Pavan Deolasee) |       Planner fixes, including improving outer join and bitmap scan | ||||||
|  |       selection logic (Tom) | ||||||
|      </para> |      </para> | ||||||
|      </listitem> |      </listitem> | ||||||
| 
 | 
 | ||||||
|      <listitem> |      <listitem> | ||||||
|      <para> |      <para> | ||||||
|       Improve outer join and bitmap join selection logic (Tom) |       Fix PANIC during enlargement of a hash index (bug introduced in 8.1.6) | ||||||
|  |       (Tom) | ||||||
|  |      </para> | ||||||
|  |      </listitem> | ||||||
|  | 
 | ||||||
|  |      <listitem> | ||||||
|  |      <para> | ||||||
|  |       Fix POSIX-style timezone specs to follow new USA DST rules (Tom) | ||||||
|      </para> |      </para> | ||||||
|      </listitem> |      </listitem> | ||||||
| 
 | 
 | ||||||
| @ -6061,7 +6115,8 @@ psql -t -f fixseq.sql db1 | psql -e db1 | |||||||
|    </note> |    </note> | ||||||
| 
 | 
 | ||||||
|    <para> |    <para> | ||||||
|     This release contains fixes from 8.0.12. |     This release contains a variety of fixes from 8.0.12, | ||||||
|  |     including a security fix. | ||||||
|    </para> |    </para> | ||||||
| 
 | 
 | ||||||
|    <sect2> |    <sect2> | ||||||
| @ -6082,25 +6137,43 @@ psql -t -f fixseq.sql db1 | psql -e db1 | |||||||
| 
 | 
 | ||||||
|      <listitem> |      <listitem> | ||||||
|      <para> |      <para> | ||||||
|       <filename>/contrib/tsearch2</> fixes (Teodor) |       Support explicit placement of the temporary-table schema within | ||||||
|  |       <varname>search_path</>, and disable searching it for functions | ||||||
|  |       and operators (Tom) | ||||||
|  |      </para> | ||||||
|  |      <para> | ||||||
|  |       This is needed to allow a security-definer function to set a | ||||||
|  |       truly secure value of <varname>search_path</>.  Without it, | ||||||
|  |       an unprivileged SQL user can use temporary objects to execute code | ||||||
|  |       with the privileges of the security-definer function (CVE-2007-2138). | ||||||
|  |       See <xref linkend="sql-createfunction" | ||||||
|  |       endterm="sql-createfunction-title"> for more information. | ||||||
|      </para> |      </para> | ||||||
|      </listitem> |      </listitem> | ||||||
| 
 | 
 | ||||||
|      <listitem> |      <listitem> | ||||||
|      <para> |      <para> | ||||||
|       Improve detection of <acronym>POSIX</>-style time zone names (Tom) |       <filename>/contrib/tsearch2</> crash fixes (Teodor) | ||||||
|      </para> |      </para> | ||||||
|      </listitem> |      </listitem> | ||||||
| 
 | 
 | ||||||
|      <listitem> |      <listitem> | ||||||
|      <para> |      <para> | ||||||
|       Fix bug in how <command>VACUUM FULL</> handles <command>UPDATE</> chains (Tom, Pavan Deolasee) |       Fix potential-data-corruption bug in how <command>VACUUM FULL</> handles | ||||||
|  |       <command>UPDATE</> chains (Tom, Pavan Deolasee) | ||||||
|      </para> |      </para> | ||||||
|      </listitem> |      </listitem> | ||||||
| 
 | 
 | ||||||
|      <listitem> |      <listitem> | ||||||
|      <para> |      <para> | ||||||
|       <filename>/contrib/tsearch2</> fixes (Teodor) |       Fix PANIC during enlargement of a hash index (bug introduced in 8.0.10) | ||||||
|  |       (Tom) | ||||||
|  |      </para> | ||||||
|  |      </listitem> | ||||||
|  | 
 | ||||||
|  |      <listitem> | ||||||
|  |      <para> | ||||||
|  |       Fix POSIX-style timezone specs to follow new USA DST rules (Tom) | ||||||
|      </para> |      </para> | ||||||
|      </listitem> |      </listitem> | ||||||
| 
 | 
 | ||||||
| @ -9552,7 +9625,8 @@ typedefs (Michael)</para></listitem> | |||||||
|    </note> |    </note> | ||||||
| 
 | 
 | ||||||
|    <para> |    <para> | ||||||
|     This release contains a variety of fixes from 7.4.16. |     This release contains fixes from 7.4.16, | ||||||
|  |     including a security fix. | ||||||
|    </para> |    </para> | ||||||
| 
 | 
 | ||||||
|    <sect2> |    <sect2> | ||||||
| @ -9573,13 +9647,37 @@ typedefs (Michael)</para></listitem> | |||||||
| 
 | 
 | ||||||
|      <listitem> |      <listitem> | ||||||
|      <para> |      <para> | ||||||
|       <filename>/contrib/tsearch2</> fixes (Teodor) |       Support explicit placement of the temporary-table schema within | ||||||
|  |       <varname>search_path</>, and disable searching it for functions | ||||||
|  |       and operators (Tom) | ||||||
|  |      </para> | ||||||
|  |      <para> | ||||||
|  |       This is needed to allow a security-definer function to set a | ||||||
|  |       truly secure value of <varname>search_path</>.  Without it, | ||||||
|  |       an unprivileged SQL user can use temporary objects to execute code | ||||||
|  |       with the privileges of the security-definer function (CVE-2007-2138). | ||||||
|  |       See <xref linkend="sql-createfunction" | ||||||
|  |       endterm="sql-createfunction-title"> for more information. | ||||||
|      </para> |      </para> | ||||||
|      </listitem> |      </listitem> | ||||||
| 
 | 
 | ||||||
|      <listitem> |      <listitem> | ||||||
|      <para> |      <para> | ||||||
|       Fix bug in how <command>VACUUM FULL</> handles <command>UPDATE</> chains (Tom, Pavan Deolasee) |       <filename>/contrib/tsearch2</> crash fixes (Teodor) | ||||||
|  |      </para> | ||||||
|  |      </listitem> | ||||||
|  | 
 | ||||||
|  |      <listitem> | ||||||
|  |      <para> | ||||||
|  |       Fix potential-data-corruption bug in how <command>VACUUM FULL</> handles | ||||||
|  |       <command>UPDATE</> chains (Tom, Pavan Deolasee) | ||||||
|  |      </para> | ||||||
|  |      </listitem> | ||||||
|  | 
 | ||||||
|  |      <listitem> | ||||||
|  |      <para> | ||||||
|  |       Fix PANIC during enlargement of a hash index (bug introduced in 7.4.15) | ||||||
|  |       (Tom) | ||||||
|      </para> |      </para> | ||||||
|      </listitem> |      </listitem> | ||||||
| 
 | 
 | ||||||
| @ -12714,7 +12812,8 @@ DROP SCHEMA information_schema CASCADE; | |||||||
|    </note> |    </note> | ||||||
| 
 | 
 | ||||||
|    <para> |    <para> | ||||||
|     This release contains a variety of fixes from 7.3.18. |     This release contains fixes from 7.3.18, | ||||||
|  |     including a security fix. | ||||||
|    </para> |    </para> | ||||||
| 
 | 
 | ||||||
|    <sect2> |    <sect2> | ||||||
| @ -12735,7 +12834,24 @@ DROP SCHEMA information_schema CASCADE; | |||||||
| 
 | 
 | ||||||
|      <listitem> |      <listitem> | ||||||
|      <para> |      <para> | ||||||
|       Fix bug in how <command>VACUUM FULL</> handles <command>UPDATE</> chains (Tom, Pavan Deolasee) |       Support explicit placement of the temporary-table schema within | ||||||
|  |       <varname>search_path</>, and disable searching it for functions | ||||||
|  |       and operators (Tom) | ||||||
|  |      </para> | ||||||
|  |      <para> | ||||||
|  |       This is needed to allow a security-definer function to set a | ||||||
|  |       truly secure value of <varname>search_path</>.  Without it, | ||||||
|  |       an unprivileged SQL user can use temporary objects to execute code | ||||||
|  |       with the privileges of the security-definer function (CVE-2007-2138). | ||||||
|  |       See <xref linkend="sql-createfunction" | ||||||
|  |       endterm="sql-createfunction-title"> for more information. | ||||||
|  |      </para> | ||||||
|  |      </listitem> | ||||||
|  | 
 | ||||||
|  |      <listitem> | ||||||
|  |      <para> | ||||||
|  |       Fix potential-data-corruption bug in how <command>VACUUM FULL</> handles | ||||||
|  |       <command>UPDATE</> chains (Tom, Pavan Deolasee) | ||||||
|      </para> |      </para> | ||||||
|      </listitem> |      </listitem> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ | |||||||
|  * |  * | ||||||
|  * |  * | ||||||
|  * IDENTIFICATION |  * IDENTIFICATION | ||||||
|  *	  $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.138 2007/03/26 16:58:38 tgl Exp $ |  *	  $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.139 2007/04/20 02:37:37 tgl Exp $ | ||||||
|  * |  * | ||||||
|  * NOTES |  * NOTES | ||||||
|  *	  See acl.h. |  *	  See acl.h. | ||||||
| @ -1833,7 +1833,7 @@ pg_namespace_aclmask(Oid nsp_oid, Oid roleid, | |||||||
| 	 */ | 	 */ | ||||||
| 	if (isTempNamespace(nsp_oid)) | 	if (isTempNamespace(nsp_oid)) | ||||||
| 	{ | 	{ | ||||||
| 		if (pg_database_aclcheck(MyDatabaseId, GetUserId(), | 		if (pg_database_aclcheck(MyDatabaseId, roleid, | ||||||
| 								 ACL_CREATE_TEMP) == ACLCHECK_OK) | 								 ACL_CREATE_TEMP) == ACLCHECK_OK) | ||||||
| 			return mask & ACL_ALL_RIGHTS_NAMESPACE; | 			return mask & ACL_ALL_RIGHTS_NAMESPACE; | ||||||
| 		else | 		else | ||||||
|  | |||||||
| @ -13,7 +13,7 @@ | |||||||
|  * Portions Copyright (c) 1994, Regents of the University of California |  * Portions Copyright (c) 1994, Regents of the University of California | ||||||
|  * |  * | ||||||
|  * IDENTIFICATION |  * IDENTIFICATION | ||||||
|  *	  $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.95 2007/04/12 22:34:45 neilc Exp $ |  *	  $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.96 2007/04/20 02:37:37 tgl Exp $ | ||||||
|  * |  * | ||||||
|  *------------------------------------------------------------------------- |  *------------------------------------------------------------------------- | ||||||
|  */ |  */ | ||||||
| @ -62,12 +62,30 @@ | |||||||
|  * SQL99.  Also, this provides a way to search the system namespace first |  * SQL99.  Also, this provides a way to search the system namespace first | ||||||
|  * without thereby making it the default creation target namespace.) |  * without thereby making it the default creation target namespace.) | ||||||
|  * |  * | ||||||
|  |  * For security reasons, searches using the search path will ignore the temp | ||||||
|  |  * namespace when searching for any object type other than relations and | ||||||
|  |  * types.  (We must allow types since temp tables have rowtypes.) | ||||||
|  |  * | ||||||
|  * The default creation target namespace is always the first element of the |  * The default creation target namespace is always the first element of the | ||||||
|  * explicit list.  If the explicit list is empty, there is no default target. |  * explicit list.  If the explicit list is empty, there is no default target. | ||||||
|  * |  * | ||||||
|  * In bootstrap mode, the search path is set equal to 'pg_catalog', so that |  * The textual specification of search_path can include "$user" to refer to | ||||||
|  |  * the namespace named the same as the current user, if any.  (This is just | ||||||
|  |  * ignored if there is no such namespace.)  Also, it can include "pg_temp" | ||||||
|  |  * to refer to the current backend's temp namespace.  This is usually also | ||||||
|  |  * ignorable if the temp namespace hasn't been set up, but there's a special | ||||||
|  |  * case: if "pg_temp" appears first then it should be the default creation | ||||||
|  |  * target.  We kluge this case a little bit so that the temp namespace isn't | ||||||
|  |  * set up until the first attempt to create something in it.  (The reason for | ||||||
|  |  * klugery is that we can't create the temp namespace outside a transaction, | ||||||
|  |  * but initial GUC processing of search_path happens outside a transaction.) | ||||||
|  |  * activeTempCreationPending is TRUE if "pg_temp" appears first in the string | ||||||
|  |  * but is not reflected in activeCreationNamespace because the namespace isn't | ||||||
|  |  * set up yet. | ||||||
|  |  * | ||||||
|  |  * In bootstrap mode, the search path is set equal to "pg_catalog", so that | ||||||
|  * the system namespace is the only one searched or inserted into. |  * the system namespace is the only one searched or inserted into. | ||||||
|  * initdb is also careful to set search_path to 'pg_catalog' for its |  * initdb is also careful to set search_path to "pg_catalog" for its | ||||||
|  * post-bootstrap standalone backend runs.	Otherwise the default search |  * post-bootstrap standalone backend runs.	Otherwise the default search | ||||||
|  * path is determined by GUC.  The factory default path contains the PUBLIC |  * path is determined by GUC.  The factory default path contains the PUBLIC | ||||||
|  * namespace (if it exists), preceded by the user's personal namespace |  * namespace (if it exists), preceded by the user's personal namespace | ||||||
| @ -102,15 +120,20 @@ static List *activeSearchPath = NIL; | |||||||
| /* default place to create stuff; if InvalidOid, no default */ | /* default place to create stuff; if InvalidOid, no default */ | ||||||
| static Oid	activeCreationNamespace = InvalidOid; | static Oid	activeCreationNamespace = InvalidOid; | ||||||
| 
 | 
 | ||||||
|  | /* if TRUE, activeCreationNamespace is wrong, it should be temp namespace */ | ||||||
|  | static bool activeTempCreationPending = false; | ||||||
|  | 
 | ||||||
| /* These variables are the values last derived from namespace_search_path: */ | /* These variables are the values last derived from namespace_search_path: */ | ||||||
| 
 | 
 | ||||||
| static List *baseSearchPath = NIL; | static List *baseSearchPath = NIL; | ||||||
| 
 | 
 | ||||||
| static Oid	baseCreationNamespace = InvalidOid; | static Oid	baseCreationNamespace = InvalidOid; | ||||||
| 
 | 
 | ||||||
|  | static bool baseTempCreationPending = false; | ||||||
|  | 
 | ||||||
| static Oid	namespaceUser = InvalidOid; | static Oid	namespaceUser = InvalidOid; | ||||||
| 
 | 
 | ||||||
| /* The above three values are valid only if baseSearchPathValid */ | /* The above four values are valid only if baseSearchPathValid */ | ||||||
| static bool baseSearchPathValid = true; | static bool baseSearchPathValid = true; | ||||||
| 
 | 
 | ||||||
| /* Override requests are remembered in a stack of OverrideStackEntry structs */ | /* Override requests are remembered in a stack of OverrideStackEntry structs */ | ||||||
| @ -262,6 +285,14 @@ RangeVarGetCreationNamespace(const RangeVar *newRelation) | |||||||
| 
 | 
 | ||||||
| 	if (newRelation->schemaname) | 	if (newRelation->schemaname) | ||||||
| 	{ | 	{ | ||||||
|  | 		/* check for pg_temp alias */ | ||||||
|  | 		if (strcmp(newRelation->schemaname, "pg_temp") == 0) | ||||||
|  | 		{ | ||||||
|  | 			/* Initialize temp namespace if first time through */ | ||||||
|  | 			if (!OidIsValid(myTempNamespace)) | ||||||
|  | 				InitTempTableNamespace(); | ||||||
|  | 			return myTempNamespace; | ||||||
|  | 		} | ||||||
| 		/* use exact schema given */ | 		/* use exact schema given */ | ||||||
| 		namespaceId = GetSysCacheOid(NAMESPACENAME, | 		namespaceId = GetSysCacheOid(NAMESPACENAME, | ||||||
| 									 CStringGetDatum(newRelation->schemaname), | 									 CStringGetDatum(newRelation->schemaname), | ||||||
| @ -277,6 +308,12 @@ RangeVarGetCreationNamespace(const RangeVar *newRelation) | |||||||
| 	{ | 	{ | ||||||
| 		/* use the default creation namespace */ | 		/* use the default creation namespace */ | ||||||
| 		recomputeNamespacePath(); | 		recomputeNamespacePath(); | ||||||
|  | 		if (activeTempCreationPending) | ||||||
|  | 		{ | ||||||
|  | 			/* Need to initialize temp namespace */ | ||||||
|  | 			InitTempTableNamespace(); | ||||||
|  | 			return myTempNamespace; | ||||||
|  | 		} | ||||||
| 		namespaceId = activeCreationNamespace; | 		namespaceId = activeCreationNamespace; | ||||||
| 		if (!OidIsValid(namespaceId)) | 		if (!OidIsValid(namespaceId)) | ||||||
| 			ereport(ERROR, | 			ereport(ERROR, | ||||||
| @ -549,12 +586,16 @@ FuncnameGetCandidates(List *names, int nargs) | |||||||
| 		} | 		} | ||||||
| 		else | 		else | ||||||
| 		{ | 		{ | ||||||
| 			/* Consider only procs that are in the search path */ | 			/*
 | ||||||
|  | 			 * Consider only procs that are in the search path and are not | ||||||
|  | 			 * in the temp namespace. | ||||||
|  | 			 */ | ||||||
| 			ListCell   *nsp; | 			ListCell   *nsp; | ||||||
| 
 | 
 | ||||||
| 			foreach(nsp, activeSearchPath) | 			foreach(nsp, activeSearchPath) | ||||||
| 			{ | 			{ | ||||||
| 				if (procform->pronamespace == lfirst_oid(nsp)) | 				if (procform->pronamespace == lfirst_oid(nsp) && | ||||||
|  | 					procform->pronamespace != myTempNamespace) | ||||||
| 					break; | 					break; | ||||||
| 				pathpos++; | 				pathpos++; | ||||||
| 			} | 			} | ||||||
| @ -770,6 +811,9 @@ OpernameGetOprid(List *names, Oid oprleft, Oid oprright) | |||||||
| 		Oid			namespaceId = lfirst_oid(l); | 		Oid			namespaceId = lfirst_oid(l); | ||||||
| 		int			i; | 		int			i; | ||||||
| 
 | 
 | ||||||
|  | 		if (namespaceId == myTempNamespace) | ||||||
|  | 			continue;			/* do not look in temp namespace */ | ||||||
|  | 
 | ||||||
| 		for (i = 0; i < catlist->n_members; i++) | 		for (i = 0; i < catlist->n_members; i++) | ||||||
| 		{ | 		{ | ||||||
| 			HeapTuple	opertup = &catlist->members[i]->tuple; | 			HeapTuple	opertup = &catlist->members[i]->tuple; | ||||||
| @ -872,12 +916,16 @@ OpernameGetCandidates(List *names, char oprkind) | |||||||
| 		} | 		} | ||||||
| 		else | 		else | ||||||
| 		{ | 		{ | ||||||
| 			/* Consider only opers that are in the search path */ | 			/*
 | ||||||
|  | 			 * Consider only opers that are in the search path and are not | ||||||
|  | 			 * in the temp namespace. | ||||||
|  | 			 */ | ||||||
| 			ListCell   *nsp; | 			ListCell   *nsp; | ||||||
| 
 | 
 | ||||||
| 			foreach(nsp, activeSearchPath) | 			foreach(nsp, activeSearchPath) | ||||||
| 			{ | 			{ | ||||||
| 				if (operform->oprnamespace == lfirst_oid(nsp)) | 				if (operform->oprnamespace == lfirst_oid(nsp) && | ||||||
|  | 					operform->oprnamespace != myTempNamespace) | ||||||
| 					break; | 					break; | ||||||
| 				pathpos++; | 				pathpos++; | ||||||
| 			} | 			} | ||||||
| @ -1025,6 +1073,9 @@ OpclassnameGetOpcid(Oid amid, const char *opcname) | |||||||
| 	{ | 	{ | ||||||
| 		Oid			namespaceId = lfirst_oid(l); | 		Oid			namespaceId = lfirst_oid(l); | ||||||
| 
 | 
 | ||||||
|  | 		if (namespaceId == myTempNamespace) | ||||||
|  | 			continue;			/* do not look in temp namespace */ | ||||||
|  | 
 | ||||||
| 		opcid = GetSysCacheOid(CLAAMNAMENSP, | 		opcid = GetSysCacheOid(CLAAMNAMENSP, | ||||||
| 							   ObjectIdGetDatum(amid), | 							   ObjectIdGetDatum(amid), | ||||||
| 							   PointerGetDatum(opcname), | 							   PointerGetDatum(opcname), | ||||||
| @ -1108,6 +1159,9 @@ OpfamilynameGetOpfid(Oid amid, const char *opfname) | |||||||
| 	{ | 	{ | ||||||
| 		Oid			namespaceId = lfirst_oid(l); | 		Oid			namespaceId = lfirst_oid(l); | ||||||
| 
 | 
 | ||||||
|  | 		if (namespaceId == myTempNamespace) | ||||||
|  | 			continue;			/* do not look in temp namespace */ | ||||||
|  | 
 | ||||||
| 		opfid = GetSysCacheOid(OPFAMILYAMNAMENSP, | 		opfid = GetSysCacheOid(OPFAMILYAMNAMENSP, | ||||||
| 							   ObjectIdGetDatum(amid), | 							   ObjectIdGetDatum(amid), | ||||||
| 							   PointerGetDatum(opfname), | 							   PointerGetDatum(opfname), | ||||||
| @ -1190,6 +1244,9 @@ ConversionGetConid(const char *conname) | |||||||
| 	{ | 	{ | ||||||
| 		Oid			namespaceId = lfirst_oid(l); | 		Oid			namespaceId = lfirst_oid(l); | ||||||
| 
 | 
 | ||||||
|  | 		if (namespaceId == myTempNamespace) | ||||||
|  | 			continue;			/* do not look in temp namespace */ | ||||||
|  | 
 | ||||||
| 		conid = GetSysCacheOid(CONNAMENSP, | 		conid = GetSysCacheOid(CONNAMENSP, | ||||||
| 							   PointerGetDatum(conname), | 							   PointerGetDatum(conname), | ||||||
| 							   ObjectIdGetDatum(namespaceId), | 							   ObjectIdGetDatum(namespaceId), | ||||||
| @ -1316,6 +1373,19 @@ LookupExplicitNamespace(const char *nspname) | |||||||
| 	Oid			namespaceId; | 	Oid			namespaceId; | ||||||
| 	AclResult	aclresult; | 	AclResult	aclresult; | ||||||
| 
 | 
 | ||||||
|  | 	/* check for pg_temp alias */ | ||||||
|  | 	if (strcmp(nspname, "pg_temp") == 0) | ||||||
|  | 	{ | ||||||
|  | 		if (OidIsValid(myTempNamespace)) | ||||||
|  | 			return myTempNamespace; | ||||||
|  | 		/*
 | ||||||
|  | 		 * Since this is used only for looking up existing objects, there | ||||||
|  | 		 * is no point in trying to initialize the temp namespace here; | ||||||
|  | 		 * and doing so might create problems for some callers. | ||||||
|  | 		 * Just fall through and give the "does not exist" error. | ||||||
|  | 		 */ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	namespaceId = GetSysCacheOid(NAMESPACENAME, | 	namespaceId = GetSysCacheOid(NAMESPACENAME, | ||||||
| 								 CStringGetDatum(nspname), | 								 CStringGetDatum(nspname), | ||||||
| 								 0, 0, 0); | 								 0, 0, 0); | ||||||
| @ -1336,7 +1406,11 @@ LookupExplicitNamespace(const char *nspname) | |||||||
|  * LookupCreationNamespace |  * LookupCreationNamespace | ||||||
|  *		Look up the schema and verify we have CREATE rights on it. |  *		Look up the schema and verify we have CREATE rights on it. | ||||||
|  * |  * | ||||||
|  * This is just like LookupExplicitNamespace except for the permission check. |  * This is just like LookupExplicitNamespace except for the permission check, | ||||||
|  |  * and that we are willing to create pg_temp if needed. | ||||||
|  |  * | ||||||
|  |  * Note: calling this may result in a CommandCounterIncrement operation, | ||||||
|  |  * if we have to create or clean out the temp namespace. | ||||||
|  */ |  */ | ||||||
| Oid | Oid | ||||||
| LookupCreationNamespace(const char *nspname) | LookupCreationNamespace(const char *nspname) | ||||||
| @ -1344,6 +1418,15 @@ LookupCreationNamespace(const char *nspname) | |||||||
| 	Oid			namespaceId; | 	Oid			namespaceId; | ||||||
| 	AclResult	aclresult; | 	AclResult	aclresult; | ||||||
| 
 | 
 | ||||||
|  | 	/* check for pg_temp alias */ | ||||||
|  | 	if (strcmp(nspname, "pg_temp") == 0) | ||||||
|  | 	{ | ||||||
|  | 		/* Initialize temp namespace if first time through */ | ||||||
|  | 		if (!OidIsValid(myTempNamespace)) | ||||||
|  | 			InitTempTableNamespace(); | ||||||
|  | 		return myTempNamespace; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	namespaceId = GetSysCacheOid(NAMESPACENAME, | 	namespaceId = GetSysCacheOid(NAMESPACENAME, | ||||||
| 								 CStringGetDatum(nspname), | 								 CStringGetDatum(nspname), | ||||||
| 								 0, 0, 0); | 								 0, 0, 0); | ||||||
| @ -1369,21 +1452,28 @@ LookupCreationNamespace(const char *nspname) | |||||||
|  * Note: this does not apply any permissions check.  Callers must check |  * Note: this does not apply any permissions check.  Callers must check | ||||||
|  * for CREATE rights on the selected namespace when appropriate. |  * for CREATE rights on the selected namespace when appropriate. | ||||||
|  * |  * | ||||||
|  * This is *not* used for tables.  Hence, the TEMP table namespace is |  * Note: calling this may result in a CommandCounterIncrement operation, | ||||||
|  * never selected as the creation target. |  * if we have to create or clean out the temp namespace. | ||||||
|  */ |  */ | ||||||
| Oid | Oid | ||||||
| QualifiedNameGetCreationNamespace(List *names, char **objname_p) | QualifiedNameGetCreationNamespace(List *names, char **objname_p) | ||||||
| { | { | ||||||
| 	char	   *schemaname; | 	char	   *schemaname; | ||||||
| 	char	   *objname; |  | ||||||
| 	Oid			namespaceId; | 	Oid			namespaceId; | ||||||
| 
 | 
 | ||||||
| 	/* deconstruct the name list */ | 	/* deconstruct the name list */ | ||||||
| 	DeconstructQualifiedName(names, &schemaname, &objname); | 	DeconstructQualifiedName(names, &schemaname, objname_p); | ||||||
| 
 | 
 | ||||||
| 	if (schemaname) | 	if (schemaname) | ||||||
| 	{ | 	{ | ||||||
|  | 		/* check for pg_temp alias */ | ||||||
|  | 		if (strcmp(schemaname, "pg_temp") == 0) | ||||||
|  | 		{ | ||||||
|  | 			/* Initialize temp namespace if first time through */ | ||||||
|  | 			if (!OidIsValid(myTempNamespace)) | ||||||
|  | 				InitTempTableNamespace(); | ||||||
|  | 			return myTempNamespace; | ||||||
|  | 		} | ||||||
| 		/* use exact schema given */ | 		/* use exact schema given */ | ||||||
| 		namespaceId = GetSysCacheOid(NAMESPACENAME, | 		namespaceId = GetSysCacheOid(NAMESPACENAME, | ||||||
| 									 CStringGetDatum(schemaname), | 									 CStringGetDatum(schemaname), | ||||||
| @ -1398,6 +1488,12 @@ QualifiedNameGetCreationNamespace(List *names, char **objname_p) | |||||||
| 	{ | 	{ | ||||||
| 		/* use the default creation namespace */ | 		/* use the default creation namespace */ | ||||||
| 		recomputeNamespacePath(); | 		recomputeNamespacePath(); | ||||||
|  | 		if (activeTempCreationPending) | ||||||
|  | 		{ | ||||||
|  | 			/* Need to initialize temp namespace */ | ||||||
|  | 			InitTempTableNamespace(); | ||||||
|  | 			return myTempNamespace; | ||||||
|  | 		} | ||||||
| 		namespaceId = activeCreationNamespace; | 		namespaceId = activeCreationNamespace; | ||||||
| 		if (!OidIsValid(namespaceId)) | 		if (!OidIsValid(namespaceId)) | ||||||
| 			ereport(ERROR, | 			ereport(ERROR, | ||||||
| @ -1405,7 +1501,6 @@ QualifiedNameGetCreationNamespace(List *names, char **objname_p) | |||||||
| 					 errmsg("no schema has been selected to create in"))); | 					 errmsg("no schema has been selected to create in"))); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	*objname_p = objname; |  | ||||||
| 	return namespaceId; | 	return namespaceId; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -1634,6 +1729,7 @@ PushOverrideSearchPath(OverrideSearchPath *newpath) | |||||||
| 	/* And make it active. */ | 	/* And make it active. */ | ||||||
| 	activeSearchPath = entry->searchPath; | 	activeSearchPath = entry->searchPath; | ||||||
| 	activeCreationNamespace = entry->creationNamespace; | 	activeCreationNamespace = entry->creationNamespace; | ||||||
|  | 	activeTempCreationPending = false;			/* XXX is this OK? */ | ||||||
| 
 | 
 | ||||||
| 	MemoryContextSwitchTo(oldcxt); | 	MemoryContextSwitchTo(oldcxt); | ||||||
| } | } | ||||||
| @ -1667,12 +1763,14 @@ PopOverrideSearchPath(void) | |||||||
| 		entry = (OverrideStackEntry *) linitial(overrideStack); | 		entry = (OverrideStackEntry *) linitial(overrideStack); | ||||||
| 		activeSearchPath = entry->searchPath; | 		activeSearchPath = entry->searchPath; | ||||||
| 		activeCreationNamespace = entry->creationNamespace; | 		activeCreationNamespace = entry->creationNamespace; | ||||||
|  | 		activeTempCreationPending = false;			/* XXX is this OK? */ | ||||||
| 	} | 	} | ||||||
| 	else | 	else | ||||||
| 	{ | 	{ | ||||||
| 		/* If not baseSearchPathValid, this is useless but harmless */ | 		/* If not baseSearchPathValid, this is useless but harmless */ | ||||||
| 		activeSearchPath = baseSearchPath; | 		activeSearchPath = baseSearchPath; | ||||||
| 		activeCreationNamespace = baseCreationNamespace; | 		activeCreationNamespace = baseCreationNamespace; | ||||||
|  | 		activeTempCreationPending = baseTempCreationPending; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -1706,6 +1804,10 @@ FindConversionByName(List *name) | |||||||
| 		foreach(l, activeSearchPath) | 		foreach(l, activeSearchPath) | ||||||
| 		{ | 		{ | ||||||
| 			namespaceId = lfirst_oid(l); | 			namespaceId = lfirst_oid(l); | ||||||
|  | 
 | ||||||
|  | 			if (namespaceId == myTempNamespace) | ||||||
|  | 				continue;			/* do not look in temp namespace */ | ||||||
|  | 
 | ||||||
| 			conoid = FindConversion(conversion_name, namespaceId); | 			conoid = FindConversion(conversion_name, namespaceId); | ||||||
| 			if (OidIsValid(conoid)) | 			if (OidIsValid(conoid)) | ||||||
| 				return conoid; | 				return conoid; | ||||||
| @ -1731,6 +1833,9 @@ FindDefaultConversionProc(int4 for_encoding, int4 to_encoding) | |||||||
| 	{ | 	{ | ||||||
| 		Oid			namespaceId = lfirst_oid(l); | 		Oid			namespaceId = lfirst_oid(l); | ||||||
| 
 | 
 | ||||||
|  | 		if (namespaceId == myTempNamespace) | ||||||
|  | 			continue;			/* do not look in temp namespace */ | ||||||
|  | 
 | ||||||
| 		proc = FindDefaultConversion(namespaceId, for_encoding, to_encoding); | 		proc = FindDefaultConversion(namespaceId, for_encoding, to_encoding); | ||||||
| 		if (OidIsValid(proc)) | 		if (OidIsValid(proc)) | ||||||
| 			return proc; | 			return proc; | ||||||
| @ -1752,6 +1857,7 @@ recomputeNamespacePath(void) | |||||||
| 	List	   *oidlist; | 	List	   *oidlist; | ||||||
| 	List	   *newpath; | 	List	   *newpath; | ||||||
| 	ListCell   *l; | 	ListCell   *l; | ||||||
|  | 	bool		temp_missing; | ||||||
| 	Oid			firstNS; | 	Oid			firstNS; | ||||||
| 	MemoryContext oldcxt; | 	MemoryContext oldcxt; | ||||||
| 
 | 
 | ||||||
| @ -1781,6 +1887,7 @@ recomputeNamespacePath(void) | |||||||
| 	 * already been accepted.)	Don't make duplicate entries, either. | 	 * already been accepted.)	Don't make duplicate entries, either. | ||||||
| 	 */ | 	 */ | ||||||
| 	oidlist = NIL; | 	oidlist = NIL; | ||||||
|  | 	temp_missing = false; | ||||||
| 	foreach(l, namelist) | 	foreach(l, namelist) | ||||||
| 	{ | 	{ | ||||||
| 		char	   *curname = (char *) lfirst(l); | 		char	   *curname = (char *) lfirst(l); | ||||||
| @ -1810,6 +1917,21 @@ recomputeNamespacePath(void) | |||||||
| 					oidlist = lappend_oid(oidlist, namespaceId); | 					oidlist = lappend_oid(oidlist, namespaceId); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 		else if (strcmp(curname, "pg_temp") == 0) | ||||||
|  | 		{ | ||||||
|  | 			/* pg_temp --- substitute temp namespace, if any */ | ||||||
|  | 			if (OidIsValid(myTempNamespace)) | ||||||
|  | 			{ | ||||||
|  | 				if (!list_member_oid(oidlist, myTempNamespace)) | ||||||
|  | 					oidlist = lappend_oid(oidlist, myTempNamespace); | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 			{ | ||||||
|  | 				/* If it ought to be the creation namespace, set flag */ | ||||||
|  | 				if (oidlist == NIL) | ||||||
|  | 					temp_missing = true; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 		else | 		else | ||||||
| 		{ | 		{ | ||||||
| 			/* normal namespace reference */ | 			/* normal namespace reference */ | ||||||
| @ -1825,7 +1947,9 @@ recomputeNamespacePath(void) | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Remember the first member of the explicit list. | 	 * Remember the first member of the explicit list.  (Note: this is | ||||||
|  | 	 * nominally wrong if temp_missing, but we need it anyway to distinguish | ||||||
|  | 	 * explicit from implicit mention of pg_catalog.) | ||||||
| 	 */ | 	 */ | ||||||
| 	if (oidlist == NIL) | 	if (oidlist == NIL) | ||||||
| 		firstNS = InvalidOid; | 		firstNS = InvalidOid; | ||||||
| @ -1856,6 +1980,7 @@ recomputeNamespacePath(void) | |||||||
| 	list_free(baseSearchPath); | 	list_free(baseSearchPath); | ||||||
| 	baseSearchPath = newpath; | 	baseSearchPath = newpath; | ||||||
| 	baseCreationNamespace = firstNS; | 	baseCreationNamespace = firstNS; | ||||||
|  | 	baseTempCreationPending = temp_missing; | ||||||
| 
 | 
 | ||||||
| 	/* Mark the path valid. */ | 	/* Mark the path valid. */ | ||||||
| 	baseSearchPathValid = true; | 	baseSearchPathValid = true; | ||||||
| @ -1864,6 +1989,7 @@ recomputeNamespacePath(void) | |||||||
| 	/* And make it active. */ | 	/* And make it active. */ | ||||||
| 	activeSearchPath = baseSearchPath; | 	activeSearchPath = baseSearchPath; | ||||||
| 	activeCreationNamespace = baseCreationNamespace; | 	activeCreationNamespace = baseCreationNamespace; | ||||||
|  | 	activeTempCreationPending = baseTempCreationPending; | ||||||
| 
 | 
 | ||||||
| 	/* Clean up. */ | 	/* Clean up. */ | ||||||
| 	pfree(rawname); | 	pfree(rawname); | ||||||
| @ -1881,6 +2007,8 @@ InitTempTableNamespace(void) | |||||||
| 	char		namespaceName[NAMEDATALEN]; | 	char		namespaceName[NAMEDATALEN]; | ||||||
| 	Oid			namespaceId; | 	Oid			namespaceId; | ||||||
| 
 | 
 | ||||||
|  | 	Assert(!OidIsValid(myTempNamespace)); | ||||||
|  | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * First, do permission check to see if we are authorized to make temp | 	 * First, do permission check to see if we are authorized to make temp | ||||||
| 	 * tables.	We use a nonstandard error message here since "databasename: | 	 * tables.	We use a nonstandard error message here since "databasename: | ||||||
| @ -1940,16 +2068,6 @@ InitTempTableNamespace(void) | |||||||
| 	baseSearchPathValid = false;	/* need to rebuild list */ | 	baseSearchPathValid = false;	/* need to rebuild list */ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 |  | ||||||
|  * Remove all temp tables from the temporary namespace. |  | ||||||
|  */ |  | ||||||
| void |  | ||||||
| ResetTempTableNamespace(void) |  | ||||||
| { |  | ||||||
| 	if (OidIsValid(myTempNamespace)) |  | ||||||
| 		RemoveTempRelations(myTempNamespace); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /*
 | /*
 | ||||||
|  * End-of-transaction cleanup for namespaces. |  * End-of-transaction cleanup for namespaces. | ||||||
|  */ |  */ | ||||||
| @ -1995,6 +2113,7 @@ AtEOXact_Namespace(bool isCommit) | |||||||
| 		/* If not baseSearchPathValid, this is useless but harmless */ | 		/* If not baseSearchPathValid, this is useless but harmless */ | ||||||
| 		activeSearchPath = baseSearchPath; | 		activeSearchPath = baseSearchPath; | ||||||
| 		activeCreationNamespace = baseCreationNamespace; | 		activeCreationNamespace = baseCreationNamespace; | ||||||
|  | 		activeTempCreationPending = baseTempCreationPending; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -2046,12 +2165,14 @@ AtEOSubXact_Namespace(bool isCommit, SubTransactionId mySubid, | |||||||
| 		entry = (OverrideStackEntry *) linitial(overrideStack); | 		entry = (OverrideStackEntry *) linitial(overrideStack); | ||||||
| 		activeSearchPath = entry->searchPath; | 		activeSearchPath = entry->searchPath; | ||||||
| 		activeCreationNamespace = entry->creationNamespace; | 		activeCreationNamespace = entry->creationNamespace; | ||||||
|  | 		activeTempCreationPending = false;			/* XXX is this OK? */ | ||||||
| 	} | 	} | ||||||
| 	else | 	else | ||||||
| 	{ | 	{ | ||||||
| 		/* If not baseSearchPathValid, this is useless but harmless */ | 		/* If not baseSearchPathValid, this is useless but harmless */ | ||||||
| 		activeSearchPath = baseSearchPath; | 		activeSearchPath = baseSearchPath; | ||||||
| 		activeCreationNamespace = baseCreationNamespace; | 		activeCreationNamespace = baseCreationNamespace; | ||||||
|  | 		activeTempCreationPending = baseTempCreationPending; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -2099,6 +2220,16 @@ RemoveTempRelationsCallback(int code, Datum arg) | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /*
 | ||||||
|  |  * Remove all temp tables from the temporary namespace. | ||||||
|  |  */ | ||||||
|  | void | ||||||
|  | ResetTempTableNamespace(void) | ||||||
|  | { | ||||||
|  | 	if (OidIsValid(myTempNamespace)) | ||||||
|  | 		RemoveTempRelations(myTempNamespace); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Routines for handling the GUC variable 'search_path'. |  * Routines for handling the GUC variable 'search_path'. | ||||||
| @ -2132,8 +2263,9 @@ assign_search_path(const char *newval, bool doit, GucSource source) | |||||||
| 	{ | 	{ | ||||||
| 		/*
 | 		/*
 | ||||||
| 		 * Verify that all the names are either valid namespace names or | 		 * Verify that all the names are either valid namespace names or | ||||||
| 		 * "$user".  We do not require $user to correspond to a valid | 		 * "$user" or "pg_temp".  We do not require $user to correspond to a | ||||||
| 		 * namespace.  We do not check for USAGE rights, either; should we? | 		 * valid namespace, and pg_temp might not exist yet.  We do not check | ||||||
|  | 		 * for USAGE rights, either; should we? | ||||||
| 		 * | 		 * | ||||||
| 		 * When source == PGC_S_TEST, we are checking the argument of an ALTER | 		 * When source == PGC_S_TEST, we are checking the argument of an ALTER | ||||||
| 		 * DATABASE SET or ALTER USER SET command.	It could be that the | 		 * DATABASE SET or ALTER USER SET command.	It could be that the | ||||||
| @ -2147,6 +2279,8 @@ assign_search_path(const char *newval, bool doit, GucSource source) | |||||||
| 
 | 
 | ||||||
| 			if (strcmp(curname, "$user") == 0) | 			if (strcmp(curname, "$user") == 0) | ||||||
| 				continue; | 				continue; | ||||||
|  | 			if (strcmp(curname, "pg_temp") == 0) | ||||||
|  | 				continue; | ||||||
| 			if (!SearchSysCacheExists(NAMESPACENAME, | 			if (!SearchSysCacheExists(NAMESPACENAME, | ||||||
| 									  CStringGetDatum(curname), | 									  CStringGetDatum(curname), | ||||||
| 									  0, 0, 0)) | 									  0, 0, 0)) | ||||||
| @ -2190,10 +2324,12 @@ InitializeSearchPath(void) | |||||||
| 		baseSearchPath = list_make1_oid(PG_CATALOG_NAMESPACE); | 		baseSearchPath = list_make1_oid(PG_CATALOG_NAMESPACE); | ||||||
| 		MemoryContextSwitchTo(oldcxt); | 		MemoryContextSwitchTo(oldcxt); | ||||||
| 		baseCreationNamespace = PG_CATALOG_NAMESPACE; | 		baseCreationNamespace = PG_CATALOG_NAMESPACE; | ||||||
|  | 		baseTempCreationPending = false; | ||||||
| 		baseSearchPathValid = true; | 		baseSearchPathValid = true; | ||||||
| 		namespaceUser = GetUserId(); | 		namespaceUser = GetUserId(); | ||||||
| 		activeSearchPath = baseSearchPath; | 		activeSearchPath = baseSearchPath; | ||||||
| 		activeCreationNamespace = baseCreationNamespace; | 		activeCreationNamespace = baseCreationNamespace; | ||||||
|  | 		activeTempCreationPending = baseTempCreationPending; | ||||||
| 	} | 	} | ||||||
| 	else | 	else | ||||||
| 	{ | 	{ | ||||||
| @ -2227,6 +2363,9 @@ NamespaceCallback(Datum arg, Oid relid) | |||||||
|  * |  * | ||||||
|  * The returned list includes the implicitly-prepended namespaces only if |  * The returned list includes the implicitly-prepended namespaces only if | ||||||
|  * includeImplicit is true. |  * includeImplicit is true. | ||||||
|  |  * | ||||||
|  |  * Note: calling this may result in a CommandCounterIncrement operation, | ||||||
|  |  * if we have to create or clean out the temp namespace. | ||||||
|  */ |  */ | ||||||
| List * | List * | ||||||
| fetch_search_path(bool includeImplicit) | fetch_search_path(bool includeImplicit) | ||||||
| @ -2235,6 +2374,19 @@ fetch_search_path(bool includeImplicit) | |||||||
| 
 | 
 | ||||||
| 	recomputeNamespacePath(); | 	recomputeNamespacePath(); | ||||||
| 
 | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * If the temp namespace should be first, force it to exist.  This is | ||||||
|  | 	 * so that callers can trust the result to reflect the actual default | ||||||
|  | 	 * creation namespace.  It's a bit bogus to do this here, since | ||||||
|  | 	 * current_schema() is supposedly a stable function without side-effects, | ||||||
|  | 	 * but the alternatives seem worse. | ||||||
|  | 	 */ | ||||||
|  | 	if (activeTempCreationPending) | ||||||
|  | 	{ | ||||||
|  | 		InitTempTableNamespace(); | ||||||
|  | 		recomputeNamespacePath(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	result = list_copy(activeSearchPath); | 	result = list_copy(activeSearchPath); | ||||||
| 	if (!includeImplicit) | 	if (!includeImplicit) | ||||||
| 	{ | 	{ | ||||||
|  | |||||||
| @ -137,3 +137,61 @@ CREATE TEMP TABLE temptest4(col int REFERENCES temptest3); | |||||||
| COMMIT; | COMMIT; | ||||||
| ERROR:  unsupported ON COMMIT and foreign key combination | ERROR:  unsupported ON COMMIT and foreign key combination | ||||||
| DETAIL:  Table "temptest4" references "temptest3", but they do not have the same ON COMMIT setting. | DETAIL:  Table "temptest4" references "temptest3", but they do not have the same ON COMMIT setting. | ||||||
|  | -- Test manipulation of temp schema's placement in search path | ||||||
|  | create table public.whereami (f1 text); | ||||||
|  | insert into public.whereami values ('public'); | ||||||
|  | create temp table whereami (f1 text); | ||||||
|  | insert into whereami values ('temp'); | ||||||
|  | create function public.whoami() returns text | ||||||
|  |   as $$select 'public'::text$$ language sql; | ||||||
|  | create function pg_temp.whoami() returns text | ||||||
|  |   as $$select 'temp'::text$$ language sql; | ||||||
|  | -- default should have pg_temp implicitly first, but only for tables | ||||||
|  | select * from whereami; | ||||||
|  |   f1   | ||||||
|  | ------ | ||||||
|  |  temp | ||||||
|  | (1 row) | ||||||
|  | 
 | ||||||
|  | select whoami(); | ||||||
|  |  whoami  | ||||||
|  | -------- | ||||||
|  |  public | ||||||
|  | (1 row) | ||||||
|  | 
 | ||||||
|  | -- can list temp first explicitly, but it still doesn't affect functions | ||||||
|  | set search_path = pg_temp, public; | ||||||
|  | select * from whereami; | ||||||
|  |   f1   | ||||||
|  | ------ | ||||||
|  |  temp | ||||||
|  | (1 row) | ||||||
|  | 
 | ||||||
|  | select whoami(); | ||||||
|  |  whoami  | ||||||
|  | -------- | ||||||
|  |  public | ||||||
|  | (1 row) | ||||||
|  | 
 | ||||||
|  | -- or put it last for security | ||||||
|  | set search_path = public, pg_temp; | ||||||
|  | select * from whereami; | ||||||
|  |    f1    | ||||||
|  | -------- | ||||||
|  |  public | ||||||
|  | (1 row) | ||||||
|  | 
 | ||||||
|  | select whoami(); | ||||||
|  |  whoami  | ||||||
|  | -------- | ||||||
|  |  public | ||||||
|  | (1 row) | ||||||
|  | 
 | ||||||
|  | -- you can invoke a temp function explicitly, though | ||||||
|  | select pg_temp.whoami(); | ||||||
|  |  whoami  | ||||||
|  | -------- | ||||||
|  |  temp | ||||||
|  | (1 row) | ||||||
|  | 
 | ||||||
|  | drop table public.whereami; | ||||||
|  | |||||||
| @ -118,3 +118,36 @@ BEGIN; | |||||||
| CREATE TEMP TABLE temptest3(col int PRIMARY KEY) ON COMMIT DELETE ROWS; | CREATE TEMP TABLE temptest3(col int PRIMARY KEY) ON COMMIT DELETE ROWS; | ||||||
| CREATE TEMP TABLE temptest4(col int REFERENCES temptest3); | CREATE TEMP TABLE temptest4(col int REFERENCES temptest3); | ||||||
| COMMIT; | COMMIT; | ||||||
|  | 
 | ||||||
|  | -- Test manipulation of temp schema's placement in search path | ||||||
|  | 
 | ||||||
|  | create table public.whereami (f1 text); | ||||||
|  | insert into public.whereami values ('public'); | ||||||
|  | 
 | ||||||
|  | create temp table whereami (f1 text); | ||||||
|  | insert into whereami values ('temp'); | ||||||
|  | 
 | ||||||
|  | create function public.whoami() returns text | ||||||
|  |   as $$select 'public'::text$$ language sql; | ||||||
|  | 
 | ||||||
|  | create function pg_temp.whoami() returns text | ||||||
|  |   as $$select 'temp'::text$$ language sql; | ||||||
|  | 
 | ||||||
|  | -- default should have pg_temp implicitly first, but only for tables | ||||||
|  | select * from whereami; | ||||||
|  | select whoami(); | ||||||
|  | 
 | ||||||
|  | -- can list temp first explicitly, but it still doesn't affect functions | ||||||
|  | set search_path = pg_temp, public; | ||||||
|  | select * from whereami; | ||||||
|  | select whoami(); | ||||||
|  | 
 | ||||||
|  | -- or put it last for security | ||||||
|  | set search_path = public, pg_temp; | ||||||
|  | select * from whereami; | ||||||
|  | select whoami(); | ||||||
|  | 
 | ||||||
|  | -- you can invoke a temp function explicitly, though | ||||||
|  | select pg_temp.whoami(); | ||||||
|  | 
 | ||||||
|  | drop table public.whereami; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user