| 1 | /* |
| 2 | * contrib/spi/autoinc.c |
| 3 | */ |
| 4 | #include "postgres.h" |
| 5 | |
| 6 | #include "access/htup_details.h" |
| 7 | #include "catalog/pg_type.h" |
| 8 | #include "commands/sequence.h" |
| 9 | #include "commands/trigger.h" |
| 10 | #include "executor/spi.h" |
| 11 | #include "utils/builtins.h" |
| 12 | #include "utils/rel.h" |
| 13 | |
| 14 | PG_MODULE_MAGIC; |
| 15 | |
| 16 | PG_FUNCTION_INFO_V1(autoinc); |
| 17 | |
| 18 | Datum |
| 19 | autoinc(PG_FUNCTION_ARGS) |
| 20 | { |
| 21 | TriggerData *trigdata = (TriggerData *) fcinfo->context; |
| 22 | Trigger *trigger; /* to get trigger name */ |
| 23 | int nargs; /* # of arguments */ |
| 24 | int *chattrs; /* attnums of attributes to change */ |
| 25 | int chnattrs = 0; /* # of above */ |
| 26 | Datum *newvals; /* vals of above */ |
| 27 | bool *newnulls; /* null flags for above */ |
| 28 | char **args; /* arguments */ |
| 29 | char *relname; /* triggered relation name */ |
| 30 | Relation rel; /* triggered relation */ |
| 31 | HeapTuple rettuple = NULL; |
| 32 | TupleDesc tupdesc; /* tuple description */ |
| 33 | bool isnull; |
| 34 | int i; |
| 35 | |
| 36 | if (!CALLED_AS_TRIGGER(fcinfo)) |
| 37 | /* internal error */ |
| 38 | elog(ERROR, "not fired by trigger manager" ); |
| 39 | if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) |
| 40 | /* internal error */ |
| 41 | elog(ERROR, "must be fired for row" ); |
| 42 | if (!TRIGGER_FIRED_BEFORE(trigdata->tg_event)) |
| 43 | /* internal error */ |
| 44 | elog(ERROR, "must be fired before event" ); |
| 45 | |
| 46 | if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) |
| 47 | rettuple = trigdata->tg_trigtuple; |
| 48 | else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) |
| 49 | rettuple = trigdata->tg_newtuple; |
| 50 | else |
| 51 | /* internal error */ |
| 52 | elog(ERROR, "cannot process DELETE events" ); |
| 53 | |
| 54 | rel = trigdata->tg_relation; |
| 55 | relname = SPI_getrelname(rel); |
| 56 | |
| 57 | trigger = trigdata->tg_trigger; |
| 58 | |
| 59 | nargs = trigger->tgnargs; |
| 60 | if (nargs <= 0 || nargs % 2 != 0) |
| 61 | /* internal error */ |
| 62 | elog(ERROR, "autoinc (%s): even number gt 0 of arguments was expected" , relname); |
| 63 | |
| 64 | args = trigger->tgargs; |
| 65 | tupdesc = rel->rd_att; |
| 66 | |
| 67 | chattrs = (int *) palloc(nargs / 2 * sizeof(int)); |
| 68 | newvals = (Datum *) palloc(nargs / 2 * sizeof(Datum)); |
| 69 | newnulls = (bool *) palloc(nargs / 2 * sizeof(bool)); |
| 70 | |
| 71 | for (i = 0; i < nargs;) |
| 72 | { |
| 73 | int attnum = SPI_fnumber(tupdesc, args[i]); |
| 74 | int32 val; |
| 75 | Datum seqname; |
| 76 | |
| 77 | if (attnum <= 0) |
| 78 | ereport(ERROR, |
| 79 | (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), |
| 80 | errmsg("\"%s\" has no attribute \"%s\"" , |
| 81 | relname, args[i]))); |
| 82 | |
| 83 | if (SPI_gettypeid(tupdesc, attnum) != INT4OID) |
| 84 | ereport(ERROR, |
| 85 | (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), |
| 86 | errmsg("attribute \"%s\" of \"%s\" must be type INT4" , |
| 87 | args[i], relname))); |
| 88 | |
| 89 | val = DatumGetInt32(SPI_getbinval(rettuple, tupdesc, attnum, &isnull)); |
| 90 | |
| 91 | if (!isnull && val != 0) |
| 92 | { |
| 93 | i += 2; |
| 94 | continue; |
| 95 | } |
| 96 | |
| 97 | i++; |
| 98 | chattrs[chnattrs] = attnum; |
| 99 | seqname = CStringGetTextDatum(args[i]); |
| 100 | newvals[chnattrs] = DirectFunctionCall1(nextval, seqname); |
| 101 | /* nextval now returns int64; coerce down to int32 */ |
| 102 | newvals[chnattrs] = Int32GetDatum((int32) DatumGetInt64(newvals[chnattrs])); |
| 103 | if (DatumGetInt32(newvals[chnattrs]) == 0) |
| 104 | { |
| 105 | newvals[chnattrs] = DirectFunctionCall1(nextval, seqname); |
| 106 | newvals[chnattrs] = Int32GetDatum((int32) DatumGetInt64(newvals[chnattrs])); |
| 107 | } |
| 108 | newnulls[chnattrs] = false; |
| 109 | pfree(DatumGetTextPP(seqname)); |
| 110 | chnattrs++; |
| 111 | i++; |
| 112 | } |
| 113 | |
| 114 | if (chnattrs > 0) |
| 115 | { |
| 116 | rettuple = heap_modify_tuple_by_cols(rettuple, tupdesc, |
| 117 | chnattrs, chattrs, |
| 118 | newvals, newnulls); |
| 119 | } |
| 120 | |
| 121 | pfree(relname); |
| 122 | pfree(chattrs); |
| 123 | pfree(newvals); |
| 124 | pfree(newnulls); |
| 125 | |
| 126 | return PointerGetDatum(rettuple); |
| 127 | } |
| 128 | |