On 27.01.22 09:10, Peter Eisentraut wrote:
On 21.01.22 17:13, Julien Rouhaud wrote:
On Fri, Jan 21, 2022 at 03:24:02PM +0100, Peter Eisentraut wrote:
On 21.01.22 14:51, Julien Rouhaud wrote:
Is that change intended?  There isn't any usage of the collversionstr before
the possible error when actual_versionstr is missing.

I wanted to move it closer to the SysCacheGetAttr() where the "datum" value is obtained.  It seemed weird to get the datum, then do other things, then
decode the datum.

Oh ok.  It won't make much difference performance-wise, so no objection.

I have committed this and will provide follow-up patches in the next few days.

Here is the main patch rebased on the various changes that have been committed in the meantime. There is still some work to be done on the user interfaces of initdb, createdb, etc.

I have split out the database-level collation version tracking into a separate patch [0]. I think we should get that one in first and then refresh this one.

[0]: https://www.postgresql.org/message-id/flat/f0ff3190-29a3-5b39-a179-fa32eee57db6%40enterprisedb.com
From 4028980f6be3662c0302575ed92de77e941e5a9e Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Wed, 2 Feb 2022 13:54:04 +0100
Subject: [PATCH v4] Add option to use ICU as global collation provider

This adds the option to use ICU as the default collation provider for
either the whole cluster or a database.  New options for initdb,
createdb, and CREATE DATABASE are used to select this.

Discussion: 
https://www.postgresql.org/message-id/flat/5e756dd6-0e91-d778-96fd-b1bcb06c161a%402ndquadrant.com
---
 doc/src/sgml/catalogs.sgml                    |   9 +
 doc/src/sgml/ref/create_database.sgml         |  16 ++
 doc/src/sgml/ref/createdb.sgml                |   9 +
 doc/src/sgml/ref/initdb.sgml                  |  23 ++
 src/backend/catalog/pg_collation.c            |  18 +-
 src/backend/commands/collationcmds.c          |  93 ++++---
 src/backend/commands/dbcommands.c             |  72 +++++-
 src/backend/utils/adt/pg_locale.c             | 242 +++++++++++-------
 src/backend/utils/init/postinit.c             |  26 ++
 src/bin/initdb/Makefile                       |   2 +
 src/bin/initdb/initdb.c                       |  64 ++++-
 src/bin/initdb/t/001_initdb.pl                |  18 +-
 src/bin/pg_dump/pg_dump.c                     |  19 ++
 src/bin/pg_upgrade/check.c                    |  10 +
 src/bin/pg_upgrade/info.c                     |  18 +-
 src/bin/pg_upgrade/pg_upgrade.h               |   2 +
 src/bin/psql/describe.c                       |  23 +-
 src/bin/psql/tab-complete.c                   |   2 +-
 src/bin/scripts/Makefile                      |   2 +
 src/bin/scripts/createdb.c                    |   9 +
 src/bin/scripts/t/020_createdb.pl             |  20 +-
 src/include/catalog/pg_collation.dat          |   3 +-
 src/include/catalog/pg_collation.h            |   6 +-
 src/include/catalog/pg_database.dat           |   4 +-
 src/include/catalog/pg_database.h             |   6 +
 src/include/utils/pg_locale.h                 |   6 +
 .../regress/expected/collate.icu.utf8.out     |  10 +-
 src/test/regress/sql/collate.icu.utf8.sql     |   8 +-
 28 files changed, 572 insertions(+), 168 deletions(-)

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 7d5b0b1656..5a5779b9a3 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2384,6 +2384,15 @@ <title><structname>pg_collation</structname> 
Columns</title>
       </para></entry>
      </row>
 
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>collicucoll</structfield> <type>text</type>
+      </para>
+      <para>
+       ICU collation string
+      </para></entry>
+     </row>
+
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
        <structfield>collversion</structfield> <type>text</type>
diff --git a/doc/src/sgml/ref/create_database.sgml 
b/doc/src/sgml/ref/create_database.sgml
index f22e28dc81..403010cddf 100644
--- a/doc/src/sgml/ref/create_database.sgml
+++ b/doc/src/sgml/ref/create_database.sgml
@@ -28,6 +28,7 @@
            [ LOCALE [=] <replaceable class="parameter">locale</replaceable> ]
            [ LC_COLLATE [=] <replaceable 
class="parameter">lc_collate</replaceable> ]
            [ LC_CTYPE [=] <replaceable 
class="parameter">lc_ctype</replaceable> ]
+           [ COLLATION_PROVIDER [=] <replaceable 
class="parameter">collation_provider</replaceable> ]
            [ TABLESPACE [=] <replaceable 
class="parameter">tablespace_name</replaceable> ]
            [ ALLOW_CONNECTIONS [=] <replaceable 
class="parameter">allowconn</replaceable> ]
            [ CONNECTION LIMIT [=] <replaceable 
class="parameter">connlimit</replaceable> ]
@@ -158,6 +159,21 @@ <title>Parameters</title>
        </para>
       </listitem>
      </varlistentry>
+
+     <varlistentry>
+      <term><replaceable>collation_provider</replaceable></term>
+
+      <listitem>
+       <para>
+        Specifies the provider to use for the default collation in this
+        database.  Possible values are:
+        <literal>icu</literal>,<indexterm><primary>ICU</primary></indexterm>
+        <literal>libc</literal>.  <literal>libc</literal> is the default.  The
+        available choices depend on the operating system and build options.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><replaceable class="parameter">tablespace_name</replaceable></term>
       <listitem>
diff --git a/doc/src/sgml/ref/createdb.sgml b/doc/src/sgml/ref/createdb.sgml
index 86473455c9..4b07363fcc 100644
--- a/doc/src/sgml/ref/createdb.sgml
+++ b/doc/src/sgml/ref/createdb.sgml
@@ -83,6 +83,15 @@ <title>Options</title>
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      
<term><option>--collation-provider={<literal>libc</literal>|<literal>icu</literal>}</option></term>
+      <listitem>
+       <para>
+        Specifies the collation provider for the database's default collation.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-D <replaceable 
class="parameter">tablespace</replaceable></option></term>
       <term><option>--tablespace=<replaceable 
class="parameter">tablespace</replaceable></option></term>
diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml
index 8f71c7c962..77618d9a7a 100644
--- a/doc/src/sgml/ref/initdb.sgml
+++ b/doc/src/sgml/ref/initdb.sgml
@@ -166,6 +166,18 @@ <title>Options</title>
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      
<term><option>--collation-provider={<literal>libc</literal>|<literal>icu</literal>}</option></term>
+      <listitem>
+       <para>
+        This option sets the collation provider for databases created in the
+        new cluster.  It can be overridden in the <command>CREATE
+        DATABASE</command> command when new databases are subsequently
+        created.  The default is <literal>libc</literal>.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-D <replaceable 
class="parameter">directory</replaceable></option></term>
       <term><option>--pgdata=<replaceable 
class="parameter">directory</replaceable></option></term>
@@ -210,6 +222,17 @@ <title>Options</title>
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      
<term><option>--icu-locale=<replaceable>locale</replaceable></option></term>
+      <listitem>
+       <para>
+        Specifies the ICU locale if the ICU collation provider is used.  If
+        this is not specified, the value from the <option>--locale</option>
+        option is used.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="app-initdb-data-checksums" xreflabel="data checksums">
       <term><option>-k</option></term>
       <term><option>--data-checksums</option></term>
diff --git a/src/backend/catalog/pg_collation.c 
b/src/backend/catalog/pg_collation.c
index bfc02d3038..5596b9be5a 100644
--- a/src/backend/catalog/pg_collation.c
+++ b/src/backend/catalog/pg_collation.c
@@ -49,6 +49,7 @@ CollationCreate(const char *collname, Oid collnamespace,
                                bool collisdeterministic,
                                int32 collencoding,
                                const char *collcollate, const char *collctype,
+                               const char *collicucoll,
                                const char *collversion,
                                bool if_not_exists,
                                bool quiet)
@@ -66,8 +67,7 @@ CollationCreate(const char *collname, Oid collnamespace,
        AssertArg(collname);
        AssertArg(collnamespace);
        AssertArg(collowner);
-       AssertArg(collcollate);
-       AssertArg(collctype);
+       AssertArg((collcollate && collctype) || collicucoll);
 
        /*
         * Make sure there is no existing collation of same name & encoding.
@@ -161,8 +161,18 @@ CollationCreate(const char *collname, Oid collnamespace,
        values[Anum_pg_collation_collprovider - 1] = CharGetDatum(collprovider);
        values[Anum_pg_collation_collisdeterministic - 1] = 
BoolGetDatum(collisdeterministic);
        values[Anum_pg_collation_collencoding - 1] = 
Int32GetDatum(collencoding);
-       values[Anum_pg_collation_collcollate - 1] = 
CStringGetTextDatum(collcollate);
-       values[Anum_pg_collation_collctype - 1] = 
CStringGetTextDatum(collctype);
+       if (collcollate)
+               values[Anum_pg_collation_collcollate - 1] = 
CStringGetTextDatum(collcollate);
+       else
+               nulls[Anum_pg_collation_collcollate - 1] = true;
+       if (collctype)
+               values[Anum_pg_collation_collctype - 1] = 
CStringGetTextDatum(collctype);
+       else
+               nulls[Anum_pg_collation_collctype - 1] = true;
+       if (collicucoll)
+               values[Anum_pg_collation_collicucoll - 1] = 
CStringGetTextDatum(collicucoll);
+       else
+               nulls[Anum_pg_collation_collicucoll - 1] = true;
        if (collversion)
                values[Anum_pg_collation_collversion - 1] = 
CStringGetTextDatum(collversion);
        else
diff --git a/src/backend/commands/collationcmds.c 
b/src/backend/commands/collationcmds.c
index 12fc2316f9..a9b50f5d2b 100644
--- a/src/backend/commands/collationcmds.c
+++ b/src/backend/commands/collationcmds.c
@@ -65,6 +65,7 @@ DefineCollation(ParseState *pstate, List *names, List 
*parameters, bool if_not_e
        DefElem    *versionEl = NULL;
        char       *collcollate = NULL;
        char       *collctype = NULL;
+       char       *collicucoll = NULL;
        char       *collproviderstr = NULL;
        bool            collisdeterministic = true;
        int                     collencoding = 0;
@@ -153,6 +154,12 @@ DefineCollation(ParseState *pstate, List *names, List 
*parameters, bool if_not_e
                else
                        collctype = NULL;
 
+               datum = SysCacheGetAttr(COLLOID, tp, 
Anum_pg_collation_collicucoll, &isnull);
+               if (!isnull)
+                       collicucoll = TextDatumGetCString(datum);
+               else
+                       collicucoll = NULL;
+
                ReleaseSysCache(tp);
 
                /*
@@ -168,18 +175,6 @@ DefineCollation(ParseState *pstate, List *names, List 
*parameters, bool if_not_e
                                         errmsg("collation \"default\" cannot 
be copied")));
        }
 
-       if (localeEl)
-       {
-               collcollate = defGetString(localeEl);
-               collctype = defGetString(localeEl);
-       }
-
-       if (lccollateEl)
-               collcollate = defGetString(lccollateEl);
-
-       if (lcctypeEl)
-               collctype = defGetString(lcctypeEl);
-
        if (providerEl)
                collproviderstr = defGetString(providerEl);
 
@@ -204,15 +199,43 @@ DefineCollation(ParseState *pstate, List *names, List 
*parameters, bool if_not_e
        else if (!fromEl)
                collprovider = COLLPROVIDER_LIBC;
 
-       if (!collcollate)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-                                errmsg("parameter \"lc_collate\" must be 
specified")));
+       if (localeEl)
+       {
+               if (collprovider == COLLPROVIDER_LIBC)
+               {
+                       collcollate = defGetString(localeEl);
+                       collctype = defGetString(localeEl);
+               }
+               else
+                       collicucoll = defGetString(localeEl);
+       }
 
-       if (!collctype)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-                                errmsg("parameter \"lc_ctype\" must be 
specified")));
+       if (lccollateEl)
+               collcollate = defGetString(lccollateEl);
+
+       if (lcctypeEl)
+               collctype = defGetString(lcctypeEl);
+
+       if (collprovider == COLLPROVIDER_LIBC)
+       {
+               if (!collcollate)
+                       ereport(ERROR,
+                                       
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                        errmsg("parameter \"lc_collate\" must 
be specified")));
+
+               if (!collctype)
+                       ereport(ERROR,
+                                       
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                        errmsg("parameter \"lc_ctype\" must be 
specified")));
+       }
+
+       if (collprovider == COLLPROVIDER_ICU)
+       {
+               if (!collicucoll)
+                       ereport(ERROR,
+                                       
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                        errmsg("parameter \"locale\" must be 
specified")));
+       }
 
        /*
         * Nondeterministic collations are currently only supported with ICU
@@ -255,7 +278,7 @@ DefineCollation(ParseState *pstate, List *names, List 
*parameters, bool if_not_e
        }
 
        if (!collversion)
-               collversion = get_collation_actual_version(collprovider, 
collcollate);
+               collversion = get_collation_actual_version(collprovider, 
collprovider == COLLPROVIDER_ICU ? collicucoll : collcollate);
 
        newoid = CollationCreate(collName,
                                                         collNamespace,
@@ -265,6 +288,7 @@ DefineCollation(ParseState *pstate, List *names, List 
*parameters, bool if_not_e
                                                         collencoding,
                                                         collcollate,
                                                         collctype,
+                                                        collicucoll,
                                                         collversion,
                                                         if_not_exists,
                                                         false);        /* not 
quiet */
@@ -347,7 +371,7 @@ AlterCollation(AlterCollationStmt *stmt)
        datum = SysCacheGetAttr(COLLOID, tup, Anum_pg_collation_collversion, 
&isnull);
        oldversion = isnull ? NULL : TextDatumGetCString(datum);
 
-       datum = SysCacheGetAttr(COLLOID, tup, Anum_pg_collation_collcollate, 
&isnull);
+       datum = SysCacheGetAttr(COLLOID, tup, collForm->collprovider == 
COLLPROVIDER_ICU ? Anum_pg_collation_collicucoll : 
Anum_pg_collation_collcollate, &isnull);
        Assert(!isnull);
        newversion = get_collation_actual_version(collForm->collprovider, 
TextDatumGetCString(datum));
 
@@ -409,9 +433,14 @@ pg_collation_actual_version(PG_FUNCTION_ARGS)
 
        collprovider = ((Form_pg_collation) GETSTRUCT(tp))->collprovider;
 
-       datum = SysCacheGetAttr(COLLOID, tp, Anum_pg_collation_collcollate, 
&isnull);
-       Assert(!isnull);
-       version = get_collation_actual_version(collprovider, 
TextDatumGetCString(datum));
+       if (collprovider != COLLPROVIDER_DEFAULT)
+       {
+               datum = SysCacheGetAttr(COLLOID, tp, collprovider == 
COLLPROVIDER_ICU ? Anum_pg_collation_collicucoll : 
Anum_pg_collation_collcollate, &isnull);
+               Assert(!isnull);
+               version = get_collation_actual_version(collprovider, 
TextDatumGetCString(datum));
+       }
+       else
+               version = NULL;
 
        ReleaseSysCache(tp);
 
@@ -638,7 +667,7 @@ pg_import_system_collations(PG_FUNCTION_ARGS)
                         */
                        collid = CollationCreate(localebuf, nspid, GetUserId(),
                                                                         
COLLPROVIDER_LIBC, true, enc,
-                                                                        
localebuf, localebuf,
+                                                                        
localebuf, localebuf, NULL,
                                                                         
get_collation_actual_version(COLLPROVIDER_LIBC, localebuf),
                                                                         true, 
true);
                        if (OidIsValid(collid))
@@ -699,7 +728,7 @@ pg_import_system_collations(PG_FUNCTION_ARGS)
 
                        collid = CollationCreate(alias, nspid, GetUserId(),
                                                                         
COLLPROVIDER_LIBC, true, enc,
-                                                                        
locale, locale,
+                                                                        
locale, locale, NULL,
                                                                         
get_collation_actual_version(COLLPROVIDER_LIBC, locale),
                                                                         true, 
true);
                        if (OidIsValid(collid))
@@ -740,7 +769,7 @@ pg_import_system_collations(PG_FUNCTION_ARGS)
                        const char *name;
                        char       *langtag;
                        char       *icucomment;
-                       const char *collcollate;
+                       const char *icucollstr;
                        Oid                     collid;
 
                        if (i == -1)
@@ -749,20 +778,20 @@ pg_import_system_collations(PG_FUNCTION_ARGS)
                                name = uloc_getAvailable(i);
 
                        langtag = get_icu_language_tag(name);
-                       collcollate = U_ICU_VERSION_MAJOR_NUM >= 54 ? langtag : 
name;
+                       icucollstr = U_ICU_VERSION_MAJOR_NUM >= 54 ? langtag : 
name;
 
                        /*
                         * Be paranoid about not allowing any non-ASCII strings 
into
                         * pg_collation
                         */
-                       if (!pg_is_ascii(langtag) || !pg_is_ascii(collcollate))
+                       if (!pg_is_ascii(langtag) || !pg_is_ascii(icucollstr))
                                continue;
 
                        collid = CollationCreate(psprintf("%s-x-icu", langtag),
                                                                         nspid, 
GetUserId(),
                                                                         
COLLPROVIDER_ICU, true, -1,
-                                                                        
collcollate, collcollate,
-                                                                        
get_collation_actual_version(COLLPROVIDER_ICU, collcollate),
+                                                                        NULL, 
NULL, icucollstr,
+                                                                        
get_collation_actual_version(COLLPROVIDER_ICU, icucollstr),
                                                                         true, 
true);
                        if (OidIsValid(collid))
                        {
diff --git a/src/backend/commands/dbcommands.c 
b/src/backend/commands/dbcommands.c
index e673138cbd..df7087cd7b 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -36,6 +36,7 @@
 #include "catalog/indexing.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_authid.h"
+#include "catalog/pg_collation.h"
 #include "catalog/pg_database.h"
 #include "catalog/pg_db_role_setting.h"
 #include "catalog/pg_subscription.h"
@@ -85,7 +86,8 @@ static bool get_db_info(const char *name, LOCKMODE lockmode,
                                                Oid *dbIdP, Oid *ownerIdP,
                                                int *encodingP, bool 
*dbIsTemplateP, bool *dbAllowConnP,
                                                TransactionId *dbFrozenXidP, 
MultiXactId *dbMinMultiP,
-                                               Oid *dbTablespace, char 
**dbCollate, char **dbCtype);
+                                               Oid *dbTablespace, char 
**dbCollate, char **dbCtype, char **dbIcucoll,
+                                               char *dbCollProvider);
 static bool have_createdb_privilege(void);
 static void remove_dbtablespaces(Oid db_id);
 static bool check_db_file_conflict(Oid db_id);
@@ -105,6 +107,8 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
        int                     src_encoding = -1;
        char       *src_collate = NULL;
        char       *src_ctype = NULL;
+       char       *src_icucoll = NULL;
+       char            src_collprovider;
        bool            src_istemplate;
        bool            src_allowconn;
        TransactionId src_frozenxid = InvalidTransactionId;
@@ -125,6 +129,7 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
        DefElem    *dlocale = NULL;
        DefElem    *dcollate = NULL;
        DefElem    *dctype = NULL;
+       DefElem    *dcollprovider = NULL;
        DefElem    *distemplate = NULL;
        DefElem    *dallowconnections = NULL;
        DefElem    *dconnlimit = NULL;
@@ -133,6 +138,8 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
        const char *dbtemplate = NULL;
        char       *dbcollate = NULL;
        char       *dbctype = NULL;
+       char       *dbicucoll = NULL;
+       char            dbcollprovider = '\0';
        char       *canonname;
        int                     encoding = -1;
        bool            dbistemplate = false;
@@ -189,6 +196,15 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
                                errorConflictingDefElem(defel, pstate);
                        dctype = defel;
                }
+               else if (strcmp(defel->defname, "collation_provider") == 0)
+               {
+                       if (dcollprovider)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                                errmsg("conflicting or 
redundant options"),
+                                                parser_errposition(pstate, 
defel->location)));
+                       dcollprovider = defel;
+               }
                else if (strcmp(defel->defname, "is_template") == 0)
                {
                        if (distemplate)
@@ -246,12 +262,6 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
                                         parser_errposition(pstate, 
defel->location)));
        }
 
-       if (dlocale && (dcollate || dctype))
-               ereport(ERROR,
-                               (errcode(ERRCODE_SYNTAX_ERROR),
-                                errmsg("conflicting or redundant options"),
-                                errdetail("LOCALE cannot be specified together 
with LC_COLLATE or LC_CTYPE.")));
-
        if (downer && downer->arg)
                dbowner = defGetString(downer);
        if (dtemplate && dtemplate->arg)
@@ -288,11 +298,29 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
        {
                dbcollate = defGetString(dlocale);
                dbctype = defGetString(dlocale);
+               dbicucoll = defGetString(dlocale);
        }
        if (dcollate && dcollate->arg)
                dbcollate = defGetString(dcollate);
        if (dctype && dctype->arg)
                dbctype = defGetString(dctype);
+       if (dcollprovider && dcollprovider->arg)
+       {
+               char       *collproviderstr = defGetString(dcollprovider);
+
+#ifdef USE_ICU
+               if (pg_strcasecmp(collproviderstr, "icu") == 0)
+                       dbcollprovider = COLLPROVIDER_ICU;
+               else
+#endif
+               if (pg_strcasecmp(collproviderstr, "libc") == 0)
+                       dbcollprovider = COLLPROVIDER_LIBC;
+               else
+                       ereport(ERROR,
+                                       
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                        errmsg("unrecognized collation 
provider: %s",
+                                                       collproviderstr)));
+       }
        if (distemplate && distemplate->arg)
                dbistemplate = defGetBoolean(distemplate);
        if (dallowconnections && dallowconnections->arg)
@@ -342,7 +370,7 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
                                         &src_dboid, &src_owner, &src_encoding,
                                         &src_istemplate, &src_allowconn,
                                         &src_frozenxid, &src_minmxid, 
&src_deftablespace,
-                                        &src_collate, &src_ctype))
+                                        &src_collate, &src_ctype, 
&src_icucoll, &src_collprovider))
                ereport(ERROR,
                                (errcode(ERRCODE_UNDEFINED_DATABASE),
                                 errmsg("template database \"%s\" does not 
exist",
@@ -368,6 +396,10 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
                dbcollate = src_collate;
        if (dbctype == NULL)
                dbctype = src_ctype;
+       if (dbicucoll == NULL)
+               dbicucoll = src_icucoll;
+       if (dbcollprovider == '\0')
+               dbcollprovider = src_collprovider;
 
        /* Some encodings are client only */
        if (!PG_VALID_BE_ENCODING(encoding))
@@ -570,6 +602,7 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
                DirectFunctionCall1(namein, CStringGetDatum(dbname));
        new_record[Anum_pg_database_datdba - 1] = ObjectIdGetDatum(datdba);
        new_record[Anum_pg_database_encoding - 1] = Int32GetDatum(encoding);
+       new_record[Anum_pg_database_datcollprovider - 1] = 
CharGetDatum(dbcollprovider);
        new_record[Anum_pg_database_datistemplate - 1] = 
BoolGetDatum(dbistemplate);
        new_record[Anum_pg_database_datallowconn - 1] = 
BoolGetDatum(dballowconnections);
        new_record[Anum_pg_database_datconnlimit - 1] = 
Int32GetDatum(dbconnlimit);
@@ -578,6 +611,10 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
        new_record[Anum_pg_database_dattablespace - 1] = 
ObjectIdGetDatum(dst_deftablespace);
        new_record[Anum_pg_database_datcollate - 1] = 
CStringGetTextDatum(dbcollate);
        new_record[Anum_pg_database_datctype - 1] = 
CStringGetTextDatum(dbctype);
+       if (dbicucoll)
+               new_record[Anum_pg_database_daticucoll - 1] = 
CStringGetTextDatum(dbicucoll);
+       else
+               new_record_nulls[Anum_pg_database_daticucoll] = true;
 
        /*
         * We deliberately set datacl to default (NULL), rather than copying it
@@ -844,7 +881,7 @@ dropdb(const char *dbname, bool missing_ok, bool force)
        pgdbrel = table_open(DatabaseRelationId, RowExclusiveLock);
 
        if (!get_db_info(dbname, AccessExclusiveLock, &db_id, NULL, NULL,
-                                        &db_istemplate, NULL, NULL, NULL, 
NULL, NULL, NULL))
+                                        &db_istemplate, NULL, NULL, NULL, 
NULL, NULL, NULL, NULL, NULL))
        {
                if (!missing_ok)
                {
@@ -1043,7 +1080,7 @@ RenameDatabase(const char *oldname, const char *newname)
        rel = table_open(DatabaseRelationId, RowExclusiveLock);
 
        if (!get_db_info(oldname, AccessExclusiveLock, &db_id, NULL, NULL,
-                                        NULL, NULL, NULL, NULL, NULL, NULL, 
NULL))
+                                        NULL, NULL, NULL, NULL, NULL, NULL, 
NULL, NULL, NULL))
                ereport(ERROR,
                                (errcode(ERRCODE_UNDEFINED_DATABASE),
                                 errmsg("database \"%s\" does not exist", 
oldname)));
@@ -1156,7 +1193,7 @@ movedb(const char *dbname, const char *tblspcname)
        pgdbrel = table_open(DatabaseRelationId, RowExclusiveLock);
 
        if (!get_db_info(dbname, AccessExclusiveLock, &db_id, NULL, NULL,
-                                        NULL, NULL, NULL, NULL, 
&src_tblspcoid, NULL, NULL))
+                                        NULL, NULL, NULL, NULL, 
&src_tblspcoid, NULL, NULL, NULL, NULL))
                ereport(ERROR,
                                (errcode(ERRCODE_UNDEFINED_DATABASE),
                                 errmsg("database \"%s\" does not exist", 
dbname)));
@@ -1800,7 +1837,8 @@ get_db_info(const char *name, LOCKMODE lockmode,
                        Oid *dbIdP, Oid *ownerIdP,
                        int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
                        TransactionId *dbFrozenXidP, MultiXactId *dbMinMultiP,
-                       Oid *dbTablespace, char **dbCollate, char **dbCtype)
+                       Oid *dbTablespace, char **dbCollate, char **dbCtype, 
char **dbIcucoll,
+                       char *dbCollProvider)
 {
        bool            result = false;
        Relation        relation;
@@ -1893,6 +1931,8 @@ get_db_info(const char *name, LOCKMODE lockmode,
                                if (dbTablespace)
                                        *dbTablespace = dbform->dattablespace;
                                /* default locale settings for this database */
+                               if (dbCollProvider)
+                                       *dbCollProvider = 
dbform->datcollprovider;
                                if (dbCollate)
                                {
                                        datum = SysCacheGetAttr(DATABASEOID, 
tuple, Anum_pg_database_datcollate, &isnull);
@@ -1905,6 +1945,14 @@ get_db_info(const char *name, LOCKMODE lockmode,
                                        Assert(!isnull);
                                        *dbCtype = TextDatumGetCString(datum);
                                }
+                               if (dbIcucoll)
+                               {
+                                       datum = SysCacheGetAttr(DATABASEOID, 
tuple, Anum_pg_database_daticucoll, &isnull);
+                                       if (isnull)
+                                               *dbIcucoll = NULL;
+                                       else
+                                               *dbIcucoll = 
TextDatumGetCString(datum);
+                               }
                                ReleaseSysCache(tuple);
                                result = true;
                                break;
diff --git a/src/backend/utils/adt/pg_locale.c 
b/src/backend/utils/adt/pg_locale.c
index aefa0818d0..0334f66a23 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -1288,26 +1288,37 @@ lookup_collation_cache(Oid collation, bool set_flags)
        {
                /* Attempt to set the flags */
                HeapTuple       tp;
-               Datum           datum;
-               bool            isnull;
-               const char *collcollate;
-               const char *collctype;
+               Form_pg_collation collform;
 
                tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collation));
                if (!HeapTupleIsValid(tp))
                        elog(ERROR, "cache lookup failed for collation %u", 
collation);
+               collform = (Form_pg_collation) GETSTRUCT(tp);
 
-               datum = SysCacheGetAttr(COLLOID, tp, 
Anum_pg_collation_collcollate, &isnull);
-               Assert(!isnull);
-               collcollate = TextDatumGetCString(datum);
-               datum = SysCacheGetAttr(COLLOID, tp, 
Anum_pg_collation_collctype, &isnull);
-               Assert(!isnull);
-               collctype = TextDatumGetCString(datum);
-
-               cache_entry->collate_is_c = ((strcmp(collcollate, "C") == 0) ||
-                                                                        
(strcmp(collcollate, "POSIX") == 0));
-               cache_entry->ctype_is_c = ((strcmp(collctype, "C") == 0) ||
-                                                                  
(strcmp(collctype, "POSIX") == 0));
+               if (collform->collprovider == COLLPROVIDER_LIBC)
+               {
+                       Datum           datum;
+                       bool            isnull;
+                       const char *collcollate;
+                       const char *collctype;
+
+                       datum = SysCacheGetAttr(COLLOID, tp, 
Anum_pg_collation_collcollate, &isnull);
+                       Assert(!isnull);
+                       collcollate = TextDatumGetCString(datum);
+                       datum = SysCacheGetAttr(COLLOID, tp, 
Anum_pg_collation_collctype, &isnull);
+                       Assert(!isnull);
+                       collctype = TextDatumGetCString(datum);
+
+                       cache_entry->collate_is_c = ((strcmp(collcollate, "C") 
== 0) ||
+                                                                               
 (strcmp(collcollate, "POSIX") == 0));
+                       cache_entry->ctype_is_c = ((strcmp(collctype, "C") == 
0) ||
+                                                                          
(strcmp(collctype, "POSIX") == 0));
+               }
+               else
+               {
+                       cache_entry->collate_is_c = false;
+                       cache_entry->ctype_is_c = false;
+               }
 
                cache_entry->flags_valid = true;
 
@@ -1340,6 +1351,9 @@ lc_collate_is_c(Oid collation)
                static int      result = -1;
                char       *localeptr;
 
+               if (default_locale.provider == COLLPROVIDER_ICU)
+                       return false;
+
                if (result >= 0)
                        return (bool) result;
                localeptr = setlocale(LC_COLLATE, NULL);
@@ -1390,6 +1404,9 @@ lc_ctype_is_c(Oid collation)
                static int      result = -1;
                char       *localeptr;
 
+               if (default_locale.provider == COLLPROVIDER_ICU)
+                       return false;
+
                if (result >= 0)
                        return (bool) result;
                localeptr = setlocale(LC_CTYPE, NULL);
@@ -1418,6 +1435,87 @@ lc_ctype_is_c(Oid collation)
        return (lookup_collation_cache(collation, true))->ctype_is_c;
 }
 
+struct pg_locale_struct default_locale;
+
+void
+make_icu_collator(const char *icucollstr,
+                                 struct pg_locale_struct *resultp)
+{
+#ifdef USE_ICU
+       UCollator  *collator;
+       UErrorCode      status;
+
+       status = U_ZERO_ERROR;
+       collator = ucol_open(icucollstr, &status);
+       if (U_FAILURE(status))
+               ereport(ERROR,
+                               (errmsg("could not open collator for locale 
\"%s\": %s",
+                                               icucollstr, 
u_errorName(status))));
+
+       if (U_ICU_VERSION_MAJOR_NUM < 54)
+               icu_set_collation_attributes(collator, icucollstr);
+
+       /* We will leak this string if we get an error below :-( */
+       resultp->info.icu.locale = MemoryContextStrdup(TopMemoryContext, 
icucollstr);
+       resultp->info.icu.ucol = collator;
+#else                                                  /* not USE_ICU */
+       /* could get here if a collation was created by a build with ICU */
+       ereport(ERROR,
+                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                        errmsg("ICU is not supported in this build"), \
+                        errhint("You need to rebuild PostgreSQL using %s.", 
"--with-icu")));
+#endif                                                 /* not USE_ICU */
+}
+
+void
+check_collation_version(HeapTuple colltuple)
+{
+       Form_pg_collation collform;
+       Datum           datum;
+       bool            isnull;
+
+       collform = (Form_pg_collation) GETSTRUCT(colltuple);
+
+       datum = SysCacheGetAttr(COLLOID, colltuple, 
Anum_pg_collation_collversion,
+                                                       &isnull);
+       if (!isnull)
+       {
+               char       *actual_versionstr;
+               char       *collversionstr;
+
+               collversionstr = TextDatumGetCString(datum);
+
+               datum = SysCacheGetAttr(COLLOID, colltuple, 
collform->collprovider == COLLPROVIDER_ICU ? Anum_pg_collation_collicucoll : 
Anum_pg_collation_collcollate, &isnull);
+               Assert(!isnull);
+
+               actual_versionstr = 
get_collation_actual_version(collform->collprovider,
+                                                                               
                                 TextDatumGetCString(datum));
+               if (!actual_versionstr)
+               {
+                       /*
+                        * This could happen when specifying a version in CREATE
+                        * COLLATION for a libc locale, or manually creating a 
mess in
+                        * the catalogs.
+                        */
+                       ereport(ERROR,
+                                       (errmsg("collation \"%s\" has no actual 
version, but a version was specified",
+                                                       
NameStr(collform->collname))));
+               }
+
+               if (strcmp(actual_versionstr, collversionstr) != 0)
+                       ereport(WARNING,
+                                       (errmsg("collation \"%s\" has version 
mismatch",
+                                                       
NameStr(collform->collname)),
+                                        errdetail("The collation in the 
database was created using version %s, "
+                                                          "but the operating 
system provides version %s.",
+                                                          collversionstr, 
actual_versionstr),
+                                        errhint("Rebuild all objects affected 
by this collation and run "
+                                                        "ALTER COLLATION %s 
REFRESH VERSION, "
+                                                        "or build PostgreSQL 
with the right library version.",
+                                                        
quote_qualified_identifier(get_namespace_name(collform->collnamespace),
+                                                                               
                                NameStr(collform->collname)))));
+       }
+}
 
 /* simple subroutine for reporting errors from newlocale() */
 #ifdef HAVE_LOCALE_T
@@ -1475,7 +1573,12 @@ pg_newlocale_from_collation(Oid collid)
        Assert(OidIsValid(collid));
 
        if (collid == DEFAULT_COLLATION_OID)
-               return (pg_locale_t) 0;
+       {
+               if (default_locale.provider == COLLPROVIDER_ICU)
+                       return &default_locale;
+               else
+                       return (pg_locale_t) 0;
+       }
 
        cache_entry = lookup_collation_cache(collid, false);
 
@@ -1484,25 +1587,14 @@ pg_newlocale_from_collation(Oid collid)
                /* We haven't computed this yet in this session, so do it */
                HeapTuple       tp;
                Form_pg_collation collform;
-               const char *collcollate;
-               const char *collctype pg_attribute_unused();
                struct pg_locale_struct result;
                pg_locale_t resultp;
-               Datum           datum;
-               bool            isnull;
 
                tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
                if (!HeapTupleIsValid(tp))
                        elog(ERROR, "cache lookup failed for collation %u", 
collid);
                collform = (Form_pg_collation) GETSTRUCT(tp);
 
-               datum = SysCacheGetAttr(COLLOID, tp, 
Anum_pg_collation_collcollate, &isnull);
-               Assert(!isnull);
-               collcollate = TextDatumGetCString(datum);
-               datum = SysCacheGetAttr(COLLOID, tp, 
Anum_pg_collation_collctype, &isnull);
-               Assert(!isnull);
-               collctype = TextDatumGetCString(datum);
-
                /* We'll fill in the result struct locally before allocating 
memory */
                memset(&result, 0, sizeof(result));
                result.provider = collform->collprovider;
@@ -1511,8 +1603,19 @@ pg_newlocale_from_collation(Oid collid)
                if (collform->collprovider == COLLPROVIDER_LIBC)
                {
 #ifdef HAVE_LOCALE_T
+                       Datum           datum;
+                       bool            isnull;
+                       const char *collcollate;
+                       const char *collctype pg_attribute_unused();
                        locale_t        loc;
 
+                       datum = SysCacheGetAttr(COLLOID, tp, 
Anum_pg_collation_collcollate, &isnull);
+                       Assert(!isnull);
+                       collcollate = TextDatumGetCString(datum);
+                       datum = SysCacheGetAttr(COLLOID, tp, 
Anum_pg_collation_collctype, &isnull);
+                       Assert(!isnull);
+                       collctype = TextDatumGetCString(datum);
+
                        if (strcmp(collcollate, collctype) == 0)
                        {
                                /* Normal case where they're the same */
@@ -1563,73 +1666,17 @@ pg_newlocale_from_collation(Oid collid)
                }
                else if (collform->collprovider == COLLPROVIDER_ICU)
                {
-#ifdef USE_ICU
-                       UCollator  *collator;
-                       UErrorCode      status;
-
-                       if (strcmp(collcollate, collctype) != 0)
-                               ereport(ERROR,
-                                               
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                errmsg("collations with 
different collate and ctype values are not supported by ICU")));
-
-                       status = U_ZERO_ERROR;
-                       collator = ucol_open(collcollate, &status);
-                       if (U_FAILURE(status))
-                               ereport(ERROR,
-                                               (errmsg("could not open 
collator for locale \"%s\": %s",
-                                                               collcollate, 
u_errorName(status))));
-
-                       if (U_ICU_VERSION_MAJOR_NUM < 54)
-                               icu_set_collation_attributes(collator, 
collcollate);
-
-                       /* We will leak this string if we get an error below 
:-( */
-                       result.info.icu.locale = 
MemoryContextStrdup(TopMemoryContext,
-                                                                               
                                 collcollate);
-                       result.info.icu.ucol = collator;
-#else                                                  /* not USE_ICU */
-                       /* could get here if a collation was created by a build 
with ICU */
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                        errmsg("ICU is not supported in this 
build"), \
-                                        errhint("You need to rebuild 
PostgreSQL using %s.", "--with-icu")));
-#endif                                                 /* not USE_ICU */
+                       Datum           datum;
+                       bool            isnull;
+                       const char *icucollstr;;
+
+                       datum = SysCacheGetAttr(COLLOID, tp, 
Anum_pg_collation_collicucoll, &isnull);
+                       Assert(!isnull);
+                       icucollstr = TextDatumGetCString(datum);
+                       make_icu_collator(icucollstr, &result);
                }
 
-               datum = SysCacheGetAttr(COLLOID, tp, 
Anum_pg_collation_collversion,
-                                                                         
&isnull);
-               if (!isnull)
-               {
-                       char       *actual_versionstr;
-                       char       *collversionstr;
-
-                       collversionstr = TextDatumGetCString(datum);
-
-                       actual_versionstr = 
get_collation_actual_version(collform->collprovider, collcollate);
-                       if (!actual_versionstr)
-                       {
-                               /*
-                                * This could happen when specifying a version 
in CREATE
-                                * COLLATION for a libc locale, or manually 
creating a mess in
-                                * the catalogs.
-                                */
-                               ereport(ERROR,
-                                               (errmsg("collation \"%s\" has 
no actual version, but a version was specified",
-                                                               
NameStr(collform->collname))));
-                       }
-
-                       if (strcmp(actual_versionstr, collversionstr) != 0)
-                               ereport(WARNING,
-                                               (errmsg("collation \"%s\" has 
version mismatch",
-                                                               
NameStr(collform->collname)),
-                                                errdetail("The collation in 
the database was created using version %s, "
-                                                                  "but the 
operating system provides version %s.",
-                                                                  
collversionstr, actual_versionstr),
-                                                errhint("Rebuild all objects 
affected by this collation and run "
-                                                                "ALTER 
COLLATION %s REFRESH VERSION, "
-                                                                "or build 
PostgreSQL with the right library version.",
-                                                                
quote_qualified_identifier(get_namespace_name(collform->collnamespace),
-                                                                               
                                        NameStr(collform->collname)))));
-               }
+               check_collation_version(tp);
 
                ReleaseSysCache(tp);
 
@@ -1652,6 +1699,17 @@ get_collation_actual_version(char collprovider, const 
char *collcollate)
 {
        char       *collversion = NULL;
 
+       if (collprovider == COLLPROVIDER_DEFAULT)
+       {
+#ifdef USE_ICU
+               if (default_locale.provider == COLLPROVIDER_ICU)
+                       collversion = 
get_collation_actual_version(default_locale.provider,
+                                                                               
                           default_locale.info.icu.locale);
+               else
+#endif
+                       collversion = NULL;
+       }
+       else
 #ifdef USE_ICU
        if (collprovider == COLLPROVIDER_ICU)
        {
diff --git a/src/backend/utils/init/postinit.c 
b/src/backend/utils/init/postinit.c
index 5b9ed2f6f5..ffe60673d5 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -31,6 +31,7 @@
 #include "catalog/catalog.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_authid.h"
+#include "catalog/pg_collation.h"
 #include "catalog/pg_database.h"
 #include "catalog/pg_db_role_setting.h"
 #include "catalog/pg_tablespace.h"
@@ -414,6 +415,31 @@ CheckMyDatabase(const char *name, bool am_superuser, bool 
override_allow_connect
                                                   " which is not recognized by 
setlocale().", ctype),
                                 errhint("Recreate the database with another 
locale or install the missing locale.")));
 
+       if (dbform->datcollprovider == COLLPROVIDER_ICU)
+       {
+               datum = SysCacheGetAttr(DATABASEOID, tup, 
Anum_pg_database_daticucoll, &isnull);
+               Assert(!isnull);
+               make_icu_collator(TextDatumGetCString(datum), &default_locale);
+       }
+
+       default_locale.provider = dbform->datcollprovider;
+       /*
+        * Default locale is currently always deterministic.  Nondeterministic
+        * locales currently don't support pattern matching, which would break a
+        * lot of things if applied globally.
+        */
+       default_locale.deterministic = true;
+
+       {
+               HeapTuple       tp;
+
+               tp = SearchSysCache1(COLLOID, 
ObjectIdGetDatum(DEFAULT_COLLATION_OID));
+               if (!HeapTupleIsValid(tp))
+                       elog(ERROR, "cache lookup failed for collation %u", 
DEFAULT_COLLATION_OID);
+               check_collation_version(tp);
+               ReleaseSysCache(tp);
+       }
+
        /* Make the locale settings visible as GUC variables, too */
        SetConfigOption("lc_collate", collate, PGC_INTERNAL, PGC_S_OVERRIDE);
        SetConfigOption("lc_ctype", ctype, PGC_INTERNAL, PGC_S_OVERRIDE);
diff --git a/src/bin/initdb/Makefile b/src/bin/initdb/Makefile
index eba282267a..b0dd13dfbd 100644
--- a/src/bin/initdb/Makefile
+++ b/src/bin/initdb/Makefile
@@ -62,6 +62,8 @@ clean distclean maintainer-clean:
 # ensure that changes in datadir propagate into object file
 initdb.o: initdb.c $(top_builddir)/src/Makefile.global
 
+export with_icu
+
 check:
        $(prove_check)
 
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d78e8e67b8..64880038ca 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -132,6 +132,8 @@ static char *lc_monetary = NULL;
 static char *lc_numeric = NULL;
 static char *lc_time = NULL;
 static char *lc_messages = NULL;
+static char collation_provider[] = {COLLPROVIDER_LIBC, '\0'};
+static char *icu_locale = NULL;
 static const char *default_text_search_config = NULL;
 static char *username = NULL;
 static bool pwprompt = false;
@@ -1405,6 +1407,12 @@ bootstrap_template1(void)
        bki_lines = replace_token(bki_lines, "LC_CTYPE",
                                                          
escape_quotes_bki(lc_ctype));
 
+       bki_lines = replace_token(bki_lines, "ICUCOLL",
+                                                         
escape_quotes_bki(collation_provider[0] == COLLPROVIDER_ICU ? icu_locale : 
"_null_"));
+
+       bki_lines = replace_token(bki_lines, "COLLPROVIDER",
+                                                         collation_provider);
+
        /* Also ensure backend isn't confused by this environment var: */
        unsetenv("PGCLIENTENCODING");
 
@@ -1587,6 +1595,12 @@ setup_description(FILE *cmdfd)
 static void
 setup_collation(FILE *cmdfd)
 {
+       /*
+        * Set version of the default collation.
+        */
+       PG_CMD_PRINTF("UPDATE pg_collation SET collversion = 
pg_collation_actual_version(oid) WHERE oid = %d;\n\n",
+                                 DEFAULT_COLLATION_OID);
+
        /*
         * Add an SQL-standard name.  We don't want to pin this, so it doesn't 
go
         * in pg_collation.h.  But add it before reading system collations, so
@@ -1854,9 +1868,6 @@ make_template0(FILE *cmdfd)
         * the new cluster should be the result of a fresh initdb.)
         */
        static const char *const template0_setup[] = {
-               "CREATE DATABASE template0 IS_TEMPLATE = true ALLOW_CONNECTIONS 
= false OID = "
-               CppAsString2(Template0ObjectId) ";\n\n",
-
                /*
                 * Explicitly revoke public create-schema and create-temp-table
                 * privileges in template1 and template0; else the latter would 
be on
@@ -1874,6 +1885,10 @@ make_template0(FILE *cmdfd)
                NULL
        };
 
+       PG_CMD_PRINTF("CREATE DATABASE template0 IS_TEMPLATE = true 
ALLOW_CONNECTIONS = false OID = "
+                                 CppAsString2(Template0ObjectId) " 
COLLATION_PROVIDER = %s;\n\n",
+                                 collation_provider[0] == COLLPROVIDER_ICU ? 
"icu" : "libc");
+
        for (line = template0_setup; *line; line++)
                PG_CMD_PUTS(*line);
 }
@@ -2147,13 +2162,14 @@ setlocales(void)
                        lc_monetary = locale;
                if (!lc_messages)
                        lc_messages = locale;
+               if (!icu_locale)
+                       icu_locale = locale;
        }
 
        /*
         * canonicalize locale names, and obtain any missing values from our
         * current environment
         */
-
        check_locale_name(LC_CTYPE, lc_ctype, &canonname);
        lc_ctype = canonname;
        check_locale_name(LC_COLLATE, lc_collate, &canonname);
@@ -2172,6 +2188,18 @@ setlocales(void)
        check_locale_name(LC_CTYPE, lc_messages, &canonname);
        lc_messages = canonname;
 #endif
+
+       /*
+        * If ICU is selected but no ICU locale has been given, take the
+        * lc_collate locale and chop off any encoding suffix.  This should give
+        * the user a configuration that resembles their operating system's 
locale
+        * setup.
+        */
+       if (collation_provider[0] == COLLPROVIDER_ICU && !icu_locale)
+       {
+               icu_locale = pg_strdup(lc_collate);
+               icu_locale[strcspn(icu_locale, ".")] = '\0';
+       }
 }
 
 /*
@@ -2187,9 +2215,12 @@ usage(const char *progname)
        printf(_("  -A, --auth=METHOD         default authentication method for 
local connections\n"));
        printf(_("      --auth-host=METHOD    default authentication method for 
local TCP/IP connections\n"));
        printf(_("      --auth-local=METHOD   default authentication method for 
local-socket connections\n"));
+       printf(_("      --collation-provider={libc|icu}\n"
+                        "                            set default collation 
provider for new databases\n"));
        printf(_(" [-D, --pgdata=]DATADIR     location for this database 
cluster\n"));
        printf(_("  -E, --encoding=ENCODING   set default encoding for new 
databases\n"));
        printf(_("  -g, --allow-group-access  allow group read/execute on data 
directory\n"));
+       printf(_("      --icu-locale          set ICU locale for new 
databases\n"));
        printf(_("  -k, --data-checksums      use data page checksums\n"));
        printf(_("      --locale=LOCALE       set default locale for new 
databases\n"));
        printf(_("      --lc-collate=, --lc-ctype=, --lc-messages=LOCALE\n"
@@ -2364,7 +2395,8 @@ setup_locale_encoding(void)
                strcmp(lc_ctype, lc_time) == 0 &&
                strcmp(lc_ctype, lc_numeric) == 0 &&
                strcmp(lc_ctype, lc_monetary) == 0 &&
-               strcmp(lc_ctype, lc_messages) == 0)
+               strcmp(lc_ctype, lc_messages) == 0 &&
+               (!icu_locale || strcmp(lc_ctype, icu_locale) == 0))
                printf(_("The database cluster will be initialized with locale 
\"%s\".\n"), lc_ctype);
        else
        {
@@ -2381,9 +2413,13 @@ setup_locale_encoding(void)
                           lc_monetary,
                           lc_numeric,
                           lc_time);
+               if (icu_locale)
+                       printf(_("  ICU:      %s\n"), icu_locale);
        }
 
-       if (!encoding)
+       if (!encoding && collation_provider[0] == COLLPROVIDER_ICU)
+               encodingid = PG_UTF8;
+       else if (!encoding)
        {
                int                     ctype_enc;
 
@@ -2887,6 +2923,8 @@ main(int argc, char *argv[])
                {"data-checksums", no_argument, NULL, 'k'},
                {"allow-group-access", no_argument, NULL, 'g'},
                {"discard-caches", no_argument, NULL, 14},
+               {"collation-provider", required_argument, NULL, 15},
+               {"icu-locale", required_argument, NULL, 16},
                {NULL, 0, NULL, 0}
        };
 
@@ -3033,6 +3071,20 @@ main(int argc, char *argv[])
                                                                                
 extra_options,
                                                                                
 "-c debug_discard_caches=1");
                                break;
+                       case 15:
+                               if (strcmp(optarg, "icu") == 0)
+                                       collation_provider[0] = 
COLLPROVIDER_ICU;
+                               else if (strcmp(optarg, "libc") == 0)
+                                       collation_provider[0] = 
COLLPROVIDER_LIBC;
+                               else
+                               {
+                                       pg_log_error("unrecognized collation 
provider: %s", optarg);
+                                       exit(1);
+                               }
+                               break;
+                       case 16:
+                               icu_locale = pg_strdup(optarg);
+                               break;
                        default:
                                /* getopt_long already emitted a complaint */
                                fprintf(stderr, _("Try \"%s --help\" for more 
information.\n"),
diff --git a/src/bin/initdb/t/001_initdb.pl b/src/bin/initdb/t/001_initdb.pl
index 02bc688a3b..ee6803490c 100644
--- a/src/bin/initdb/t/001_initdb.pl
+++ b/src/bin/initdb/t/001_initdb.pl
@@ -11,7 +11,7 @@
 use File::stat qw{lstat};
 use PostgreSQL::Test::Cluster;
 use PostgreSQL::Test::Utils;
-use Test::More tests => 22;
+use Test::More tests => 24;
 
 my $tempdir = PostgreSQL::Test::Utils::tempdir;
 my $xlogdir = "$tempdir/pgxlog";
@@ -92,3 +92,19 @@
        ok(check_mode_recursive($datadir_group, 0750, 0640),
                'check PGDATA permissions');
 }
+
+# Collation provider tests
+
+if ($ENV{with_icu} eq 'yes')
+{
+       command_ok(['initdb', '--no-sync', '--collation-provider=icu', 
"$tempdir/data2"],
+                          'collation provider ICU');
+}
+else
+{
+       command_fails(['initdb', '--no-sync', '--collation-provider=icu', 
"$tempdir/data2"],
+                                 'collation provider ICU fails since no ICU 
support');
+}
+
+command_fails(['initdb', '--no-sync', '--collation-provider=xyz', 
"$tempdir/dataX"],
+                         'fails for invalid collation provider');
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index e3ddf19959..0c162f7d42 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -2753,6 +2753,7 @@ dumpDatabase(Archive *fout)
                                i_datname,
                                i_datdba,
                                i_encoding,
+                               i_datcollprovider,
                                i_collate,
                                i_ctype,
                                i_frozenxid,
@@ -2768,6 +2769,7 @@ dumpDatabase(Archive *fout)
        const char *datname,
                           *dba,
                           *encoding,
+                          *datcollprovider,
                           *collate,
                           *ctype,
                           *datistemplate,
@@ -2792,6 +2794,10 @@ dumpDatabase(Archive *fout)
                appendPQExpBuffer(dbQry, "datminmxid, ");
        else
                appendPQExpBuffer(dbQry, "0 AS datminmxid, ");
+       if (fout->remoteVersion >= 150000)
+               appendPQExpBuffer(dbQry, "datcollprovider, ");
+       else
+               appendPQExpBuffer(dbQry, "'c' AS datcollprovider, ");
        appendPQExpBuffer(dbQry,
                                          "(SELECT spcname FROM pg_tablespace t 
WHERE t.oid = dattablespace) AS tablespace, "
                                          "shobj_description(oid, 
'pg_database') AS description "
@@ -2805,6 +2811,7 @@ dumpDatabase(Archive *fout)
        i_datname = PQfnumber(res, "datname");
        i_datdba = PQfnumber(res, "datdba");
        i_encoding = PQfnumber(res, "encoding");
+       i_datcollprovider = PQfnumber(res, "datcollprovider");
        i_collate = PQfnumber(res, "datcollate");
        i_ctype = PQfnumber(res, "datctype");
        i_frozenxid = PQfnumber(res, "datfrozenxid");
@@ -2820,6 +2827,7 @@ dumpDatabase(Archive *fout)
        datname = PQgetvalue(res, 0, i_datname);
        dba = getRoleName(PQgetvalue(res, 0, i_datdba));
        encoding = PQgetvalue(res, 0, i_encoding);
+       datcollprovider = PQgetvalue(res, 0, i_datcollprovider);
        collate = PQgetvalue(res, 0, i_collate);
        ctype = PQgetvalue(res, 0, i_ctype);
        frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
@@ -2853,6 +2861,17 @@ dumpDatabase(Archive *fout)
                appendPQExpBufferStr(creaQry, " ENCODING = ");
                appendStringLiteralAH(creaQry, encoding, fout);
        }
+       if (strlen(datcollprovider) > 0)
+       {
+               appendPQExpBufferStr(creaQry, " COLLATION_PROVIDER = ");
+               if (datcollprovider[0] == 'c')
+                       appendPQExpBufferStr(creaQry, "libc");
+               else if (datcollprovider[0] == 'i')
+                       appendPQExpBufferStr(creaQry, "icu");
+               else
+                       fatal("unrecognized collation provider: %s",
+                                 datcollprovider);
+       }
        if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
        {
                appendPQExpBufferStr(creaQry, " LOCALE = ");
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 3d218c2ad2..5c5aa78b2c 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -349,6 +349,16 @@ check_locale_and_encoding(DbInfo *olddb, DbInfo *newdb)
        if (!equivalent_locale(LC_CTYPE, olddb->db_ctype, newdb->db_ctype))
                pg_fatal("lc_ctype values for database \"%s\" do not match:  
old \"%s\", new \"%s\"\n",
                                 olddb->db_name, olddb->db_ctype, 
newdb->db_ctype);
+       if (olddb->db_collprovider != newdb->db_collprovider)
+               pg_fatal("collation providers for database \"%s\" do not match: 
 old \"%c\", new \"%c\"\n",
+                                olddb->db_name, olddb->db_collprovider, 
newdb->db_collprovider);
+       if ((olddb->db_icucoll == NULL && newdb->db_icucoll != NULL) ||
+               (olddb->db_icucoll != NULL && newdb->db_icucoll == NULL) ||
+               (olddb->db_icucoll != NULL && newdb->db_icucoll != NULL && 
strcmp(olddb->db_icucoll, newdb->db_icucoll) != 0))
+               pg_fatal("ICU collation values for database \"%s\" do not 
match:  old \"%s\", new \"%s\"\n",
+                                olddb->db_name,
+                                olddb->db_icucoll ? olddb->db_icucoll : 
"(null)",
+                                newdb->db_icucoll ? newdb->db_icucoll : 
"(null)");
 }
 
 /*
diff --git a/src/bin/pg_upgrade/info.c b/src/bin/pg_upgrade/info.c
index 69ef23119f..2a9ca0e389 100644
--- a/src/bin/pg_upgrade/info.c
+++ b/src/bin/pg_upgrade/info.c
@@ -312,11 +312,20 @@ get_db_infos(ClusterInfo *cluster)
                                i_encoding,
                                i_datcollate,
                                i_datctype,
+                               i_datcollprovider,
+                               i_daticucoll,
                                i_spclocation;
        char            query[QUERY_ALLOC];
 
        snprintf(query, sizeof(query),
-                        "SELECT d.oid, d.datname, d.encoding, d.datcollate, 
d.datctype, "
+                        "SELECT d.oid, d.datname, d.encoding, d.datcollate, 
d.datctype, ");
+       if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1500)
+               snprintf(query + strlen(query), sizeof(query) - strlen(query),
+                                "'c' AS datcollprovider, NULL AS daticucoll, 
");
+       else
+               snprintf(query + strlen(query), sizeof(query) - strlen(query),
+                                "datcollprovider, daticucoll, ");
+       snprintf(query + strlen(query), sizeof(query) - strlen(query),
                         "pg_catalog.pg_tablespace_location(t.oid) AS 
spclocation "
                         "FROM pg_catalog.pg_database d "
                         " LEFT OUTER JOIN pg_catalog.pg_tablespace t "
@@ -331,6 +340,8 @@ get_db_infos(ClusterInfo *cluster)
        i_encoding = PQfnumber(res, "encoding");
        i_datcollate = PQfnumber(res, "datcollate");
        i_datctype = PQfnumber(res, "datctype");
+       i_datcollprovider = PQfnumber(res, "datcollprovider");
+       i_daticucoll = PQfnumber(res, "daticucoll");
        i_spclocation = PQfnumber(res, "spclocation");
 
        ntups = PQntuples(res);
@@ -343,6 +354,11 @@ get_db_infos(ClusterInfo *cluster)
                dbinfos[tupnum].db_encoding = atoi(PQgetvalue(res, tupnum, 
i_encoding));
                dbinfos[tupnum].db_collate = pg_strdup(PQgetvalue(res, tupnum, 
i_datcollate));
                dbinfos[tupnum].db_ctype = pg_strdup(PQgetvalue(res, tupnum, 
i_datctype));
+               dbinfos[tupnum].db_collprovider = PQgetvalue(res, tupnum, 
i_datcollprovider)[0];
+               if (PQgetisnull(res, tupnum, i_daticucoll))
+                       dbinfos[tupnum].db_icucoll = NULL;
+               else
+                       dbinfos[tupnum].db_icucoll = pg_strdup(PQgetvalue(res, 
tupnum, i_daticucoll));
                snprintf(dbinfos[tupnum].db_tablespace, 
sizeof(dbinfos[tupnum].db_tablespace), "%s",
                                 PQgetvalue(res, tupnum, i_spclocation));
        }
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1db8e3f0fb..5c0f256598 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -163,6 +163,8 @@ typedef struct
                                                                                
         * path */
        char       *db_collate;
        char       *db_ctype;
+       char            db_collprovider;
+       char       *db_icucoll;
        int                     db_encoding;
        RelInfoArr      rel_arr;                /* array of all user relinfos */
 } DbInfo;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 346cd92793..b85a7b1794 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -896,6 +896,18 @@ listAllDbs(const char *pattern, bool verbose)
                                          gettext_noop("Encoding"),
                                          gettext_noop("Collate"),
                                          gettext_noop("Ctype"));
+       if (pset.sversion >= 150000)
+               appendPQExpBuffer(&buf,
+                                                 "       d.daticucoll as 
\"%s\",\n"
+                                                 "       CASE 
d.datcollprovider WHEN 'c' THEN 'libc' WHEN 'i' THEN 'icu' END AS \"%s\",\n",
+                                                 gettext_noop("ICU Collation"),
+                                                 gettext_noop("Coll. 
Provider"));
+       else
+               appendPQExpBuffer(&buf,
+                                                 "       d.datcollate as 
\"%s\",\n"
+                                                 "       'libc' AS \"%s\",\n",
+                                                 gettext_noop("ICU Collation"),
+                                                 gettext_noop("Coll. 
Provider"));
        appendPQExpBufferStr(&buf, "       ");
        printACLColumn(&buf, "d.datacl");
        if (verbose)
@@ -4603,7 +4615,7 @@ listCollations(const char *pattern, bool verbose, bool 
showSystem)
        PQExpBufferData buf;
        PGresult   *res;
        printQueryOpt myopt = pset.popt;
-       static const bool translate_columns[] = {false, false, false, false, 
false, true, false};
+       static const bool translate_columns[] = {false, false, false, false, 
false, false, true, false};
 
        initPQExpBuffer(&buf);
 
@@ -4617,6 +4629,15 @@ listCollations(const char *pattern, bool verbose, bool 
showSystem)
                                          gettext_noop("Collate"),
                                          gettext_noop("Ctype"));
 
+       if (pset.sversion >= 150000)
+               appendPQExpBuffer(&buf,
+                                                 ",\n       c.collicucoll AS 
\"%s\"",
+                                                 gettext_noop("ICU 
Collation"));
+       else
+               appendPQExpBuffer(&buf,
+                                                 ",\n       c.collcollate AS 
\"%s\"",
+                                                 gettext_noop("ICU 
Collation"));
+
        if (pset.sversion >= 100000)
                appendPQExpBuffer(&buf,
                                                  ",\n       CASE 
c.collprovider WHEN 'd' THEN 'default' WHEN 'c' THEN 'libc' WHEN 'i' THEN 'icu' 
END AS \"%s\"",
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 0ee94d7362..ff05d395e0 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -2716,7 +2716,7 @@ psql_completion(const char *text, int start, int end)
                COMPLETE_WITH("OWNER", "TEMPLATE", "ENCODING", "TABLESPACE",
                                          "IS_TEMPLATE",
                                          "ALLOW_CONNECTIONS", "CONNECTION 
LIMIT",
-                                         "LC_COLLATE", "LC_CTYPE", "LOCALE", 
"OID");
+                                         "LC_COLLATE", "LC_CTYPE", "LOCALE", 
"OID", "COLLATION_PROVIDER");
 
        else if (Matches("CREATE", "DATABASE", MatchAny, "TEMPLATE"))
                COMPLETE_WITH_QUERY(Query_for_list_of_template_databases);
diff --git a/src/bin/scripts/Makefile b/src/bin/scripts/Makefile
index b833109da6..25e7da3d3f 100644
--- a/src/bin/scripts/Makefile
+++ b/src/bin/scripts/Makefile
@@ -53,6 +53,8 @@ clean distclean maintainer-clean:
        rm -f common.o $(WIN32RES)
        rm -rf tmp_check
 
+export with_icu
+
 check:
        $(prove_check)
 
diff --git a/src/bin/scripts/createdb.c b/src/bin/scripts/createdb.c
index b0c6805bc9..471cae7cca 100644
--- a/src/bin/scripts/createdb.c
+++ b/src/bin/scripts/createdb.c
@@ -38,6 +38,7 @@ main(int argc, char *argv[])
                {"lc-ctype", required_argument, NULL, 2},
                {"locale", required_argument, NULL, 'l'},
                {"maintenance-db", required_argument, NULL, 3},
+               {"collation-provider", required_argument, NULL, 4},
                {NULL, 0, NULL, 0}
        };
 
@@ -61,6 +62,7 @@ main(int argc, char *argv[])
        char       *lc_collate = NULL;
        char       *lc_ctype = NULL;
        char       *locale = NULL;
+       char       *collation_provider = NULL;
 
        PQExpBufferData sql;
 
@@ -119,6 +121,9 @@ main(int argc, char *argv[])
                        case 3:
                                maintenance_db = pg_strdup(optarg);
                                break;
+                       case 4:
+                               collation_provider = pg_strdup(optarg);
+                               break;
                        default:
                                fprintf(stderr, _("Try \"%s --help\" for more 
information.\n"), progname);
                                exit(1);
@@ -217,6 +222,8 @@ main(int argc, char *argv[])
                appendPQExpBufferStr(&sql, " LC_CTYPE ");
                appendStringLiteralConn(&sql, lc_ctype, conn);
        }
+       if (collation_provider)
+               appendPQExpBuffer(&sql, " COLLATION_PROVIDER %s", 
collation_provider);
 
        appendPQExpBufferChar(&sql, ';');
 
@@ -267,6 +274,8 @@ help(const char *progname)
        printf(_("Usage:\n"));
        printf(_("  %s [OPTION]... [DBNAME] [DESCRIPTION]\n"), progname);
        printf(_("\nOptions:\n"));
+       printf(_("      --collation-provider={libc|icu}\n"
+                        "                               collation provider for 
the database's default collation\n"));
        printf(_("  -D, --tablespace=TABLESPACE  default tablespace for the 
database\n"));
        printf(_("  -e, --echo                   show the commands being sent 
to the server\n"));
        printf(_("  -E, --encoding=ENCODING      encoding for the database\n"));
diff --git a/src/bin/scripts/t/020_createdb.pl 
b/src/bin/scripts/t/020_createdb.pl
index c54c291b7a..9712714a46 100644
--- a/src/bin/scripts/t/020_createdb.pl
+++ b/src/bin/scripts/t/020_createdb.pl
@@ -6,7 +6,7 @@
 
 use PostgreSQL::Test::Cluster;
 use PostgreSQL::Test::Utils;
-use Test::More tests => 25;
+use Test::More tests => 28;
 
 program_help_ok('createdb');
 program_version_ok('createdb');
@@ -25,9 +25,27 @@
        qr/statement: CREATE DATABASE foobar2 ENCODING 'LATIN1'/,
        'create database with encoding');
 
+if ($ENV{with_icu} eq 'yes')
+{
+       $node->issues_sql_like(
+               [ 'createdb', '-T', 'template0', '--collation-provider=icu', 
'foobar4' ],
+               qr/statement: CREATE DATABASE foobar4 .* COLLATION_PROVIDER 
icu/,
+               'create database with ICU');
+}
+else
+{
+       $node->command_fails(
+               [ 'createdb', '-T', 'template0', '--collation-provider=icu', 
'foobar4' ],
+               'create database with ICU fails since no ICU support');
+       pass;
+}
+
 $node->command_fails([ 'createdb', 'foobar1' ],
        'fails if database already exists');
 
+$node->command_fails([ 'createdb', '-T', 'template0', 
'--collation-provider=xyz', 'foobarX' ],
+       'fails for invalid collation provider');
+
 # Check use of templates with shared dependencies copied from the template.
 my ($ret, $stdout, $stderr) = $node->psql(
        'foobar2',
diff --git a/src/include/catalog/pg_collation.dat 
b/src/include/catalog/pg_collation.dat
index 4b56825d82..f7470ead49 100644
--- a/src/include/catalog/pg_collation.dat
+++ b/src/include/catalog/pg_collation.dat
@@ -14,8 +14,7 @@
 
 { oid => '100', oid_symbol => 'DEFAULT_COLLATION_OID',
   descr => 'database\'s default collation',
-  collname => 'default', collprovider => 'd', collencoding => '-1',
-  collcollate => '', collctype => '' },
+  collname => 'default', collprovider => 'd', collencoding => '-1' },
 { oid => '950', oid_symbol => 'C_COLLATION_OID',
   descr => 'standard C collation',
   collname => 'C', collprovider => 'c', collencoding => '-1',
diff --git a/src/include/catalog/pg_collation.h 
b/src/include/catalog/pg_collation.h
index 8763dd4080..590e06d1a8 100644
--- a/src/include/catalog/pg_collation.h
+++ b/src/include/catalog/pg_collation.h
@@ -40,8 +40,9 @@ CATALOG(pg_collation,3456,CollationRelationId)
        bool            collisdeterministic BKI_DEFAULT(t);
        int32           collencoding;   /* encoding for this collation; -1 = 
"all" */
 #ifdef CATALOG_VARLEN                  /* variable-length fields start here */
-       text            collcollate BKI_FORCE_NOT_NULL;         /* LC_COLLATE 
setting */
-       text            collctype BKI_FORCE_NOT_NULL;           /* LC_CTYPE 
setting */
+       text            collcollate BKI_DEFAULT(_null_);        /* LC_COLLATE 
setting */
+       text            collctype BKI_DEFAULT(_null_);          /* LC_CTYPE 
setting */
+       text            collicucoll BKI_DEFAULT(_null_);        /* ICU 
collation string */
        text            collversion BKI_DEFAULT(_null_);        /* 
provider-dependent
                                                                                
                         * version of collation
                                                                                
                         * data */
@@ -75,6 +76,7 @@ extern Oid    CollationCreate(const char *collname, Oid 
collnamespace,
                                                        bool 
collisdeterministic,
                                                        int32 collencoding,
                                                        const char 
*collcollate, const char *collctype,
+                                                       const char *collicucoll,
                                                        const char *collversion,
                                                        bool if_not_exists,
                                                        bool quiet);
diff --git a/src/include/catalog/pg_database.dat 
b/src/include/catalog/pg_database.dat
index e7e42d6023..cce12e27bd 100644
--- a/src/include/catalog/pg_database.dat
+++ b/src/include/catalog/pg_database.dat
@@ -14,9 +14,9 @@
 
 { oid => '1', oid_symbol => 'TemplateDbOid',
   descr => 'default template for new databases',
-  datname => 'template1', encoding => 'ENCODING', datistemplate => 't',
+  datname => 'template1', encoding => 'ENCODING', datcollprovider => 
'COLLPROVIDER', datistemplate => 't',
   datallowconn => 't', datconnlimit => '-1', datfrozenxid => '0',
   datminmxid => '1', dattablespace => 'pg_default', datcollate => 'LC_COLLATE',
-  datctype => 'LC_CTYPE', datacl => '_null_' },
+  datctype => 'LC_CTYPE', daticucoll => 'ICUCOLL', datacl => '_null_' },
 
 ]
diff --git a/src/include/catalog/pg_database.h 
b/src/include/catalog/pg_database.h
index 90b43a4ecc..13dc5e1ba9 100644
--- a/src/include/catalog/pg_database.h
+++ b/src/include/catalog/pg_database.h
@@ -40,6 +40,9 @@ CATALOG(pg_database,1262,DatabaseRelationId) 
BKI_SHARED_RELATION BKI_ROWTYPE_OID
        /* character encoding */
        int32           encoding;
 
+       /* see pg_collation.collprovider */
+       char            datcollprovider;
+
        /* allowed as CREATE DATABASE template? */
        bool            datistemplate;
 
@@ -65,6 +68,9 @@ CATALOG(pg_database,1262,DatabaseRelationId) 
BKI_SHARED_RELATION BKI_ROWTYPE_OID
        /* LC_CTYPE setting */
        text            datctype BKI_FORCE_NOT_NULL;
 
+       /* ICU collation */
+       text            daticucoll;
+
        /* access permissions */
        aclitem         datacl[1];
 #endif
diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h
index 30e423af0e..11138e02f3 100644
--- a/src/include/utils/pg_locale.h
+++ b/src/include/utils/pg_locale.h
@@ -103,6 +103,12 @@ struct pg_locale_struct
 
 typedef struct pg_locale_struct *pg_locale_t;
 
+extern struct pg_locale_struct default_locale;
+
+extern void make_icu_collator(const char *icucollstr,
+                                                         struct 
pg_locale_struct *resultp);
+extern void check_collation_version(HeapTuple colltuple);
+
 extern pg_locale_t pg_newlocale_from_collation(Oid collid);
 
 extern char *get_collation_actual_version(char collprovider, const char 
*collcollate);
diff --git a/src/test/regress/expected/collate.icu.utf8.out 
b/src/test/regress/expected/collate.icu.utf8.out
index 70133df804..3d9647b597 100644
--- a/src/test/regress/expected/collate.icu.utf8.out
+++ b/src/test/regress/expected/collate.icu.utf8.out
@@ -1029,14 +1029,12 @@ CREATE COLLATION test0 FROM "C"; -- fail, duplicate name
 ERROR:  collation "test0" already exists
 do $$
 BEGIN
-  EXECUTE 'CREATE COLLATION test1 (provider = icu, lc_collate = ' ||
-          quote_literal(current_setting('lc_collate')) ||
-          ', lc_ctype = ' ||
-          quote_literal(current_setting('lc_ctype')) || ');';
+  EXECUTE 'CREATE COLLATION test1 (provider = icu, locale = ' ||
+          quote_literal(current_setting('lc_collate')) || ');';
 END
 $$;
-CREATE COLLATION test3 (provider = icu, lc_collate = 'en_US.utf8'); -- fail, 
need lc_ctype
-ERROR:  parameter "lc_ctype" must be specified
+CREATE COLLATION test3 (provider = icu, lc_collate = 'en_US.utf8'); -- fail, 
needs "locale"
+ERROR:  parameter "locale" must be specified
 CREATE COLLATION testx (provider = icu, locale = 'nonsense'); /* never fails 
with ICU */  DROP COLLATION testx;
 CREATE COLLATION test4 FROM nonsense;
 ERROR:  collation "nonsense" for encoding "UTF8" does not exist
diff --git a/src/test/regress/sql/collate.icu.utf8.sql 
b/src/test/regress/sql/collate.icu.utf8.sql
index 9cee3d0042..0677ba56e4 100644
--- a/src/test/regress/sql/collate.icu.utf8.sql
+++ b/src/test/regress/sql/collate.icu.utf8.sql
@@ -366,13 +366,11 @@ CREATE SCHEMA test_schema;
 CREATE COLLATION test0 FROM "C"; -- fail, duplicate name
 do $$
 BEGIN
-  EXECUTE 'CREATE COLLATION test1 (provider = icu, lc_collate = ' ||
-          quote_literal(current_setting('lc_collate')) ||
-          ', lc_ctype = ' ||
-          quote_literal(current_setting('lc_ctype')) || ');';
+  EXECUTE 'CREATE COLLATION test1 (provider = icu, locale = ' ||
+          quote_literal(current_setting('lc_collate')) || ');';
 END
 $$;
-CREATE COLLATION test3 (provider = icu, lc_collate = 'en_US.utf8'); -- fail, 
need lc_ctype
+CREATE COLLATION test3 (provider = icu, lc_collate = 'en_US.utf8'); -- fail, 
needs "locale"
 CREATE COLLATION testx (provider = icu, locale = 'nonsense'); /* never fails 
with ICU */  DROP COLLATION testx;
 
 CREATE COLLATION test4 FROM nonsense;

base-commit: 87669de72c2249e6aec84b8c27fdc3ffb7284e13
-- 
2.35.1

Reply via email to