diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 9c118d2be250e0ff9db2d80d6ed14a0ec36b9c14..43b6931fe18f27ad2861ca02e9d4199e0fed661b 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.110 2002/03/24 04:31:06 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.111 2002/04/01 03:34:24 tgl Exp $
 -->
 
 <Chapter Id="runtime">
@@ -1174,7 +1174,7 @@ dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir'
 
        <para>
         The default value for this parameter is
-        <literal>$libdir</literal>. If the value is set to an empty
+        <literal>'$libdir'</literal>. If the value is set to an empty
         string, the automatic path search is turned off.
        </para>
 
@@ -1189,6 +1189,69 @@ dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir'
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><varname>SEARCH_PATH</varname> (<type>string</type>)</term>
+      <indexterm><primary>search_path</></>
+      <indexterm><primary>namespaces</></>
+      <listitem>
+       <para>
+        This variable specifies the order in which namespaces are searched
+	when an object (table, datatype, function, etc) is referenced by a
+	simple name with no schema component.  When there are objects of
+	identical names in different namespaces, the one found first
+	in the search path is used.  An object that is not in any of the
+	namespaces in the search path can only be referenced by specifying
+	its containing namespace with a qualified (dotted) name.
+       </para>
+
+       <para>
+        The value for search_path has to be a comma-separated
+        list of namespace (schema) names.  If one of the list items is
+        the special value <literal>$user</literal>, then the namespace
+	having the same name as the SESSION_USER is substituted, if there
+	is such a namespace.  (If not, <literal>$user</literal> is ignored.)
+       </para>
+
+       <para>
+        The system catalog namespace, <literal>pg_catalog</>, is always
+	searched, whether it is mentioned in the path or not.  If it is
+	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
+	be searched <emphasis>before</> searching any of the path items.
+	It should also be noted that the temporary-table namespace,
+	<literal>pg_temp_nnn</>, is implicitly searched before any of
+	these.
+       </para>
+
+       <para>
+        When objects are created without specifying a particular target
+	namespace, they will be placed in the first namespace listed
+	in the search path, or in <literal>pg_catalog</> if the search
+	path list is empty.  (Note that users do not normally have
+	permission to write in <literal>pg_catalog</>, so an empty search
+	path is not a very useful setting.)
+       </para>
+
+       <para>
+        The default value for this parameter is
+        <literal>'$user, public'</literal> (where the second part will be
+	ignored if there is no namespace named <literal>public</>).
+	This supports shared use of a database (where no users
+	have private namespaces, and all share use of <literal>public</>),
+	private per-user namespaces, and combinations of these.  Other
+	effects can be obtained by altering the default search path
+	setting, either globally or per-user.
+       </para>
+
+       <para>
+        By default, a newly created database will contain a world-writable
+	namespace named <literal>public</>, but no private namespaces.
+	The administrator may choose to restrict permissions on
+	<literal>public</> or even remove it, if that suits his purposes.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <indexterm>
        <primary>fsync</primary>
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 046f3d52aad9cd85d664a21c1dc10ab6143a3c82..ca66b0afafab8332918ac2f086d9f17e79cf1dfa 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.119 2002/03/31 06:26:29 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.120 2002/04/01 03:34:25 tgl Exp $
  *
  * NOTES
  *		Transaction aborts can now occur two ways:
@@ -241,8 +241,10 @@ bool		AMI_OVERRIDE = false;
  * ----------------------------------------------------------------
  */
 
+#ifdef NOT_USED
+
 /* --------------------------------
- *		TranactionFlushEnabled()
+ *		TransactionFlushEnabled()
  *		SetTransactionFlushEnabled()
  *
  *		These are used to test and set the "TransactionFlushState"
@@ -261,13 +263,14 @@ TransactionFlushEnabled(void)
 	return TransactionFlushState;
 }
 
-#ifdef NOT_USED
 void
 SetTransactionFlushEnabled(bool state)
 {
 	TransactionFlushState = (state == true);
 }
 
+#endif
+
 
 /* --------------------------------
  *		IsTransactionState
@@ -300,7 +303,6 @@ IsTransactionState(void)
 	 */
 	return false;
 }
-#endif
 
 /* --------------------------------
  *		IsAbortedTransactionBlockState
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index a1be64bd80e96f96572aad34b7199dd2ffffb212..fab1912b1b537f370378871441159836779bed60 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -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.4 2002/03/31 06:26:30 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.5 2002/04/01 03:34:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,7 +30,9 @@
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "storage/backendid.h"
+#include "utils/builtins.h"
 #include "utils/fmgroids.h"
+#include "utils/guc.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
@@ -71,6 +73,12 @@ static Oid	defaultCreationNamespace = PG_CATALOG_NAMESPACE;
  */
 static Oid	myTempNamespace = InvalidOid;
 
+/*
+ * This is the text equivalent of the search path --- it's the value
+ * of the GUC variable 'search_path'.
+ */
+char *namespace_search_path = NULL;
+
 
 /*
  * Deletion ordering constraint item.
@@ -702,3 +710,202 @@ RemoveTempRelationsCallback(void)
 		CommitTransactionCommand();
 	}
 }
+
+/*
+ * Routines for handling the GUC variable 'search_path'.
+ */
+
+/* parse_hook: is proposed value valid? */
+bool
+check_search_path(const char *proposed)
+{
+	char	   *rawname;
+	List	   *namelist;
+	List	   *l;
+
+	/* Need a modifiable copy of string */
+	rawname = pstrdup(proposed);
+
+	/* Parse string into list of identifiers */
+	if (!SplitIdentifierString(rawname, ',', &namelist))
+	{
+		/* syntax error in name list */
+		pfree(rawname);
+		freeList(namelist);
+		return false;
+	}
+
+	/*
+	 * If we aren't inside a transaction, we cannot do database access so
+	 * cannot verify the individual names.  Must accept the list on faith.
+	 * (This case can happen, for example, when the postmaster reads a
+	 * search_path setting from postgresql.conf.)
+	 */
+	if (!IsTransactionState())
+	{
+		pfree(rawname);
+		freeList(namelist);
+		return true;
+	}
+
+	/*
+	 * Verify that all the names are either valid namespace names or "$user".
+	 * (We do not require $user to correspond to a valid namespace; should we?)
+	 */
+	foreach(l, namelist)
+	{
+		char   *curname = (char *) lfirst(l);
+
+		if (strcmp(curname, "$user") == 0)
+			continue;
+		if (!SearchSysCacheExists(NAMESPACENAME,
+								  CStringGetDatum(curname),
+								  0, 0, 0))
+		{
+			pfree(rawname);
+			freeList(namelist);
+			return false;
+		}
+	}
+
+	pfree(rawname);
+	freeList(namelist);
+
+	return true;
+}
+
+/* assign_hook: do extra actions needed when assigning to search_path */
+void
+assign_search_path(const char *newval)
+{
+	char	   *rawname;
+	List	   *namelist;
+	List	   *oidlist;
+	List	   *newpath;
+	List	   *l;
+	MemoryContext oldcxt;
+
+	/*
+	 * If we aren't inside a transaction, we cannot do database access so
+	 * cannot look up the names.  In this case, do nothing; the internal
+	 * search path will be fixed later by InitializeSearchPath.  (We assume
+	 * this situation can only happen in the postmaster or early in backend
+	 * startup.)
+	 */
+	if (!IsTransactionState())
+		return;
+
+	/* Need a modifiable copy of string */
+	rawname = pstrdup(newval);
+
+	/* Parse string into list of identifiers */
+	if (!SplitIdentifierString(rawname, ',', &namelist))
+	{
+		/* syntax error in name list */
+		/* this should not happen if GUC checked check_search_path */
+		elog(ERROR, "assign_search_path: invalid list syntax");
+	}
+
+	/*
+	 * Convert the list of names to a list of OIDs.  If any names are not
+	 * recognizable, just leave them out of the list.  (This is our only
+	 * reasonable recourse when the already-accepted default is bogus.)
+	 */
+	oidlist = NIL;
+	foreach(l, namelist)
+	{
+		char   *curname = (char *) lfirst(l);
+		Oid		namespaceId;
+
+		if (strcmp(curname, "$user") == 0)
+		{
+			/* $user --- substitute namespace matching user name, if any */
+			HeapTuple	tuple;
+
+			tuple = SearchSysCache(SHADOWSYSID,
+								   ObjectIdGetDatum(GetSessionUserId()),
+								   0, 0, 0);
+			if (HeapTupleIsValid(tuple))
+			{
+				char   *uname;
+
+				uname = NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename);
+				namespaceId = GetSysCacheOid(NAMESPACENAME,
+											 CStringGetDatum(uname),
+											 0, 0, 0);
+				if (OidIsValid(namespaceId))
+					oidlist = lappendi(oidlist, namespaceId);
+				ReleaseSysCache(tuple);
+			}
+		}
+		else
+		{
+			/* normal namespace reference */
+			namespaceId = GetSysCacheOid(NAMESPACENAME,
+										 CStringGetDatum(curname),
+										 0, 0, 0);
+			if (OidIsValid(namespaceId))
+				oidlist = lappendi(oidlist, namespaceId);
+		}
+	}
+
+	/*
+	 * Now that we've successfully built the new list of namespace OIDs,
+	 * save it in permanent storage.
+	 */
+	oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+	newpath = listCopy(oidlist);
+	MemoryContextSwitchTo(oldcxt);
+
+	/* Now safe to assign to state variable. */
+	freeList(namespaceSearchPath);
+	namespaceSearchPath = newpath;
+
+	/*
+	 * Update info derived from search path.
+	 */
+	pathContainsSystemNamespace = intMember(PG_CATALOG_NAMESPACE,
+											namespaceSearchPath);
+
+	if (namespaceSearchPath == NIL)
+		defaultCreationNamespace = PG_CATALOG_NAMESPACE;
+	else
+		defaultCreationNamespace = (Oid) lfirsti(namespaceSearchPath);
+
+	/* Clean up. */
+	pfree(rawname);
+	freeList(namelist);
+	freeList(oidlist);
+}
+
+/*
+ * InitializeSearchPath: initialize module during InitPostgres.
+ *
+ * This is called after we are up enough to be able to do catalog lookups.
+ */
+void
+InitializeSearchPath(void)
+{
+	/*
+	 * In normal multi-user mode, we want the default search path to be
+	 * '$user,public' (or as much of that as exists, anyway; see the
+	 * error handling in assign_search_path); which is what guc.c has as
+	 * the wired-in default value.  But in bootstrap or standalone-backend
+	 * mode, the default search path must be empty so that initdb correctly
+	 * creates everything in PG_CATALOG_NAMESPACE.  Accordingly, adjust the
+	 * default setting if we are not running under postmaster.  (If a
+	 * non-default setting has been supplied, this will not overwrite it.)
+	 */
+	if (!IsUnderPostmaster)
+	{
+		SetConfigOption("search_path", "",
+						PGC_POSTMASTER, PGC_S_DEFAULT);
+	}
+	/*
+	 * If a search path setting was provided before we were able to execute
+	 * lookups, establish the internal search path now.
+	 */
+	if (namespace_search_path && *namespace_search_path &&
+		namespaceSearchPath == NIL)
+		assign_search_path(namespace_search_path);
+}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4ebddde891bb9e920df30bcabf6191f838d6b278..64cc3d60ca7517b7fd4c19be2bcb2eec19002a09 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.297 2002/03/29 19:06:10 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.298 2002/04/01 03:34:25 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -94,6 +94,7 @@ static void insertSelectOptions(SelectStmt *stmt,
 static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg);
 static Node *doNegate(Node *n);
 static void doNegateFloat(Value *v);
+static bool set_name_needs_quotes(const char *name);
 
 #define MASK(b) (1 << (b))
 
@@ -909,19 +910,26 @@ var_value:  opt_boolean						{ $$ = $1; }
 				if ($1 == NIL)
 					elog(ERROR, "SET must have at least one argument");
 
+				/* compute space needed; allow for quotes and comma */
 				foreach (n, $1)
 				{
 					Value *p = (Value *) lfirst(n);
 					Assert(IsA(p, String));
-					/* keep track of room for string and trailing comma */
-					slen += (strlen(p->val.str) + 1);
+					slen += (strlen(p->val.str) + 3);
 				}
 				result = palloc(slen + 1);
 				*result = '\0';
 				foreach (n, $1)
 				{
 					Value *p = (Value *) lfirst(n);
-					strcat(result, p->val.str);
+					if (set_name_needs_quotes(p->val.str))
+					{
+						strcat(result, "\"");
+						strcat(result, p->val.str);
+						strcat(result, "\"");
+					}
+					else
+						strcat(result, p->val.str);
 					strcat(result, ",");
 				}
 				/* remove the trailing comma from the last element */
@@ -6568,3 +6576,25 @@ doNegateFloat(Value *v)
 		v->val.str = newval;
 	}
 }
+
+/*
+ * Decide whether to put double quotes around a name appearing in a SET
+ * name_list.  Presently, do so if the name contains whitespace, commas,
+ * or uppercase characters.  (This is correct assuming that the result
+ * will be deparsed by SplitIdentifierString or similar logic.)
+ */
+static bool
+set_name_needs_quotes(const char *name)
+{
+	if (*name == '\0')
+		return true;			/* empty name does need quotes */
+	while (*name)
+	{
+		if (*name == ',' ||
+			isspace((unsigned char) *name) ||
+			isupper((unsigned char) *name))
+			return true;
+		name++;
+	}
+	return false;
+}
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index ce3e8dc254dbab978ff36123768027f0c51ce937..5afaf398879eec0b382710230407b4993b07087c 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.80 2002/03/30 01:02:42 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.81 2002/04/01 03:34:26 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1046,28 +1046,82 @@ name_text(PG_FUNCTION_ARGS)
  * functions that take a text parameter representing a qualified name.
  * We split the name at dots, downcase if not double-quoted, and
  * truncate names if they're too long.
- *
- * This is a kluge, really, and exists only for historical reasons.
- * A better notation for such functions would be nextval(relname).
  */
 List *
 textToQualifiedNameList(text *textval, const char *caller)
 {
 	char	   *rawname;
-	char	   *nextp;
 	List	   *result = NIL;
+	List	   *namelist;
+	List	   *l;
 
 	/* Convert to C string (handles possible detoasting). */
 	/* Note we rely on being able to modify rawname below. */
 	rawname = DatumGetCString(DirectFunctionCall1(textout,
 												  PointerGetDatum(textval)));
-	nextp = rawname;
 
+	if (!SplitIdentifierString(rawname, '.', &namelist))
+		elog(ERROR, "%s: invalid name syntax", caller);
+
+	if (namelist == NIL)
+		elog(ERROR, "%s: invalid name syntax", caller);
+
+	foreach(l, namelist)
+	{
+		char   *curname = (char *) lfirst(l);
+
+		result = lappend(result, makeString(pstrdup(curname)));
+	}
+
+	pfree(rawname);
+	freeList(namelist);
+
+	return result;
+}
+
+/*
+ * SplitIdentifierString --- parse a string containing identifiers
+ *
+ * This is the guts of textToQualifiedNameList, and is exported for use in
+ * other situations such as parsing GUC variables.  In the GUC case, it's
+ * important to avoid memory leaks, so the API is designed to minimize the
+ * amount of stuff that needs to be allocated and freed.
+ *
+ * Inputs:
+ *	rawstring: the input string; must be overwritable!  On return, it's
+ *			   been modified to contain the separated identifiers.
+ *	separator: the separator punctuation expected between identifiers
+ *			   (typically '.' or ',').  Whitespace may also appear around
+ *			   identifiers.
+ * Outputs:
+ *	namelist: filled with a palloc'd list of pointers to identifiers within
+ *			  rawstring.  Caller should freeList() this even on error return.
+ *
+ * Returns TRUE if okay, FALSE if there is a syntax error in the string.
+ *
+ * Note that an empty string is considered okay here, though not in
+ * textToQualifiedNameList.
+ */
+bool
+SplitIdentifierString(char *rawstring, char separator,
+					  List **namelist)
+{
+	char	   *nextp = rawstring;
+	bool		done = false;
+
+	*namelist = NIL;
+
+	while (isspace((unsigned char) *nextp))
+		nextp++;				/* skip leading whitespace */
+
+	if (*nextp == '\0')
+		return true;			/* allow empty string */
+
+	/* At the top of the loop, we are at start of a new identifier. */
 	do
 	{
 		char	   *curname;
 		char	   *endp;
-		char	   *cp;
 		int			curlen;
 
 		if (*nextp == '\"')
@@ -1078,56 +1132,54 @@ textToQualifiedNameList(text *textval, const char *caller)
 			{
 				endp = strchr(nextp + 1, '\"');
 				if (endp == NULL)
-					elog(ERROR, "%s: invalid quoted name: mismatched quotes",
-						 caller);
+					return false; /* mismatched quotes */
 				if (endp[1] != '\"')
 					break;		/* found end of quoted name */
 				/* Collapse adjacent quotes into one quote, and look again */
 				memmove(endp, endp+1, strlen(endp));
 				nextp = endp;
 			}
-			*endp = '\0';
+			/* endp now points at the terminating quote */
 			nextp = endp + 1;
-			if (*nextp)
-			{
-				if (*nextp != '.')
-					elog(ERROR, "%s: invalid name syntax",
-						 caller);
-				nextp++;
-				if (*nextp == '\0')
-					elog(ERROR, "%s: invalid name syntax",
-						 caller);
-			}
 		}
 		else
 		{
-			/* Unquoted name --- extends to next dot */
-			if (*nextp == '\0')				/* empty name not okay here */
-				elog(ERROR, "%s: invalid name syntax",
-					 caller);
+			/* Unquoted name --- extends to separator or whitespace */
 			curname = nextp;
-			endp = strchr(nextp, '.');
-			if (endp)
+			while (*nextp && *nextp != separator &&
+				   !isspace((unsigned char) *nextp))
 			{
-				*endp = '\0';
-				nextp = endp + 1;
-				if (*nextp == '\0')
-					elog(ERROR, "%s: invalid name syntax",
-						 caller);
-			}
-			else
-				nextp = nextp + strlen(nextp);
-			/*
-			 * It's important that this match the identifier downcasing code
-			 * used by backend/parser/scan.l.
-			 */
-			for (cp = curname; *cp; cp++)
-			{
-				if (isupper((unsigned char) *cp))
-					*cp = tolower((unsigned char) *cp);
+				/*
+				 * It's important that this match the identifier downcasing
+				 * code used by backend/parser/scan.l.
+				 */
+				if (isupper((unsigned char) *nextp))
+					*nextp = tolower((unsigned char) *nextp);
+				nextp++;
 			}
+			endp = nextp;
+			if (curname == nextp)
+				return false;	/* empty unquoted name not allowed */
 		}
 
+		while (isspace((unsigned char) *nextp))
+			nextp++;			/* skip trailing whitespace */
+
+		if (*nextp == separator)
+		{
+			nextp++;
+			while (isspace((unsigned char) *nextp))
+				nextp++;		/* skip leading whitespace for next */
+			/* we expect another name, so done remains false */
+		}
+		else if (*nextp == '\0')
+			done = true;
+		else
+			return false;		/* invalid syntax */
+
+		/* Now safe to overwrite separator with a null */
+		*endp = '\0';
+
 		/* Truncate name if it's overlength; again, should match scan.l */
 		curlen = strlen(curname);
 		if (curlen >= NAMEDATALEN)
@@ -1143,15 +1195,12 @@ textToQualifiedNameList(text *textval, const char *caller)
 		/*
 		 * Finished isolating current name --- add it to list
 		 */
-		result = lappend(result, makeString(pstrdup(curname)));
-		/*
-		 * Loop back if we found a dot
-		 */
-	} while (*nextp);
+		*namelist = lappend(*namelist, curname);
 
-	pfree(rawname);
+		/* Loop back if we didn't reach end of string */
+	} while (!done);
 
-	return result;
+	return true;
 }
 
 
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 9cd5b194d922535007e1e8319db2921a1b9fe3f0..0d36d70bea8f9bd2db28ae1716a5a6270e02358e 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.101 2002/03/31 06:26:32 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.102 2002/04/01 03:34:26 tgl Exp $
  *
  *
  *-------------------------------------------------------------------------
@@ -24,10 +24,11 @@
 #include "catalog/catalog.h"
 #include "access/heapam.h"
 #include "catalog/catname.h"
+#include "catalog/namespace.h"
 #include "catalog/pg_database.h"
 #include "catalog/pg_shadow.h"
 #include "commands/trigger.h"
-#include "commands/variable.h"	/* for set_default_client_encoding() */
+#include "commands/variable.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "storage/backendid.h"
@@ -372,11 +373,6 @@ InitPostgres(const char *dbname, const char *username)
 	if (!bootstrap)
 		ReverifyMyDatabase(dbname);
 
-#ifdef MULTIBYTE
-	/* set default client encoding --- uses info from ReverifyMyDatabase */
-	set_default_client_encoding();
-#endif
-
 	/*
 	 * Final phase of relation cache startup: write a new cache file
 	 * if necessary.  This is done after ReverifyMyDatabase to avoid
@@ -384,6 +380,19 @@ InitPostgres(const char *dbname, const char *username)
 	 */
 	RelationCacheInitializePhase3();
 
+	/*
+	 * Initialize various default states that can't be set up until
+	 * we've selected the active user and done ReverifyMyDatabase.
+	 */
+
+	/* set default namespace search path */
+	InitializeSearchPath();
+
+#ifdef MULTIBYTE
+	/* set default client encoding --- uses info from ReverifyMyDatabase */
+	set_default_client_encoding();
+#endif
+
 	/*
 	 * Set up process-exit callback to do pre-shutdown cleanup.  This should
 	 * be last because we want shmem_exit to call this routine before the exit
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index d1f6d2bdb00a63f8738fc5cc52f4c2d8003dc789..9c35f0949aa92c74199389b88ca88fc783314059 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -4,7 +4,7 @@
  * Support for grand unified configuration scheme, including SET
  * command, configuration file, and command line options.
  *
- * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.63 2002/03/24 04:31:08 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.64 2002/04/01 03:34:26 tgl Exp $
  *
  * Copyright 2000 by PostgreSQL Global Development Group
  * Written by Peter Eisentraut <peter_e@gmx.net>.
@@ -21,6 +21,7 @@
 #include "utils/guc.h"
 
 #include "access/xlog.h"
+#include "catalog/namespace.h"
 #include "commands/async.h"
 #include "fmgr.h"
 #include "libpq/auth.h"
@@ -574,6 +575,11 @@ static struct config_string
 		"$libdir", NULL, NULL
 	},
 
+	{
+		"search_path", PGC_USERSET, PGC_S_DEFAULT, &namespace_search_path,
+		"$user,public", check_search_path, assign_search_path
+	},
+
 	{
 		"krb_server_keyfile", PGC_POSTMASTER, PGC_S_DEFAULT, &pg_krb_server_keyfile,
 		PG_KRB_SRVTAB, NULL, NULL
@@ -899,7 +905,7 @@ set_config_option(const char *name, const char *value,
 	int			elevel;
 	bool		makeDefault;
 
-	if (context == PGC_SIGHUP)
+	if (context == PGC_SIGHUP || source == PGC_S_DEFAULT)
 		elevel = DEBUG1;
 	else if (guc_session_init)
 		elevel = INFO;
@@ -1414,7 +1420,7 @@ ProcessGUCArray(ArrayType *array, GucSource source)
 		ParseLongOption(s, &name, &value);
 		if (!value)
 		{
-			elog(WARNING, "cannot to parse setting \"%s\"", name);
+			elog(WARNING, "cannot parse setting \"%s\"", name);
 			continue;
 		}
 
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index a40b6ce7fac0e957aa180736119afc0c9b26a64a..01f55682b8800646224d2e5eefe0c345535ce62d 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -191,6 +191,7 @@
 #	Misc
 #
 #dynamic_library_path = '$libdir'
+#search_path = '$user,public'
 #australian_timezones = false
 #authentication_timeout = 60	# min 1, max 600
 #deadlock_timeout = 1000
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 003ec9fe33667493b8df99751fb8b96a052bd201..f25a45ef622585a0ee6321ca0765e5b92b76b04f 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -3,7 +3,7 @@
  *
  * Copyright 2000 by PostgreSQL Global Development Group
  *
- * $Header: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v 1.46 2002/03/24 04:31:08 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v 1.47 2002/04/01 03:34:27 tgl Exp $
  */
 
 /*----------------------------------------------------------------------
@@ -267,6 +267,7 @@ psql_completion(char *text, int start, int end)
 		"geqo_selection_bias",
 
 		"default_transaction_isolation",
+		"search_path",
 
 		NULL
 	};
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index 796a05383607e8c0bb8926b1fa5cb300ec9d5966..0cfc1652f22ea032adcb95514a21415b9ed52abf 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: xact.h,v 1.41 2001/11/05 17:46:31 momjian Exp $
+ * $Id: xact.h,v 1.42 2002/04/01 03:34:27 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -97,9 +97,7 @@ typedef struct xl_xact_abort
  *		extern definitions
  * ----------------
  */
-extern int	TransactionFlushEnabled(void);
-extern void SetTransactionFlushEnabled(bool state);
-
+extern bool IsTransactionState(void);
 extern bool IsAbortedTransactionBlockState(void);
 extern TransactionId GetCurrentTransactionId(void);
 extern CommandId GetCurrentCommandId(void);
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index aed33a10b31d8d4fdcb1040e3f742a7f081686d2..a8a64bd7db1151f4ce6365e200cdb58422b0f271 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -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.4 2002/03/31 06:26:32 tgl Exp $
+ * $Id: namespace.h,v 1.5 2002/04/01 03:34:27 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,4 +31,11 @@ extern RangeVar *makeRangeVarFromNameList(List *names);
 
 extern bool isTempNamespace(Oid namespaceId);
 
+/* stuff for search_path GUC variable */
+extern char *namespace_search_path;
+
+extern bool check_search_path(const char *proposed);
+extern void assign_search_path(const char *newval);
+extern void InitializeSearchPath(void);
+
 #endif   /* NAMESPACE_H */
diff --git a/src/include/catalog/pg_namespace.h b/src/include/catalog/pg_namespace.h
index fceee09fcb774689716524dd44cfc9f3b08c6bdc..d058206daec59dbae76f01b005e5f5c3058cc826 100644
--- a/src/include/catalog/pg_namespace.h
+++ b/src/include/catalog/pg_namespace.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_namespace.h,v 1.3 2002/03/31 06:26:32 tgl Exp $
+ * $Id: pg_namespace.h,v 1.4 2002/04/01 03:34:27 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -72,7 +72,7 @@ DESCR("System catalog namespace");
 DATA(insert OID = 99 ( "pg_toast" PGUID "{=}" ));
 DESCR("Reserved namespace for TOAST tables");
 #define PG_TOAST_NAMESPACE 99
-DATA(insert OID = 2071 ( "pg_public" PGUID "{=rw}" ));
+DATA(insert OID = 2071 ( "public" PGUID "{=rw}" ));
 DESCR("Standard public namespace");
 #define PG_PUBLIC_NAMESPACE 2071
 
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 7bd111cd544ec7006aac37e8e1e74aad6735f8c4..342aa4e99f6375089cd398b646aa07a9d04c6a31 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -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.174 2002/03/30 01:02:42 tgl Exp $
+ * $Id: builtins.h,v 1.175 2002/04/01 03:34:27 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -411,6 +411,8 @@ extern Datum name_text(PG_FUNCTION_ARGS);
 extern Datum text_name(PG_FUNCTION_ARGS);
 extern int	varstr_cmp(char *arg1, int len1, char *arg2, int len2);
 extern List *textToQualifiedNameList(text *textval, const char *caller);
+extern bool SplitIdentifierString(char *rawstring, char separator,
+								  List **namelist);
 
 extern Datum byteain(PG_FUNCTION_ARGS);
 extern Datum byteaout(PG_FUNCTION_ARGS);