/src/postgres/src/backend/utils/adt/name.c
Line | Count | Source |
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-2025, 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 cstring to internal representation |
42 | | * |
43 | | * Note: |
44 | | * [Old] Currently if strlen(s) < NAMEDATALEN, the extra chars are nulls |
45 | | * Now, always NULL terminated |
46 | | */ |
47 | | Datum |
48 | | namein(PG_FUNCTION_ARGS) |
49 | 0 | { |
50 | 0 | char *s = PG_GETARG_CSTRING(0); |
51 | 0 | Name result; |
52 | 0 | int len; |
53 | |
|
54 | 0 | len = strlen(s); |
55 | | |
56 | | /* Truncate oversize input */ |
57 | 0 | if (len >= NAMEDATALEN) |
58 | 0 | len = pg_mbcliplen(s, len, NAMEDATALEN - 1); |
59 | | |
60 | | /* We use palloc0 here to ensure result is zero-padded */ |
61 | 0 | result = (Name) palloc0(NAMEDATALEN); |
62 | 0 | memcpy(NameStr(*result), s, len); |
63 | |
|
64 | 0 | PG_RETURN_NAME(result); |
65 | 0 | } |
66 | | |
67 | | /* |
68 | | * nameout - converts internal representation to cstring |
69 | | */ |
70 | | Datum |
71 | | nameout(PG_FUNCTION_ARGS) |
72 | 0 | { |
73 | 0 | Name s = PG_GETARG_NAME(0); |
74 | |
|
75 | 0 | PG_RETURN_CSTRING(pstrdup(NameStr(*s))); |
76 | 0 | } |
77 | | |
78 | | /* |
79 | | * namerecv - converts external binary format to name |
80 | | */ |
81 | | Datum |
82 | | namerecv(PG_FUNCTION_ARGS) |
83 | 0 | { |
84 | 0 | StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); |
85 | 0 | Name result; |
86 | 0 | char *str; |
87 | 0 | int nbytes; |
88 | |
|
89 | 0 | str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes); |
90 | 0 | if (nbytes >= NAMEDATALEN) |
91 | 0 | ereport(ERROR, |
92 | 0 | (errcode(ERRCODE_NAME_TOO_LONG), |
93 | 0 | errmsg("identifier too long"), |
94 | 0 | errdetail("Identifier must be less than %d characters.", |
95 | 0 | NAMEDATALEN))); |
96 | 0 | result = (NameData *) palloc0(NAMEDATALEN); |
97 | 0 | memcpy(result, str, nbytes); |
98 | 0 | pfree(str); |
99 | 0 | PG_RETURN_NAME(result); |
100 | 0 | } |
101 | | |
102 | | /* |
103 | | * namesend - converts name to binary format |
104 | | */ |
105 | | Datum |
106 | | namesend(PG_FUNCTION_ARGS) |
107 | 0 | { |
108 | 0 | Name s = PG_GETARG_NAME(0); |
109 | 0 | StringInfoData buf; |
110 | |
|
111 | 0 | pq_begintypsend(&buf); |
112 | 0 | pq_sendtext(&buf, NameStr(*s), strlen(NameStr(*s))); |
113 | 0 | PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); |
114 | 0 | } |
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 | | */ |
134 | | static int |
135 | | namecmp(Name arg1, Name arg2, Oid collid) |
136 | 0 | { |
137 | | /* Fast path for common case used in system catalogs */ |
138 | 0 | if (collid == C_COLLATION_OID) |
139 | 0 | return strncmp(NameStr(*arg1), NameStr(*arg2), NAMEDATALEN); |
140 | | |
141 | | /* Else rely on the varstr infrastructure */ |
142 | 0 | return varstr_cmp(NameStr(*arg1), strlen(NameStr(*arg1)), |
143 | 0 | NameStr(*arg2), strlen(NameStr(*arg2)), |
144 | 0 | collid); |
145 | 0 | } |
146 | | |
147 | | Datum |
148 | | nameeq(PG_FUNCTION_ARGS) |
149 | 0 | { |
150 | 0 | Name arg1 = PG_GETARG_NAME(0); |
151 | 0 | Name arg2 = PG_GETARG_NAME(1); |
152 | |
|
153 | 0 | PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) == 0); |
154 | 0 | } |
155 | | |
156 | | Datum |
157 | | namene(PG_FUNCTION_ARGS) |
158 | 0 | { |
159 | 0 | Name arg1 = PG_GETARG_NAME(0); |
160 | 0 | Name arg2 = PG_GETARG_NAME(1); |
161 | |
|
162 | 0 | PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) != 0); |
163 | 0 | } |
164 | | |
165 | | Datum |
166 | | namelt(PG_FUNCTION_ARGS) |
167 | 0 | { |
168 | 0 | Name arg1 = PG_GETARG_NAME(0); |
169 | 0 | Name arg2 = PG_GETARG_NAME(1); |
170 | |
|
171 | 0 | PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) < 0); |
172 | 0 | } |
173 | | |
174 | | Datum |
175 | | namele(PG_FUNCTION_ARGS) |
176 | 0 | { |
177 | 0 | Name arg1 = PG_GETARG_NAME(0); |
178 | 0 | Name arg2 = PG_GETARG_NAME(1); |
179 | |
|
180 | 0 | PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) <= 0); |
181 | 0 | } |
182 | | |
183 | | Datum |
184 | | namegt(PG_FUNCTION_ARGS) |
185 | 0 | { |
186 | 0 | Name arg1 = PG_GETARG_NAME(0); |
187 | 0 | Name arg2 = PG_GETARG_NAME(1); |
188 | |
|
189 | 0 | PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) > 0); |
190 | 0 | } |
191 | | |
192 | | Datum |
193 | | namege(PG_FUNCTION_ARGS) |
194 | 0 | { |
195 | 0 | Name arg1 = PG_GETARG_NAME(0); |
196 | 0 | Name arg2 = PG_GETARG_NAME(1); |
197 | |
|
198 | 0 | PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) >= 0); |
199 | 0 | } |
200 | | |
201 | | Datum |
202 | | btnamecmp(PG_FUNCTION_ARGS) |
203 | 0 | { |
204 | 0 | Name arg1 = PG_GETARG_NAME(0); |
205 | 0 | Name arg2 = PG_GETARG_NAME(1); |
206 | |
|
207 | 0 | PG_RETURN_INT32(namecmp(arg1, arg2, PG_GET_COLLATION())); |
208 | 0 | } |
209 | | |
210 | | Datum |
211 | | btnamesortsupport(PG_FUNCTION_ARGS) |
212 | 0 | { |
213 | 0 | SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); |
214 | 0 | Oid collid = ssup->ssup_collation; |
215 | 0 | MemoryContext oldcontext; |
216 | |
|
217 | 0 | oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt); |
218 | | |
219 | | /* Use generic string SortSupport */ |
220 | 0 | varstr_sortsupport(ssup, NAMEOID, collid); |
221 | |
|
222 | 0 | MemoryContextSwitchTo(oldcontext); |
223 | |
|
224 | 0 | PG_RETURN_VOID(); |
225 | 0 | } |
226 | | |
227 | | |
228 | | /***************************************************************************** |
229 | | * MISCELLANEOUS PUBLIC ROUTINES * |
230 | | *****************************************************************************/ |
231 | | |
232 | | void |
233 | | namestrcpy(Name name, const char *str) |
234 | 0 | { |
235 | | /* NB: We need to zero-pad the destination. */ |
236 | 0 | strncpy(NameStr(*name), str, NAMEDATALEN); |
237 | 0 | NameStr(*name)[NAMEDATALEN - 1] = '\0'; |
238 | 0 | } |
239 | | |
240 | | /* |
241 | | * Compare a NAME to a C string |
242 | | * |
243 | | * Assumes C collation always; be careful when using this for |
244 | | * anything but equality checks! |
245 | | */ |
246 | | int |
247 | | namestrcmp(Name name, const char *str) |
248 | 0 | { |
249 | 0 | if (!name && !str) |
250 | 0 | return 0; |
251 | 0 | if (!name) |
252 | 0 | return -1; /* NULL < anything */ |
253 | 0 | if (!str) |
254 | 0 | return 1; /* NULL < anything */ |
255 | 0 | return strncmp(NameStr(*name), str, NAMEDATALEN); |
256 | 0 | } |
257 | | |
258 | | |
259 | | /* |
260 | | * SQL-functions CURRENT_USER, SESSION_USER |
261 | | */ |
262 | | Datum |
263 | | current_user(PG_FUNCTION_ARGS) |
264 | 0 | { |
265 | 0 | PG_RETURN_DATUM(DirectFunctionCall1(namein, CStringGetDatum(GetUserNameFromId(GetUserId(), false)))); |
266 | 0 | } |
267 | | |
268 | | Datum |
269 | | session_user(PG_FUNCTION_ARGS) |
270 | 0 | { |
271 | 0 | PG_RETURN_DATUM(DirectFunctionCall1(namein, CStringGetDatum(GetUserNameFromId(GetSessionUserId(), false)))); |
272 | 0 | } |
273 | | |
274 | | |
275 | | /* |
276 | | * SQL-functions CURRENT_SCHEMA, CURRENT_SCHEMAS |
277 | | */ |
278 | | Datum |
279 | | current_schema(PG_FUNCTION_ARGS) |
280 | 0 | { |
281 | 0 | List *search_path = fetch_search_path(false); |
282 | 0 | char *nspname; |
283 | |
|
284 | 0 | if (search_path == NIL) |
285 | 0 | PG_RETURN_NULL(); |
286 | 0 | nspname = get_namespace_name(linitial_oid(search_path)); |
287 | 0 | list_free(search_path); |
288 | 0 | if (!nspname) |
289 | 0 | PG_RETURN_NULL(); /* recently-deleted namespace? */ |
290 | 0 | PG_RETURN_DATUM(DirectFunctionCall1(namein, CStringGetDatum(nspname))); |
291 | 0 | } |
292 | | |
293 | | Datum |
294 | | current_schemas(PG_FUNCTION_ARGS) |
295 | 0 | { |
296 | 0 | List *search_path = fetch_search_path(PG_GETARG_BOOL(0)); |
297 | 0 | ListCell *l; |
298 | 0 | Datum *names; |
299 | 0 | int i; |
300 | 0 | ArrayType *array; |
301 | |
|
302 | 0 | names = (Datum *) palloc(list_length(search_path) * sizeof(Datum)); |
303 | 0 | i = 0; |
304 | 0 | foreach(l, search_path) |
305 | 0 | { |
306 | 0 | char *nspname; |
307 | |
|
308 | 0 | nspname = get_namespace_name(lfirst_oid(l)); |
309 | 0 | if (nspname) /* watch out for deleted namespace */ |
310 | 0 | { |
311 | 0 | names[i] = DirectFunctionCall1(namein, CStringGetDatum(nspname)); |
312 | 0 | i++; |
313 | 0 | } |
314 | 0 | } |
315 | 0 | list_free(search_path); |
316 | |
|
317 | 0 | array = construct_array_builtin(names, i, NAMEOID); |
318 | |
|
319 | 0 | PG_RETURN_POINTER(array); |
320 | 0 | } |
321 | | |
322 | | /* |
323 | | * SQL-function nameconcatoid(name, oid) returns name |
324 | | * |
325 | | * This is used in the information_schema to produce specific_name columns, |
326 | | * which are supposed to be unique per schema. We achieve that (in an ugly |
327 | | * way) by appending the object's OID. The result is the same as |
328 | | * ($1::text || '_' || $2::text)::name |
329 | | * except that, if it would not fit in NAMEDATALEN, we make it do so by |
330 | | * truncating the name input (not the oid). |
331 | | */ |
332 | | Datum |
333 | | nameconcatoid(PG_FUNCTION_ARGS) |
334 | 0 | { |
335 | 0 | Name nam = PG_GETARG_NAME(0); |
336 | 0 | Oid oid = PG_GETARG_OID(1); |
337 | 0 | Name result; |
338 | 0 | char suffix[20]; |
339 | 0 | int suflen; |
340 | 0 | int namlen; |
341 | |
|
342 | 0 | suflen = snprintf(suffix, sizeof(suffix), "_%u", oid); |
343 | 0 | namlen = strlen(NameStr(*nam)); |
344 | | |
345 | | /* Truncate oversize input by truncating name part, not suffix */ |
346 | 0 | if (namlen + suflen >= NAMEDATALEN) |
347 | 0 | namlen = pg_mbcliplen(NameStr(*nam), namlen, NAMEDATALEN - 1 - suflen); |
348 | | |
349 | | /* We use palloc0 here to ensure result is zero-padded */ |
350 | 0 | result = (Name) palloc0(NAMEDATALEN); |
351 | 0 | memcpy(NameStr(*result), NameStr(*nam), namlen); |
352 | 0 | memcpy(NameStr(*result) + namlen, suffix, suflen); |
353 | |
|
354 | 0 | PG_RETURN_NAME(result); |
355 | 0 | } |