ri_triggers.c
来自「PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统」· C语言 代码 · 共 2,372 行 · 第 1/5 页
C
2,372 行
RI_KEYPAIR_PK_IDX)) { heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); } if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "SPI_connect failed"); /* * "MATCH <unspecified>" only changes columns corresponding to the * referenced columns that have changed in pk_rel. This means the * "SET attrn=NULL [, attrn=NULL]" string will be change as well. * In this case, we need to build a temporary plan rather than use * our cached plan, unless the update happens to change all * columns in the key. Fortunately, for the most common case of a * single-column foreign key, this will be true. * * In case you're wondering, the inequality check works because we * know that the old key value has no NULLs (see above). */ use_cached_query = match_type == RI_MATCH_TYPE_FULL || ri_AllKeysUnequal(pk_rel, old_row, new_row, &qkey, RI_KEYPAIR_PK_IDX); /* * Fetch or prepare a saved plan for the set null update operation * if possible, or build a temporary plan if not. */ if (!use_cached_query || (qplan = ri_FetchPreparedPlan(&qkey)) == NULL) { char querystr[MAX_QUOTED_REL_NAME_LEN + 100 + (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS * 2]; char qualstr[(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS]; char fkrelname[MAX_QUOTED_REL_NAME_LEN]; char attname[MAX_QUOTED_NAME_LEN]; const char *querysep; const char *qualsep; Oid queryoids[RI_MAX_NUMKEYS]; /* ---------- * The query string built is * UPDATE ONLY <fktable> SET fkatt1 = NULL [, ...] * WHERE fkatt1 = $1 [AND ...] * The type id's for the $ parameters are those of the * corresponding PK attributes. Thus, ri_PlanCheck could * eventually fail if the parser cannot identify some way * how to compare these two types by '='. * ---------- */ quoteRelationName(fkrelname, fk_rel); snprintf(querystr, sizeof(querystr), "UPDATE ONLY %s SET", fkrelname); qualstr[0] = '\0'; querysep = ""; qualsep = "WHERE"; for (i = 0; i < qkey.nkeypairs; i++) { quoteOneName(attname, tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]); /* * MATCH <unspecified> - only change columns corresponding * to changed columns in pk_rel's key */ if (match_type == RI_MATCH_TYPE_FULL || !ri_OneKeyEqual(pk_rel, i, old_row, new_row, &qkey, RI_KEYPAIR_PK_IDX)) { snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr), "%s %s = NULL", querysep, attname); querysep = ","; } snprintf(qualstr + strlen(qualstr), sizeof(qualstr) - strlen(qualstr), " %s %s = $%d", qualsep, attname, i + 1); qualsep = "AND"; queryoids[i] = SPI_gettypeid(pk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_PK_IDX]); } strcat(querystr, qualstr); /* * Prepare the plan. Save it only if we're building the * "standard" plan. */ qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids, &qkey, fk_rel, pk_rel, use_cached_query); } /* * We have a plan now. Run it to update the existing references. */ ri_PerformCheck(&qkey, qplan, fk_rel, pk_rel, old_row, NULL, true, /* must detect new rows */ SPI_OK_UPDATE, tgargs[RI_CONSTRAINT_NAME_ARGNO]); if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "SPI_finish failed"); heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); /* * Handle MATCH PARTIAL set null update. */ case RI_MATCH_TYPE_PARTIAL: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("MATCH PARTIAL not yet implemented"))); return PointerGetDatum(NULL); } /* * Never reached */ elog(ERROR, "invalid match_type"); return PointerGetDatum(NULL);}/* ---------- * RI_FKey_setdefault_del - * * Set foreign key references to defaults at delete event on PK table. * ---------- */DatumRI_FKey_setdefault_del(PG_FUNCTION_ARGS){ TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple old_row; RI_QueryKey qkey; void *qplan; /* * Check that this is a valid trigger call on the right time and event. */ ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_del", RI_TRIGTYPE_DELETE); tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; /* * Nothing to do if no column names to compare given */ if (tgnargs == 4) return PointerGetDatum(NULL); /* * Get the relation descriptors of the FK and PK tables and the old tuple. * * fk_rel is opened in RowExclusiveLock mode since that's what our * eventual UPDATE will get on it. */ fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock); pk_rel = trigdata->tg_relation; old_row = trigdata->tg_trigtuple; switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO])) { /* ---------- * SQL3 11.9 <referential constraint definition> * Gereral rules 6) a) iii): * MATCH <UNSPECIFIED> or MATCH FULL * ... ON DELETE SET DEFAULT * ---------- */ case RI_MATCH_TYPE_UNSPECIFIED: case RI_MATCH_TYPE_FULL: ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid, RI_PLAN_SETNULL_DEL_DOUPDATE, fk_rel, pk_rel, tgnargs, tgargs); switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX)) { case RI_KEYS_ALL_NULL: case RI_KEYS_SOME_NULL: /* * No update - MATCH FULL means there cannot be any * reference to old key if it contains NULL */ heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); case RI_KEYS_NONE_NULL: /* * Have a full qualified key - continue below */ break; } if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "SPI_connect failed"); /* * Prepare a plan for the set default delete operation. * Unfortunately we need to do it on every invocation because the * default value could potentially change between calls. */ { char querystr[MAX_QUOTED_REL_NAME_LEN + 100 + (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS * 2]; char qualstr[(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS]; char fkrelname[MAX_QUOTED_REL_NAME_LEN]; char attname[MAX_QUOTED_NAME_LEN]; const char *querysep; const char *qualsep; Oid queryoids[RI_MAX_NUMKEYS]; int i; /* ---------- * The query string built is * UPDATE ONLY <fktable> SET fkatt1 = DEFAULT [, ...] * WHERE fkatt1 = $1 [AND ...] * The type id's for the $ parameters are those of the * corresponding PK attributes. Thus, ri_PlanCheck could * eventually fail if the parser cannot identify some way * how to compare these two types by '='. * ---------- */ quoteRelationName(fkrelname, fk_rel); snprintf(querystr, sizeof(querystr), "UPDATE ONLY %s SET", fkrelname); qualstr[0] = '\0'; querysep = ""; qualsep = "WHERE"; for (i = 0; i < qkey.nkeypairs; i++) { quoteOneName(attname, tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]); snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr), "%s %s = DEFAULT", querysep, attname); snprintf(qualstr + strlen(qualstr), sizeof(qualstr) - strlen(qualstr), " %s %s = $%d", qualsep, attname, i + 1); querysep = ","; qualsep = "AND"; queryoids[i] = SPI_gettypeid(pk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_PK_IDX]); } strcat(querystr, qualstr); /* Prepare the plan, don't save it */ qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids, &qkey, fk_rel, pk_rel, false); } /* * We have a plan now. Run it to update the existing references. */ ri_PerformCheck(&qkey, qplan, fk_rel, pk_rel, old_row, NULL, true, /* must detect new rows */ SPI_OK_UPDATE, tgargs[RI_CONSTRAINT_NAME_ARGNO]); if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "SPI_finish failed"); heap_close(fk_rel, RowExclusiveLock); /* * In the case we delete the row who's key is equal to the default * values AND a referencing row in the foreign key table exists, * we would just have updated it to the same values. We need to do * another lookup now and in case a reference exists, abort the * operation. That is already implemented in the NO ACTION * trigger. */ RI_FKey_noaction_del(fcinfo); return PointerGetDatum(NULL); /* * Handle MATCH PARTIAL set null delete. */ case RI_MATCH_TYPE_PARTIAL: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("MATCH PARTIAL not yet implemented"))); return PointerGetDatum(NULL); } /* * Never reached */ elog(ERROR, "invalid match_type"); return PointerGetDatum(NULL);}/* ---------- * RI_FKey_setdefault_upd - * * Set foreign key references to defaults at update event on PK table. * ---------- */DatumRI_FKey_setdefault_upd(PG_FUNCTION_ARGS){ TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple new_row; HeapTuple old_row; RI_QueryKey qkey; void *qplan; int match_type; /* * Check that this is a valid trigger call on the right time and event. */ ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_upd", RI_TRIGTYPE_UPDATE); tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; /* * Nothing to do if no column names to compare given */ if (tgnargs == 4) return PointerGetDatum(NULL); /* * Get the relation descriptors of the FK and PK tables and the old tuple. * * fk_rel is opened in RowExclusiveLock mode since that's what our * eventual UPDATE will get on it. */ fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock); pk_rel = trigdata->tg_relation; new_row = trigdata->tg_newtuple; old_row = trigdata->tg_trigtuple; match_type = ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]); switch (match_type) { /* ---------- * SQL3 11.9 <referential constraint definition> * Gereral rules 7) a) iii): * MATCH <UNSPECIFIED> or MATCH FULL * ... ON UPDATE SET DEFAULT * ---------- */ case RI_MATCH_TYPE_UNSPECIFIED: case RI_MATCH_TYPE_FULL: ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid, RI_PLAN_SETNULL_DEL_DOUPDATE, fk_rel, pk_rel, tgnargs, tgargs); switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX)) { case RI_KEYS_ALL_NULL: case RI_KEYS_SOME_NULL: /* * No update - MATCH FULL means there cannot be any * reference to old key if it contains NULL */ heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); case RI_KEYS_NONE_NULL: /* * Have a full qualified key - continue below */ break; } /* * No need to do anything if old and new keys are equal */ if (ri_KeysEqual(pk_rel, old_row, new_row, &qkey, RI_KEYPAIR_PK_IDX)) { heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); } if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "SPI_connect failed"); /* * Prepare a plan for the set default delete operation. * Unfortunately we need to do it on every invocation because the * default value could potentially change between calls. */ { char querystr[MAX_QUOTED_REL_NAME_LEN + 100 + (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS * 2]; char qualstr[(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS]; char fkrelname[MAX_QUOTED_REL_NAME_LEN]; char attname[MAX_QUOTED_NAME_LEN]; const char *querysep; const char *qualsep; Oid queryoids[RI_MAX_NUMKEYS]; int i; /* ---------- * The query string built is * UPDATE ONLY <fktable> SET fkatt1 = DEFAULT [, ...] * WHERE fkatt1 = $1 [AND ...] * The type id's for the $ parameters are those of the * corresponding PK attributes. Thus, ri_PlanCheck could * eventually fail if the parser cannot identify some way * how to compare these two types by '='. * ---------- */ quoteRelationName(fkrelname, fk_rel); snprintf(querystr, sizeof(querystr), "UPDATE ONLY %s SET", fkrelname); qualstr[0] = '\0'; querysep = ""; qualsep = "WHERE"; for (i = 0; i < qkey.nkeypairs; i++) { quoteOneName(attname, tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]); /* * MATCH <unspecified> - only change columns corresponding * to changed columns in pk_rel's key */ if (match_type == RI_MATCH_TYPE_FULL || !ri_OneKeyEqual(pk_rel, i, old_row, new_row, &qkey, RI_KEYPAIR_PK_IDX)) { snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr), "%s %s = DEFAULT", querysep, attname); querysep = ","; } snprintf(qualstr + strlen(qualstr), sizeof(qualstr) - strlen(qualstr), " %s %s = $%d", qualsep, attname, i + 1); qualsep = "AND"; queryoids[i] = SPI_gettypeid(pk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_PK_IDX]); } strcat(querystr, qualstr); /* Prepare the plan, don't save it */ qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids, &qkey, fk_rel, pk_rel, false); } /* * We have a plan now. Run it to update the existing references. */ ri_PerformCheck(&qkey, qplan, fk_rel, pk_rel, old_row, NULL, true, /* must detect new rows */ SPI_OK_UPDATE, tgargs[RI_CONSTRAINT_NAME_ARGNO]); if (SPI_finish() != SPI_OK_FINISH)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?