Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * collationcmds.c
4 : : * collation-related commands support code
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/commands/collationcmds.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : #include "postgres.h"
16 : :
17 : : #ifdef USE_ICU
18 : : #include <unicode/uloc.h>
19 : : #endif
20 : :
21 : : #include "access/htup_details.h"
22 : : #include "access/table.h"
23 : : #include "access/xact.h"
24 : : #include "catalog/indexing.h"
25 : : #include "catalog/namespace.h"
26 : : #include "catalog/objectaccess.h"
27 : : #include "catalog/pg_collation.h"
28 : : #include "catalog/pg_database.h"
29 : : #include "catalog/pg_namespace.h"
30 : : #include "commands/collationcmds.h"
31 : : #include "commands/comment.h"
32 : : #include "commands/dbcommands.h"
33 : : #include "commands/defrem.h"
34 : : #include "common/string.h"
35 : : #include "mb/pg_wchar.h"
36 : : #include "miscadmin.h"
37 : : #include "storage/fd.h"
38 : : #include "utils/acl.h"
39 : : #include "utils/builtins.h"
40 : : #include "utils/lsyscache.h"
41 : : #include "utils/pg_locale.h"
42 : : #include "utils/rel.h"
43 : : #include "utils/syscache.h"
44 : :
45 : :
46 : : typedef struct
47 : : {
48 : : char *localename; /* name of locale, as per "locale -a" */
49 : : char *alias; /* shortened alias for same */
50 : : int enc; /* encoding */
51 : : } CollAliasData;
52 : :
53 : :
54 : : /*
55 : : * CREATE COLLATION
56 : : */
57 : : ObjectAddress
3322 peter_e@gmx.net 58 :CBC 182 : DefineCollation(ParseState *pstate, List *names, List *parameters, bool if_not_exists)
59 : : {
60 : : char *collName;
61 : : Oid collNamespace;
62 : : AclResult aclresult;
63 : : ListCell *pl;
5453 bruce@momjian.us 64 : 182 : DefElem *fromEl = NULL;
65 : 182 : DefElem *localeEl = NULL;
66 : 182 : DefElem *lccollateEl = NULL;
67 : 182 : DefElem *lcctypeEl = NULL;
3279 peter_e@gmx.net 68 : 182 : DefElem *providerEl = NULL;
2550 peter@eisentraut.org 69 : 182 : DefElem *deterministicEl = NULL;
1103 70 : 182 : DefElem *rulesEl = NULL;
1773 tmunro@postgresql.or 71 : 182 : DefElem *versionEl = NULL;
72 : : char *collcollate;
73 : : char *collctype;
74 : : const char *colllocale;
75 : : char *collicurules;
76 : : bool collisdeterministic;
77 : : int collencoding;
78 : : char collprovider;
79 : 182 : char *collversion = NULL;
80 : : Oid newoid;
81 : : ObjectAddress address;
82 : :
5510 peter_e@gmx.net 83 : 182 : collNamespace = QualifiedNameGetCreationNamespace(names, &collName);
84 : :
1218 peter@eisentraut.org 85 : 182 : aclresult = object_aclcheck(NamespaceRelationId, collNamespace, GetUserId(), ACL_CREATE);
5510 peter_e@gmx.net 86 [ - + ]: 182 : if (aclresult != ACLCHECK_OK)
3025 peter_e@gmx.net 87 :UBC 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
5510 88 : 0 : get_namespace_name(collNamespace));
89 : :
5510 peter_e@gmx.net 90 [ + - + + :CBC 522 : foreach(pl, parameters)
+ + ]
91 : : {
3261 tgl@sss.pgh.pa.us 92 : 361 : DefElem *defel = lfirst_node(DefElem, pl);
93 : : DefElem **defelp;
94 : :
2970 95 [ + + ]: 361 : if (strcmp(defel->defname, "from") == 0)
5510 peter_e@gmx.net 96 : 30 : defelp = &fromEl;
2970 tgl@sss.pgh.pa.us 97 [ + + ]: 331 : else if (strcmp(defel->defname, "locale") == 0)
5510 peter_e@gmx.net 98 : 115 : defelp = &localeEl;
2970 tgl@sss.pgh.pa.us 99 [ + + ]: 216 : else if (strcmp(defel->defname, "lc_collate") == 0)
5510 peter_e@gmx.net 100 : 31 : defelp = &lccollateEl;
2970 tgl@sss.pgh.pa.us 101 [ + + ]: 185 : else if (strcmp(defel->defname, "lc_ctype") == 0)
5510 peter_e@gmx.net 102 : 28 : defelp = &lcctypeEl;
2970 tgl@sss.pgh.pa.us 103 [ + + ]: 157 : else if (strcmp(defel->defname, "provider") == 0)
3279 peter_e@gmx.net 104 : 114 : defelp = &providerEl;
2550 peter@eisentraut.org 105 [ + + ]: 43 : else if (strcmp(defel->defname, "deterministic") == 0)
106 : 22 : defelp = &deterministicEl;
1103 107 [ + + ]: 21 : else if (strcmp(defel->defname, "rules") == 0)
108 : 6 : defelp = &rulesEl;
1773 tmunro@postgresql.or 109 [ + + ]: 15 : else if (strcmp(defel->defname, "version") == 0)
110 : 12 : defelp = &versionEl;
111 : : else
112 : : {
5510 peter_e@gmx.net 113 [ + - ]: 3 : ereport(ERROR,
114 : : (errcode(ERRCODE_SYNTAX_ERROR),
115 : : errmsg("collation attribute \"%s\" not recognized",
116 : : defel->defname),
117 : : parser_errposition(pstate, defel->location)));
118 : : break;
119 : : }
1701 dean.a.rasheed@gmail 120 [ + + ]: 358 : if (*defelp != NULL)
121 : 18 : errorConflictingDefElem(defel, pstate);
5510 peter_e@gmx.net 122 : 340 : *defelp = defel;
123 : : }
124 : :
1701 dean.a.rasheed@gmail 125 [ + + + + : 161 : if (localeEl && (lccollateEl || lcctypeEl))
+ + ]
126 [ + - ]: 9 : ereport(ERROR,
127 : : errcode(ERRCODE_SYNTAX_ERROR),
128 : : errmsg("conflicting or redundant options"),
129 : : errdetail("LOCALE cannot be specified together with LC_COLLATE or LC_CTYPE."));
130 : :
131 [ + + + + ]: 152 : if (fromEl && list_length(parameters) != 1)
5510 peter_e@gmx.net 132 [ + - ]: 3 : ereport(ERROR,
133 : : errcode(ERRCODE_SYNTAX_ERROR),
134 : : errmsg("conflicting or redundant options"),
135 : : errdetail("FROM cannot be specified together with any other options."));
136 : :
137 [ + + ]: 149 : if (fromEl)
138 : : {
139 : : Oid collid;
140 : : HeapTuple tp;
141 : : Datum datum;
142 : : bool isnull;
143 : :
5453 bruce@momjian.us 144 : 27 : collid = get_collation_oid(defGetQualifiedName(fromEl), false);
5510 peter_e@gmx.net 145 : 24 : tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
146 [ - + ]: 24 : if (!HeapTupleIsValid(tp))
5510 peter_e@gmx.net 147 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for collation %u", collid);
148 : :
3279 peter_e@gmx.net 149 :CBC 24 : collprovider = ((Form_pg_collation) GETSTRUCT(tp))->collprovider;
2550 peter@eisentraut.org 150 : 24 : collisdeterministic = ((Form_pg_collation) GETSTRUCT(tp))->collisdeterministic;
3180 peter_e@gmx.net 151 : 24 : collencoding = ((Form_pg_collation) GETSTRUCT(tp))->collencoding;
152 : :
1508 peter@eisentraut.org 153 : 24 : datum = SysCacheGetAttr(COLLOID, tp, Anum_pg_collation_collcollate, &isnull);
154 [ + + ]: 24 : if (!isnull)
155 : 15 : collcollate = TextDatumGetCString(datum);
156 : : else
157 : 9 : collcollate = NULL;
158 : :
159 : 24 : datum = SysCacheGetAttr(COLLOID, tp, Anum_pg_collation_collctype, &isnull);
160 [ + + ]: 24 : if (!isnull)
161 : 15 : collctype = TextDatumGetCString(datum);
162 : : else
163 : 9 : collctype = NULL;
164 : :
736 jdavis@postgresql.or 165 : 24 : datum = SysCacheGetAttr(COLLOID, tp, Anum_pg_collation_colllocale, &isnull);
1459 peter@eisentraut.org 166 [ + + ]: 24 : if (!isnull)
736 jdavis@postgresql.or 167 : 6 : colllocale = TextDatumGetCString(datum);
168 : : else
169 : 18 : colllocale = NULL;
170 : :
171 : : /*
172 : : * When the ICU locale comes from an existing collation, do not
173 : : * canonicalize to a language tag.
174 : : */
175 : :
1103 peter@eisentraut.org 176 : 24 : datum = SysCacheGetAttr(COLLOID, tp, Anum_pg_collation_collicurules, &isnull);
177 [ - + ]: 24 : if (!isnull)
1103 peter@eisentraut.org 178 :UBC 0 : collicurules = TextDatumGetCString(datum);
179 : : else
1103 peter@eisentraut.org 180 :CBC 24 : collicurules = NULL;
181 : :
5510 peter_e@gmx.net 182 : 24 : ReleaseSysCache(tp);
183 : :
184 : : /*
185 : : * Copying the "default" collation is not allowed because most code
186 : : * checks for DEFAULT_COLLATION_OID instead of COLLPROVIDER_DEFAULT,
187 : : * and so having a second collation with COLLPROVIDER_DEFAULT would
188 : : * not work and potentially confuse or crash some code. This could be
189 : : * fixed with some legwork.
190 : : */
3197 191 [ + + ]: 24 : if (collprovider == COLLPROVIDER_DEFAULT)
192 [ + - ]: 3 : ereport(ERROR,
193 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
194 : : errmsg("collation \"default\" cannot be copied")));
195 : : }
196 : : else
197 : : {
1465 peter@eisentraut.org 198 : 122 : char *collproviderstr = NULL;
199 : :
200 : 122 : collcollate = NULL;
201 : 122 : collctype = NULL;
736 jdavis@postgresql.or 202 : 122 : colllocale = NULL;
1103 peter@eisentraut.org 203 : 122 : collicurules = NULL;
204 : :
1465 205 [ + + ]: 122 : if (providerEl)
206 : 108 : collproviderstr = defGetString(providerEl);
207 : :
208 [ + + ]: 122 : if (deterministicEl)
209 : 16 : collisdeterministic = defGetBoolean(deterministicEl);
210 : : else
211 : 106 : collisdeterministic = true;
212 : :
1103 213 [ + + ]: 122 : if (rulesEl)
214 : 6 : collicurules = defGetString(rulesEl);
215 : :
1465 216 [ + + ]: 122 : if (versionEl)
217 : 3 : collversion = defGetString(versionEl);
218 : :
219 [ + + ]: 122 : if (collproviderstr)
220 : : {
732 jdavis@postgresql.or 221 [ + + ]: 108 : if (pg_strcasecmp(collproviderstr, "builtin") == 0)
222 : 32 : collprovider = COLLPROVIDER_BUILTIN;
223 [ + - ]: 76 : else if (pg_strcasecmp(collproviderstr, "icu") == 0)
1465 peter@eisentraut.org 224 : 76 : collprovider = COLLPROVIDER_ICU;
1465 peter@eisentraut.org 225 [ # # ]:UBC 0 : else if (pg_strcasecmp(collproviderstr, "libc") == 0)
226 : 0 : collprovider = COLLPROVIDER_LIBC;
227 : : else
228 [ # # ]: 0 : ereport(ERROR,
229 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
230 : : errmsg("unrecognized collation provider: %s",
231 : : collproviderstr)));
232 : : }
233 : : else
1465 peter@eisentraut.org 234 :CBC 14 : collprovider = COLLPROVIDER_LIBC;
235 : :
1459 236 [ + + ]: 122 : if (localeEl)
237 : : {
238 [ + + ]: 100 : if (collprovider == COLLPROVIDER_LIBC)
239 : : {
240 : 1 : collcollate = defGetString(localeEl);
241 : 1 : collctype = defGetString(localeEl);
242 : : }
243 : : else
736 jdavis@postgresql.or 244 : 99 : colllocale = defGetString(localeEl);
245 : : }
246 : :
1459 peter@eisentraut.org 247 [ + + ]: 122 : if (lccollateEl)
248 : 19 : collcollate = defGetString(lccollateEl);
249 : :
250 [ + + ]: 122 : if (lcctypeEl)
251 : 16 : collctype = defGetString(lcctypeEl);
252 : :
732 jdavis@postgresql.or 253 [ + + ]: 122 : if (collprovider == COLLPROVIDER_BUILTIN)
254 : : {
255 [ + + ]: 32 : if (!colllocale)
256 [ + - ]: 6 : ereport(ERROR,
257 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
258 : : errmsg("parameter \"%s\" must be specified",
259 : : "locale")));
260 : :
261 : 26 : colllocale = builtin_validate_locale(GetDatabaseEncoding(),
262 : : colllocale);
263 : : }
264 [ + + ]: 90 : else if (collprovider == COLLPROVIDER_LIBC)
265 : : {
1459 peter@eisentraut.org 266 [ - + ]: 14 : if (!collcollate)
1459 peter@eisentraut.org 267 [ # # ]:UBC 0 : ereport(ERROR,
268 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
269 : : errmsg("parameter \"%s\" must be specified",
270 : : "lc_collate")));
271 : :
1459 peter@eisentraut.org 272 [ - + ]:CBC 14 : if (!collctype)
1459 peter@eisentraut.org 273 [ # # ]:UBC 0 : ereport(ERROR,
274 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
275 : : errmsg("parameter \"%s\" must be specified",
276 : : "lc_ctype")));
277 : : }
1459 peter@eisentraut.org 278 [ + - ]:CBC 76 : else if (collprovider == COLLPROVIDER_ICU)
279 : : {
736 jdavis@postgresql.or 280 [ + + ]: 76 : if (!colllocale)
1459 peter@eisentraut.org 281 [ + - ]: 3 : ereport(ERROR,
282 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
283 : : errmsg("parameter \"%s\" must be specified",
284 : : "locale")));
285 : :
286 : : /*
287 : : * During binary upgrade, preserve the locale string. Otherwise,
288 : : * canonicalize to a language tag.
289 : : */
1076 jdavis@postgresql.or 290 [ + + ]: 73 : if (!IsBinaryUpgrade)
291 : : {
736 292 : 72 : char *langtag = icu_language_tag(colllocale,
293 : : icu_validation_level);
294 : :
295 [ + + + + ]: 69 : if (langtag && strcmp(colllocale, langtag) != 0)
296 : : {
1076 297 [ + + ]: 46 : ereport(NOTICE,
298 : : (errmsg("using standard form \"%s\" for ICU locale \"%s\"",
299 : : langtag, colllocale)));
300 : :
736 301 : 46 : colllocale = langtag;
302 : : }
303 : : }
304 : :
305 : 70 : icu_validate_locale(colllocale);
306 : : }
307 : :
308 : : /*
309 : : * Nondeterministic collations are currently only supported with ICU
310 : : * because that's the only case where it can actually make a
311 : : * difference. So we can save writing the code for the other
312 : : * providers.
313 : : */
1465 peter@eisentraut.org 314 [ + + - + ]: 95 : if (!collisdeterministic && collprovider != COLLPROVIDER_ICU)
1465 peter@eisentraut.org 315 [ # # ]:UBC 0 : ereport(ERROR,
316 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
317 : : errmsg("nondeterministic collations not supported with this provider")));
318 : :
1102 peter@eisentraut.org 319 [ + + - + ]:CBC 95 : if (collicurules && collprovider != COLLPROVIDER_ICU)
1102 peter@eisentraut.org 320 [ # # ]:UBC 0 : ereport(ERROR,
321 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
322 : : errmsg("ICU rules cannot be specified unless locale provider is ICU")));
323 : :
732 jdavis@postgresql.or 324 [ + + ]:CBC 95 : if (collprovider == COLLPROVIDER_BUILTIN)
325 : : {
727 326 : 17 : collencoding = builtin_locale_encoding(colllocale);
327 : : }
732 328 [ + + ]: 78 : else if (collprovider == COLLPROVIDER_ICU)
329 : : {
330 : : #ifdef USE_ICU
331 : : /*
332 : : * We could create ICU collations with collencoding == database
333 : : * encoding, but it seems better to use -1 so that it matches the
334 : : * way initdb would create ICU collations. However, only allow
335 : : * one to be created when the current database's encoding is
336 : : * supported. Otherwise the collation is useless, plus we get
337 : : * surprising behaviors like not being able to drop the collation.
338 : : *
339 : : * Skip this test when !USE_ICU, because the error we want to
340 : : * throw for that isn't thrown till later.
341 : : */
1654 tgl@sss.pgh.pa.us 342 [ - + ]: 64 : if (!is_encoding_supported_by_icu(GetDatabaseEncoding()))
1654 tgl@sss.pgh.pa.us 343 [ # # ]:UBC 0 : ereport(ERROR,
344 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
345 : : errmsg("current database's encoding is not supported with this provider")));
346 : : #endif
3180 peter_e@gmx.net 347 :CBC 64 : collencoding = -1;
348 : : }
349 : : else
350 : : {
351 : 14 : collencoding = GetDatabaseEncoding();
352 : 14 : check_encoding_locale_matches(collencoding, collcollate, collctype);
353 : : }
354 : : }
355 : :
1773 tmunro@postgresql.or 356 [ + + ]: 116 : if (!collversion)
357 : : {
358 : : const char *locale;
359 : :
732 jdavis@postgresql.or 360 [ + + ]: 113 : if (collprovider == COLLPROVIDER_LIBC)
361 : 29 : locale = collcollate;
362 : : else
363 : 84 : locale = colllocale;
364 : :
365 : 113 : collversion = get_collation_actual_version(collprovider, locale);
366 : : }
367 : :
5490 peter_e@gmx.net 368 : 116 : newoid = CollationCreate(collName,
369 : : collNamespace,
370 : : GetUserId(),
371 : : collprovider,
372 : : collisdeterministic,
373 : : collencoding,
374 : : collcollate,
375 : : collctype,
376 : : colllocale,
377 : : collicurules,
378 : : collversion,
379 : : if_not_exists,
380 : : false); /* not quiet */
381 : :
3343 382 [ + + ]: 112 : if (!OidIsValid(newoid))
383 : 1 : return InvalidObjectAddress;
384 : :
385 : : /* Check that the locales can be loaded. */
5490 386 : 111 : CommandCounterIncrement();
557 jdavis@postgresql.or 387 : 111 : (void) pg_newlocale_from_collation(newoid);
388 : :
3148 tgl@sss.pgh.pa.us 389 : 108 : ObjectAddressSet(address, CollationRelationId, newoid);
390 : :
4030 alvherre@alvh.no-ip. 391 : 108 : return address;
392 : : }
393 : :
394 : : /*
395 : : * Subroutine for ALTER COLLATION SET SCHEMA and RENAME
396 : : *
397 : : * Is there a collation with the same name of the given collation already in
398 : : * the given namespace? If so, raise an appropriate error message.
399 : : */
400 : : void
4807 401 : 9 : IsThereCollationInNamespace(const char *collname, Oid nspOid)
402 : : {
403 : : /* make sure the name doesn't already exist in new schema */
5483 tgl@sss.pgh.pa.us 404 [ - + ]: 9 : if (SearchSysCacheExists3(COLLNAMEENCNSP,
405 : : CStringGetDatum(collname),
406 : : Int32GetDatum(GetDatabaseEncoding()),
407 : : ObjectIdGetDatum(nspOid)))
5483 tgl@sss.pgh.pa.us 408 [ # # ]:UBC 0 : ereport(ERROR,
409 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
410 : : errmsg("collation \"%s\" for encoding \"%s\" already exists in schema \"%s\"",
411 : : collname, GetDatabaseEncodingName(),
412 : : get_namespace_name(nspOid))));
413 : :
414 : : /* mustn't match an any-encoding entry, either */
5483 tgl@sss.pgh.pa.us 415 [ + + ]:CBC 9 : if (SearchSysCacheExists3(COLLNAMEENCNSP,
416 : : CStringGetDatum(collname),
417 : : Int32GetDatum(-1),
418 : : ObjectIdGetDatum(nspOid)))
419 [ + - ]: 3 : ereport(ERROR,
420 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
421 : : errmsg("collation \"%s\" already exists in schema \"%s\"",
422 : : collname, get_namespace_name(nspOid))));
5510 peter_e@gmx.net 423 : 6 : }
424 : :
425 : : /*
426 : : * ALTER COLLATION
427 : : */
428 : : ObjectAddress
1773 tmunro@postgresql.or 429 : 3 : AlterCollation(AlterCollationStmt *stmt)
430 : : {
431 : : Relation rel;
432 : : Oid collOid;
433 : : HeapTuple tup;
434 : : Form_pg_collation collForm;
435 : : Datum datum;
436 : : bool isnull;
437 : : char *oldversion;
438 : : char *newversion;
439 : : ObjectAddress address;
440 : :
441 : 3 : rel = table_open(CollationRelationId, RowExclusiveLock);
442 : 3 : collOid = get_collation_oid(stmt->collname, false);
443 : :
1233 jdavis@postgresql.or 444 [ - + ]: 3 : if (collOid == DEFAULT_COLLATION_OID)
1233 jdavis@postgresql.or 445 [ # # ]:UBC 0 : ereport(ERROR,
446 : : (errmsg("cannot refresh version of default collation"),
447 : : /* translator: %s is an SQL command */
448 : : errhint("Use %s instead.",
449 : : "ALTER DATABASE ... REFRESH COLLATION VERSION")));
450 : :
1218 peter@eisentraut.org 451 [ - + ]:CBC 3 : if (!object_ownercheck(CollationRelationId, collOid, GetUserId()))
1773 tmunro@postgresql.or 452 :UBC 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_COLLATION,
453 : 0 : NameListToString(stmt->collname));
454 : :
1773 tmunro@postgresql.or 455 :CBC 3 : tup = SearchSysCacheCopy1(COLLOID, ObjectIdGetDatum(collOid));
456 [ - + ]: 3 : if (!HeapTupleIsValid(tup))
1773 tmunro@postgresql.or 457 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for collation %u", collOid);
458 : :
1773 tmunro@postgresql.or 459 :CBC 3 : collForm = (Form_pg_collation) GETSTRUCT(tup);
1508 peter@eisentraut.org 460 : 3 : datum = SysCacheGetAttr(COLLOID, tup, Anum_pg_collation_collversion, &isnull);
461 [ + - ]: 3 : oldversion = isnull ? NULL : TextDatumGetCString(datum);
462 : :
732 jdavis@postgresql.or 463 [ - + ]: 3 : if (collForm->collprovider == COLLPROVIDER_LIBC)
732 jdavis@postgresql.or 464 :UBC 0 : datum = SysCacheGetAttrNotNull(COLLOID, tup, Anum_pg_collation_collcollate);
465 : : else
732 jdavis@postgresql.or 466 :CBC 3 : datum = SysCacheGetAttrNotNull(COLLOID, tup, Anum_pg_collation_colllocale);
467 : :
468 : 3 : newversion = get_collation_actual_version(collForm->collprovider,
469 : 3 : TextDatumGetCString(datum));
470 : :
471 : : /* cannot change from NULL to non-NULL or vice versa */
1773 tmunro@postgresql.or 472 [ - + - - : 3 : if ((!oldversion && newversion) || (oldversion && !newversion))
+ - - + ]
1773 tmunro@postgresql.or 473 [ # # ]:UBC 0 : elog(ERROR, "invalid collation version change");
1773 tmunro@postgresql.or 474 [ + - + - :CBC 3 : else if (oldversion && newversion && strcmp(newversion, oldversion) != 0)
- + ]
1773 tmunro@postgresql.or 475 :UBC 0 : {
476 : : bool nulls[Natts_pg_collation];
477 : : bool replaces[Natts_pg_collation];
478 : : Datum values[Natts_pg_collation];
479 : :
480 [ # # ]: 0 : ereport(NOTICE,
481 : : (errmsg("changing version from %s to %s",
482 : : oldversion, newversion)));
483 : :
484 : 0 : memset(values, 0, sizeof(values));
485 : 0 : memset(nulls, false, sizeof(nulls));
486 : 0 : memset(replaces, false, sizeof(replaces));
487 : :
488 : 0 : values[Anum_pg_collation_collversion - 1] = CStringGetTextDatum(newversion);
489 : 0 : replaces[Anum_pg_collation_collversion - 1] = true;
490 : :
491 : 0 : tup = heap_modify_tuple(tup, RelationGetDescr(rel),
492 : : values, nulls, replaces);
493 : : }
494 : : else
1773 tmunro@postgresql.or 495 [ + - ]:CBC 3 : ereport(NOTICE,
496 : : (errmsg("version has not changed")));
497 : :
498 : 3 : CatalogTupleUpdate(rel, &tup->t_self, tup);
499 : :
500 [ - + ]: 3 : InvokeObjectPostAlterHook(CollationRelationId, collOid, 0);
501 : :
502 : 3 : ObjectAddressSet(address, CollationRelationId, collOid);
503 : :
504 : 3 : heap_freetuple(tup);
505 : 3 : table_close(rel, NoLock);
506 : :
507 : 3 : return address;
508 : : }
509 : :
510 : :
511 : : Datum
1843 512 : 49 : pg_collation_actual_version(PG_FUNCTION_ARGS)
513 : : {
1031 tgl@sss.pgh.pa.us 514 : 49 : Oid collid = PG_GETARG_OID(0);
515 : : char provider;
516 : : char *locale;
517 : : char *version;
518 : : Datum datum;
519 : :
1233 jdavis@postgresql.or 520 [ - + ]: 49 : if (collid == DEFAULT_COLLATION_OID)
521 : : {
522 : : /* retrieve from pg_database */
523 : :
1233 jdavis@postgresql.or 524 :UBC 0 : HeapTuple dbtup = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
525 : :
526 [ # # ]: 0 : if (!HeapTupleIsValid(dbtup))
527 [ # # ]: 0 : ereport(ERROR,
528 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
529 : : errmsg("database with OID %u does not exist", MyDatabaseId)));
530 : :
531 : 0 : provider = ((Form_pg_database) GETSTRUCT(dbtup))->datlocprovider;
532 : :
732 533 [ # # ]: 0 : if (provider == COLLPROVIDER_LIBC)
534 : : {
535 : 0 : datum = SysCacheGetAttrNotNull(DATABASEOID, dbtup, Anum_pg_database_datcollate);
536 : 0 : locale = TextDatumGetCString(datum);
537 : : }
538 : : else
539 : : {
540 : 0 : datum = SysCacheGetAttrNotNull(DATABASEOID, dbtup, Anum_pg_database_datlocale);
541 : 0 : locale = TextDatumGetCString(datum);
542 : : }
543 : :
1233 544 : 0 : ReleaseSysCache(dbtup);
545 : : }
546 : : else
547 : : {
548 : : /* retrieve from pg_collation */
549 : :
1031 tgl@sss.pgh.pa.us 550 :CBC 49 : HeapTuple colltp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
551 : :
1233 jdavis@postgresql.or 552 [ - + ]: 49 : if (!HeapTupleIsValid(colltp))
1233 jdavis@postgresql.or 553 [ # # ]:UBC 0 : ereport(ERROR,
554 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
555 : : errmsg("collation with OID %u does not exist", collid)));
556 : :
1233 jdavis@postgresql.or 557 :CBC 49 : provider = ((Form_pg_collation) GETSTRUCT(colltp))->collprovider;
558 [ - + ]: 49 : Assert(provider != COLLPROVIDER_DEFAULT);
559 : :
732 560 [ - + ]: 49 : if (provider == COLLPROVIDER_LIBC)
561 : : {
732 jdavis@postgresql.or 562 :UBC 0 : datum = SysCacheGetAttrNotNull(COLLOID, colltp, Anum_pg_collation_collcollate);
563 : 0 : locale = TextDatumGetCString(datum);
564 : : }
565 : : else
566 : : {
732 jdavis@postgresql.or 567 :CBC 49 : datum = SysCacheGetAttrNotNull(COLLOID, colltp, Anum_pg_collation_colllocale);
568 : 49 : locale = TextDatumGetCString(datum);
569 : : }
570 : :
1233 571 : 49 : ReleaseSysCache(colltp);
572 : : }
573 : :
574 : 49 : version = get_collation_actual_version(provider, locale);
3279 peter_e@gmx.net 575 [ + - ]: 49 : if (version)
576 : 49 : PG_RETURN_TEXT_P(cstring_to_text(version));
577 : : else
3279 peter_e@gmx.net 578 :UBC 0 : PG_RETURN_NULL();
579 : : }
580 : :
581 : :
582 : : /* will we use "locale -a" in pg_import_system_collations? */
583 : : #if !defined(WIN32)
584 : : #define READ_LOCALE_A_OUTPUT
585 : : #endif
586 : :
587 : : /* will we use EnumSystemLocalesEx in pg_import_system_collations? */
588 : : #ifdef WIN32
589 : : #define ENUM_SYSTEM_LOCALE
590 : : #endif
591 : :
592 : :
593 : : #ifdef READ_LOCALE_A_OUTPUT
594 : : /*
595 : : * "Normalize" a libc locale name, stripping off encoding tags such as
596 : : * ".utf8" (e.g., "en_US.utf8" -> "en_US", but "br_FR.iso885915@euro"
597 : : * -> "br_FR@euro"). Return true if a new, different name was
598 : : * generated.
599 : : */
600 : : static bool
3279 peter_e@gmx.net 601 :CBC 24402 : normalize_libc_locale_name(char *new, const char *old)
602 : : {
3343 603 : 24402 : char *n = new;
604 : 24402 : const char *o = old;
605 : 24402 : bool changed = false;
606 : :
607 [ + + ]: 167531 : while (*o)
608 : : {
609 [ + + ]: 143129 : if (*o == '.')
610 : : {
611 : : /* skip over encoding tag such as ".utf8" or ".UTF-8" */
612 : 7938 : o++;
613 [ - + ]: 32046 : while ((*o >= 'A' && *o <= 'Z')
614 [ + + + - ]: 41209 : || (*o >= 'a' && *o <= 'z')
615 [ + + + - ]: 17101 : || (*o >= '0' && *o <= '9')
616 [ + + - + ]: 49147 : || (*o == '-'))
617 : 33271 : o++;
618 : 7938 : changed = true;
619 : : }
620 : : else
621 : 135191 : *n++ = *o++;
622 : : }
623 : 24402 : *n = '\0';
624 : :
625 : 24402 : return changed;
626 : : }
627 : :
628 : : /*
629 : : * qsort comparator for CollAliasData items
630 : : */
631 : : static int
3187 tgl@sss.pgh.pa.us 632 : 7889 : cmpaliases(const void *a, const void *b)
633 : : {
634 : 7889 : const CollAliasData *ca = (const CollAliasData *) a;
635 : 7889 : const CollAliasData *cb = (const CollAliasData *) b;
636 : :
637 : : /* comparing localename is enough because other fields are derived */
638 : 7889 : return strcmp(ca->localename, cb->localename);
639 : : }
640 : : #endif /* READ_LOCALE_A_OUTPUT */
641 : :
642 : :
643 : : #ifdef USE_ICU
644 : : /*
645 : : * Get a comment (specifically, the display name) for an ICU locale.
646 : : * The result is a palloc'd string, or NULL if we can't get a comment
647 : : * or find that it's not all ASCII. (We can *not* accept non-ASCII
648 : : * comments, because the contents of template0 must be encoding-agnostic.)
649 : : */
650 : : static char *
3279 peter_e@gmx.net 651 : 41748 : get_icu_locale_comment(const char *localename)
652 : : {
653 : : UErrorCode status;
654 : : UChar displayname[128];
655 : : int32 len_uchar;
656 : : int32 i;
657 : : char *result;
658 : :
659 : 41748 : status = U_ZERO_ERROR;
3187 tgl@sss.pgh.pa.us 660 : 41748 : len_uchar = uloc_getDisplayName(localename, "en",
661 : : displayname, lengthof(displayname),
662 : : &status);
3279 peter_e@gmx.net 663 [ - + ]: 41748 : if (U_FAILURE(status))
3186 tgl@sss.pgh.pa.us 664 :UBC 0 : return NULL; /* no good reason to raise an error */
665 : :
666 : : /* Check for non-ASCII comment (can't use pg_is_ascii for this) */
3186 tgl@sss.pgh.pa.us 667 [ + + ]:CBC 702170 : for (i = 0; i < len_uchar; i++)
668 : : {
669 [ + + ]: 661255 : if (displayname[i] > 127)
670 : 833 : return NULL;
671 : : }
672 : :
673 : : /* OK, transcribe */
674 : 40915 : result = palloc(len_uchar + 1);
675 [ + + ]: 694379 : for (i = 0; i < len_uchar; i++)
676 : 653464 : result[i] = displayname[i];
677 : 40915 : result[len_uchar] = '\0';
678 : :
3279 peter_e@gmx.net 679 : 40915 : return result;
680 : : }
681 : : #endif /* USE_ICU */
682 : :
683 : :
684 : : /*
685 : : * Create a new collation using the input locale 'locale'. (subroutine for
686 : : * pg_import_system_collations())
687 : : *
688 : : * 'nspid' is the namespace id where the collation will be created.
689 : : *
690 : : * 'nvalidp' is incremented if the locale has a valid encoding.
691 : : *
692 : : * 'ncreatedp' is incremented if the collation is actually created. If the
693 : : * collation already exists it will quietly do nothing.
694 : : *
695 : : * The returned value is the encoding of the locale, -1 if the locale is not
696 : : * valid for creating a collation.
697 : : *
698 : : */
699 : : pg_attribute_unused()
700 : : static int
1167 peter@eisentraut.org 701 : 25039 : create_collation_from_locale(const char *locale, int nspid,
702 : : int *nvalidp, int *ncreatedp)
703 : : {
704 : : int enc;
705 : : Oid collid;
706 : :
707 : : /*
708 : : * Some systems have locale names that don't consist entirely of ASCII
709 : : * letters (such as "bokmål" or "français"). This is pretty
710 : : * silly, since we need the locale itself to interpret the non-ASCII
711 : : * characters. We can't do much with those, so we filter them out.
712 : : */
713 [ - + ]: 25039 : if (!pg_is_ascii(locale))
714 : : {
1167 peter@eisentraut.org 715 [ # # ]:UBC 0 : elog(DEBUG1, "skipping locale with non-ASCII name: \"%s\"", locale);
716 : 0 : return -1;
717 : : }
718 : :
1167 peter@eisentraut.org 719 :CBC 25039 : enc = pg_get_encoding_from_locale(locale, false);
720 [ + + ]: 25039 : if (enc < 0)
721 : : {
722 [ - + ]: 294 : elog(DEBUG1, "skipping locale with unrecognized encoding: \"%s\"", locale);
723 : 294 : return -1;
724 : : }
725 [ + - + + ]: 24745 : if (!PG_VALID_BE_ENCODING(enc))
726 : : {
727 [ - + ]: 245 : elog(DEBUG1, "skipping locale with client-only encoding: \"%s\"", locale);
728 : 245 : return -1;
729 : : }
730 [ + + ]: 24500 : if (enc == PG_SQL_ASCII)
1031 tgl@sss.pgh.pa.us 731 : 98 : return -1; /* C/POSIX are already in the catalog */
732 : :
733 : : /* count valid locales found in operating system */
1167 peter@eisentraut.org 734 : 24402 : (*nvalidp)++;
735 : :
736 : : /*
737 : : * Create a collation named the same as the locale, but quietly doing
738 : : * nothing if it already exists. This is the behavior we need even at
739 : : * initdb time, because some versions of "locale -a" can report the same
740 : : * locale name more than once. And it's convenient for later import runs,
741 : : * too, since you just about always want to add on new locales without a
742 : : * lot of chatter about existing ones.
743 : : */
744 : 24402 : collid = CollationCreate(locale, nspid, GetUserId(),
745 : : COLLPROVIDER_LIBC, true, enc,
746 : : locale, locale, NULL, NULL,
747 : 24402 : get_collation_actual_version(COLLPROVIDER_LIBC, locale),
748 : : true, true);
749 [ + - ]: 24402 : if (OidIsValid(collid))
750 : : {
751 : 24402 : (*ncreatedp)++;
752 : :
753 : : /* Must do CCI between inserts to handle duplicates correctly */
754 : 24402 : CommandCounterIncrement();
755 : : }
756 : :
757 : 24402 : return enc;
758 : : }
759 : :
760 : :
761 : : #ifdef ENUM_SYSTEM_LOCALE
762 : : /* parameter to be passed to the callback function win32_read_locale() */
763 : : typedef struct
764 : : {
765 : : Oid nspid;
766 : : int *ncreatedp;
767 : : int *nvalidp;
768 : : } CollParam;
769 : :
770 : : /*
771 : : * Callback function for EnumSystemLocalesEx() in
772 : : * pg_import_system_collations(). Creates a collation for every valid locale
773 : : * and a POSIX alias collation.
774 : : *
775 : : * The callback contract is to return TRUE to continue enumerating and FALSE
776 : : * to stop enumerating. We always want to continue.
777 : : */
778 : : static BOOL CALLBACK
779 : : win32_read_locale(LPWSTR pStr, DWORD dwFlags, LPARAM lparam)
780 : : {
781 : : CollParam *param = (CollParam *) lparam;
782 : : char localebuf[NAMEDATALEN];
783 : : int result;
784 : : int enc;
785 : :
786 : : (void) dwFlags;
787 : :
788 : : result = WideCharToMultiByte(CP_ACP, 0, pStr, -1, localebuf, NAMEDATALEN,
789 : : NULL, NULL);
790 : :
791 : : if (result == 0)
792 : : {
793 : : if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
794 : : elog(DEBUG1, "skipping locale with too-long name: \"%s\"", localebuf);
795 : : return TRUE;
796 : : }
797 : : if (localebuf[0] == '\0')
798 : : return TRUE;
799 : :
800 : : enc = create_collation_from_locale(localebuf, param->nspid,
801 : : param->nvalidp, param->ncreatedp);
802 : : if (enc < 0)
803 : : return TRUE;
804 : :
805 : : /*
806 : : * Windows will use hyphens between language and territory, where POSIX
807 : : * uses an underscore. Simply create a POSIX alias.
808 : : */
809 : : if (strchr(localebuf, '-'))
810 : : {
811 : : char alias[NAMEDATALEN];
812 : : Oid collid;
813 : :
814 : : strcpy(alias, localebuf);
815 : : for (char *p = alias; *p; p++)
816 : : if (*p == '-')
817 : : *p = '_';
818 : :
819 : : collid = CollationCreate(alias, param->nspid, GetUserId(),
820 : : COLLPROVIDER_LIBC, true, enc,
821 : : localebuf, localebuf, NULL, NULL,
822 : : get_collation_actual_version(COLLPROVIDER_LIBC, localebuf),
823 : : true, true);
824 : : if (OidIsValid(collid))
825 : : {
826 : : (*param->ncreatedp)++;
827 : :
828 : : CommandCounterIncrement();
829 : : }
830 : : }
831 : :
832 : : return TRUE;
833 : : }
834 : : #endif /* ENUM_SYSTEM_LOCALE */
835 : :
836 : :
837 : : /*
838 : : * pg_import_system_collations: add known system collations to pg_collation
839 : : */
840 : : Datum
3343 peter_e@gmx.net 841 : 49 : pg_import_system_collations(PG_FUNCTION_ARGS)
842 : : {
3187 tgl@sss.pgh.pa.us 843 : 49 : Oid nspid = PG_GETARG_OID(0);
844 : 49 : int ncreated = 0;
845 : :
3343 peter_e@gmx.net 846 [ - + ]: 49 : if (!superuser())
3343 peter_e@gmx.net 847 [ # # ]:UBC 0 : ereport(ERROR,
848 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
849 : : errmsg("must be superuser to import system collations")));
850 : :
1833 tgl@sss.pgh.pa.us 851 [ - + ]:CBC 49 : if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(nspid)))
1833 tgl@sss.pgh.pa.us 852 [ # # ]:UBC 0 : ereport(ERROR,
853 : : (errcode(ERRCODE_UNDEFINED_SCHEMA),
854 : : errmsg("schema with OID %u does not exist", nspid)));
855 : :
856 : : /* Load collations known to libc, using "locale -a" to enumerate them */
857 : : #ifdef READ_LOCALE_A_OUTPUT
858 : : {
859 : : FILE *locale_a_handle;
860 : : char localebuf[LOCALE_NAME_BUFLEN];
3187 tgl@sss.pgh.pa.us 861 :CBC 49 : int nvalid = 0;
862 : : Oid collid;
863 : : CollAliasData *aliases;
864 : : int naliases,
865 : : maxaliases,
866 : : i;
867 : :
868 : : /* expansible array of aliases */
869 : 49 : maxaliases = 100;
870 : 49 : aliases = (CollAliasData *) palloc(maxaliases * sizeof(CollAliasData));
871 : 49 : naliases = 0;
872 : :
873 : 49 : locale_a_handle = OpenPipeStream("locale -a", "r");
874 [ - + ]: 49 : if (locale_a_handle == NULL)
3187 tgl@sss.pgh.pa.us 875 [ # # ]:UBC 0 : ereport(ERROR,
876 : : (errcode_for_file_access(),
877 : : errmsg("could not execute command \"%s\": %m",
878 : : "locale -a")));
879 : :
3187 tgl@sss.pgh.pa.us 880 [ + + ]:CBC 25088 : while (fgets(localebuf, sizeof(localebuf), locale_a_handle))
881 : : {
882 : : size_t len;
883 : : int enc;
884 : : char alias[LOCALE_NAME_BUFLEN];
885 : :
886 : 25039 : len = strlen(localebuf);
887 : :
888 [ + - - + ]: 25039 : if (len == 0 || localebuf[len - 1] != '\n')
889 : : {
1643 tgl@sss.pgh.pa.us 890 [ # # ]:UBC 0 : elog(DEBUG1, "skipping locale with too-long name: \"%s\"", localebuf);
3187 tgl@sss.pgh.pa.us 891 :CBC 637 : continue;
892 : : }
893 : 25039 : localebuf[len - 1] = '\0';
894 : :
1167 peter@eisentraut.org 895 : 25039 : enc = create_collation_from_locale(localebuf, nspid, &nvalid, &ncreated);
3187 tgl@sss.pgh.pa.us 896 [ + + ]: 25039 : if (enc < 0)
1643 897 : 637 : continue;
898 : :
899 : : /*
900 : : * Generate aliases such as "en_US" in addition to "en_US.utf8"
901 : : * for ease of use. Note that collation names are unique per
902 : : * encoding only, so this doesn't clash with "en_US" for LATIN1,
903 : : * say.
904 : : *
905 : : * However, it might conflict with a name we'll see later in the
906 : : * "locale -a" output. So save up the aliases and try to add them
907 : : * after we've read all the output.
908 : : */
3187 909 [ + + ]: 24402 : if (normalize_libc_locale_name(alias, localebuf))
910 : : {
911 [ + + ]: 7938 : if (naliases >= maxaliases)
912 : : {
913 : 49 : maxaliases *= 2;
914 : : aliases = (CollAliasData *)
915 : 49 : repalloc(aliases, maxaliases * sizeof(CollAliasData));
916 : : }
917 : 7938 : aliases[naliases].localename = pstrdup(localebuf);
918 : 7938 : aliases[naliases].alias = pstrdup(alias);
919 : 7938 : aliases[naliases].enc = enc;
920 : 7938 : naliases++;
921 : : }
922 : : }
923 : :
924 : : /*
925 : : * We don't check the return value of this, because we want to support
926 : : * the case where there "locale" command does not exist. (This is
927 : : * unusual but can happen on minimalized Linux distributions, for
928 : : * example.) We will warn below if no locales could be found.
929 : : */
1214 peter@eisentraut.org 930 : 49 : ClosePipeStream(locale_a_handle);
931 : :
932 : : /*
933 : : * Before processing the aliases, sort them by locale name. The point
934 : : * here is that if "locale -a" gives us multiple locale names with the
935 : : * same encoding and base name, say "en_US.utf8" and "en_US.utf-8", we
936 : : * want to pick a deterministic one of them. First in ASCII sort
937 : : * order is a good enough rule. (Before PG 10, the code corresponding
938 : : * to this logic in initdb.c had an additional ordering rule, to
939 : : * prefer the locale name exactly matching the alias, if any. We
940 : : * don't need to consider that here, because we would have already
941 : : * created such a pg_collation entry above, and that one will win.)
942 : : */
3187 tgl@sss.pgh.pa.us 943 [ + - ]: 49 : if (naliases > 1)
1132 peter@eisentraut.org 944 : 49 : qsort(aliases, naliases, sizeof(CollAliasData), cmpaliases);
945 : :
946 : : /* Now add aliases, ignoring any that match pre-existing entries */
3187 tgl@sss.pgh.pa.us 947 [ + + ]: 7987 : for (i = 0; i < naliases; i++)
948 : : {
949 : 7938 : char *locale = aliases[i].localename;
950 : 7938 : char *alias = aliases[i].alias;
951 : 7938 : int enc = aliases[i].enc;
952 : :
953 : 7938 : collid = CollationCreate(alias, nspid, GetUserId(),
954 : : COLLPROVIDER_LIBC, true, enc,
955 : : locale, locale, NULL, NULL,
1773 tmunro@postgresql.or 956 : 7938 : get_collation_actual_version(COLLPROVIDER_LIBC, locale),
957 : : true, true);
3187 tgl@sss.pgh.pa.us 958 [ + + ]: 7938 : if (OidIsValid(collid))
959 : : {
960 : 7889 : ncreated++;
961 : :
962 : 7889 : CommandCounterIncrement();
963 : : }
964 : : }
965 : :
966 : : /* Give a warning if "locale -a" seems to be malfunctioning */
967 [ - + ]: 49 : if (nvalid == 0)
3187 tgl@sss.pgh.pa.us 968 [ # # ]:UBC 0 : ereport(WARNING,
969 : : (errmsg("no usable system locales were found")));
970 : : }
971 : : #endif /* READ_LOCALE_A_OUTPUT */
972 : :
973 : : /*
974 : : * Load collations known to ICU
975 : : *
976 : : * We use uloc_countAvailable()/uloc_getAvailable() rather than
977 : : * ucol_countAvailable()/ucol_getAvailable(). The former returns a full
978 : : * set of language+region combinations, whereas the latter only returns
979 : : * language+region combinations if they are distinct from the language's
980 : : * base collation. So there might not be a de-DE or en-GB, which would be
981 : : * confusing.
982 : : */
983 : : #ifdef USE_ICU
984 : : {
985 : : int i;
986 : :
987 : : /*
988 : : * Start the loop at -1 to sneak in the root locale without too much
989 : : * code duplication.
990 : : */
3128 peter_e@gmx.net 991 [ + + ]:CBC 41797 : for (i = -1; i < uloc_countAvailable(); i++)
992 : : {
993 : : const char *name;
994 : : char *langtag;
995 : : char *icucomment;
996 : : Oid collid;
997 : :
3279 998 [ + + ]: 41748 : if (i == -1)
3224 bruce@momjian.us 999 : 49 : name = ""; /* ICU root locale */
1000 : : else
3128 peter_e@gmx.net 1001 : 41699 : name = uloc_getAvailable(i);
1002 : :
1076 jdavis@postgresql.or 1003 : 41748 : langtag = icu_language_tag(name, ERROR);
1004 : :
1005 : : /*
1006 : : * Be paranoid about not allowing any non-ASCII strings into
1007 : : * pg_collation
1008 : : */
1090 1009 [ - + ]: 41748 : if (!pg_is_ascii(langtag))
3186 tgl@sss.pgh.pa.us 1010 :UBC 0 : continue;
1011 : :
3279 peter_e@gmx.net 1012 :CBC 41748 : collid = CollationCreate(psprintf("%s-x-icu", langtag),
1013 : : nspid, GetUserId(),
1014 : : COLLPROVIDER_ICU, true, -1,
1015 : : NULL, NULL, langtag, NULL,
1090 jdavis@postgresql.or 1016 : 41748 : get_collation_actual_version(COLLPROVIDER_ICU, langtag),
1017 : : true, true);
3187 tgl@sss.pgh.pa.us 1018 [ + - ]: 41748 : if (OidIsValid(collid))
1019 : : {
1020 : 41748 : ncreated++;
1021 : :
1022 : 41748 : CommandCounterIncrement();
1023 : :
3186 1024 : 41748 : icucomment = get_icu_locale_comment(name);
1025 [ + + ]: 41748 : if (icucomment)
1026 : 40915 : CreateComments(collid, CollationRelationId, 0,
1027 : : icucomment);
1028 : : }
1029 : : }
1030 : : }
1031 : : #endif /* USE_ICU */
1032 : :
1033 : : /* Load collations known to WIN32 */
1034 : : #ifdef ENUM_SYSTEM_LOCALE
1035 : : {
1036 : : int nvalid = 0;
1037 : : CollParam param;
1038 : :
1039 : : param.nspid = nspid;
1040 : : param.ncreatedp = &ncreated;
1041 : : param.nvalidp = &nvalid;
1042 : :
1043 : : /*
1044 : : * Enumerate the locales that are either installed on or supported by
1045 : : * the OS.
1046 : : */
1047 : : if (!EnumSystemLocalesEx(win32_read_locale, LOCALE_ALL,
1048 : : (LPARAM) ¶m, NULL))
1049 : : _dosmaperr(GetLastError());
1050 : :
1051 : : /* Give a warning if EnumSystemLocalesEx seems to be malfunctioning */
1052 : : if (nvalid == 0)
1053 : : ereport(WARNING,
1054 : : (errmsg("no usable system locales were found")));
1055 : : }
1056 : : #endif /* ENUM_SYSTEM_LOCALE */
1057 : :
3187 1058 : 49 : PG_RETURN_INT32(ncreated);
1059 : : }
|