/* * PgQ log trigger, takes 2 arguments: * 1. queue name to be inserted to. * * Queue events will be in format: * ev_type - operation type, I/U/D * ev_data - urlencoded column values * ev_extra1 - table name * ev_extra2 - optional urlencoded backup */ Datum pgq_logutriga(PG_FUNCTION_ARGS) { TriggerData *tg; struct PgqTriggerEvent ev; HeapTuple row; /* * Get the trigger call context */ if (!CALLED_AS_TRIGGER(fcinfo)) elog(ERROR, "pgq.logutriga not called as trigger"); tg = (TriggerData *)(fcinfo->context); if (TRIGGER_FIRED_BY_UPDATE(tg->tg_event)) row = tg->tg_newtuple; else row = tg->tg_trigtuple; if (pgq_is_logging_disabled()) goto skip_it; /* * Connect to the SPI manager */ if (SPI_connect() < 0) elog(ERROR, "logutriga: SPI_connect() failed"); pgq_prepare_event(&ev, tg, true); appendStringInfoChar(ev.field[EV_TYPE], ev.op_type); appendStringInfoChar(ev.field[EV_TYPE], ':'); appendStringInfoString(ev.field[EV_TYPE], ev.pkey_list); appendStringInfoString(ev.field[EV_EXTRA1], ev.info->table_name); if (is_interesting_change(&ev, tg)) { /* * create type, data */ pgq_urlenc_row(&ev, row, ev.field[EV_DATA]); /* * Construct the parameter array and insert the log row. */ pgq_insert_tg_event(&ev); } if (SPI_finish() < 0) elog(ERROR, "SPI_finish failed"); /* * After trigger ignores result, * before trigger skips event if NULL. */ skip_it: if (TRIGGER_FIRED_AFTER(tg->tg_event) || ev.tgargs->skip) return PointerGetDatum(NULL); else return PointerGetDatum(row); }
/* * parse trigger arguments. */ void pgq_prepare_event(struct PgqTriggerEvent *ev, TriggerData *tg, bool newstyle) { memset(ev, 0, sizeof(*ev)); /* * Check trigger calling conventions */ if (TRIGGER_FIRED_BY_TRUNCATE(tg->tg_event)) { if (!TRIGGER_FIRED_FOR_STATEMENT(tg->tg_event)) elog(ERROR, "pgq tRuncate trigger must be fired FOR EACH STATEMENT"); } else if (!TRIGGER_FIRED_FOR_ROW(tg->tg_event)) { elog(ERROR, "pgq Ins/Upd/Del trigger must be fired FOR EACH ROW"); } if (tg->tg_trigger->tgnargs < 1) elog(ERROR, "pgq trigger must have destination queue as argument"); /* * check operation type */ if (TRIGGER_FIRED_BY_INSERT(tg->tg_event)) ev->op_type = 'I'; else if (TRIGGER_FIRED_BY_UPDATE(tg->tg_event)) ev->op_type = 'U'; else if (TRIGGER_FIRED_BY_DELETE(tg->tg_event)) ev->op_type = 'D'; else if (TRIGGER_FIRED_BY_TRUNCATE(tg->tg_event)) ev->op_type = 'R'; else elog(ERROR, "unknown event for pgq trigger"); /* * load table info */ ev->tgdata = tg; ev->info = find_table_info(tg->tg_relation); ev->table_name = ev->info->table_name; ev->pkey_list = ev->info->pkey_list; ev->queue_name = tg->tg_trigger->tgargs[0]; /* * parse args, newstyle args are cached */ ev->tgargs = find_trigger_info(ev->info, tg->tg_trigger->tgoid, true); if (newstyle) { if (!ev->tgargs->finalized) parse_newstyle_args(ev, tg); if (ev->tgargs->pkey_list) ev->pkey_list = ev->tgargs->pkey_list; /* Check if we have pkey */ if (ev->op_type == 'U' || ev->op_type == 'D') { if (ev->pkey_list[0] == 0) elog(ERROR, "Update/Delete on table without pkey"); } } else { parse_oldstyle_args(ev, tg); } ev->tgargs->finalized = true; /* * Check if BEFORE/AFTER makes sense. */ if (ev->tgargs->skip) { if (TRIGGER_FIRED_AFTER(tg->tg_event)) elog(ERROR, "SKIP does not work in AFTER trigger."); } else { if (!TRIGGER_FIRED_AFTER(tg->tg_event)) /* dont care ??? */ ; } if (ev->tgargs->deny) { elog(ERROR, "Table '%s' to queue '%s': change not allowed (%c)", ev->table_name, ev->queue_name, ev->op_type); } /* * init data */ ev->field[EV_TYPE] = pgq_init_varbuf(); ev->field[EV_DATA] = pgq_init_varbuf(); ev->field[EV_EXTRA1] = pgq_init_varbuf(); /* * Do the backup, if requested. */ if (ev->tgargs->backup) { ev->field[EV_EXTRA2] = pgq_init_varbuf(); pgq_urlenc_row(ev, tg->tg_trigtuple, ev->field[EV_EXTRA2]); } }