@@ -136,7 +136,11 @@ DefineCollation(ParseState *pstate, List *names, List *parameters)
136
136
GetUserId (),
137
137
GetDatabaseEncoding (),
138
138
collcollate ,
139
- collctype );
139
+ collctype ,
140
+ false);
141
+
142
+ if (!OidIsValid (newoid ))
143
+ return InvalidObjectAddress ;
140
144
141
145
ObjectAddressSet (address , CollationRelationId , newoid );
142
146
@@ -177,3 +181,151 @@ IsThereCollationInNamespace(const char *collname, Oid nspOid)
177
181
errmsg ("collation \"%s\" already exists in schema \"%s\"" ,
178
182
collname , get_namespace_name (nspOid ))));
179
183
}
184
+
185
+
186
+ /*
187
+ * "Normalize" a locale name, stripping off encoding tags such as
188
+ * ".utf8" (e.g., "en_US.utf8" -> "en_US", but "br_FR.iso885915@euro"
189
+ * -> "br_FR@euro"). Return true if a new, different name was
190
+ * generated.
191
+ */
192
+ pg_attribute_unused ()
193
+ static bool
194
+ normalize_locale_name (char * new , const char * old )
195
+ {
196
+ char * n = new ;
197
+ const char * o = old ;
198
+ bool changed = false;
199
+
200
+ while (* o )
201
+ {
202
+ if (* o == '.' )
203
+ {
204
+ /* skip over encoding tag such as ".utf8" or ".UTF-8" */
205
+ o ++ ;
206
+ while ((* o >= 'A' && * o <= 'Z' )
207
+ || (* o >= 'a' && * o <= 'z' )
208
+ || (* o >= '0' && * o <= '9' )
209
+ || (* o == '-' ))
210
+ o ++ ;
211
+ changed = true;
212
+ }
213
+ else
214
+ * n ++ = * o ++ ;
215
+ }
216
+ * n = '\0' ;
217
+
218
+ return changed ;
219
+ }
220
+
221
+
222
+ Datum
223
+ pg_import_system_collations (PG_FUNCTION_ARGS )
224
+ {
225
+ #if defined(HAVE_LOCALE_T ) && !defined(WIN32 )
226
+ bool if_not_exists = PG_GETARG_BOOL (0 );
227
+ Oid nspid = PG_GETARG_OID (1 );
228
+
229
+ FILE * locale_a_handle ;
230
+ char localebuf [NAMEDATALEN ]; /* we assume ASCII so this is fine */
231
+ int count = 0 ;
232
+ #endif
233
+
234
+ if (!superuser ())
235
+ ereport (ERROR ,
236
+ (errcode (ERRCODE_INSUFFICIENT_PRIVILEGE ),
237
+ (errmsg ("must be superuser to import system collations" ))));
238
+
239
+ #if defined(HAVE_LOCALE_T ) && !defined(WIN32 )
240
+ locale_a_handle = OpenPipeStream ("locale -a" , "r" );
241
+ if (locale_a_handle == NULL )
242
+ ereport (ERROR ,
243
+ (errcode_for_file_access (),
244
+ errmsg ("could not execute command \"%s\": %m" ,
245
+ "locale -a" )));
246
+
247
+ while (fgets (localebuf , sizeof (localebuf ), locale_a_handle ))
248
+ {
249
+ int i ;
250
+ size_t len ;
251
+ int enc ;
252
+ bool skip ;
253
+ char alias [NAMEDATALEN ];
254
+
255
+ len = strlen (localebuf );
256
+
257
+ if (len == 0 || localebuf [len - 1 ] != '\n' )
258
+ {
259
+ elog (DEBUG1 , "locale name too long, skipped: \"%s\"" , localebuf );
260
+ continue ;
261
+ }
262
+ localebuf [len - 1 ] = '\0' ;
263
+
264
+ /*
265
+ * Some systems have locale names that don't consist entirely of ASCII
266
+ * letters (such as "bokmål" or "français"). This is
267
+ * pretty silly, since we need the locale itself to interpret the
268
+ * non-ASCII characters. We can't do much with those, so we filter
269
+ * them out.
270
+ */
271
+ skip = false;
272
+ for (i = 0 ; i < len ; i ++ )
273
+ {
274
+ if (IS_HIGHBIT_SET (localebuf [i ]))
275
+ {
276
+ skip = true;
277
+ break ;
278
+ }
279
+ }
280
+ if (skip )
281
+ {
282
+ elog (DEBUG1 , "locale name has non-ASCII characters, skipped: \"%s\"" , localebuf );
283
+ continue ;
284
+ }
285
+
286
+ enc = pg_get_encoding_from_locale (localebuf , false);
287
+ if (enc < 0 )
288
+ {
289
+ /* error message printed by pg_get_encoding_from_locale() */
290
+ continue ;
291
+ }
292
+ if (!PG_VALID_BE_ENCODING (enc ))
293
+ continue ; /* ignore locales for client-only encodings */
294
+ if (enc == PG_SQL_ASCII )
295
+ continue ; /* C/POSIX are already in the catalog */
296
+
297
+ count ++ ;
298
+
299
+ CollationCreate (localebuf , nspid , GetUserId (), enc ,
300
+ localebuf , localebuf , if_not_exists );
301
+
302
+ CommandCounterIncrement ();
303
+
304
+ /*
305
+ * Generate aliases such as "en_US" in addition to "en_US.utf8" for
306
+ * ease of use. Note that collation names are unique per encoding
307
+ * only, so this doesn't clash with "en_US" for LATIN1, say.
308
+ *
309
+ * This always runs in "if not exists" mode, to skip aliases that
310
+ * conflict with an existing locale name for the same encoding. For
311
+ * example, "br_FR.iso88591" is normalized to "br_FR", both for
312
+ * encoding LATIN1. But the unnormalized locale "br_FR" already
313
+ * exists for LATIN1.
314
+ */
315
+ if (normalize_locale_name (alias , localebuf ))
316
+ {
317
+ CollationCreate (alias , nspid , GetUserId (), enc ,
318
+ localebuf , localebuf , true);
319
+ CommandCounterIncrement ();
320
+ }
321
+ }
322
+
323
+ ClosePipeStream (locale_a_handle );
324
+
325
+ if (count == 0 )
326
+ ereport (ERROR ,
327
+ (errmsg ("no usable system locales were found" )));
328
+ #endif /* not HAVE_LOCALE_T && not WIN32 */
329
+
330
+ PG_RETURN_VOID ();
331
+ }
0 commit comments