LCOV - code coverage report
Current view: top level - src/test/regress - regress.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 246 467 52.7 %
Date: 2017-09-29 15:12:54 Functions: 26 42 61.9 %
Legend: Lines: hit not hit

          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             : }

Generated by: LCOV version 1.11