text: #include "postgres.h"
  
  #include "catalog/pg_type.h"
  #include "tcop/deparse_utility.h"
  #include "tcop/utility.h"
  #include "utils/builtins.h"
  
  PG_MODULE_MAGIC;
  
  PG_FUNCTION_INFO_V1(get_command_type);
  PG_FUNCTION_INFO_V1(get_command_tag);
  PG_FUNCTION_INFO_V1(get_altertable_subcmdtypes);
  
  Datum
  get_command_type(PG_FUNCTION_ARGS)
  {
  	CollectedCommand *cmd = (CollectedCommand *) PG_GETARG_POINTER(0);
  	const char *type;
  
  	switch (cmd->type)
  	{
  		case SCT_Simple:
  			type = "simple";
  			break;
  		case SCT_AlterTable:
  			type = "alter table";
  			break;
  		case SCT_Grant:
  			type = "grant";
  			break;
  		case SCT_AlterOpFamily:
  			type = "alter operator family";
  			break;
  		case SCT_AlterDefaultPrivileges:
  			type = "alter default privileges";
  			break;
  		case SCT_CreateOpClass:
  			type = "create operator class";
  			break;
  		case SCT_AlterTSConfig:
  			type = "alter text search configuration";
  			break;
  		default:
  			type = "unknown command type";
  			break;
  	}
  
  	PG_RETURN_TEXT_P(cstring_to_text(type));
  }
  
  Datum
  get_command_tag(PG_FUNCTION_ARGS)
  {
  	CollectedCommand *cmd = (CollectedCommand *) PG_GETARG_POINTER(0);
  
  	if (!cmd->parsetree)
  		PG_RETURN_NULL();
  
  	PG_RETURN_TEXT_P(cstring_to_text(CreateCommandTag(cmd->parsetree)));
  }
  
  Datum
  get_altertable_subcmdtypes(PG_FUNCTION_ARGS)
  {
  	CollectedCommand *cmd = (CollectedCommand *) PG_GETARG_POINTER(0);
  	ArrayBuildState *astate = NULL;
  	ListCell   *cell;
  
  	if (cmd->type != SCT_AlterTable)
  		elog(ERROR, "command is not ALTER TABLE");
  
  	foreach(cell, cmd->d.alterTable.subcmds)
  	{
  		CollectedATSubcmd *sub = lfirst(cell);
  		AlterTableCmd *subcmd = (AlterTableCmd *) sub->parsetree;
  		const char *strtype;
  
  		Assert(IsA(subcmd, AlterTableCmd));
  
  		switch (subcmd->subtype)
  		{
  			case AT_AddColumn:
  				strtype = "ADD COLUMN";
  				break;
  			case AT_AddColumnRecurse:
  				strtype = "ADD COLUMN (and recurse)";
  				break;
  			case AT_AddColumnToView:
  				strtype = "ADD COLUMN TO VIEW";
  				break;
  			case AT_ColumnDefault:
  				strtype = "ALTER COLUMN SET DEFAULT";
  				break;
  			case AT_DropNotNull:
  				strtype = "DROP NOT NULL";
  				break;
  			case AT_SetNotNull:
  				strtype = "SET NOT NULL";
  				break;
  			case AT_SetStatistics:
  				strtype = "SET STATS";
  				break;
  			case AT_SetOptions:
  				strtype = "SET OPTIONS";
  				break;
  			case AT_ResetOptions:
  				strtype = "RESET OPTIONS";
  				break;
  			case AT_SetStorage:
  				strtype = "SET STORAGE";
  				break;
  			case AT_DropColumn:
  				strtype = "DROP COLUMN";
  				break;
  			case AT_DropColumnRecurse:
  				strtype = "DROP COLUMN (and recurse)";
  				break;
  			case AT_AddIndex:
  				strtype = "ADD INDEX";
  				break;
  			case AT_ReAddIndex:
  				strtype = "(re) ADD INDEX";
  				break;
  			case AT_AddConstraint:
  				strtype = "ADD CONSTRAINT";
  				break;
  			case AT_AddConstraintRecurse:
  				strtype = "ADD CONSTRAINT (and recurse)";
  				break;
  			case AT_ReAddConstraint:
  				strtype = "(re) ADD CONSTRAINT";
  				break;
  			case AT_AlterConstraint:
  				strtype = "ALTER CONSTRAINT";
  				break;
  			case AT_ValidateConstraint:
  				strtype = "VALIDATE CONSTRAINT";
  				break;
  			case AT_ValidateConstraintRecurse:
  				strtype = "VALIDATE CONSTRAINT (and recurse)";
  				break;
  			case AT_ProcessedConstraint:
  				strtype = "ADD (processed) CONSTRAINT";
  				break;
  			case AT_AddIndexConstraint:
  				strtype = "ADD CONSTRAINT (using index)";
  				break;
  			case AT_DropConstraint:
  				strtype = "DROP CONSTRAINT";
  				break;
  			case AT_DropConstraintRecurse:
  				strtype = "DROP CONSTRAINT (and recurse)";
  				break;
  			case AT_AlterColumnType:
  				strtype = "ALTER COLUMN SET TYPE";
  				break;
  			case AT_AlterColumnGenericOptions:
  				strtype = "ALTER COLUMN SET OPTIONS";
  				break;
  			case AT_ChangeOwner:
  				strtype = "CHANGE OWNER";
  				break;
  			case AT_ClusterOn:
  				strtype = "CLUSTER";
  				break;
  			case AT_DropCluster:
  				strtype = "DROP CLUSTER";
  				break;
  			case AT_SetLogged:
  				strtype = "SET LOGGED";
  				break;
  			case AT_SetUnLogged:
  				strtype = "SET UNLOGGED";
  				break;
  			case AT_AddOids:
  				strtype = "ADD OIDS";
  				break;
  			case AT_AddOidsRecurse:
  				strtype = "ADD OIDS (and recurse)";
  				break;
  			case AT_DropOids:
  				strtype = "DROP OIDS";
  				break;
  			case AT_SetTableSpace:
  				strtype = "SET TABLESPACE";
  				break;
  			case AT_SetRelOptions:
  				strtype = "SET RELOPTIONS";
  				break;
  			case AT_ResetRelOptions:
  				strtype = "RESET RELOPTIONS";
  				break;
  			case AT_ReplaceRelOptions:
  				strtype = "REPLACE RELOPTIONS";
  				break;
  			case AT_EnableTrig:
  				strtype = "ENABLE TRIGGER";
  				break;
  			case AT_EnableAlwaysTrig:
  				strtype = "ENABLE TRIGGER (always)";
  				break;
  			case AT_EnableReplicaTrig:
  				strtype = "ENABLE TRIGGER (replica)";
  				break;
  			case AT_DisableTrig:
  				strtype = "DISABLE TRIGGER";
  				break;
  			case AT_EnableTrigAll:
  				strtype = "ENABLE TRIGGER (all)";
  				break;
  			case AT_DisableTrigAll:
  				strtype = "DISABLE TRIGGER (all)";
  				break;
  			case AT_EnableTrigUser:
  				strtype = "ENABLE TRIGGER (user)";
  				break;
  			case AT_DisableTrigUser:
  				strtype = "DISABLE TRIGGER (user)";
  				break;
  			case AT_EnableRule:
  				strtype = "ENABLE RULE";
  				break;
  			case AT_EnableAlwaysRule:
  				strtype = "ENABLE RULE (always)";
  				break;
  			case AT_EnableReplicaRule:
  				strtype = "ENABLE RULE (replica)";
  				break;
  			case AT_DisableRule:
  				strtype = "DISABLE RULE";
  				break;
  			case AT_AddInherit:
  				strtype = "ADD INHERIT";
  				break;
  			case AT_DropInherit:
  				strtype = "DROP INHERIT";
  				break;
  			case AT_AddOf:
  				strtype = "OF";
  				break;
  			case AT_DropOf:
  				strtype = "NOT OF";
  				break;
  			case AT_ReplicaIdentity:
  				strtype = "REPLICA IDENTITY";
  				break;
  			case AT_EnableRowSecurity:
  				strtype = "ENABLE ROW SECURITY";
  				break;
  			case AT_DisableRowSecurity:
  				strtype = "DISABLE ROW SECURITY";
  				break;
  			case AT_GenericOptions:
  				strtype = "SET OPTIONS";
  				break;
  		}
  
  		astate =
  			accumArrayResult(astate, CStringGetTextDatum(strtype),
  							 false, TEXTOID, CurrentMemoryContext);
  	}
  
  	if (astate == NULL)
  		elog(ERROR, "empty alter table subcommand list");
  
  	PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));
  }
  