diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 4c998fe51f92cf5046d53883ca9fa0d87b1d5150..00fc364c0ab232b1a45e526021b9b8851a4f0b34 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -6329,6 +6329,13 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
         setting, either globally or per-user.
        </para>
 
+       <para>
+        For more information on schema handling, see
+        <xref linkend="ddl-schemas"/>.  In particular, the default
+        configuration is suitable only when the database has a single user or
+        a few mutually-trusting users.
+       </para>
+
        <para>
         The current effective value of the search path can be examined
         via the <acronym>SQL</acronym> function
@@ -6340,9 +6347,6 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
         appearing in <varname>search_path</varname> were resolved.
        </para>
 
-       <para>
-        For more information on schema handling, see <xref linkend="ddl-schemas"/>.
-       </para>
       </listitem>
      </varlistentry>
 
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
index 0622227bee02dbafa9d1cd2bac68d90449742e47..b626a345f3eec49a67a7f97b089a849ece688a03 100644
--- a/doc/src/sgml/contrib.sgml
+++ b/doc/src/sgml/contrib.sgml
@@ -75,7 +75,7 @@ CREATE EXTENSION <replaceable>module_name</replaceable>;
   choice.  To do that, add <literal>SCHEMA
   <replaceable>schema_name</replaceable></literal> to the <command>CREATE EXTENSION</command>
   command.  By default, the objects will be placed in your current creation
-  target schema, typically <literal>public</literal>.
+  target schema, which in turn defaults to <literal>public</literal>.
  </para>
 
  <para>
diff --git a/doc/src/sgml/dblink.sgml b/doc/src/sgml/dblink.sgml
index 4c07f886aa79a3ee1e734fdebff9a780736afece..87e14ea093def7e481f79ec52e30d8b98616340f 100644
--- a/doc/src/sgml/dblink.sgml
+++ b/doc/src/sgml/dblink.sgml
@@ -83,7 +83,7 @@ dblink_connect(text connname, text connstr) returns text
      <listitem>
       <para><application>libpq</application>-style connection info string, for example
        <literal>hostaddr=127.0.0.1 port=5432 dbname=mydb user=postgres
-       password=mypasswd</literal>.
+       password=mypasswd options=-csearch_path=</literal>.
        For details see <xref linkend="libpq-connstring"/>.
        Alternatively, the name of a foreign server.
       </para>
@@ -104,6 +104,17 @@ dblink_connect(text connname, text connstr) returns text
   <refsect1>
    <title>Notes</title>
 
+   <para>
+    If untrusted users have access to a database that has not adopted a
+    <link linkend="ddl-schemas-patterns">secure schema usage pattern</link>,
+    begin each session by removing publicly-writable schemas from
+    <varname>search_path</varname>.  One could, for example,
+    add <literal>options=-csearch_path=</literal> to
+    <parameter>connstr</parameter>.  This consideration is not specific
+    to <filename>dblink</filename>; it applies to every interface for
+    executing arbitrary SQL commands.
+   </para>
+
    <para>
     Only superusers may use <function>dblink_connect</function> to create
     non-password-authenticated connections.  If non-superusers need this
@@ -121,13 +132,13 @@ dblink_connect(text connname, text connstr) returns text
    <title>Examples</title>
 
 <screen>
-SELECT dblink_connect('dbname=postgres');
+SELECT dblink_connect('dbname=postgres options=-csearch_path=');
  dblink_connect
 ----------------
  OK
 (1 row)
 
-SELECT dblink_connect('myconn', 'dbname=postgres');
+SELECT dblink_connect('myconn', 'dbname=postgres options=-csearch_path=');
  dblink_connect
 ----------------
  OK
@@ -416,7 +427,8 @@ dblink(text sql [, bool fail_on_error]) returns setof record
 
 <programlisting>
 SELECT *
-    FROM dblink('dbname=mydb', 'select proname, prosrc from pg_proc')
+    FROM dblink('dbname=mydb options=-csearch_path=',
+                'select proname, prosrc from pg_proc')
       AS t1(proname name, prosrc text)
     WHERE proname LIKE 'bytea%';
 </programlisting>
@@ -450,7 +462,8 @@ SELECT *
 <programlisting>
 CREATE VIEW myremote_pg_proc AS
   SELECT *
-    FROM dblink('dbname=postgres', 'select proname, prosrc from pg_proc')
+    FROM dblink('dbname=postgres options=-csearch_path=',
+                'select proname, prosrc from pg_proc')
     AS t1(proname name, prosrc text);
 
 SELECT * FROM myremote_pg_proc WHERE proname LIKE 'bytea%';
@@ -461,7 +474,8 @@ SELECT * FROM myremote_pg_proc WHERE proname LIKE 'bytea%';
    <title>Examples</title>
 
 <screen>
-SELECT * FROM dblink('dbname=postgres', 'select proname, prosrc from pg_proc')
+SELECT * FROM dblink('dbname=postgres options=-csearch_path=',
+                     'select proname, prosrc from pg_proc')
   AS t1(proname name, prosrc text) WHERE proname LIKE 'bytea%';
   proname   |   prosrc
 ------------+------------
@@ -479,7 +493,7 @@ SELECT * FROM dblink('dbname=postgres', 'select proname, prosrc from pg_proc')
  byteaout   | byteaout
 (12 rows)
 
-SELECT dblink_connect('dbname=postgres');
+SELECT dblink_connect('dbname=postgres options=-csearch_path=');
  dblink_connect
 ----------------
  OK
@@ -503,7 +517,7 @@ SELECT * FROM dblink('select proname, prosrc from pg_proc')
  byteaout   | byteaout
 (12 rows)
 
-SELECT dblink_connect('myconn', 'dbname=regression');
+SELECT dblink_connect('myconn', 'dbname=regression options=-csearch_path=');
  dblink_connect
 ----------------
  OK
@@ -778,7 +792,7 @@ dblink_open(text connname, text cursorname, text sql [, bool fail_on_error]) ret
    <title>Examples</title>
 
 <screen>
-SELECT dblink_connect('dbname=postgres');
+SELECT dblink_connect('dbname=postgres options=-csearch_path=');
  dblink_connect
 ----------------
  OK
@@ -899,7 +913,7 @@ dblink_fetch(text connname, text cursorname, int howmany [, bool fail_on_error])
    <title>Examples</title>
 
 <screen>
-SELECT dblink_connect('dbname=postgres');
+SELECT dblink_connect('dbname=postgres options=-csearch_path=');
  dblink_connect
 ----------------
  OK
@@ -1036,7 +1050,7 @@ dblink_close(text connname, text cursorname [, bool fail_on_error]) returns text
    <title>Examples</title>
 
 <screen>
-SELECT dblink_connect('dbname=postgres');
+SELECT dblink_connect('dbname=postgres options=-csearch_path=');
  dblink_connect
 ----------------
  OK
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 15a9285136debefebfcf548e7ede36d1ae638bb0..2b879ead4b2c672a29e8155c9c38ca1c0442e414 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2172,6 +2172,20 @@ CREATE TABLE public.products ( ... );
     in other schemas in the database.
    </para>
 
+  <para>
+    The ability to create like-named objects in different schemas complicates
+    writing a query that references precisely the same objects every time.  It
+    also opens up the potential for users to change the behavior of other
+    users' queries, maliciously or accidentally.  Due to the prevalence of
+    unqualified names in queries and their use
+    in <productname>PostgreSQL</productname> internals, adding a schema
+    to <varname>search_path</varname> effectively trusts all users having
+    <literal>CREATE</literal> privilege on that schema.  When you run an
+    ordinary query, a malicious user able to create objects in a schema of
+    your search path can take control and execute arbitrary SQL functions as
+    though you executed them.
+   </para>
+
    <indexterm>
     <primary>schema</primary>
     <secondary>current</secondary>
@@ -2288,8 +2302,9 @@ SELECT 3 OPERATOR(pg_catalog.+) 4;
     the schema
     <literal>public</literal>.  This allows all users that are able to
     connect to a given database to create objects in its
-    <literal>public</literal> schema.  If you do
-    not want to allow that, you can revoke that privilege:
+    <literal>public</literal> schema.
+    Some <link linkend="ddl-schemas-patterns">usage patterns</link> call for
+    revoking that privilege:
 <programlisting>
 REVOKE CREATE ON SCHEMA public FROM PUBLIC;
 </programlisting>
@@ -2339,50 +2354,75 @@ REVOKE CREATE ON SCHEMA public FROM PUBLIC;
    <title>Usage Patterns</title>
 
    <para>
-    Schemas can be used to organize your data in many ways.  There are
-    a few usage patterns that are recommended and are easily supported by
-    the default configuration:
+    Schemas can be used to organize your data in many ways.  There are a few
+    usage patterns easily supported by the default configuration, only one of
+    which suffices when database users mistrust other database users:
     <itemizedlist>
      <listitem>
+      <!-- "DROP SCHEMA public" is inferior to this REVOKE, because pg_dump
+           doesn't preserve that DROP. -->
       <para>
-       If you do not create any schemas then all users access the
-       public schema implicitly.  This simulates the situation where
-       schemas are not available at all.  This setup is mainly
-       recommended when there is only a single user or a few cooperating
-       users in a database.  This setup also allows smooth transition
-       from the non-schema-aware world.
+       Constrain ordinary users to user-private schemas.  To implement this,
+       issue <literal>REVOKE CREATE ON SCHEMA public FROM PUBLIC</literal>,
+       and create a schema for each user with the same name as that user.  If
+       affected users had logged in before this, consider auditing the public
+       schema for objects named like objects in
+       schema <literal>pg_catalog</literal>.  Recall that the default search
+       path starts with <literal>$user</literal>, which resolves to the user
+       name.  Therefore, if each user has a separate schema, they access their
+       own schemas by default.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       You can create a schema for each user with the same name as
-       that user.  Recall that the default search path starts with
-       <literal>$user</literal>, which resolves to the user name.
-       Therefore, if each user has a separate schema, they access their
-       own schemas by default.
+       Remove the public schema from each user's default search path
+       using <literal>ALTER ROLE <replaceable>user</replaceable> SET
+       search_path = "$user"</literal>.  Everyone retains the ability to
+       create objects in the public schema, but only qualified names will
+       choose those objects.  A user holding the <literal>CREATEROLE</literal>
+       privilege can undo this setting and issue arbitrary queries under the
+       identity of users relying on the setting.  If you
+       grant <literal>CREATEROLE</literal> to users not warranting this
+       almost-superuser ability, use the first pattern instead.
       </para>
+     </listitem>
 
+     <listitem>
       <para>
-       If you use this setup then you might also want to revoke access
-       to the public schema (or drop it altogether), so users are
-       truly constrained to their own schemas.
+       Remove the public schema from <varname>search_path</varname> in
+       <link linkend="config-setting-configuration-file"><filename>postgresql.conf</filename></link>.
+       The ensuing user experience matches the previous pattern.  In addition
+       to that pattern's implications for <literal>CREATEROLE</literal>, this
+       trusts database owners the same way.  If you assign
+       the <literal>CREATEROLE</literal>
+       privilege, <literal>CREATEDB</literal> privilege or individual database
+       ownership to users not warranting almost-superuser access, use the
+       first pattern instead.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       To install shared applications (tables to be used by everyone,
-       additional functions provided by third parties, etc.), put them
-       into separate schemas.  Remember to grant appropriate
-       privileges to allow the other users to access them.  Users can
-       then refer to these additional objects by qualifying the names
-       with a schema name, or they can put the additional schemas into
-       their search path, as they choose.
+       Keep the default.  All users access the public schema implicitly.  This
+       simulates the situation where schemas are not available at all, giving
+       a smooth transition from the non-schema-aware world.  However, any user
+       can issue arbitrary queries under the identity of any user not electing
+       to protect itself individually.  This pattern is acceptable only when
+       the database has a single user or a few mutually-trusting users.
       </para>
      </listitem>
     </itemizedlist>
    </para>
+
+   <para>
+    For any pattern, to install shared applications (tables to be used by
+    everyone, additional functions provided by third parties, etc.), put them
+    into separate schemas.  Remember to grant appropriate privileges to allow
+    the other users to access them.  Users can then refer to these additional
+    objects by qualifying the names with a schema name, or they can put the
+    additional schemas into their search path, as they choose.
+   </para>
   </sect2>
 
   <sect2 id="ddl-schemas-portability">
@@ -2405,7 +2445,7 @@ REVOKE CREATE ON SCHEMA public FROM PUBLIC;
    <para>
     Also, there is no concept of a <literal>public</literal> schema in the
     SQL standard.  For maximum conformance to the standard, you should
-    not use (perhaps even remove) the <literal>public</literal> schema.
+    not use the <literal>public</literal> schema.
    </para>
 
    <para>
diff --git a/doc/src/sgml/ecpg.sgml b/doc/src/sgml/ecpg.sgml
index 5a8d1f1b95ba152790347b5e5faca9b9a161f42d..98b68405207ba9f625769570f2b6ed3b331b0ad1 100644
--- a/doc/src/sgml/ecpg.sgml
+++ b/doc/src/sgml/ecpg.sgml
@@ -186,6 +186,18 @@ EXEC SQL CONNECT TO <replaceable>target</replaceable> <optional>AS <replaceable>
    chapter).
   </para>
 
+  <para>
+   If untrusted users have access to a database that has not adopted a
+   <link linkend="ddl-schemas-patterns">secure schema usage pattern</link>,
+   begin each session by removing publicly-writable schemas
+   from <varname>search_path</varname>.  For example,
+   add <literal>options=-csearch_path=</literal>
+   to <literal><replaceable>options</replaceable></literal>, or
+   issue <literal>EXEC SQL SELECT pg_catalog.set_config('search_path', '',
+   false);</literal> after connecting.  This consideration is not specific to
+   ECPG; it applies to every interface for executing arbitrary SQL commands.
+  </para>
+
   <para>
    Here are some examples of <command>CONNECT</command> statements:
 <programlisting>
@@ -266,8 +278,11 @@ int
 main()
 {
     EXEC SQL CONNECT TO testdb1 AS con1 USER testuser;
+    EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
     EXEC SQL CONNECT TO testdb2 AS con2 USER testuser;
+    EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
     EXEC SQL CONNECT TO testdb3 AS con3 USER testuser;
+    EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
 
     /* This query would be executed in the last opened database "testdb3". */
     EXEC SQL SELECT current_database() INTO :dbname;
@@ -1093,6 +1108,7 @@ EXEC SQL BEGIN DECLARE SECTION;
 EXEC SQL END DECLARE SECTION;
 
     EXEC SQL CONNECT TO testdb;
+    EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
 
     in = PGTYPESinterval_new();
     EXEC SQL SELECT '1 min'::interval INTO :in;
@@ -1147,6 +1163,7 @@ EXEC SQL BEGIN DECLARE SECTION;
 EXEC SQL END DECLARE SECTION;
 
     EXEC SQL CONNECT TO testdb;
+    EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
 
     num = PGTYPESnumeric_new();
     dec = PGTYPESdecimal_new();
@@ -1221,6 +1238,7 @@ EXEC SQL END DECLARE SECTION;
     memset(dbid, 0, sizeof(int) * 8);
 
     EXEC SQL CONNECT TO testdb;
+    EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
 
     /* Retrieve multiple rows into arrays at once. */
     EXEC SQL SELECT oid,datname INTO :dbid, :dbname FROM pg_database;
@@ -1887,6 +1905,7 @@ char *stmt = "SELECT u.usename as dbaname, d.datname "
 EXEC SQL END DECLARE SECTION;
 
 EXEC SQL CONNECT TO testdb AS con1 USER testuser;
+EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
 
 EXEC SQL PREPARE stmt1 FROM :stmt;
 
@@ -4317,6 +4336,7 @@ main(void)
     EXEC SQL END DECLARE SECTION;
 
     EXEC SQL CONNECT TO testdb AS con1 USER testuser;
+    EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
 
     EXEC SQL PREPARE stmt1 FROM :query;
     EXEC SQL DECLARE cur1 CURSOR FOR stmt1;
@@ -4478,6 +4498,7 @@ main(void)
     EXEC SQL END DECLARE SECTION;
 
     EXEC SQL CONNECT TO uptimedb AS con1 USER uptime;
+    EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
 
     EXEC SQL PREPARE stmt1 FROM :query;
     EXEC SQL DECLARE cur1 CURSOR FOR stmt1;
@@ -5909,6 +5930,7 @@ main(void)
     memset(buf, 1, buflen);
 
     EXEC SQL CONNECT TO testdb AS con1;
+    EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
 
     conn = ECPGget_PGconn("con1");
     printf("conn = %p\n", conn);
@@ -6038,6 +6060,7 @@ class TestCpp
 TestCpp::TestCpp()
 {
     EXEC SQL CONNECT TO testdb1;
+    EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
 }
 
 void Test::test()
@@ -6117,6 +6140,7 @@ void
 db_connect()
 {
     EXEC SQL CONNECT TO testdb1;
+    EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
 }
 
 void
@@ -6510,12 +6534,14 @@ EXEC SQL END DECLARE SECTION;
     ECPGdebug(1, stderr);
 
     EXEC SQL CONNECT TO :dbname USER :user;
+    EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
     EXEC SQL SELECT version() INTO :ver;
     EXEC SQL DISCONNECT;
 
     printf("version: %s\n", ver);
 
     EXEC SQL CONNECT TO :connection USER :user;
+    EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
     EXEC SQL SELECT version() INTO :ver;
     EXEC SQL DISCONNECT;
 
@@ -7116,6 +7142,7 @@ EXEC SQL BEGIN DECLARE SECTION;
 EXEC SQL END DECLARE SECTION;
 
     EXEC SQL CONNECT TO testdb AS con1 USER testuser;
+    EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
     EXEC SQL ALLOCATE DESCRIPTOR d;
 
     /* Declare, open a cursor, and assign a descriptor to the cursor  */
@@ -7673,6 +7700,7 @@ EXEC SQL BEGIN DECLARE SECTION;
 EXEC SQL END DECLARE SECTION;
 
     EXEC SQL CONNECT TO testdb AS con1;
+    EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
 
     EXEC SQL SELECT current_database(), 256 INTO :t:t_ind LIMIT 1;
 
@@ -7829,6 +7857,7 @@ int
 main(void)
 {
     EXEC SQL CONNECT TO testdb AS con1;
+    EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
     EXEC SQL ALLOCATE DESCRIPTOR d;
     EXEC SQL DECLARE cur CURSOR FOR SELECT current_database(), 'hoge', 256;
     EXEC SQL OPEN cur;
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index 5f1bb70e97ab05e2e3d74a86c6c68bd0c94b8119..6c043cdd02aeb709a546203a5702e7caffe08049 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -430,6 +430,32 @@
     dropping the whole extension.
    </para>
 
+   <sect2 id="extend-extensions-style">
+    <title>Defining Extension Objects</title>
+
+    <!-- XXX It's not enough to use qualified names, because one might write a
+         qualified name to an object that itself uses unqualified names.  Many
+         information_schema functions have that defect, for example.  However,
+         that's a defect in the referenced object, and relatively few queries
+         will be affected.  Also, we direct applications to secure search_path
+         when connecting to an untrusted database; if applications do that,
+         they are immune to known attacks even if some extension refers to a
+         defective object.  Therefore, guide extension authors as though core
+         PostgreSQL contained no such defect. -->
+    <para>
+     Widely-distributed extensions should assume little about the database
+     they occupy.  In particular, unless you issued <literal>SET search_path =
+     pg_temp</literal>, assume each unqualified name could resolve to an
+     object that a malicious user has defined.  Beware of constructs that
+     depend on <varname>search_path</varname> implicitly: <token>IN</token>
+     and <literal>CASE <replaceable>expression</replaceable> WHEN</literal>
+     always select an operator using the search path.  In their place, use
+     <literal>OPERATOR(<replaceable>schema</replaceable>.=) ANY</literal>
+     and <literal>CASE WHEN <replaceable>expression</replaceable></literal>.
+    </para>
+
+   </sect2>
+
    <sect2>
     <title>Extension Files</title>
 
@@ -984,24 +1010,24 @@ SELECT * FROM pg_extension_update_paths('<replaceable>extension_name</replaceabl
 -- complain if script is sourced in psql, rather than via CREATE EXTENSION
 \echo Use "CREATE EXTENSION pair" to load this file. \quit
 
-CREATE TYPE pair AS ( k text, v text );
-
-CREATE OR REPLACE FUNCTION pair(anyelement, text)
-RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
+CREATE TYPE pair AS ( k pg_catalog.text, v pg_catalog.text );
 
-CREATE OR REPLACE FUNCTION pair(text, anyelement)
-RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
+CREATE OR REPLACE FUNCTION pair(pg_catalog.text, pg_catalog.text)
+RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::@extschema@.pair;';
 
-CREATE OR REPLACE FUNCTION pair(anyelement, anyelement)
-RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
+CREATE OPERATOR ~> (LEFTARG = pg_catalog.text,
+                    RIGHTARG = pg_catalog.text, PROCEDURE = pair);
 
-CREATE OR REPLACE FUNCTION pair(text, text)
-RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair;';
+-- "SET search_path" is easy to get right, but qualified names perform better.
+CREATE OR REPLACE FUNCTION lower(pair)
+RETURNS pair LANGUAGE SQL
+AS 'SELECT ROW(lower($1.k), lower($1.v))::@extschema@.pair;'
+SET search_path = pg_temp;
 
-CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = anyelement, PROCEDURE = pair);
-CREATE OPERATOR ~> (LEFTARG = anyelement, RIGHTARG = text, PROCEDURE = pair);
-CREATE OPERATOR ~> (LEFTARG = anyelement, RIGHTARG = anyelement, PROCEDURE = pair);
-CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = text, PROCEDURE = pair);
+CREATE OR REPLACE FUNCTION pair_concat(pair, pair)
+RETURNS pair LANGUAGE SQL
+AS 'SELECT ROW($1.k OPERATOR(pg_catalog.||) $2.k,
+               $1.v OPERATOR(pg_catalog.||) $2.v)::@extschema@.pair;';
 ]]>
 </programlisting>
     </para>
@@ -1013,7 +1039,7 @@ CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = text, PROCEDURE = pair);
 # pair extension
 comment = 'A key/value pair data type'
 default_version = '1.0'
-relocatable = true
+relocatable = false
 </programlisting>
     </para>
 
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index f327d4b5b5b4b383e7fd7f8e50bd727597713ab3..2a8e1f2e07d99665c457baaa5bf2342c7c2768af 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -65,6 +65,22 @@
    the return value for a successful connection before queries are sent
    via the connection object.
 
+   <warning>
+    <para>
+     If untrusted users have access to a database that has not adopted a
+     <link linkend="ddl-schemas-patterns">secure schema usage pattern</link>,
+     begin each session by removing publicly-writable schemas from
+     <varname>search_path</varname>.  One can set parameter key
+     word <literal>options</literal> to
+     value <literal>-csearch_path=</literal>.  Alternately, one can
+     issue <literal>PQexec(<replaceable>conn</replaceable>, "SELECT
+     pg_catalog.set_config('search_path', '', false)")</literal> after
+     connecting.  This consideration is not specific
+     to <application>libpq</application>; it applies to every interface for
+     executing arbitrary SQL commands.
+    </para>
+   </warning>
+
    <warning>
     <para>
      On Unix, forking a process with open libpq connections can lead to
@@ -6878,7 +6894,8 @@ main(void)
 {
     mydata *data;
     PGresult *res;
-    PGconn *conn = PQconnectdb("dbname = postgres");
+    PGconn *conn =
+        PQconnectdb("dbname=postgres options=-csearch_path=");
 
     if (PQstatus(conn) != CONNECTION_OK)
     {
@@ -8305,6 +8322,22 @@ main(int argc, char **argv)
         exit_nicely(conn);
     }
 
+    /* Set always-secure search path, so malicous users can't take control. */
+    res = PQexec(conn,
+                 "SELECT pg_catalog.set_config('search_path', '', false)");
+    if (PQresultStatus(res) != PGRES_COMMAND_OK)
+    {
+        fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
+        PQclear(res);
+        exit_nicely(conn);
+    }
+
+    /*
+     * Should PQclear PGresult whenever it is no longer needed to avoid memory
+     * leaks
+     */
+    PQclear(res);
+
     /*
      * Our test case here involves using a cursor, for which we must be inside
      * a transaction block.  We could do the whole thing with a single
@@ -8320,11 +8353,6 @@ main(int argc, char **argv)
         PQclear(res);
         exit_nicely(conn);
     }
-
-    /*
-     * Should PQclear PGresult whenever it is no longer needed to avoid memory
-     * leaks
-     */
     PQclear(res);
 
     /*
@@ -8400,16 +8428,16 @@ main(int argc, char **argv)
  * populate a database with the following commands
  * (provided in src/test/examples/testlibpq2.sql):
  *
+ *   CREATE SCHEMA TESTLIBPQ2;
+ *   SET search_path = TESTLIBPQ2;
  *   CREATE TABLE TBL1 (i int4);
- *
  *   CREATE TABLE TBL2 (i int4);
- *
  *   CREATE RULE r1 AS ON INSERT TO TBL1 DO
  *     (INSERT INTO TBL2 VALUES (new.i); NOTIFY TBL2);
  *
- * and do this four times:
+ * Start this program, then from psql do this four times:
  *
- *   INSERT INTO TBL1 VALUES (10);
+ *   INSERT INTO TESTLIBPQ2.TBL1 VALUES (10);
  */
 
 #ifdef WIN32
@@ -8464,6 +8492,22 @@ main(int argc, char **argv)
         exit_nicely(conn);
     }
 
+    /* Set always-secure search path, so malicous users can't take control. */
+    res = PQexec(conn,
+                 "SELECT pg_catalog.set_config('search_path', '', false)");
+    if (PQresultStatus(res) != PGRES_COMMAND_OK)
+    {
+        fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
+        PQclear(res);
+        exit_nicely(conn);
+    }
+
+    /*
+     * Should PQclear PGresult whenever it is no longer needed to avoid memory
+     * leaks
+     */
+    PQclear(res);
+
     /*
      * Issue LISTEN command to enable notifications from the rule's NOTIFY.
      */
@@ -8474,11 +8518,6 @@ main(int argc, char **argv)
         PQclear(res);
         exit_nicely(conn);
     }
-
-    /*
-     * should PQclear PGresult whenever it is no longer needed to avoid memory
-     * leaks
-     */
     PQclear(res);
 
     /* Quit after four notifies are received. */
@@ -8545,8 +8584,9 @@ main(int argc, char **argv)
  * Before running this, populate a database with the following commands
  * (provided in src/test/examples/testlibpq3.sql):
  *
+ * CREATE SCHEMA testlibpq3;
+ * SET search_path = testlibpq3;
  * CREATE TABLE test1 (i int4, t text, b bytea);
- *
  * INSERT INTO test1 values (1, 'joe''s place', '\\000\\001\\002\\003\\004');
  * INSERT INTO test1 values (2, 'ho there', '\\004\\003\\002\\001\\000');
  *
@@ -8678,6 +8718,16 @@ main(int argc, char **argv)
         exit_nicely(conn);
     }
 
+    /* Set always-secure search path, so malicous users can't take control. */
+    res = PQexec(conn, "SET search_path = testlibpq3");
+    if (PQresultStatus(res) != PGRES_COMMAND_OK)
+    {
+        fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
+        PQclear(res);
+        exit_nicely(conn);
+    }
+    PQclear(res);
+
     /*
      * The point of this program is to illustrate use of PQexecParams() with
      * out-of-line parameters, as well as binary transmission of data.
diff --git a/doc/src/sgml/lobj.sgml b/doc/src/sgml/lobj.sgml
index 6b5aaebbbc4ec4ef3680a111eb1e0f4777357b9e..771795ae668cf43d9b4df78d370a06769a7516a6 100644
--- a/doc/src/sgml/lobj.sgml
+++ b/doc/src/sgml/lobj.sgml
@@ -933,6 +933,17 @@ main(int argc, char **argv)
         exit_nicely(conn);
     }
 
+    /* Set always-secure search path, so malicous users can't take control. */
+    res = PQexec(conn,
+                 "SELECT pg_catalog.set_config('search_path', '', false)");
+    if (PQresultStatus(res) != PGRES_COMMAND_OK)
+    {
+        fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
+        PQclear(res);
+        exit_nicely(conn);
+    }
+    PQclear(res);
+
     res = PQexec(conn, "begin");
     PQclear(res);
     printf("importing file \"%s\" ...\n", in_filename);
diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml
index 3dd492cec11a47ab04805d7c4cc64c63c45339e7..59b573b50116fd603da43dafd48490ec4b9b5390 100644
--- a/doc/src/sgml/ref/pgbench.sgml
+++ b/doc/src/sgml/ref/pgbench.sgml
@@ -1688,5 +1688,16 @@ statement latencies in milliseconds:
    database server.
   </para>
  </refsect2>
+ <refsect2>
+  <title>Security</title>
+
+  <para>
+    If untrusted users have access to a database that has not adopted a
+    <link linkend="ddl-schemas-patterns">secure schema usage pattern</link>,
+    do not run <application>pgbench</application> in that
+    database.  <application>pgbench</application> uses unqualified names and
+    does not manipulate the search path.
+  </para>
+ </refsect2>
  </refsect1>
 </refentry>
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 8bd9b9387ec4fcf0c7654741b2e6c1e779f1076a..bfdf859731145e58de9956dfb56ec881bd9a473a 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -735,6 +735,18 @@ testdb=&gt;
     of the command are displayed on the screen.
     </para>
 
+    <para>
+    If untrusted users have access to a database that has not adopted a
+    <link linkend="ddl-schemas-patterns">secure schema usage pattern</link>,
+    begin your session by removing publicly-writable schemas
+    from <varname>search_path</varname>.  One can
+    add <literal>options=-csearch_path=</literal> to the connection string or
+    issue <literal>SELECT pg_catalog.set_config('search_path', '',
+    false)</literal> before other SQL commands.  This consideration is not
+    specific to <application>psql</application>; it applies to every interface
+    for executing arbitrary SQL commands.
+    </para>
+
     <para>
     Whenever a command is executed, <application>psql</application> also polls
     for asynchronous notification events generated by
diff --git a/doc/src/sgml/user-manag.sgml b/doc/src/sgml/user-manag.sgml
index ae15efed957514fa413c707cc02c36f396b4ca5f..94fd4ebf5821e68f31c08714b612999adf73b18f 100644
--- a/doc/src/sgml/user-manag.sgml
+++ b/doc/src/sgml/user-manag.sgml
@@ -571,14 +571,17 @@ GRANT pg_signal_backend TO admin_user;
  </sect1>
 
  <sect1 id="perm-functions">
-  <title>Function and Trigger Security</title>
+  <title>Function Security</title>
 
   <para>
-   Functions and triggers allow users to insert code into the backend
-   server that other users might execute unintentionally. Hence, both
-   mechanisms permit users to <quote>Trojan horse</quote>
-   others with relative ease. The only real protection is tight
-   control over who can define functions.
+   Functions, triggers and row-level security policies allow users to insert
+   code into the backend server that other users might execute
+   unintentionally. Hence, these mechanisms permit users to <quote>Trojan
+   horse</quote> others with relative ease. The strongest protection is tight
+   control over who can define objects. Where that is infeasible, write
+   queries referring only to objects having trusted owners.  Remove
+   from <varname>search_path</varname> the public schema and any other schemas
+   that permit untrusted users to create objects.
   </para>
 
   <para>
diff --git a/src/test/examples/testlibpq.c b/src/test/examples/testlibpq.c
index 4d9af82dd18d53bf088960f2708e85c5e8c2de3a..92a05e53093ee88c819610f727e50542aa91d8c7 100644
--- a/src/test/examples/testlibpq.c
+++ b/src/test/examples/testlibpq.c
@@ -48,6 +48,22 @@ main(int argc, char **argv)
 		exit_nicely(conn);
 	}
 
+	/* Set always-secure search path, so malicous users can't take control. */
+	res = PQexec(conn,
+				 "SELECT pg_catalog.set_config('search_path', '', false)");
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+	{
+		fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
+		PQclear(res);
+		exit_nicely(conn);
+	}
+
+	/*
+	 * Should PQclear PGresult whenever it is no longer needed to avoid memory
+	 * leaks
+	 */
+	PQclear(res);
+
 	/*
 	 * Our test case here involves using a cursor, for which we must be inside
 	 * a transaction block.  We could do the whole thing with a single
@@ -63,11 +79,6 @@ main(int argc, char **argv)
 		PQclear(res);
 		exit_nicely(conn);
 	}
-
-	/*
-	 * Should PQclear PGresult whenever it is no longer needed to avoid memory
-	 * leaks
-	 */
 	PQclear(res);
 
 	/*
diff --git a/src/test/examples/testlibpq2.c b/src/test/examples/testlibpq2.c
index 07c6317a2123dd3b7808bef9895bd35bb9a9cf0e..76787fe010b8ed942f5321c2583a04326aae43f9 100644
--- a/src/test/examples/testlibpq2.c
+++ b/src/test/examples/testlibpq2.c
@@ -13,16 +13,16 @@
  * populate a database with the following commands
  * (provided in src/test/examples/testlibpq2.sql):
  *
+ *	 CREATE SCHEMA TESTLIBPQ2;
+ *	 SET search_path = TESTLIBPQ2;
  *	 CREATE TABLE TBL1 (i int4);
- *
  *	 CREATE TABLE TBL2 (i int4);
- *
  *	 CREATE RULE r1 AS ON INSERT TO TBL1 DO
  *	   (INSERT INTO TBL2 VALUES (new.i); NOTIFY TBL2);
  *
- * and do this four times:
+ * Start this program, then from psql do this four times:
  *
- *	 INSERT INTO TBL1 VALUES (10);
+ *	 INSERT INTO TESTLIBPQ2.TBL1 VALUES (10);
  */
 
 #ifdef WIN32
@@ -77,6 +77,22 @@ main(int argc, char **argv)
 		exit_nicely(conn);
 	}
 
+	/* Set always-secure search path, so malicous users can't take control. */
+	res = PQexec(conn,
+				 "SELECT pg_catalog.set_config('search_path', '', false)");
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+	{
+		fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
+		PQclear(res);
+		exit_nicely(conn);
+	}
+
+	/*
+	 * Should PQclear PGresult whenever it is no longer needed to avoid memory
+	 * leaks
+	 */
+	PQclear(res);
+
 	/*
 	 * Issue LISTEN command to enable notifications from the rule's NOTIFY.
 	 */
@@ -87,11 +103,6 @@ main(int argc, char **argv)
 		PQclear(res);
 		exit_nicely(conn);
 	}
-
-	/*
-	 * should PQclear PGresult whenever it is no longer needed to avoid memory
-	 * leaks
-	 */
 	PQclear(res);
 
 	/* Quit after four notifies are received. */
diff --git a/src/test/examples/testlibpq2.sql b/src/test/examples/testlibpq2.sql
index 1686c3ed0dfff0c19f21ce599405f6ba6fabb039..e8173e4293b55840384b5d67a9d47a87f61f19ef 100644
--- a/src/test/examples/testlibpq2.sql
+++ b/src/test/examples/testlibpq2.sql
@@ -1,6 +1,6 @@
+CREATE SCHEMA TESTLIBPQ2;
+SET search_path = TESTLIBPQ2;
 CREATE TABLE TBL1 (i int4);
-
 CREATE TABLE TBL2 (i int4);
-
 CREATE RULE r1 AS ON INSERT TO TBL1 DO
   (INSERT INTO TBL2 VALUES (new.i); NOTIFY TBL2);
diff --git a/src/test/examples/testlibpq3.c b/src/test/examples/testlibpq3.c
index e11e0567cac815239b0a5e34960cf5b81b2a9e2d..00e62b43d2874697fb693b1539f93df1d382499c 100644
--- a/src/test/examples/testlibpq3.c
+++ b/src/test/examples/testlibpq3.c
@@ -8,8 +8,9 @@
  * Before running this, populate a database with the following commands
  * (provided in src/test/examples/testlibpq3.sql):
  *
+ * CREATE SCHEMA testlibpq3;
+ * SET search_path = testlibpq3;
  * CREATE TABLE test1 (i int4, t text, b bytea);
- *
  * INSERT INTO test1 values (1, 'joe''s place', '\\000\\001\\002\\003\\004');
  * INSERT INTO test1 values (2, 'ho there', '\\004\\003\\002\\001\\000');
  *
@@ -141,6 +142,16 @@ main(int argc, char **argv)
 		exit_nicely(conn);
 	}
 
+	/* Set always-secure search path, so malicous users can't take control. */
+	res = PQexec(conn, "SET search_path = testlibpq3");
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+	{
+		fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
+		PQclear(res);
+		exit_nicely(conn);
+	}
+	PQclear(res);
+
 	/*
 	 * The point of this program is to illustrate use of PQexecParams() with
 	 * out-of-line parameters, as well as binary transmission of data.
diff --git a/src/test/examples/testlibpq3.sql b/src/test/examples/testlibpq3.sql
index 9d9e217e5ddf1289a399cbeb1b7d3727d32eff2f..221330650990a1a1ecc68adffcd66cc682c9d4aa 100644
--- a/src/test/examples/testlibpq3.sql
+++ b/src/test/examples/testlibpq3.sql
@@ -1,4 +1,5 @@
+CREATE SCHEMA testlibpq3;
+SET search_path = testlibpq3;
 CREATE TABLE test1 (i int4, t text, b bytea);
-
 INSERT INTO test1 values (1, 'joe''s place', '\\000\\001\\002\\003\\004');
 INSERT INTO test1 values (2, 'ho there', '\\004\\003\\002\\001\\000');
diff --git a/src/test/examples/testlibpq4.c b/src/test/examples/testlibpq4.c
index 0ec04313c0cc03fcd8a147dc72c79ea7ac4fa479..a20f6249b4eeb3dda9ebd17b35a98d88c6051e57 100644
--- a/src/test/examples/testlibpq4.c
+++ b/src/test/examples/testlibpq4.c
@@ -22,8 +22,10 @@ exit_nicely(PGconn *conn1, PGconn *conn2)
 }
 
 static void
-check_conn(PGconn *conn, const char *dbName)
+check_prepare_conn(PGconn *conn, const char *dbName)
 {
+	PGresult   *res;
+
 	/* check to see that the backend connection was successfully made */
 	if (PQstatus(conn) != CONNECTION_OK)
 	{
@@ -31,6 +33,17 @@ check_conn(PGconn *conn, const char *dbName)
 				dbName, PQerrorMessage(conn));
 		exit(1);
 	}
+
+	/* Set always-secure search path, so malicous users can't take control. */
+	res = PQexec(conn,
+				 "SELECT pg_catalog.set_config('search_path', '', false)");
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+	{
+		fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
+		PQclear(res);
+		exit(1);
+	}
+	PQclear(res);
 }
 
 int
@@ -80,10 +93,10 @@ main(int argc, char **argv)
 
 	/* make a connection to the database */
 	conn1 = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName1);
-	check_conn(conn1, dbName1);
+	check_prepare_conn(conn1, dbName1);
 
 	conn2 = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName2);
-	check_conn(conn2, dbName2);
+	check_prepare_conn(conn2, dbName2);
 
 	/* start a transaction block */
 	res1 = PQexec(conn1, "BEGIN");
diff --git a/src/test/examples/testlo.c b/src/test/examples/testlo.c
index 7afe24714a26e52e5674c172c01ca8fbdbfc0176..be5c72b9a67ef82a2462f853f68b5369f7d87913 100644
--- a/src/test/examples/testlo.c
+++ b/src/test/examples/testlo.c
@@ -232,6 +232,17 @@ main(int argc, char **argv)
 		exit_nicely(conn);
 	}
 
+	/* Set always-secure search path, so malicous users can't take control. */
+	res = PQexec(conn,
+				 "SELECT pg_catalog.set_config('search_path', '', false)");
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+	{
+		fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
+		PQclear(res);
+		exit_nicely(conn);
+	}
+	PQclear(res);
+
 	res = PQexec(conn, "begin");
 	PQclear(res);
 	printf("importing file \"%s\" ...\n", in_filename);
diff --git a/src/test/examples/testlo64.c b/src/test/examples/testlo64.c
index bb188cc3a18e2d5c7f8461c8db5e7d86424a3937..39ba009c52355f3f212c43df36592af79f769e09 100644
--- a/src/test/examples/testlo64.c
+++ b/src/test/examples/testlo64.c
@@ -256,6 +256,17 @@ main(int argc, char **argv)
 		exit_nicely(conn);
 	}
 
+	/* Set always-secure search path, so malicous users can't take control. */
+	res = PQexec(conn,
+				 "SELECT pg_catalog.set_config('search_path', '', false)");
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+	{
+		fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
+		PQclear(res);
+		exit_nicely(conn);
+	}
+	PQclear(res);
+
 	res = PQexec(conn, "begin");
 	PQclear(res);
 	printf("importing file \"%s\" ...\n", in_filename);