Line data Source code
1 : /*------------------------------------------------------------------------
2 : *
3 : * regress.c
4 : * Code for various C-language functions defined as part of the
5 : * regression tests.
6 : *
7 : * This code is released under the terms of the PostgreSQL License.
8 : *
9 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
10 : * Portions Copyright (c) 1994, Regents of the University of California
11 : *
12 : * src/test/regress/regress.c
13 : *
14 : *-------------------------------------------------------------------------
15 : */
16 :
17 : #include "postgres.h"
18 :
19 : #include <math.h>
20 : #include <signal.h>
21 :
22 : #include "access/detoast.h"
23 : #include "access/htup_details.h"
24 : #include "catalog/catalog.h"
25 : #include "catalog/namespace.h"
26 : #include "catalog/pg_operator.h"
27 : #include "catalog/pg_type.h"
28 : #include "commands/sequence.h"
29 : #include "commands/trigger.h"
30 : #include "common/pg_lzcompress.h"
31 : #include "executor/executor.h"
32 : #include "executor/functions.h"
33 : #include "executor/spi.h"
34 : #include "funcapi.h"
35 : #include "mb/pg_wchar.h"
36 : #include "miscadmin.h"
37 : #include "nodes/supportnodes.h"
38 : #include "optimizer/optimizer.h"
39 : #include "optimizer/plancat.h"
40 : #include "parser/parse_coerce.h"
41 : #include "port/atomics.h"
42 : #include "portability/instr_time.h"
43 : #include "postmaster/postmaster.h" /* for MAX_BACKENDS */
44 : #include "storage/spin.h"
45 : #include "tcop/tcopprot.h"
46 : #include "utils/array.h"
47 : #include "utils/builtins.h"
48 : #include "utils/geo_decls.h"
49 : #include "utils/memutils.h"
50 : #include "utils/rel.h"
51 : #include "utils/typcache.h"
52 :
53 : /* define our text domain for translations */
54 : #undef TEXTDOMAIN
55 : #define TEXTDOMAIN PG_TEXTDOMAIN("postgresql-regress")
56 :
57 : #define EXPECT_TRUE(expr) \
58 : do { \
59 : if (!(expr)) \
60 : elog(ERROR, \
61 : "%s was unexpectedly false in file \"%s\" line %u", \
62 : #expr, __FILE__, __LINE__); \
63 : } while (0)
64 :
65 : #define EXPECT_EQ_U32(result_expr, expected_expr) \
66 : do { \
67 : uint32 actual_result = (result_expr); \
68 : uint32 expected_result = (expected_expr); \
69 : if (actual_result != expected_result) \
70 : elog(ERROR, \
71 : "%s yielded %u, expected %s in file \"%s\" line %u", \
72 : #result_expr, actual_result, #expected_expr, __FILE__, __LINE__); \
73 : } while (0)
74 :
75 : #define EXPECT_EQ_U64(result_expr, expected_expr) \
76 : do { \
77 : uint64 actual_result = (result_expr); \
78 : uint64 expected_result = (expected_expr); \
79 : if (actual_result != expected_result) \
80 : elog(ERROR, \
81 : "%s yielded " UINT64_FORMAT ", expected %s in file \"%s\" line %u", \
82 : #result_expr, actual_result, #expected_expr, __FILE__, __LINE__); \
83 : } while (0)
84 :
85 : #define LDELIM '('
86 : #define RDELIM ')'
87 : #define DELIM ','
88 :
89 : static void regress_lseg_construct(LSEG *lseg, Point *pt1, Point *pt2);
90 :
91 95 : PG_MODULE_MAGIC_EXT(
92 : .name = "regress",
93 : .version = PG_VERSION
94 : );
95 :
96 :
97 : /* return the point where two paths intersect, or NULL if no intersection. */
98 9 : PG_FUNCTION_INFO_V1(interpt_pp);
99 :
100 : Datum
101 3584 : interpt_pp(PG_FUNCTION_ARGS)
102 : {
103 3584 : PATH *p1 = PG_GETARG_PATH_P(0);
104 3584 : PATH *p2 = PG_GETARG_PATH_P(1);
105 : int i,
106 : j;
107 : LSEG seg1,
108 : seg2;
109 : bool found; /* We've found the intersection */
110 :
111 3584 : found = false; /* Haven't found it yet */
112 :
113 11764 : for (i = 0; i < p1->npts - 1 && !found; i++)
114 : {
115 8180 : regress_lseg_construct(&seg1, &p1->p[i], &p1->p[i + 1]);
116 25092 : for (j = 0; j < p2->npts - 1 && !found; j++)
117 : {
118 16912 : regress_lseg_construct(&seg2, &p2->p[j], &p2->p[j + 1]);
119 16912 : if (DatumGetBool(DirectFunctionCall2(lseg_intersect,
120 : LsegPGetDatum(&seg1),
121 : LsegPGetDatum(&seg2))))
122 3576 : found = true;
123 : }
124 : }
125 :
126 3584 : if (!found)
127 8 : PG_RETURN_NULL();
128 :
129 : /*
130 : * Note: DirectFunctionCall2 will kick out an error if lseg_interpt()
131 : * returns NULL, but that should be impossible since we know the two
132 : * segments intersect.
133 : */
134 3576 : PG_RETURN_DATUM(DirectFunctionCall2(lseg_interpt,
135 : LsegPGetDatum(&seg1),
136 : LsegPGetDatum(&seg2)));
137 : }
138 :
139 :
140 : /* like lseg_construct, but assume space already allocated */
141 : static void
142 25092 : regress_lseg_construct(LSEG *lseg, Point *pt1, Point *pt2)
143 : {
144 25092 : lseg->p[0].x = pt1->x;
145 25092 : lseg->p[0].y = pt1->y;
146 25092 : lseg->p[1].x = pt2->x;
147 25092 : lseg->p[1].y = pt2->y;
148 25092 : }
149 :
150 9 : PG_FUNCTION_INFO_V1(overpaid);
151 :
152 : Datum
153 24 : overpaid(PG_FUNCTION_ARGS)
154 : {
155 24 : HeapTupleHeader tuple = PG_GETARG_HEAPTUPLEHEADER(0);
156 : bool isnull;
157 : int32 salary;
158 :
159 24 : salary = DatumGetInt32(GetAttributeByName(tuple, "salary", &isnull));
160 24 : if (isnull)
161 0 : PG_RETURN_NULL();
162 24 : PG_RETURN_BOOL(salary > 699);
163 : }
164 :
165 : /*
166 : * New type "widget"
167 : * This used to be "circle", but I added circle to builtins,
168 : * so needed to make sure the names do not collide. - tgl 97/04/21
169 : */
170 :
171 : typedef struct
172 : {
173 : Point center;
174 : double radius;
175 : } WIDGET;
176 :
177 13 : PG_FUNCTION_INFO_V1(widget_in);
178 9 : PG_FUNCTION_INFO_V1(widget_out);
179 :
180 : #define NARGS 3
181 :
182 : Datum
183 44 : widget_in(PG_FUNCTION_ARGS)
184 : {
185 44 : char *str = PG_GETARG_CSTRING(0);
186 : char *p,
187 : *coord[NARGS];
188 : int i;
189 : WIDGET *result;
190 :
191 252 : for (i = 0, p = str; *p && i < NARGS && *p != RDELIM; p++)
192 : {
193 208 : if (*p == DELIM || (*p == LDELIM && i == 0))
194 108 : coord[i++] = p + 1;
195 : }
196 :
197 : /*
198 : * Note: DON'T convert this error to "soft" style (errsave/ereturn). We
199 : * want this data type to stay permanently in the hard-error world so that
200 : * it can be used for testing that such cases still work reasonably.
201 : */
202 44 : if (i < NARGS)
203 16 : ereport(ERROR,
204 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
205 : errmsg("invalid input syntax for type %s: \"%s\"",
206 : "widget", str)));
207 :
208 28 : result = palloc_object(WIDGET);
209 28 : result->center.x = atof(coord[0]);
210 28 : result->center.y = atof(coord[1]);
211 28 : result->radius = atof(coord[2]);
212 :
213 28 : PG_RETURN_POINTER(result);
214 : }
215 :
216 : Datum
217 8 : widget_out(PG_FUNCTION_ARGS)
218 : {
219 8 : WIDGET *widget = (WIDGET *) PG_GETARG_POINTER(0);
220 8 : char *str = psprintf("(%g,%g,%g)",
221 : widget->center.x, widget->center.y, widget->radius);
222 :
223 8 : PG_RETURN_CSTRING(str);
224 : }
225 :
226 9 : PG_FUNCTION_INFO_V1(pt_in_widget);
227 :
228 : Datum
229 8 : pt_in_widget(PG_FUNCTION_ARGS)
230 : {
231 8 : Point *point = PG_GETARG_POINT_P(0);
232 8 : WIDGET *widget = (WIDGET *) PG_GETARG_POINTER(1);
233 : float8 distance;
234 :
235 8 : distance = DatumGetFloat8(DirectFunctionCall2(point_distance,
236 : PointPGetDatum(point),
237 : PointPGetDatum(&widget->center)));
238 :
239 8 : PG_RETURN_BOOL(distance < widget->radius);
240 : }
241 :
242 9 : PG_FUNCTION_INFO_V1(reverse_name);
243 :
244 : Datum
245 32 : reverse_name(PG_FUNCTION_ARGS)
246 : {
247 32 : char *string = PG_GETARG_CSTRING(0);
248 : int i;
249 : int len;
250 : char *new_string;
251 :
252 32 : new_string = palloc0(NAMEDATALEN);
253 224 : for (i = 0; i < NAMEDATALEN && string[i]; ++i)
254 : ;
255 32 : if (i == NAMEDATALEN || !string[i])
256 32 : --i;
257 32 : len = i;
258 224 : for (; i >= 0; --i)
259 192 : new_string[len - i] = string[i];
260 32 : PG_RETURN_CSTRING(new_string);
261 : }
262 :
263 9 : PG_FUNCTION_INFO_V1(trigger_return_old);
264 :
265 : Datum
266 60 : trigger_return_old(PG_FUNCTION_ARGS)
267 : {
268 60 : TriggerData *trigdata = (TriggerData *) fcinfo->context;
269 : HeapTuple tuple;
270 :
271 60 : if (!CALLED_AS_TRIGGER(fcinfo))
272 0 : elog(ERROR, "trigger_return_old: not fired by trigger manager");
273 :
274 60 : tuple = trigdata->tg_trigtuple;
275 :
276 60 : return PointerGetDatum(tuple);
277 : }
278 :
279 :
280 : /*
281 : * Type int44 has no real-world use, but the regression tests use it
282 : * (under the alias "city_budget"). It's a four-element vector of int4's.
283 : */
284 :
285 : /*
286 : * int44in - converts "num, num, ..." to internal form
287 : *
288 : * Note: Fills any missing positions with zeroes.
289 : */
290 9 : PG_FUNCTION_INFO_V1(int44in);
291 :
292 : Datum
293 8 : int44in(PG_FUNCTION_ARGS)
294 : {
295 8 : char *input_string = PG_GETARG_CSTRING(0);
296 8 : int32 *result = (int32 *) palloc(4 * sizeof(int32));
297 : int i;
298 :
299 8 : i = sscanf(input_string,
300 : "%d, %d, %d, %d",
301 : &result[0],
302 : &result[1],
303 : &result[2],
304 : &result[3]);
305 12 : while (i < 4)
306 4 : result[i++] = 0;
307 :
308 8 : PG_RETURN_POINTER(result);
309 : }
310 :
311 : /*
312 : * int44out - converts internal form to "num, num, ..."
313 : */
314 13 : PG_FUNCTION_INFO_V1(int44out);
315 :
316 : Datum
317 16 : int44out(PG_FUNCTION_ARGS)
318 : {
319 16 : int32 *an_array = (int32 *) PG_GETARG_POINTER(0);
320 16 : char *result = (char *) palloc(16 * 4);
321 :
322 16 : snprintf(result, 16 * 4, "%d,%d,%d,%d",
323 : an_array[0],
324 16 : an_array[1],
325 16 : an_array[2],
326 16 : an_array[3]);
327 :
328 16 : PG_RETURN_CSTRING(result);
329 : }
330 :
331 9 : PG_FUNCTION_INFO_V1(test_canonicalize_path);
332 : Datum
333 110 : test_canonicalize_path(PG_FUNCTION_ARGS)
334 : {
335 110 : char *path = text_to_cstring(PG_GETARG_TEXT_PP(0));
336 :
337 110 : canonicalize_path(path);
338 110 : PG_RETURN_TEXT_P(cstring_to_text(path));
339 : }
340 :
341 9 : PG_FUNCTION_INFO_V1(make_tuple_indirect);
342 : Datum
343 84 : make_tuple_indirect(PG_FUNCTION_ARGS)
344 : {
345 84 : HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0);
346 : HeapTupleData tuple;
347 : int ncolumns;
348 : Datum *values;
349 : bool *nulls;
350 :
351 : Oid tupType;
352 : int32 tupTypmod;
353 : TupleDesc tupdesc;
354 :
355 : HeapTuple newtup;
356 :
357 : int i;
358 :
359 : MemoryContext old_context;
360 :
361 : /* Extract type info from the tuple itself */
362 84 : tupType = HeapTupleHeaderGetTypeId(rec);
363 84 : tupTypmod = HeapTupleHeaderGetTypMod(rec);
364 84 : tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
365 84 : ncolumns = tupdesc->natts;
366 :
367 : /* Build a temporary HeapTuple control structure */
368 84 : tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
369 84 : ItemPointerSetInvalid(&(tuple.t_self));
370 84 : tuple.t_tableOid = InvalidOid;
371 84 : tuple.t_data = rec;
372 :
373 84 : values = (Datum *) palloc(ncolumns * sizeof(Datum));
374 84 : nulls = (bool *) palloc(ncolumns * sizeof(bool));
375 :
376 84 : heap_deform_tuple(&tuple, tupdesc, values, nulls);
377 :
378 84 : old_context = MemoryContextSwitchTo(TopTransactionContext);
379 :
380 420 : for (i = 0; i < ncolumns; i++)
381 : {
382 : varlena *attr;
383 : varlena *new_attr;
384 : varatt_indirect redirect_pointer;
385 :
386 : /* only work on existing, not-null varlenas */
387 336 : if (TupleDescAttr(tupdesc, i)->attisdropped ||
388 336 : nulls[i] ||
389 292 : TupleDescAttr(tupdesc, i)->attlen != -1 ||
390 208 : TupleDescAttr(tupdesc, i)->attstorage == TYPSTORAGE_PLAIN)
391 128 : continue;
392 :
393 208 : attr = (varlena *) DatumGetPointer(values[i]);
394 :
395 : /* don't recursively indirect */
396 208 : if (VARATT_IS_EXTERNAL_INDIRECT(attr))
397 0 : continue;
398 :
399 : /* copy datum, so it still lives later */
400 208 : if (VARATT_IS_EXTERNAL_ONDISK(attr))
401 0 : attr = detoast_external_attr(attr);
402 : else
403 : {
404 208 : varlena *oldattr = attr;
405 :
406 208 : attr = palloc0(VARSIZE_ANY(oldattr));
407 208 : memcpy(attr, oldattr, VARSIZE_ANY(oldattr));
408 : }
409 :
410 : /* build indirection Datum */
411 208 : new_attr = (varlena *) palloc0(INDIRECT_POINTER_SIZE);
412 208 : redirect_pointer.pointer = attr;
413 208 : SET_VARTAG_EXTERNAL(new_attr, VARTAG_INDIRECT);
414 208 : memcpy(VARDATA_EXTERNAL(new_attr), &redirect_pointer,
415 : sizeof(redirect_pointer));
416 :
417 208 : values[i] = PointerGetDatum(new_attr);
418 : }
419 :
420 84 : newtup = heap_form_tuple(tupdesc, values, nulls);
421 84 : pfree(values);
422 84 : pfree(nulls);
423 84 : ReleaseTupleDesc(tupdesc);
424 :
425 84 : MemoryContextSwitchTo(old_context);
426 :
427 : /*
428 : * We intentionally don't use PG_RETURN_HEAPTUPLEHEADER here, because that
429 : * would cause the indirect toast pointers to be flattened out of the
430 : * tuple immediately, rendering subsequent testing irrelevant. So just
431 : * return the HeapTupleHeader pointer as-is. This violates the general
432 : * rule that composite Datums shouldn't contain toast pointers, but so
433 : * long as the regression test scripts don't insert the result of this
434 : * function into a container type (record, array, etc) it should be OK.
435 : */
436 84 : PG_RETURN_POINTER(newtup->t_data);
437 : }
438 :
439 2 : PG_FUNCTION_INFO_V1(get_environ);
440 :
441 : Datum
442 1 : get_environ(PG_FUNCTION_ARGS)
443 : {
444 : #if !defined(WIN32)
445 : extern char **environ;
446 : #endif
447 1 : int nvals = 0;
448 : ArrayType *result;
449 : Datum *env;
450 :
451 35 : for (char **s = environ; *s; s++)
452 34 : nvals++;
453 :
454 1 : env = palloc(nvals * sizeof(Datum));
455 :
456 35 : for (int i = 0; i < nvals; i++)
457 34 : env[i] = CStringGetTextDatum(environ[i]);
458 :
459 1 : result = construct_array_builtin(env, nvals, TEXTOID);
460 :
461 1 : PG_RETURN_POINTER(result);
462 : }
463 :
464 2 : PG_FUNCTION_INFO_V1(regress_setenv);
465 :
466 : Datum
467 1 : regress_setenv(PG_FUNCTION_ARGS)
468 : {
469 1 : char *envvar = text_to_cstring(PG_GETARG_TEXT_PP(0));
470 1 : char *envval = text_to_cstring(PG_GETARG_TEXT_PP(1));
471 :
472 1 : if (!superuser())
473 0 : elog(ERROR, "must be superuser to change environment variables");
474 :
475 1 : if (setenv(envvar, envval, 1) != 0)
476 0 : elog(ERROR, "could not set environment variable: %m");
477 :
478 1 : PG_RETURN_VOID();
479 : }
480 :
481 : /* Sleep until no process has a given PID. */
482 5 : PG_FUNCTION_INFO_V1(wait_pid);
483 :
484 : Datum
485 2 : wait_pid(PG_FUNCTION_ARGS)
486 : {
487 2 : int pid = PG_GETARG_INT32(0);
488 :
489 2 : if (!superuser())
490 0 : elog(ERROR, "must be superuser to check PID liveness");
491 :
492 6 : while (kill(pid, 0) == 0)
493 : {
494 4 : CHECK_FOR_INTERRUPTS();
495 4 : pg_usleep(50000);
496 : }
497 :
498 2 : if (errno != ESRCH)
499 0 : elog(ERROR, "could not check PID %d liveness: %m", pid);
500 :
501 2 : PG_RETURN_VOID();
502 : }
503 :
504 : static void
505 4 : test_atomic_flag(void)
506 : {
507 : pg_atomic_flag flag;
508 :
509 4 : pg_atomic_init_flag(&flag);
510 4 : EXPECT_TRUE(pg_atomic_unlocked_test_flag(&flag));
511 4 : EXPECT_TRUE(pg_atomic_test_set_flag(&flag));
512 4 : EXPECT_TRUE(!pg_atomic_unlocked_test_flag(&flag));
513 4 : EXPECT_TRUE(!pg_atomic_test_set_flag(&flag));
514 4 : pg_atomic_clear_flag(&flag);
515 4 : EXPECT_TRUE(pg_atomic_unlocked_test_flag(&flag));
516 4 : EXPECT_TRUE(pg_atomic_test_set_flag(&flag));
517 4 : pg_atomic_clear_flag(&flag);
518 4 : }
519 :
520 : static void
521 4 : test_atomic_uint32(void)
522 : {
523 : pg_atomic_uint32 var;
524 : uint32 expected;
525 : int i;
526 :
527 4 : pg_atomic_init_u32(&var, 0);
528 4 : EXPECT_EQ_U32(pg_atomic_read_u32(&var), 0);
529 4 : pg_atomic_write_u32(&var, 3);
530 4 : EXPECT_EQ_U32(pg_atomic_read_u32(&var), 3);
531 4 : EXPECT_EQ_U32(pg_atomic_fetch_add_u32(&var, pg_atomic_read_u32(&var) - 2),
532 : 3);
533 4 : EXPECT_EQ_U32(pg_atomic_fetch_sub_u32(&var, 1), 4);
534 4 : EXPECT_EQ_U32(pg_atomic_sub_fetch_u32(&var, 3), 0);
535 4 : EXPECT_EQ_U32(pg_atomic_add_fetch_u32(&var, 10), 10);
536 4 : EXPECT_EQ_U32(pg_atomic_exchange_u32(&var, 5), 10);
537 4 : EXPECT_EQ_U32(pg_atomic_exchange_u32(&var, 0), 5);
538 :
539 : /* test around numerical limits */
540 4 : EXPECT_EQ_U32(pg_atomic_fetch_add_u32(&var, INT_MAX), 0);
541 4 : EXPECT_EQ_U32(pg_atomic_fetch_add_u32(&var, INT_MAX), INT_MAX);
542 4 : pg_atomic_fetch_add_u32(&var, 2); /* wrap to 0 */
543 4 : EXPECT_EQ_U32(pg_atomic_fetch_add_u32(&var, PG_INT16_MAX), 0);
544 4 : EXPECT_EQ_U32(pg_atomic_fetch_add_u32(&var, PG_INT16_MAX + 1),
545 : PG_INT16_MAX);
546 4 : EXPECT_EQ_U32(pg_atomic_fetch_add_u32(&var, PG_INT16_MIN),
547 : 2 * PG_INT16_MAX + 1);
548 4 : EXPECT_EQ_U32(pg_atomic_fetch_add_u32(&var, PG_INT16_MIN - 1),
549 : PG_INT16_MAX);
550 4 : pg_atomic_fetch_add_u32(&var, 1); /* top up to UINT_MAX */
551 4 : EXPECT_EQ_U32(pg_atomic_read_u32(&var), UINT_MAX);
552 4 : EXPECT_EQ_U32(pg_atomic_fetch_sub_u32(&var, INT_MAX), UINT_MAX);
553 4 : EXPECT_EQ_U32(pg_atomic_read_u32(&var), (uint32) INT_MAX + 1);
554 4 : EXPECT_EQ_U32(pg_atomic_sub_fetch_u32(&var, INT_MAX), 1);
555 4 : pg_atomic_sub_fetch_u32(&var, 1);
556 4 : expected = PG_INT16_MAX;
557 4 : EXPECT_TRUE(!pg_atomic_compare_exchange_u32(&var, &expected, 1));
558 4 : expected = PG_INT16_MAX + 1;
559 4 : EXPECT_TRUE(!pg_atomic_compare_exchange_u32(&var, &expected, 1));
560 4 : expected = PG_INT16_MIN;
561 4 : EXPECT_TRUE(!pg_atomic_compare_exchange_u32(&var, &expected, 1));
562 4 : expected = PG_INT16_MIN - 1;
563 4 : EXPECT_TRUE(!pg_atomic_compare_exchange_u32(&var, &expected, 1));
564 :
565 : /* fail exchange because of old expected */
566 4 : expected = 10;
567 4 : EXPECT_TRUE(!pg_atomic_compare_exchange_u32(&var, &expected, 1));
568 :
569 : /* CAS is allowed to fail due to interrupts, try a couple of times */
570 8 : for (i = 0; i < 1000; i++)
571 : {
572 8 : expected = 0;
573 8 : if (!pg_atomic_compare_exchange_u32(&var, &expected, 1))
574 4 : break;
575 : }
576 4 : if (i == 1000)
577 0 : elog(ERROR, "atomic_compare_exchange_u32() never succeeded");
578 4 : EXPECT_EQ_U32(pg_atomic_read_u32(&var), 1);
579 4 : pg_atomic_write_u32(&var, 0);
580 :
581 : /* try setting flagbits */
582 4 : EXPECT_TRUE(!(pg_atomic_fetch_or_u32(&var, 1) & 1));
583 4 : EXPECT_TRUE(pg_atomic_fetch_or_u32(&var, 2) & 1);
584 4 : EXPECT_EQ_U32(pg_atomic_read_u32(&var), 3);
585 : /* try clearing flagbits */
586 4 : EXPECT_EQ_U32(pg_atomic_fetch_and_u32(&var, ~2) & 3, 3);
587 4 : EXPECT_EQ_U32(pg_atomic_fetch_and_u32(&var, ~1), 1);
588 : /* no bits set anymore */
589 4 : EXPECT_EQ_U32(pg_atomic_fetch_and_u32(&var, ~0), 0);
590 4 : }
591 :
592 : static void
593 4 : test_atomic_uint64(void)
594 : {
595 : pg_atomic_uint64 var;
596 : uint64 expected;
597 : int i;
598 :
599 4 : pg_atomic_init_u64(&var, 0);
600 4 : EXPECT_EQ_U64(pg_atomic_read_u64(&var), 0);
601 4 : pg_atomic_write_u64(&var, 3);
602 4 : EXPECT_EQ_U64(pg_atomic_read_u64(&var), 3);
603 4 : EXPECT_EQ_U64(pg_atomic_fetch_add_u64(&var, pg_atomic_read_u64(&var) - 2),
604 : 3);
605 4 : EXPECT_EQ_U64(pg_atomic_fetch_sub_u64(&var, 1), 4);
606 4 : EXPECT_EQ_U64(pg_atomic_sub_fetch_u64(&var, 3), 0);
607 4 : EXPECT_EQ_U64(pg_atomic_add_fetch_u64(&var, 10), 10);
608 4 : EXPECT_EQ_U64(pg_atomic_exchange_u64(&var, 5), 10);
609 4 : EXPECT_EQ_U64(pg_atomic_exchange_u64(&var, 0), 5);
610 :
611 : /* fail exchange because of old expected */
612 4 : expected = 10;
613 4 : EXPECT_TRUE(!pg_atomic_compare_exchange_u64(&var, &expected, 1));
614 :
615 : /* CAS is allowed to fail due to interrupts, try a couple of times */
616 8 : for (i = 0; i < 100; i++)
617 : {
618 8 : expected = 0;
619 8 : if (!pg_atomic_compare_exchange_u64(&var, &expected, 1))
620 4 : break;
621 : }
622 4 : if (i == 100)
623 0 : elog(ERROR, "atomic_compare_exchange_u64() never succeeded");
624 4 : EXPECT_EQ_U64(pg_atomic_read_u64(&var), 1);
625 :
626 4 : pg_atomic_write_u64(&var, 0);
627 :
628 : /* try setting flagbits */
629 4 : EXPECT_TRUE(!(pg_atomic_fetch_or_u64(&var, 1) & 1));
630 4 : EXPECT_TRUE(pg_atomic_fetch_or_u64(&var, 2) & 1);
631 4 : EXPECT_EQ_U64(pg_atomic_read_u64(&var), 3);
632 : /* try clearing flagbits */
633 4 : EXPECT_EQ_U64((pg_atomic_fetch_and_u64(&var, ~2) & 3), 3);
634 4 : EXPECT_EQ_U64(pg_atomic_fetch_and_u64(&var, ~1), 1);
635 : /* no bits set anymore */
636 4 : EXPECT_EQ_U64(pg_atomic_fetch_and_u64(&var, ~0), 0);
637 4 : }
638 :
639 : /*
640 : * Perform, fairly minimal, testing of the spinlock implementation.
641 : *
642 : * It's likely worth expanding these to actually test concurrency etc, but
643 : * having some regularly run tests is better than none.
644 : */
645 : static void
646 4 : test_spinlock(void)
647 : {
648 : /*
649 : * Basic tests for spinlocks, as well as the underlying operations.
650 : *
651 : * We embed the spinlock in a struct with other members to test that the
652 : * spinlock operations don't perform too wide writes.
653 : */
654 : {
655 : struct test_lock_struct
656 : {
657 : char data_before[4];
658 : slock_t lock;
659 : char data_after[4];
660 : } struct_w_lock;
661 :
662 4 : memcpy(struct_w_lock.data_before, "abcd", 4);
663 4 : memcpy(struct_w_lock.data_after, "ef12", 4);
664 :
665 : /* test basic operations via the SpinLock* API */
666 4 : SpinLockInit(&struct_w_lock.lock);
667 4 : SpinLockAcquire(&struct_w_lock.lock);
668 4 : SpinLockRelease(&struct_w_lock.lock);
669 :
670 : /* test basic operations via underlying S_* API */
671 4 : S_INIT_LOCK(&struct_w_lock.lock);
672 4 : S_LOCK(&struct_w_lock.lock);
673 4 : S_UNLOCK(&struct_w_lock.lock);
674 :
675 : /* and that "contended" acquisition works */
676 4 : s_lock(&struct_w_lock.lock, "testfile", 17, "testfunc");
677 4 : S_UNLOCK(&struct_w_lock.lock);
678 :
679 : /*
680 : * Check, using TAS directly, that a single spin cycle doesn't block
681 : * when acquiring an already acquired lock.
682 : */
683 : #ifdef TAS
684 4 : S_LOCK(&struct_w_lock.lock);
685 :
686 4 : if (!TAS(&struct_w_lock.lock))
687 0 : elog(ERROR, "acquired already held spinlock");
688 :
689 : #ifdef TAS_SPIN
690 4 : if (!TAS_SPIN(&struct_w_lock.lock))
691 0 : elog(ERROR, "acquired already held spinlock");
692 : #endif /* defined(TAS_SPIN) */
693 :
694 4 : S_UNLOCK(&struct_w_lock.lock);
695 : #endif /* defined(TAS) */
696 :
697 : /*
698 : * Verify that after all of this the non-lock contents are still
699 : * correct.
700 : */
701 4 : if (memcmp(struct_w_lock.data_before, "abcd", 4) != 0)
702 0 : elog(ERROR, "padding before spinlock modified");
703 4 : if (memcmp(struct_w_lock.data_after, "ef12", 4) != 0)
704 0 : elog(ERROR, "padding after spinlock modified");
705 : }
706 4 : }
707 :
708 9 : PG_FUNCTION_INFO_V1(test_atomic_ops);
709 : Datum
710 4 : test_atomic_ops(PG_FUNCTION_ARGS)
711 : {
712 4 : test_atomic_flag();
713 :
714 4 : test_atomic_uint32();
715 :
716 4 : test_atomic_uint64();
717 :
718 : /*
719 : * Arguably this shouldn't be tested as part of this function, but it's
720 : * closely enough related that that seems ok for now.
721 : */
722 4 : test_spinlock();
723 :
724 4 : PG_RETURN_BOOL(true);
725 : }
726 :
727 5 : PG_FUNCTION_INFO_V1(test_fdw_handler);
728 : Datum
729 0 : test_fdw_handler(PG_FUNCTION_ARGS)
730 : {
731 0 : elog(ERROR, "test_fdw_handler is not implemented");
732 : PG_RETURN_NULL();
733 : }
734 :
735 13 : PG_FUNCTION_INFO_V1(test_fdw_connection);
736 : Datum
737 8 : test_fdw_connection(PG_FUNCTION_ARGS)
738 : {
739 8 : PG_RETURN_TEXT_P(cstring_to_text("dbname=regress_doesnotexist user=doesnotexist password=secret"));
740 : }
741 :
742 9 : PG_FUNCTION_INFO_V1(is_catalog_text_unique_index_oid);
743 : Datum
744 882 : is_catalog_text_unique_index_oid(PG_FUNCTION_ARGS)
745 : {
746 882 : return BoolGetDatum(IsCatalogTextUniqueIndexOid(PG_GETARG_OID(0)));
747 : }
748 :
749 9 : PG_FUNCTION_INFO_V1(test_support_func);
750 : Datum
751 60 : test_support_func(PG_FUNCTION_ARGS)
752 : {
753 60 : Node *rawreq = (Node *) PG_GETARG_POINTER(0);
754 60 : Node *ret = NULL;
755 :
756 60 : if (IsA(rawreq, SupportRequestSelectivity))
757 : {
758 : /*
759 : * Assume that the target is int4eq; that's safe as long as we don't
760 : * attach this to any other boolean-returning function.
761 : */
762 5 : SupportRequestSelectivity *req = (SupportRequestSelectivity *) rawreq;
763 : Selectivity s1;
764 :
765 5 : if (req->is_join)
766 0 : s1 = join_selectivity(req->root, Int4EqualOperator,
767 : req->args,
768 : req->inputcollid,
769 : req->jointype,
770 : req->sjinfo);
771 : else
772 5 : s1 = restriction_selectivity(req->root, Int4EqualOperator,
773 : req->args,
774 : req->inputcollid,
775 : req->varRelid);
776 :
777 5 : req->selectivity = s1;
778 5 : ret = (Node *) req;
779 : }
780 :
781 60 : if (IsA(rawreq, SupportRequestCost))
782 : {
783 : /* Provide some generic estimate */
784 15 : SupportRequestCost *req = (SupportRequestCost *) rawreq;
785 :
786 15 : req->startup = 0;
787 15 : req->per_tuple = 2 * cpu_operator_cost;
788 15 : ret = (Node *) req;
789 : }
790 :
791 60 : if (IsA(rawreq, SupportRequestRows))
792 : {
793 : /*
794 : * Assume that the target is generate_series_int4; that's safe as long
795 : * as we don't attach this to any other set-returning function.
796 : */
797 10 : SupportRequestRows *req = (SupportRequestRows *) rawreq;
798 :
799 10 : if (req->node && IsA(req->node, FuncExpr)) /* be paranoid */
800 : {
801 10 : List *args = ((FuncExpr *) req->node)->args;
802 10 : Node *arg1 = linitial(args);
803 10 : Node *arg2 = lsecond(args);
804 :
805 10 : if (IsA(arg1, Const) &&
806 10 : !((Const *) arg1)->constisnull &&
807 10 : IsA(arg2, Const) &&
808 10 : !((Const *) arg2)->constisnull)
809 : {
810 10 : int32 val1 = DatumGetInt32(((Const *) arg1)->constvalue);
811 10 : int32 val2 = DatumGetInt32(((Const *) arg2)->constvalue);
812 :
813 10 : req->rows = val2 - val1 + 1;
814 10 : ret = (Node *) req;
815 : }
816 : }
817 : }
818 :
819 60 : PG_RETURN_POINTER(ret);
820 : }
821 :
822 9 : PG_FUNCTION_INFO_V1(test_inline_in_from_support_func);
823 : Datum
824 40 : test_inline_in_from_support_func(PG_FUNCTION_ARGS)
825 : {
826 40 : Node *rawreq = (Node *) PG_GETARG_POINTER(0);
827 :
828 40 : if (IsA(rawreq, SupportRequestInlineInFrom))
829 : {
830 : /*
831 : * Assume that the target is foo_from_bar; that's safe as long as we
832 : * don't attach this to any other function.
833 : */
834 20 : SupportRequestInlineInFrom *req = (SupportRequestInlineInFrom *) rawreq;
835 : StringInfoData sql;
836 20 : RangeTblFunction *rtfunc = req->rtfunc;
837 20 : FuncExpr *expr = (FuncExpr *) rtfunc->funcexpr;
838 : Node *node;
839 : Const *c;
840 : char *colname;
841 : char *tablename;
842 : SQLFunctionParseInfoPtr pinfo;
843 : List *raw_parsetree_list;
844 : List *querytree_list;
845 : Query *querytree;
846 :
847 20 : if (list_length(expr->args) != 3)
848 : {
849 0 : ereport(WARNING, (errmsg("test_inline_in_from_support_func called with %d args but expected 3", list_length(expr->args))));
850 0 : PG_RETURN_POINTER(NULL);
851 : }
852 :
853 : /* Get colname */
854 20 : node = linitial(expr->args);
855 20 : if (!IsA(node, Const))
856 : {
857 0 : ereport(WARNING, (errmsg("test_inline_in_from_support_func called with non-Const parameters")));
858 0 : PG_RETURN_POINTER(NULL);
859 : }
860 :
861 20 : c = (Const *) node;
862 20 : if (c->consttype != TEXTOID || c->constisnull)
863 : {
864 0 : ereport(WARNING, (errmsg("test_inline_in_from_support_func called with non-TEXT parameters")));
865 0 : PG_RETURN_POINTER(NULL);
866 : }
867 20 : colname = TextDatumGetCString(c->constvalue);
868 :
869 : /* Get tablename */
870 20 : node = lsecond(expr->args);
871 20 : if (!IsA(node, Const))
872 : {
873 0 : ereport(WARNING, (errmsg("test_inline_in_from_support_func called with non-Const parameters")));
874 0 : PG_RETURN_POINTER(NULL);
875 : }
876 :
877 20 : c = (Const *) node;
878 20 : if (c->consttype != TEXTOID || c->constisnull)
879 : {
880 0 : ereport(WARNING, (errmsg("test_inline_in_from_support_func called with non-TEXT parameters")));
881 0 : PG_RETURN_POINTER(NULL);
882 : }
883 20 : tablename = TextDatumGetCString(c->constvalue);
884 :
885 : /* Begin constructing replacement SELECT query. */
886 20 : initStringInfo(&sql);
887 20 : appendStringInfo(&sql, "SELECT %s::text FROM %s",
888 : quote_identifier(colname),
889 : quote_identifier(tablename));
890 :
891 : /* Add filter expression if present. */
892 20 : node = lthird(expr->args);
893 20 : if (!(IsA(node, Const) && ((Const *) node)->constisnull))
894 : {
895 : /*
896 : * We only filter if $3 is not constant-NULL. This is not a very
897 : * exact implementation of the PL/pgSQL original, but it's close
898 : * enough for demonstration purposes.
899 : */
900 10 : appendStringInfo(&sql, " WHERE %s::text = $3",
901 : quote_identifier(colname));
902 : }
903 :
904 : /* Build a SQLFunctionParseInfo with the parameters of my function. */
905 20 : pinfo = prepare_sql_fn_parse_info(req->proc,
906 : (Node *) expr,
907 : expr->inputcollid);
908 :
909 : /* Parse the generated SQL. */
910 20 : raw_parsetree_list = pg_parse_query(sql.data);
911 20 : if (list_length(raw_parsetree_list) != 1)
912 : {
913 0 : ereport(WARNING, (errmsg("test_inline_in_from_support_func parsed to more than one node")));
914 0 : PG_RETURN_POINTER(NULL);
915 : }
916 :
917 : /* Analyze the parse tree as if it were a SQL-language body. */
918 20 : querytree_list = pg_analyze_and_rewrite_withcb(linitial(raw_parsetree_list),
919 20 : sql.data,
920 : (ParserSetupHook) sql_fn_parser_setup,
921 : pinfo, NULL);
922 20 : if (list_length(querytree_list) != 1)
923 : {
924 0 : ereport(WARNING, (errmsg("test_inline_in_from_support_func rewrote to more than one node")));
925 0 : PG_RETURN_POINTER(NULL);
926 : }
927 :
928 20 : querytree = linitial(querytree_list);
929 20 : if (!IsA(querytree, Query))
930 : {
931 0 : ereport(WARNING, (errmsg("test_inline_in_from_support_func didn't parse to a Query")));
932 0 : PG_RETURN_POINTER(NULL);
933 : }
934 :
935 20 : PG_RETURN_POINTER(querytree);
936 : }
937 :
938 20 : PG_RETURN_POINTER(NULL);
939 : }
940 :
941 5 : PG_FUNCTION_INFO_V1(test_opclass_options_func);
942 : Datum
943 0 : test_opclass_options_func(PG_FUNCTION_ARGS)
944 : {
945 0 : PG_RETURN_NULL();
946 : }
947 :
948 : /* one-time tests for encoding infrastructure */
949 9 : PG_FUNCTION_INFO_V1(test_enc_setup);
950 : Datum
951 4 : test_enc_setup(PG_FUNCTION_ARGS)
952 : {
953 : /* Test pg_encoding_set_invalid() */
954 172 : for (int i = 0; i < _PG_LAST_ENCODING_; i++)
955 : {
956 : char buf[2],
957 : bigbuf[16];
958 : int len,
959 : mblen,
960 : valid;
961 :
962 168 : if (!PG_VALID_ENCODING(i))
963 116 : continue;
964 164 : if (pg_encoding_max_length(i) == 1)
965 112 : continue;
966 52 : pg_encoding_set_invalid(i, buf);
967 52 : len = strnlen(buf, 2);
968 52 : if (len != 2)
969 0 : elog(WARNING,
970 : "official invalid string for encoding \"%s\" has length %d",
971 : pg_enc2name_tbl[i].name, len);
972 52 : mblen = pg_encoding_mblen(i, buf);
973 52 : if (mblen != 2)
974 0 : elog(WARNING,
975 : "official invalid string for encoding \"%s\" has mblen %d",
976 : pg_enc2name_tbl[i].name, mblen);
977 52 : valid = pg_encoding_verifymbstr(i, buf, len);
978 52 : if (valid != 0)
979 0 : elog(WARNING,
980 : "official invalid string for encoding \"%s\" has valid prefix of length %d",
981 : pg_enc2name_tbl[i].name, valid);
982 52 : valid = pg_encoding_verifymbstr(i, buf, 1);
983 52 : if (valid != 0)
984 0 : elog(WARNING,
985 : "first byte of official invalid string for encoding \"%s\" has valid prefix of length %d",
986 : pg_enc2name_tbl[i].name, valid);
987 52 : memset(bigbuf, ' ', sizeof(bigbuf));
988 52 : bigbuf[0] = buf[0];
989 52 : bigbuf[1] = buf[1];
990 52 : valid = pg_encoding_verifymbstr(i, bigbuf, sizeof(bigbuf));
991 52 : if (valid != 0)
992 0 : elog(WARNING,
993 : "trailing data changed official invalid string for encoding \"%s\" to have valid prefix of length %d",
994 : pg_enc2name_tbl[i].name, valid);
995 : }
996 :
997 4 : PG_RETURN_VOID();
998 : }
999 :
1000 : /*
1001 : * Call an encoding conversion or verification function.
1002 : *
1003 : * Arguments:
1004 : * string bytea -- string to convert
1005 : * src_enc name -- source encoding
1006 : * dest_enc name -- destination encoding
1007 : * noError bool -- if set, don't ereport() on invalid or untranslatable
1008 : * input
1009 : *
1010 : * Result is a tuple with two attributes:
1011 : * int4 -- number of input bytes successfully converted
1012 : * bytea -- converted string
1013 : */
1014 9 : PG_FUNCTION_INFO_V1(test_enc_conversion);
1015 : Datum
1016 5152 : test_enc_conversion(PG_FUNCTION_ARGS)
1017 : {
1018 5152 : bytea *string = PG_GETARG_BYTEA_PP(0);
1019 5152 : char *src_encoding_name = NameStr(*PG_GETARG_NAME(1));
1020 5152 : int src_encoding = pg_char_to_encoding(src_encoding_name);
1021 5152 : char *dest_encoding_name = NameStr(*PG_GETARG_NAME(2));
1022 5152 : int dest_encoding = pg_char_to_encoding(dest_encoding_name);
1023 5152 : bool noError = PG_GETARG_BOOL(3);
1024 : TupleDesc tupdesc;
1025 : char *src;
1026 : char *dst;
1027 : bytea *retval;
1028 : Size srclen;
1029 : Size dstsize;
1030 : Oid proc;
1031 : int convertedbytes;
1032 : int dstlen;
1033 : Datum values[2];
1034 5152 : bool nulls[2] = {0};
1035 : HeapTuple tuple;
1036 :
1037 5152 : if (src_encoding < 0)
1038 0 : ereport(ERROR,
1039 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1040 : errmsg("invalid source encoding name \"%s\"",
1041 : src_encoding_name)));
1042 5152 : if (dest_encoding < 0)
1043 0 : ereport(ERROR,
1044 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1045 : errmsg("invalid destination encoding name \"%s\"",
1046 : dest_encoding_name)));
1047 :
1048 : /* Build a tuple descriptor for our result type */
1049 5152 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1050 0 : elog(ERROR, "return type must be a row type");
1051 5152 : tupdesc = BlessTupleDesc(tupdesc);
1052 :
1053 5152 : srclen = VARSIZE_ANY_EXHDR(string);
1054 5152 : src = VARDATA_ANY(string);
1055 :
1056 5152 : if (src_encoding == dest_encoding)
1057 : {
1058 : /* just check that the source string is valid */
1059 : int oklen;
1060 :
1061 2572 : oklen = pg_encoding_verifymbstr(src_encoding, src, srclen);
1062 :
1063 2572 : if (oklen == srclen)
1064 : {
1065 652 : convertedbytes = oklen;
1066 652 : retval = string;
1067 : }
1068 1920 : else if (!noError)
1069 : {
1070 960 : report_invalid_encoding(src_encoding, src + oklen, srclen - oklen);
1071 : }
1072 : else
1073 : {
1074 : /*
1075 : * build bytea data type structure.
1076 : */
1077 : Assert(oklen < srclen);
1078 960 : convertedbytes = oklen;
1079 960 : retval = (bytea *) palloc(oklen + VARHDRSZ);
1080 960 : SET_VARSIZE(retval, oklen + VARHDRSZ);
1081 960 : memcpy(VARDATA(retval), src, oklen);
1082 : }
1083 : }
1084 : else
1085 : {
1086 2580 : proc = FindDefaultConversionProc(src_encoding, dest_encoding);
1087 2580 : if (!OidIsValid(proc))
1088 0 : ereport(ERROR,
1089 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
1090 : errmsg("default conversion function for encoding \"%s\" to \"%s\" does not exist",
1091 : pg_encoding_to_char(src_encoding),
1092 : pg_encoding_to_char(dest_encoding))));
1093 :
1094 2580 : if (srclen >= (MaxAllocSize / (Size) MAX_CONVERSION_GROWTH))
1095 0 : ereport(ERROR,
1096 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1097 : errmsg("out of memory"),
1098 : errdetail("String of %d bytes is too long for encoding conversion.",
1099 : (int) srclen)));
1100 :
1101 2580 : dstsize = (Size) srclen * MAX_CONVERSION_GROWTH + 1;
1102 2580 : dst = MemoryContextAlloc(CurrentMemoryContext, dstsize);
1103 :
1104 : /* perform conversion */
1105 2580 : convertedbytes = pg_do_encoding_conversion_buf(proc,
1106 : src_encoding,
1107 : dest_encoding,
1108 : (unsigned char *) src, srclen,
1109 : (unsigned char *) dst, dstsize,
1110 : noError);
1111 1548 : dstlen = strlen(dst);
1112 :
1113 : /*
1114 : * build bytea data type structure.
1115 : */
1116 1548 : retval = (bytea *) palloc(dstlen + VARHDRSZ);
1117 1548 : SET_VARSIZE(retval, dstlen + VARHDRSZ);
1118 1548 : memcpy(VARDATA(retval), dst, dstlen);
1119 :
1120 1548 : pfree(dst);
1121 : }
1122 :
1123 3160 : values[0] = Int32GetDatum(convertedbytes);
1124 3160 : values[1] = PointerGetDatum(retval);
1125 3160 : tuple = heap_form_tuple(tupdesc, values, nulls);
1126 :
1127 3160 : PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
1128 : }
1129 :
1130 : /* Convert bytea to text without validation for corruption tests from SQL. */
1131 8 : PG_FUNCTION_INFO_V1(test_bytea_to_text);
1132 : Datum
1133 220 : test_bytea_to_text(PG_FUNCTION_ARGS)
1134 : {
1135 220 : PG_RETURN_TEXT_P(PG_GETARG_BYTEA_PP(0));
1136 : }
1137 :
1138 : /* And the reverse. */
1139 8 : PG_FUNCTION_INFO_V1(test_text_to_bytea);
1140 : Datum
1141 200 : test_text_to_bytea(PG_FUNCTION_ARGS)
1142 : {
1143 200 : PG_RETURN_BYTEA_P(PG_GETARG_TEXT_PP(0));
1144 : }
1145 :
1146 : /* Corruption tests in C. */
1147 8 : PG_FUNCTION_INFO_V1(test_mblen_func);
1148 : Datum
1149 24 : test_mblen_func(PG_FUNCTION_ARGS)
1150 : {
1151 24 : const char *func = text_to_cstring(PG_GETARG_BYTEA_PP(0));
1152 24 : const char *encoding = text_to_cstring(PG_GETARG_BYTEA_PP(1));
1153 24 : text *string = PG_GETARG_BYTEA_PP(2);
1154 24 : int offset = PG_GETARG_INT32(3);
1155 24 : const char *data = VARDATA_ANY(string);
1156 24 : size_t size = VARSIZE_ANY_EXHDR(string);
1157 24 : int result = 0;
1158 :
1159 24 : if (strcmp(func, "pg_mblen_unbounded") == 0)
1160 8 : result = pg_mblen_unbounded(data + offset);
1161 16 : else if (strcmp(func, "pg_mblen_cstr") == 0)
1162 4 : result = pg_mblen_cstr(data + offset);
1163 12 : else if (strcmp(func, "pg_mblen_with_len") == 0)
1164 4 : result = pg_mblen_with_len(data + offset, size - offset);
1165 8 : else if (strcmp(func, "pg_mblen_range") == 0)
1166 4 : result = pg_mblen_range(data + offset, data + size);
1167 4 : else if (strcmp(func, "pg_encoding_mblen") == 0)
1168 4 : result = pg_encoding_mblen(pg_char_to_encoding(encoding), data + offset);
1169 : else
1170 0 : elog(ERROR, "unknown function");
1171 :
1172 12 : PG_RETURN_INT32(result);
1173 : }
1174 :
1175 8 : PG_FUNCTION_INFO_V1(test_text_to_wchars);
1176 : Datum
1177 200 : test_text_to_wchars(PG_FUNCTION_ARGS)
1178 : {
1179 200 : const char *encoding_name = text_to_cstring(PG_GETARG_BYTEA_PP(0));
1180 200 : text *string = PG_GETARG_TEXT_PP(1);
1181 200 : const char *data = VARDATA_ANY(string);
1182 200 : size_t size = VARSIZE_ANY_EXHDR(string);
1183 200 : pg_wchar *wchars = palloc(sizeof(pg_wchar) * (size + 1));
1184 : Datum *datums;
1185 : int wlen;
1186 : int encoding;
1187 :
1188 200 : encoding = pg_char_to_encoding(encoding_name);
1189 200 : if (encoding < 0)
1190 0 : elog(ERROR, "unknown encoding name: %s", encoding_name);
1191 :
1192 200 : if (size > 0)
1193 : {
1194 200 : datums = palloc(sizeof(Datum) * size);
1195 200 : wlen = pg_encoding_mb2wchar_with_len(encoding,
1196 : data,
1197 : wchars,
1198 : size);
1199 : Assert(wlen >= 0);
1200 : Assert(wlen <= size);
1201 : Assert(wchars[wlen] == 0);
1202 :
1203 416 : for (int i = 0; i < wlen; ++i)
1204 216 : datums[i] = UInt32GetDatum(wchars[i]);
1205 : }
1206 : else
1207 : {
1208 0 : datums = NULL;
1209 0 : wlen = 0;
1210 : }
1211 :
1212 200 : PG_RETURN_ARRAYTYPE_P(construct_array_builtin(datums, wlen, INT4OID));
1213 : }
1214 :
1215 8 : PG_FUNCTION_INFO_V1(test_wchars_to_text);
1216 : Datum
1217 200 : test_wchars_to_text(PG_FUNCTION_ARGS)
1218 : {
1219 200 : const char *encoding_name = text_to_cstring(PG_GETARG_BYTEA_PP(0));
1220 200 : ArrayType *array = PG_GETARG_ARRAYTYPE_P(1);
1221 : Datum *datums;
1222 : bool *nulls;
1223 : char *mb;
1224 : text *result;
1225 : int wlen;
1226 : int bytes;
1227 : int encoding;
1228 :
1229 200 : encoding = pg_char_to_encoding(encoding_name);
1230 200 : if (encoding < 0)
1231 0 : elog(ERROR, "unknown encoding name: %s", encoding_name);
1232 :
1233 200 : deconstruct_array_builtin(array, INT4OID, &datums, &nulls, &wlen);
1234 :
1235 200 : if (wlen > 0)
1236 : {
1237 116 : pg_wchar *wchars = palloc(sizeof(pg_wchar) * wlen);
1238 :
1239 332 : for (int i = 0; i < wlen; ++i)
1240 : {
1241 216 : if (nulls[i])
1242 0 : elog(ERROR, "unexpected NULL in array");
1243 216 : wchars[i] = DatumGetInt32(datums[i]);
1244 : }
1245 :
1246 116 : mb = palloc(pg_encoding_max_length(encoding) * wlen + 1);
1247 116 : bytes = pg_encoding_wchar2mb_with_len(encoding, wchars, mb, wlen);
1248 : }
1249 : else
1250 : {
1251 84 : mb = "";
1252 84 : bytes = 0;
1253 : }
1254 :
1255 200 : result = palloc(bytes + VARHDRSZ);
1256 200 : SET_VARSIZE(result, bytes + VARHDRSZ);
1257 200 : memcpy(VARDATA(result), mb, bytes);
1258 :
1259 200 : PG_RETURN_TEXT_P(result);
1260 : }
1261 :
1262 8 : PG_FUNCTION_INFO_V1(test_valid_server_encoding);
1263 : Datum
1264 200 : test_valid_server_encoding(PG_FUNCTION_ARGS)
1265 : {
1266 200 : PG_RETURN_BOOL(pg_valid_server_encoding(text_to_cstring(PG_GETARG_TEXT_PP(0))) >= 0);
1267 : }
1268 :
1269 : /* Provide SQL access to IsBinaryCoercible() */
1270 9 : PG_FUNCTION_INFO_V1(binary_coercible);
1271 : Datum
1272 25376 : binary_coercible(PG_FUNCTION_ARGS)
1273 : {
1274 25376 : Oid srctype = PG_GETARG_OID(0);
1275 25376 : Oid targettype = PG_GETARG_OID(1);
1276 :
1277 25376 : PG_RETURN_BOOL(IsBinaryCoercible(srctype, targettype));
1278 : }
1279 :
1280 : /*
1281 : * Sanity checks for functions in relpath.h
1282 : */
1283 9 : PG_FUNCTION_INFO_V1(test_relpath);
1284 : Datum
1285 4 : test_relpath(PG_FUNCTION_ARGS)
1286 : {
1287 : RelPathStr rpath;
1288 :
1289 : /*
1290 : * Verify that PROCNUMBER_CHARS and MAX_BACKENDS stay in sync.
1291 : * Unfortunately I don't know how to express that in a way suitable for a
1292 : * static assert.
1293 : */
1294 : if ((int) ceil(log10(MAX_BACKENDS)) != PROCNUMBER_CHARS)
1295 : elog(WARNING, "mismatch between MAX_BACKENDS and PROCNUMBER_CHARS");
1296 :
1297 : /* verify that the max-length relpath is generated ok */
1298 4 : rpath = GetRelationPath(OID_MAX, OID_MAX, OID_MAX, MAX_BACKENDS - 1,
1299 : INIT_FORKNUM);
1300 :
1301 4 : if (strlen(rpath.str) != REL_PATH_STR_MAXLEN)
1302 0 : elog(WARNING, "maximum length relpath is if length %zu instead of %zu",
1303 : strlen(rpath.str), REL_PATH_STR_MAXLEN);
1304 :
1305 4 : PG_RETURN_VOID();
1306 : }
1307 :
1308 : /*
1309 : * Simple test to verify NLS support, particularly that the PRI* macros work.
1310 : *
1311 : * A secondary objective is to verify that <inttypes.h>'s values for the
1312 : * PRI* macros match what our snprintf.c code will do. Therefore, we run
1313 : * the ereport() calls even when we know that translation will not happen.
1314 : */
1315 9 : PG_FUNCTION_INFO_V1(test_translation);
1316 : Datum
1317 4 : test_translation(PG_FUNCTION_ARGS)
1318 : {
1319 : #ifdef ENABLE_NLS
1320 : static bool inited = false;
1321 :
1322 : /*
1323 : * Ideally we'd do this bit in a _PG_init() hook. However, it seems best
1324 : * that the Solaris hack only get applied in the nls.sql test, so it
1325 : * doesn't risk affecting other tests that load this module.
1326 : */
1327 4 : if (!inited)
1328 : {
1329 : /*
1330 : * Solaris' built-in gettext is not bright about associating locales
1331 : * with message catalogs that are named after just the language.
1332 : * Apparently the customary workaround is for users to set the
1333 : * LANGUAGE environment variable to provide a mapping. Do so here to
1334 : * ensure that the nls.sql regression test will work.
1335 : */
1336 : #if defined(__sun__)
1337 : setenv("LANGUAGE", "es_ES.UTF-8:es", 1);
1338 : #endif
1339 4 : pg_bindtextdomain(TEXTDOMAIN);
1340 4 : inited = true;
1341 : }
1342 :
1343 : /*
1344 : * If nls.sql failed to select a non-C locale, no translation will happen.
1345 : * Report that so that we can distinguish this outcome from brokenness.
1346 : * (We do this here, not in nls.sql, so as to need only 3 expected files.)
1347 : */
1348 4 : if (strcmp(GetConfigOption("lc_messages", false, false), "C") == 0)
1349 4 : elog(NOTICE, "lc_messages is 'C'");
1350 : #else
1351 : elog(NOTICE, "NLS is not enabled");
1352 : #endif
1353 :
1354 4 : ereport(NOTICE,
1355 : errmsg("translated PRId64 = %" PRId64, (int64) 424242424242));
1356 4 : ereport(NOTICE,
1357 : errmsg("translated PRId32 = %" PRId32, (int32) -1234));
1358 4 : ereport(NOTICE,
1359 : errmsg("translated PRIdMAX = %" PRIdMAX, (intmax_t) -123456789012));
1360 4 : ereport(NOTICE,
1361 : errmsg("translated PRIdPTR = %" PRIdPTR, (intptr_t) -9999));
1362 :
1363 4 : ereport(NOTICE,
1364 : errmsg("translated PRIu64 = %" PRIu64, (uint64) 424242424242));
1365 4 : ereport(NOTICE,
1366 : errmsg("translated PRIu32 = %" PRIu32, (uint32) -1234));
1367 4 : ereport(NOTICE,
1368 : errmsg("translated PRIuMAX = %" PRIuMAX, (uintmax_t) 123456789012));
1369 4 : ereport(NOTICE,
1370 : errmsg("translated PRIuPTR = %" PRIuPTR, (uintptr_t) 9999));
1371 :
1372 4 : ereport(NOTICE,
1373 : errmsg("translated PRIx64 = %" PRIx64, (uint64) 424242424242));
1374 4 : ereport(NOTICE,
1375 : errmsg("translated PRIx32 = %" PRIx32, (uint32) -1234));
1376 4 : ereport(NOTICE,
1377 : errmsg("translated PRIxMAX = %" PRIxMAX, (uintmax_t) 123456789012));
1378 4 : ereport(NOTICE,
1379 : errmsg("translated PRIxPTR = %" PRIxPTR, (uintptr_t) 9999));
1380 :
1381 4 : ereport(NOTICE,
1382 : errmsg("translated PRIX64 = %" PRIX64, (uint64) 424242424242));
1383 4 : ereport(NOTICE,
1384 : errmsg("translated PRIX32 = %" PRIX32, (uint32) -1234));
1385 4 : ereport(NOTICE,
1386 : errmsg("translated PRIXMAX = %" PRIXMAX, (uintmax_t) 123456789012));
1387 4 : ereport(NOTICE,
1388 : errmsg("translated PRIXPTR = %" PRIXPTR, (uintptr_t) 9999));
1389 :
1390 4 : PG_RETURN_VOID();
1391 : }
1392 :
1393 : /* Verify that pg_ticks_to_ns behaves correct, including overflow */
1394 9 : PG_FUNCTION_INFO_V1(test_instr_time);
1395 : Datum
1396 4 : test_instr_time(PG_FUNCTION_ARGS)
1397 : {
1398 : instr_time t;
1399 4 : int64 test_ns[] = {0, 1000, INT64CONST(1000000000000000)};
1400 : int64 max_err;
1401 :
1402 : /*
1403 : * The ns-to-ticks-to-ns roundtrip may lose precision due to integer
1404 : * truncation in the fixed-point conversion. The maximum error depends on
1405 : * ticks_per_ns_scaled relative to the shift factor.
1406 : */
1407 4 : max_err = (ticks_per_ns_scaled >> TICKS_TO_NS_SHIFT) + 1;
1408 :
1409 16 : for (int i = 0; i < lengthof(test_ns); i++)
1410 : {
1411 : int64 result;
1412 :
1413 12 : INSTR_TIME_SET_ZERO(t);
1414 12 : INSTR_TIME_ADD_NANOSEC(t, test_ns[i]);
1415 12 : result = INSTR_TIME_GET_NANOSEC(t);
1416 :
1417 12 : if (result < test_ns[i] - max_err || result > test_ns[i])
1418 0 : elog(ERROR,
1419 : "INSTR_TIME_GET_NANOSEC(t) yielded " INT64_FORMAT
1420 : ", expected " INT64_FORMAT " (max_err " INT64_FORMAT
1421 : ") in file \"%s\" line %u",
1422 : result, test_ns[i], max_err, __FILE__, __LINE__);
1423 : }
1424 :
1425 4 : PG_RETURN_BOOL(true);
1426 : }
1427 :
1428 : /*
1429 : * test_pglz_compress
1430 : *
1431 : * Compress the input using pglz_compress(). Only the "always" strategy is
1432 : * currently supported.
1433 : *
1434 : * Returns the compressed data, or NULL if compression fails.
1435 : */
1436 8 : PG_FUNCTION_INFO_V1(test_pglz_compress);
1437 : Datum
1438 16 : test_pglz_compress(PG_FUNCTION_ARGS)
1439 : {
1440 16 : bytea *input = PG_GETARG_BYTEA_PP(0);
1441 16 : char *source = VARDATA_ANY(input);
1442 16 : int32 slen = VARSIZE_ANY_EXHDR(input);
1443 16 : int32 maxout = PGLZ_MAX_OUTPUT(slen);
1444 : bytea *result;
1445 : int32 clen;
1446 :
1447 16 : result = (bytea *) palloc(maxout + VARHDRSZ);
1448 16 : clen = pglz_compress(source, slen, VARDATA(result),
1449 : PGLZ_strategy_always);
1450 16 : if (clen < 0)
1451 0 : PG_RETURN_NULL();
1452 :
1453 16 : SET_VARSIZE(result, clen + VARHDRSZ);
1454 16 : PG_RETURN_BYTEA_P(result);
1455 : }
1456 :
1457 : /*
1458 : * test_pglz_decompress
1459 : *
1460 : * Decompress the input using pglz_decompress().
1461 : *
1462 : * The second argument is the expected uncompressed data size. The third
1463 : * argument is here for the check_complete flag.
1464 : *
1465 : * Returns the decompressed data, or raises an error if decompression fails.
1466 : */
1467 8 : PG_FUNCTION_INFO_V1(test_pglz_decompress);
1468 : Datum
1469 56 : test_pglz_decompress(PG_FUNCTION_ARGS)
1470 : {
1471 56 : bytea *input = PG_GETARG_BYTEA_PP(0);
1472 56 : int32 rawsize = PG_GETARG_INT32(1);
1473 56 : bool check_complete = PG_GETARG_BOOL(2);
1474 56 : char *source = VARDATA_ANY(input);
1475 56 : int32 slen = VARSIZE_ANY_EXHDR(input);
1476 : bytea *result;
1477 : int32 dlen;
1478 :
1479 56 : if (rawsize < 0)
1480 0 : elog(ERROR, "rawsize must not be negative");
1481 :
1482 56 : result = (bytea *) palloc(rawsize + VARHDRSZ);
1483 :
1484 56 : dlen = pglz_decompress(source, slen, VARDATA(result),
1485 : rawsize, check_complete);
1486 56 : if (dlen < 0)
1487 44 : elog(ERROR, "pglz_decompress failed");
1488 :
1489 12 : SET_VARSIZE(result, dlen + VARHDRSZ);
1490 12 : PG_RETURN_BYTEA_P(result);
1491 : }
|