7 #include <float.h> /* faked on sunos */
9 #include "access/transam.h"
10 #include "utils/geo_decls.h" /* includes <math.h> */
11 #include "executor/executor.h" /* For GetAttributeByName */
12 #include "commands/sequence.h" /* for nextval() */
19 extern Datum
regress_dist_ptpath(PG_FUNCTION_ARGS
);
20 extern Datum
regress_path_dist(PG_FUNCTION_ARGS
);
21 extern PATH
*poly2path(POLYGON
*poly
);
22 extern Datum
interpt_pp(PG_FUNCTION_ARGS
);
23 extern void regress_lseg_construct(LSEG
*lseg
, Point
*pt1
, Point
*pt2
);
24 extern Datum
overpaid(PG_FUNCTION_ARGS
);
25 extern Datum
boxarea(PG_FUNCTION_ARGS
);
26 extern char *reverse_name(char *string
);
27 extern int oldstyle_length(int n
, text
*t
);
28 extern Datum
int44in(PG_FUNCTION_ARGS
);
29 extern Datum
int44out(PG_FUNCTION_ARGS
);
31 #ifdef PG_MODULE_MAGIC
37 * Distance from a point to a path
39 PG_FUNCTION_INFO_V1(regress_dist_ptpath
);
42 regress_dist_ptpath(PG_FUNCTION_ARGS
)
44 Point
*pt
= PG_GETARG_POINT_P(0);
45 PATH
*path
= PG_GETARG_PATH_P(1);
46 float8 result
= 0.0; /* keep compiler quiet */
56 result
= point_dt(pt
, &path
->p
[0]);
61 * the distance from a point to a path is the smallest distance
62 * from the point to any of its constituent segments.
64 Assert(path
->npts
> 1);
65 for (i
= 0; i
< path
->npts
- 1; ++i
)
67 regress_lseg_construct(&lseg
, &path
->p
[i
], &path
->p
[i
+ 1]);
68 tmp
= DatumGetFloat8(DirectFunctionCall2(dist_ps
,
70 LsegPGetDatum(&lseg
)));
71 if (i
== 0 || tmp
< result
)
76 PG_RETURN_FLOAT8(result
);
80 * this essentially does a cartesian product of the lsegs in the
81 * two paths, and finds the min distance between any two lsegs
83 PG_FUNCTION_INFO_V1(regress_path_dist
);
86 regress_path_dist(PG_FUNCTION_ARGS
)
88 PATH
*p1
= PG_GETARG_PATH_P(0);
89 PATH
*p2
= PG_GETARG_PATH_P(1);
90 bool have_min
= false;
91 float8 min
= 0.0; /* initialize to keep compiler quiet */
98 for (i
= 0; i
< p1
->npts
- 1; i
++)
100 for (j
= 0; j
< p2
->npts
- 1; j
++)
102 regress_lseg_construct(&seg1
, &p1
->p
[i
], &p1
->p
[i
+ 1]);
103 regress_lseg_construct(&seg2
, &p2
->p
[j
], &p2
->p
[j
+ 1]);
105 tmp
= DatumGetFloat8(DirectFunctionCall2(lseg_distance
,
106 LsegPGetDatum(&seg1
),
107 LsegPGetDatum(&seg2
)));
108 if (!have_min
|| tmp
< min
)
119 PG_RETURN_FLOAT8(min
);
123 poly2path(POLYGON
*poly
)
126 char *output
= (char *) palloc(2 * (P_MAXDIG
+ 1) * poly
->npts
+ 64);
127 char buf
[2 * (P_MAXDIG
) + 20];
129 sprintf(output
, "(1, %*d", P_MAXDIG
, poly
->npts
);
131 for (i
= 0; i
< poly
->npts
; i
++)
133 snprintf(buf
, sizeof(buf
), ",%*g,%*g",
134 P_MAXDIG
, poly
->p
[i
].x
, P_MAXDIG
, poly
->p
[i
].y
);
138 snprintf(buf
, sizeof(buf
), "%c", RDELIM
);
140 return DatumGetPathP(DirectFunctionCall1(path_in
,
141 CStringGetDatum(output
)));
144 /* return the point where two paths intersect, or NULL if no intersection. */
145 PG_FUNCTION_INFO_V1(interpt_pp
);
148 interpt_pp(PG_FUNCTION_ARGS
)
150 PATH
*p1
= PG_GETARG_PATH_P(0);
151 PATH
*p2
= PG_GETARG_PATH_P(1);
156 bool found
; /* We've found the intersection */
158 found
= false; /* Haven't found it yet */
160 for (i
= 0; i
< p1
->npts
- 1 && !found
; i
++)
162 regress_lseg_construct(&seg1
, &p1
->p
[i
], &p1
->p
[i
+ 1]);
163 for (j
= 0; j
< p2
->npts
- 1 && !found
; j
++)
165 regress_lseg_construct(&seg2
, &p2
->p
[j
], &p2
->p
[j
+ 1]);
166 if (DatumGetBool(DirectFunctionCall2(lseg_intersect
,
167 LsegPGetDatum(&seg1
),
168 LsegPGetDatum(&seg2
))))
177 * Note: DirectFunctionCall2 will kick out an error if lseg_interpt()
178 * returns NULL, but that should be impossible since we know the two
179 * segments intersect.
181 PG_RETURN_DATUM(DirectFunctionCall2(lseg_interpt
,
182 LsegPGetDatum(&seg1
),
183 LsegPGetDatum(&seg2
)));
187 /* like lseg_construct, but assume space already allocated */
189 regress_lseg_construct(LSEG
*lseg
, Point
*pt1
, Point
*pt2
)
191 lseg
->p
[0].x
= pt1
->x
;
192 lseg
->p
[0].y
= pt1
->y
;
193 lseg
->p
[1].x
= pt2
->x
;
194 lseg
->p
[1].y
= pt2
->y
;
195 lseg
->m
= point_sl(pt1
, pt2
);
198 PG_FUNCTION_INFO_V1(overpaid
);
201 overpaid(PG_FUNCTION_ARGS
)
203 HeapTupleHeader tuple
= PG_GETARG_HEAPTUPLEHEADER(0);
207 salary
= DatumGetInt32(GetAttributeByName(tuple
, "salary", &isnull
));
210 PG_RETURN_BOOL(salary
> 699);
214 * This used to be "circle", but I added circle to builtins,
215 * so needed to make sure the names do not collide. - tgl 97/04/21
224 WIDGET
*widget_in(char *str
);
225 char *widget_out(WIDGET
* widget
);
226 extern Datum
pt_in_widget(PG_FUNCTION_ARGS
);
241 for (i
= 0, p
= str
; *p
&& i
< NARGS
&& *p
!= RDELIM
; p
++)
242 if (*p
== ',' || (*p
== LDELIM
&& !i
))
246 result
= (WIDGET
*) palloc(sizeof(WIDGET
));
247 result
->center
.x
= atof(coord
[0]);
248 result
->center
.y
= atof(coord
[1]);
249 result
->radius
= atof(coord
[2]);
251 snprintf(buf2
, sizeof(buf2
), "widget_in: read (%f, %f, %f)\n",
252 result
->center
.x
, result
->center
.y
, result
->radius
);
257 widget_out(WIDGET
* widget
)
264 result
= (char *) palloc(60);
265 sprintf(result
, "(%g,%g,%g)",
266 widget
->center
.x
, widget
->center
.y
, widget
->radius
);
270 PG_FUNCTION_INFO_V1(pt_in_widget
);
273 pt_in_widget(PG_FUNCTION_ARGS
)
275 Point
*point
= PG_GETARG_POINT_P(0);
276 WIDGET
*widget
= (WIDGET
*) PG_GETARG_POINTER(1);
278 PG_RETURN_BOOL(point_dt(point
, &widget
->center
) < widget
->radius
);
281 PG_FUNCTION_INFO_V1(boxarea
);
284 boxarea(PG_FUNCTION_ARGS
)
286 BOX
*box
= PG_GETARG_BOX_P(0);
290 width
= Abs(box
->high
.x
- box
->low
.x
);
291 height
= Abs(box
->high
.y
- box
->low
.y
);
292 PG_RETURN_FLOAT8(width
* height
);
296 reverse_name(char *string
)
302 new_string
= palloc0(NAMEDATALEN
);
303 for (i
= 0; i
< NAMEDATALEN
&& string
[i
]; ++i
)
305 if (i
== NAMEDATALEN
|| !string
[i
])
309 new_string
[len
- i
] = string
[i
];
314 * This rather silly function is just to test that oldstyle functions
315 * work correctly on toast-able inputs.
318 oldstyle_length(int n
, text
*t
)
323 len
= VARSIZE(t
) - VARHDRSZ
;
328 #include "executor/spi.h" /* this is what you need to work with SPI */
329 #include "commands/trigger.h" /* -"- and triggers */
331 static TransactionId fd17b_xid
= InvalidTransactionId
;
332 static TransactionId fd17a_xid
= InvalidTransactionId
;
333 static int fd17b_level
= 0;
334 static int fd17a_level
= 0;
335 static bool fd17b_recursion
= true;
336 static bool fd17a_recursion
= true;
337 extern Datum
funny_dup17(PG_FUNCTION_ARGS
);
339 PG_FUNCTION_INFO_V1(funny_dup17
);
342 funny_dup17(PG_FUNCTION_ARGS
)
344 TriggerData
*trigdata
= (TriggerData
*) fcinfo
->context
;
359 if (!CALLED_AS_TRIGGER(fcinfo
))
360 elog(ERROR
, "funny_dup17: not fired by trigger manager");
362 tuple
= trigdata
->tg_trigtuple
;
363 rel
= trigdata
->tg_relation
;
364 tupdesc
= rel
->rd_att
;
365 if (TRIGGER_FIRED_BEFORE(trigdata
->tg_event
))
368 level
= &fd17b_level
;
369 recursion
= &fd17b_recursion
;
375 level
= &fd17a_level
;
376 recursion
= &fd17a_recursion
;
380 if (!TransactionIdIsCurrentTransactionId(*xid
))
382 *xid
= GetCurrentTransactionId();
390 return PointerGetDatum(tuple
);
394 return PointerGetDatum(tuple
);
400 fieldval
= SPI_getvalue(tuple
, tupdesc
, 1);
401 fieldtype
= SPI_gettype(tupdesc
, 1);
403 query
= (char *) palloc(100 + NAMEDATALEN
* 3 +
404 strlen(fieldval
) + strlen(fieldtype
));
406 sprintf(query
, "insert into %s select * from %s where %s = '%s'::%s",
407 SPI_getrelname(rel
), SPI_getrelname(rel
),
408 SPI_fname(tupdesc
, 1),
409 fieldval
, fieldtype
);
411 if ((ret
= SPI_exec(query
, 0)) < 0)
412 elog(ERROR
, "funny_dup17 (fired %s) on level %3d: SPI_exec (insert ...) returned %d",
415 inserted
= SPI_processed
;
417 sprintf(query
, "select count (*) from %s where %s = '%s'::%s",
419 SPI_fname(tupdesc
, 1),
420 fieldval
, fieldtype
);
422 if ((ret
= SPI_exec(query
, 0)) < 0)
423 elog(ERROR
, "funny_dup17 (fired %s) on level %3d: SPI_exec (select ...) returned %d",
426 if (SPI_processed
> 0)
428 selected
= DatumGetInt32(DirectFunctionCall1(int4in
,
429 CStringGetDatum(SPI_getvalue(
430 SPI_tuptable
->vals
[0],
431 SPI_tuptable
->tupdesc
,
436 elog(DEBUG4
, "funny_dup17 (fired %s) on level %3d: %d/%d tuples inserted/selected",
437 when
, *level
, inserted
, selected
);
444 *xid
= InvalidTransactionId
;
446 return PointerGetDatum(tuple
);
449 extern Datum
ttdummy(PG_FUNCTION_ARGS
);
450 extern Datum
set_ttdummy(PG_FUNCTION_ARGS
);
452 #define TTDUMMY_INFINITY 999999
454 static SPIPlanPtr splan
= NULL
;
455 static bool ttoff
= false;
457 PG_FUNCTION_INFO_V1(ttdummy
);
460 ttdummy(PG_FUNCTION_ARGS
)
462 TriggerData
*trigdata
= (TriggerData
*) fcinfo
->context
;
463 Trigger
*trigger
; /* to get trigger name */
464 char **args
; /* arguments */
465 int attnum
[2]; /* fnumbers of start/stop columns */
470 Datum
*cvals
; /* column values */
471 char *cnulls
; /* column nulls */
472 char *relname
; /* triggered relation name */
473 Relation rel
; /* triggered relation */
475 HeapTuple newtuple
= NULL
;
477 TupleDesc tupdesc
; /* tuple description */
478 int natts
; /* # of attributes */
479 bool isnull
; /* to know is some column NULL or not */
483 if (!CALLED_AS_TRIGGER(fcinfo
))
484 elog(ERROR
, "ttdummy: not fired by trigger manager");
485 if (TRIGGER_FIRED_FOR_STATEMENT(trigdata
->tg_event
))
486 elog(ERROR
, "ttdummy: cannot process STATEMENT events");
487 if (TRIGGER_FIRED_AFTER(trigdata
->tg_event
))
488 elog(ERROR
, "ttdummy: must be fired before event");
489 if (TRIGGER_FIRED_BY_INSERT(trigdata
->tg_event
))
490 elog(ERROR
, "ttdummy: cannot process INSERT event");
491 if (TRIGGER_FIRED_BY_UPDATE(trigdata
->tg_event
))
492 newtuple
= trigdata
->tg_newtuple
;
494 trigtuple
= trigdata
->tg_trigtuple
;
496 rel
= trigdata
->tg_relation
;
497 relname
= SPI_getrelname(rel
);
499 /* check if TT is OFF for this relation */
500 if (ttoff
) /* OFF - nothing to do */
503 return PointerGetDatum((newtuple
!= NULL
) ? newtuple
: trigtuple
);
506 trigger
= trigdata
->tg_trigger
;
508 if (trigger
->tgnargs
!= 2)
509 elog(ERROR
, "ttdummy (%s): invalid (!= 2) number of arguments %d",
510 relname
, trigger
->tgnargs
);
512 args
= trigger
->tgargs
;
513 tupdesc
= rel
->rd_att
;
514 natts
= tupdesc
->natts
;
516 for (i
= 0; i
< 2; i
++)
518 attnum
[i
] = SPI_fnumber(tupdesc
, args
[i
]);
520 elog(ERROR
, "ttdummy (%s): there is no attribute %s", relname
, args
[i
]);
521 if (SPI_gettypeid(tupdesc
, attnum
[i
]) != INT4OID
)
522 elog(ERROR
, "ttdummy (%s): attributes %s and %s must be of abstime type",
523 relname
, args
[0], args
[1]);
526 oldon
= SPI_getbinval(trigtuple
, tupdesc
, attnum
[0], &isnull
);
528 elog(ERROR
, "ttdummy (%s): %s must be NOT NULL", relname
, args
[0]);
530 oldoff
= SPI_getbinval(trigtuple
, tupdesc
, attnum
[1], &isnull
);
532 elog(ERROR
, "ttdummy (%s): %s must be NOT NULL", relname
, args
[1]);
534 if (newtuple
!= NULL
) /* UPDATE */
536 newon
= SPI_getbinval(newtuple
, tupdesc
, attnum
[0], &isnull
);
538 elog(ERROR
, "ttdummy (%s): %s must be NOT NULL", relname
, args
[0]);
539 newoff
= SPI_getbinval(newtuple
, tupdesc
, attnum
[1], &isnull
);
541 elog(ERROR
, "ttdummy (%s): %s must be NOT NULL", relname
, args
[1]);
543 if (oldon
!= newon
|| oldoff
!= newoff
)
544 elog(ERROR
, "ttdummy (%s): you cannot change %s and/or %s columns (use set_ttdummy)",
545 relname
, args
[0], args
[1]);
547 if (newoff
!= TTDUMMY_INFINITY
)
549 pfree(relname
); /* allocated in upper executor context */
550 return PointerGetDatum(NULL
);
553 else if (oldoff
!= TTDUMMY_INFINITY
) /* DELETE */
556 return PointerGetDatum(NULL
);
559 newoff
= DirectFunctionCall1(nextval
, CStringGetTextDatum("ttdummy_seq"));
560 /* nextval now returns int64; coerce down to int32 */
561 newoff
= Int32GetDatum((int32
) DatumGetInt64(newoff
));
563 /* Connect to SPI manager */
564 if ((ret
= SPI_connect()) < 0)
565 elog(ERROR
, "ttdummy (%s): SPI_connect returned %d", relname
, ret
);
567 /* Fetch tuple values and nulls */
568 cvals
= (Datum
*) palloc(natts
* sizeof(Datum
));
569 cnulls
= (char *) palloc(natts
* sizeof(char));
570 for (i
= 0; i
< natts
; i
++)
572 cvals
[i
] = SPI_getbinval((newtuple
!= NULL
) ? newtuple
: trigtuple
,
573 tupdesc
, i
+ 1, &isnull
);
574 cnulls
[i
] = (isnull
) ? 'n' : ' ';
577 /* change date column(s) */
578 if (newtuple
) /* UPDATE */
580 cvals
[attnum
[0] - 1] = newoff
; /* start_date eq current date */
581 cnulls
[attnum
[0] - 1] = ' ';
582 cvals
[attnum
[1] - 1] = TTDUMMY_INFINITY
; /* stop_date eq INFINITY */
583 cnulls
[attnum
[1] - 1] = ' ';
588 cvals
[attnum
[1] - 1] = newoff
; /* stop_date eq current date */
589 cnulls
[attnum
[1] - 1] = ' ';
592 /* if there is no plan ... */
599 /* allocate space in preparation */
600 ctypes
= (Oid
*) palloc(natts
* sizeof(Oid
));
601 query
= (char *) palloc(100 + 16 * natts
);
604 * Construct query: INSERT INTO _relation_ VALUES ($1, ...)
606 sprintf(query
, "INSERT INTO %s VALUES (", relname
);
607 for (i
= 1; i
<= natts
; i
++)
609 sprintf(query
+ strlen(query
), "$%d%s",
610 i
, (i
< natts
) ? ", " : ")");
611 ctypes
[i
- 1] = SPI_gettypeid(tupdesc
, i
);
614 /* Prepare plan for query */
615 pplan
= SPI_prepare(query
, natts
, ctypes
);
617 elog(ERROR
, "ttdummy (%s): SPI_prepare returned %d", relname
, SPI_result
);
619 pplan
= SPI_saveplan(pplan
);
621 elog(ERROR
, "ttdummy (%s): SPI_saveplan returned %d", relname
, SPI_result
);
626 ret
= SPI_execp(splan
, cvals
, cnulls
, 0);
629 elog(ERROR
, "ttdummy (%s): SPI_execp returned %d", relname
, ret
);
631 /* Tuple to return to upper Executor ... */
632 if (newtuple
) /* UPDATE */
636 tmptuple
= SPI_copytuple(trigtuple
);
637 rettuple
= SPI_modifytuple(rel
, tmptuple
, 1, &(attnum
[1]), &newoff
, NULL
);
638 SPI_freetuple(tmptuple
);
642 rettuple
= trigtuple
;
644 SPI_finish(); /* don't forget say Bye to SPI mgr */
648 return PointerGetDatum(rettuple
);
651 PG_FUNCTION_INFO_V1(set_ttdummy
);
654 set_ttdummy(PG_FUNCTION_ARGS
)
656 int32 on
= PG_GETARG_INT32(0);
658 if (ttoff
) /* OFF currently */
680 * Type int44 has no real-world use, but the regression tests use it.
681 * It's a four-element vector of int4's.
685 * int44in - converts "num num ..." to internal form
687 * Note: Fills any missing positions with zeroes.
689 PG_FUNCTION_INFO_V1(int44in
);
692 int44in(PG_FUNCTION_ARGS
)
694 char *input_string
= PG_GETARG_CSTRING(0);
695 int32
*result
= (int32
*) palloc(4 * sizeof(int32
));
698 i
= sscanf(input_string
,
707 PG_RETURN_POINTER(result
);
711 * int44out - converts internal form to "num num ..."
713 PG_FUNCTION_INFO_V1(int44out
);
716 int44out(PG_FUNCTION_ARGS
)
718 int32
*an_array
= (int32
*) PG_GETARG_POINTER(0);
719 char *result
= (char *) palloc(16 * 4); /* Allow 14 digits +
725 for (i
= 0; i
< 4; i
++)
727 pg_ltoa(an_array
[i
], walk
);
728 while (*++walk
!= '\0')
733 PG_RETURN_CSTRING(result
);