PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
collationcmds.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * collationcmds.c
4  * collation-related commands support code
5  *
6  * Portions Copyright (c) 1996-2017, 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 #include "access/htup_details.h"
18 #include "access/xact.h"
19 #include "catalog/dependency.h"
20 #include "catalog/indexing.h"
21 #include "catalog/namespace.h"
22 #include "catalog/pg_collation.h"
24 #include "commands/alter.h"
25 #include "commands/collationcmds.h"
26 #include "commands/dbcommands.h"
27 #include "commands/defrem.h"
28 #include "mb/pg_wchar.h"
29 #include "miscadmin.h"
30 #include "utils/builtins.h"
31 #include "utils/lsyscache.h"
32 #include "utils/pg_locale.h"
33 #include "utils/rel.h"
34 #include "utils/syscache.h"
35 
36 /*
37  * CREATE COLLATION
38  */
40 DefineCollation(ParseState *pstate, List *names, List *parameters, bool if_not_exists)
41 {
42  char *collName;
43  Oid collNamespace;
44  AclResult aclresult;
45  ListCell *pl;
46  DefElem *fromEl = NULL;
47  DefElem *localeEl = NULL;
48  DefElem *lccollateEl = NULL;
49  DefElem *lcctypeEl = NULL;
50  char *collcollate = NULL;
51  char *collctype = NULL;
52  Oid newoid;
53  ObjectAddress address;
54 
55  collNamespace = QualifiedNameGetCreationNamespace(names, &collName);
56 
57  aclresult = pg_namespace_aclcheck(collNamespace, GetUserId(), ACL_CREATE);
58  if (aclresult != ACLCHECK_OK)
60  get_namespace_name(collNamespace));
61 
62  foreach(pl, parameters)
63  {
64  DefElem *defel = castNode(DefElem, lfirst(pl));
65  DefElem **defelp;
66 
67  if (pg_strcasecmp(defel->defname, "from") == 0)
68  defelp = &fromEl;
69  else if (pg_strcasecmp(defel->defname, "locale") == 0)
70  defelp = &localeEl;
71  else if (pg_strcasecmp(defel->defname, "lc_collate") == 0)
72  defelp = &lccollateEl;
73  else if (pg_strcasecmp(defel->defname, "lc_ctype") == 0)
74  defelp = &lcctypeEl;
75  else
76  {
77  ereport(ERROR,
78  (errcode(ERRCODE_SYNTAX_ERROR),
79  errmsg("collation attribute \"%s\" not recognized",
80  defel->defname),
81  parser_errposition(pstate, defel->location)));
82  break;
83  }
84 
85  *defelp = defel;
86  }
87 
88  if ((localeEl && (lccollateEl || lcctypeEl))
89  || (fromEl && list_length(parameters) != 1))
90  ereport(ERROR,
91  (errcode(ERRCODE_SYNTAX_ERROR),
92  errmsg("conflicting or redundant options")));
93 
94  if (fromEl)
95  {
96  Oid collid;
97  HeapTuple tp;
98 
99  collid = get_collation_oid(defGetQualifiedName(fromEl), false);
101  if (!HeapTupleIsValid(tp))
102  elog(ERROR, "cache lookup failed for collation %u", collid);
103 
104  collcollate = pstrdup(NameStr(((Form_pg_collation) GETSTRUCT(tp))->collcollate));
105  collctype = pstrdup(NameStr(((Form_pg_collation) GETSTRUCT(tp))->collctype));
106 
107  ReleaseSysCache(tp);
108  }
109 
110  if (localeEl)
111  {
112  collcollate = defGetString(localeEl);
113  collctype = defGetString(localeEl);
114  }
115 
116  if (lccollateEl)
117  collcollate = defGetString(lccollateEl);
118 
119  if (lcctypeEl)
120  collctype = defGetString(lcctypeEl);
121 
122  if (!collcollate)
123  ereport(ERROR,
124  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
125  errmsg("parameter \"lc_collate\" must be specified")));
126 
127  if (!collctype)
128  ereport(ERROR,
129  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
130  errmsg("parameter \"lc_ctype\" must be specified")));
131 
132  check_encoding_locale_matches(GetDatabaseEncoding(), collcollate, collctype);
133 
134  newoid = CollationCreate(collName,
135  collNamespace,
136  GetUserId(),
138  collcollate,
139  collctype,
140  if_not_exists);
141 
142  if (!OidIsValid(newoid))
143  return InvalidObjectAddress;
144 
145  ObjectAddressSet(address, CollationRelationId, newoid);
146 
147  /* check that the locales can be loaded */
149  (void) pg_newlocale_from_collation(newoid);
150 
151  return address;
152 }
153 
154 /*
155  * Subroutine for ALTER COLLATION SET SCHEMA and RENAME
156  *
157  * Is there a collation with the same name of the given collation already in
158  * the given namespace? If so, raise an appropriate error message.
159  */
160 void
161 IsThereCollationInNamespace(const char *collname, Oid nspOid)
162 {
163  /* make sure the name doesn't already exist in new schema */
165  CStringGetDatum(collname),
167  ObjectIdGetDatum(nspOid)))
168  ereport(ERROR,
170  errmsg("collation \"%s\" for encoding \"%s\" already exists in schema \"%s\"",
171  collname, GetDatabaseEncodingName(),
172  get_namespace_name(nspOid))));
173 
174  /* mustn't match an any-encoding entry, either */
176  CStringGetDatum(collname),
177  Int32GetDatum(-1),
178  ObjectIdGetDatum(nspOid)))
179  ereport(ERROR,
181  errmsg("collation \"%s\" already exists in schema \"%s\"",
182  collname, get_namespace_name(nspOid))));
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  */
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
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  List *aliaslist = NIL;
233  List *localelist = NIL;
234  List *enclist = NIL;
235  ListCell *lca,
236  *lcl,
237  *lce;
238 #endif
239 
240  if (!superuser())
241  ereport(ERROR,
242  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
243  (errmsg("must be superuser to import system collations"))));
244 
245 #if defined(HAVE_LOCALE_T) && !defined(WIN32)
246  locale_a_handle = OpenPipeStream("locale -a", "r");
247  if (locale_a_handle == NULL)
248  ereport(ERROR,
250  errmsg("could not execute command \"%s\": %m",
251  "locale -a")));
252 
253  while (fgets(localebuf, sizeof(localebuf), locale_a_handle))
254  {
255  int i;
256  size_t len;
257  int enc;
258  bool skip;
259  char alias[NAMEDATALEN];
260 
261  len = strlen(localebuf);
262 
263  if (len == 0 || localebuf[len - 1] != '\n')
264  {
265  elog(DEBUG1, "locale name too long, skipped: \"%s\"", localebuf);
266  continue;
267  }
268  localebuf[len - 1] = '\0';
269 
270  /*
271  * Some systems have locale names that don't consist entirely of ASCII
272  * letters (such as "bokm&aring;l" or "fran&ccedil;ais"). This is
273  * pretty silly, since we need the locale itself to interpret the
274  * non-ASCII characters. We can't do much with those, so we filter
275  * them out.
276  */
277  skip = false;
278  for (i = 0; i < len; i++)
279  {
280  if (IS_HIGHBIT_SET(localebuf[i]))
281  {
282  skip = true;
283  break;
284  }
285  }
286  if (skip)
287  {
288  elog(DEBUG1, "locale name has non-ASCII characters, skipped: \"%s\"", localebuf);
289  continue;
290  }
291 
292  enc = pg_get_encoding_from_locale(localebuf, false);
293  if (enc < 0)
294  {
295  /* error message printed by pg_get_encoding_from_locale() */
296  continue;
297  }
298  if (!PG_VALID_BE_ENCODING(enc))
299  continue; /* ignore locales for client-only encodings */
300  if (enc == PG_SQL_ASCII)
301  continue; /* C/POSIX are already in the catalog */
302 
303  count++;
304 
305  CollationCreate(localebuf, nspid, GetUserId(), enc,
306  localebuf, localebuf, if_not_exists);
307 
309 
310  /*
311  * Generate aliases such as "en_US" in addition to "en_US.utf8" for
312  * ease of use. Note that collation names are unique per encoding
313  * only, so this doesn't clash with "en_US" for LATIN1, say.
314  *
315  * However, it might conflict with a name we'll see later in the
316  * "locale -a" output. So save up the aliases and try to add them
317  * after we've read all the output.
318  */
319  if (normalize_locale_name(alias, localebuf))
320  {
321  aliaslist = lappend(aliaslist, pstrdup(alias));
322  localelist = lappend(localelist, pstrdup(localebuf));
323  enclist = lappend_int(enclist, enc);
324  }
325  }
326 
327  ClosePipeStream(locale_a_handle);
328 
329  /* Now try to add any aliases we created */
330  forthree(lca, aliaslist, lcl, localelist, lce, enclist)
331  {
332  char *alias = (char *) lfirst(lca);
333  char *locale = (char *) lfirst(lcl);
334  int enc = lfirst_int(lce);
335 
336  CollationCreate(alias, nspid, GetUserId(), enc,
337  locale, locale, true);
339  }
340 
341  if (count == 0)
343  (errmsg("no usable system locales were found")));
344 #endif /* not HAVE_LOCALE_T && not WIN32 */
345 
346  PG_RETURN_VOID();
347 }
#define NIL
Definition: pg_list.h:69
static void skip(struct vars *v)
Definition: regc_lex.c:1109
#define DEBUG1
Definition: elog.h:25
#define GETSTRUCT(TUP)
Definition: htup_details.h:656
void check_encoding_locale_matches(int encoding, const char *collate, const char *ctype)
Definition: dbcommands.c:723
Oid GetUserId(void)
Definition: miscinit.c:283
#define castNode(_type_, nodeptr)
Definition: nodes.h:577
Oid QualifiedNameGetCreationNamespace(List *names, char **objname_p)
Definition: namespace.c:2790
#define forthree(cell1, list1, cell2, list2, cell3, list3)
Definition: pg_list.h:183
char * pstrdup(const char *in)
Definition: mcxt.c:1165
int errcode(int sqlerrcode)
Definition: elog.c:575
bool superuser(void)
Definition: superuser.c:47
#define PG_GETARG_BOOL(n)
Definition: fmgr.h:230
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
unsigned int Oid
Definition: postgres_ext.h:31
Oid CollationCreate(const char *collname, Oid collnamespace, Oid collowner, int32 collencoding, const char *collcollate, const char *collctype, bool if_not_exists)
Definition: pg_collation.c:41
#define OidIsValid(objectId)
Definition: c.h:533
AclResult pg_namespace_aclcheck(Oid nsp_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4459
#define SearchSysCache1(cacheId, key1)
Definition: syscache.h:149
int ClosePipeStream(FILE *file)
Definition: fd.c:2419
#define NAMEDATALEN
pg_attribute_unused()
#define IS_HIGHBIT_SET(ch)
Definition: c.h:968
#define ObjectIdGetDatum(X)
Definition: postgres.h:515
#define ERROR
Definition: elog.h:43
#define ACL_CREATE
Definition: parsenodes.h:75
#define SearchSysCacheExists3(cacheId, key1, key2, key3)
Definition: syscache.h:171
struct pg_encoding enc
Definition: encode.c:522
#define lfirst_int(lc)
Definition: pg_list.h:107
char * defGetString(DefElem *def)
Definition: define.c:49
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3006
#define PG_GETARG_OID(n)
Definition: fmgr.h:231
void aclcheck_error(AclResult aclerr, AclObjectKind objectkind, const char *objectname)
Definition: aclchk.c:3378
int location
Definition: parsenodes.h:678
int errcode_for_file_access(void)
Definition: elog.c:598
#define CStringGetDatum(X)
Definition: postgres.h:586
void IsThereCollationInNamespace(const char *collname, Oid nspOid)
static const struct @26 enclist[]
FILE * OpenPipeStream(const char *command, const char *mode)
Definition: fd.c:2133
#define ereport(elevel, rest)
Definition: elog.h:122
List * lappend_int(List *list, int datum)
Definition: list.c:146
List * lappend(List *list, void *datum)
Definition: list.c:128
#define WARNING
Definition: elog.h:40
pg_locale_t pg_newlocale_from_collation(Oid collid)
Definition: pg_locale.c:1260
AclResult
Definition: acl.h:170
uintptr_t Datum
Definition: postgres.h:374
void CommandCounterIncrement(void)
Definition: xact.c:921
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1083
int GetDatabaseEncoding(void)
Definition: mbutils.c:1015
#define CollationRelationId
Definition: pg_collation.h:30
ObjectAddress DefineCollation(ParseState *pstate, List *names, List *parameters, bool if_not_exists)
Definition: collationcmds.c:40
int pg_get_encoding_from_locale(const char *ctype, bool write_message)
Definition: chklocale.c:438
#define PG_RETURN_VOID()
Definition: fmgr.h:293
#define PG_VALID_BE_ENCODING(_enc)
Definition: pg_wchar.h:293
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
#define NULL
Definition: c.h:226
#define lfirst(lc)
Definition: pg_list.h:106
const char * GetDatabaseEncodingName(void)
Definition: mbutils.c:1021
Datum lca(PG_FUNCTION_ARGS)
Definition: ltree_op.c:471
List * defGetQualifiedName(DefElem *def)
Definition: define.c:223
static int list_length(const List *l)
Definition: pg_list.h:89
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:109
FormData_pg_collation * Form_pg_collation
Definition: pg_collation.h:47
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
#define Int32GetDatum(X)
Definition: postgres.h:487
const ObjectAddress InvalidObjectAddress
Datum pg_import_system_collations(PG_FUNCTION_ARGS)
int errmsg(const char *fmt,...)
Definition: elog.c:797
int i
#define NameStr(name)
Definition: c.h:494
static char * locale
Definition: initdb.c:122
#define PG_FUNCTION_ARGS
Definition: fmgr.h:150
char * defname
Definition: parsenodes.h:675
#define elog
Definition: elog.h:219
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:34
Oid get_collation_oid(List *name, bool missing_ok)
Definition: namespace.c:3324
Definition: pg_list.h:45