]> granicus.if.org Git - postgresql/blob - src/test/regress/regress.c
Convert all remaining geometric operators to new fmgr style. This
[postgresql] / src / test / regress / regress.c
1 /*
2  * $Header: /cvsroot/pgsql/src/test/regress/regress.c,v 1.43 2000/07/30 20:43:54 tgl Exp $
3  */
4
5 #include <float.h>                              /* faked on sunos */
6
7 #include "postgres.h"
8
9 #include "utils/geo_decls.h"    /* includes <math.h> */
10 #include "executor/executor.h"  /* For GetAttributeByName */
11 #include "commands/sequence.h"  /* for nextval() */
12
13 #define P_MAXDIG 12
14 #define LDELIM                  '('
15 #define RDELIM                  ')'
16 #define DELIM                   ','
17
18 typedef TupleTableSlot *TUPLE;
19
20 extern Datum regress_dist_ptpath(PG_FUNCTION_ARGS);
21 extern Datum regress_path_dist(PG_FUNCTION_ARGS);
22 extern PATH *poly2path(POLYGON *poly);
23 extern Datum interpt_pp(PG_FUNCTION_ARGS);
24 extern void regress_lseg_construct(LSEG *lseg, Point *pt1, Point *pt2);
25 extern Datum overpaid(PG_FUNCTION_ARGS);
26 extern Datum boxarea(PG_FUNCTION_ARGS);
27 extern char *reverse_name(char *string);
28
29 /*
30 ** Distance from a point to a path
31 */
32 Datum
33 regress_dist_ptpath(PG_FUNCTION_ARGS)
34 {
35         Point      *pt = PG_GETARG_POINT_P(0);
36         PATH       *path = PG_GETARG_PATH_P(1);
37         float8          result = 0.0;   /* keep compiler quiet */
38         float8          tmp;
39         int                     i;
40         LSEG            lseg;
41
42         switch (path->npts)
43         {
44                 case 0:
45                         PG_RETURN_NULL();
46                 case 1:
47                         result = point_dt(pt, &path->p[0]);
48                         break;
49                 default:
50
51                         /*
52                          * the distance from a point to a path is the smallest
53                          * distance from the point to any of its constituent segments.
54                          */
55                         Assert(path->npts > 1);
56                         for (i = 0; i < path->npts - 1; ++i)
57                         {
58                                 regress_lseg_construct(&lseg, &path->p[i], &path->p[i + 1]);
59                                 tmp = DatumGetFloat8(DirectFunctionCall2(dist_ps,
60                                                                                                         PointPGetDatum(pt),
61                                                                                                         LsegPGetDatum(&lseg)));
62                                 if (i == 0 || tmp < result)
63                                         result = tmp;
64                         }
65                         break;
66         }
67         PG_RETURN_FLOAT8(result);
68 }
69
70 /* this essentially does a cartesian product of the lsegs in the
71    two paths, and finds the min distance between any two lsegs */
72 Datum
73 regress_path_dist(PG_FUNCTION_ARGS)
74 {
75         PATH       *p1 = PG_GETARG_PATH_P(0);
76         PATH       *p2 = PG_GETARG_PATH_P(1);
77         bool            have_min = false;
78         float8          min = 0.0;              /* initialize to keep compiler quiet */
79         float8          tmp;
80         int                     i,
81                                 j;
82         LSEG            seg1,
83                                 seg2;
84
85         for (i = 0; i < p1->npts - 1; i++)
86         {
87                 for (j = 0; j < p2->npts - 1; j++)
88                 {
89                         regress_lseg_construct(&seg1, &p1->p[i], &p1->p[i + 1]);
90                         regress_lseg_construct(&seg2, &p2->p[j], &p2->p[j + 1]);
91
92                         tmp = DatumGetFloat8(DirectFunctionCall2(lseg_distance,
93                                                                                                          LsegPGetDatum(&seg1),
94                                                                                                          LsegPGetDatum(&seg2)));
95                         if (!have_min || tmp < min)
96                         {
97                                 min = tmp;
98                                 have_min = true;
99                         }
100                 }
101         }
102
103         if (! have_min)
104                 PG_RETURN_NULL();
105
106         PG_RETURN_FLOAT8(min);
107 }
108
109 PATH *
110 poly2path(poly)
111 POLYGON    *poly;
112 {
113         int                     i;
114         char       *output = (char *) palloc(2 * (P_MAXDIG + 1) * poly->npts + 64);
115         char            buf[2 * (P_MAXDIG) + 20];
116
117         sprintf(output, "(1, %*d", P_MAXDIG, poly->npts);
118
119         for (i = 0; i < poly->npts; i++)
120         {
121                 sprintf(buf, ",%*g,%*g", P_MAXDIG, poly->p[i].x, P_MAXDIG, poly->p[i].y);
122                 strcat(output, buf);
123         }
124
125         sprintf(buf, "%c", RDELIM);
126         strcat(output, buf);
127         return DatumGetPathP(DirectFunctionCall1(path_in,
128                                                                                          CStringGetDatum(output)));
129 }
130
131 /* return the point where two paths intersect, or NULL if no intersection. */
132 Datum
133 interpt_pp(PG_FUNCTION_ARGS)
134 {
135         PATH       *p1 = PG_GETARG_PATH_P(0);
136         PATH       *p2 = PG_GETARG_PATH_P(1);
137         int                     i,
138                                 j;
139         LSEG            seg1,
140                                 seg2;
141         bool            found;                  /* We've found the intersection */
142
143         found = false;                          /* Haven't found it yet */
144
145         for (i = 0; i < p1->npts - 1 && !found; i++)
146         {
147                 regress_lseg_construct(&seg1, &p1->p[i], &p1->p[i + 1]);
148                 for (j = 0; j < p2->npts - 1 && !found; j++)
149                 {
150                         regress_lseg_construct(&seg2, &p2->p[j], &p2->p[j + 1]);
151                         if (DatumGetBool(DirectFunctionCall2(lseg_intersect,
152                                                                                                  LsegPGetDatum(&seg1),
153                                                                                                  LsegPGetDatum(&seg2))))
154                                 found = true;
155                 }
156         }
157
158         if (!found)
159                 PG_RETURN_NULL();
160
161         /* Note: DirectFunctionCall2 will kick out an error if lseg_interpt()
162          * returns NULL, but that should be impossible since we know the two
163          * segments intersect.
164          */
165         PG_RETURN_DATUM(DirectFunctionCall2(lseg_interpt,
166                                                                                 LsegPGetDatum(&seg1),
167                                                                                 LsegPGetDatum(&seg2)));
168 }
169
170
171 /* like lseg_construct, but assume space already allocated */
172 void
173 regress_lseg_construct(lseg, pt1, pt2)
174 LSEG       *lseg;
175 Point      *pt1;
176 Point      *pt2;
177 {
178         lseg->p[0].x = pt1->x;
179         lseg->p[0].y = pt1->y;
180         lseg->p[1].x = pt2->x;
181         lseg->p[1].y = pt2->y;
182         lseg->m = point_sl(pt1, pt2);
183 }
184
185 Datum
186 overpaid(PG_FUNCTION_ARGS)
187 {
188         TUPLE           tuple = (TUPLE) PG_GETARG_POINTER(0);
189         bool            isnull;
190         long            salary;
191
192         salary = (long) GetAttributeByName(tuple, "salary", &isnull);
193         if (isnull)
194                 PG_RETURN_NULL();
195         PG_RETURN_BOOL(salary > 699);
196 }
197
198 /* New type "widget"
199  * This used to be "circle", but I added circle to builtins,
200  *      so needed to make sure the names do not collide. - tgl 97/04/21
201  */
202
203 typedef struct
204 {
205         Point           center;
206         double          radius;
207 }                       WIDGET;
208
209 WIDGET     *widget_in(char *str);
210 char       *widget_out(WIDGET * widget);
211 extern Datum pt_in_widget(PG_FUNCTION_ARGS);
212
213 #define NARGS   3
214
215 WIDGET *
216 widget_in(str)
217 char       *str;
218 {
219         char       *p,
220                            *coord[NARGS],
221                                 buf2[1000];
222         int                     i;
223         WIDGET     *result;
224
225         if (str == NULL)
226                 return NULL;
227         for (i = 0, p = str; *p && i < NARGS && *p != RDELIM; p++)
228                 if (*p == ',' || (*p == LDELIM && !i))
229                         coord[i++] = p + 1;
230         if (i < NARGS - 1)
231                 return NULL;
232         result = (WIDGET *) palloc(sizeof(WIDGET));
233         result->center.x = atof(coord[0]);
234         result->center.y = atof(coord[1]);
235         result->radius = atof(coord[2]);
236
237         sprintf(buf2, "widget_in: read (%f, %f, %f)\n", result->center.x,
238                         result->center.y, result->radius);
239         return result;
240 }
241
242 char *
243 widget_out(widget)
244 WIDGET     *widget;
245 {
246         char       *result;
247
248         if (widget == NULL)
249                 return NULL;
250
251         result = (char *) palloc(60);
252         sprintf(result, "(%g,%g,%g)",
253                         widget->center.x, widget->center.y, widget->radius);
254         return result;
255 }
256
257 Datum
258 pt_in_widget(PG_FUNCTION_ARGS)
259 {
260         Point      *point = PG_GETARG_POINT_P(0);
261         WIDGET     *widget = (WIDGET *) PG_GETARG_POINTER(1);
262
263         PG_RETURN_BOOL(point_dt(point, &widget->center) < widget->radius);
264 }
265
266 #define ABS(X) ((X) >= 0 ? (X) : -(X))
267
268 Datum
269 boxarea(PG_FUNCTION_ARGS)
270 {
271         BOX                *box = PG_GETARG_BOX_P(0);
272         double          width,
273                                 height;
274
275         width = ABS(box->high.x - box->low.x);
276         height = ABS(box->high.y - box->low.y);
277         PG_RETURN_FLOAT8(width * height);
278 }
279
280 char *
281 reverse_name(string)
282 char       *string;
283 {
284         int                     i;
285         int                     len;
286         char       *new_string;
287
288         if (!(new_string = palloc(NAMEDATALEN)))
289         {
290                 fprintf(stderr, "reverse_name: palloc failed\n");
291                 return NULL;
292         }
293         MemSet(new_string, 0, NAMEDATALEN);
294         for (i = 0; i < NAMEDATALEN && string[i]; ++i)
295                 ;
296         if (i == NAMEDATALEN || !string[i])
297                 --i;
298         len = i;
299         for (; i >= 0; --i)
300                 new_string[len - i] = string[i];
301         return new_string;
302 }
303
304 #include "executor/spi.h"               /* this is what you need to work with SPI */
305 #include "commands/trigger.h"   /* -"- and triggers */
306
307 static TransactionId fd17b_xid = InvalidTransactionId;
308 static TransactionId fd17a_xid = InvalidTransactionId;
309 static int      fd17b_level = 0;
310 static int      fd17a_level = 0;
311 static bool fd17b_recursion = true;
312 static bool fd17a_recursion = true;
313 extern Datum funny_dup17(PG_FUNCTION_ARGS);
314
315 Datum
316 funny_dup17(PG_FUNCTION_ARGS)
317 {
318         TriggerData *trigdata = (TriggerData *) fcinfo->context;
319         TransactionId *xid;
320         int                *level;
321         bool       *recursion;
322         Relation        rel;
323         TupleDesc       tupdesc;
324         HeapTuple       tuple;
325         char       *query,
326                            *fieldval,
327                            *fieldtype;
328         char       *when;
329         int                     inserted;
330         int                     selected = 0;
331         int                     ret;
332
333         if (!CALLED_AS_TRIGGER(fcinfo))
334                 elog(ERROR, "funny_dup17: not fired by trigger manager");
335
336         tuple = trigdata->tg_trigtuple;
337         rel = trigdata->tg_relation;
338         tupdesc = rel->rd_att;
339         if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
340         {
341                 xid = &fd17b_xid;
342                 level = &fd17b_level;
343                 recursion = &fd17b_recursion;
344                 when = "BEFORE";
345         }
346         else
347         {
348                 xid = &fd17a_xid;
349                 level = &fd17a_level;
350                 recursion = &fd17a_recursion;
351                 when = "AFTER ";
352         }
353
354         if (!TransactionIdIsCurrentTransactionId(*xid))
355         {
356                 *xid = GetCurrentTransactionId();
357                 *level = 0;
358                 *recursion = true;
359         }
360
361         if (*level == 17)
362         {
363                 *recursion = false;
364                 return PointerGetDatum(tuple);
365         }
366
367         if (!(*recursion))
368                 return PointerGetDatum(tuple);
369
370         (*level)++;
371
372         SPI_connect();
373
374         fieldval = SPI_getvalue(tuple, tupdesc, 1);
375         fieldtype = SPI_gettype(tupdesc, 1);
376
377         query = (char *) palloc(100 + NAMEDATALEN * 3 +
378                                                         strlen(fieldval) + strlen(fieldtype));
379
380         sprintf(query, "insert into %s select * from %s where %s = '%s'::%s",
381                         SPI_getrelname(rel), SPI_getrelname(rel),
382                         SPI_fname(tupdesc, 1),
383                         fieldval, fieldtype);
384
385         if ((ret = SPI_exec(query, 0)) < 0)
386                 elog(ERROR, "funny_dup17 (fired %s) on level %3d: SPI_exec (insert ...) returned %d",
387                          when, *level, ret);
388
389         inserted = SPI_processed;
390
391         sprintf(query, "select count (*) from %s where %s = '%s'::%s",
392                         SPI_getrelname(rel),
393                         SPI_fname(tupdesc, 1),
394                         fieldval, fieldtype);
395
396         if ((ret = SPI_exec(query, 0)) < 0)
397                 elog(ERROR, "funny_dup17 (fired %s) on level %3d: SPI_exec (select ...) returned %d",
398                          when, *level, ret);
399
400         if (SPI_processed > 0)
401         {
402                 selected = DatumGetInt32(DirectFunctionCall1(int4in,
403                                                                  CStringGetDatum(SPI_getvalue(
404                                                                            SPI_tuptable->vals[0],
405                                                                            SPI_tuptable->tupdesc,
406                                                                            1
407                                                                            ))));
408         }
409
410         elog(NOTICE, "funny_dup17 (fired %s) on level %3d: %d/%d tuples inserted/selected",
411                  when, *level, inserted, selected);
412
413         SPI_finish();
414
415         (*level)--;
416
417         if (*level == 0)
418                 *xid = InvalidTransactionId;
419
420         return PointerGetDatum(tuple);
421 }
422
423 extern Datum ttdummy(PG_FUNCTION_ARGS);
424 extern Datum set_ttdummy(PG_FUNCTION_ARGS);
425
426 #define TTDUMMY_INFINITY        999999
427
428 static void *splan = NULL;
429 static bool ttoff = false;
430
431 Datum
432 ttdummy(PG_FUNCTION_ARGS)
433 {
434         TriggerData *trigdata = (TriggerData *) fcinfo->context;
435         Trigger    *trigger;            /* to get trigger name */
436         char      **args;                       /* arguments */
437         int                     attnum[2];              /* fnumbers of start/stop columns */
438         Datum           oldon,
439                                 oldoff;
440         Datum           newon,
441                                 newoff;
442         Datum      *cvals;                      /* column values */
443         char       *cnulls;                     /* column nulls */
444         char       *relname;            /* triggered relation name */
445         Relation        rel;                    /* triggered relation */
446         HeapTuple       trigtuple;
447         HeapTuple       newtuple = NULL;
448         HeapTuple       rettuple;
449         TupleDesc       tupdesc;                /* tuple description */
450         int                     natts;                  /* # of attributes */
451         bool            isnull;                 /* to know is some column NULL or not */
452         int                     ret;
453         int                     i;
454
455         if (!CALLED_AS_TRIGGER(fcinfo))
456                 elog(ERROR, "ttdummy: not fired by trigger manager");
457         if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
458                 elog(ERROR, "ttdummy: can't process STATEMENT events");
459         if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
460                 elog(ERROR, "ttdummy: must be fired before event");
461         if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
462                 elog(ERROR, "ttdummy: can't process INSERT event");
463         if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
464                 newtuple = trigdata->tg_newtuple;
465
466         trigtuple = trigdata->tg_trigtuple;
467
468         rel = trigdata->tg_relation;
469         relname = SPI_getrelname(rel);
470
471         /* check if TT is OFF for this relation */
472         if (ttoff)                                      /* OFF - nothing to do */
473         {
474                 pfree(relname);
475                 return PointerGetDatum((newtuple != NULL) ? newtuple : trigtuple);
476         }
477
478         trigger = trigdata->tg_trigger;
479
480         if (trigger->tgnargs != 2)
481                 elog(ERROR, "ttdummy (%s): invalid (!= 2) number of arguments %d",
482                          relname, trigger->tgnargs);
483
484         args = trigger->tgargs;
485         tupdesc = rel->rd_att;
486         natts = tupdesc->natts;
487
488         for (i = 0; i < 2; i++)
489         {
490                 attnum[i] = SPI_fnumber(tupdesc, args[i]);
491                 if (attnum[i] < 0)
492                         elog(ERROR, "ttdummy (%s): there is no attribute %s", relname, args[i]);
493                 if (SPI_gettypeid(tupdesc, attnum[i]) != INT4OID)
494                         elog(ERROR, "ttdummy (%s): attributes %s and %s must be of abstime type",
495                                  relname, args[0], args[1]);
496         }
497
498         oldon = SPI_getbinval(trigtuple, tupdesc, attnum[0], &isnull);
499         if (isnull)
500                 elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[0]);
501
502         oldoff = SPI_getbinval(trigtuple, tupdesc, attnum[1], &isnull);
503         if (isnull)
504                 elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[1]);
505
506         if (newtuple != NULL)           /* UPDATE */
507         {
508                 newon = SPI_getbinval(newtuple, tupdesc, attnum[0], &isnull);
509                 if (isnull)
510                         elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[0]);
511                 newoff = SPI_getbinval(newtuple, tupdesc, attnum[1], &isnull);
512                 if (isnull)
513                         elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[1]);
514
515                 if (oldon != newon || oldoff != newoff)
516                         elog(ERROR, "ttdummy (%s): you can't change %s and/or %s columns (use set_ttdummy)",
517                                  relname, args[0], args[1]);
518
519                 if (newoff != TTDUMMY_INFINITY)
520                 {
521                         pfree(relname);         /* allocated in upper executor context */
522                         return PointerGetDatum(NULL);
523                 }
524         }
525         else if (oldoff != TTDUMMY_INFINITY)            /* DELETE */
526         {
527                 pfree(relname);
528                 return PointerGetDatum(NULL);
529         }
530
531         {
532                 text   *seqname = DatumGetTextP(DirectFunctionCall1(textin,
533                                                                                         CStringGetDatum("ttdummy_seq")));
534
535                 newoff = DirectFunctionCall1(nextval,
536                                                                          PointerGetDatum(seqname));
537                 pfree(seqname);
538         }
539
540         /* Connect to SPI manager */
541         if ((ret = SPI_connect()) < 0)
542                 elog(ERROR, "ttdummy (%s): SPI_connect returned %d", relname, ret);
543
544         /* Fetch tuple values and nulls */
545         cvals = (Datum *) palloc(natts * sizeof(Datum));
546         cnulls = (char *) palloc(natts * sizeof(char));
547         for (i = 0; i < natts; i++)
548         {
549                 cvals[i] = SPI_getbinval((newtuple != NULL) ? newtuple : trigtuple,
550                                                                  tupdesc, i + 1, &isnull);
551                 cnulls[i] = (isnull) ? 'n' : ' ';
552         }
553
554         /* change date column(s) */
555         if (newtuple)                           /* UPDATE */
556         {
557                 cvals[attnum[0] - 1] = newoff;  /* start_date eq current date */
558                 cnulls[attnum[0] - 1] = ' ';
559                 cvals[attnum[1] - 1] = TTDUMMY_INFINITY;                /* stop_date eq INFINITY */
560                 cnulls[attnum[1] - 1] = ' ';
561         }
562         else
563 /* DELETE */
564         {
565                 cvals[attnum[1] - 1] = newoff;  /* stop_date eq current date */
566                 cnulls[attnum[1] - 1] = ' ';
567         }
568
569         /* if there is no plan ... */
570         if (splan == NULL)
571         {
572                 void       *pplan;
573                 Oid                *ctypes;
574                 char       *query;
575
576                 /* allocate space in preparation */
577                 ctypes = (Oid *) palloc(natts * sizeof(Oid));
578                 query = (char *) palloc(100 + 16 * natts);
579
580                 /*
581                  * Construct query: INSERT INTO _relation_ VALUES ($1, ...)
582                  */
583                 sprintf(query, "INSERT INTO %s VALUES (", relname);
584                 for (i = 1; i <= natts; i++)
585                 {
586                         sprintf(query + strlen(query), "$%d%s",
587                                         i, (i < natts) ? ", " : ")");
588                         ctypes[i - 1] = SPI_gettypeid(tupdesc, i);
589                 }
590
591                 /* Prepare plan for query */
592                 pplan = SPI_prepare(query, natts, ctypes);
593                 if (pplan == NULL)
594                         elog(ERROR, "ttdummy (%s): SPI_prepare returned %d", relname, SPI_result);
595
596                 pplan = SPI_saveplan(pplan);
597                 if (pplan == NULL)
598                         elog(ERROR, "ttdummy (%s): SPI_saveplan returned %d", relname, SPI_result);
599
600                 splan = pplan;
601         }
602
603         ret = SPI_execp(splan, cvals, cnulls, 0);
604
605         if (ret < 0)
606                 elog(ERROR, "ttdummy (%s): SPI_execp returned %d", relname, ret);
607
608         /* Tuple to return to upper Executor ... */
609         if (newtuple)                           /* UPDATE */
610         {
611                 HeapTuple       tmptuple;
612
613                 tmptuple = SPI_copytuple(trigtuple);
614                 rettuple = SPI_modifytuple(rel, tmptuple, 1, &(attnum[1]), &newoff, NULL);
615                 SPI_freetuple(tmptuple);
616         }
617         else
618 /* DELETE */
619                 rettuple = trigtuple;
620
621         SPI_finish();                           /* don't forget say Bye to SPI mgr */
622
623         pfree(relname);
624
625         return PointerGetDatum(rettuple);
626 }
627
628 Datum
629 set_ttdummy(PG_FUNCTION_ARGS)
630 {
631         int32           on = PG_GETARG_INT32(0);
632
633         if (ttoff)                                      /* OFF currently */
634         {
635                 if (on == 0)
636                         PG_RETURN_INT32(0);
637
638                 /* turn ON */
639                 ttoff = false;
640                 PG_RETURN_INT32(0);
641         }
642
643         /* ON currently */
644         if (on != 0)
645                 PG_RETURN_INT32(1);
646
647         /* turn OFF */
648         ttoff = true;
649
650         PG_RETURN_INT32(1);
651 }