Header And Logo

PostgreSQL
| The world's most advanced open source database.

regress.c

Go to the documentation of this file.
00001 /*
00002  * src/test/regress/regress.c
00003  */
00004 
00005 #include "postgres.h"
00006 
00007 #include <float.h>
00008 #include <math.h>
00009 
00010 #include "access/transam.h"
00011 #include "access/xact.h"
00012 #include "catalog/pg_type.h"
00013 #include "commands/sequence.h"
00014 #include "commands/trigger.h"
00015 #include "executor/executor.h"
00016 #include "executor/spi.h"
00017 #include "utils/builtins.h"
00018 #include "utils/geo_decls.h"
00019 #include "utils/rel.h"
00020 
00021 
00022 #define P_MAXDIG 12
00023 #define LDELIM          '('
00024 #define RDELIM          ')'
00025 #define DELIM           ','
00026 
00027 extern Datum regress_dist_ptpath(PG_FUNCTION_ARGS);
00028 extern Datum regress_path_dist(PG_FUNCTION_ARGS);
00029 extern PATH *poly2path(POLYGON *poly);
00030 extern Datum interpt_pp(PG_FUNCTION_ARGS);
00031 extern void regress_lseg_construct(LSEG *lseg, Point *pt1, Point *pt2);
00032 extern Datum overpaid(PG_FUNCTION_ARGS);
00033 extern Datum boxarea(PG_FUNCTION_ARGS);
00034 extern char *reverse_name(char *string);
00035 extern int  oldstyle_length(int n, text *t);
00036 extern Datum int44in(PG_FUNCTION_ARGS);
00037 extern Datum int44out(PG_FUNCTION_ARGS);
00038 
00039 #ifdef PG_MODULE_MAGIC
00040 PG_MODULE_MAGIC;
00041 #endif
00042 
00043 
00044 /*
00045  * Distance from a point to a path
00046  */
00047 PG_FUNCTION_INFO_V1(regress_dist_ptpath);
00048 
00049 Datum
00050 regress_dist_ptpath(PG_FUNCTION_ARGS)
00051 {
00052     Point      *pt = PG_GETARG_POINT_P(0);
00053     PATH       *path = PG_GETARG_PATH_P(1);
00054     float8      result = 0.0;   /* keep compiler quiet */
00055     float8      tmp;
00056     int         i;
00057     LSEG        lseg;
00058 
00059     switch (path->npts)
00060     {
00061         case 0:
00062             PG_RETURN_NULL();
00063         case 1:
00064             result = point_dt(pt, &path->p[0]);
00065             break;
00066         default:
00067 
00068             /*
00069              * the distance from a point to a path is the smallest distance
00070              * from the point to any of its constituent segments.
00071              */
00072             Assert(path->npts > 1);
00073             for (i = 0; i < path->npts - 1; ++i)
00074             {
00075                 regress_lseg_construct(&lseg, &path->p[i], &path->p[i + 1]);
00076                 tmp = DatumGetFloat8(DirectFunctionCall2(dist_ps,
00077                                                          PointPGetDatum(pt),
00078                                                       LsegPGetDatum(&lseg)));
00079                 if (i == 0 || tmp < result)
00080                     result = tmp;
00081             }
00082             break;
00083     }
00084     PG_RETURN_FLOAT8(result);
00085 }
00086 
00087 /*
00088  * this essentially does a cartesian product of the lsegs in the
00089  * two paths, and finds the min distance between any two lsegs
00090  */
00091 PG_FUNCTION_INFO_V1(regress_path_dist);
00092 
00093 Datum
00094 regress_path_dist(PG_FUNCTION_ARGS)
00095 {
00096     PATH       *p1 = PG_GETARG_PATH_P(0);
00097     PATH       *p2 = PG_GETARG_PATH_P(1);
00098     bool        have_min = false;
00099     float8      min = 0.0;      /* initialize to keep compiler quiet */
00100     float8      tmp;
00101     int         i,
00102                 j;
00103     LSEG        seg1,
00104                 seg2;
00105 
00106     for (i = 0; i < p1->npts - 1; i++)
00107     {
00108         for (j = 0; j < p2->npts - 1; j++)
00109         {
00110             regress_lseg_construct(&seg1, &p1->p[i], &p1->p[i + 1]);
00111             regress_lseg_construct(&seg2, &p2->p[j], &p2->p[j + 1]);
00112 
00113             tmp = DatumGetFloat8(DirectFunctionCall2(lseg_distance,
00114                                                      LsegPGetDatum(&seg1),
00115                                                      LsegPGetDatum(&seg2)));
00116             if (!have_min || tmp < min)
00117             {
00118                 min = tmp;
00119                 have_min = true;
00120             }
00121         }
00122     }
00123 
00124     if (!have_min)
00125         PG_RETURN_NULL();
00126 
00127     PG_RETURN_FLOAT8(min);
00128 }
00129 
00130 PATH *
00131 poly2path(POLYGON *poly)
00132 {
00133     int         i;
00134     char       *output = (char *) palloc(2 * (P_MAXDIG + 1) * poly->npts + 64);
00135     char        buf[2 * (P_MAXDIG) + 20];
00136 
00137     sprintf(output, "(1, %*d", P_MAXDIG, poly->npts);
00138 
00139     for (i = 0; i < poly->npts; i++)
00140     {
00141         snprintf(buf, sizeof(buf), ",%*g,%*g",
00142                  P_MAXDIG, poly->p[i].x, P_MAXDIG, poly->p[i].y);
00143         strcat(output, buf);
00144     }
00145 
00146     snprintf(buf, sizeof(buf), "%c", RDELIM);
00147     strcat(output, buf);
00148     return DatumGetPathP(DirectFunctionCall1(path_in,
00149                                              CStringGetDatum(output)));
00150 }
00151 
00152 /* return the point where two paths intersect, or NULL if no intersection. */
00153 PG_FUNCTION_INFO_V1(interpt_pp);
00154 
00155 Datum
00156 interpt_pp(PG_FUNCTION_ARGS)
00157 {
00158     PATH       *p1 = PG_GETARG_PATH_P(0);
00159     PATH       *p2 = PG_GETARG_PATH_P(1);
00160     int         i,
00161                 j;
00162     LSEG        seg1,
00163                 seg2;
00164     bool        found;          /* We've found the intersection */
00165 
00166     found = false;              /* Haven't found it yet */
00167 
00168     for (i = 0; i < p1->npts - 1 && !found; i++)
00169     {
00170         regress_lseg_construct(&seg1, &p1->p[i], &p1->p[i + 1]);
00171         for (j = 0; j < p2->npts - 1 && !found; j++)
00172         {
00173             regress_lseg_construct(&seg2, &p2->p[j], &p2->p[j + 1]);
00174             if (DatumGetBool(DirectFunctionCall2(lseg_intersect,
00175                                                  LsegPGetDatum(&seg1),
00176                                                  LsegPGetDatum(&seg2))))
00177                 found = true;
00178         }
00179     }
00180 
00181     if (!found)
00182         PG_RETURN_NULL();
00183 
00184     /*
00185      * Note: DirectFunctionCall2 will kick out an error if lseg_interpt()
00186      * returns NULL, but that should be impossible since we know the two
00187      * segments intersect.
00188      */
00189     PG_RETURN_DATUM(DirectFunctionCall2(lseg_interpt,
00190                                         LsegPGetDatum(&seg1),
00191                                         LsegPGetDatum(&seg2)));
00192 }
00193 
00194 
00195 /* like lseg_construct, but assume space already allocated */
00196 void
00197 regress_lseg_construct(LSEG *lseg, Point *pt1, Point *pt2)
00198 {
00199     lseg->p[0].x = pt1->x;
00200     lseg->p[0].y = pt1->y;
00201     lseg->p[1].x = pt2->x;
00202     lseg->p[1].y = pt2->y;
00203     lseg->m = point_sl(pt1, pt2);
00204 }
00205 
00206 PG_FUNCTION_INFO_V1(overpaid);
00207 
00208 Datum
00209 overpaid(PG_FUNCTION_ARGS)
00210 {
00211     HeapTupleHeader tuple = PG_GETARG_HEAPTUPLEHEADER(0);
00212     bool        isnull;
00213     int32       salary;
00214 
00215     salary = DatumGetInt32(GetAttributeByName(tuple, "salary", &isnull));
00216     if (isnull)
00217         PG_RETURN_NULL();
00218     PG_RETURN_BOOL(salary > 699);
00219 }
00220 
00221 /* New type "widget"
00222  * This used to be "circle", but I added circle to builtins,
00223  *  so needed to make sure the names do not collide. - tgl 97/04/21
00224  */
00225 
00226 typedef struct
00227 {
00228     Point       center;
00229     double      radius;
00230 }   WIDGET;
00231 
00232 WIDGET     *widget_in(char *str);
00233 char       *widget_out(WIDGET * widget);
00234 extern Datum pt_in_widget(PG_FUNCTION_ARGS);
00235 
00236 #define NARGS   3
00237 
00238 WIDGET *
00239 widget_in(char *str)
00240 {
00241     char       *p,
00242                *coord[NARGS],
00243                 buf2[1000];
00244     int         i;
00245     WIDGET     *result;
00246 
00247     if (str == NULL)
00248         return NULL;
00249     for (i = 0, p = str; *p && i < NARGS && *p != RDELIM; p++)
00250         if (*p == ',' || (*p == LDELIM && !i))
00251             coord[i++] = p + 1;
00252     if (i < NARGS - 1)
00253         return NULL;
00254     result = (WIDGET *) palloc(sizeof(WIDGET));
00255     result->center.x = atof(coord[0]);
00256     result->center.y = atof(coord[1]);
00257     result->radius = atof(coord[2]);
00258 
00259     snprintf(buf2, sizeof(buf2), "widget_in: read (%f, %f, %f)\n",
00260              result->center.x, result->center.y, result->radius);
00261     return result;
00262 }
00263 
00264 char *
00265 widget_out(WIDGET * widget)
00266 {
00267     char       *result;
00268 
00269     if (widget == NULL)
00270         return NULL;
00271 
00272     result = (char *) palloc(60);
00273     sprintf(result, "(%g,%g,%g)",
00274             widget->center.x, widget->center.y, widget->radius);
00275     return result;
00276 }
00277 
00278 PG_FUNCTION_INFO_V1(pt_in_widget);
00279 
00280 Datum
00281 pt_in_widget(PG_FUNCTION_ARGS)
00282 {
00283     Point      *point = PG_GETARG_POINT_P(0);
00284     WIDGET     *widget = (WIDGET *) PG_GETARG_POINTER(1);
00285 
00286     PG_RETURN_BOOL(point_dt(point, &widget->center) < widget->radius);
00287 }
00288 
00289 PG_FUNCTION_INFO_V1(boxarea);
00290 
00291 Datum
00292 boxarea(PG_FUNCTION_ARGS)
00293 {
00294     BOX        *box = PG_GETARG_BOX_P(0);
00295     double      width,
00296                 height;
00297 
00298     width = Abs(box->high.x - box->low.x);
00299     height = Abs(box->high.y - box->low.y);
00300     PG_RETURN_FLOAT8(width * height);
00301 }
00302 
00303 char *
00304 reverse_name(char *string)
00305 {
00306     int         i;
00307     int         len;
00308     char       *new_string;
00309 
00310     new_string = palloc0(NAMEDATALEN);
00311     for (i = 0; i < NAMEDATALEN && string[i]; ++i)
00312         ;
00313     if (i == NAMEDATALEN || !string[i])
00314         --i;
00315     len = i;
00316     for (; i >= 0; --i)
00317         new_string[len - i] = string[i];
00318     return new_string;
00319 }
00320 
00321 /*
00322  * This rather silly function is just to test that oldstyle functions
00323  * work correctly on toast-able inputs.
00324  */
00325 int
00326 oldstyle_length(int n, text *t)
00327 {
00328     int         len = 0;
00329 
00330     if (t)
00331         len = VARSIZE(t) - VARHDRSZ;
00332 
00333     return n + len;
00334 }
00335 
00336 
00337 static TransactionId fd17b_xid = InvalidTransactionId;
00338 static TransactionId fd17a_xid = InvalidTransactionId;
00339 static int  fd17b_level = 0;
00340 static int  fd17a_level = 0;
00341 static bool fd17b_recursion = true;
00342 static bool fd17a_recursion = true;
00343 extern Datum funny_dup17(PG_FUNCTION_ARGS);
00344 
00345 PG_FUNCTION_INFO_V1(funny_dup17);
00346 
00347 Datum
00348 funny_dup17(PG_FUNCTION_ARGS)
00349 {
00350     TriggerData *trigdata = (TriggerData *) fcinfo->context;
00351     TransactionId *xid;
00352     int        *level;
00353     bool       *recursion;
00354     Relation    rel;
00355     TupleDesc   tupdesc;
00356     HeapTuple   tuple;
00357     char       *query,
00358                *fieldval,
00359                *fieldtype;
00360     char       *when;
00361     int         inserted;
00362     int         selected = 0;
00363     int         ret;
00364 
00365     if (!CALLED_AS_TRIGGER(fcinfo))
00366         elog(ERROR, "funny_dup17: not fired by trigger manager");
00367 
00368     tuple = trigdata->tg_trigtuple;
00369     rel = trigdata->tg_relation;
00370     tupdesc = rel->rd_att;
00371     if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
00372     {
00373         xid = &fd17b_xid;
00374         level = &fd17b_level;
00375         recursion = &fd17b_recursion;
00376         when = "BEFORE";
00377     }
00378     else
00379     {
00380         xid = &fd17a_xid;
00381         level = &fd17a_level;
00382         recursion = &fd17a_recursion;
00383         when = "AFTER ";
00384     }
00385 
00386     if (!TransactionIdIsCurrentTransactionId(*xid))
00387     {
00388         *xid = GetCurrentTransactionId();
00389         *level = 0;
00390         *recursion = true;
00391     }
00392 
00393     if (*level == 17)
00394     {
00395         *recursion = false;
00396         return PointerGetDatum(tuple);
00397     }
00398 
00399     if (!(*recursion))
00400         return PointerGetDatum(tuple);
00401 
00402     (*level)++;
00403 
00404     SPI_connect();
00405 
00406     fieldval = SPI_getvalue(tuple, tupdesc, 1);
00407     fieldtype = SPI_gettype(tupdesc, 1);
00408 
00409     query = (char *) palloc(100 + NAMEDATALEN * 3 +
00410                             strlen(fieldval) + strlen(fieldtype));
00411 
00412     sprintf(query, "insert into %s select * from %s where %s = '%s'::%s",
00413             SPI_getrelname(rel), SPI_getrelname(rel),
00414             SPI_fname(tupdesc, 1),
00415             fieldval, fieldtype);
00416 
00417     if ((ret = SPI_exec(query, 0)) < 0)
00418         elog(ERROR, "funny_dup17 (fired %s) on level %3d: SPI_exec (insert ...) returned %d",
00419              when, *level, ret);
00420 
00421     inserted = SPI_processed;
00422 
00423     sprintf(query, "select count (*) from %s where %s = '%s'::%s",
00424             SPI_getrelname(rel),
00425             SPI_fname(tupdesc, 1),
00426             fieldval, fieldtype);
00427 
00428     if ((ret = SPI_exec(query, 0)) < 0)
00429         elog(ERROR, "funny_dup17 (fired %s) on level %3d: SPI_exec (select ...) returned %d",
00430              when, *level, ret);
00431 
00432     if (SPI_processed > 0)
00433     {
00434         selected = DatumGetInt32(DirectFunctionCall1(int4in,
00435                                                 CStringGetDatum(SPI_getvalue(
00436                                                        SPI_tuptable->vals[0],
00437                                                        SPI_tuptable->tupdesc,
00438                                                                              1
00439                                                                         ))));
00440     }
00441 
00442     elog(DEBUG4, "funny_dup17 (fired %s) on level %3d: %d/%d tuples inserted/selected",
00443          when, *level, inserted, selected);
00444 
00445     SPI_finish();
00446 
00447     (*level)--;
00448 
00449     if (*level == 0)
00450         *xid = InvalidTransactionId;
00451 
00452     return PointerGetDatum(tuple);
00453 }
00454 
00455 extern Datum ttdummy(PG_FUNCTION_ARGS);
00456 extern Datum set_ttdummy(PG_FUNCTION_ARGS);
00457 
00458 #define TTDUMMY_INFINITY    999999
00459 
00460 static SPIPlanPtr splan = NULL;
00461 static bool ttoff = false;
00462 
00463 PG_FUNCTION_INFO_V1(ttdummy);
00464 
00465 Datum
00466 ttdummy(PG_FUNCTION_ARGS)
00467 {
00468     TriggerData *trigdata = (TriggerData *) fcinfo->context;
00469     Trigger    *trigger;        /* to get trigger name */
00470     char      **args;           /* arguments */
00471     int         attnum[2];      /* fnumbers of start/stop columns */
00472     Datum       oldon,
00473                 oldoff;
00474     Datum       newon,
00475                 newoff;
00476     Datum      *cvals;          /* column values */
00477     char       *cnulls;         /* column nulls */
00478     char       *relname;        /* triggered relation name */
00479     Relation    rel;            /* triggered relation */
00480     HeapTuple   trigtuple;
00481     HeapTuple   newtuple = NULL;
00482     HeapTuple   rettuple;
00483     TupleDesc   tupdesc;        /* tuple description */
00484     int         natts;          /* # of attributes */
00485     bool        isnull;         /* to know is some column NULL or not */
00486     int         ret;
00487     int         i;
00488 
00489     if (!CALLED_AS_TRIGGER(fcinfo))
00490         elog(ERROR, "ttdummy: not fired by trigger manager");
00491     if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
00492         elog(ERROR, "ttdummy: must be fired for row");
00493     if (!TRIGGER_FIRED_BEFORE(trigdata->tg_event))
00494         elog(ERROR, "ttdummy: must be fired before event");
00495     if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
00496         elog(ERROR, "ttdummy: cannot process INSERT event");
00497     if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
00498         newtuple = trigdata->tg_newtuple;
00499 
00500     trigtuple = trigdata->tg_trigtuple;
00501 
00502     rel = trigdata->tg_relation;
00503     relname = SPI_getrelname(rel);
00504 
00505     /* check if TT is OFF for this relation */
00506     if (ttoff)                  /* OFF - nothing to do */
00507     {
00508         pfree(relname);
00509         return PointerGetDatum((newtuple != NULL) ? newtuple : trigtuple);
00510     }
00511 
00512     trigger = trigdata->tg_trigger;
00513 
00514     if (trigger->tgnargs != 2)
00515         elog(ERROR, "ttdummy (%s): invalid (!= 2) number of arguments %d",
00516              relname, trigger->tgnargs);
00517 
00518     args = trigger->tgargs;
00519     tupdesc = rel->rd_att;
00520     natts = tupdesc->natts;
00521 
00522     for (i = 0; i < 2; i++)
00523     {
00524         attnum[i] = SPI_fnumber(tupdesc, args[i]);
00525         if (attnum[i] < 0)
00526             elog(ERROR, "ttdummy (%s): there is no attribute %s", relname, args[i]);
00527         if (SPI_gettypeid(tupdesc, attnum[i]) != INT4OID)
00528             elog(ERROR, "ttdummy (%s): attributes %s and %s must be of abstime type",
00529                  relname, args[0], args[1]);
00530     }
00531 
00532     oldon = SPI_getbinval(trigtuple, tupdesc, attnum[0], &isnull);
00533     if (isnull)
00534         elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[0]);
00535 
00536     oldoff = SPI_getbinval(trigtuple, tupdesc, attnum[1], &isnull);
00537     if (isnull)
00538         elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[1]);
00539 
00540     if (newtuple != NULL)       /* UPDATE */
00541     {
00542         newon = SPI_getbinval(newtuple, tupdesc, attnum[0], &isnull);
00543         if (isnull)
00544             elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[0]);
00545         newoff = SPI_getbinval(newtuple, tupdesc, attnum[1], &isnull);
00546         if (isnull)
00547             elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[1]);
00548 
00549         if (oldon != newon || oldoff != newoff)
00550             elog(ERROR, "ttdummy (%s): you cannot change %s and/or %s columns (use set_ttdummy)",
00551                  relname, args[0], args[1]);
00552 
00553         if (newoff != TTDUMMY_INFINITY)
00554         {
00555             pfree(relname);     /* allocated in upper executor context */
00556             return PointerGetDatum(NULL);
00557         }
00558     }
00559     else if (oldoff != TTDUMMY_INFINITY)        /* DELETE */
00560     {
00561         pfree(relname);
00562         return PointerGetDatum(NULL);
00563     }
00564 
00565     newoff = DirectFunctionCall1(nextval, CStringGetTextDatum("ttdummy_seq"));
00566     /* nextval now returns int64; coerce down to int32 */
00567     newoff = Int32GetDatum((int32) DatumGetInt64(newoff));
00568 
00569     /* Connect to SPI manager */
00570     if ((ret = SPI_connect()) < 0)
00571         elog(ERROR, "ttdummy (%s): SPI_connect returned %d", relname, ret);
00572 
00573     /* Fetch tuple values and nulls */
00574     cvals = (Datum *) palloc(natts * sizeof(Datum));
00575     cnulls = (char *) palloc(natts * sizeof(char));
00576     for (i = 0; i < natts; i++)
00577     {
00578         cvals[i] = SPI_getbinval((newtuple != NULL) ? newtuple : trigtuple,
00579                                  tupdesc, i + 1, &isnull);
00580         cnulls[i] = (isnull) ? 'n' : ' ';
00581     }
00582 
00583     /* change date column(s) */
00584     if (newtuple)               /* UPDATE */
00585     {
00586         cvals[attnum[0] - 1] = newoff;  /* start_date eq current date */
00587         cnulls[attnum[0] - 1] = ' ';
00588         cvals[attnum[1] - 1] = TTDUMMY_INFINITY;        /* stop_date eq INFINITY */
00589         cnulls[attnum[1] - 1] = ' ';
00590     }
00591     else
00592         /* DELETE */
00593     {
00594         cvals[attnum[1] - 1] = newoff;  /* stop_date eq current date */
00595         cnulls[attnum[1] - 1] = ' ';
00596     }
00597 
00598     /* if there is no plan ... */
00599     if (splan == NULL)
00600     {
00601         SPIPlanPtr  pplan;
00602         Oid        *ctypes;
00603         char       *query;
00604 
00605         /* allocate space in preparation */
00606         ctypes = (Oid *) palloc(natts * sizeof(Oid));
00607         query = (char *) palloc(100 + 16 * natts);
00608 
00609         /*
00610          * Construct query: INSERT INTO _relation_ VALUES ($1, ...)
00611          */
00612         sprintf(query, "INSERT INTO %s VALUES (", relname);
00613         for (i = 1; i <= natts; i++)
00614         {
00615             sprintf(query + strlen(query), "$%d%s",
00616                     i, (i < natts) ? ", " : ")");
00617             ctypes[i - 1] = SPI_gettypeid(tupdesc, i);
00618         }
00619 
00620         /* Prepare plan for query */
00621         pplan = SPI_prepare(query, natts, ctypes);
00622         if (pplan == NULL)
00623             elog(ERROR, "ttdummy (%s): SPI_prepare returned %d", relname, SPI_result);
00624 
00625         if (SPI_keepplan(pplan))
00626             elog(ERROR, "ttdummy (%s): SPI_keepplan failed", relname);
00627 
00628         splan = pplan;
00629     }
00630 
00631     ret = SPI_execp(splan, cvals, cnulls, 0);
00632 
00633     if (ret < 0)
00634         elog(ERROR, "ttdummy (%s): SPI_execp returned %d", relname, ret);
00635 
00636     /* Tuple to return to upper Executor ... */
00637     if (newtuple)               /* UPDATE */
00638     {
00639         HeapTuple   tmptuple;
00640 
00641         tmptuple = SPI_copytuple(trigtuple);
00642         rettuple = SPI_modifytuple(rel, tmptuple, 1, &(attnum[1]), &newoff, NULL);
00643         SPI_freetuple(tmptuple);
00644     }
00645     else
00646         /* DELETE */
00647         rettuple = trigtuple;
00648 
00649     SPI_finish();               /* don't forget say Bye to SPI mgr */
00650 
00651     pfree(relname);
00652 
00653     return PointerGetDatum(rettuple);
00654 }
00655 
00656 PG_FUNCTION_INFO_V1(set_ttdummy);
00657 
00658 Datum
00659 set_ttdummy(PG_FUNCTION_ARGS)
00660 {
00661     int32       on = PG_GETARG_INT32(0);
00662 
00663     if (ttoff)                  /* OFF currently */
00664     {
00665         if (on == 0)
00666             PG_RETURN_INT32(0);
00667 
00668         /* turn ON */
00669         ttoff = false;
00670         PG_RETURN_INT32(0);
00671     }
00672 
00673     /* ON currently */
00674     if (on != 0)
00675         PG_RETURN_INT32(1);
00676 
00677     /* turn OFF */
00678     ttoff = true;
00679 
00680     PG_RETURN_INT32(1);
00681 }
00682 
00683 
00684 /*
00685  * Type int44 has no real-world use, but the regression tests use it.
00686  * It's a four-element vector of int4's.
00687  */
00688 
00689 /*
00690  *      int44in         - converts "num num ..." to internal form
00691  *
00692  *      Note: Fills any missing positions with zeroes.
00693  */
00694 PG_FUNCTION_INFO_V1(int44in);
00695 
00696 Datum
00697 int44in(PG_FUNCTION_ARGS)
00698 {
00699     char       *input_string = PG_GETARG_CSTRING(0);
00700     int32      *result = (int32 *) palloc(4 * sizeof(int32));
00701     int         i;
00702 
00703     i = sscanf(input_string,
00704                "%d, %d, %d, %d",
00705                &result[0],
00706                &result[1],
00707                &result[2],
00708                &result[3]);
00709     while (i < 4)
00710         result[i++] = 0;
00711 
00712     PG_RETURN_POINTER(result);
00713 }
00714 
00715 /*
00716  *      int44out        - converts internal form to "num num ..."
00717  */
00718 PG_FUNCTION_INFO_V1(int44out);
00719 
00720 Datum
00721 int44out(PG_FUNCTION_ARGS)
00722 {
00723     int32      *an_array = (int32 *) PG_GETARG_POINTER(0);
00724     char       *result = (char *) palloc(16 * 4);       /* Allow 14 digits +
00725                                                          * sign */
00726     int         i;
00727     char       *walk;
00728 
00729     walk = result;
00730     for (i = 0; i < 4; i++)
00731     {
00732         pg_ltoa(an_array[i], walk);
00733         while (*++walk != '\0')
00734             ;
00735         *walk++ = ' ';
00736     }
00737     *--walk = '\0';
00738     PG_RETURN_CSTRING(result);
00739 }