10 #include "access/transam.h"
11 #include "access/xact.h"
12 #include "catalog/pg_type.h"
13 #include "commands/sequence.h"
14 #include "commands/trigger.h"
15 #include "executor/executor.h"
16 #include "executor/spi.h"
17 #include "utils/builtins.h"
18 #include "utils/geo_decls.h"
26 extern Datum
regress_dist_ptpath(PG_FUNCTION_ARGS
);
27 extern Datum
regress_path_dist(PG_FUNCTION_ARGS
);
28 extern PATH
*poly2path(POLYGON
*poly
);
29 extern Datum
interpt_pp(PG_FUNCTION_ARGS
);
30 extern void regress_lseg_construct(LSEG
*lseg
, Point
*pt1
, Point
*pt2
);
31 extern Datum
overpaid(PG_FUNCTION_ARGS
);
32 extern Datum
boxarea(PG_FUNCTION_ARGS
);
33 extern char *reverse_name(char *string
);
34 extern int oldstyle_length(int n
, text
*t
);
35 extern Datum
int44in(PG_FUNCTION_ARGS
);
36 extern Datum
int44out(PG_FUNCTION_ARGS
);
38 #ifdef PG_MODULE_MAGIC
44 * Distance from a point to a path
46 PG_FUNCTION_INFO_V1(regress_dist_ptpath
);
49 regress_dist_ptpath(PG_FUNCTION_ARGS
)
51 Point
*pt
= PG_GETARG_POINT_P(0);
52 PATH
*path
= PG_GETARG_PATH_P(1);
53 float8 result
= 0.0; /* keep compiler quiet */
63 result
= point_dt(pt
, &path
->p
[0]);
68 * the distance from a point to a path is the smallest distance
69 * from the point to any of its constituent segments.
71 Assert(path
->npts
> 1);
72 for (i
= 0; i
< path
->npts
- 1; ++i
)
74 regress_lseg_construct(&lseg
, &path
->p
[i
], &path
->p
[i
+ 1]);
75 tmp
= DatumGetFloat8(DirectFunctionCall2(dist_ps
,
77 LsegPGetDatum(&lseg
)));
78 if (i
== 0 || tmp
< result
)
83 PG_RETURN_FLOAT8(result
);
87 * this essentially does a cartesian product of the lsegs in the
88 * two paths, and finds the min distance between any two lsegs
90 PG_FUNCTION_INFO_V1(regress_path_dist
);
93 regress_path_dist(PG_FUNCTION_ARGS
)
95 PATH
*p1
= PG_GETARG_PATH_P(0);
96 PATH
*p2
= PG_GETARG_PATH_P(1);
97 bool have_min
= false;
98 float8 min
= 0.0; /* initialize to keep compiler quiet */
105 for (i
= 0; i
< p1
->npts
- 1; i
++)
107 for (j
= 0; j
< p2
->npts
- 1; j
++)
109 regress_lseg_construct(&seg1
, &p1
->p
[i
], &p1
->p
[i
+ 1]);
110 regress_lseg_construct(&seg2
, &p2
->p
[j
], &p2
->p
[j
+ 1]);
112 tmp
= DatumGetFloat8(DirectFunctionCall2(lseg_distance
,
113 LsegPGetDatum(&seg1
),
114 LsegPGetDatum(&seg2
)));
115 if (!have_min
|| tmp
< min
)
126 PG_RETURN_FLOAT8(min
);
130 poly2path(POLYGON
*poly
)
133 char *output
= (char *) palloc(2 * (P_MAXDIG
+ 1) * poly
->npts
+ 64);
134 char buf
[2 * (P_MAXDIG
) + 20];
136 sprintf(output
, "(1, %*d", P_MAXDIG
, poly
->npts
);
138 for (i
= 0; i
< poly
->npts
; i
++)
140 snprintf(buf
, sizeof(buf
), ",%*g,%*g",
141 P_MAXDIG
, poly
->p
[i
].x
, P_MAXDIG
, poly
->p
[i
].y
);
145 snprintf(buf
, sizeof(buf
), "%c", RDELIM
);
147 return DatumGetPathP(DirectFunctionCall1(path_in
,
148 CStringGetDatum(output
)));
151 /* return the point where two paths intersect, or NULL if no intersection. */
152 PG_FUNCTION_INFO_V1(interpt_pp
);
155 interpt_pp(PG_FUNCTION_ARGS
)
157 PATH
*p1
= PG_GETARG_PATH_P(0);
158 PATH
*p2
= PG_GETARG_PATH_P(1);
163 bool found
; /* We've found the intersection */
165 found
= false; /* Haven't found it yet */
167 for (i
= 0; i
< p1
->npts
- 1 && !found
; i
++)
169 regress_lseg_construct(&seg1
, &p1
->p
[i
], &p1
->p
[i
+ 1]);
170 for (j
= 0; j
< p2
->npts
- 1 && !found
; j
++)
172 regress_lseg_construct(&seg2
, &p2
->p
[j
], &p2
->p
[j
+ 1]);
173 if (DatumGetBool(DirectFunctionCall2(lseg_intersect
,
174 LsegPGetDatum(&seg1
),
175 LsegPGetDatum(&seg2
))))
184 * Note: DirectFunctionCall2 will kick out an error if lseg_interpt()
185 * returns NULL, but that should be impossible since we know the two
186 * segments intersect.
188 PG_RETURN_DATUM(DirectFunctionCall2(lseg_interpt
,
189 LsegPGetDatum(&seg1
),
190 LsegPGetDatum(&seg2
)));
194 /* like lseg_construct, but assume space already allocated */
196 regress_lseg_construct(LSEG
*lseg
, Point
*pt1
, Point
*pt2
)
198 lseg
->p
[0].x
= pt1
->x
;
199 lseg
->p
[0].y
= pt1
->y
;
200 lseg
->p
[1].x
= pt2
->x
;
201 lseg
->p
[1].y
= pt2
->y
;
202 lseg
->m
= point_sl(pt1
, pt2
);
205 PG_FUNCTION_INFO_V1(overpaid
);
208 overpaid(PG_FUNCTION_ARGS
)
210 HeapTupleHeader tuple
= PG_GETARG_HEAPTUPLEHEADER(0);
214 salary
= DatumGetInt32(GetAttributeByName(tuple
, "salary", &isnull
));
217 PG_RETURN_BOOL(salary
> 699);
221 * This used to be "circle", but I added circle to builtins,
222 * so needed to make sure the names do not collide. - tgl 97/04/21
231 WIDGET
*widget_in(char *str
);
232 char *widget_out(WIDGET
* widget
);
233 extern Datum
pt_in_widget(PG_FUNCTION_ARGS
);
248 for (i
= 0, p
= str
; *p
&& i
< NARGS
&& *p
!= RDELIM
; p
++)
249 if (*p
== ',' || (*p
== LDELIM
&& !i
))
253 result
= (WIDGET
*) palloc(sizeof(WIDGET
));
254 result
->center
.x
= atof(coord
[0]);
255 result
->center
.y
= atof(coord
[1]);
256 result
->radius
= atof(coord
[2]);
258 snprintf(buf2
, sizeof(buf2
), "widget_in: read (%f, %f, %f)\n",
259 result
->center
.x
, result
->center
.y
, result
->radius
);
264 widget_out(WIDGET
* widget
)
271 result
= (char *) palloc(60);
272 sprintf(result
, "(%g,%g,%g)",
273 widget
->center
.x
, widget
->center
.y
, widget
->radius
);
277 PG_FUNCTION_INFO_V1(pt_in_widget
);
280 pt_in_widget(PG_FUNCTION_ARGS
)
282 Point
*point
= PG_GETARG_POINT_P(0);
283 WIDGET
*widget
= (WIDGET
*) PG_GETARG_POINTER(1);
285 PG_RETURN_BOOL(point_dt(point
, &widget
->center
) < widget
->radius
);
288 PG_FUNCTION_INFO_V1(boxarea
);
291 boxarea(PG_FUNCTION_ARGS
)
293 BOX
*box
= PG_GETARG_BOX_P(0);
297 width
= Abs(box
->high
.x
- box
->low
.x
);
298 height
= Abs(box
->high
.y
- box
->low
.y
);
299 PG_RETURN_FLOAT8(width
* height
);
303 reverse_name(char *string
)
309 new_string
= palloc0(NAMEDATALEN
);
310 for (i
= 0; i
< NAMEDATALEN
&& string
[i
]; ++i
)
312 if (i
== NAMEDATALEN
|| !string
[i
])
316 new_string
[len
- i
] = string
[i
];
321 * This rather silly function is just to test that oldstyle functions
322 * work correctly on toast-able inputs.
325 oldstyle_length(int n
, text
*t
)
330 len
= VARSIZE(t
) - VARHDRSZ
;
336 static TransactionId fd17b_xid
= InvalidTransactionId
;
337 static TransactionId fd17a_xid
= InvalidTransactionId
;
338 static int fd17b_level
= 0;
339 static int fd17a_level
= 0;
340 static bool fd17b_recursion
= true;
341 static bool fd17a_recursion
= true;
342 extern Datum
funny_dup17(PG_FUNCTION_ARGS
);
344 PG_FUNCTION_INFO_V1(funny_dup17
);
347 funny_dup17(PG_FUNCTION_ARGS
)
349 TriggerData
*trigdata
= (TriggerData
*) fcinfo
->context
;
364 if (!CALLED_AS_TRIGGER(fcinfo
))
365 elog(ERROR
, "funny_dup17: not fired by trigger manager");
367 tuple
= trigdata
->tg_trigtuple
;
368 rel
= trigdata
->tg_relation
;
369 tupdesc
= rel
->rd_att
;
370 if (TRIGGER_FIRED_BEFORE(trigdata
->tg_event
))
373 level
= &fd17b_level
;
374 recursion
= &fd17b_recursion
;
380 level
= &fd17a_level
;
381 recursion
= &fd17a_recursion
;
385 if (!TransactionIdIsCurrentTransactionId(*xid
))
387 *xid
= GetCurrentTransactionId();
395 return PointerGetDatum(tuple
);
399 return PointerGetDatum(tuple
);
405 fieldval
= SPI_getvalue(tuple
, tupdesc
, 1);
406 fieldtype
= SPI_gettype(tupdesc
, 1);
408 query
= (char *) palloc(100 + NAMEDATALEN
* 3 +
409 strlen(fieldval
) + strlen(fieldtype
));
411 sprintf(query
, "insert into %s select * from %s where %s = '%s'::%s",
412 SPI_getrelname(rel
), SPI_getrelname(rel
),
413 SPI_fname(tupdesc
, 1),
414 fieldval
, fieldtype
);
416 if ((ret
= SPI_exec(query
, 0)) < 0)
417 elog(ERROR
, "funny_dup17 (fired %s) on level %3d: SPI_exec (insert ...) returned %d",
420 inserted
= SPI_processed
;
422 sprintf(query
, "select count (*) from %s where %s = '%s'::%s",
424 SPI_fname(tupdesc
, 1),
425 fieldval
, fieldtype
);
427 if ((ret
= SPI_exec(query
, 0)) < 0)
428 elog(ERROR
, "funny_dup17 (fired %s) on level %3d: SPI_exec (select ...) returned %d",
431 if (SPI_processed
> 0)
433 selected
= DatumGetInt32(DirectFunctionCall1(int4in
,
434 CStringGetDatum(SPI_getvalue(
435 SPI_tuptable
->vals
[0],
436 SPI_tuptable
->tupdesc
,
441 elog(DEBUG4
, "funny_dup17 (fired %s) on level %3d: %d/%d tuples inserted/selected",
442 when
, *level
, inserted
, selected
);
449 *xid
= InvalidTransactionId
;
451 return PointerGetDatum(tuple
);
454 extern Datum
ttdummy(PG_FUNCTION_ARGS
);
455 extern Datum
set_ttdummy(PG_FUNCTION_ARGS
);
457 #define TTDUMMY_INFINITY 999999
459 static SPIPlanPtr splan
= NULL
;
460 static bool ttoff
= false;
462 PG_FUNCTION_INFO_V1(ttdummy
);
465 ttdummy(PG_FUNCTION_ARGS
)
467 TriggerData
*trigdata
= (TriggerData
*) fcinfo
->context
;
468 Trigger
*trigger
; /* to get trigger name */
469 char **args
; /* arguments */
470 int attnum
[2]; /* fnumbers of start/stop columns */
475 Datum
*cvals
; /* column values */
476 char *cnulls
; /* column nulls */
477 char *relname
; /* triggered relation name */
478 Relation rel
; /* triggered relation */
480 HeapTuple newtuple
= NULL
;
482 TupleDesc tupdesc
; /* tuple description */
483 int natts
; /* # of attributes */
484 bool isnull
; /* to know is some column NULL or not */
488 if (!CALLED_AS_TRIGGER(fcinfo
))
489 elog(ERROR
, "ttdummy: not fired by trigger manager");
490 if (TRIGGER_FIRED_FOR_STATEMENT(trigdata
->tg_event
))
491 elog(ERROR
, "ttdummy: cannot process STATEMENT events");
492 if (TRIGGER_FIRED_AFTER(trigdata
->tg_event
))
493 elog(ERROR
, "ttdummy: must be fired before event");
494 if (TRIGGER_FIRED_BY_INSERT(trigdata
->tg_event
))
495 elog(ERROR
, "ttdummy: cannot process INSERT event");
496 if (TRIGGER_FIRED_BY_UPDATE(trigdata
->tg_event
))
497 newtuple
= trigdata
->tg_newtuple
;
499 trigtuple
= trigdata
->tg_trigtuple
;
501 rel
= trigdata
->tg_relation
;
502 relname
= SPI_getrelname(rel
);
504 /* check if TT is OFF for this relation */
505 if (ttoff
) /* OFF - nothing to do */
508 return PointerGetDatum((newtuple
!= NULL
) ? newtuple
: trigtuple
);
511 trigger
= trigdata
->tg_trigger
;
513 if (trigger
->tgnargs
!= 2)
514 elog(ERROR
, "ttdummy (%s): invalid (!= 2) number of arguments %d",
515 relname
, trigger
->tgnargs
);
517 args
= trigger
->tgargs
;
518 tupdesc
= rel
->rd_att
;
519 natts
= tupdesc
->natts
;
521 for (i
= 0; i
< 2; i
++)
523 attnum
[i
] = SPI_fnumber(tupdesc
, args
[i
]);
525 elog(ERROR
, "ttdummy (%s): there is no attribute %s", relname
, args
[i
]);
526 if (SPI_gettypeid(tupdesc
, attnum
[i
]) != INT4OID
)
527 elog(ERROR
, "ttdummy (%s): attributes %s and %s must be of abstime type",
528 relname
, args
[0], args
[1]);
531 oldon
= SPI_getbinval(trigtuple
, tupdesc
, attnum
[0], &isnull
);
533 elog(ERROR
, "ttdummy (%s): %s must be NOT NULL", relname
, args
[0]);
535 oldoff
= SPI_getbinval(trigtuple
, tupdesc
, attnum
[1], &isnull
);
537 elog(ERROR
, "ttdummy (%s): %s must be NOT NULL", relname
, args
[1]);
539 if (newtuple
!= NULL
) /* UPDATE */
541 newon
= SPI_getbinval(newtuple
, tupdesc
, attnum
[0], &isnull
);
543 elog(ERROR
, "ttdummy (%s): %s must be NOT NULL", relname
, args
[0]);
544 newoff
= SPI_getbinval(newtuple
, tupdesc
, attnum
[1], &isnull
);
546 elog(ERROR
, "ttdummy (%s): %s must be NOT NULL", relname
, args
[1]);
548 if (oldon
!= newon
|| oldoff
!= newoff
)
549 elog(ERROR
, "ttdummy (%s): you cannot change %s and/or %s columns (use set_ttdummy)",
550 relname
, args
[0], args
[1]);
552 if (newoff
!= TTDUMMY_INFINITY
)
554 pfree(relname
); /* allocated in upper executor context */
555 return PointerGetDatum(NULL
);
558 else if (oldoff
!= TTDUMMY_INFINITY
) /* DELETE */
561 return PointerGetDatum(NULL
);
564 newoff
= DirectFunctionCall1(nextval
, CStringGetTextDatum("ttdummy_seq"));
565 /* nextval now returns int64; coerce down to int32 */
566 newoff
= Int32GetDatum((int32
) DatumGetInt64(newoff
));
568 /* Connect to SPI manager */
569 if ((ret
= SPI_connect()) < 0)
570 elog(ERROR
, "ttdummy (%s): SPI_connect returned %d", relname
, ret
);
572 /* Fetch tuple values and nulls */
573 cvals
= (Datum
*) palloc(natts
* sizeof(Datum
));
574 cnulls
= (char *) palloc(natts
* sizeof(char));
575 for (i
= 0; i
< natts
; i
++)
577 cvals
[i
] = SPI_getbinval((newtuple
!= NULL
) ? newtuple
: trigtuple
,
578 tupdesc
, i
+ 1, &isnull
);
579 cnulls
[i
] = (isnull
) ? 'n' : ' ';
582 /* change date column(s) */
583 if (newtuple
) /* UPDATE */
585 cvals
[attnum
[0] - 1] = newoff
; /* start_date eq current date */
586 cnulls
[attnum
[0] - 1] = ' ';
587 cvals
[attnum
[1] - 1] = TTDUMMY_INFINITY
; /* stop_date eq INFINITY */
588 cnulls
[attnum
[1] - 1] = ' ';
593 cvals
[attnum
[1] - 1] = newoff
; /* stop_date eq current date */
594 cnulls
[attnum
[1] - 1] = ' ';
597 /* if there is no plan ... */
604 /* allocate space in preparation */
605 ctypes
= (Oid
*) palloc(natts
* sizeof(Oid
));
606 query
= (char *) palloc(100 + 16 * natts
);
609 * Construct query: INSERT INTO _relation_ VALUES ($1, ...)
611 sprintf(query
, "INSERT INTO %s VALUES (", relname
);
612 for (i
= 1; i
<= natts
; i
++)
614 sprintf(query
+ strlen(query
), "$%d%s",
615 i
, (i
< natts
) ? ", " : ")");
616 ctypes
[i
- 1] = SPI_gettypeid(tupdesc
, i
);
619 /* Prepare plan for query */
620 pplan
= SPI_prepare(query
, natts
, ctypes
);
622 elog(ERROR
, "ttdummy (%s): SPI_prepare returned %d", relname
, SPI_result
);
624 pplan
= SPI_saveplan(pplan
);
626 elog(ERROR
, "ttdummy (%s): SPI_saveplan returned %d", relname
, SPI_result
);
631 ret
= SPI_execp(splan
, cvals
, cnulls
, 0);
634 elog(ERROR
, "ttdummy (%s): SPI_execp returned %d", relname
, ret
);
636 /* Tuple to return to upper Executor ... */
637 if (newtuple
) /* UPDATE */
641 tmptuple
= SPI_copytuple(trigtuple
);
642 rettuple
= SPI_modifytuple(rel
, tmptuple
, 1, &(attnum
[1]), &newoff
, NULL
);
643 SPI_freetuple(tmptuple
);
647 rettuple
= trigtuple
;
649 SPI_finish(); /* don't forget say Bye to SPI mgr */
653 return PointerGetDatum(rettuple
);
656 PG_FUNCTION_INFO_V1(set_ttdummy
);
659 set_ttdummy(PG_FUNCTION_ARGS
)
661 int32 on
= PG_GETARG_INT32(0);
663 if (ttoff
) /* OFF currently */
685 * Type int44 has no real-world use, but the regression tests use it.
686 * It's a four-element vector of int4's.
690 * int44in - converts "num num ..." to internal form
692 * Note: Fills any missing positions with zeroes.
694 PG_FUNCTION_INFO_V1(int44in
);
697 int44in(PG_FUNCTION_ARGS
)
699 char *input_string
= PG_GETARG_CSTRING(0);
700 int32
*result
= (int32
*) palloc(4 * sizeof(int32
));
703 i
= sscanf(input_string
,
712 PG_RETURN_POINTER(result
);
716 * int44out - converts internal form to "num num ..."
718 PG_FUNCTION_INFO_V1(int44out
);
721 int44out(PG_FUNCTION_ARGS
)
723 int32
*an_array
= (int32
*) PG_GETARG_POINTER(0);
724 char *result
= (char *) palloc(16 * 4); /* Allow 14 digits +
730 for (i
= 0; i
< 4; i
++)
732 pg_ltoa(an_array
[i
], walk
);
733 while (*++walk
!= '\0')
738 PG_RETURN_CSTRING(result
);