refint.c
上传用户:blenddy
上传日期:2007-01-07
资源大小:6495k
文件大小:17k
源码类别:

数据库系统

开发平台:

Unix_Linux

  1. /*
  2.  * refint.c -- set of functions to define referential integrity
  3.  * constraints using general triggers.
  4.  */
  5. #include "executor/spi.h" /* this is what you need to work with SPI */
  6. #include "commands/trigger.h" /* -"- and triggers */
  7. #include <ctype.h> /* tolower () */
  8. HeapTuple check_primary_key(void);
  9. HeapTuple check_foreign_key(void);
  10. typedef struct
  11. {
  12. char    *ident;
  13. int nplans;
  14. void   **splan;
  15. } EPlan;
  16. static EPlan *FPlans = NULL;
  17. static int nFPlans = 0;
  18. static EPlan *PPlans = NULL;
  19. static int nPPlans = 0;
  20. static EPlan *find_plan(char *ident, EPlan ** eplan, int *nplans);
  21. /*
  22.  * check_primary_key () -- check that key in tuple being inserted/updated
  23.  *  references existing tuple in "primary" table.
  24.  * Though it's called without args You have to specify referenced
  25.  * table/keys while creating trigger:  key field names in triggered table,
  26.  * referenced table name, referenced key field names:
  27.  * EXECUTE PROCEDURE
  28.  * check_primary_key ('Fkey1', 'Fkey2', 'Ptable', 'Pkey1', 'Pkey2').
  29.  */
  30. HeapTuple /* have to return HeapTuple to Executor */
  31. check_primary_key()
  32. {
  33. Trigger    *trigger; /* to get trigger name */
  34. int nargs; /* # of args specified in CREATE TRIGGER */
  35. char   **args; /* arguments: column names and table name */
  36. int nkeys; /* # of key columns (= nargs / 2) */
  37. Datum    *kvals; /* key values */
  38. char    *relname; /* referenced relation name */
  39. Relation rel; /* triggered relation */
  40. HeapTuple tuple = NULL; /* tuple to return */
  41. TupleDesc tupdesc; /* tuple description */
  42. EPlan    *plan; /* prepared plan */
  43. Oid    *argtypes = NULL;/* key types to prepare execution plan */
  44. bool isnull; /* to know is some column NULL or not */
  45. char ident[2 * NAMEDATALEN]; /* to identify myself */
  46. int ret;
  47. int i;
  48. /*
  49.  * Some checks first...
  50.  */
  51. #ifdef DEBUG_QUERY
  52. elog(NOTICE, "Check_primary_key Enter Function");
  53. #endif
  54. /* Called by trigger manager ? */
  55. if (!CurrentTriggerData)
  56. elog(ERROR, "check_primary_key: triggers are not initialized");
  57. /* Should be called for ROW trigger */
  58. if (TRIGGER_FIRED_FOR_STATEMENT(CurrentTriggerData->tg_event))
  59. elog(ERROR, "check_primary_key: can't process STATEMENT events");
  60. /* If INSERTion then must check Tuple to being inserted */
  61. if (TRIGGER_FIRED_BY_INSERT(CurrentTriggerData->tg_event))
  62. tuple = CurrentTriggerData->tg_trigtuple;
  63. /* Not should be called for DELETE */
  64. else if (TRIGGER_FIRED_BY_DELETE(CurrentTriggerData->tg_event))
  65. elog(ERROR, "check_primary_key: can't process DELETE events");
  66. /* If UPDATion the must check new Tuple, not old one */
  67. else
  68. tuple = CurrentTriggerData->tg_newtuple;
  69. trigger = CurrentTriggerData->tg_trigger;
  70. nargs = trigger->tgnargs;
  71. args = trigger->tgargs;
  72. if (nargs % 2 != 1) /* odd number of arguments! */
  73. elog(ERROR, "check_primary_key: odd number of arguments should be specified");
  74. nkeys = nargs / 2;
  75. relname = args[nkeys];
  76. rel = CurrentTriggerData->tg_relation;
  77. tupdesc = rel->rd_att;
  78. /*
  79.  * Setting CurrentTriggerData to NULL prevents direct calls to trigger
  80.  * functions in queries. Normally, trigger functions have to be called
  81.  * by trigger manager code only.
  82.  */
  83. CurrentTriggerData = NULL;
  84. /* Connect to SPI manager */
  85. if ((ret = SPI_connect()) < 0)
  86. elog(ERROR, "check_primary_key: SPI_connect returned %d", ret);
  87. /*
  88.  * We use SPI plan preparation feature, so allocate space to place key
  89.  * values.
  90.  */
  91. kvals = (Datum *) palloc(nkeys * sizeof(Datum));
  92. /*
  93.  * Construct ident string as TriggerName $ TriggeredRelationId and try
  94.  * to find prepared execution plan.
  95.  */
  96. sprintf(ident, "%s$%u", trigger->tgname, rel->rd_id);
  97. plan = find_plan(ident, &PPlans, &nPPlans);
  98. /* if there is no plan then allocate argtypes for preparation */
  99. if (plan->nplans <= 0)
  100. argtypes = (Oid *) palloc(nkeys * sizeof(Oid));
  101. /* For each column in key ... */
  102. for (i = 0; i < nkeys; i++)
  103. {
  104. /* get index of column in tuple */
  105. int fnumber = SPI_fnumber(tupdesc, args[i]);
  106. /* Bad guys may give us un-existing column in CREATE TRIGGER */
  107. if (fnumber < 0)
  108. elog(ERROR, "check_primary_key: there is no attribute %s in relation %s",
  109.  args[i], SPI_getrelname(rel));
  110. /* Well, get binary (in internal format) value of column */
  111. kvals[i] = SPI_getbinval(tuple, tupdesc, fnumber, &isnull);
  112. /*
  113.  * If it's NULL then nothing to do! DON'T FORGET call SPI_finish
  114.  * ()! DON'T FORGET return tuple! Executor inserts tuple you're
  115.  * returning! If you return NULL then nothing will be inserted!
  116.  */
  117. if (isnull)
  118. {
  119. SPI_finish();
  120. return (tuple);
  121. }
  122. if (plan->nplans <= 0) /* Get typeId of column */
  123. argtypes[i] = SPI_gettypeid(tupdesc, fnumber);
  124. }
  125. /*
  126.  * If we have to prepare plan ...
  127.  */
  128. if (plan->nplans <= 0)
  129. {
  130. void    *pplan;
  131. char sql[8192];
  132. /*
  133.  * Construct query: SELECT 1 FROM _referenced_relation_ WHERE
  134.  * Pkey1 = $1 [AND Pkey2 = $2 [...]]
  135.  */
  136. sprintf(sql, "select 1 from %s where ", relname);
  137. for (i = 0; i < nkeys; i++)
  138. {
  139. sprintf(sql + strlen(sql), "%s = $%d %s",
  140.   args[i + nkeys + 1], i + 1, (i < nkeys - 1) ? "and " : "");
  141. }
  142. /* Prepare plan for query */
  143. pplan = SPI_prepare(sql, nkeys, argtypes);
  144. if (pplan == NULL)
  145. elog(ERROR, "check_primary_key: SPI_prepare returned %d", SPI_result);
  146. /*
  147.  * Remember that SPI_prepare places plan in current memory context
  148.  * - so, we have to save plan in Top memory context for latter
  149.  * use.
  150.  */
  151. pplan = SPI_saveplan(pplan);
  152. if (pplan == NULL)
  153. elog(ERROR, "check_primary_key: SPI_saveplan returned %d", SPI_result);
  154. plan->splan = (void **) malloc(sizeof(void *));
  155. *(plan->splan) = pplan;
  156. plan->nplans = 1;
  157. }
  158. /*
  159.  * Ok, execute prepared plan.
  160.  */
  161. ret = SPI_execp(*(plan->splan), kvals, NULL, 1);
  162. /* we have no NULLs - so we pass   ^^^^   here */
  163. if (ret < 0)
  164. elog(ERROR, "check_primary_key: SPI_execp returned %d", ret);
  165. /*
  166.  * If there are no tuples returned by SELECT then ...
  167.  */
  168. if (SPI_processed == 0)
  169. elog(ERROR, "%s: tuple references non-existing key in %s",
  170.  trigger->tgname, relname);
  171. SPI_finish();
  172. return (tuple);
  173. }
  174. /*
  175.  * check_foreign_key () -- check that key in tuple being deleted/updated
  176.  *  is not referenced by tuples in "foreign" table(s).
  177.  * Though it's called without args You have to specify (while creating trigger):
  178.  * number of references, action to do if key referenced
  179.  * ('restrict' | 'setnull' | 'cascade'), key field names in triggered
  180.  * ("primary") table and referencing table(s)/keys:
  181.  * EXECUTE PROCEDURE
  182.  * check_foreign_key (2, 'restrict', 'Pkey1', 'Pkey2',
  183.  * 'Ftable1', 'Fkey11', 'Fkey12', 'Ftable2', 'Fkey21', 'Fkey22').
  184.  */
  185. HeapTuple /* have to return HeapTuple to Executor */
  186. check_foreign_key()
  187. {
  188. Trigger    *trigger; /* to get trigger name */
  189. int nargs; /* # of args specified in CREATE TRIGGER */
  190. char   **args; /* arguments: as described above */
  191. char   **args_temp;
  192. int nrefs; /* number of references (== # of plans) */
  193. char action; /* 'R'estrict | 'S'etnull | 'C'ascade */
  194. int nkeys; /* # of key columns */
  195. Datum    *kvals; /* key values */
  196. char    *relname; /* referencing relation name */
  197. Relation rel; /* triggered relation */
  198. HeapTuple trigtuple = NULL; /* tuple to being changed */
  199. HeapTuple newtuple = NULL;/* tuple to return */
  200. TupleDesc tupdesc; /* tuple description */
  201. EPlan    *plan; /* prepared plan(s) */
  202. Oid    *argtypes = NULL;/* key types to prepare execution plan */
  203. bool isnull; /* to know is some column NULL or not */
  204. bool isequal = true; /* are keys in both tuples equal (in
  205.  * UPDATE) */
  206. char ident[2 * NAMEDATALEN]; /* to identify myself */
  207. int is_update = 0;
  208. int ret;
  209. int i,
  210. r;
  211. #ifdef DEBUG_QUERY
  212. elog(NOTICE, "Check_foreign_key Enter Function");
  213. #endif
  214. /*
  215.  * Some checks first...
  216.  */
  217. /* Called by trigger manager ? */
  218. if (!CurrentTriggerData)
  219. elog(ERROR, "check_foreign_key: triggers are not initialized");
  220. /* Should be called for ROW trigger */
  221. if (TRIGGER_FIRED_FOR_STATEMENT(CurrentTriggerData->tg_event))
  222. elog(ERROR, "check_foreign_key: can't process STATEMENT events");
  223. /* Not should be called for INSERT */
  224. if (TRIGGER_FIRED_BY_INSERT(CurrentTriggerData->tg_event))
  225. elog(ERROR, "check_foreign_key: can't process INSERT events");
  226. /* Have to check tg_trigtuple - tuple being deleted */
  227. trigtuple = CurrentTriggerData->tg_trigtuple;
  228. /*
  229.  * But if this is UPDATE then we have to return tg_newtuple. Also, if
  230.  * key in tg_newtuple is the same as in tg_trigtuple then nothing to
  231.  * do.
  232.  */
  233. is_update = 0;
  234. if (TRIGGER_FIRED_BY_UPDATE(CurrentTriggerData->tg_event))
  235. {
  236. newtuple = CurrentTriggerData->tg_newtuple;
  237. is_update = 1;
  238. }
  239. trigger = CurrentTriggerData->tg_trigger;
  240. nargs = trigger->tgnargs;
  241. args = trigger->tgargs;
  242. if (nargs < 5) /* nrefs, action, key, Relation, key - at
  243.  * least */
  244. elog(ERROR, "check_foreign_key: too short %d (< 5) list of arguments", nargs);
  245. nrefs = pg_atoi(args[0], sizeof(int), 0);
  246. if (nrefs < 1)
  247. elog(ERROR, "check_foreign_key: %d (< 1) number of references specified", nrefs);
  248. action = tolower(*(args[1]));
  249. if (action != 'r' && action != 'c' && action != 's')
  250. elog(ERROR, "check_foreign_key: invalid action %s", args[1]);
  251. nargs -= 2;
  252. args += 2;
  253. nkeys = (nargs - nrefs) / (nrefs + 1);
  254. if (nkeys <= 0 || nargs != (nrefs + nkeys * (nrefs + 1)))
  255. elog(ERROR, "check_foreign_key: invalid number of arguments %d for %d references",
  256.  nargs + 2, nrefs);
  257. rel = CurrentTriggerData->tg_relation;
  258. tupdesc = rel->rd_att;
  259. /*
  260.  * Setting CurrentTriggerData to NULL prevents direct calls to trigger
  261.  * functions in queries. Normally, trigger functions have to be called
  262.  * by trigger manager code only.
  263.  */
  264. CurrentTriggerData = NULL;
  265. /* Connect to SPI manager */
  266. if ((ret = SPI_connect()) < 0)
  267. elog(ERROR, "check_foreign_key: SPI_connect returned %d", ret);
  268. /*
  269.  * We use SPI plan preparation feature, so allocate space to place key
  270.  * values.
  271.  */
  272. kvals = (Datum *) palloc(nkeys * sizeof(Datum));
  273. /*
  274.  * Construct ident string as TriggerName $ TriggeredRelationId and try
  275.  * to find prepared execution plan(s).
  276.  */
  277. sprintf(ident, "%s$%u", trigger->tgname, rel->rd_id);
  278. plan = find_plan(ident, &FPlans, &nFPlans);
  279. /* if there is no plan(s) then allocate argtypes for preparation */
  280. if (plan->nplans <= 0)
  281. argtypes = (Oid *) palloc(nkeys * sizeof(Oid));
  282. /*
  283.  * else - check that we have exactly nrefs plan(s) ready
  284.  */
  285. else if (plan->nplans != nrefs)
  286. elog(ERROR, "%s: check_foreign_key: # of plans changed in meantime",
  287.  trigger->tgname);
  288. /* For each column in key ... */
  289. for (i = 0; i < nkeys; i++)
  290. {
  291. /* get index of column in tuple */
  292. int fnumber = SPI_fnumber(tupdesc, args[i]);
  293. /* Bad guys may give us un-existing column in CREATE TRIGGER */
  294. if (fnumber < 0)
  295. elog(ERROR, "check_foreign_key: there is no attribute %s in relation %s",
  296.  args[i], SPI_getrelname(rel));
  297. /* Well, get binary (in internal format) value of column */
  298. kvals[i] = SPI_getbinval(trigtuple, tupdesc, fnumber, &isnull);
  299. /*
  300.  * If it's NULL then nothing to do! DON'T FORGET call SPI_finish
  301.  * ()! DON'T FORGET return tuple! Executor inserts tuple you're
  302.  * returning! If you return NULL then nothing will be inserted!
  303.  */
  304. if (isnull)
  305. {
  306. SPI_finish();
  307. return ((newtuple == NULL) ? trigtuple : newtuple);
  308. }
  309. /*
  310.  * If UPDATE then get column value from new tuple being inserted
  311.  * and compare is this the same as old one. For the moment we use
  312.  * string presentation of values...
  313.  */
  314. if (newtuple != NULL)
  315. {
  316. char    *oldval = SPI_getvalue(trigtuple, tupdesc, fnumber);
  317. char    *newval;
  318. /* this shouldn't happen! SPI_ERROR_NOOUTFUNC ? */
  319. if (oldval == NULL)
  320. elog(ERROR, "check_foreign_key: SPI_getvalue returned %d", SPI_result);
  321. newval = SPI_getvalue(newtuple, tupdesc, fnumber);
  322. if (newval == NULL || strcmp(oldval, newval) != 0)
  323. isequal = false;
  324. }
  325. if (plan->nplans <= 0) /* Get typeId of column */
  326. argtypes[i] = SPI_gettypeid(tupdesc, fnumber);
  327. }
  328. args_temp = args;
  329. nargs -= nkeys;
  330. args += nkeys;
  331. /*
  332.  * If we have to prepare plans ...
  333.  */
  334. if (plan->nplans <= 0)
  335. {
  336. void    *pplan;
  337. char sql[8192];
  338. char   **args2 = args;
  339. plan->splan = (void **) malloc(nrefs * sizeof(void *));
  340. for (r = 0; r < nrefs; r++)
  341. {
  342. relname = args2[0];
  343. /*
  344.  * For 'R'estrict action we construct SELECT query - SELECT 1
  345.  * FROM _referencing_relation_ WHERE Fkey1 = $1 [AND Fkey2 =
  346.  * $2 [...]] - to check is tuple referenced or not.
  347.  */
  348. if (action == 'r')
  349. sprintf(sql, "select 1 from %s where ", relname);
  350. /*
  351.  * For 'C'ascade action we construct DELETE query - DELETE
  352.  * FROM _referencing_relation_ WHERE Fkey1 = $1 [AND Fkey2 =
  353.  * $2 [...]] - to delete all referencing tuples.
  354.  */
  355. /*
  356.  * Max : Cascade with UPDATE query i create update query that
  357.  * updates new key values in referenced tables
  358.  */
  359. else if (action == 'c')
  360. {
  361. if (is_update == 1)
  362. {
  363. int fn;
  364. char    *nv;
  365. int k;
  366. sprintf(sql, "update %s set ", relname);
  367. for (k = 1; k <= nkeys; k++)
  368. {
  369. int is_char_type = 0;
  370. char    *type;
  371. fn = SPI_fnumber(tupdesc, args_temp[k - 1]);
  372. nv = SPI_getvalue(newtuple, tupdesc, fn);
  373. type = SPI_gettype(tupdesc, fn);
  374. if ((strcmp(type, "text") && strcmp(type, "varchar") &&
  375. strcmp(type, "char") && strcmp(type, "bpchar") &&
  376.  strcmp(type, "date") && strcmp(type, "datetime")) == 0)
  377. is_char_type = 1;
  378. #ifdef DEBUG_QUERY
  379. elog(NOTICE, "Check_foreign_key Debug value %s type %s %d",
  380.  nv, type, is_char_type);
  381. #endif
  382. /*
  383.  * is_char_type =1 i set ' ' for define a new
  384.  * value
  385.  */
  386. sprintf(sql + strlen(sql), " %s = %s%s%s %s ",
  387. args2[k], (is_char_type > 0) ? "'" : "",
  388. nv, (is_char_type > 0) ? "'" : "", (k < nkeys) ? ", " : "");
  389. is_char_type = 0;
  390. }
  391. strcat(sql, " where ");
  392. }
  393. else
  394. /* DELETE */
  395. sprintf(sql, "delete from %s where ", relname);
  396. }
  397. /*
  398.  * For 'S'etnull action we construct UPDATE query - UPDATE
  399.  * _referencing_relation_ SET Fkey1 null [, Fkey2 null [...]]
  400.  * WHERE Fkey1 = $1 [AND Fkey2 = $2 [...]] - to set key
  401.  * columns in all referencing tuples to NULL.
  402.  */
  403. else if (action == 's')
  404. {
  405. sprintf(sql, "update %s set ", relname);
  406. for (i = 1; i <= nkeys; i++)
  407. {
  408. sprintf(sql + strlen(sql), "%s = null%s",
  409. args2[i], (i < nkeys) ? ", " : "");
  410. }
  411. strcat(sql, " where ");
  412. }
  413. /* Construct WHERE qual */
  414. for (i = 1; i <= nkeys; i++)
  415. {
  416. sprintf(sql + strlen(sql), "%s = $%d %s",
  417. args2[i], i, (i < nkeys) ? "and " : "");
  418. }
  419. /* Prepare plan for query */
  420. pplan = SPI_prepare(sql, nkeys, argtypes);
  421. if (pplan == NULL)
  422. elog(ERROR, "check_foreign_key: SPI_prepare returned %d", SPI_result);
  423. /*
  424.  * Remember that SPI_prepare places plan in current memory
  425.  * context - so, we have to save plan in Top memory context
  426.  * for latter use.
  427.  */
  428. pplan = SPI_saveplan(pplan);
  429. if (pplan == NULL)
  430. elog(ERROR, "check_foreign_key: SPI_saveplan returned %d", SPI_result);
  431. plan->splan[r] = pplan;
  432. args2 += nkeys + 1; /* to the next relation */
  433. }
  434. plan->nplans = nrefs;
  435. #ifdef DEBUG_QUERY
  436. elog(NOTICE, "Check_foreign_key Debug Query is :  %s ", sql);
  437. #endif
  438. }
  439. /*
  440.  * If UPDATE and key is not changed ...
  441.  */
  442. if (newtuple != NULL && isequal)
  443. {
  444. SPI_finish();
  445. return (newtuple);
  446. }
  447. /*
  448.  * Ok, execute prepared plan(s).
  449.  */
  450. for (r = 0; r < nrefs; r++)
  451. {
  452. /*
  453.  * For 'R'estrict we may to execute plan for one tuple only, for
  454.  * other actions - for all tuples.
  455.  */
  456. int tcount = (action == 'r') ? 1 : 0;
  457. relname = args[0];
  458. sprintf(ident, "%s$%u", trigger->tgname, rel->rd_id);
  459. plan = find_plan(ident, &FPlans, &nFPlans);
  460. ret = SPI_execp(plan->splan[r], kvals, NULL, tcount);
  461. /* we have no NULLs - so we pass   ^^^^  here */
  462. if (ret < 0)
  463. elog(ERROR, "check_foreign_key: SPI_execp returned %d", ret);
  464. /* If action is 'R'estrict ... */
  465. if (action == 'r')
  466. {
  467. /* If there is tuple returned by SELECT then ... */
  468. if (SPI_processed > 0)
  469. elog(ERROR, "%s: tuple referenced in %s",
  470.  trigger->tgname, relname);
  471. }
  472. #ifdef REFINT_VERBOSE
  473. else
  474. elog(NOTICE, "%s: %d tuple(s) of %s are %s",
  475.  trigger->tgname, SPI_processed, relname,
  476.  (action == 'c') ? "deleted" : "setted to null");
  477. #endif
  478. args += nkeys + 1; /* to the next relation */
  479. }
  480. SPI_finish();
  481. return ((newtuple == NULL) ? trigtuple : newtuple);
  482. }
  483. static EPlan *
  484. find_plan(char *ident, EPlan ** eplan, int *nplans)
  485. {
  486. EPlan    *newp;
  487. int i;
  488. if (*nplans > 0)
  489. {
  490. for (i = 0; i < *nplans; i++)
  491. {
  492. if (strcmp((*eplan)[i].ident, ident) == 0)
  493. break;
  494. }
  495. if (i != *nplans)
  496. return (*eplan + i);
  497. *eplan = (EPlan *) realloc(*eplan, (i + 1) * sizeof(EPlan));
  498. newp = *eplan + i;
  499. }
  500. else
  501. {
  502. newp = *eplan = (EPlan *) malloc(sizeof(EPlan));
  503. (*nplans) = i = 0;
  504. }
  505. newp->ident = (char *) malloc(strlen(ident) + 1);
  506. strcpy(newp->ident, ident);
  507. newp->nplans = 0;
  508. newp->splan = NULL;
  509. (*nplans)++;
  510. return (newp);
  511. }