diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 5a021db33b..4635cf2bb5 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -13269,7 +13269,7 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); - format_type(type_oid [, typemod [, collation_oid ]]) + format_type(type_oid, typemod) text get SQL name of a data type @@ -13410,9 +13410,7 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); format_type returns the SQL name of a data type that is identified by its type OID and possibly a type modifier. Pass NULL - for the type modifier or omit the argument if no specific modifier is known. - If a collation is given as third argument, a COLLATE clause - followed by a formatted collation name is appended. + for the type modifier if no specific modifier is known. diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 3f7d7d913a..325d4523e6 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -693,10 +693,6 @@ COMMENT ON FUNCTION ts_debug(text) IS -- to get filled in.) -- -CREATE OR REPLACE FUNCTION - format_type(oid, int DEFAULT NULL, oid DEFAULT NULL) - RETURNS text STABLE LANGUAGE internal AS 'format_type'; - CREATE OR REPLACE FUNCTION pg_start_backup(label text, fast boolean DEFAULT false) RETURNS text STRICT VOLATILE LANGUAGE internal AS 'pg_start_backup'; diff --git a/src/backend/utils/adt/format_type.c b/src/backend/utils/adt/format_type.c index f85e0bbd00..b56bb74bdc 100644 --- a/src/backend/utils/adt/format_type.c +++ b/src/backend/utils/adt/format_type.c @@ -18,7 +18,6 @@ #include #include "catalog/namespace.h" -#include "catalog/pg_collation.h" #include "catalog/pg_type.h" #include "utils/builtins.h" #include "utils/lsyscache.h" @@ -29,8 +28,7 @@ #define MAX_INT32_LEN 11 static char *format_type_internal(Oid type_oid, int32 typemod, - bool typemod_given, bool allow_invalid, - Oid collation_oid); + bool typemod_given, bool allow_invalid); static char *printTypmod(const char *typname, int32 typmod, Oid typmodout); static char * psnprintf(size_t len, const char *fmt,...) @@ -69,7 +67,6 @@ format_type(PG_FUNCTION_ARGS) { Oid type_oid; int32 typemod; - Oid collation_oid; char *result; /* Since this function is not strict, we must test for null args */ @@ -77,14 +74,13 @@ format_type(PG_FUNCTION_ARGS) PG_RETURN_NULL(); type_oid = PG_GETARG_OID(0); - collation_oid = PG_ARGISNULL(2) ? InvalidOid : PG_GETARG_OID(2); if (PG_ARGISNULL(1)) - result = format_type_internal(type_oid, -1, false, true, collation_oid); + result = format_type_internal(type_oid, -1, false, true); else { typemod = PG_GETARG_INT32(1); - result = format_type_internal(type_oid, typemod, true, true, collation_oid); + result = format_type_internal(type_oid, typemod, true, true); } PG_RETURN_TEXT_P(cstring_to_text(result)); @@ -99,7 +95,7 @@ format_type(PG_FUNCTION_ARGS) char * format_type_be(Oid type_oid) { - return format_type_internal(type_oid, -1, false, false, InvalidOid); + return format_type_internal(type_oid, -1, false, false); } /* @@ -108,15 +104,14 @@ format_type_be(Oid type_oid) char * format_type_with_typemod(Oid type_oid, int32 typemod) { - return format_type_internal(type_oid, typemod, true, false, InvalidOid); + return format_type_internal(type_oid, typemod, true, false); } static char * format_type_internal(Oid type_oid, int32 typemod, - bool typemod_given, bool allow_invalid, - Oid collation_oid) + bool typemod_given, bool allow_invalid) { bool with_typemod = typemod_given && (typemod >= 0); HeapTuple tuple; @@ -322,12 +317,6 @@ format_type_internal(Oid type_oid, int32 typemod, ReleaseSysCache(tuple); - if (collation_oid && collation_oid != DEFAULT_COLLATION_OID) - { - char *collstr = generate_collation_name(collation_oid); - buf = psnprintf(strlen(buf) + 10 + strlen(collstr), "%s COLLATE %s", buf, collstr); - } - return buf; } @@ -431,7 +420,7 @@ oidvectortypes(PG_FUNCTION_ARGS) for (num = 0; num < numargs; num++) { char *typename = format_type_internal(oidArray->values[num], -1, - false, true, InvalidOid); + false, true); size_t slen = strlen(typename); if (left < (slen + 2)) diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index ce24d6101d..acd251415d 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -1388,6 +1388,8 @@ setup_depend(void) " FROM pg_ts_template;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_ts_config;\n", + "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " + " FROM pg_collation;\n", "INSERT INTO pg_shdepend SELECT 0,0,0,0, tableoid,oid, 'p' " " FROM pg_authid;\n", NULL diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c index 12b22bc256..472760edf1 100644 --- a/src/bin/pg_dump/common.c +++ b/src/bin/pg_dump/common.c @@ -54,10 +54,12 @@ static int numTables; static int numTypes; static int numFuncs; static int numOperators; +static int numCollations; static DumpableObject **tblinfoindex; static DumpableObject **typinfoindex; static DumpableObject **funinfoindex; static DumpableObject **oprinfoindex; +static DumpableObject **collinfoindex; static void flagInhTables(TableInfo *tbinfo, int numTables, @@ -105,7 +107,6 @@ getSchemaData(int *numTablesPtr) int numCasts; int numOpclasses; int numOpfamilies; - int numCollations; int numConversions; int numTSParsers; int numTSTemplates; @@ -187,6 +188,7 @@ getSchemaData(int *numTablesPtr) if (g_verbose) write_msg(NULL, "reading user-defined collations\n"); collinfo = getCollations(&numCollations); + collinfoindex = buildIndexArray(collinfo, numCollations, sizeof(CollInfo)); if (g_verbose) write_msg(NULL, "reading user-defined conversions\n"); @@ -784,6 +786,17 @@ findOprByOid(Oid oid) return (OprInfo *) findObjectByOid(oid, oprinfoindex, numOperators); } +/* + * findCollationByOid + * finds the entry (in collinfo) of the collation with the given oid + * returns NULL if not found + */ +CollInfo * +findCollationByOid(Oid oid) +{ + return (CollInfo *) findObjectByOid(oid, collinfoindex, numCollations); +} + /* * findParentsByOid diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index dfbdcadd14..0884517331 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -5502,6 +5502,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) int i_attalign; int i_attislocal; int i_attoptions; + int i_attcollation; PGresult *res; int ntups; bool hasdefaults; @@ -5541,13 +5542,20 @@ getTableAttrs(TableInfo *tblinfo, int numTables) if (g_fout->remoteVersion >= 90100) { - /* attcollation is new in 9.1 */ + /* + * attcollation is new in 9.1. Since we only want to dump + * COLLATE clauses for attributes whose collation is different + * from their type's default, we use a CASE here to suppress + * uninteresting attcollations cheaply. + */ appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, " "a.attstattarget, a.attstorage, t.typstorage, " "a.attnotnull, a.atthasdef, a.attisdropped, " "a.attlen, a.attalign, a.attislocal, " - "pg_catalog.format_type(t.oid,a.atttypmod,a.attcollation) AS atttypname, " - "array_to_string(attoptions, ', ') AS attoptions " + "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " + "array_to_string(a.attoptions, ', ') AS attoptions, " + "CASE WHEN a.attcollation <> t.typcollation " + "THEN a.attcollation ELSE 0 END AS attcollation " "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " "ON a.atttypid = t.oid " "WHERE a.attrelid = '%u'::pg_catalog.oid " @@ -5563,7 +5571,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables) "a.attnotnull, a.atthasdef, a.attisdropped, " "a.attlen, a.attalign, a.attislocal, " "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " - "array_to_string(attoptions, ', ') AS attoptions " + "array_to_string(a.attoptions, ', ') AS attoptions, " + "0 AS attcollation " "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " "ON a.atttypid = t.oid " "WHERE a.attrelid = '%u'::pg_catalog.oid " @@ -5579,7 +5588,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) "a.attnotnull, a.atthasdef, a.attisdropped, " "a.attlen, a.attalign, a.attislocal, " "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " - "'' AS attoptions " + "'' AS attoptions, 0 AS attcollation " "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " "ON a.atttypid = t.oid " "WHERE a.attrelid = '%u'::pg_catalog.oid " @@ -5600,7 +5609,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) "false AS attisdropped, a.attlen, " "a.attalign, false AS attislocal, " "format_type(t.oid,a.atttypmod) AS atttypname, " - "'' AS attoptions " + "'' AS attoptions, 0 AS attcollation " "FROM pg_attribute a LEFT JOIN pg_type t " "ON a.atttypid = t.oid " "WHERE a.attrelid = '%u'::oid " @@ -5618,7 +5627,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) "attlen, attalign, " "false AS attislocal, " "(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname, " - "'' AS attoptions " + "'' AS attoptions, 0 AS attcollation " "FROM pg_attribute a " "WHERE attrelid = '%u'::oid " "AND attnum > 0::int2 " @@ -5645,6 +5654,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) i_attalign = PQfnumber(res, "attalign"); i_attislocal = PQfnumber(res, "attislocal"); i_attoptions = PQfnumber(res, "attoptions"); + i_attcollation = PQfnumber(res, "attcollation"); tbinfo->numatts = ntups; tbinfo->attnames = (char **) malloc(ntups * sizeof(char *)); @@ -5660,6 +5670,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool)); tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *)); tbinfo->attoptions = (char **) malloc(ntups * sizeof(char *)); + tbinfo->attcollation = (Oid *) malloc(ntups * sizeof(Oid)); tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool)); tbinfo->inhAttrDef = (bool *) malloc(ntups * sizeof(bool)); tbinfo->inhNotNull = (bool *) malloc(ntups * sizeof(bool)); @@ -5685,6 +5696,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't'); tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't'); tbinfo->attoptions[j] = strdup(PQgetvalue(res, j, i_attoptions)); + tbinfo->attcollation[j] = atooid(PQgetvalue(res, j, i_attcollation)); tbinfo->attrdefs[j] = NULL; /* fix below */ if (PQgetvalue(res, j, i_atthasdef)[0] == 't') hasdefaults = true; @@ -7359,7 +7371,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) "typanalyze::pg_catalog.oid AS typanalyzeoid, " "typcategory, typispreferred, " "typdelim, typbyval, typalign, typstorage, " - "(typcollation = (SELECT oid FROM pg_catalog.pg_collation WHERE collname = 'default')) AS typcollatable, " + "(typcollation <> 0) AS typcollatable, " "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault " "FROM pg_catalog.pg_type " "WHERE oid = '%u'::pg_catalog.oid", @@ -7736,6 +7748,7 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) char *typnotnull; char *typdefn; char *typdefault; + Oid typcollation; bool typdefault_is_literal = false; /* Set proper schema search path so type references list correctly */ @@ -7745,11 +7758,14 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) if (g_fout->remoteVersion >= 90100) { /* typcollation is new in 9.1 */ - appendPQExpBuffer(query, "SELECT typnotnull, " - "pg_catalog.format_type(typbasetype, typtypmod, typcollation) AS typdefn, " - "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, " - "typdefault " + appendPQExpBuffer(query, "SELECT t.typnotnull, " + "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, " + "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, " + "t.typdefault, " + "CASE WHEN t.typcollation <> u.typcollation " + "THEN t.typcollation ELSE 0 END AS typcollation " "FROM pg_catalog.pg_type t " + "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) " "WHERE t.oid = '%u'::pg_catalog.oid", tyinfo->dobj.catId.oid); } @@ -7759,7 +7775,7 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) appendPQExpBuffer(query, "SELECT typnotnull, " "pg_catalog.format_type(typbasetype, typtypmod) AS typdefn, " "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, " - "typdefault " + "typdefault, 0 AS typcollation " "FROM pg_catalog.pg_type " "WHERE oid = '%u'::pg_catalog.oid", tyinfo->dobj.catId.oid); @@ -7790,6 +7806,7 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) } else typdefault = NULL; + typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation"))); if (binary_upgrade) binary_upgrade_set_type_oids_by_type_oid(q, tyinfo->dobj.catId.oid); @@ -7799,6 +7816,22 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) fmtId(tyinfo->dobj.name), typdefn); + /* Print collation only if different from base type's collation */ + if (OidIsValid(typcollation)) + { + CollInfo *coll; + + coll = findCollationByOid(typcollation); + if (coll) + { + /* always schema-qualify, don't try to be smart */ + appendPQExpBuffer(q, " COLLATE %s.", + fmtId(coll->dobj.namespace->dobj.name)); + appendPQExpBuffer(q, "%s", + fmtId(coll->dobj.name)); + } + } + if (typnotnull[0] == 't') appendPQExpBuffer(q, " NOT NULL"); @@ -11966,6 +11999,22 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) tbinfo->atttypmod[j])); } + /* Add collation if not default for the type */ + if (OidIsValid(tbinfo->attcollation[j])) + { + CollInfo *coll; + + coll = findCollationByOid(tbinfo->attcollation[j]); + if (coll) + { + /* always schema-qualify, don't try to be smart */ + appendPQExpBuffer(q, " COLLATE %s.", + fmtId(coll->dobj.namespace->dobj.name)); + appendPQExpBuffer(q, "%s", + fmtId(coll->dobj.name)); + } + } + if (has_default) appendPQExpBuffer(q, " DEFAULT %s", tbinfo->attrdefs[j]->adef_expr); diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 94b7a6bf92..113ecb1846 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -272,6 +272,7 @@ typedef struct _tableInfo char *attalign; /* attribute align, used by binary_upgrade */ bool *attislocal; /* true if attr has local definition */ char **attoptions; /* per-attribute options */ + Oid *attcollation; /* per-attribute collation selection */ /* * Note: we need to store per-attribute notnull, default, and constraint @@ -510,6 +511,7 @@ extern TableInfo *findTableByOid(Oid oid); extern TypeInfo *findTypeByOid(Oid oid); extern FuncInfo *findFuncByOid(Oid oid); extern OprInfo *findOprByOid(Oid oid); +extern CollInfo *findCollationByOid(Oid oid); extern void simple_oid_list_append(SimpleOidList *list, Oid val); extern void simple_string_list_append(SimpleStringList *list, const char *val); diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index e21ee0a835..22a0b89b44 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201103061 +#define CATALOG_VERSION_NO 201103101 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 0533e5a686..cff64ba6b0 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1100,8 +1100,8 @@ DATA(insert OID = 1078 ( bpcharcmp PGNSP PGUID 12 1 0 0 f f f t f i 2 0 23 DESCR("less-equal-greater"); DATA(insert OID = 1080 ( hashbpchar PGNSP PGUID 12 1 0 0 f f f t f i 1 0 23 "1042" _null_ _null_ _null_ _null_ hashbpchar _null_ _null_ _null_ )); DESCR("hash"); -DATA(insert OID = 1081 ( format_type PGNSP PGUID 12 1 0 0 f f f f f s 3 0 25 "26 23 26" _null_ _null_ _null_ _null_ format_type _null_ _null_ _null_ )); -DESCR("format a type OID, atttypmod, and collation OID to canonical SQL"); +DATA(insert OID = 1081 ( format_type PGNSP PGUID 12 1 0 0 f f f f f s 2 0 25 "26 23" _null_ _null_ _null_ _null_ format_type _null_ _null_ _null_ )); +DESCR("format a type oid and atttypmod to canonical SQL"); DATA(insert OID = 1084 ( date_in PGNSP PGUID 12 1 0 0 f f f t f s 1 0 1082 "2275" _null_ _null_ _null_ _null_ date_in _null_ _null_ _null_ )); DESCR("I/O"); DATA(insert OID = 1085 ( date_out PGNSP PGUID 12 1 0 0 f f f t f s 1 0 2275 "1082" _null_ _null_ _null_ _null_ date_out _null_ _null_ _null_ ));