00001
00002
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
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;
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
00070
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
00089
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;
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
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;
00165
00166 found = false;
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
00186
00187
00188
00189 PG_RETURN_DATUM(DirectFunctionCall2(lseg_interpt,
00190 LsegPGetDatum(&seg1),
00191 LsegPGetDatum(&seg2)));
00192 }
00193
00194
00195
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
00222
00223
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
00323
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;
00470 char **args;
00471 int attnum[2];
00472 Datum oldon,
00473 oldoff;
00474 Datum newon,
00475 newoff;
00476 Datum *cvals;
00477 char *cnulls;
00478 char *relname;
00479 Relation rel;
00480 HeapTuple trigtuple;
00481 HeapTuple newtuple = NULL;
00482 HeapTuple rettuple;
00483 TupleDesc tupdesc;
00484 int natts;
00485 bool isnull;
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
00506 if (ttoff)
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)
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);
00556 return PointerGetDatum(NULL);
00557 }
00558 }
00559 else if (oldoff != TTDUMMY_INFINITY)
00560 {
00561 pfree(relname);
00562 return PointerGetDatum(NULL);
00563 }
00564
00565 newoff = DirectFunctionCall1(nextval, CStringGetTextDatum("ttdummy_seq"));
00566
00567 newoff = Int32GetDatum((int32) DatumGetInt64(newoff));
00568
00569
00570 if ((ret = SPI_connect()) < 0)
00571 elog(ERROR, "ttdummy (%s): SPI_connect returned %d", relname, ret);
00572
00573
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
00584 if (newtuple)
00585 {
00586 cvals[attnum[0] - 1] = newoff;
00587 cnulls[attnum[0] - 1] = ' ';
00588 cvals[attnum[1] - 1] = TTDUMMY_INFINITY;
00589 cnulls[attnum[1] - 1] = ' ';
00590 }
00591 else
00592
00593 {
00594 cvals[attnum[1] - 1] = newoff;
00595 cnulls[attnum[1] - 1] = ' ';
00596 }
00597
00598
00599 if (splan == NULL)
00600 {
00601 SPIPlanPtr pplan;
00602 Oid *ctypes;
00603 char *query;
00604
00605
00606 ctypes = (Oid *) palloc(natts * sizeof(Oid));
00607 query = (char *) palloc(100 + 16 * natts);
00608
00609
00610
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
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
00637 if (newtuple)
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
00647 rettuple = trigtuple;
00648
00649 SPI_finish();
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)
00664 {
00665 if (on == 0)
00666 PG_RETURN_INT32(0);
00667
00668
00669 ttoff = false;
00670 PG_RETURN_INT32(0);
00671 }
00672
00673
00674 if (on != 0)
00675 PG_RETURN_INT32(1);
00676
00677
00678 ttoff = true;
00679
00680 PG_RETURN_INT32(1);
00681 }
00682
00683
00684
00685
00686
00687
00688
00689
00690
00691
00692
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
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);
00725
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 }