00001
00002
00003
00004
00005
00006
00007
00008 #include "postgres.h"
00009
00010 #include <ctype.h>
00011
00012 #include "commands/trigger.h"
00013 #include "executor/spi.h"
00014 #include "utils/builtins.h"
00015 #include "utils/rel.h"
00016
00017 PG_MODULE_MAGIC;
00018
00019 extern Datum check_primary_key(PG_FUNCTION_ARGS);
00020 extern Datum check_foreign_key(PG_FUNCTION_ARGS);
00021
00022
00023 typedef struct
00024 {
00025 char *ident;
00026 int nplans;
00027 SPIPlanPtr *splan;
00028 } EPlan;
00029
00030 static EPlan *FPlans = NULL;
00031 static int nFPlans = 0;
00032 static EPlan *PPlans = NULL;
00033 static int nPPlans = 0;
00034
00035 static EPlan *find_plan(char *ident, EPlan **eplan, int *nplans);
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047 PG_FUNCTION_INFO_V1(check_primary_key);
00048
00049 Datum
00050 check_primary_key(PG_FUNCTION_ARGS)
00051 {
00052 TriggerData *trigdata = (TriggerData *) fcinfo->context;
00053 Trigger *trigger;
00054 int nargs;
00055 char **args;
00056 int nkeys;
00057 Datum *kvals;
00058 char *relname;
00059 Relation rel;
00060 HeapTuple tuple = NULL;
00061 TupleDesc tupdesc;
00062 EPlan *plan;
00063 Oid *argtypes = NULL;
00064 bool isnull;
00065 char ident[2 * NAMEDATALEN];
00066 int ret;
00067 int i;
00068
00069 #ifdef DEBUG_QUERY
00070 elog(DEBUG4, "check_primary_key: Enter Function");
00071 #endif
00072
00073
00074
00075
00076
00077
00078 if (!CALLED_AS_TRIGGER(fcinfo))
00079
00080 elog(ERROR, "check_primary_key: not fired by trigger manager");
00081
00082
00083 if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
00084
00085 elog(ERROR, "check_primary_key: must be fired for row");
00086
00087
00088 if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
00089 tuple = trigdata->tg_trigtuple;
00090
00091
00092 else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
00093
00094 elog(ERROR, "check_primary_key: cannot process DELETE events");
00095
00096
00097 else
00098 tuple = trigdata->tg_newtuple;
00099
00100 trigger = trigdata->tg_trigger;
00101 nargs = trigger->tgnargs;
00102 args = trigger->tgargs;
00103
00104 if (nargs % 2 != 1)
00105
00106 elog(ERROR, "check_primary_key: odd number of arguments should be specified");
00107
00108 nkeys = nargs / 2;
00109 relname = args[nkeys];
00110 rel = trigdata->tg_relation;
00111 tupdesc = rel->rd_att;
00112
00113
00114 if ((ret = SPI_connect()) < 0)
00115
00116 elog(ERROR, "check_primary_key: SPI_connect returned %d", ret);
00117
00118
00119
00120
00121
00122 kvals = (Datum *) palloc(nkeys * sizeof(Datum));
00123
00124
00125
00126
00127
00128 snprintf(ident, sizeof(ident), "%s$%u", trigger->tgname, rel->rd_id);
00129 plan = find_plan(ident, &PPlans, &nPPlans);
00130
00131
00132 if (plan->nplans <= 0)
00133 argtypes = (Oid *) palloc(nkeys * sizeof(Oid));
00134
00135
00136 for (i = 0; i < nkeys; i++)
00137 {
00138
00139 int fnumber = SPI_fnumber(tupdesc, args[i]);
00140
00141
00142 if (fnumber < 0)
00143 ereport(ERROR,
00144 (errcode(ERRCODE_UNDEFINED_COLUMN),
00145 errmsg("there is no attribute \"%s\" in relation \"%s\"",
00146 args[i], SPI_getrelname(rel))));
00147
00148
00149 kvals[i] = SPI_getbinval(tuple, tupdesc, fnumber, &isnull);
00150
00151
00152
00153
00154
00155
00156 if (isnull)
00157 {
00158 SPI_finish();
00159 return PointerGetDatum(tuple);
00160 }
00161
00162 if (plan->nplans <= 0)
00163 argtypes[i] = SPI_gettypeid(tupdesc, fnumber);
00164 }
00165
00166
00167
00168
00169 if (plan->nplans <= 0)
00170 {
00171 SPIPlanPtr pplan;
00172 char sql[8192];
00173
00174
00175
00176
00177
00178 snprintf(sql, sizeof(sql), "select 1 from %s where ", relname);
00179 for (i = 0; i < nkeys; i++)
00180 {
00181 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s = $%d %s",
00182 args[i + nkeys + 1], i + 1, (i < nkeys - 1) ? "and " : "");
00183 }
00184
00185
00186 pplan = SPI_prepare(sql, nkeys, argtypes);
00187 if (pplan == NULL)
00188
00189 elog(ERROR, "check_primary_key: SPI_prepare returned %d", SPI_result);
00190
00191
00192
00193
00194
00195 if (SPI_keepplan(pplan))
00196
00197 elog(ERROR, "check_primary_key: SPI_keepplan failed");
00198 plan->splan = (SPIPlanPtr *) malloc(sizeof(SPIPlanPtr));
00199 *(plan->splan) = pplan;
00200 plan->nplans = 1;
00201 }
00202
00203
00204
00205
00206 ret = SPI_execp(*(plan->splan), kvals, NULL, 1);
00207
00208
00209 if (ret < 0)
00210
00211 elog(ERROR, "check_primary_key: SPI_execp returned %d", ret);
00212
00213
00214
00215
00216 if (SPI_processed == 0)
00217 ereport(ERROR,
00218 (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
00219 errmsg("tuple references non-existent key"),
00220 errdetail("Trigger \"%s\" found tuple referencing non-existent key in \"%s\".", trigger->tgname, relname)));
00221
00222 SPI_finish();
00223
00224 return PointerGetDatum(tuple);
00225 }
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239 PG_FUNCTION_INFO_V1(check_foreign_key);
00240
00241 Datum
00242 check_foreign_key(PG_FUNCTION_ARGS)
00243 {
00244 TriggerData *trigdata = (TriggerData *) fcinfo->context;
00245 Trigger *trigger;
00246 int nargs;
00247 char **args;
00248 char **args_temp;
00249 int nrefs;
00250 char action;
00251 int nkeys;
00252 Datum *kvals;
00253 char *relname;
00254 Relation rel;
00255 HeapTuple trigtuple = NULL;
00256 HeapTuple newtuple = NULL;
00257 TupleDesc tupdesc;
00258 EPlan *plan;
00259 Oid *argtypes = NULL;
00260 bool isnull;
00261 bool isequal = true;
00262 char ident[2 * NAMEDATALEN];
00263 int is_update = 0;
00264 int ret;
00265 int i,
00266 r;
00267
00268 #ifdef DEBUG_QUERY
00269 elog(DEBUG4, "check_foreign_key: Enter Function");
00270 #endif
00271
00272
00273
00274
00275
00276
00277 if (!CALLED_AS_TRIGGER(fcinfo))
00278
00279 elog(ERROR, "check_foreign_key: not fired by trigger manager");
00280
00281
00282 if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
00283
00284 elog(ERROR, "check_foreign_key: must be fired for row");
00285
00286
00287 if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
00288
00289 elog(ERROR, "check_foreign_key: cannot process INSERT events");
00290
00291
00292 trigtuple = trigdata->tg_trigtuple;
00293
00294
00295
00296
00297
00298 is_update = 0;
00299 if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
00300 {
00301 newtuple = trigdata->tg_newtuple;
00302 is_update = 1;
00303 }
00304 trigger = trigdata->tg_trigger;
00305 nargs = trigger->tgnargs;
00306 args = trigger->tgargs;
00307
00308 if (nargs < 5)
00309
00310
00311 elog(ERROR, "check_foreign_key: too short %d (< 5) list of arguments", nargs);
00312
00313 nrefs = pg_atoi(args[0], sizeof(int), 0);
00314 if (nrefs < 1)
00315
00316 elog(ERROR, "check_foreign_key: %d (< 1) number of references specified", nrefs);
00317 action = tolower((unsigned char) *(args[1]));
00318 if (action != 'r' && action != 'c' && action != 's')
00319
00320 elog(ERROR, "check_foreign_key: invalid action %s", args[1]);
00321 nargs -= 2;
00322 args += 2;
00323 nkeys = (nargs - nrefs) / (nrefs + 1);
00324 if (nkeys <= 0 || nargs != (nrefs + nkeys * (nrefs + 1)))
00325
00326 elog(ERROR, "check_foreign_key: invalid number of arguments %d for %d references",
00327 nargs + 2, nrefs);
00328
00329 rel = trigdata->tg_relation;
00330 tupdesc = rel->rd_att;
00331
00332
00333 if ((ret = SPI_connect()) < 0)
00334
00335 elog(ERROR, "check_foreign_key: SPI_connect returned %d", ret);
00336
00337
00338
00339
00340
00341 kvals = (Datum *) palloc(nkeys * sizeof(Datum));
00342
00343
00344
00345
00346
00347 snprintf(ident, sizeof(ident), "%s$%u", trigger->tgname, rel->rd_id);
00348 plan = find_plan(ident, &FPlans, &nFPlans);
00349
00350
00351 if (plan->nplans <= 0)
00352 argtypes = (Oid *) palloc(nkeys * sizeof(Oid));
00353
00354
00355
00356
00357 else if (plan->nplans != nrefs)
00358
00359 elog(ERROR, "%s: check_foreign_key: # of plans changed in meantime",
00360 trigger->tgname);
00361
00362
00363 for (i = 0; i < nkeys; i++)
00364 {
00365
00366 int fnumber = SPI_fnumber(tupdesc, args[i]);
00367
00368
00369 if (fnumber < 0)
00370 ereport(ERROR,
00371 (errcode(ERRCODE_UNDEFINED_COLUMN),
00372 errmsg("there is no attribute \"%s\" in relation \"%s\"",
00373 args[i], SPI_getrelname(rel))));
00374
00375
00376 kvals[i] = SPI_getbinval(trigtuple, tupdesc, fnumber, &isnull);
00377
00378
00379
00380
00381
00382
00383 if (isnull)
00384 {
00385 SPI_finish();
00386 return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple);
00387 }
00388
00389
00390
00391
00392
00393
00394 if (newtuple != NULL)
00395 {
00396 char *oldval = SPI_getvalue(trigtuple, tupdesc, fnumber);
00397 char *newval;
00398
00399
00400 if (oldval == NULL)
00401
00402 elog(ERROR, "check_foreign_key: SPI_getvalue returned %d", SPI_result);
00403 newval = SPI_getvalue(newtuple, tupdesc, fnumber);
00404 if (newval == NULL || strcmp(oldval, newval) != 0)
00405 isequal = false;
00406 }
00407
00408 if (plan->nplans <= 0)
00409 argtypes[i] = SPI_gettypeid(tupdesc, fnumber);
00410 }
00411 args_temp = args;
00412 nargs -= nkeys;
00413 args += nkeys;
00414
00415
00416
00417
00418 if (plan->nplans <= 0)
00419 {
00420 SPIPlanPtr pplan;
00421 char sql[8192];
00422 char **args2 = args;
00423
00424 plan->splan = (SPIPlanPtr *) malloc(nrefs * sizeof(SPIPlanPtr));
00425
00426 for (r = 0; r < nrefs; r++)
00427 {
00428 relname = args2[0];
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440 if (action == 'r')
00441
00442 snprintf(sql, sizeof(sql), "select 1 from %s where ", relname);
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461 else if (action == 'c')
00462 {
00463 if (is_update == 1)
00464 {
00465 int fn;
00466 char *nv;
00467 int k;
00468
00469 snprintf(sql, sizeof(sql), "update %s set ", relname);
00470 for (k = 1; k <= nkeys; k++)
00471 {
00472 int is_char_type = 0;
00473 char *type;
00474
00475 fn = SPI_fnumber(tupdesc, args_temp[k - 1]);
00476 nv = SPI_getvalue(newtuple, tupdesc, fn);
00477 type = SPI_gettype(tupdesc, fn);
00478
00479 if ((strcmp(type, "text") && strcmp(type, "varchar") &&
00480 strcmp(type, "char") && strcmp(type, "bpchar") &&
00481 strcmp(type, "date") && strcmp(type, "timestamp")) == 0)
00482 is_char_type = 1;
00483 #ifdef DEBUG_QUERY
00484 elog(DEBUG4, "check_foreign_key Debug value %s type %s %d",
00485 nv, type, is_char_type);
00486 #endif
00487
00488
00489
00490
00491 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
00492 " %s = %s%s%s %s ",
00493 args2[k], (is_char_type > 0) ? "'" : "",
00494 nv, (is_char_type > 0) ? "'" : "", (k < nkeys) ? ", " : "");
00495 is_char_type = 0;
00496 }
00497 strcat(sql, " where ");
00498
00499 }
00500 else
00501
00502 snprintf(sql, sizeof(sql), "delete from %s where ", relname);
00503
00504 }
00505
00506
00507
00508
00509
00510
00511
00512 else if (action == 's')
00513 {
00514 snprintf(sql, sizeof(sql), "update %s set ", relname);
00515 for (i = 1; i <= nkeys; i++)
00516 {
00517 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
00518 "%s = null%s",
00519 args2[i], (i < nkeys) ? ", " : "");
00520 }
00521 strcat(sql, " where ");
00522 }
00523
00524
00525 for (i = 1; i <= nkeys; i++)
00526 {
00527 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s = $%d %s",
00528 args2[i], i, (i < nkeys) ? "and " : "");
00529 }
00530
00531
00532 pplan = SPI_prepare(sql, nkeys, argtypes);
00533 if (pplan == NULL)
00534
00535 elog(ERROR, "check_foreign_key: SPI_prepare returned %d", SPI_result);
00536
00537
00538
00539
00540
00541 if (SPI_keepplan(pplan))
00542
00543 elog(ERROR, "check_foreign_key: SPI_keepplan failed");
00544
00545 plan->splan[r] = pplan;
00546
00547 args2 += nkeys + 1;
00548 }
00549 plan->nplans = nrefs;
00550 #ifdef DEBUG_QUERY
00551 elog(DEBUG4, "check_foreign_key Debug Query is : %s ", sql);
00552 #endif
00553 }
00554
00555
00556
00557
00558 if (newtuple != NULL && isequal)
00559 {
00560 SPI_finish();
00561 return PointerGetDatum(newtuple);
00562 }
00563
00564
00565
00566
00567 for (r = 0; r < nrefs; r++)
00568 {
00569
00570
00571
00572
00573 int tcount = (action == 'r') ? 1 : 0;
00574
00575 relname = args[0];
00576
00577 snprintf(ident, sizeof(ident), "%s$%u", trigger->tgname, rel->rd_id);
00578 plan = find_plan(ident, &FPlans, &nFPlans);
00579 ret = SPI_execp(plan->splan[r], kvals, NULL, tcount);
00580
00581
00582 if (ret < 0)
00583 ereport(ERROR,
00584 (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
00585 errmsg("SPI_execp returned %d", ret)));
00586
00587
00588 if (action == 'r')
00589 {
00590
00591 if (SPI_processed > 0)
00592 ereport(ERROR,
00593 (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
00594 errmsg("\"%s\": tuple is referenced in \"%s\"",
00595 trigger->tgname, relname)));
00596 }
00597 else
00598 {
00599 #ifdef REFINT_VERBOSE
00600 elog(NOTICE, "%s: %d tuple(s) of %s are %s",
00601 trigger->tgname, SPI_processed, relname,
00602 (action == 'c') ? "deleted" : "set to null");
00603 #endif
00604 }
00605 args += nkeys + 1;
00606 }
00607
00608 SPI_finish();
00609
00610 return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple);
00611 }
00612
00613 static EPlan *
00614 find_plan(char *ident, EPlan **eplan, int *nplans)
00615 {
00616 EPlan *newp;
00617 int i;
00618
00619 if (*nplans > 0)
00620 {
00621 for (i = 0; i < *nplans; i++)
00622 {
00623 if (strcmp((*eplan)[i].ident, ident) == 0)
00624 break;
00625 }
00626 if (i != *nplans)
00627 return (*eplan + i);
00628 *eplan = (EPlan *) realloc(*eplan, (i + 1) * sizeof(EPlan));
00629 newp = *eplan + i;
00630 }
00631 else
00632 {
00633 newp = *eplan = (EPlan *) malloc(sizeof(EPlan));
00634 (*nplans) = i = 0;
00635 }
00636
00637 newp->ident = (char *) malloc(strlen(ident) + 1);
00638 strcpy(newp->ident, ident);
00639 newp->nplans = 0;
00640 newp->splan = NULL;
00641 (*nplans)++;
00642
00643 return (newp);
00644 }