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