#include <postgres.h>
#include <fmgr.h>
#include <tcop/utility.h> // ProcessUtility hook
#include <access/xact.h> // RegisterXactCallback

void myTransactionCallback(XactEvent event, void *arg);
void _PG_init(void);
void _PG_fini(void);
void mySubtransactionCallback(SubXactEvent event,
                              SubTransactionId subid,
                              SubTransactionId parentSubid,
                              void *arg);

static ProcessUtility_hook_type prev_utility_hook = NULL;
static void my_utility_hook(PlannedStmt *pstmt,
                            const char *queryString,
                            ProcessUtilityContext context,
                            ParamListInfo params,
                            QueryEnvironment *queryEnv,
                            DestReceiver *dest,
                            QueryCompletion *qc)
{
  Node *parsetree = pstmt->utilityStmt;
  if (IsA(parsetree, TransactionStmt))
  {
    TransactionStmt *transact = (TransactionStmt *) parsetree;
    elog(INFO, "my_utility_hook() utility transaction kind %d (%s rollback)",
         transact->kind, (transact->kind == TRANS_STMT_ROLLBACK ? "is" : "is not"));
  }

  if (prev_utility_hook)
    prev_utility_hook(pstmt,
                      queryString,
                      context,
                      params,
                      queryEnv,
                      dest, qc);
  else
    standard_ProcessUtility(pstmt,
                            queryString,
                            context,
                            params,
                            queryEnv,
                            dest, qc);
}

void myTransactionCallback(XactEvent event, void *arg) 
{
  elog(INFO, "myTransactionCallback() XactEvent %d (%s abort) level %d",
       event,
       (event == XACT_EVENT_ABORT ? "is" : "is not"),
       GetCurrentTransactionNestLevel());
}

void mySubtransactionCallback(SubXactEvent event,
                              SubTransactionId subid,
                              SubTransactionId parentSubid,
                              void *arg)
{
  elog(INFO, "mySubTransactionCallback() SubXactEvent %d level %d",
       event,
       GetCurrentTransactionNestLevel());
  // inject SAVEPOINT error
  if (event == SUBXACT_EVENT_START_SUB)
    elog(ERROR, "no no no");
}

void _PG_init(void) 
{
  elog(INFO, "_PG_init()");

  // save and replace the process utility hook
  prev_utility_hook = ProcessUtility_hook;
  ProcessUtility_hook = my_utility_hook;

  // register sub/xact callbacks
  RegisterXactCallback(myTransactionCallback, NULL);
  RegisterSubXactCallback(mySubtransactionCallback, NULL);
}

void _PG_fini(void)
{
  elog(INFO, "_PG_fini()");
  ProcessUtility_hook = prev_utility_hook;
  UnregisterXactCallback(myTransactionCallback, NULL);
  UnregisterSubXactCallback(mySubtransactionCallback, NULL);
} // _PG_fini()

PG_MODULE_MAGIC;

PG_FUNCTION_INFO_V1(myinit);

Datum myinit(PG_FUNCTION_ARGS)
{
  elog(INFO, "myinit()");
  PG_RETURN_VOID();
}
