1/*-------------------------------------------------------------------------
2 *
3 * name.c
4 * Functions for the built-in type "name".
5 *
6 * name replaces char16 and is carefully implemented so that it
7 * is a string of physical length NAMEDATALEN.
8 * DO NOT use hard-coded constants anywhere
9 * always use NAMEDATALEN as the symbolic constant! - jolly 8/21/95
10 *
11 *
12 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
13 * Portions Copyright (c) 1994, Regents of the University of California
14 *
15 *
16 * IDENTIFICATION
17 * src/backend/utils/adt/name.c
18 *
19 *-------------------------------------------------------------------------
20 */
21#include "postgres.h"
22
23#include "catalog/namespace.h"
24#include "catalog/pg_collation.h"
25#include "catalog/pg_type.h"
26#include "libpq/pqformat.h"
27#include "mb/pg_wchar.h"
28#include "miscadmin.h"
29#include "utils/array.h"
30#include "utils/builtins.h"
31#include "utils/lsyscache.h"
32#include "utils/varlena.h"
33
34
35/*****************************************************************************
36 * USER I/O ROUTINES (none) *
37 *****************************************************************************/
38
39
40/*
41 * namein - converts "..." to internal representation
42 *
43 * Note:
44 * [Old] Currently if strlen(s) < NAMEDATALEN, the extra chars are nulls
45 * Now, always NULL terminated
46 */
47Datum
48namein(PG_FUNCTION_ARGS)
49{
50 char *s = PG_GETARG_CSTRING(0);
51 Name result;
52 int len;
53
54 len = strlen(s);
55
56 /* Truncate oversize input */
57 if (len >= NAMEDATALEN)
58 len = pg_mbcliplen(s, len, NAMEDATALEN - 1);
59
60 /* We use palloc0 here to ensure result is zero-padded */
61 result = (Name) palloc0(NAMEDATALEN);
62 memcpy(NameStr(*result), s, len);
63
64 PG_RETURN_NAME(result);
65}
66
67/*
68 * nameout - converts internal representation to "..."
69 */
70Datum
71nameout(PG_FUNCTION_ARGS)
72{
73 Name s = PG_GETARG_NAME(0);
74
75 PG_RETURN_CSTRING(pstrdup(NameStr(*s)));
76}
77
78/*
79 * namerecv - converts external binary format to name
80 */
81Datum
82namerecv(PG_FUNCTION_ARGS)
83{
84 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
85 Name result;
86 char *str;
87 int nbytes;
88
89 str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
90 if (nbytes >= NAMEDATALEN)
91 ereport(ERROR,
92 (errcode(ERRCODE_NAME_TOO_LONG),
93 errmsg("identifier too long"),
94 errdetail("Identifier must be less than %d characters.",
95 NAMEDATALEN)));
96 result = (NameData *) palloc0(NAMEDATALEN);
97 memcpy(result, str, nbytes);
98 pfree(str);
99 PG_RETURN_NAME(result);
100}
101
102/*
103 * namesend - converts name to binary format
104 */
105Datum
106namesend(PG_FUNCTION_ARGS)
107{
108 Name s = PG_GETARG_NAME(0);
109 StringInfoData buf;
110
111 pq_begintypsend(&buf);
112 pq_sendtext(&buf, NameStr(*s), strlen(NameStr(*s)));
113 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
114}
115
116
117/*****************************************************************************
118 * COMPARISON/SORTING ROUTINES *
119 *****************************************************************************/
120
121/*
122 * nameeq - returns 1 iff arguments are equal
123 * namene - returns 1 iff arguments are not equal
124 * namelt - returns 1 iff a < b
125 * namele - returns 1 iff a <= b
126 * namegt - returns 1 iff a > b
127 * namege - returns 1 iff a >= b
128 *
129 * Note that the use of strncmp with NAMEDATALEN limit is mostly historical;
130 * strcmp would do as well, because we do not allow NAME values that don't
131 * have a '\0' terminator. Whatever might be past the terminator is not
132 * considered relevant to comparisons.
133 */
134static int
135namecmp(Name arg1, Name arg2, Oid collid)
136{
137 /* Fast path for common case used in system catalogs */
138 if (collid == C_COLLATION_OID)
139 return strncmp(NameStr(*arg1), NameStr(*arg2), NAMEDATALEN);
140
141 /* Else rely on the varstr infrastructure */
142 return varstr_cmp(NameStr(*arg1), strlen(NameStr(*arg1)),
143 NameStr(*arg2), strlen(NameStr(*arg2)),
144 collid);
145}
146
147Datum
148nameeq(PG_FUNCTION_ARGS)
149{
150 Name arg1 = PG_GETARG_NAME(0);
151 Name arg2 = PG_GETARG_NAME(1);
152
153 PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) == 0);
154}
155
156Datum
157namene(PG_FUNCTION_ARGS)
158{
159 Name arg1 = PG_GETARG_NAME(0);
160 Name arg2 = PG_GETARG_NAME(1);
161
162 PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) != 0);
163}
164
165Datum
166namelt(PG_FUNCTION_ARGS)
167{
168 Name arg1 = PG_GETARG_NAME(0);
169 Name arg2 = PG_GETARG_NAME(1);
170
171 PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) < 0);
172}
173
174Datum
175namele(PG_FUNCTION_ARGS)
176{
177 Name arg1 = PG_GETARG_NAME(0);
178 Name arg2 = PG_GETARG_NAME(1);
179
180 PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) <= 0);
181}
182
183Datum
184namegt(PG_FUNCTION_ARGS)
185{
186 Name arg1 = PG_GETARG_NAME(0);
187 Name arg2 = PG_GETARG_NAME(1);
188
189 PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) > 0);
190}
191
192Datum
193namege(PG_FUNCTION_ARGS)
194{
195 Name arg1 = PG_GETARG_NAME(0);
196 Name arg2 = PG_GETARG_NAME(1);
197
198 PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) >= 0);
199}
200
201Datum
202btnamecmp(PG_FUNCTION_ARGS)
203{
204 Name arg1 = PG_GETARG_NAME(0);
205 Name arg2 = PG_GETARG_NAME(1);
206
207 PG_RETURN_INT32(namecmp(arg1, arg2, PG_GET_COLLATION()));
208}
209
210Datum
211btnamesortsupport(PG_FUNCTION_ARGS)
212{
213 SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
214 Oid collid = ssup->ssup_collation;
215 MemoryContext oldcontext;
216
217 oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt);
218
219 /* Use generic string SortSupport */
220 varstr_sortsupport(ssup, NAMEOID, collid);
221
222 MemoryContextSwitchTo(oldcontext);
223
224 PG_RETURN_VOID();
225}
226
227
228/*****************************************************************************
229 * MISCELLANEOUS PUBLIC ROUTINES *
230 *****************************************************************************/
231
232int
233namecpy(Name n1, const NameData *n2)
234{
235 if (!n1 || !n2)
236 return -1;
237 StrNCpy(NameStr(*n1), NameStr(*n2), NAMEDATALEN);
238 return 0;
239}
240
241#ifdef NOT_USED
242int
243namecat(Name n1, Name n2)
244{
245 return namestrcat(n1, NameStr(*n2)); /* n2 can't be any longer than n1 */
246}
247#endif
248
249int
250namestrcpy(Name name, const char *str)
251{
252 if (!name || !str)
253 return -1;
254 StrNCpy(NameStr(*name), str, NAMEDATALEN);
255 return 0;
256}
257
258#ifdef NOT_USED
259int
260namestrcat(Name name, const char *str)
261{
262 int i;
263 char *p,
264 *q;
265
266 if (!name || !str)
267 return -1;
268 for (i = 0, p = NameStr(*name); i < NAMEDATALEN && *p; ++i, ++p)
269 ;
270 for (q = str; i < NAMEDATALEN; ++i, ++p, ++q)
271 {
272 *p = *q;
273 if (!*q)
274 break;
275 }
276 return 0;
277}
278#endif
279
280/*
281 * Compare a NAME to a C string
282 *
283 * Assumes C collation always; be careful when using this for
284 * anything but equality checks!
285 */
286int
287namestrcmp(Name name, const char *str)
288{
289 if (!name && !str)
290 return 0;
291 if (!name)
292 return -1; /* NULL < anything */
293 if (!str)
294 return 1; /* NULL < anything */
295 return strncmp(NameStr(*name), str, NAMEDATALEN);
296}
297
298
299/*
300 * SQL-functions CURRENT_USER, SESSION_USER
301 */
302Datum
303current_user(PG_FUNCTION_ARGS)
304{
305 PG_RETURN_DATUM(DirectFunctionCall1(namein, CStringGetDatum(GetUserNameFromId(GetUserId(), false))));
306}
307
308Datum
309session_user(PG_FUNCTION_ARGS)
310{
311 PG_RETURN_DATUM(DirectFunctionCall1(namein, CStringGetDatum(GetUserNameFromId(GetSessionUserId(), false))));
312}
313
314
315/*
316 * SQL-functions CURRENT_SCHEMA, CURRENT_SCHEMAS
317 */
318Datum
319current_schema(PG_FUNCTION_ARGS)
320{
321 List *search_path = fetch_search_path(false);
322 char *nspname;
323
324 if (search_path == NIL)
325 PG_RETURN_NULL();
326 nspname = get_namespace_name(linitial_oid(search_path));
327 list_free(search_path);
328 if (!nspname)
329 PG_RETURN_NULL(); /* recently-deleted namespace? */
330 PG_RETURN_DATUM(DirectFunctionCall1(namein, CStringGetDatum(nspname)));
331}
332
333Datum
334current_schemas(PG_FUNCTION_ARGS)
335{
336 List *search_path = fetch_search_path(PG_GETARG_BOOL(0));
337 ListCell *l;
338 Datum *names;
339 int i;
340 ArrayType *array;
341
342 names = (Datum *) palloc(list_length(search_path) * sizeof(Datum));
343 i = 0;
344 foreach(l, search_path)
345 {
346 char *nspname;
347
348 nspname = get_namespace_name(lfirst_oid(l));
349 if (nspname) /* watch out for deleted namespace */
350 {
351 names[i] = DirectFunctionCall1(namein, CStringGetDatum(nspname));
352 i++;
353 }
354 }
355 list_free(search_path);
356
357 array = construct_array(names, i,
358 NAMEOID,
359 NAMEDATALEN, /* sizeof(Name) */
360 false, /* Name is not by-val */
361 'c'); /* alignment of Name */
362
363 PG_RETURN_POINTER(array);
364}
365
366/*
367 * SQL-function nameconcatoid(name, oid) returns name
368 *
369 * This is used in the information_schema to produce specific_name columns,
370 * which are supposed to be unique per schema. We achieve that (in an ugly
371 * way) by appending the object's OID. The result is the same as
372 * ($1::text || '_' || $2::text)::name
373 * except that, if it would not fit in NAMEDATALEN, we make it do so by
374 * truncating the name input (not the oid).
375 */
376Datum
377nameconcatoid(PG_FUNCTION_ARGS)
378{
379 Name nam = PG_GETARG_NAME(0);
380 Oid oid = PG_GETARG_OID(1);
381 Name result;
382 char suffix[20];
383 int suflen;
384 int namlen;
385
386 suflen = snprintf(suffix, sizeof(suffix), "_%u", oid);
387 namlen = strlen(NameStr(*nam));
388
389 /* Truncate oversize input by truncating name part, not suffix */
390 if (namlen + suflen >= NAMEDATALEN)
391 namlen = pg_mbcliplen(NameStr(*nam), namlen, NAMEDATALEN - 1 - suflen);
392
393 /* We use palloc0 here to ensure result is zero-padded */
394 result = (Name) palloc0(NAMEDATALEN);
395 memcpy(NameStr(*result), NameStr(*nam), namlen);
396 memcpy(NameStr(*result) + namlen, suffix, suflen);
397
398 PG_RETURN_NAME(result);
399}
400