Header And Logo

PostgreSQL
| The world's most advanced open source database.

autoinc.c

Go to the documentation of this file.
00001 /*
00002  * contrib/spi/autoinc.c
00003  */
00004 #include "postgres.h"
00005 
00006 #include "catalog/pg_type.h"
00007 #include "commands/sequence.h"
00008 #include "commands/trigger.h"
00009 #include "executor/spi.h"
00010 #include "utils/builtins.h"
00011 #include "utils/rel.h"
00012 
00013 PG_MODULE_MAGIC;
00014 
00015 extern Datum autoinc(PG_FUNCTION_ARGS);
00016 
00017 PG_FUNCTION_INFO_V1(autoinc);
00018 
00019 Datum
00020 autoinc(PG_FUNCTION_ARGS)
00021 {
00022     TriggerData *trigdata = (TriggerData *) fcinfo->context;
00023     Trigger    *trigger;        /* to get trigger name */
00024     int         nargs;          /* # of arguments */
00025     int        *chattrs;        /* attnums of attributes to change */
00026     int         chnattrs = 0;   /* # of above */
00027     Datum      *newvals;        /* vals of above */
00028     char      **args;           /* arguments */
00029     char       *relname;        /* triggered relation name */
00030     Relation    rel;            /* triggered relation */
00031     HeapTuple   rettuple = NULL;
00032     TupleDesc   tupdesc;        /* tuple description */
00033     bool        isnull;
00034     int         i;
00035 
00036     if (!CALLED_AS_TRIGGER(fcinfo))
00037         /* internal error */
00038         elog(ERROR, "not fired by trigger manager");
00039     if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
00040         /* internal error */
00041         elog(ERROR, "must be fired for row");
00042     if (!TRIGGER_FIRED_BEFORE(trigdata->tg_event))
00043         /* internal error */
00044         elog(ERROR, "must be fired before event");
00045 
00046     if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
00047         rettuple = trigdata->tg_trigtuple;
00048     else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
00049         rettuple = trigdata->tg_newtuple;
00050     else
00051         /* internal error */
00052         elog(ERROR, "cannot process DELETE events");
00053 
00054     rel = trigdata->tg_relation;
00055     relname = SPI_getrelname(rel);
00056 
00057     trigger = trigdata->tg_trigger;
00058 
00059     nargs = trigger->tgnargs;
00060     if (nargs <= 0 || nargs % 2 != 0)
00061         /* internal error */
00062         elog(ERROR, "autoinc (%s): even number gt 0 of arguments was expected", relname);
00063 
00064     args = trigger->tgargs;
00065     tupdesc = rel->rd_att;
00066 
00067     chattrs = (int *) palloc(nargs / 2 * sizeof(int));
00068     newvals = (Datum *) palloc(nargs / 2 * sizeof(Datum));
00069 
00070     for (i = 0; i < nargs;)
00071     {
00072         int         attnum = SPI_fnumber(tupdesc, args[i]);
00073         int32       val;
00074         Datum       seqname;
00075 
00076         if (attnum < 0)
00077             ereport(ERROR,
00078                     (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
00079                      errmsg("\"%s\" has no attribute \"%s\"",
00080                             relname, args[i])));
00081 
00082         if (SPI_gettypeid(tupdesc, attnum) != INT4OID)
00083             ereport(ERROR,
00084                     (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
00085                      errmsg("attribute \"%s\" of \"%s\" must be type INT4",
00086                             args[i], relname)));
00087 
00088         val = DatumGetInt32(SPI_getbinval(rettuple, tupdesc, attnum, &isnull));
00089 
00090         if (!isnull && val != 0)
00091         {
00092             i += 2;
00093             continue;
00094         }
00095 
00096         i++;
00097         chattrs[chnattrs] = attnum;
00098         seqname = CStringGetTextDatum(args[i]);
00099         newvals[chnattrs] = DirectFunctionCall1(nextval, seqname);
00100         /* nextval now returns int64; coerce down to int32 */
00101         newvals[chnattrs] = Int32GetDatum((int32) DatumGetInt64(newvals[chnattrs]));
00102         if (DatumGetInt32(newvals[chnattrs]) == 0)
00103         {
00104             newvals[chnattrs] = DirectFunctionCall1(nextval, seqname);
00105             newvals[chnattrs] = Int32GetDatum((int32) DatumGetInt64(newvals[chnattrs]));
00106         }
00107         pfree(DatumGetTextP(seqname));
00108         chnattrs++;
00109         i++;
00110     }
00111 
00112     if (chnattrs > 0)
00113     {
00114         rettuple = SPI_modifytuple(rel, rettuple, chnattrs, chattrs, newvals, NULL);
00115         if (rettuple == NULL)
00116             /* internal error */
00117             elog(ERROR, "autoinc (%s): %d returned by SPI_modifytuple",
00118                  relname, SPI_result);
00119     }
00120 
00121     pfree(relname);
00122     pfree(chattrs);
00123     pfree(newvals);
00124 
00125     return PointerGetDatum(rettuple);
00126 }