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