| /* |
| * PostgreSQL definitions for managed Large Objects. |
| * |
| * contrib/lo/lo.c |
| * |
| */ |
| |
| #include "postgres.h" |
| |
| #include "commands/trigger.h" |
| #include "executor/spi.h" |
| #include "utils/builtins.h" |
| #include "utils/rel.h" |
| |
| PG_MODULE_MAGIC; |
| |
| |
| /* |
| * This is the trigger that protects us from orphaned large objects |
| */ |
| PG_FUNCTION_INFO_V1(lo_manage); |
| |
| Datum |
| lo_manage(PG_FUNCTION_ARGS) |
| { |
| TriggerData *trigdata = (TriggerData *) fcinfo->context; |
| int attnum; /* attribute number to monitor */ |
| char **args; /* Args containing attr name */ |
| TupleDesc tupdesc; /* Tuple Descriptor */ |
| HeapTuple rettuple; /* Tuple to be returned */ |
| bool isdelete; /* are we deleting? */ |
| HeapTuple newtuple; /* The new value for tuple */ |
| HeapTuple trigtuple; /* The original value of tuple */ |
| |
| if (!CALLED_AS_TRIGGER(fcinfo)) /* internal error */ |
| elog(ERROR, "lo_manage: not fired by trigger manager"); |
| |
| if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) /* internal error */ |
| elog(ERROR, "%s: must be fired for row", |
| trigdata->tg_trigger->tgname); |
| |
| /* |
| * Fetch some values from trigdata |
| */ |
| newtuple = trigdata->tg_newtuple; |
| trigtuple = trigdata->tg_trigtuple; |
| tupdesc = trigdata->tg_relation->rd_att; |
| args = trigdata->tg_trigger->tgargs; |
| |
| if (args == NULL) /* internal error */ |
| elog(ERROR, "%s: no column name provided in the trigger definition", |
| trigdata->tg_trigger->tgname); |
| |
| /* tuple to return to Executor */ |
| if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) |
| rettuple = newtuple; |
| else |
| rettuple = trigtuple; |
| |
| /* Are we deleting the row? */ |
| isdelete = TRIGGER_FIRED_BY_DELETE(trigdata->tg_event); |
| |
| /* Get the column we're interested in */ |
| attnum = SPI_fnumber(tupdesc, args[0]); |
| |
| if (attnum <= 0) |
| elog(ERROR, "%s: column \"%s\" does not exist", |
| trigdata->tg_trigger->tgname, args[0]); |
| |
| /* |
| * Handle updates |
| * |
| * Here, if the value of the monitored attribute changes, then the large |
| * object associated with the original value is unlinked. |
| */ |
| if (newtuple != NULL && |
| bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, trigdata->tg_updatedcols)) |
| { |
| char *orig = SPI_getvalue(trigtuple, tupdesc, attnum); |
| char *newv = SPI_getvalue(newtuple, tupdesc, attnum); |
| |
| if (orig != NULL && (newv == NULL || strcmp(orig, newv) != 0)) |
| DirectFunctionCall1(be_lo_unlink, |
| ObjectIdGetDatum(atooid(orig))); |
| |
| if (newv) |
| pfree(newv); |
| if (orig) |
| pfree(orig); |
| } |
| |
| /* |
| * Handle deleting of rows |
| * |
| * Here, we unlink the large object associated with the managed attribute |
| */ |
| if (isdelete) |
| { |
| char *orig = SPI_getvalue(trigtuple, tupdesc, attnum); |
| |
| if (orig != NULL) |
| { |
| DirectFunctionCall1(be_lo_unlink, |
| ObjectIdGetDatum(atooid(orig))); |
| |
| pfree(orig); |
| } |
| } |
| |
| return PointerGetDatum(rettuple); |
| } |