1 /*------------------------------------------------------------------------
4 * Code for various C-language functions defined as part of the
7 * This code is released under the terms of the PostgreSQL License.
9 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
10 * Portions Copyright (c) 1994, Regents of the University of California
12 * src/test/regress/regress.c
14 *-------------------------------------------------------------------------
22 #include "access/detoast.h"
23 #include "access/htup_details.h"
24 #include "access/transam.h"
25 #include "access/xact.h"
26 #include "catalog/namespace.h"
27 #include "catalog/pg_operator.h"
28 #include "catalog/pg_type.h"
29 #include "commands/sequence.h"
30 #include "commands/trigger.h"
31 #include "executor/executor.h"
32 #include "executor/spi.h"
34 #include "mb/pg_wchar.h"
35 #include "miscadmin.h"
36 #include "nodes/supportnodes.h"
37 #include "optimizer/optimizer.h"
38 #include "optimizer/plancat.h"
39 #include "parser/parse_coerce.h"
40 #include "port/atomics.h"
41 #include "storage/spin.h"
42 #include "utils/array.h"
43 #include "utils/builtins.h"
44 #include "utils/geo_decls.h"
45 #include "utils/memutils.h"
46 #include "utils/rel.h"
47 #include "utils/typcache.h"
49 #define EXPECT_TRUE(expr) \
53 "%s was unexpectedly false in file \"%s\" line %u", \
54 #expr, __FILE__, __LINE__); \
57 #define EXPECT_EQ_U32(result_expr, expected_expr) \
59 uint32 actual_result = (result_expr); \
60 uint32 expected_result = (expected_expr); \
61 if (actual_result != expected_result) \
63 "%s yielded %u, expected %s in file \"%s\" line %u", \
64 #result_expr, actual_result, #expected_expr, __FILE__, __LINE__); \
67 #define EXPECT_EQ_U64(result_expr, expected_expr) \
69 uint64 actual_result = (result_expr); \
70 uint64 expected_result = (expected_expr); \
71 if (actual_result != expected_result) \
73 "%s yielded " UINT64_FORMAT ", expected %s in file \"%s\" line %u", \
74 #result_expr, actual_result, #expected_expr, __FILE__, __LINE__); \
81 static void regress_lseg_construct(LSEG
*lseg
, Point
*pt1
, Point
*pt2
);
86 /* return the point where two paths intersect, or NULL if no intersection. */
87 PG_FUNCTION_INFO_V1(interpt_pp
);
90 interpt_pp(PG_FUNCTION_ARGS
)
92 PATH
*p1
= PG_GETARG_PATH_P(0);
93 PATH
*p2
= PG_GETARG_PATH_P(1);
98 bool found
; /* We've found the intersection */
100 found
= false; /* Haven't found it yet */
102 for (i
= 0; i
< p1
->npts
- 1 && !found
; i
++)
104 regress_lseg_construct(&seg1
, &p1
->p
[i
], &p1
->p
[i
+ 1]);
105 for (j
= 0; j
< p2
->npts
- 1 && !found
; j
++)
107 regress_lseg_construct(&seg2
, &p2
->p
[j
], &p2
->p
[j
+ 1]);
108 if (DatumGetBool(DirectFunctionCall2(lseg_intersect
,
109 LsegPGetDatum(&seg1
),
110 LsegPGetDatum(&seg2
))))
119 * Note: DirectFunctionCall2 will kick out an error if lseg_interpt()
120 * returns NULL, but that should be impossible since we know the two
121 * segments intersect.
123 PG_RETURN_DATUM(DirectFunctionCall2(lseg_interpt
,
124 LsegPGetDatum(&seg1
),
125 LsegPGetDatum(&seg2
)));
129 /* like lseg_construct, but assume space already allocated */
131 regress_lseg_construct(LSEG
*lseg
, Point
*pt1
, Point
*pt2
)
133 lseg
->p
[0].x
= pt1
->x
;
134 lseg
->p
[0].y
= pt1
->y
;
135 lseg
->p
[1].x
= pt2
->x
;
136 lseg
->p
[1].y
= pt2
->y
;
139 PG_FUNCTION_INFO_V1(overpaid
);
142 overpaid(PG_FUNCTION_ARGS
)
144 HeapTupleHeader tuple
= PG_GETARG_HEAPTUPLEHEADER(0);
148 salary
= DatumGetInt32(GetAttributeByName(tuple
, "salary", &isnull
));
151 PG_RETURN_BOOL(salary
> 699);
155 * This used to be "circle", but I added circle to builtins,
156 * so needed to make sure the names do not collide. - tgl 97/04/21
165 PG_FUNCTION_INFO_V1(widget_in
);
166 PG_FUNCTION_INFO_V1(widget_out
);
171 widget_in(PG_FUNCTION_ARGS
)
173 char *str
= PG_GETARG_CSTRING(0);
179 for (i
= 0, p
= str
; *p
&& i
< NARGS
&& *p
!= RDELIM
; p
++)
181 if (*p
== DELIM
|| (*p
== LDELIM
&& i
== 0))
186 * Note: DON'T convert this error to "soft" style (errsave/ereturn). We
187 * want this data type to stay permanently in the hard-error world so that
188 * it can be used for testing that such cases still work reasonably.
192 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
193 errmsg("invalid input syntax for type %s: \"%s\"",
196 result
= (WIDGET
*) palloc(sizeof(WIDGET
));
197 result
->center
.x
= atof(coord
[0]);
198 result
->center
.y
= atof(coord
[1]);
199 result
->radius
= atof(coord
[2]);
201 PG_RETURN_POINTER(result
);
205 widget_out(PG_FUNCTION_ARGS
)
207 WIDGET
*widget
= (WIDGET
*) PG_GETARG_POINTER(0);
208 char *str
= psprintf("(%g,%g,%g)",
209 widget
->center
.x
, widget
->center
.y
, widget
->radius
);
211 PG_RETURN_CSTRING(str
);
214 PG_FUNCTION_INFO_V1(pt_in_widget
);
217 pt_in_widget(PG_FUNCTION_ARGS
)
219 Point
*point
= PG_GETARG_POINT_P(0);
220 WIDGET
*widget
= (WIDGET
*) PG_GETARG_POINTER(1);
223 distance
= DatumGetFloat8(DirectFunctionCall2(point_distance
,
224 PointPGetDatum(point
),
225 PointPGetDatum(&widget
->center
)));
227 PG_RETURN_BOOL(distance
< widget
->radius
);
230 PG_FUNCTION_INFO_V1(reverse_name
);
233 reverse_name(PG_FUNCTION_ARGS
)
235 char *string
= PG_GETARG_CSTRING(0);
240 new_string
= palloc0(NAMEDATALEN
);
241 for (i
= 0; i
< NAMEDATALEN
&& string
[i
]; ++i
)
243 if (i
== NAMEDATALEN
|| !string
[i
])
247 new_string
[len
- i
] = string
[i
];
248 PG_RETURN_CSTRING(new_string
);
251 PG_FUNCTION_INFO_V1(trigger_return_old
);
254 trigger_return_old(PG_FUNCTION_ARGS
)
256 TriggerData
*trigdata
= (TriggerData
*) fcinfo
->context
;
259 if (!CALLED_AS_TRIGGER(fcinfo
))
260 elog(ERROR
, "trigger_return_old: not fired by trigger manager");
262 tuple
= trigdata
->tg_trigtuple
;
264 return PointerGetDatum(tuple
);
267 #define TTDUMMY_INFINITY 999999
269 static SPIPlanPtr splan
= NULL
;
270 static bool ttoff
= false;
272 PG_FUNCTION_INFO_V1(ttdummy
);
275 ttdummy(PG_FUNCTION_ARGS
)
277 TriggerData
*trigdata
= (TriggerData
*) fcinfo
->context
;
278 Trigger
*trigger
; /* to get trigger name */
279 char **args
; /* arguments */
280 int attnum
[2]; /* fnumbers of start/stop columns */
285 Datum
*cvals
; /* column values */
286 char *cnulls
; /* column nulls */
287 char *relname
; /* triggered relation name */
288 Relation rel
; /* triggered relation */
290 HeapTuple newtuple
= NULL
;
292 TupleDesc tupdesc
; /* tuple description */
293 int natts
; /* # of attributes */
294 bool isnull
; /* to know is some column NULL or not */
298 if (!CALLED_AS_TRIGGER(fcinfo
))
299 elog(ERROR
, "ttdummy: not fired by trigger manager");
300 if (!TRIGGER_FIRED_FOR_ROW(trigdata
->tg_event
))
301 elog(ERROR
, "ttdummy: must be fired for row");
302 if (!TRIGGER_FIRED_BEFORE(trigdata
->tg_event
))
303 elog(ERROR
, "ttdummy: must be fired before event");
304 if (TRIGGER_FIRED_BY_INSERT(trigdata
->tg_event
))
305 elog(ERROR
, "ttdummy: cannot process INSERT event");
306 if (TRIGGER_FIRED_BY_UPDATE(trigdata
->tg_event
))
307 newtuple
= trigdata
->tg_newtuple
;
309 trigtuple
= trigdata
->tg_trigtuple
;
311 rel
= trigdata
->tg_relation
;
312 relname
= SPI_getrelname(rel
);
314 /* check if TT is OFF for this relation */
315 if (ttoff
) /* OFF - nothing to do */
318 return PointerGetDatum((newtuple
!= NULL
) ? newtuple
: trigtuple
);
321 trigger
= trigdata
->tg_trigger
;
323 if (trigger
->tgnargs
!= 2)
324 elog(ERROR
, "ttdummy (%s): invalid (!= 2) number of arguments %d",
325 relname
, trigger
->tgnargs
);
327 args
= trigger
->tgargs
;
328 tupdesc
= rel
->rd_att
;
329 natts
= tupdesc
->natts
;
331 for (i
= 0; i
< 2; i
++)
333 attnum
[i
] = SPI_fnumber(tupdesc
, args
[i
]);
335 elog(ERROR
, "ttdummy (%s): there is no attribute %s",
337 if (SPI_gettypeid(tupdesc
, attnum
[i
]) != INT4OID
)
338 elog(ERROR
, "ttdummy (%s): attribute %s must be of integer type",
342 oldon
= SPI_getbinval(trigtuple
, tupdesc
, attnum
[0], &isnull
);
344 elog(ERROR
, "ttdummy (%s): %s must be NOT NULL", relname
, args
[0]);
346 oldoff
= SPI_getbinval(trigtuple
, tupdesc
, attnum
[1], &isnull
);
348 elog(ERROR
, "ttdummy (%s): %s must be NOT NULL", relname
, args
[1]);
350 if (newtuple
!= NULL
) /* UPDATE */
352 newon
= SPI_getbinval(newtuple
, tupdesc
, attnum
[0], &isnull
);
354 elog(ERROR
, "ttdummy (%s): %s must be NOT NULL", relname
, args
[0]);
355 newoff
= SPI_getbinval(newtuple
, tupdesc
, attnum
[1], &isnull
);
357 elog(ERROR
, "ttdummy (%s): %s must be NOT NULL", relname
, args
[1]);
359 if (oldon
!= newon
|| oldoff
!= newoff
)
361 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
362 errmsg("ttdummy (%s): you cannot change %s and/or %s columns (use set_ttdummy)",
363 relname
, args
[0], args
[1])));
365 if (newoff
!= TTDUMMY_INFINITY
)
367 pfree(relname
); /* allocated in upper executor context */
368 return PointerGetDatum(NULL
);
371 else if (oldoff
!= TTDUMMY_INFINITY
) /* DELETE */
374 return PointerGetDatum(NULL
);
377 newoff
= DirectFunctionCall1(nextval
, CStringGetTextDatum("ttdummy_seq"));
378 /* nextval now returns int64; coerce down to int32 */
379 newoff
= Int32GetDatum((int32
) DatumGetInt64(newoff
));
381 /* Connect to SPI manager */
384 /* Fetch tuple values and nulls */
385 cvals
= (Datum
*) palloc(natts
* sizeof(Datum
));
386 cnulls
= (char *) palloc(natts
* sizeof(char));
387 for (i
= 0; i
< natts
; i
++)
389 cvals
[i
] = SPI_getbinval((newtuple
!= NULL
) ? newtuple
: trigtuple
,
390 tupdesc
, i
+ 1, &isnull
);
391 cnulls
[i
] = (isnull
) ? 'n' : ' ';
394 /* change date column(s) */
395 if (newtuple
) /* UPDATE */
397 cvals
[attnum
[0] - 1] = newoff
; /* start_date eq current date */
398 cnulls
[attnum
[0] - 1] = ' ';
399 cvals
[attnum
[1] - 1] = TTDUMMY_INFINITY
; /* stop_date eq INFINITY */
400 cnulls
[attnum
[1] - 1] = ' ';
405 cvals
[attnum
[1] - 1] = newoff
; /* stop_date eq current date */
406 cnulls
[attnum
[1] - 1] = ' ';
409 /* if there is no plan ... */
416 /* allocate space in preparation */
417 ctypes
= (Oid
*) palloc(natts
* sizeof(Oid
));
418 query
= (char *) palloc(100 + 16 * natts
);
421 * Construct query: INSERT INTO _relation_ VALUES ($1, ...)
423 sprintf(query
, "INSERT INTO %s VALUES (", relname
);
424 for (i
= 1; i
<= natts
; i
++)
426 sprintf(query
+ strlen(query
), "$%d%s",
427 i
, (i
< natts
) ? ", " : ")");
428 ctypes
[i
- 1] = SPI_gettypeid(tupdesc
, i
);
431 /* Prepare plan for query */
432 pplan
= SPI_prepare(query
, natts
, ctypes
);
434 elog(ERROR
, "ttdummy (%s): SPI_prepare returned %s", relname
, SPI_result_code_string(SPI_result
));
436 if (SPI_keepplan(pplan
))
437 elog(ERROR
, "ttdummy (%s): SPI_keepplan failed", relname
);
442 ret
= SPI_execp(splan
, cvals
, cnulls
, 0);
445 elog(ERROR
, "ttdummy (%s): SPI_execp returned %d", relname
, ret
);
447 /* Tuple to return to upper Executor ... */
448 if (newtuple
) /* UPDATE */
449 rettuple
= SPI_modifytuple(rel
, trigtuple
, 1, &(attnum
[1]), &newoff
, NULL
);
451 rettuple
= trigtuple
;
453 SPI_finish(); /* don't forget say Bye to SPI mgr */
457 return PointerGetDatum(rettuple
);
460 PG_FUNCTION_INFO_V1(set_ttdummy
);
463 set_ttdummy(PG_FUNCTION_ARGS
)
465 int32 on
= PG_GETARG_INT32(0);
467 if (ttoff
) /* OFF currently */
489 * Type int44 has no real-world use, but the regression tests use it
490 * (under the alias "city_budget"). It's a four-element vector of int4's.
494 * int44in - converts "num, num, ..." to internal form
496 * Note: Fills any missing positions with zeroes.
498 PG_FUNCTION_INFO_V1(int44in
);
501 int44in(PG_FUNCTION_ARGS
)
503 char *input_string
= PG_GETARG_CSTRING(0);
504 int32
*result
= (int32
*) palloc(4 * sizeof(int32
));
507 i
= sscanf(input_string
,
516 PG_RETURN_POINTER(result
);
520 * int44out - converts internal form to "num, num, ..."
522 PG_FUNCTION_INFO_V1(int44out
);
525 int44out(PG_FUNCTION_ARGS
)
527 int32
*an_array
= (int32
*) PG_GETARG_POINTER(0);
528 char *result
= (char *) palloc(16 * 4);
530 snprintf(result
, 16 * 4, "%d,%d,%d,%d",
536 PG_RETURN_CSTRING(result
);
539 PG_FUNCTION_INFO_V1(test_canonicalize_path
);
541 test_canonicalize_path(PG_FUNCTION_ARGS
)
543 char *path
= text_to_cstring(PG_GETARG_TEXT_PP(0));
545 canonicalize_path(path
);
546 PG_RETURN_TEXT_P(cstring_to_text(path
));
549 PG_FUNCTION_INFO_V1(make_tuple_indirect
);
551 make_tuple_indirect(PG_FUNCTION_ARGS
)
553 HeapTupleHeader rec
= PG_GETARG_HEAPTUPLEHEADER(0);
567 MemoryContext old_context
;
569 /* Extract type info from the tuple itself */
570 tupType
= HeapTupleHeaderGetTypeId(rec
);
571 tupTypmod
= HeapTupleHeaderGetTypMod(rec
);
572 tupdesc
= lookup_rowtype_tupdesc(tupType
, tupTypmod
);
573 ncolumns
= tupdesc
->natts
;
575 /* Build a temporary HeapTuple control structure */
576 tuple
.t_len
= HeapTupleHeaderGetDatumLength(rec
);
577 ItemPointerSetInvalid(&(tuple
.t_self
));
578 tuple
.t_tableOid
= InvalidOid
;
581 values
= (Datum
*) palloc(ncolumns
* sizeof(Datum
));
582 nulls
= (bool *) palloc(ncolumns
* sizeof(bool));
584 heap_deform_tuple(&tuple
, tupdesc
, values
, nulls
);
586 old_context
= MemoryContextSwitchTo(TopTransactionContext
);
588 for (i
= 0; i
< ncolumns
; i
++)
590 struct varlena
*attr
;
591 struct varlena
*new_attr
;
592 struct varatt_indirect redirect_pointer
;
594 /* only work on existing, not-null varlenas */
595 if (TupleDescAttr(tupdesc
, i
)->attisdropped
||
597 TupleDescAttr(tupdesc
, i
)->attlen
!= -1 ||
598 TupleDescAttr(tupdesc
, i
)->attstorage
== TYPSTORAGE_PLAIN
)
601 attr
= (struct varlena
*) DatumGetPointer(values
[i
]);
603 /* don't recursively indirect */
604 if (VARATT_IS_EXTERNAL_INDIRECT(attr
))
607 /* copy datum, so it still lives later */
608 if (VARATT_IS_EXTERNAL_ONDISK(attr
))
609 attr
= detoast_external_attr(attr
);
612 struct varlena
*oldattr
= attr
;
614 attr
= palloc0(VARSIZE_ANY(oldattr
));
615 memcpy(attr
, oldattr
, VARSIZE_ANY(oldattr
));
618 /* build indirection Datum */
619 new_attr
= (struct varlena
*) palloc0(INDIRECT_POINTER_SIZE
);
620 redirect_pointer
.pointer
= attr
;
621 SET_VARTAG_EXTERNAL(new_attr
, VARTAG_INDIRECT
);
622 memcpy(VARDATA_EXTERNAL(new_attr
), &redirect_pointer
,
623 sizeof(redirect_pointer
));
625 values
[i
] = PointerGetDatum(new_attr
);
628 newtup
= heap_form_tuple(tupdesc
, values
, nulls
);
631 ReleaseTupleDesc(tupdesc
);
633 MemoryContextSwitchTo(old_context
);
636 * We intentionally don't use PG_RETURN_HEAPTUPLEHEADER here, because that
637 * would cause the indirect toast pointers to be flattened out of the
638 * tuple immediately, rendering subsequent testing irrelevant. So just
639 * return the HeapTupleHeader pointer as-is. This violates the general
640 * rule that composite Datums shouldn't contain toast pointers, but so
641 * long as the regression test scripts don't insert the result of this
642 * function into a container type (record, array, etc) it should be OK.
644 PG_RETURN_POINTER(newtup
->t_data
);
647 PG_FUNCTION_INFO_V1(regress_setenv
);
650 regress_setenv(PG_FUNCTION_ARGS
)
652 char *envvar
= text_to_cstring(PG_GETARG_TEXT_PP(0));
653 char *envval
= text_to_cstring(PG_GETARG_TEXT_PP(1));
656 elog(ERROR
, "must be superuser to change environment variables");
658 if (setenv(envvar
, envval
, 1) != 0)
659 elog(ERROR
, "could not set environment variable: %m");
664 /* Sleep until no process has a given PID. */
665 PG_FUNCTION_INFO_V1(wait_pid
);
668 wait_pid(PG_FUNCTION_ARGS
)
670 int pid
= PG_GETARG_INT32(0);
673 elog(ERROR
, "must be superuser to check PID liveness");
675 while (kill(pid
, 0) == 0)
677 CHECK_FOR_INTERRUPTS();
682 elog(ERROR
, "could not check PID %d liveness: %m", pid
);
688 test_atomic_flag(void)
692 pg_atomic_init_flag(&flag
);
693 EXPECT_TRUE(pg_atomic_unlocked_test_flag(&flag
));
694 EXPECT_TRUE(pg_atomic_test_set_flag(&flag
));
695 EXPECT_TRUE(!pg_atomic_unlocked_test_flag(&flag
));
696 EXPECT_TRUE(!pg_atomic_test_set_flag(&flag
));
697 pg_atomic_clear_flag(&flag
);
698 EXPECT_TRUE(pg_atomic_unlocked_test_flag(&flag
));
699 EXPECT_TRUE(pg_atomic_test_set_flag(&flag
));
700 pg_atomic_clear_flag(&flag
);
704 test_atomic_uint32(void)
706 pg_atomic_uint32 var
;
710 pg_atomic_init_u32(&var
, 0);
711 EXPECT_EQ_U32(pg_atomic_read_u32(&var
), 0);
712 pg_atomic_write_u32(&var
, 3);
713 EXPECT_EQ_U32(pg_atomic_read_u32(&var
), 3);
714 EXPECT_EQ_U32(pg_atomic_fetch_add_u32(&var
, pg_atomic_read_u32(&var
) - 2),
716 EXPECT_EQ_U32(pg_atomic_fetch_sub_u32(&var
, 1), 4);
717 EXPECT_EQ_U32(pg_atomic_sub_fetch_u32(&var
, 3), 0);
718 EXPECT_EQ_U32(pg_atomic_add_fetch_u32(&var
, 10), 10);
719 EXPECT_EQ_U32(pg_atomic_exchange_u32(&var
, 5), 10);
720 EXPECT_EQ_U32(pg_atomic_exchange_u32(&var
, 0), 5);
722 /* test around numerical limits */
723 EXPECT_EQ_U32(pg_atomic_fetch_add_u32(&var
, INT_MAX
), 0);
724 EXPECT_EQ_U32(pg_atomic_fetch_add_u32(&var
, INT_MAX
), INT_MAX
);
725 pg_atomic_fetch_add_u32(&var
, 2); /* wrap to 0 */
726 EXPECT_EQ_U32(pg_atomic_fetch_add_u32(&var
, PG_INT16_MAX
), 0);
727 EXPECT_EQ_U32(pg_atomic_fetch_add_u32(&var
, PG_INT16_MAX
+ 1),
729 EXPECT_EQ_U32(pg_atomic_fetch_add_u32(&var
, PG_INT16_MIN
),
730 2 * PG_INT16_MAX
+ 1);
731 EXPECT_EQ_U32(pg_atomic_fetch_add_u32(&var
, PG_INT16_MIN
- 1),
733 pg_atomic_fetch_add_u32(&var
, 1); /* top up to UINT_MAX */
734 EXPECT_EQ_U32(pg_atomic_read_u32(&var
), UINT_MAX
);
735 EXPECT_EQ_U32(pg_atomic_fetch_sub_u32(&var
, INT_MAX
), UINT_MAX
);
736 EXPECT_EQ_U32(pg_atomic_read_u32(&var
), (uint32
) INT_MAX
+ 1);
737 EXPECT_EQ_U32(pg_atomic_sub_fetch_u32(&var
, INT_MAX
), 1);
738 pg_atomic_sub_fetch_u32(&var
, 1);
739 expected
= PG_INT16_MAX
;
740 EXPECT_TRUE(!pg_atomic_compare_exchange_u32(&var
, &expected
, 1));
741 expected
= PG_INT16_MAX
+ 1;
742 EXPECT_TRUE(!pg_atomic_compare_exchange_u32(&var
, &expected
, 1));
743 expected
= PG_INT16_MIN
;
744 EXPECT_TRUE(!pg_atomic_compare_exchange_u32(&var
, &expected
, 1));
745 expected
= PG_INT16_MIN
- 1;
746 EXPECT_TRUE(!pg_atomic_compare_exchange_u32(&var
, &expected
, 1));
748 /* fail exchange because of old expected */
750 EXPECT_TRUE(!pg_atomic_compare_exchange_u32(&var
, &expected
, 1));
752 /* CAS is allowed to fail due to interrupts, try a couple of times */
753 for (i
= 0; i
< 1000; i
++)
756 if (!pg_atomic_compare_exchange_u32(&var
, &expected
, 1))
760 elog(ERROR
, "atomic_compare_exchange_u32() never succeeded");
761 EXPECT_EQ_U32(pg_atomic_read_u32(&var
), 1);
762 pg_atomic_write_u32(&var
, 0);
764 /* try setting flagbits */
765 EXPECT_TRUE(!(pg_atomic_fetch_or_u32(&var
, 1) & 1));
766 EXPECT_TRUE(pg_atomic_fetch_or_u32(&var
, 2) & 1);
767 EXPECT_EQ_U32(pg_atomic_read_u32(&var
), 3);
768 /* try clearing flagbits */
769 EXPECT_EQ_U32(pg_atomic_fetch_and_u32(&var
, ~2) & 3, 3);
770 EXPECT_EQ_U32(pg_atomic_fetch_and_u32(&var
, ~1), 1);
771 /* no bits set anymore */
772 EXPECT_EQ_U32(pg_atomic_fetch_and_u32(&var
, ~0), 0);
776 test_atomic_uint64(void)
778 pg_atomic_uint64 var
;
782 pg_atomic_init_u64(&var
, 0);
783 EXPECT_EQ_U64(pg_atomic_read_u64(&var
), 0);
784 pg_atomic_write_u64(&var
, 3);
785 EXPECT_EQ_U64(pg_atomic_read_u64(&var
), 3);
786 EXPECT_EQ_U64(pg_atomic_fetch_add_u64(&var
, pg_atomic_read_u64(&var
) - 2),
788 EXPECT_EQ_U64(pg_atomic_fetch_sub_u64(&var
, 1), 4);
789 EXPECT_EQ_U64(pg_atomic_sub_fetch_u64(&var
, 3), 0);
790 EXPECT_EQ_U64(pg_atomic_add_fetch_u64(&var
, 10), 10);
791 EXPECT_EQ_U64(pg_atomic_exchange_u64(&var
, 5), 10);
792 EXPECT_EQ_U64(pg_atomic_exchange_u64(&var
, 0), 5);
794 /* fail exchange because of old expected */
796 EXPECT_TRUE(!pg_atomic_compare_exchange_u64(&var
, &expected
, 1));
798 /* CAS is allowed to fail due to interrupts, try a couple of times */
799 for (i
= 0; i
< 100; i
++)
802 if (!pg_atomic_compare_exchange_u64(&var
, &expected
, 1))
806 elog(ERROR
, "atomic_compare_exchange_u64() never succeeded");
807 EXPECT_EQ_U64(pg_atomic_read_u64(&var
), 1);
809 pg_atomic_write_u64(&var
, 0);
811 /* try setting flagbits */
812 EXPECT_TRUE(!(pg_atomic_fetch_or_u64(&var
, 1) & 1));
813 EXPECT_TRUE(pg_atomic_fetch_or_u64(&var
, 2) & 1);
814 EXPECT_EQ_U64(pg_atomic_read_u64(&var
), 3);
815 /* try clearing flagbits */
816 EXPECT_EQ_U64((pg_atomic_fetch_and_u64(&var
, ~2) & 3), 3);
817 EXPECT_EQ_U64(pg_atomic_fetch_and_u64(&var
, ~1), 1);
818 /* no bits set anymore */
819 EXPECT_EQ_U64(pg_atomic_fetch_and_u64(&var
, ~0), 0);
823 * Perform, fairly minimal, testing of the spinlock implementation.
825 * It's likely worth expanding these to actually test concurrency etc, but
826 * having some regularly run tests is better than none.
832 * Basic tests for spinlocks, as well as the underlying operations.
834 * We embed the spinlock in a struct with other members to test that the
835 * spinlock operations don't perform too wide writes.
838 struct test_lock_struct
845 memcpy(struct_w_lock
.data_before
, "abcd", 4);
846 memcpy(struct_w_lock
.data_after
, "ef12", 4);
848 /* test basic operations via the SpinLock* API */
849 SpinLockInit(&struct_w_lock
.lock
);
850 SpinLockAcquire(&struct_w_lock
.lock
);
851 SpinLockRelease(&struct_w_lock
.lock
);
853 /* test basic operations via underlying S_* API */
854 S_INIT_LOCK(&struct_w_lock
.lock
);
855 S_LOCK(&struct_w_lock
.lock
);
856 S_UNLOCK(&struct_w_lock
.lock
);
858 /* and that "contended" acquisition works */
859 s_lock(&struct_w_lock
.lock
, "testfile", 17, "testfunc");
860 S_UNLOCK(&struct_w_lock
.lock
);
863 * Check, using TAS directly, that a single spin cycle doesn't block
864 * when acquiring an already acquired lock.
867 S_LOCK(&struct_w_lock
.lock
);
869 if (!TAS(&struct_w_lock
.lock
))
870 elog(ERROR
, "acquired already held spinlock");
873 if (!TAS_SPIN(&struct_w_lock
.lock
))
874 elog(ERROR
, "acquired already held spinlock");
875 #endif /* defined(TAS_SPIN) */
877 S_UNLOCK(&struct_w_lock
.lock
);
878 #endif /* defined(TAS) */
881 * Verify that after all of this the non-lock contents are still
884 if (memcmp(struct_w_lock
.data_before
, "abcd", 4) != 0)
885 elog(ERROR
, "padding before spinlock modified");
886 if (memcmp(struct_w_lock
.data_after
, "ef12", 4) != 0)
887 elog(ERROR
, "padding after spinlock modified");
891 PG_FUNCTION_INFO_V1(test_atomic_ops
);
893 test_atomic_ops(PG_FUNCTION_ARGS
)
897 test_atomic_uint32();
899 test_atomic_uint64();
902 * Arguably this shouldn't be tested as part of this function, but it's
903 * closely enough related that that seems ok for now.
907 PG_RETURN_BOOL(true);
910 PG_FUNCTION_INFO_V1(test_fdw_handler
);
912 test_fdw_handler(PG_FUNCTION_ARGS
)
914 elog(ERROR
, "test_fdw_handler is not implemented");
918 PG_FUNCTION_INFO_V1(test_support_func
);
920 test_support_func(PG_FUNCTION_ARGS
)
922 Node
*rawreq
= (Node
*) PG_GETARG_POINTER(0);
925 if (IsA(rawreq
, SupportRequestSelectivity
))
928 * Assume that the target is int4eq; that's safe as long as we don't
929 * attach this to any other boolean-returning function.
931 SupportRequestSelectivity
*req
= (SupportRequestSelectivity
*) rawreq
;
935 s1
= join_selectivity(req
->root
, Int4EqualOperator
,
941 s1
= restriction_selectivity(req
->root
, Int4EqualOperator
,
946 req
->selectivity
= s1
;
950 if (IsA(rawreq
, SupportRequestCost
))
952 /* Provide some generic estimate */
953 SupportRequestCost
*req
= (SupportRequestCost
*) rawreq
;
956 req
->per_tuple
= 2 * cpu_operator_cost
;
960 if (IsA(rawreq
, SupportRequestRows
))
963 * Assume that the target is generate_series_int4; that's safe as long
964 * as we don't attach this to any other set-returning function.
966 SupportRequestRows
*req
= (SupportRequestRows
*) rawreq
;
968 if (req
->node
&& IsA(req
->node
, FuncExpr
)) /* be paranoid */
970 List
*args
= ((FuncExpr
*) req
->node
)->args
;
971 Node
*arg1
= linitial(args
);
972 Node
*arg2
= lsecond(args
);
974 if (IsA(arg1
, Const
) &&
975 !((Const
*) arg1
)->constisnull
&&
977 !((Const
*) arg2
)->constisnull
)
979 int32 val1
= DatumGetInt32(((Const
*) arg1
)->constvalue
);
980 int32 val2
= DatumGetInt32(((Const
*) arg2
)->constvalue
);
982 req
->rows
= val2
- val1
+ 1;
988 PG_RETURN_POINTER(ret
);
991 PG_FUNCTION_INFO_V1(test_opclass_options_func
);
993 test_opclass_options_func(PG_FUNCTION_ARGS
)
999 * Call an encoding conversion or verification function.
1002 * string bytea -- string to convert
1003 * src_enc name -- source encoding
1004 * dest_enc name -- destination encoding
1005 * noError bool -- if set, don't ereport() on invalid or untranslatable
1008 * Result is a tuple with two attributes:
1009 * int4 -- number of input bytes successfully converted
1010 * bytea -- converted string
1012 PG_FUNCTION_INFO_V1(test_enc_conversion
);
1014 test_enc_conversion(PG_FUNCTION_ARGS
)
1016 bytea
*string
= PG_GETARG_BYTEA_PP(0);
1017 char *src_encoding_name
= NameStr(*PG_GETARG_NAME(1));
1018 int src_encoding
= pg_char_to_encoding(src_encoding_name
);
1019 char *dest_encoding_name
= NameStr(*PG_GETARG_NAME(2));
1020 int dest_encoding
= pg_char_to_encoding(dest_encoding_name
);
1021 bool noError
= PG_GETARG_BOOL(3);
1032 bool nulls
[2] = {0};
1035 if (src_encoding
< 0)
1037 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
1038 errmsg("invalid source encoding name \"%s\"",
1039 src_encoding_name
)));
1040 if (dest_encoding
< 0)
1042 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
1043 errmsg("invalid destination encoding name \"%s\"",
1044 dest_encoding_name
)));
1046 /* Build a tuple descriptor for our result type */
1047 if (get_call_result_type(fcinfo
, NULL
, &tupdesc
) != TYPEFUNC_COMPOSITE
)
1048 elog(ERROR
, "return type must be a row type");
1049 tupdesc
= BlessTupleDesc(tupdesc
);
1051 srclen
= VARSIZE_ANY_EXHDR(string
);
1052 src
= VARDATA_ANY(string
);
1054 if (src_encoding
== dest_encoding
)
1056 /* just check that the source string is valid */
1059 oklen
= pg_encoding_verifymbstr(src_encoding
, src
, srclen
);
1061 if (oklen
== srclen
)
1063 convertedbytes
= oklen
;
1068 report_invalid_encoding(src_encoding
, src
+ oklen
, srclen
- oklen
);
1073 * build bytea data type structure.
1075 Assert(oklen
< srclen
);
1076 convertedbytes
= oklen
;
1077 retval
= (bytea
*) palloc(oklen
+ VARHDRSZ
);
1078 SET_VARSIZE(retval
, oklen
+ VARHDRSZ
);
1079 memcpy(VARDATA(retval
), src
, oklen
);
1084 proc
= FindDefaultConversionProc(src_encoding
, dest_encoding
);
1085 if (!OidIsValid(proc
))
1087 (errcode(ERRCODE_UNDEFINED_FUNCTION
),
1088 errmsg("default conversion function for encoding \"%s\" to \"%s\" does not exist",
1089 pg_encoding_to_char(src_encoding
),
1090 pg_encoding_to_char(dest_encoding
))));
1092 if (srclen
>= (MaxAllocSize
/ (Size
) MAX_CONVERSION_GROWTH
))
1094 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
1095 errmsg("out of memory"),
1096 errdetail("String of %d bytes is too long for encoding conversion.",
1099 dstsize
= (Size
) srclen
* MAX_CONVERSION_GROWTH
+ 1;
1100 dst
= MemoryContextAlloc(CurrentMemoryContext
, dstsize
);
1102 /* perform conversion */
1103 convertedbytes
= pg_do_encoding_conversion_buf(proc
,
1106 (unsigned char *) src
, srclen
,
1107 (unsigned char *) dst
, dstsize
,
1109 dstlen
= strlen(dst
);
1112 * build bytea data type structure.
1114 retval
= (bytea
*) palloc(dstlen
+ VARHDRSZ
);
1115 SET_VARSIZE(retval
, dstlen
+ VARHDRSZ
);
1116 memcpy(VARDATA(retval
), dst
, dstlen
);
1121 values
[0] = Int32GetDatum(convertedbytes
);
1122 values
[1] = PointerGetDatum(retval
);
1123 tuple
= heap_form_tuple(tupdesc
, values
, nulls
);
1125 PG_RETURN_DATUM(HeapTupleGetDatum(tuple
));
1128 /* Provide SQL access to IsBinaryCoercible() */
1129 PG_FUNCTION_INFO_V1(binary_coercible
);
1131 binary_coercible(PG_FUNCTION_ARGS
)
1133 Oid srctype
= PG_GETARG_OID(0);
1134 Oid targettype
= PG_GETARG_OID(1);
1136 PG_RETURN_BOOL(IsBinaryCoercible(srctype
, targettype
));