1/*-------------------------------------------------------------------------
2 *
3 * oid.c
4 * Functions for the built-in type Oid ... also oidvector.
5 *
6 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/utils/adt/oid.c
12 *
13 *-------------------------------------------------------------------------
14 */
15#include "postgres.h"
16
17#include <ctype.h>
18#include <limits.h>
19
20#include "catalog/pg_type.h"
21#include "libpq/pqformat.h"
22#include "nodes/value.h"
23#include "utils/array.h"
24#include "utils/builtins.h"
25
26
27#define OidVectorSize(n) (offsetof(oidvector, values) + (n) * sizeof(Oid))
28
29
30/*****************************************************************************
31 * USER I/O ROUTINES *
32 *****************************************************************************/
33
34static Oid
35oidin_subr(const char *s, char **endloc)
36{
37 unsigned long cvt;
38 char *endptr;
39 Oid result;
40
41 if (*s == '\0')
42 ereport(ERROR,
43 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
44 errmsg("invalid input syntax for type %s: \"%s\"",
45 "oid", s)));
46
47 errno = 0;
48 cvt = strtoul(s, &endptr, 10);
49
50 /*
51 * strtoul() normally only sets ERANGE. On some systems it also may set
52 * EINVAL, which simply means it couldn't parse the input string. This is
53 * handled by the second "if" consistent across platforms.
54 */
55 if (errno && errno != ERANGE && errno != EINVAL)
56 ereport(ERROR,
57 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
58 errmsg("invalid input syntax for type %s: \"%s\"",
59 "oid", s)));
60
61 if (endptr == s && *s != '\0')
62 ereport(ERROR,
63 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
64 errmsg("invalid input syntax for type %s: \"%s\"",
65 "oid", s)));
66
67 if (errno == ERANGE)
68 ereport(ERROR,
69 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
70 errmsg("value \"%s\" is out of range for type %s",
71 s, "oid")));
72
73 if (endloc)
74 {
75 /* caller wants to deal with rest of string */
76 *endloc = endptr;
77 }
78 else
79 {
80 /* allow only whitespace after number */
81 while (*endptr && isspace((unsigned char) *endptr))
82 endptr++;
83 if (*endptr)
84 ereport(ERROR,
85 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
86 errmsg("invalid input syntax for type %s: \"%s\"",
87 "oid", s)));
88 }
89
90 result = (Oid) cvt;
91
92 /*
93 * Cope with possibility that unsigned long is wider than Oid, in which
94 * case strtoul will not raise an error for some values that are out of
95 * the range of Oid.
96 *
97 * For backwards compatibility, we want to accept inputs that are given
98 * with a minus sign, so allow the input value if it matches after either
99 * signed or unsigned extension to long.
100 *
101 * To ensure consistent results on 32-bit and 64-bit platforms, make sure
102 * the error message is the same as if strtoul() had returned ERANGE.
103 */
104#if OID_MAX != ULONG_MAX
105 if (cvt != (unsigned long) result &&
106 cvt != (unsigned long) ((int) result))
107 ereport(ERROR,
108 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
109 errmsg("value \"%s\" is out of range for type %s",
110 s, "oid")));
111#endif
112
113 return result;
114}
115
116Datum
117oidin(PG_FUNCTION_ARGS)
118{
119 char *s = PG_GETARG_CSTRING(0);
120 Oid result;
121
122 result = oidin_subr(s, NULL);
123 PG_RETURN_OID(result);
124}
125
126Datum
127oidout(PG_FUNCTION_ARGS)
128{
129 Oid o = PG_GETARG_OID(0);
130 char *result = (char *) palloc(12);
131
132 snprintf(result, 12, "%u", o);
133 PG_RETURN_CSTRING(result);
134}
135
136/*
137 * oidrecv - converts external binary format to oid
138 */
139Datum
140oidrecv(PG_FUNCTION_ARGS)
141{
142 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
143
144 PG_RETURN_OID((Oid) pq_getmsgint(buf, sizeof(Oid)));
145}
146
147/*
148 * oidsend - converts oid to binary format
149 */
150Datum
151oidsend(PG_FUNCTION_ARGS)
152{
153 Oid arg1 = PG_GETARG_OID(0);
154 StringInfoData buf;
155
156 pq_begintypsend(&buf);
157 pq_sendint32(&buf, arg1);
158 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
159}
160
161/*
162 * construct oidvector given a raw array of Oids
163 *
164 * If oids is NULL then caller must fill values[] afterward
165 */
166oidvector *
167buildoidvector(const Oid *oids, int n)
168{
169 oidvector *result;
170
171 result = (oidvector *) palloc0(OidVectorSize(n));
172
173 if (n > 0 && oids)
174 memcpy(result->values, oids, n * sizeof(Oid));
175
176 /*
177 * Attach standard array header. For historical reasons, we set the index
178 * lower bound to 0 not 1.
179 */
180 SET_VARSIZE(result, OidVectorSize(n));
181 result->ndim = 1;
182 result->dataoffset = 0; /* never any nulls */
183 result->elemtype = OIDOID;
184 result->dim1 = n;
185 result->lbound1 = 0;
186
187 return result;
188}
189
190/*
191 * oidvectorin - converts "num num ..." to internal form
192 */
193Datum
194oidvectorin(PG_FUNCTION_ARGS)
195{
196 char *oidString = PG_GETARG_CSTRING(0);
197 oidvector *result;
198 int n;
199
200 result = (oidvector *) palloc0(OidVectorSize(FUNC_MAX_ARGS));
201
202 for (n = 0; n < FUNC_MAX_ARGS; n++)
203 {
204 while (*oidString && isspace((unsigned char) *oidString))
205 oidString++;
206 if (*oidString == '\0')
207 break;
208 result->values[n] = oidin_subr(oidString, &oidString);
209 }
210 while (*oidString && isspace((unsigned char) *oidString))
211 oidString++;
212 if (*oidString)
213 ereport(ERROR,
214 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
215 errmsg("oidvector has too many elements")));
216
217 SET_VARSIZE(result, OidVectorSize(n));
218 result->ndim = 1;
219 result->dataoffset = 0; /* never any nulls */
220 result->elemtype = OIDOID;
221 result->dim1 = n;
222 result->lbound1 = 0;
223
224 PG_RETURN_POINTER(result);
225}
226
227/*
228 * oidvectorout - converts internal form to "num num ..."
229 */
230Datum
231oidvectorout(PG_FUNCTION_ARGS)
232{
233 oidvector *oidArray = (oidvector *) PG_GETARG_POINTER(0);
234 int num,
235 nnums = oidArray->dim1;
236 char *rp;
237 char *result;
238
239 /* assumes sign, 10 digits, ' ' */
240 rp = result = (char *) palloc(nnums * 12 + 1);
241 for (num = 0; num < nnums; num++)
242 {
243 if (num != 0)
244 *rp++ = ' ';
245 sprintf(rp, "%u", oidArray->values[num]);
246 while (*++rp != '\0')
247 ;
248 }
249 *rp = '\0';
250 PG_RETURN_CSTRING(result);
251}
252
253/*
254 * oidvectorrecv - converts external binary format to oidvector
255 */
256Datum
257oidvectorrecv(PG_FUNCTION_ARGS)
258{
259 LOCAL_FCINFO(locfcinfo, 3);
260 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
261 oidvector *result;
262
263 /*
264 * Normally one would call array_recv() using DirectFunctionCall3, but
265 * that does not work since array_recv wants to cache some data using
266 * fcinfo->flinfo->fn_extra. So we need to pass it our own flinfo
267 * parameter.
268 */
269 InitFunctionCallInfoData(*locfcinfo, fcinfo->flinfo, 3,
270 InvalidOid, NULL, NULL);
271
272 locfcinfo->args[0].value = PointerGetDatum(buf);
273 locfcinfo->args[0].isnull = false;
274 locfcinfo->args[1].value = ObjectIdGetDatum(OIDOID);
275 locfcinfo->args[1].isnull = false;
276 locfcinfo->args[2].value = Int32GetDatum(-1);
277 locfcinfo->args[2].isnull = false;
278
279 result = (oidvector *) DatumGetPointer(array_recv(locfcinfo));
280
281 Assert(!locfcinfo->isnull);
282
283 /* sanity checks: oidvector must be 1-D, 0-based, no nulls */
284 if (ARR_NDIM(result) != 1 ||
285 ARR_HASNULL(result) ||
286 ARR_ELEMTYPE(result) != OIDOID ||
287 ARR_LBOUND(result)[0] != 0)
288 ereport(ERROR,
289 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
290 errmsg("invalid oidvector data")));
291
292 /* check length for consistency with oidvectorin() */
293 if (ARR_DIMS(result)[0] > FUNC_MAX_ARGS)
294 ereport(ERROR,
295 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
296 errmsg("oidvector has too many elements")));
297
298 PG_RETURN_POINTER(result);
299}
300
301/*
302 * oidvectorsend - converts oidvector to binary format
303 */
304Datum
305oidvectorsend(PG_FUNCTION_ARGS)
306{
307 return array_send(fcinfo);
308}
309
310/*
311 * oidparse - get OID from IConst/FConst node
312 */
313Oid
314oidparse(Node *node)
315{
316 switch (nodeTag(node))
317 {
318 case T_Integer:
319 return intVal(node);
320 case T_Float:
321
322 /*
323 * Values too large for int4 will be represented as Float
324 * constants by the lexer. Accept these if they are valid OID
325 * strings.
326 */
327 return oidin_subr(strVal(node), NULL);
328 default:
329 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
330 }
331 return InvalidOid; /* keep compiler quiet */
332}
333
334/* qsort comparison function for Oids */
335int
336oid_cmp(const void *p1, const void *p2)
337{
338 Oid v1 = *((const Oid *) p1);
339 Oid v2 = *((const Oid *) p2);
340
341 if (v1 < v2)
342 return -1;
343 if (v1 > v2)
344 return 1;
345 return 0;
346}
347
348
349/*****************************************************************************
350 * PUBLIC ROUTINES *
351 *****************************************************************************/
352
353Datum
354oideq(PG_FUNCTION_ARGS)
355{
356 Oid arg1 = PG_GETARG_OID(0);
357 Oid arg2 = PG_GETARG_OID(1);
358
359 PG_RETURN_BOOL(arg1 == arg2);
360}
361
362Datum
363oidne(PG_FUNCTION_ARGS)
364{
365 Oid arg1 = PG_GETARG_OID(0);
366 Oid arg2 = PG_GETARG_OID(1);
367
368 PG_RETURN_BOOL(arg1 != arg2);
369}
370
371Datum
372oidlt(PG_FUNCTION_ARGS)
373{
374 Oid arg1 = PG_GETARG_OID(0);
375 Oid arg2 = PG_GETARG_OID(1);
376
377 PG_RETURN_BOOL(arg1 < arg2);
378}
379
380Datum
381oidle(PG_FUNCTION_ARGS)
382{
383 Oid arg1 = PG_GETARG_OID(0);
384 Oid arg2 = PG_GETARG_OID(1);
385
386 PG_RETURN_BOOL(arg1 <= arg2);
387}
388
389Datum
390oidge(PG_FUNCTION_ARGS)
391{
392 Oid arg1 = PG_GETARG_OID(0);
393 Oid arg2 = PG_GETARG_OID(1);
394
395 PG_RETURN_BOOL(arg1 >= arg2);
396}
397
398Datum
399oidgt(PG_FUNCTION_ARGS)
400{
401 Oid arg1 = PG_GETARG_OID(0);
402 Oid arg2 = PG_GETARG_OID(1);
403
404 PG_RETURN_BOOL(arg1 > arg2);
405}
406
407Datum
408oidlarger(PG_FUNCTION_ARGS)
409{
410 Oid arg1 = PG_GETARG_OID(0);
411 Oid arg2 = PG_GETARG_OID(1);
412
413 PG_RETURN_OID((arg1 > arg2) ? arg1 : arg2);
414}
415
416Datum
417oidsmaller(PG_FUNCTION_ARGS)
418{
419 Oid arg1 = PG_GETARG_OID(0);
420 Oid arg2 = PG_GETARG_OID(1);
421
422 PG_RETURN_OID((arg1 < arg2) ? arg1 : arg2);
423}
424
425Datum
426oidvectoreq(PG_FUNCTION_ARGS)
427{
428 int32 cmp = DatumGetInt32(btoidvectorcmp(fcinfo));
429
430 PG_RETURN_BOOL(cmp == 0);
431}
432
433Datum
434oidvectorne(PG_FUNCTION_ARGS)
435{
436 int32 cmp = DatumGetInt32(btoidvectorcmp(fcinfo));
437
438 PG_RETURN_BOOL(cmp != 0);
439}
440
441Datum
442oidvectorlt(PG_FUNCTION_ARGS)
443{
444 int32 cmp = DatumGetInt32(btoidvectorcmp(fcinfo));
445
446 PG_RETURN_BOOL(cmp < 0);
447}
448
449Datum
450oidvectorle(PG_FUNCTION_ARGS)
451{
452 int32 cmp = DatumGetInt32(btoidvectorcmp(fcinfo));
453
454 PG_RETURN_BOOL(cmp <= 0);
455}
456
457Datum
458oidvectorge(PG_FUNCTION_ARGS)
459{
460 int32 cmp = DatumGetInt32(btoidvectorcmp(fcinfo));
461
462 PG_RETURN_BOOL(cmp >= 0);
463}
464
465Datum
466oidvectorgt(PG_FUNCTION_ARGS)
467{
468 int32 cmp = DatumGetInt32(btoidvectorcmp(fcinfo));
469
470 PG_RETURN_BOOL(cmp > 0);
471}
472