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-2019, 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/htup_details.h"
23#include "access/transam.h"
24#include "access/tuptoaster.h"
25#include "access/xact.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 "executor/executor.h"
31#include "executor/spi.h"
32#include "miscadmin.h"
33#include "nodes/supportnodes.h"
34#include "optimizer/optimizer.h"
35#include "optimizer/plancat.h"
36#include "port/atomics.h"
37#include "utils/builtins.h"
38#include "utils/geo_decls.h"
39#include "utils/rel.h"
40#include "utils/typcache.h"
41#include "utils/memutils.h"
42
43
44#define LDELIM '('
45#define RDELIM ')'
46#define DELIM ','
47
48static void regress_lseg_construct(LSEG *lseg, Point *pt1, Point *pt2);
49
50PG_MODULE_MAGIC;
51
52
53/* return the point where two paths intersect, or NULL if no intersection. */
54PG_FUNCTION_INFO_V1(interpt_pp);
55
56Datum
57interpt_pp(PG_FUNCTION_ARGS)
58{
59 PATH *p1 = PG_GETARG_PATH_P(0);
60 PATH *p2 = PG_GETARG_PATH_P(1);
61 int i,
62 j;
63 LSEG seg1,
64 seg2;
65 bool found; /* We've found the intersection */
66
67 found = false; /* Haven't found it yet */
68
69 for (i = 0; i < p1->npts - 1 && !found; i++)
70 {
71 regress_lseg_construct(&seg1, &p1->p[i], &p1->p[i + 1]);
72 for (j = 0; j < p2->npts - 1 && !found; j++)
73 {
74 regress_lseg_construct(&seg2, &p2->p[j], &p2->p[j + 1]);
75 if (DatumGetBool(DirectFunctionCall2(lseg_intersect,
76 LsegPGetDatum(&seg1),
77 LsegPGetDatum(&seg2))))
78 found = true;
79 }
80 }
81
82 if (!found)
83 PG_RETURN_NULL();
84
85 /*
86 * Note: DirectFunctionCall2 will kick out an error if lseg_interpt()
87 * returns NULL, but that should be impossible since we know the two
88 * segments intersect.
89 */
90 PG_RETURN_DATUM(DirectFunctionCall2(lseg_interpt,
91 LsegPGetDatum(&seg1),
92 LsegPGetDatum(&seg2)));
93}
94
95
96/* like lseg_construct, but assume space already allocated */
97static void
98regress_lseg_construct(LSEG *lseg, Point *pt1, Point *pt2)
99{
100 lseg->p[0].x = pt1->x;
101 lseg->p[0].y = pt1->y;
102 lseg->p[1].x = pt2->x;
103 lseg->p[1].y = pt2->y;
104}
105
106PG_FUNCTION_INFO_V1(overpaid);
107
108Datum
109overpaid(PG_FUNCTION_ARGS)
110{
111 HeapTupleHeader tuple = PG_GETARG_HEAPTUPLEHEADER(0);
112 bool isnull;
113 int32 salary;
114
115 salary = DatumGetInt32(GetAttributeByName(tuple, "salary", &isnull));
116 if (isnull)
117 PG_RETURN_NULL();
118 PG_RETURN_BOOL(salary > 699);
119}
120
121/* New type "widget"
122 * This used to be "circle", but I added circle to builtins,
123 * so needed to make sure the names do not collide. - tgl 97/04/21
124 */
125
126typedef struct
127{
128 Point center;
129 double radius;
130} WIDGET;
131
132PG_FUNCTION_INFO_V1(widget_in);
133PG_FUNCTION_INFO_V1(widget_out);
134
135#define NARGS 3
136
137Datum
138widget_in(PG_FUNCTION_ARGS)
139{
140 char *str = PG_GETARG_CSTRING(0);
141 char *p,
142 *coord[NARGS];
143 int i;
144 WIDGET *result;
145
146 for (i = 0, p = str; *p && i < NARGS && *p != RDELIM; p++)
147 {
148 if (*p == DELIM || (*p == LDELIM && i == 0))
149 coord[i++] = p + 1;
150 }
151
152 if (i < NARGS)
153 ereport(ERROR,
154 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
155 errmsg("invalid input syntax for type %s: \"%s\"",
156 "widget", str)));
157
158 result = (WIDGET *) palloc(sizeof(WIDGET));
159 result->center.x = atof(coord[0]);
160 result->center.y = atof(coord[1]);
161 result->radius = atof(coord[2]);
162
163 PG_RETURN_POINTER(result);
164}
165
166Datum
167widget_out(PG_FUNCTION_ARGS)
168{
169 WIDGET *widget = (WIDGET *) PG_GETARG_POINTER(0);
170 char *str = psprintf("(%g,%g,%g)",
171 widget->center.x, widget->center.y, widget->radius);
172
173 PG_RETURN_CSTRING(str);
174}
175
176PG_FUNCTION_INFO_V1(pt_in_widget);
177
178Datum
179pt_in_widget(PG_FUNCTION_ARGS)
180{
181 Point *point = PG_GETARG_POINT_P(0);
182 WIDGET *widget = (WIDGET *) PG_GETARG_POINTER(1);
183 float8 distance;
184
185 distance = DatumGetFloat8(DirectFunctionCall2(point_distance,
186 PointPGetDatum(point),
187 PointPGetDatum(&widget->center)));
188
189 PG_RETURN_BOOL(distance < widget->radius);
190}
191
192PG_FUNCTION_INFO_V1(reverse_name);
193
194Datum
195reverse_name(PG_FUNCTION_ARGS)
196{
197 char *string = PG_GETARG_CSTRING(0);
198 int i;
199 int len;
200 char *new_string;
201
202 new_string = palloc0(NAMEDATALEN);
203 for (i = 0; i < NAMEDATALEN && string[i]; ++i)
204 ;
205 if (i == NAMEDATALEN || !string[i])
206 --i;
207 len = i;
208 for (; i >= 0; --i)
209 new_string[len - i] = string[i];
210 PG_RETURN_CSTRING(new_string);
211}
212
213PG_FUNCTION_INFO_V1(trigger_return_old);
214
215Datum
216trigger_return_old(PG_FUNCTION_ARGS)
217{
218 TriggerData *trigdata = (TriggerData *) fcinfo->context;
219 HeapTuple tuple;
220
221 if (!CALLED_AS_TRIGGER(fcinfo))
222 elog(ERROR, "trigger_return_old: not fired by trigger manager");
223
224 tuple = trigdata->tg_trigtuple;
225
226 return PointerGetDatum(tuple);
227}
228
229#define TTDUMMY_INFINITY 999999
230
231static SPIPlanPtr splan = NULL;
232static bool ttoff = false;
233
234PG_FUNCTION_INFO_V1(ttdummy);
235
236Datum
237ttdummy(PG_FUNCTION_ARGS)
238{
239 TriggerData *trigdata = (TriggerData *) fcinfo->context;
240 Trigger *trigger; /* to get trigger name */
241 char **args; /* arguments */
242 int attnum[2]; /* fnumbers of start/stop columns */
243 Datum oldon,
244 oldoff;
245 Datum newon,
246 newoff;
247 Datum *cvals; /* column values */
248 char *cnulls; /* column nulls */
249 char *relname; /* triggered relation name */
250 Relation rel; /* triggered relation */
251 HeapTuple trigtuple;
252 HeapTuple newtuple = NULL;
253 HeapTuple rettuple;
254 TupleDesc tupdesc; /* tuple description */
255 int natts; /* # of attributes */
256 bool isnull; /* to know is some column NULL or not */
257 int ret;
258 int i;
259
260 if (!CALLED_AS_TRIGGER(fcinfo))
261 elog(ERROR, "ttdummy: not fired by trigger manager");
262 if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
263 elog(ERROR, "ttdummy: must be fired for row");
264 if (!TRIGGER_FIRED_BEFORE(trigdata->tg_event))
265 elog(ERROR, "ttdummy: must be fired before event");
266 if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
267 elog(ERROR, "ttdummy: cannot process INSERT event");
268 if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
269 newtuple = trigdata->tg_newtuple;
270
271 trigtuple = trigdata->tg_trigtuple;
272
273 rel = trigdata->tg_relation;
274 relname = SPI_getrelname(rel);
275
276 /* check if TT is OFF for this relation */
277 if (ttoff) /* OFF - nothing to do */
278 {
279 pfree(relname);
280 return PointerGetDatum((newtuple != NULL) ? newtuple : trigtuple);
281 }
282
283 trigger = trigdata->tg_trigger;
284
285 if (trigger->tgnargs != 2)
286 elog(ERROR, "ttdummy (%s): invalid (!= 2) number of arguments %d",
287 relname, trigger->tgnargs);
288
289 args = trigger->tgargs;
290 tupdesc = rel->rd_att;
291 natts = tupdesc->natts;
292
293 for (i = 0; i < 2; i++)
294 {
295 attnum[i] = SPI_fnumber(tupdesc, args[i]);
296 if (attnum[i] <= 0)
297 elog(ERROR, "ttdummy (%s): there is no attribute %s",
298 relname, args[i]);
299 if (SPI_gettypeid(tupdesc, attnum[i]) != INT4OID)
300 elog(ERROR, "ttdummy (%s): attribute %s must be of integer type",
301 relname, args[i]);
302 }
303
304 oldon = SPI_getbinval(trigtuple, tupdesc, attnum[0], &isnull);
305 if (isnull)
306 elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[0]);
307
308 oldoff = SPI_getbinval(trigtuple, tupdesc, attnum[1], &isnull);
309 if (isnull)
310 elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[1]);
311
312 if (newtuple != NULL) /* UPDATE */
313 {
314 newon = SPI_getbinval(newtuple, tupdesc, attnum[0], &isnull);
315 if (isnull)
316 elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[0]);
317 newoff = SPI_getbinval(newtuple, tupdesc, attnum[1], &isnull);
318 if (isnull)
319 elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[1]);
320
321 if (oldon != newon || oldoff != newoff)
322 ereport(ERROR,
323 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
324 errmsg("ttdummy (%s): you cannot change %s and/or %s columns (use set_ttdummy)",
325 relname, args[0], args[1])));
326
327 if (newoff != TTDUMMY_INFINITY)
328 {
329 pfree(relname); /* allocated in upper executor context */
330 return PointerGetDatum(NULL);
331 }
332 }
333 else if (oldoff != TTDUMMY_INFINITY) /* DELETE */
334 {
335 pfree(relname);
336 return PointerGetDatum(NULL);
337 }
338
339 newoff = DirectFunctionCall1(nextval, CStringGetTextDatum("ttdummy_seq"));
340 /* nextval now returns int64; coerce down to int32 */
341 newoff = Int32GetDatum((int32) DatumGetInt64(newoff));
342
343 /* Connect to SPI manager */
344 if ((ret = SPI_connect()) < 0)
345 elog(ERROR, "ttdummy (%s): SPI_connect returned %d", relname, ret);
346
347 /* Fetch tuple values and nulls */
348 cvals = (Datum *) palloc(natts * sizeof(Datum));
349 cnulls = (char *) palloc(natts * sizeof(char));
350 for (i = 0; i < natts; i++)
351 {
352 cvals[i] = SPI_getbinval((newtuple != NULL) ? newtuple : trigtuple,
353 tupdesc, i + 1, &isnull);
354 cnulls[i] = (isnull) ? 'n' : ' ';
355 }
356
357 /* change date column(s) */
358 if (newtuple) /* UPDATE */
359 {
360 cvals[attnum[0] - 1] = newoff; /* start_date eq current date */
361 cnulls[attnum[0] - 1] = ' ';
362 cvals[attnum[1] - 1] = TTDUMMY_INFINITY; /* stop_date eq INFINITY */
363 cnulls[attnum[1] - 1] = ' ';
364 }
365 else
366 /* DELETE */
367 {
368 cvals[attnum[1] - 1] = newoff; /* stop_date eq current date */
369 cnulls[attnum[1] - 1] = ' ';
370 }
371
372 /* if there is no plan ... */
373 if (splan == NULL)
374 {
375 SPIPlanPtr pplan;
376 Oid *ctypes;
377 char *query;
378
379 /* allocate space in preparation */
380 ctypes = (Oid *) palloc(natts * sizeof(Oid));
381 query = (char *) palloc(100 + 16 * natts);
382
383 /*
384 * Construct query: INSERT INTO _relation_ VALUES ($1, ...)
385 */
386 sprintf(query, "INSERT INTO %s VALUES (", relname);
387 for (i = 1; i <= natts; i++)
388 {
389 sprintf(query + strlen(query), "$%d%s",
390 i, (i < natts) ? ", " : ")");
391 ctypes[i - 1] = SPI_gettypeid(tupdesc, i);
392 }
393
394 /* Prepare plan for query */
395 pplan = SPI_prepare(query, natts, ctypes);
396 if (pplan == NULL)
397 elog(ERROR, "ttdummy (%s): SPI_prepare returned %s", relname, SPI_result_code_string(SPI_result));
398
399 if (SPI_keepplan(pplan))
400 elog(ERROR, "ttdummy (%s): SPI_keepplan failed", relname);
401
402 splan = pplan;
403 }
404
405 ret = SPI_execp(splan, cvals, cnulls, 0);
406
407 if (ret < 0)
408 elog(ERROR, "ttdummy (%s): SPI_execp returned %d", relname, ret);
409
410 /* Tuple to return to upper Executor ... */
411 if (newtuple) /* UPDATE */
412 rettuple = SPI_modifytuple(rel, trigtuple, 1, &(attnum[1]), &newoff, NULL);
413 else /* DELETE */
414 rettuple = trigtuple;
415
416 SPI_finish(); /* don't forget say Bye to SPI mgr */
417
418 pfree(relname);
419
420 return PointerGetDatum(rettuple);
421}
422
423PG_FUNCTION_INFO_V1(set_ttdummy);
424
425Datum
426set_ttdummy(PG_FUNCTION_ARGS)
427{
428 int32 on = PG_GETARG_INT32(0);
429
430 if (ttoff) /* OFF currently */
431 {
432 if (on == 0)
433 PG_RETURN_INT32(0);
434
435 /* turn ON */
436 ttoff = false;
437 PG_RETURN_INT32(0);
438 }
439
440 /* ON currently */
441 if (on != 0)
442 PG_RETURN_INT32(1);
443
444 /* turn OFF */
445 ttoff = true;
446
447 PG_RETURN_INT32(1);
448}
449
450
451/*
452 * Type int44 has no real-world use, but the regression tests use it
453 * (under the alias "city_budget"). It's a four-element vector of int4's.
454 */
455
456/*
457 * int44in - converts "num, num, ..." to internal form
458 *
459 * Note: Fills any missing positions with zeroes.
460 */
461PG_FUNCTION_INFO_V1(int44in);
462
463Datum
464int44in(PG_FUNCTION_ARGS)
465{
466 char *input_string = PG_GETARG_CSTRING(0);
467 int32 *result = (int32 *) palloc(4 * sizeof(int32));
468 int i;
469
470 i = sscanf(input_string,
471 "%d, %d, %d, %d",
472 &result[0],
473 &result[1],
474 &result[2],
475 &result[3]);
476 while (i < 4)
477 result[i++] = 0;
478
479 PG_RETURN_POINTER(result);
480}
481
482/*
483 * int44out - converts internal form to "num, num, ..."
484 */
485PG_FUNCTION_INFO_V1(int44out);
486
487Datum
488int44out(PG_FUNCTION_ARGS)
489{
490 int32 *an_array = (int32 *) PG_GETARG_POINTER(0);
491 char *result = (char *) palloc(16 * 4);
492
493 snprintf(result, 16 * 4, "%d,%d,%d,%d",
494 an_array[0],
495 an_array[1],
496 an_array[2],
497 an_array[3]);
498
499 PG_RETURN_CSTRING(result);
500}
501
502PG_FUNCTION_INFO_V1(make_tuple_indirect);
503Datum
504make_tuple_indirect(PG_FUNCTION_ARGS)
505{
506 HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0);
507 HeapTupleData tuple;
508 int ncolumns;
509 Datum *values;
510 bool *nulls;
511
512 Oid tupType;
513 int32 tupTypmod;
514 TupleDesc tupdesc;
515
516 HeapTuple newtup;
517
518 int i;
519
520 MemoryContext old_context;
521
522 /* Extract type info from the tuple itself */
523 tupType = HeapTupleHeaderGetTypeId(rec);
524 tupTypmod = HeapTupleHeaderGetTypMod(rec);
525 tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
526 ncolumns = tupdesc->natts;
527
528 /* Build a temporary HeapTuple control structure */
529 tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
530 ItemPointerSetInvalid(&(tuple.t_self));
531 tuple.t_tableOid = InvalidOid;
532 tuple.t_data = rec;
533
534 values = (Datum *) palloc(ncolumns * sizeof(Datum));
535 nulls = (bool *) palloc(ncolumns * sizeof(bool));
536
537 heap_deform_tuple(&tuple, tupdesc, values, nulls);
538
539 old_context = MemoryContextSwitchTo(TopTransactionContext);
540
541 for (i = 0; i < ncolumns; i++)
542 {
543 struct varlena *attr;
544 struct varlena *new_attr;
545 struct varatt_indirect redirect_pointer;
546
547 /* only work on existing, not-null varlenas */
548 if (TupleDescAttr(tupdesc, i)->attisdropped ||
549 nulls[i] ||
550 TupleDescAttr(tupdesc, i)->attlen != -1)
551 continue;
552
553 attr = (struct varlena *) DatumGetPointer(values[i]);
554
555 /* don't recursively indirect */
556 if (VARATT_IS_EXTERNAL_INDIRECT(attr))
557 continue;
558
559 /* copy datum, so it still lives later */
560 if (VARATT_IS_EXTERNAL_ONDISK(attr))
561 attr = heap_tuple_fetch_attr(attr);
562 else
563 {
564 struct varlena *oldattr = attr;
565
566 attr = palloc0(VARSIZE_ANY(oldattr));
567 memcpy(attr, oldattr, VARSIZE_ANY(oldattr));
568 }
569
570 /* build indirection Datum */
571 new_attr = (struct varlena *) palloc0(INDIRECT_POINTER_SIZE);
572 redirect_pointer.pointer = attr;
573 SET_VARTAG_EXTERNAL(new_attr, VARTAG_INDIRECT);
574 memcpy(VARDATA_EXTERNAL(new_attr), &redirect_pointer,
575 sizeof(redirect_pointer));
576
577 values[i] = PointerGetDatum(new_attr);
578 }
579
580 newtup = heap_form_tuple(tupdesc, values, nulls);
581 pfree(values);
582 pfree(nulls);
583 ReleaseTupleDesc(tupdesc);
584
585 MemoryContextSwitchTo(old_context);
586
587 /*
588 * We intentionally don't use PG_RETURN_HEAPTUPLEHEADER here, because that
589 * would cause the indirect toast pointers to be flattened out of the
590 * tuple immediately, rendering subsequent testing irrelevant. So just
591 * return the HeapTupleHeader pointer as-is. This violates the general
592 * rule that composite Datums shouldn't contain toast pointers, but so
593 * long as the regression test scripts don't insert the result of this
594 * function into a container type (record, array, etc) it should be OK.
595 */
596 PG_RETURN_POINTER(newtup->t_data);
597}
598
599PG_FUNCTION_INFO_V1(regress_putenv);
600
601Datum
602regress_putenv(PG_FUNCTION_ARGS)
603{
604 MemoryContext oldcontext;
605 char *envbuf;
606
607 if (!superuser())
608 elog(ERROR, "must be superuser to change environment variables");
609
610 oldcontext = MemoryContextSwitchTo(TopMemoryContext);
611 envbuf = text_to_cstring((text *) PG_GETARG_POINTER(0));
612 MemoryContextSwitchTo(oldcontext);
613
614 if (putenv(envbuf) != 0)
615 elog(ERROR, "could not set environment variable: %m");
616
617 PG_RETURN_VOID();
618}
619
620/* Sleep until no process has a given PID. */
621PG_FUNCTION_INFO_V1(wait_pid);
622
623Datum
624wait_pid(PG_FUNCTION_ARGS)
625{
626 int pid = PG_GETARG_INT32(0);
627
628 if (!superuser())
629 elog(ERROR, "must be superuser to check PID liveness");
630
631 while (kill(pid, 0) == 0)
632 {
633 CHECK_FOR_INTERRUPTS();
634 pg_usleep(50000);
635 }
636
637 if (errno != ESRCH)
638 elog(ERROR, "could not check PID %d liveness: %m", pid);
639
640 PG_RETURN_VOID();
641}
642
643static void
644test_atomic_flag(void)
645{
646 pg_atomic_flag flag;
647
648 pg_atomic_init_flag(&flag);
649
650 if (!pg_atomic_unlocked_test_flag(&flag))
651 elog(ERROR, "flag: unexpectedly set");
652
653 if (!pg_atomic_test_set_flag(&flag))
654 elog(ERROR, "flag: couldn't set");
655
656 if (pg_atomic_unlocked_test_flag(&flag))
657 elog(ERROR, "flag: unexpectedly unset");
658
659 if (pg_atomic_test_set_flag(&flag))
660 elog(ERROR, "flag: set spuriously #2");
661
662 pg_atomic_clear_flag(&flag);
663
664 if (!pg_atomic_unlocked_test_flag(&flag))
665 elog(ERROR, "flag: unexpectedly set #2");
666
667 if (!pg_atomic_test_set_flag(&flag))
668 elog(ERROR, "flag: couldn't set");
669
670 pg_atomic_clear_flag(&flag);
671}
672
673static void
674test_atomic_uint32(void)
675{
676 pg_atomic_uint32 var;
677 uint32 expected;
678 int i;
679
680 pg_atomic_init_u32(&var, 0);
681
682 if (pg_atomic_read_u32(&var) != 0)
683 elog(ERROR, "atomic_read_u32() #1 wrong");
684
685 pg_atomic_write_u32(&var, 3);
686
687 if (pg_atomic_read_u32(&var) != 3)
688 elog(ERROR, "atomic_read_u32() #2 wrong");
689
690 if (pg_atomic_fetch_add_u32(&var, pg_atomic_read_u32(&var) - 2) != 3)
691 elog(ERROR, "atomic_fetch_add_u32() #1 wrong");
692
693 if (pg_atomic_fetch_sub_u32(&var, 1) != 4)
694 elog(ERROR, "atomic_fetch_sub_u32() #1 wrong");
695
696 if (pg_atomic_sub_fetch_u32(&var, 3) != 0)
697 elog(ERROR, "atomic_sub_fetch_u32() #1 wrong");
698
699 if (pg_atomic_add_fetch_u32(&var, 10) != 10)
700 elog(ERROR, "atomic_add_fetch_u32() #1 wrong");
701
702 if (pg_atomic_exchange_u32(&var, 5) != 10)
703 elog(ERROR, "pg_atomic_exchange_u32() #1 wrong");
704
705 if (pg_atomic_exchange_u32(&var, 0) != 5)
706 elog(ERROR, "pg_atomic_exchange_u32() #0 wrong");
707
708 /* test around numerical limits */
709 if (pg_atomic_fetch_add_u32(&var, INT_MAX) != 0)
710 elog(ERROR, "pg_atomic_fetch_add_u32() #2 wrong");
711
712 if (pg_atomic_fetch_add_u32(&var, INT_MAX) != INT_MAX)
713 elog(ERROR, "pg_atomic_add_fetch_u32() #3 wrong");
714
715 pg_atomic_fetch_add_u32(&var, 2); /* wrap to 0 */
716
717 if (pg_atomic_fetch_add_u32(&var, PG_INT16_MAX) != 0)
718 elog(ERROR, "pg_atomic_fetch_add_u32() #3 wrong");
719
720 if (pg_atomic_fetch_add_u32(&var, PG_INT16_MAX + 1) != PG_INT16_MAX)
721 elog(ERROR, "pg_atomic_fetch_add_u32() #4 wrong");
722
723 if (pg_atomic_fetch_add_u32(&var, PG_INT16_MIN) != 2 * PG_INT16_MAX + 1)
724 elog(ERROR, "pg_atomic_fetch_add_u32() #5 wrong");
725
726 if (pg_atomic_fetch_add_u32(&var, PG_INT16_MIN - 1) != PG_INT16_MAX)
727 elog(ERROR, "pg_atomic_fetch_add_u32() #6 wrong");
728
729 pg_atomic_fetch_add_u32(&var, 1); /* top up to UINT_MAX */
730
731 if (pg_atomic_read_u32(&var) != UINT_MAX)
732 elog(ERROR, "atomic_read_u32() #2 wrong");
733
734 if (pg_atomic_fetch_sub_u32(&var, INT_MAX) != UINT_MAX)
735 elog(ERROR, "pg_atomic_fetch_sub_u32() #2 wrong");
736
737 if (pg_atomic_read_u32(&var) != (uint32) INT_MAX + 1)
738 elog(ERROR, "atomic_read_u32() #3 wrong: %u", pg_atomic_read_u32(&var));
739
740 expected = pg_atomic_sub_fetch_u32(&var, INT_MAX);
741 if (expected != 1)
742 elog(ERROR, "pg_atomic_sub_fetch_u32() #3 wrong: %u", expected);
743
744 pg_atomic_sub_fetch_u32(&var, 1);
745
746 /* fail exchange because of old expected */
747 expected = 10;
748 if (pg_atomic_compare_exchange_u32(&var, &expected, 1))
749 elog(ERROR, "atomic_compare_exchange_u32() changed value spuriously");
750
751 /* CAS is allowed to fail due to interrupts, try a couple of times */
752 for (i = 0; i < 1000; i++)
753 {
754 expected = 0;
755 if (!pg_atomic_compare_exchange_u32(&var, &expected, 1))
756 break;
757 }
758 if (i == 1000)
759 elog(ERROR, "atomic_compare_exchange_u32() never succeeded");
760 if (pg_atomic_read_u32(&var) != 1)
761 elog(ERROR, "atomic_compare_exchange_u32() didn't set value properly");
762
763 pg_atomic_write_u32(&var, 0);
764
765 /* try setting flagbits */
766 if (pg_atomic_fetch_or_u32(&var, 1) & 1)
767 elog(ERROR, "pg_atomic_fetch_or_u32() #1 wrong");
768
769 if (!(pg_atomic_fetch_or_u32(&var, 2) & 1))
770 elog(ERROR, "pg_atomic_fetch_or_u32() #2 wrong");
771
772 if (pg_atomic_read_u32(&var) != 3)
773 elog(ERROR, "invalid result after pg_atomic_fetch_or_u32()");
774
775 /* try clearing flagbits */
776 if ((pg_atomic_fetch_and_u32(&var, ~2) & 3) != 3)
777 elog(ERROR, "pg_atomic_fetch_and_u32() #1 wrong");
778
779 if (pg_atomic_fetch_and_u32(&var, ~1) != 1)
780 elog(ERROR, "pg_atomic_fetch_and_u32() #2 wrong: is %u",
781 pg_atomic_read_u32(&var));
782 /* no bits set anymore */
783 if (pg_atomic_fetch_and_u32(&var, ~0) != 0)
784 elog(ERROR, "pg_atomic_fetch_and_u32() #3 wrong");
785}
786
787static void
788test_atomic_uint64(void)
789{
790 pg_atomic_uint64 var;
791 uint64 expected;
792 int i;
793
794 pg_atomic_init_u64(&var, 0);
795
796 if (pg_atomic_read_u64(&var) != 0)
797 elog(ERROR, "atomic_read_u64() #1 wrong");
798
799 pg_atomic_write_u64(&var, 3);
800
801 if (pg_atomic_read_u64(&var) != 3)
802 elog(ERROR, "atomic_read_u64() #2 wrong");
803
804 if (pg_atomic_fetch_add_u64(&var, pg_atomic_read_u64(&var) - 2) != 3)
805 elog(ERROR, "atomic_fetch_add_u64() #1 wrong");
806
807 if (pg_atomic_fetch_sub_u64(&var, 1) != 4)
808 elog(ERROR, "atomic_fetch_sub_u64() #1 wrong");
809
810 if (pg_atomic_sub_fetch_u64(&var, 3) != 0)
811 elog(ERROR, "atomic_sub_fetch_u64() #1 wrong");
812
813 if (pg_atomic_add_fetch_u64(&var, 10) != 10)
814 elog(ERROR, "atomic_add_fetch_u64() #1 wrong");
815
816 if (pg_atomic_exchange_u64(&var, 5) != 10)
817 elog(ERROR, "pg_atomic_exchange_u64() #1 wrong");
818
819 if (pg_atomic_exchange_u64(&var, 0) != 5)
820 elog(ERROR, "pg_atomic_exchange_u64() #0 wrong");
821
822 /* fail exchange because of old expected */
823 expected = 10;
824 if (pg_atomic_compare_exchange_u64(&var, &expected, 1))
825 elog(ERROR, "atomic_compare_exchange_u64() changed value spuriously");
826
827 /* CAS is allowed to fail due to interrupts, try a couple of times */
828 for (i = 0; i < 100; i++)
829 {
830 expected = 0;
831 if (!pg_atomic_compare_exchange_u64(&var, &expected, 1))
832 break;
833 }
834 if (i == 100)
835 elog(ERROR, "atomic_compare_exchange_u64() never succeeded");
836 if (pg_atomic_read_u64(&var) != 1)
837 elog(ERROR, "atomic_compare_exchange_u64() didn't set value properly");
838
839 pg_atomic_write_u64(&var, 0);
840
841 /* try setting flagbits */
842 if (pg_atomic_fetch_or_u64(&var, 1) & 1)
843 elog(ERROR, "pg_atomic_fetch_or_u64() #1 wrong");
844
845 if (!(pg_atomic_fetch_or_u64(&var, 2) & 1))
846 elog(ERROR, "pg_atomic_fetch_or_u64() #2 wrong");
847
848 if (pg_atomic_read_u64(&var) != 3)
849 elog(ERROR, "invalid result after pg_atomic_fetch_or_u64()");
850
851 /* try clearing flagbits */
852 if ((pg_atomic_fetch_and_u64(&var, ~2) & 3) != 3)
853 elog(ERROR, "pg_atomic_fetch_and_u64() #1 wrong");
854
855 if (pg_atomic_fetch_and_u64(&var, ~1) != 1)
856 elog(ERROR, "pg_atomic_fetch_and_u64() #2 wrong: is " UINT64_FORMAT,
857 pg_atomic_read_u64(&var));
858 /* no bits set anymore */
859 if (pg_atomic_fetch_and_u64(&var, ~0) != 0)
860 elog(ERROR, "pg_atomic_fetch_and_u64() #3 wrong");
861}
862
863
864PG_FUNCTION_INFO_V1(test_atomic_ops);
865Datum
866test_atomic_ops(PG_FUNCTION_ARGS)
867{
868 test_atomic_flag();
869
870 test_atomic_uint32();
871
872 test_atomic_uint64();
873
874 PG_RETURN_BOOL(true);
875}
876
877PG_FUNCTION_INFO_V1(test_fdw_handler);
878Datum
879test_fdw_handler(PG_FUNCTION_ARGS)
880{
881 elog(ERROR, "test_fdw_handler is not implemented");
882 PG_RETURN_NULL();
883}
884
885PG_FUNCTION_INFO_V1(test_support_func);
886Datum
887test_support_func(PG_FUNCTION_ARGS)
888{
889 Node *rawreq = (Node *) PG_GETARG_POINTER(0);
890 Node *ret = NULL;
891
892 if (IsA(rawreq, SupportRequestSelectivity))
893 {
894 /*
895 * Assume that the target is int4eq; that's safe as long as we don't
896 * attach this to any other boolean-returning function.
897 */
898 SupportRequestSelectivity *req = (SupportRequestSelectivity *) rawreq;
899 Selectivity s1;
900
901 if (req->is_join)
902 s1 = join_selectivity(req->root, Int4EqualOperator,
903 req->args,
904 req->inputcollid,
905 req->jointype,
906 req->sjinfo);
907 else
908 s1 = restriction_selectivity(req->root, Int4EqualOperator,
909 req->args,
910 req->inputcollid,
911 req->varRelid);
912
913 req->selectivity = s1;
914 ret = (Node *) req;
915 }
916
917 if (IsA(rawreq, SupportRequestCost))
918 {
919 /* Provide some generic estimate */
920 SupportRequestCost *req = (SupportRequestCost *) rawreq;
921
922 req->startup = 0;
923 req->per_tuple = 2 * cpu_operator_cost;
924 ret = (Node *) req;
925 }
926
927 if (IsA(rawreq, SupportRequestRows))
928 {
929 /*
930 * Assume that the target is generate_series_int4; that's safe as long
931 * as we don't attach this to any other set-returning function.
932 */
933 SupportRequestRows *req = (SupportRequestRows *) rawreq;
934
935 if (req->node && IsA(req->node, FuncExpr)) /* be paranoid */
936 {
937 List *args = ((FuncExpr *) req->node)->args;
938 Node *arg1 = linitial(args);
939 Node *arg2 = lsecond(args);
940
941 if (IsA(arg1, Const) &&
942 !((Const *) arg1)->constisnull &&
943 IsA(arg2, Const) &&
944 !((Const *) arg2)->constisnull)
945 {
946 int32 val1 = DatumGetInt32(((Const *) arg1)->constvalue);
947 int32 val2 = DatumGetInt32(((Const *) arg2)->constvalue);
948
949 req->rows = val2 - val1 + 1;
950 ret = (Node *) req;
951 }
952 }
953 }
954
955 PG_RETURN_POINTER(ret);
956}
957