/*-------------------------------------------------------------------------
 *
 * outfuncs.c
 *	  Output functions for Postgres tree nodes.
 *
 * Portions Copyright (c) 2003-2025, PgPool Global Development Group
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/nodes/outfuncs.c
 *
 *-------------------------------------------------------------------------
 */
#include <string.h>
#include <limits.h>
#include <ctype.h>
#include "pool_type.h"
#include "utils/palloc.h"
#include "utils/elog.h"
#include "parser.h"
#include "pg_list.h"
#include "parsenodes.h"
#include "pg_class.h"
#include "pg_trigger.h"
#include "value.h"

/* State flag that determines how nodeToStringInternal() should treat location fields */
static bool write_location_fields = false;

#define booltostr(x)  ((x) ? "true" : "false")
#define SINGLE_QUOTE '\''

void		_outNode(StringInfo str, void *obj);

static void _outList(StringInfo str, List *node);
static void _outIdList(StringInfo str, List *node);
static void _outAlias(StringInfo str, Alias *node);
static void _outRangeVar(StringInfo str, RangeVar *node);
static void _outVar(StringInfo str, Var *node);
static void _outConst(StringInfo str, Const *node);
static void _outSingleQuote(StringInfo str, const char *s);
static void _outParam(StringInfo str, Param *node);
static void _outAggref(StringInfo str, Aggref *node);
static void _outGroupingFunc(StringInfo str, GroupingFunc *node);
static void _outSubscriptingRef(StringInfo str, SubscriptingRef *node);
static void _outFuncExpr(StringInfo str, FuncExpr *node);
static void _outNamedArgExpr(StringInfo str, NamedArgExpr *node);
static void _outOpExpr(StringInfo str, OpExpr *node);
static void _outDistinctExpr(StringInfo str, DistinctExpr *node);
static void _outScalarArrayOpExpr(StringInfo str, ScalarArrayOpExpr *node);
static void _outBoolExpr(StringInfo str, BoolExpr *node);
static void _outSubLink(StringInfo str, SubLink *node);
static void _outSubPlan(StringInfo str, SubPlan *node);
static void _outFieldSelect(StringInfo str, FieldSelect *node);
static void _outFieldStore(StringInfo str, FieldStore *node);
static void _outRelabelType(StringInfo str, RelabelType *node);
static void _outConvertRowtypeExpr(StringInfo str, ConvertRowtypeExpr *node);
static void _outCaseExpr(StringInfo str, CaseExpr *node);
static void _outCaseWhen(StringInfo str, CaseWhen *node);
static void _outCaseTestExpr(StringInfo str, CaseTestExpr *node);
static void _outArrayExpr(StringInfo str, ArrayExpr *node);
static void _outRowExpr(StringInfo str, RowExpr *node);
static void _outCoalesceExpr(StringInfo str, CoalesceExpr *node);
static void _outMinMaxExpr(StringInfo str, MinMaxExpr *node);
static void _outNullIfExpr(StringInfo str, NullIfExpr *node);
static void _outNullTest(StringInfo str, NullTest *node);
static void _outBooleanTest(StringInfo str, BooleanTest *node);
static void _outCoerceToDomain(StringInfo str, CoerceToDomain *node);
static void _outCoerceToDomainValue(StringInfo str, CoerceToDomainValue *node);
static void _outSetToDefault(StringInfo str, SetToDefault *node);
static void _outCurrentOfExpr(StringInfo str, CurrentOfExpr *node);
static void _outInferenceElem(StringInfo str, InferenceElem *node);
static void _outReturningExpr(StringInfo str, ReturningExpr *node);
static void _outTargetEntry(StringInfo str, TargetEntry *node);
static void _outRangeTblRef(StringInfo str, RangeTblRef *node);
static void _outJoinExpr(StringInfo str, JoinExpr *node);
static void _outFromExpr(StringInfo str, FromExpr *node);
static void _outCreateStmt(StringInfo str, CreateStmt *node);
static void _outCreateTableAsStmt(StringInfo str, CreateTableAsStmt *node);
static void _outCreateForeignTableStmt(StringInfo str, CreateForeignTableStmt *node);
static void _outImportForeignSchemaStmt(StringInfo str, ImportForeignSchemaStmt *node);
static void _outIndexStmt(StringInfo str, IndexStmt *node);
static void _outNotifyStmt(StringInfo str, NotifyStmt *node);
static void _outDeclareCursorStmt(StringInfo str, DeclareCursorStmt *node);
static void _outSelectStmt(StringInfo str, SelectStmt *node);
static void _outFuncCall(StringInfo str, FuncCall *node);
static void _outDefElem(StringInfo str, DefElem *node);
static void _outLockingClause(StringInfo str, LockingClause *node);
static void _outReturnStmt(StringInfo str, ReturnStmt *node);
static void _outPLAssignStmt(StringInfo str, PLAssignStmt *node);
static void _outColumnDef(StringInfo str, ColumnDef *node);
static void _outTypeName(StringInfo str, TypeName *node);
static void _outTypeCast(StringInfo str, TypeCast *node);
static void _outIndexElem(StringInfo str, IndexElem *node);
static void _outGroupingSet(StringInfo str, GroupingSet *node);
static void _outWithClause(StringInfo str, WithClause *node);
static void _outCTESearchClause(StringInfo str, CTESearchClause *node);
static void _outCTECycleClause(StringInfo str, CTECycleClause *node);
static void _outCommonTableExpr(StringInfo str, CommonTableExpr *node);
static void _outMergeWhenClauses(StringInfo str, List *node);
static void _outMergeAction(StringInfo str, MergeAction *node);
static void _outReturningOption(StringInfo str, List *node);
static void _outReturningClause(StringInfo str, ReturningClause *node);
static void _outSetOperationStmt(StringInfo str, SetOperationStmt *node);
static void _outTableSampleClause(StringInfo str, TableSampleClause *node);
static void _outA_Expr(StringInfo str, A_Expr *node);
static void _outColumnRef(StringInfo str, ColumnRef *node);
static void _outParamRef(StringInfo str, ParamRef *node);
static void _outA_Const(StringInfo str, A_Const *node);
static void _outA_Indices(StringInfo str, A_Indices *node);
static void _outA_Indirection(StringInfo str, A_Indirection *node);
static void _outResTarget(StringInfo str, ResTarget *node);
static void _outMultiAssignRef(StringInfo str, MultiAssignRef *node);
static void _outA_ArrayExpr(StringInfo str, A_ArrayExpr *node);
static void _outWindowDef(StringInfo str, WindowDef *node);
static void _outConstraint(StringInfo str, Constraint *node);

static void _outSortBy(StringInfo str, SortBy *node);
static void _outInsertStmt(StringInfo str, InsertStmt *node);
static void _outSetClause(StringInfo str, List *node);
static void _outUpdateStmt(StringInfo str, UpdateStmt *node);
static void _outDeleteStmt(StringInfo str, DeleteStmt *node);
static void _outMergeStmt(StringInfo str, MergeStmt *node);
static void _outTransactionStmt(StringInfo str, TransactionStmt *node);
static void _outTruncateStmt(StringInfo str, TruncateStmt *node);
static void _outVacuumStmt(StringInfo str, VacuumStmt *node);
static void _outExplainStmt(StringInfo str, ExplainStmt *node);
static void _outClusterStmt(StringInfo str, ClusterStmt *node);
static void _outCheckPointStmt(StringInfo str, CheckPointStmt *node);
static void _outClosePortalStmt(StringInfo str, ClosePortalStmt *node);
static void _outListenStmt(StringInfo str, ListenStmt *node);
static void _outUnlistenStmt(StringInfo str, UnlistenStmt *node);
static void _outLoadStmt(StringInfo str, LoadStmt *node);
static void _outCopyStmt(StringInfo str, CopyStmt *node);
static void _outDeallocateStmt(StringInfo str, DeallocateStmt *node);
static void _outRenameStmt(StringInfo str, RenameStmt *node);
static void _outCreateRoleStmt(StringInfo str, CreateRoleStmt *node);
static void _outAlterRoleStmt(StringInfo str, AlterRoleStmt *node);
static void _outDropRoleStmt(StringInfo str, DropRoleStmt *node);
static void _outCreateSchemaStmt(StringInfo str, CreateSchemaStmt *node);
static void _outVariableSetStmt(StringInfo str, VariableSetStmt *node);
static void _outVariableShowStmt(StringInfo str, VariableShowStmt *node);
static void _outConstraintsSetStmt(StringInfo str, ConstraintsSetStmt *node);
static void _outAlterTableStmt(StringInfo str, AlterTableStmt *node);
static void _outCreateSeqStmt(StringInfo str, CreateSeqStmt *node);
static void _outAlterSeqStmt(StringInfo str, AlterSeqStmt *node);
static void _outCreatePLangStmt(StringInfo str, CreatePLangStmt *node);
static void _outCreateTableSpaceStmt(StringInfo str, CreateTableSpaceStmt *node);
static void _outDropTableSpaceStmt(StringInfo str, DropTableSpaceStmt *node);
static void _outCreateTrigStmt(StringInfo str, CreateTrigStmt *node);
static void _outDefineStmt(StringInfo str, DefineStmt *node);
static void _outCreateOpClassStmt(StringInfo str, CreateOpClassStmt *node);
static void _outDropStmt(StringInfo str, DropStmt *node);
static void _outFetchStmt(StringInfo str, FetchStmt *node);
static void _outGrantStmt(StringInfo str, GrantStmt *node);
static void _outGrantRoleStmt(StringInfo str, GrantRoleStmt *node);
static void _outCreateFunctionStmt(StringInfo str, CreateFunctionStmt *node);
static void _outAlterFunctionStmt(StringInfo str, AlterFunctionStmt *node);
static void _outCreateCastStmt(StringInfo str, CreateCastStmt *node);
static void _outReindexStmt(StringInfo str, ReindexStmt *node);
static void _outRuleStmt(StringInfo str, RuleStmt *node);
static void _outViewStmt(StringInfo str, ViewStmt *node);
static void _outCreatedbStmt(StringInfo str, CreatedbStmt *node);
static void _outAlterDatabaseStmt(StringInfo str, AlterDatabaseStmt *node);
static void _outAlterDatabaseSetStmt(StringInfo str, AlterDatabaseSetStmt *node);
static void _outDropdbStmt(StringInfo str, DropdbStmt *node);
static void _outCreateDomainStmt(StringInfo str, CreateDomainStmt *node);
static void _outAlterDomainStmt(StringInfo str, AlterDomainStmt *node);
static void _outCreateConversionStmt(StringInfo str, CreateConversionStmt *node);
static void _outPrepareStmt(StringInfo str, PrepareStmt *node);
static void _outExecuteStmt(StringInfo str, ExecuteStmt *node);
static void _outLockStmt(StringInfo str, LockStmt *node);
static void _outCommentStmt(StringInfo str, CommentStmt *node);
static void _outDiscardStmt(StringInfo str, DiscardStmt *node);
static void _outCreateOpFamilyStmt(StringInfo str, CreateOpFamilyStmt *node);
static void _outAlterOpFamilyStmt(StringInfo str, AlterOpFamilyStmt *node);
static void _outCreateEnumStmt(StringInfo str, CreateEnumStmt *node);
static void _outDropOwnedStmt(StringInfo str, DropOwnedStmt *node);
static void _outReassignOwnedStmt(StringInfo str, ReassignOwnedStmt *node);
static void _outAlterTSDictionaryStmt(StringInfo str, AlterTSDictionaryStmt *node);
static void _outAlterTSConfigurationStmt(StringInfo str, AlterTSConfigurationStmt *node);
static void _outXmlExpr(StringInfo str, XmlExpr *node);
static void _outXmlSerialize(StringInfo str, XmlSerialize *node);

static void _outFuncName(StringInfo str, List *func_name);
static void _outSetRest(StringInfo str, VariableSetStmt *node);
static void _outSetTransactionModeList(StringInfo str, List *list);
static void _outAlterTableCmd(StringInfo str, AlterTableCmd *node);
static void _outATAlterConstraint(StringInfo str, ATAlterConstraint *node);
static void _outOptSeqList(StringInfo str, List *options);
static void _outObjectWithArgs(StringInfo str, ObjectWithArgs *node);
static void _outFunctionParameter(StringInfo str, FunctionParameter *node);
static void _outPrivilegeList(StringInfo str, List *list);
static void _outFuncOptList(StringInfo str, List *list);
static void _outCreatedbOptList(StringInfo str, List *options);
static void _outOperatorArgTypes(StringInfo str, List *args);
static void _outRangeFunction(StringInfo str, RangeFunction *node);
static void _outRangeTableSample(StringInfo str, RangeTableSample *node);
static void _outWithDefinition(StringInfo str, List *def_list);
static void _outOnConflictClause(StringInfo str, OnConflictClause *node);


/*
 * Borrowed from backend/catalog/namespace.c
 * NameListToString
 *      Utility routine to convert a qualified-name list into a string.
 *
 * This is used primarily to form error messages, and so we do not quote
 * the list elements, for the sake of legibility.
 *
 * In most scenarios the list elements should always be Value strings,
 * but we also allow A_Star for the convenience of ColumnRef processing.
 */
char *
NameListToString(List *names)
{
	StringInfoData string;
	ListCell   *l;

	initStringInfo(&string);

	foreach(l, names)
	{
		Node	   *name = (Node *) lfirst(l);

		if (l != list_head(names))
			appendStringInfoChar(&string, '.');

		if (IsA(name, String))
			appendStringInfoString(&string, strVal(name));
		else if (IsA(name, A_Star))
			appendStringInfoString(&string, "*");
		else
			elog(ERROR, "unexpected node type in name list: %d",
				 (int) nodeTag(name));
	}

	return string.data;
}


static char *
escape_string(char *str)
{
	int			len = strlen(str),
				i,
				j;
	char	   *es = palloc0(len * 2 + 1);

	if (es == NULL)
	{
		return NULL;
	}

	for (i = 0, j = 0; i < len; i++, j++)
	{
		if (str[i] == '\'')
		{
			es[j++] = '\'';
		}
		else if (str[i] == '\\')
		{
			if (!standard_conforming_strings)
				es[j++] = '\\';
		}
		es[j] = str[i];
	}

	return es;
}

/* Assuming that node is a list of String */
static void
_outIdList(StringInfo str, List *node)
{
	ListCell   *lc;
	char		first = 0;

	foreach(lc, node)
	{
		if (first == 0)
			first = 1;
		else
			appendStringInfoString(str, ", ");

		appendStringInfoString(str, "\"");
		appendStringInfoString(str, strVal(lfirst(lc)));
		appendStringInfoString(str, "\"");
	}
}

static void
_outListWith(StringInfo str, List *node, char *op)
{
	ListCell   *lc;
	char		first = 0;

	foreach(lc, node)
	{
		if (first == 0)
			first = 1;
		else
		{
			if (lfirst(lc) != NIL && !IsA(lfirst(lc), A_Indices))
				appendStringInfoString(str, op);
		}
		_outNode(str, lfirst(lc));
	}
}

static void
_outList(StringInfo str, List *node)
{
	_outListWith(str, node, ",");
}



/*****************************************************************************
 *
 *	Stuff from primnodes.h.
 *
 *****************************************************************************/

/* for use by extensions which define extensible nodes */
void
outToken(StringInfo str, const char *s)
{
	if (s == NULL || *s == '\0')
	{
		appendStringInfoString(str, "<>");
		return;
	}

	/*
	 * Look for characters or patterns that are treated specially by read.c
	 * (either in pg_strtok() or in nodeRead()), and therefore need a
	 * protective backslash.
	 */
	/* These characters only need to be quoted at the start of the string */
	if (*s == '<' ||
		*s == '"' ||
		isdigit((unsigned char) *s) ||
		((*s == '+' || *s == '-') &&
		 (isdigit((unsigned char) s[1]) || s[1] == '.')))
		appendStringInfoChar(str, '\\');
	while (*s)
	{
		/* These chars must be backslashed anywhere in the string */
		if (*s == ' ' || *s == '\n' || *s == '\t' ||
			*s == '(' || *s == ')' || *s == '{' || *s == '}' ||
			*s == '\\')
			appendStringInfoChar(str, '\\');
		appendStringInfoChar(str, *s++);
	}
}

static void
_outSingleQuote(StringInfo str, const char *s)
{
	if (s == NULL || *s == '\0')
	{
		return;
	}

	while (*s)
	{
		if (*s == SINGLE_QUOTE)
			appendStringInfoChar(str, SINGLE_QUOTE);
		appendStringInfoChar(str, *s++);
	}
}

static void
_outAlias(StringInfo str, Alias *node)
{
	appendStringInfoString(str, " AS \"");
	appendStringInfoString(str, node->aliasname);
	appendStringInfoString(str, "\"");

	if (node->colnames)
	{
		appendStringInfoString(str, "(");
		_outNode(str, node->colnames);
		appendStringInfoString(str, ")");
	}
}

static void
_outRangeVar(StringInfo str, RangeVar *node)
{
	if (node->catalogname)
	{
		appendStringInfoString(str, "\"");
		appendStringInfoString(str, node->catalogname);
		appendStringInfoString(str, "\".");
	}

	if (node->schemaname)
	{
		appendStringInfoString(str, "\"");
		appendStringInfoString(str, node->schemaname);
		appendStringInfoString(str, "\".");
	}

	appendStringInfoString(str, "\"");
	appendStringInfoString(str, node->relname);
	appendStringInfoString(str, "\"");

	if (node->alias)
		_outNode(str, node->alias);
}

static void
_outVar(StringInfo str, Var *node)
{

}

static void
_outConst(StringInfo str, Const *node)
{

}

static void
_outParam(StringInfo str, Param *node)
{

}

static void
_outAggref(StringInfo str, Aggref *node)
{

}

static void
_outGroupingFunc(StringInfo str, GroupingFunc *node)
{
	appendStringInfoString(str, "GROUPING (");
	_outNode(str, node->args);
	appendStringInfoString(str, ")");

}

static void
_outWindowFuncRunCondition(StringInfo str, const WindowFuncRunCondition *node)
{
}

static void
_outMergeSupportFunc(StringInfo str, const MergeSupportFunc *node)
{

	if (IsA(node, MergeSupportFunc))
	{
		appendStringInfoString(str, "MERGE_ACTION()");
	}
}

static void
_outSubscriptingRef(StringInfo str, SubscriptingRef *node)
{
}

static void
_outJsonBehavior(StringInfo str, const JsonBehavior *node)
{
}

static void
_outJsonExpr(StringInfo str, const JsonExpr *node)
{

}

static void
_outJsonTablePath(StringInfo str, const JsonTablePath *node)
{
}

static void
_outJsonTablePathScan(StringInfo str, const JsonTablePathScan *node)
{
}

static void
_outJsonTableSiblingJoin(StringInfo str, const JsonTableSiblingJoin *node)
{
}

static void
_outFuncExpr(StringInfo str, FuncExpr *node)
{
}

static void
_outNamedArgExpr(StringInfo str, NamedArgExpr *node)
{
	appendStringInfoString(str, node->name);
	appendStringInfoString(str, " := ");
	_outNode(str, node->arg);
}

static void
_outOpExpr(StringInfo str, OpExpr *node)
{

}

static void
_outDistinctExpr(StringInfo str, DistinctExpr *node)
{

}

static void
_outScalarArrayOpExpr(StringInfo str, ScalarArrayOpExpr *node)
{

}

static void
_outBoolExpr(StringInfo str, BoolExpr *node)
{
	switch (node->boolop)
	{
		case AND_EXPR:
			appendStringInfoString(str, " (");
			_outListWith(str, node->args, " AND ");
			appendStringInfoString(str, ")");
			break;

		case OR_EXPR:
			appendStringInfoString(str, " (");
			_outListWith(str, node->args, " OR ");
			appendStringInfoString(str, ")");
			break;

		case NOT_EXPR:
			appendStringInfoString(str, " (NOT ");
			_outList(str, node->args);
			appendStringInfoString(str, ")");
			break;
	}
}

static void
_outSubLink(StringInfo str, SubLink *node)
{
	_outNode(str, node->testexpr);

	/*
	 * If the source was "x IN (select)", convert to "x = ANY (select)".
	 */
	if (node->subLinkType == ANY_SUBLINK && node->operName == NIL)
		node->operName = list_make1(makeString("="));

	if (node->operName != NIL)
	{
		String	   *v = linitial(node->operName);

		if (strcmp(strVal(v), "=") == 0)
			appendStringInfoString(str, " IN ");
		else
		{
			appendStringInfoString(str, strVal(v));
		}
	}

	switch (node->subLinkType)
	{
		case EXISTS_SUBLINK:
			appendStringInfoString(str, " EXISTS ");
			break;

		case ARRAY_SUBLINK:
			appendStringInfoString(str, " ARRAY ");
			break;

		case ANY_SUBLINK:
			if (node->operName != NIL)
			{
				String	   *v = linitial(node->operName);

				if (strcmp(strVal(v), "=") != 0)
				{
					appendStringInfoString(str, strVal(v));
					appendStringInfoString(str, " ANY ");
				}
			}
			break;

		case ALL_SUBLINK:
			appendStringInfoString(str, " ALL ");
			break;

		default:
			break;
	}


	if (node->subselect)
	{
		appendStringInfoString(str, "(");
		_outNode(str, node->subselect);
		appendStringInfoString(str, ")");
	}
}

static void
_outSubPlan(StringInfo str, SubPlan *node)
{

}

static void
_outFieldSelect(StringInfo str, FieldSelect *node)
{

}

static void
_outFieldStore(StringInfo str, FieldStore *node)
{

}

static void
_outRelabelType(StringInfo str, RelabelType *node)
{

}

static void
_outConvertRowtypeExpr(StringInfo str, ConvertRowtypeExpr *node)
{

}

static void
_outCaseExpr(StringInfo str, CaseExpr *node)
{
	ListCell   *lc;

	appendStringInfoString(str, "CASE ");
	if (node->arg)
		_outNode(str, node->arg);

	foreach(lc, node->args)
	{
		_outNode(str, lfirst(lc));
	}

	if (node->defresult)
	{
		appendStringInfoString(str, " ELSE ");
		_outNode(str, node->defresult);
	}

	appendStringInfoString(str, " END");
}

static void
_outCaseWhen(StringInfo str, CaseWhen *node)
{
	appendStringInfoString(str, " WHEN ");
	_outNode(str, node->expr);
	appendStringInfoString(str, " THEN ");
	_outNode(str, node->result);
}

static void
_outCaseTestExpr(StringInfo str, CaseTestExpr *node)
{

}

static void
_outArrayExpr(StringInfo str, ArrayExpr *node)
{
	appendStringInfoString(str, "[");
	_outNode(str, node->elements);
	appendStringInfoString(str, "]");
}

static void
_outRowExpr(StringInfo str, RowExpr *node)
{
	if (node->row_format == COERCE_EXPLICIT_CAST)
		appendStringInfoString(str, "ROW (");
	else
		appendStringInfoString(str, "(");

	if (node->args != NIL)
		_outNode(str, node->args);

	appendStringInfoString(str, ")");
}

static void
_outCoalesceExpr(StringInfo str, CoalesceExpr *node)
{
	appendStringInfoString(str, "COALESCE (");
	_outNode(str, node->args);
	appendStringInfoString(str, ")");
}

static void
_outMinMaxExpr(StringInfo str, MinMaxExpr *node)
{
	if (node->op == IS_GREATEST)
	{
		appendStringInfoString(str, "GREATEST (");
		_outNode(str, node->args);
		appendStringInfoString(str, ")");
	}
	else if (node->op == IS_LEAST)
	{
		appendStringInfoString(str, "LEAST (");
		_outNode(str, node->args);
		appendStringInfoString(str, ")");
	}
}

static void
_outNullIfExpr(StringInfo str, NullIfExpr *node)
{

}

static void
_outNullTest(StringInfo str, NullTest *node)
{
	_outNode(str, node->arg);
	if (node->nulltesttype == IS_NOT_NULL)
		appendStringInfoString(str, " IS NOT NULL");
	else
		appendStringInfoString(str, " IS NULL");
}

static void
_outBooleanTest(StringInfo str, BooleanTest *node)
{
	_outNode(str, node->arg);

	switch (node->booltesttype)
	{
		case IS_TRUE:
			appendStringInfoString(str, " IS TRUE ");
			break;

		case IS_NOT_TRUE:
			appendStringInfoString(str, " IS NOT TRUE ");
			break;

		case IS_FALSE:
			appendStringInfoString(str, " IS FALSE ");
			break;

		case IS_NOT_FALSE:
			appendStringInfoString(str, " IS NOT FALSE ");
			break;

		case IS_UNKNOWN:
			appendStringInfoString(str, " IS UNKNOWN ");
			break;

		case IS_NOT_UNKNOWN:
			appendStringInfoString(str, " IS NOT UNKNOWN ");
			break;
	}
}

static void
_outCoerceToDomain(StringInfo str, CoerceToDomain *node)
{

}

static void
_outCoerceToDomainValue(StringInfo str, CoerceToDomainValue *node)
{

}

static void
_outSetToDefault(StringInfo str, SetToDefault *node)
{
	appendStringInfoString(str, "DEFAULT");
}

static void
_outCurrentOfExpr(StringInfo str, CurrentOfExpr *node)
{
	appendStringInfoString(str, "CURRENT OF ");
	if (node->cursor_name == NULL)
	{
		char		n[10];

		snprintf(n, sizeof(n), "$%d", node->cursor_param);
		appendStringInfoString(str, n);
	}
	else
		appendStringInfoString(str, node->cursor_name);
}

static void
_outInferenceElem(StringInfo str, InferenceElem *node)
{

}

static void
_outReturningExpr(StringInfo str, ReturningExpr *node)
{

}

static void
_outTargetEntry(StringInfo str, TargetEntry *node)
{

}

static void
_outRangeTblRef(StringInfo str, RangeTblRef *node)
{

}

static void
_outJoinExpr(StringInfo str, JoinExpr *node)
{
	_outNode(str, node->larg);

	if (node->isNatural == TRUE)
		appendStringInfoString(str, " NATURAL");

	if (node->jointype == JOIN_INNER)
	{
		if (node->usingClause == NIL && node->quals == NULL && !node->isNatural)
			appendStringInfoString(str, " CROSS JOIN ");
		else
			appendStringInfoString(str, " JOIN ");
	}
	else if (node->jointype == JOIN_LEFT)
		appendStringInfoString(str, " LEFT OUTER JOIN ");
	else if (node->jointype == JOIN_FULL)
		appendStringInfoString(str, " FULL OUTER JOIN ");
	else if (node->jointype == JOIN_RIGHT)
		appendStringInfoString(str, " RIGHT OUTER JOIN ");

	_outNode(str, node->rarg);

	if (node->usingClause != NIL && IsA(node->usingClause, List))
	{
		ListCell   *lc;
		char		comma = 0;

		appendStringInfoString(str, " USING(");

		foreach(lc, node->usingClause)
		{
			if (comma == 0)
				comma = 1;
			else
				appendStringInfoString(str, ",");

			appendStringInfoString(str, "\"");
			appendStringInfoString(str, strVal(lfirst(lc)));
			appendStringInfoString(str, "\"");
		}

		appendStringInfoString(str, ")");

		if (node->join_using_alias)
			_outAlias(str, node->join_using_alias);

	}

	if (node->quals)
	{
		appendStringInfoString(str, " ON ");
		_outNode(str, node->quals);
	}
}

static void
_outFromExpr(StringInfo str, FromExpr *node)
{

}

static void
_outOnConflictExpr(StringInfo str, const OnConflictExpr *node)
{

}

#ifdef NOT_USED
/*****************************************************************************
 *
 *	Stuff from extensible.h
 *
 *****************************************************************************/

static void
_outExtensibleNode(StringInfo str, const ExtensibleNode * node)
{
	const		ExtensibleNodeMethods *methods;

	methods = GetExtensibleNodeMethods(node->extnodename, false);

	WRITE_NODE_TYPE("EXTENSIBLENODE");

	WRITE_STRING_FIELD(extnodename);

	/* serialize the private fields */
	methods->nodeOut(str, node);
}
#endif

/*****************************************************************************
 *
 *	Stuff from parsenodes.h.
 *
 *****************************************************************************/

static void
_outCreateStmt(StringInfo str, CreateStmt *node)
{
	appendStringInfoString(str, "CREATE ");

	if (node->relation->relpersistence == RELPERSISTENCE_TEMP)
		appendStringInfoString(str, "TEMP ");
	else if (node->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
		appendStringInfoString(str, "UNLOGGED ");

	appendStringInfoString(str, "TABLE ");

	if (node->if_not_exists)
		appendStringInfoString(str, "IF NOT EXISTS ");

	_outNode(str, node->relation);
	appendStringInfoString(str, " (");
	_outNode(str, node->tableElts);
	appendStringInfoString(str, ") ");

	if (node->inhRelations != NIL)
	{
		appendStringInfoString(str, "INHERITS (");
		_outNode(str, node->inhRelations);
		appendStringInfoString(str, ")");
	}

	if (node->accessMethod)
	{
		appendStringInfoString(str, " USING ");
		appendStringInfoString(str, node->accessMethod);
	}

	if (node->options)
		_outWithDefinition(str, node->options);

	switch (node->oncommit)
	{
		case ONCOMMIT_DROP:
			appendStringInfoString(str, " ON COMMIT DROP");
			break;

		case ONCOMMIT_DELETE_ROWS:
			appendStringInfoString(str, " ON COMMIT DELETE ROWS");
			break;

		case ONCOMMIT_PRESERVE_ROWS:
			appendStringInfoString(str, " ON COMMIT PRESERVE ROWS");
			break;

		default:
			break;
	}

	if (node->tablespacename)
	{
		appendStringInfoString(str, " TABLESPACE \"");
		appendStringInfoString(str, node->tablespacename);
		appendStringInfoString(str, "\"");
	}
}

static void
_outCreateTableAsStmt(StringInfo str, CreateTableAsStmt *node)
{
	appendStringInfoString(str, "CREATE ");

	if (node->into->rel->relpersistence == RELPERSISTENCE_TEMP)
		appendStringInfoString(str, "TEMP ");
	else if (node->into->rel->relpersistence == RELPERSISTENCE_UNLOGGED)
		appendStringInfoString(str, "UNLOGGED ");

	appendStringInfoString(str, "TABLE ");

	if (node->if_not_exists)
		appendStringInfoString(str, "IF NOT EXISTS ");

	_outNode(str, node->into->rel);

	if (node->into->colNames)
	{
		appendStringInfoString(str, " (");
		_outIdList(str, node->into->colNames);
		appendStringInfoString(str, ") ");
	}

	if (node->into->accessMethod)
	{
		appendStringInfoString(str, " USING ");
		appendStringInfoString(str, node->into->accessMethod);
	}

	if (node->into->options)
		_outWithDefinition(str, node->into->options);

	switch (node->into->onCommit)
	{
		case ONCOMMIT_DROP:
			appendStringInfoString(str, " ON COMMIT DROP");
			break;

		case ONCOMMIT_DELETE_ROWS:
			appendStringInfoString(str, " ON COMMIT DELETE ROWS");
			break;

		case ONCOMMIT_PRESERVE_ROWS:
			appendStringInfoString(str, " ON COMMIT PRESERVE ROWS");
			break;

		default:
			break;
	}

	if (node->into->tableSpaceName)
	{
		appendStringInfoString(str, " TABLESPACE \"");
		appendStringInfoString(str, node->into->tableSpaceName);
		appendStringInfoString(str, "\"");
	}

	if (node->query)
	{
		appendStringInfoString(str, " AS");
		_outSelectStmt(str, (SelectStmt *) node->query);
	}

	if (node->into->skipData)
	{
		if (node->into->skipData == TRUE)
			appendStringInfoString(str, " WITH NO DATA");
		else
			appendStringInfoString(str, " WITH DATA");
	}
}

static void
_outCreateForeignTableStmt(StringInfo str, CreateForeignTableStmt *node)
{

}

static void
_outImportForeignSchemaStmt(StringInfo str, ImportForeignSchemaStmt *node)
{

}

static void
_outIndexStmt(StringInfo str, IndexStmt *node)
{
	appendStringInfoString(str, "CREATE ");

	if (node->unique == TRUE)
		appendStringInfoString(str, "UNIQUE ");

	if (node->concurrent == true)
		appendStringInfoString(str, "INDEX CONCURRENTLY ");
	else
		appendStringInfoString(str, "INDEX ");
	if (node->idxname)
	{
		appendStringInfoString(str, "\"");
		appendStringInfoString(str, node->idxname);
		appendStringInfoString(str, "\" ");
	}
	appendStringInfoString(str, "ON ");
	_outNode(str, node->relation);

	if (strcmp(node->accessMethod, DEFAULT_INDEX_TYPE))
	{
		appendStringInfoString(str, " USING ");
		appendStringInfoString(str, node->accessMethod);
	}

	appendStringInfoString(str, "(");
	_outNode(str, node->indexParams);
	appendStringInfoString(str, ")");

	if (node->tableSpace)
	{
		appendStringInfoString(str, " TABLESPACE \"");
		appendStringInfoString(str, node->tableSpace);
		appendStringInfoString(str, "\"");
	}

	if (node->whereClause)
	{
		appendStringInfoString(str, " WHERE ");
		_outNode(str, node->whereClause);
	}
}

static void
_outCreateStatsStmt(StringInfo str, CreateStatsStmt *node)
{
}

static void
_outAlterStatsStmt(StringInfo str, const AlterStatsStmt *node)
{
}

static void
_outNotifyStmt(StringInfo str, NotifyStmt *node)
{
	appendStringInfoString(str, "NOTIFY ");
	appendStringInfoString(str, "\"");
	appendStringInfoString(str, node->conditionname);
	appendStringInfoString(str, "\"");
}

static void
_outDeclareCursorStmt(StringInfo str, DeclareCursorStmt *node)
{
	appendStringInfoString(str, "DECLARE \"");
	appendStringInfoString(str, node->portalname);
	appendStringInfoString(str, "\" ");

	if (node->options & CURSOR_OPT_SCROLL)
		appendStringInfoString(str, "SCROLL ");
	if (node->options & CURSOR_OPT_BINARY)
		appendStringInfoString(str, "BINARY ");
	if (node->options & CURSOR_OPT_INSENSITIVE)
		appendStringInfoString(str, "INSENSITIVE ");

	appendStringInfoString(str, "CURSOR ");
	if (node->options & CURSOR_OPT_HOLD)
		appendStringInfoString(str, "WITH HOLD ");
	appendStringInfoString(str, "FOR");
	_outNode(str, node->query);
}

static void
_outSelectStmt(StringInfo str, SelectStmt *node)
{
	if (node->larg)				/* SETOP */
	{
		appendStringInfoString(str, "(");
		_outNode(str, node->larg);
		appendStringInfoString(str, ") ");

		switch (node->op)
		{
			case SETOP_UNION:
				appendStringInfoString(str, " UNION ");
				break;

			case SETOP_INTERSECT:
				appendStringInfoString(str, " INTERSECT ");
				break;

			case SETOP_EXCEPT:
				appendStringInfoString(str, " EXCEPT ");

			default:
				break;
		}

		if (node->all)
			appendStringInfoString(str, "ALL ");

		if (node->rarg)
		{
			appendStringInfoString(str, "(");
			_outNode(str, node->rarg);
			appendStringInfoString(str, ") ");
		}
	}
	else if (node->valuesLists) /* VALUES ... */
	{
		ListCell   *lc;
		int			comma = 0;

		appendStringInfoString(str, " VALUES");
		foreach(lc, node->valuesLists)
		{
			if (comma == 0)
				comma = 1;
			else
				appendStringInfoString(str, ",");

			appendStringInfoString(str, " (");
			_outNode(str, lfirst(lc));
			appendStringInfoString(str, ")");
		}
	}
	else
	{
		if (node->intoClause)
		{
			IntoClause *into = (IntoClause *) node->intoClause;
			RangeVar   *rel = (RangeVar *) into->rel;

			appendStringInfoString(str, "CREATE ");
			if (rel->relpersistence == RELPERSISTENCE_TEMP)
				appendStringInfoString(str, "TEMP ");
			else if (rel->relpersistence == RELPERSISTENCE_UNLOGGED)
				appendStringInfoString(str, "UNLOGGED ");
			appendStringInfoString(str, "TABLE ");
			_outNode(str, into->rel);

			if (into->colNames)
			{
				appendStringInfoString(str, " (");
				_outNode(str, into->colNames);
				appendStringInfoString(str, ") ");
			}

			if (into->options)
				_outWithDefinition(str, into->options);

			switch (into->onCommit)
			{
				case ONCOMMIT_DROP:
					appendStringInfoString(str, " ON COMMIT DROP");
					break;

				case ONCOMMIT_DELETE_ROWS:
					appendStringInfoString(str, " ON COMMIT DELETE ROWS");
					break;

				case ONCOMMIT_PRESERVE_ROWS:
					appendStringInfoString(str, " ON COMMIT PRESERVE ROWS");
					break;

				default:
					break;
			}

			appendStringInfoString(str, " AS");
		}

		if (node->withClause)
			_outWithClause(str, node->withClause);

		appendStringInfoString(str, " SELECT ");

		if (node->distinctClause)
		{
			appendStringInfoString(str, "DISTINCT ");
			if (lfirst(list_head(node->distinctClause)) != NIL)
			{
				appendStringInfoString(str, "ON (");
				_outNode(str, node->distinctClause);
				appendStringInfoString(str, " ) ");
			}
		}

		_outNode(str, node->targetList);

		if (node->fromClause)
		{
			appendStringInfoString(str, " FROM ");
			_outNode(str, node->fromClause);
		}

		if (node->whereClause)
		{
			appendStringInfoString(str, " WHERE ");
			_outNode(str, node->whereClause);
		}

		if (node->groupClause)
		{
			appendStringInfoString(str, " GROUP BY ");

			if (node->groupDistinct)
				appendStringInfoString(str, "DISTINCT ");

			_outNode(str, node->groupClause);
		}

		if (node->havingClause)
		{
			appendStringInfoString(str, " HAVING ");
			_outNode(str, node->havingClause);
		}

		if (node->windowClause)
		{
			appendStringInfoString(str, " WINDOW ");
			_outNode(str, node->windowClause);
		}
	}

	if (node->sortClause)
	{
		appendStringInfoString(str, " ORDER BY ");
		_outNode(str, node->sortClause);
	}

	if (node->limitOffset)
	{
		appendStringInfoString(str, " OFFSET ");
		_outNode(str, node->limitOffset);
	}

	if (node->limitCount)
	{
		appendStringInfoString(str, " LIMIT ");
		if (IsA(node->limitCount, A_Const) &&
			((A_Const *) node->limitCount)->isnull)
		{
			appendStringInfoString(str, "ALL ");
		}
		else
		{
			_outNode(str, node->limitCount);
		}
	}

	_outNode(str, node->lockingClause);
}

static void
_outFuncCall(StringInfo str, FuncCall *node)
{
	char	   *funcname;

	_outFuncName(str, node->funcname);

	funcname = strVal(lfirst(list_head(node->funcname)));

	if (strcmp(funcname, "user") == 0 ||
		strcmp(funcname, "current_user") == 0 ||
		strcmp(funcname, "session_user") == 0 ||
		strcmp(funcname, "current_role") == 0)
		return;

	appendStringInfoString(str, "(");

	if (node->func_variadic == TRUE)
		appendStringInfoString(str, "VARIADIC ");

	if (node->agg_distinct == TRUE)
		appendStringInfoString(str, "DISTINCT ");

	if (node->agg_star == TRUE)
		appendStringInfoString(str, "*");
	else
		_outNode(str, node->args);

	if (node->agg_order != NIL)
	{
		appendStringInfoString(str, " ORDER BY ");
		_outNode(str, node->agg_order);
	}

	appendStringInfoString(str, ")");

	if (node->over)
	{
		appendStringInfoString(str, " OVER ");
		if (node->over->name)
		{
			appendStringInfoString(str, "\"");
			appendStringInfoString(str, node->over->name);
			appendStringInfoString(str, "\"");
		}
		else
			_outWindowDef(str, node->over);
	}
}

static void
_outDefElem(StringInfo str, DefElem *node)
{

}

static void
_outLockingClause(StringInfo str, LockingClause *node)
{
	if (node == NULL)
		return;

	switch (node->strength)
	{
		case LCS_FORKEYSHARE:
			appendStringInfoString(str, " FOR KEY SHARE");
			break;
		case LCS_FORSHARE:
			appendStringInfoString(str, " FOR SHARE");
			break;
		case LCS_FORNOKEYUPDATE:
			appendStringInfoString(str, " FOR NO KEY UPDATE");
			break;
		case LCS_FORUPDATE:
			appendStringInfoString(str, " FOR UPDATE");
			break;
		case LCS_NONE:
			break;
	}

	if (node->lockedRels)
	{
		appendStringInfoString(str, " OF ");
		_outNode(str, node->lockedRels);
	}

	switch (node->waitPolicy)
	{
		case LockWaitError:
			appendStringInfoString(str, " NOWAIT");
			break;
		case LockWaitSkip:
			appendStringInfoString(str, " SKIP LOCKED");
			break;
		case LockWaitBlock:
			break;
	}
}

static void
_outTriggerTransition(StringInfo str, TriggerTransition *node)
{
}

static void
_outJsonArgument(StringInfo str, const JsonArgument *node)
{
}

static void
_outJsonFuncExpr(StringInfo str, const JsonFuncExpr *node)
{
}

static void
_outJsonTablePathSpec(StringInfo str, const JsonTablePathSpec *node)
{
}

static void
_outJsonTable(StringInfo str, const JsonTable *node)
{
}

static void
_outJsonTableColumn(StringInfo str, const JsonTableColumn *node)
{
}

static void
_outReturnStmt(StringInfo str, ReturnStmt *node)
{
}

static void
_outPLAssignStmt(StringInfo str, PLAssignStmt *node)
{
}

static void
_outColumnDef(StringInfo str, ColumnDef *node)
{
	appendStringInfoString(str, "\"");
	appendStringInfoString(str, node->colname);
	appendStringInfoString(str, "\" ");
	_outNode(str, node->typeName);
	_outNode(str, node->constraints);
}

static void
_outTypeName(StringInfo str, TypeName *node)
{

	/*
	 * don't quote SystemType name, because 1. char is not "char". 2. in 8.4,
	 * interval with fields cause error. =# SELECT '1'::"interval" year;
	 * ERROR:  syntax error at or near "year" LINE 1: SELECT '1'::"interval"
	 * year;
	 */
	if (list_length(node->names) == 2 &&
		strcmp("pg_catalog", strVal(linitial(node->names))) == 0)
	{
		appendStringInfoString(str, strVal(lsecond(node->names)));

		if (strcmp("interval", strVal(lsecond(node->names))) == 0)
		{
			if (node->typmods != NIL)
			{
				A_Const    *v = (A_Const *) linitial(node->typmods);
				int			mask = v->val.ival.ival;

				/*
				 * precision for SECOND field. backward compatibility. use
				 * `'1.2 second'::interval(0) second' not `'1.2
				 * second'::interval second(0)'(standard for 8.4).
				 */
				if ((INTERVAL_MASK(SECOND) & mask) &&
					list_length(node->typmods) == 2)
				{
					appendStringInfoString(str, "(");
					_outA_Const(str, lsecond(node->typmods));
					appendStringInfoString(str, ")");
				}

				/* optional fields */
				if (mask == INTERVAL_MASK(YEAR))
					appendStringInfoString(str, " YEAR");
				else if (mask == INTERVAL_MASK(MONTH))
					appendStringInfoString(str, " MONTH");
				else if (mask == INTERVAL_MASK(DAY))
					appendStringInfoString(str, " DAY");
				else if (mask == INTERVAL_MASK(HOUR))
					appendStringInfoString(str, " HOUR");
				else if (mask == INTERVAL_MASK(MINUTE))
					appendStringInfoString(str, " MINUTE");
				else if (mask == INTERVAL_MASK(SECOND))
					appendStringInfoString(str, " SECOND");
				else if (mask == (INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH)))
					appendStringInfoString(str, " YEAR TO MONTH");
				else if (mask == (INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR)))
					appendStringInfoString(str, " DAY TO HOUR");
				else if (mask == (INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) |
								  INTERVAL_MASK(MINUTE)))
					appendStringInfoString(str, " DAY TO MINUTE");
				else if (mask == (INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) |
								  INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND)))
					appendStringInfoString(str, " DAY TO SECOND");
				else if (mask == (INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE)))
					appendStringInfoString(str, " HOUR TO MINUTE");
				else if (mask == (INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) |
								  INTERVAL_MASK(SECOND)))
					appendStringInfoString(str, " HOUR TO SECOND");
				else if (mask == (INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND)))
					appendStringInfoString(str, " MINUTE TO SECOND");
			}

			return;
		}
	}
	else
	{
		ListCell   *lc;
		char		dot = 0;

		foreach(lc, node->names)
		{
			String	   *s = (String *) lfirst(lc);
			char	   *typename = s->sval;

			if (dot == 0)
				dot = 1;
			else
				appendStringInfoString(str, ".");
			if (node->typemod < 0)
			{
				appendStringInfoString(str, "\"");
				appendStringInfoString(str, typename);
				appendStringInfoString(str, "\"");
			}
			else
				appendStringInfoString(str, typename);
		}
	}

	/* precisions */
	if (node->typmods)
	{
		appendStringInfoString(str, "(");
		_outList(str, node->typmods);
		appendStringInfoString(str, ")");
	}

	if (node->arrayBounds != NIL)
	{
		ListCell   *lc;

		foreach(lc, node->arrayBounds)
		{
			if (intVal(lfirst(lc)) == -1)
				appendStringInfoString(str, "[]");
			else
			{
				appendStringInfoString(str, "[");
				_outNode(str, lfirst(lc));
				appendStringInfoString(str, "]");
			}
		}
	}
}

static void
_outTypeCast(StringInfo str, TypeCast *node)
{
	_outNode(str, node->arg);
	appendStringInfoString(str, "::");
	_outNode(str, node->typeName);

}

static void
_outCollateClause(StringInfo str, const CollateClause *node)
{
	if (node->arg)
		_outNode(str, node->arg);
	appendStringInfoString(str, " COLLATE ");
	appendStringInfoString(str, "\"");
	/* XXX collation name can be schema qualified */
	appendStringInfoString(str, strVal(linitial(node->collname)));
	appendStringInfoString(str, "\"");
}

static void
_outIndexElem(StringInfo str, IndexElem *node)
{
	if (node->name)
	{
		appendStringInfoString(str, "\"");
		appendStringInfoString(str, node->name);
		appendStringInfoString(str, "\"");
		if (node->opclass != NIL)
			_outNode(str, node->opclass);
	}
	else
	{
		appendStringInfoString(str, "(");
		_outNode(str, node->expr);
		appendStringInfoString(str, ")");
		if (node->opclass != NIL)
			_outNode(str, node->opclass);
	}
}

static void
_outGroupingSet(StringInfo str, GroupingSet *node)
{
	switch (node->kind)
	{
		case GROUPING_SET_EMPTY:
			appendStringInfoString(str, " (");
			break;
		case GROUPING_SET_ROLLUP:
			appendStringInfoString(str, " ROLLUP (");
			_outNode(str, node->content);
			break;
		case GROUPING_SET_CUBE:
			appendStringInfoString(str, " CUBE (");
			_outNode(str, node->content);
			break;
		case GROUPING_SET_SETS:
			appendStringInfoString(str, " GROUPING SETS (");
			_outNode(str, node->content);
			break;
		default:
			break;
	}

	appendStringInfoString(str, " )");

}

static void
_outWithClause(StringInfo str, WithClause *node)
{
	appendStringInfoString(str, " WITH ");
	if (node->recursive)
		appendStringInfoString(str, "RECURSIVE ");

	_outList(str, node->ctes);
}

static void
_outCTESearchClause(StringInfo str, CTESearchClause *node)
{
	if (node->search_breadth_first)
		appendStringInfoString(str, " SEARCH BREADTH FIRST BY ");
	else
		appendStringInfoString(str, " SEARCH DEPTH FIRST BY ");

	_outList(str, node->search_col_list);

	if (node->search_seq_column)
	{
		appendStringInfoString(str, " SET ");
		appendStringInfoString(str, "\"");
		appendStringInfoString(str, node->search_seq_column);
		appendStringInfoString(str, "\"");
	}
}

static void
_outCTECycleClause(StringInfo str, CTECycleClause *node)
{
	appendStringInfoString(str, " CYCLE ");
	_outList(str, node->cycle_col_list);

	if (node->cycle_mark_column)
	{
		appendStringInfoString(str, " SET ");
		appendStringInfoString(str, "\"");
		appendStringInfoString(str, node->cycle_mark_column);
		appendStringInfoString(str, "\"");
	}

	if (node->cycle_mark_value && node->cycle_mark_default)
	{
		appendStringInfoString(str, " TO ");
		_outNode(str, node->cycle_mark_value);
		appendStringInfoString(str, " DEFAULT ");
		_outNode(str, node->cycle_mark_default);
	}

	if (node->cycle_path_column)
	{
		appendStringInfoString(str, " USING ");
		appendStringInfoString(str, "\"");
		appendStringInfoString(str, node->cycle_path_column);
		appendStringInfoString(str, "\"");
	}
}

static void
_outCommonTableExpr(StringInfo str, CommonTableExpr *node)
{
	appendStringInfoString(str, "\"");
	appendStringInfoString(str, node->ctename);
	appendStringInfoString(str, "\" ");

	if (node->aliascolnames)
	{
		appendStringInfoString(str, "(");
		_outIdList(str, node->aliascolnames);
		appendStringInfoString(str, ") ");
	}

	appendStringInfoString(str, "AS ");

	if (node->ctematerialized & CTEMaterializeAlways)
		appendStringInfoString(str, "MATERIALIZED ");
	else if (node->ctematerialized & CTEMaterializeNever)
		appendStringInfoString(str, "NOT MATERIALIZED ");

	appendStringInfoString(str, "(");
	_outNode(str, node->ctequery);
	appendStringInfoString(str, ")");

	if (node->search_clause)
		_outCTESearchClause(str, node->search_clause);

	if (node->cycle_clause)
		_outCTECycleClause(str, node->cycle_clause);
}

static void
_outMergeWhenClauses(StringInfo str, List *node)
{
	ListCell   *temp;
	char		comma;

	foreach(temp, node)
	{
		MergeWhenClause *m = (MergeWhenClause *) lfirst(temp);

		if (m->matchKind == MERGE_WHEN_MATCHED)
			appendStringInfoString(str, " WHEN MATCHED ");
		else if (m->matchKind == MERGE_WHEN_NOT_MATCHED_BY_SOURCE)
			appendStringInfoString(str, " WHEN NOT MATCHED BY SOURCE ");
		else
			appendStringInfoString(str, " WHEN NOT MATCHED BY TARGET ");

		if (m->condition)
		{
			appendStringInfoString(str, "AND ");
			_outNode(str, m->condition);
		}

		appendStringInfoString(str, "THEN ");

		switch (m->commandType)
		{
				ListCell   *s;

			case CMD_UPDATE:
				comma = 0;
				appendStringInfo(str, "UPDATE SET ");
				foreach(s, m->targetList)
				{
					ResTarget  *r = (ResTarget *) lfirst(s);

					if (comma == 0)
						comma = 1;
					else
						appendStringInfoString(str, ", ");

					appendStringInfo(str, "\"%s\" = ", r->name);
					_outNode(str, r->val);
				}
				break;
			case CMD_INSERT:
				appendStringInfo(str, "INSERT ");
				if (m->targetList)
				{
					comma = 0;
					appendStringInfoString(str, "(");
					foreach(s, m->targetList)
					{
						ResTarget  *r = (ResTarget *) lfirst(s);

						if (comma == 0)
							comma = 1;
						else
							appendStringInfoString(str, ", ");

						appendStringInfo(str, "\"%s\"", r->name);

					}
					appendStringInfoString(str, ") ");
				}

				if (m->override == OVERRIDING_SYSTEM_VALUE)
					appendStringInfoString(str, "OVERRIDING SYSTEM VALUE ");
				else if (m->override == OVERRIDING_USER_VALUE)
					appendStringInfoString(str, "OVERRIDING USER VALUE ");

				if (m->values)
				{
					comma = 0;
					appendStringInfo(str, "VALUES ");
					appendStringInfoString(str, "(");

					foreach(s, m->values)
					{
						if (comma == 0)
							comma = 1;
						else
							appendStringInfoString(str, ", ");

						_outNode(str, lfirst(s));
					}
					appendStringInfoString(str, ")");
				}
				break;
			case CMD_DELETE:
				appendStringInfo(str, "DELETE ");
				break;
			case CMD_NOTHING:
				appendStringInfo(str, "DO NOTHING ");
				break;
			default:
				break;
		}
	}
}

static void
_outMergeAction(StringInfo str, MergeAction *node)
{
}

static void
_outReturningOption(StringInfo str, List *node)
{
	ListCell   *lc;
	char		comma = 0;

	appendStringInfoString(str, "WITH (");

	foreach(lc, node)
	{
		ReturningOption *r = (ReturningOption *) lfirst(lc);

		if (comma == 0)
			comma = 1;
		else
			appendStringInfoString(str, ", ");

		if (r->option == RETURNING_OPTION_OLD)
			appendStringInfoString(str, "OLD ");
		else if (r->option == RETURNING_OPTION_NEW)
			appendStringInfoString(str, "NEW ");

		appendStringInfoString(str, "AS ");
		appendStringInfoString(str, "\"");
		appendStringInfoString(str, r->value);
		appendStringInfoString(str, "\"");
	}

	appendStringInfoString(str, ") ");
}

static void
_outReturningClause(StringInfo str, ReturningClause *node)
{

	if (node->options)
		_outReturningOption(str, node->options);

	if (node->exprs)
		_outList(str, node->exprs);
}

static void
_outSetOperationStmt(StringInfo str, SetOperationStmt *node)
{

}


static void
_outTableSampleClause(StringInfo str, TableSampleClause *node)
{

}

static void
_outA_Expr(StringInfo str, A_Expr *node)
{
	String	   *s;

	switch (node->kind)
	{
		case AEXPR_OP:
			if (list_length(node->name) == 1)
			{
				/*
				 * From PostgreSQL 15, node->name is type of String, rather
				 * than A_Const because "Value" struct has gone.
				 * https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=639a86e36aaecb84faaf941dcd0b183ba0aba9e9
				 */

				String	   *op = (String *) lfirst(list_head(node->name));

				appendStringInfoString(str, " (");
				_outNode(str, node->lexpr);
				appendStringInfoString(str, " ");
				appendStringInfoString(str, strVal(op));
				appendStringInfoString(str, " ");
				_outNode(str, node->rexpr);
				appendStringInfoString(str, " )");
			}
			break;

		case AEXPR_OP_ANY:
			_outNode(str, node->lexpr);
			s = linitial(node->name);
			appendStringInfoString(str, strVal(s));
			appendStringInfoString(str, "ANY(");
			_outNode(str, node->rexpr);
			appendStringInfoString(str, ")");
			break;

		case AEXPR_OP_ALL:
			_outNode(str, node->lexpr);
			s = linitial(node->name);
			appendStringInfoString(str, strVal(s));
			appendStringInfoString(str, "ALL(");
			_outNode(str, node->rexpr);
			appendStringInfoString(str, ")");
			break;

		case AEXPR_DISTINCT:
			appendStringInfoString(str, " (");
			_outNode(str, node->lexpr);
			appendStringInfoString(str, " IS DISTINCT FROM ");
			_outNode(str, node->rexpr);
			appendStringInfoString(str, ")");
			break;

		case AEXPR_NOT_DISTINCT:
			appendStringInfoString(str, " (");
			_outNode(str, node->lexpr);
			appendStringInfoString(str, " IS NOT DISTINCT FROM ");
			_outNode(str, node->rexpr);
			appendStringInfoString(str, ")");
			break;

		case AEXPR_NULLIF:
			appendStringInfoString(str, " NULLIF(");
			_outNode(str, node->lexpr);
			appendStringInfoString(str, ", ");
			_outNode(str, node->rexpr);
			appendStringInfoString(str, ")");
			break;

		case AEXPR_IN:
			_outNode(str, node->lexpr);
			s = (String *) lfirst(list_head(node->name));
			if (*strVal(s) == '=')
				appendStringInfoString(str, " IN (");
			else
				appendStringInfoString(str, " NOT IN (");
			_outNode(str, node->rexpr);
			appendStringInfoString(str, ")");
			break;

		case AEXPR_LIKE:
			_outNode(str, node->lexpr);
			s = lfirst(list_head(node->name));
			if (!strcmp(strVal(s), "~~"))
				appendStringInfoString(str, " LIKE ");
			else
				appendStringInfoString(str, " NOT LIKE ");
			if (IsA(node->rexpr, FuncCall))
			{
				FuncCall   *rexpr = (FuncCall *) node->rexpr;

				_outNode(str, linitial(rexpr->args));
				appendStringInfoString(str, " ESCAPE ");
				_outNode(str, lsecond(rexpr->args));
			}
			else
				_outNode(str, node->rexpr);
			break;

		case AEXPR_ILIKE:
			_outNode(str, node->lexpr);
			s = lfirst(list_head(node->name));
			if (!strcmp(strVal(s), "~~*"))
				appendStringInfoString(str, " ILIKE ");
			else
				appendStringInfoString(str, " NOT ILIKE ");
			if (IsA(node->rexpr, FuncCall))
			{
				FuncCall   *rexpr = (FuncCall *) node->rexpr;

				_outNode(str, linitial(rexpr->args));
				appendStringInfoString(str, " ESCAPE ");
				_outNode(str, lsecond(rexpr->args));
			}
			else
				_outNode(str, node->rexpr);
			break;

		case AEXPR_SIMILAR:
			_outNode(str, node->lexpr);
			s = lfirst(list_head(node->name));
			if (!strcmp(strVal(s), "~"))
				appendStringInfoString(str, " SIMILAR TO ");
			else
				appendStringInfoString(str, " NOT SIMILAR TO ");
			if (IsA(node->rexpr, FuncCall))
			{
				FuncCall   *rexpr = (FuncCall *) node->rexpr;

				_outNode(str, linitial(rexpr->args));
				appendStringInfoString(str, " ESCAPE ");
				_outNode(str, lsecond(rexpr->args));
			}
			else
				_outNode(str, node->rexpr);
			break;

		case AEXPR_BETWEEN:
			_outNode(str, node->lexpr);
			appendStringInfoString(str, " BETWEEN ");
			_outNode(str, linitial((List *) node->rexpr));
			appendStringInfoString(str, " AND ");
			_outNode(str, lsecond((List *) node->rexpr));
			break;

		case AEXPR_NOT_BETWEEN:
			_outNode(str, node->lexpr);
			appendStringInfoString(str, " NOT BETWEEN ");
			_outNode(str, linitial((List *) node->rexpr));
			appendStringInfoString(str, " AND ");
			_outNode(str, lsecond((List *) node->rexpr));
			break;

		case AEXPR_BETWEEN_SYM:
			_outNode(str, node->lexpr);
			appendStringInfoString(str, " BETWEEN SYMMETRIC ");
			_outNode(str, linitial((List *) node->rexpr));
			appendStringInfoString(str, " AND ");
			_outNode(str, lsecond((List *) node->rexpr));
			break;

		case AEXPR_NOT_BETWEEN_SYM:
			_outNode(str, node->lexpr);
			appendStringInfoString(str, " NOT BETWEEN SYMMETRIC ");
			_outNode(str, linitial((List *) node->rexpr));
			appendStringInfoString(str, " AND ");
			_outNode(str, lsecond((List *) node->rexpr));
			break;

		default:
			break;
	}
}

static void
_outInteger(StringInfo str, const Integer *node)
{
	appendStringInfo(str, "%d", node->ival);
}

static void
_outFloat(StringInfo str, const Float *node)
{
	/*
	 * * We assume the value is a valid numeric literal and so does not need *
	 * quoting.
	 */
	appendStringInfoString(str, node->fval);
}

static void
_outBoolean(StringInfo str, const Boolean *node)
{
	appendStringInfoString(str, node->boolval ? "true" : "false");
}

static void
_outString(StringInfo str, const String *node)
{
	/*
	 * We use outToken to provide escaping of the string's content, but we
	 * don't want it to do anything with an empty string.
	 */
	appendStringInfoChar(str, '"');
	if (node->sval[0] != '\0')
		outToken(str, node->sval);
	appendStringInfoChar(str, '"');
}

static void
_outBitString(StringInfo str, const BitString *node)
{
	/*
	 * The lexer will always produce a string starting with 'b' or 'x'.  There
	 * might be characters following that that need escaping, but outToken
	 * won't escape the 'b' or 'x'.  This is relied on by nodeTokenType.
	 */
	Assert(node->bsval[0] == 'b' || node->bsval[0] == 'x');
	outToken(str, node->bsval);
}

static void
_outBitmapset(StringInfo str, const Bitmapset *bms)
{
}

/*
 * Node types found in raw parse trees (supported for debug purposes)
 */

static void
_outRawStmt(StringInfo str, const RawStmt *node)
{
}

static void
_outColumnRef(StringInfo str, ColumnRef *node)
{
	ListCell   *c;
	char		first = 0;

	foreach(c, node->fields)
	{
		Node	   *n = (Node *) lfirst(c);

		if (IsA(n, String))
		{
			String	   *v = (String *) lfirst(c);

			if (first == 0)
				first = 1;
			else
				appendStringInfoString(str, ".");

			appendStringInfoString(str, "\"");
			appendStringInfoString(str, strVal(v));
			appendStringInfoString(str, "\"");
		}
		else if (IsA(n, A_Star))
		{
			if (first == 0)
				first = 1;
			else
				appendStringInfoString(str, ".");

			appendStringInfoString(str, "*");
		}
	}
}

static void
_outParamRef(StringInfo str, ParamRef *node)
{
	char		buf[16];

	snprintf(buf, 16, "%d", node->number);
	appendStringInfoString(str, "$");
	appendStringInfoString(str, buf);
}

static void
_outA_Const(StringInfo str, A_Const *node)
{
	char	   *p;

	if (node->isnull)
	{
		appendStringInfoString(str, " NULL");
		return;
	}

	switch (nodeTag(&node->val))
	{
		case T_Integer:
			_outInteger(str, &node->val.ival);
			break;

		case T_Float:
			_outFloat(str, &node->val.fval);
			break;

		case T_String:
			appendStringInfoString(str, "'");
			p = escape_string(node->val.sval.sval);
			appendStringInfoString(str, p);
			pfree(p);
			appendStringInfoString(str, "'");
			break;

		case T_BitString:
			_outBitString(str, &node->val.bsval);
			break;

		case T_Boolean:
			_outBoolean(str, &node->val.boolval);
			break;

		default:
			elog(ERROR, "unrecognized A_Const kind: %d", nodeTag(&node->val));
			break;
	}
}

static void
_outA_Indices(StringInfo str, A_Indices *node)
{
	appendStringInfoString(str, "[");
	if (node->lidx)
	{
		_outNode(str, node->lidx);
		appendStringInfoString(str, ":");
	}
	_outNode(str, node->uidx);
	appendStringInfoString(str, "]");
}

static void
_outA_Indirection(StringInfo str, A_Indirection *node)
{
	ListCell   *lc;

	if (node->indirection != NIL)
	{
		if (IsA(node->arg, ParamRef))
			/* "$1[1]" OR "$1.foo" */
			_outParamRef(str, (ParamRef *) node->arg);
		else
		{
			/* "(ARRAY[1])[1]" */
			appendStringInfoString(str, "(");
			_outNode(str, node->arg);
			appendStringInfoString(str, ")");
		}

		foreach(lc, node->indirection)
		{
			Node	   *ind = lfirst(lc);

			if (IsA(ind, A_Star))
				/* foo.* */
				appendStringInfoString(str, ".*");
			else if (IsA(ind, String))
			{
				/* foo.bar */
				appendStringInfoString(str, ".\"");
				appendStringInfoString(str, strVal(ind));
				appendStringInfoString(str, "\"");
			}
			else
				/* foo[1] (A_Indices) */
				_outNode(str, ind);
		}
	}
}

static void
_outA_ArrayExpr(StringInfo str, A_ArrayExpr *node)
{
	appendStringInfoString(str, "ARRAY [");
	_outNode(str, node->elements);
	appendStringInfoString(str, "]");
}

static void
_outResTarget(StringInfo str, ResTarget *node)
{
	if (node->indirection != NIL)
	{
		appendStringInfoString(str, "\"");
		appendStringInfoString(str, node->name);
		appendStringInfoString(str, "\"=");
		_outNode(str, node->val);
	}
	else
	{
		_outNode(str, node->val);

		if (node->name)
		{
			appendStringInfoString(str, " AS ");
			appendStringInfoString(str, "\"");
			appendStringInfoString(str, node->name);
			appendStringInfoString(str, "\" ");
		}
	}
}


static void
_outMultiAssignRef(StringInfo str, MultiAssignRef *node)
{
	_outNode(str, node->source);
}

static void
_outWindowDef(StringInfo str, WindowDef *node)
{
	if (node->name)
	{
		appendStringInfoString(str, "\"");
		appendStringInfoString(str, node->name);
		appendStringInfoString(str, "\" AS ");
	}
	appendStringInfoString(str, "(");

	if (node->refname)
	{
		appendStringInfoString(str, "\"");
		appendStringInfoString(str, node->refname);
		appendStringInfoString(str, "\" ");
	}

	if (node->partitionClause)
	{
		appendStringInfoString(str, " PARTITION BY ");
		_outNode(str, node->partitionClause);
	}

	if (node->orderClause)
	{
		appendStringInfoString(str, " ORDER BY ");
		_outNode(str, node->orderClause);
	}

	if (node->frameOptions != FRAMEOPTION_DEFAULTS)
	{
		if (node->frameOptions & FRAMEOPTION_RANGE)
			appendStringInfoString(str, " RANGE");
		else if (node->frameOptions & FRAMEOPTION_ROWS)
			appendStringInfoString(str, " ROWS");
		else if (node->frameOptions & FRAMEOPTION_GROUPS)
			appendStringInfoString(str, " GROUPS");

		if (node->frameOptions & FRAMEOPTION_BETWEEN)
			appendStringInfoString(str, " BETWEEN");

		if (node->frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
			appendStringInfoString(str, " UNBOUNDED PRECEDING");
		else if (node->frameOptions & FRAMEOPTION_START_UNBOUNDED_FOLLOWING)
			appendStringInfoString(str, " UNBOUNDED FOLLOWING");
		else if (node->frameOptions & FRAMEOPTION_START_CURRENT_ROW)
			appendStringInfoString(str, " UNBOUNDED CURRENT ROW");
		else if (node->frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
		{
			appendStringInfoString(str, " ");
			_outNode(str, node->startOffset);
			appendStringInfoString(str, " PRECEDING");
		}
		else if (node->frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING)
		{
			appendStringInfoString(str, " ");
			_outNode(str, node->startOffset);
			appendStringInfoString(str, " FOLLOWING");
		}

		if (node->frameOptions & FRAMEOPTION_BETWEEN)
		{
			appendStringInfoString(str, " AND");
			if (node->frameOptions & FRAMEOPTION_END_UNBOUNDED_PRECEDING)
				appendStringInfoString(str, " UNBOUNDED PRECEDING");
			else if (node->frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
				appendStringInfoString(str, " UNBOUNDED FOLLOWING");
			else if (node->frameOptions & FRAMEOPTION_END_CURRENT_ROW)
				appendStringInfoString(str, " UNBOUNDED CURRENT ROW");
			else if (node->frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
			{
				appendStringInfoString(str, " ");
				_outNode(str, node->endOffset);
				appendStringInfoString(str, " PRECEDING");
			}
			else if (node->frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING)
			{
				appendStringInfoString(str, " ");
				_outNode(str, node->endOffset);
				appendStringInfoString(str, " FOLLOWING");
			}
		}

		if (node->frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
		{
			appendStringInfoString(str, " EXCLUDE CURRENT ROW");
		}
		else if (node->frameOptions & FRAMEOPTION_EXCLUDE_GROUP)
		{
			appendStringInfoString(str, " EXCLUDE GROUP");
		}
		else if (node->frameOptions & FRAMEOPTION_EXCLUDE_TIES)
		{
			appendStringInfoString(str, " EXCLUDE TIES");
		}
	}
	appendStringInfoString(str, ")");
}

static void
_outConstraint(StringInfo str, Constraint *node)
{
	if (node->conname)
	{
		appendStringInfoString(str, "CONSTRAINT \"");
		appendStringInfoString(str, node->conname);
		appendStringInfoString(str, "\"");
	}

	switch (node->contype)
	{
		case CONSTR_CHECK:
			appendStringInfoString(str, " CHECK (");
			_outNode(str, node->raw_expr);
			appendStringInfoString(str, ")");
			break;

		case CONSTR_UNIQUE:
			appendStringInfoString(str, " UNIQUE");
			if (node->keys)
			{
				appendStringInfoString(str, "(");
				_outIdList(str, node->keys);
				appendStringInfoString(str, ")");
			}

			if (node->options)
			{
				_outWithDefinition(str, node->options);
			}

			if (node->indexspace)
			{
				appendStringInfoString(str, " USING INDEX TABLESPACE \"");
				appendStringInfoString(str, node->indexspace);
				appendStringInfoString(str, "\"");
			}
			break;

		case CONSTR_PRIMARY:
			appendStringInfoString(str, " PRIMARY KEY");
			if (node->keys)
			{
				appendStringInfoString(str, "(");
				_outIdList(str, node->keys);
				appendStringInfoString(str, ")");
			}
			if (node->options)
				;

			if (node->indexspace)
			{
				appendStringInfoString(str, " USING INDEX TABLESPACE \"");
				appendStringInfoString(str, node->indexspace);
				appendStringInfoString(str, "\"");
			}
			break;

		case CONSTR_FOREIGN:
			if (node->fk_attrs != NIL)
			{
				appendStringInfoString(str, " FOREIGN KEY(");
				_outIdList(str, node->fk_attrs);
				appendStringInfoString(str, ")");
			}

			appendStringInfoString(str, " REFERENCES ");
			_outNode(str, node->pktable);

			if (node->pk_attrs != NIL)
			{
				appendStringInfoString(str, "(");
				_outIdList(str, node->pk_attrs);
				appendStringInfoString(str, ")");
			}

			switch (node->fk_matchtype)
			{
				case FKCONSTR_MATCH_FULL:
					appendStringInfoString(str, " MATCH FULL");
					break;

				case FKCONSTR_MATCH_PARTIAL:
					appendStringInfoString(str, " MATCH PARTIAL");
					break;

				default:
					break;
			}

			switch (node->fk_upd_action)
			{
				case FKCONSTR_ACTION_RESTRICT:
					appendStringInfoString(str, " ON UPDATE RESTRICT");
					break;

				case FKCONSTR_ACTION_CASCADE:
					appendStringInfoString(str, " ON UPDATE CASCADE");
					break;

				case FKCONSTR_ACTION_SETNULL:
					appendStringInfoString(str, " ON UPDATE SET NULL");
					break;

				case FKCONSTR_ACTION_SETDEFAULT:
					appendStringInfoString(str, " ON UPDATE SET DEFAULT");
					break;

				default:
					break;
			}

			switch (node->fk_del_action)
			{
				case FKCONSTR_ACTION_RESTRICT:
					appendStringInfoString(str, " ON DELETE RESTRICT");
					break;

				case FKCONSTR_ACTION_CASCADE:
					appendStringInfoString(str, " ON DELETE CASCADE");
					break;

				case FKCONSTR_ACTION_SETNULL:
					appendStringInfoString(str, " ON DELETE SET NULL");
					break;

				case FKCONSTR_ACTION_SETDEFAULT:
					appendStringInfoString(str, " ON DELETE SET DEFAULT");
					break;

				default:
					break;
			}

			if (node->deferrable)
				appendStringInfoString(str, " DEFERRABLE");

			if (node->initdeferred)
				appendStringInfoString(str, " INITIALLY DEFERRED");
			break;

		case CONSTR_NOTNULL:
			appendStringInfoString(str, " NOT NULL");
			break;

		case CONSTR_NULL:
			appendStringInfoString(str, " NULL");
			break;

		case CONSTR_DEFAULT:
			appendStringInfoString(str, "DEFAULT ");
			_outNode(str, node->raw_expr);
			break;

		default:
			break;
	}
}


static void
_outSortBy(StringInfo str, SortBy *node)
{
	_outNode(str, node->node);

	if (node->sortby_dir == SORTBY_USING)
	{
		appendStringInfoString(str, " USING ");
		_outNode(str, node->useOp);
	}
	else if (node->sortby_dir == SORTBY_DESC)
		appendStringInfoString(str, " DESC ");

	if (node->sortby_nulls == SORTBY_NULLS_FIRST)
		appendStringInfoString(str, " NULLS FIRST ");
	else if (node->sortby_nulls == SORTBY_NULLS_LAST)
		appendStringInfoString(str, " NULLS LAST ");
}

static void
_outInsertStmt(StringInfo str, InsertStmt *node)
{
	if (node->withClause)
		_outWithClause(str, node->withClause);

	appendStringInfoString(str, "INSERT INTO ");
	_outNode(str, node->relation);

	if (node->cols == NIL && node->selectStmt == NULL)
		appendStringInfoString(str, " DEFAULT VALUES");

	if (node->cols)
	{
		char		comma = 0;
		ListCell   *lc;

		appendStringInfoString(str, "(");

		foreach(lc, node->cols)
		{
			ResTarget  *node = lfirst(lc);
			ListCell   *lc_ind;

			if (comma == 0)
				comma = 1;
			else
				appendStringInfoString(str, ", ");

			appendStringInfoString(str, "\"");
			appendStringInfoString(str, node->name);
			appendStringInfoString(str, "\"");

			foreach(lc_ind, node->indirection)
			{
				Node	   *ind = lfirst(lc_ind);

				if (IsA(ind, String))
				{
					/* foo.bar */
					appendStringInfoString(str, ".\"");
					appendStringInfoString(str, strVal(ind));
					appendStringInfoString(str, "\"");
				}
				else
					/* foo[1] (A_Indices) */
					_outNode(str, ind);
			}
		}
		appendStringInfoString(str, ")");
	}

	if (node->selectStmt)
	{
		_outNode(str, node->selectStmt);
	}

	if (node->onConflictClause)
	{
		_outOnConflictClause(str, node->onConflictClause);
	}

	if (node->returningClause)
	{
		appendStringInfoString(str, " RETURNING ");
		_outNode(str, node->returningClause);
	}
}

static void
_outSetClause(StringInfo str, List *node)
{
	ListCell   *lc;
	char		comma = 0;

	ResTarget  *first = linitial(node);

	appendStringInfoString(str, " SET ");

	if (IsA(first->val, MultiAssignRef))
	{
		appendStringInfoString(str, "(");
		foreach(lc, node)
		{
			ResTarget  *node = lfirst(lc);
			ListCell   *lc_ind;

			if (comma == 0)
				comma = 1;
			else
				appendStringInfoString(str, ", ");

			appendStringInfoString(str, "\"");
			appendStringInfoString(str, node->name);
			appendStringInfoString(str, "\"");

			foreach(lc_ind, node->indirection)
			{
				Node	   *ind = lfirst(lc_ind);

				if (IsA(ind, String))
				{
					appendStringInfoString(str, ".\"");
					appendStringInfoString(str, strVal(ind));
					appendStringInfoString(str, "\"");
				}
				else
					/* foo[1] (A_Indices) */
					_outNode(str, ind);
			}
		}
		appendStringInfoString(str, ") = ");

		_outNode(str, first->val);

	}
	else
	{
		foreach(lc, node)
		{
			ResTarget  *node = lfirst(lc);
			ListCell   *lc_ind;

			if (comma == 0)
				comma = 1;
			else
				appendStringInfoString(str, ", ");

			appendStringInfoString(str, "\"");
			appendStringInfoString(str, node->name);
			appendStringInfoString(str, "\"");

			foreach(lc_ind, node->indirection)
			{
				Node	   *ind = lfirst(lc_ind);

				if (IsA(ind, String))
				{
					appendStringInfoString(str, ".\"");
					appendStringInfoString(str, strVal(ind));
					appendStringInfoString(str, "\"");
				}
				else
					/* foo[1] (A_Indices) */
					_outNode(str, ind);
			}

			appendStringInfoString(str, " = ");
			_outNode(str, node->val);
		}
	}
}

static void
_outUpdateStmt(StringInfo str, UpdateStmt *node)
{
	if (node->withClause)
		_outWithClause(str, node->withClause);

	appendStringInfoString(str, "UPDATE ");

	_outNode(str, node->relation);

	_outSetClause(str, node->targetList);

	if (node->fromClause)
	{
		appendStringInfoString(str, " FROM ");
		_outNode(str, node->fromClause);
	}

	if (node->whereClause)
	{
		appendStringInfoString(str, " WHERE ");
		_outNode(str, node->whereClause);
	}

	if (node->returningClause)
	{
		appendStringInfoString(str, " RETURNING ");
		_outNode(str, node->returningClause);
	}
}

static void
_outDeleteStmt(StringInfo str, DeleteStmt *node)
{
	if (node->withClause)
		_outWithClause(str, node->withClause);

	appendStringInfoString(str, "DELETE FROM ");

	_outNode(str, node->relation);

	if (node->usingClause)
	{
		appendStringInfoString(str, " USING ");
		_outNode(str, node->usingClause);
	}

	if (node->whereClause)
	{
		appendStringInfoString(str, " WHERE ");
		_outNode(str, node->whereClause);
	}

	if (node->returningClause)
	{
		appendStringInfoString(str, " RETURNING ");
		_outNode(str, node->returningClause);
	}
}

static void
_outMergeStmt(StringInfo str, MergeStmt *node)
{
	appendStringInfoString(str, "MERGE INTO ");
	_outRangeVar(str, node->relation);

	if (node->sourceRelation)
	{
		appendStringInfoString(str, " USING ");
		_outNode(str, node->sourceRelation);
	}

	if (node->joinCondition)
	{
		appendStringInfoString(str, " ON ");
		_outNode(str, node->joinCondition);
	}

	if (node->mergeWhenClauses != NIL)
	{
		_outMergeWhenClauses(str, node->mergeWhenClauses);
	}

	if (node->returningClause)
	{
		appendStringInfoString(str, " RETURNING ");
		_outNode(str, node->returningClause);
	}
}

static void
_outTransactionStmt(StringInfo str, TransactionStmt *node)
{
	switch (node->kind)
	{
		case TRANS_STMT_BEGIN:
			appendStringInfoString(str, "BEGIN ");
			break;

		case TRANS_STMT_START:
			appendStringInfoString(str, "START TRANSACTION ");
			break;

		case TRANS_STMT_COMMIT:
			appendStringInfoString(str, "COMMIT ");
			break;

		case TRANS_STMT_ROLLBACK:
			appendStringInfoString(str, "ABORT ");
			break;

		case TRANS_STMT_SAVEPOINT:
			appendStringInfoString(str, "SAVEPOINT ");
			break;

		case TRANS_STMT_RELEASE:
			appendStringInfoString(str, "RELEASE ");
			break;

		case TRANS_STMT_ROLLBACK_TO:
			appendStringInfoString(str, "ROLLBACK TO ");
			break;

		case TRANS_STMT_PREPARE:
			appendStringInfoString(str, "PREPARE TRANSACTION ");
			break;

		case TRANS_STMT_COMMIT_PREPARED:
			appendStringInfoString(str, "COMMIT PREPARED ");
			break;

		case TRANS_STMT_ROLLBACK_PREPARED:
			appendStringInfoString(str, "ROLLBACK PREPARED ");
			break;

		default:
			break;
	}

	if (node->options)
		_outSetTransactionModeList(str, node->options);

	if (node->gid)
		appendStringInfoString(str, node->gid);
}


static void
_outTruncateStmt(StringInfo str, TruncateStmt *node)
{
	appendStringInfoString(str, "TRUNCATE ");
	_outNode(str, node->relations);
}

static void
_outVacuumStmt(StringInfo str, VacuumStmt *node)
{

	VacuumParams params;

	params.options = node->is_vacuumcmd ? VACOPT_VACUUM : VACOPT_ANALYZE;

	if (params.options & VACOPT_VACUUM)
		appendStringInfoString(str, "VACUUM ");
	else
		appendStringInfoString(str, "ANALYZE ");

	if (params.options & VACOPT_FULL)
		appendStringInfoString(str, "FULL ");

	if (params.options & VACOPT_FREEZE)
		appendStringInfoString(str, "FREEZE ");

	if (params.options & VACOPT_VERBOSE)
		appendStringInfoString(str, "VERBOSE ");

	if (params.options & VACOPT_VACUUM && params.options & VACOPT_ANALYZE)
		appendStringInfoString(str, "ANALYZE ");

	if (params.options & VACOPT_DISABLE_PAGE_SKIPPING)
		appendStringInfoString(str, "DISABLE_PAGE_SKIPPING ");

	if (params.options & VACOPT_SKIP_LOCKED)
		appendStringInfoString(str, "SKIP_LOCKED ");

	ListCell   *lc;

	foreach(lc, node->rels)
	{
		VacuumRelation *vrel = lfirst_node(VacuumRelation, lc);

		_outNode(str, vrel->relation);
		if (vrel->va_cols)
		{
			appendStringInfoString(str, "(");
			_outIdList(str, vrel->va_cols);
			appendStringInfoString(str, ") ");
		}
	}
}

static void
_outExplainStmt(StringInfo str, ExplainStmt *node)
{
	ListCell   *lc;

	appendStringInfoString(str, "EXPLAIN ");

	if (server_version_num < 90000)
	{
		foreach(lc, node->options)
		{
			DefElem    *opt = (DefElem *) lfirst(lc);

			if (strcmp(opt->defname, "analyze") == 0)
				appendStringInfoString(str, "ANALYZE ");
			else if (strcmp(opt->defname, "verbose") == 0)
				appendStringInfoString(str, "VERBOSE ");
		}
	}
	else
	{
		if (node->options)
		{
			appendStringInfoString(str, "(");
			foreach(lc, node->options)
			{
				DefElem    *opt = (DefElem *) lfirst(lc);

				if (list_head(node->options) != lc)
					appendStringInfoString(str, ", ");

				appendStringInfoString(str, opt->defname);
				appendStringInfoString(str, " ");
				if (opt->arg)
					_outNode(str, opt->arg);
			}
			appendStringInfoString(str, ")");
		}
	}

	_outNode(str, node->query);
}

static void
_outClusterStmt(StringInfo str, ClusterStmt *node)
{
	appendStringInfoString(str, "CLUSTER ");

	if (node->indexname)
	{
		appendStringInfoString(str, "\"");
		appendStringInfoString(str, node->indexname);
		appendStringInfoString(str, "\" ON ");
	}
	if (node->relation)
		_outNode(str, node->relation);
}

static void
_outCheckPointStmt(StringInfo str, CheckPointStmt *node)
{
	appendStringInfoString(str, "CHECKPOINT");
}

static void
_outClosePortalStmt(StringInfo str, ClosePortalStmt *node)
{
	appendStringInfoString(str, "CLOSE ");
	appendStringInfoString(str, "\"");
	appendStringInfoString(str, node->portalname);
	appendStringInfoString(str, "\"");
}

static void
_outListenStmt(StringInfo str, ListenStmt *node)
{
	appendStringInfoString(str, "LISTEN ");
	appendStringInfoString(str, "\"");
	appendStringInfoString(str, node->conditionname);
	appendStringInfoString(str, "\"");
}

static void
_outUnlistenStmt(StringInfo str, UnlistenStmt *node)
{
	appendStringInfoString(str, "UNLISTEN ");
	if (node->conditionname == NULL)
		appendStringInfoString(str, "*");
	else
	{
		appendStringInfoString(str, "\"");
		appendStringInfoString(str, node->conditionname);
		appendStringInfoString(str, "\"");
	}
}

static void
_outLoadStmt(StringInfo str, LoadStmt *node)
{
	appendStringInfoString(str, "LOAD '");
	appendStringInfoString(str, node->filename);
	appendStringInfoString(str, "'");
}

static void
_outCopyStmt(StringInfo str, CopyStmt *node)
{
	ListCell   *lc;

	appendStringInfoString(str, "COPY ");

	if (node->query)
	{
		appendStringInfoString(str, "(");
		_outNode(str, node->query);
		appendStringInfoString(str, ")");
	}

	_outNode(str, node->relation);

	if (node->attlist)
	{
		appendStringInfoString(str, "(");
		_outIdList(str, node->attlist);
		appendStringInfoString(str, ")");
	}

	if (node->is_from == TRUE)
		appendStringInfoString(str, " FROM ");
	else
		appendStringInfoString(str, " TO ");

	if (node->filename)
	{
		appendStringInfoString(str, "'");
		appendStringInfoString(str, node->filename);
		appendStringInfoString(str, "' ");
	}
	else
		appendStringInfoString(str, node->is_from == TRUE ? "STDIN " : "STDOUT ");


	if (server_version_num < 90000)
	{
		foreach(lc, node->options)
		{
			DefElem    *e = lfirst(lc);

			if (strcmp(e->defname, "format") == 0)
			{
				char	   *fmt = strVal(e->arg);

				if (strcmp(fmt, "text") == 0)
					;
				else if (strcmp(fmt, "binary") == 0)
					appendStringInfoString(str, "BINARY ");
				else if (strcmp(fmt, "csv") == 0)
					appendStringInfoString(str, "CSV ");
			}
			else if (strcmp(e->defname, "oids") == 0)
				appendStringInfoString(str, "OIDS ");
			else if (strcmp(e->defname, "delimiter") == 0)
			{
				appendStringInfoString(str, "DELIMITERS ");
				_outNode(str, e->arg);
				appendStringInfoString(str, " ");
			}
			else if (strcmp(e->defname, "null") == 0)
			{
				appendStringInfoString(str, "NULL ");
				_outNode(str, e->arg);
				appendStringInfoString(str, " ");
			}
			else if (strcmp(e->defname, "header") == 0)
				appendStringInfoString(str, "HEADER ");
			else if (strcmp(e->defname, "quote") == 0)
			{
				appendStringInfoString(str, "QUOTE ");
				_outNode(str, e->arg);
				appendStringInfoString(str, " ");
			}
			else if (strcmp(e->defname, "escape") == 0)
			{
				appendStringInfoString(str, "ESCAPE ");
				_outNode(str, e->arg);
				appendStringInfoString(str, " ");
			}
			else if (strcmp(e->defname, "force_quote") == 0)
			{
				appendStringInfoString(str, "FORCE QUOTE ");
				_outIdList(str, (List *) e->arg);
			}
			else if (strcmp(e->defname, "force_not_null") == 0)
			{
				appendStringInfoString(str, "FORCE NOT NULL ");
				_outIdList(str, (List *) e->arg);
			}
		}
	}
	else
	{
		/* version_num >= 90000 */
		if (node->options)
		{
			appendStringInfoString(str, "WITH (");

			foreach(lc, node->options)
			{
				DefElem    *e = lfirst(lc);

				if (list_head(node->options) != lc)
					appendStringInfoString(str, ", ");

				appendStringInfoString(str, e->defname);
				appendStringInfoString(str, " ");

				if (strcmp(e->defname, "format") == 0
					|| strcmp(e->defname, "freeze") == 0
					|| strcmp(e->defname, "oids") == 0
					|| strcmp(e->defname, "header") == 0
					|| strcmp(e->defname, "on_error") == 0
					|| strcmp(e->defname, "log_verbosity") == 0)
					_outNode(str, e->arg);
				else if (strcmp(e->defname, "delimiter") == 0
						 || strcmp(e->defname, "null") == 0
						 || strcmp(e->defname, "default") == 0
						 || strcmp(e->defname, "quote") == 0
						 || strcmp(e->defname, "escape") == 0
						 || strcmp(e->defname, "encoding") == 0)
				{
					String	   *value = (String *) e->arg;

					appendStringInfoString(str, "'");
					_outSingleQuote(str, value->sval);
					appendStringInfoString(str, "'");
				}
				else if (strcmp(e->defname, "force_not_null") == 0
						 || strcmp(e->defname, "force_null") == 0)
				{
					if (IsA(e->arg, A_Star))
						appendStringInfoString(str, "*");
					else if (IsA(e->arg, List))
					{
						appendStringInfoString(str, "(");
						_outIdList(str, (List *) e->arg);
						appendStringInfoString(str, ")");
					}
				}
				else if (strcmp(e->defname, "force_quote") == 0)
				{
					if (IsA(e->arg, A_Star))
						appendStringInfoString(str, "*");
					else if (IsA(e->arg, List))
					{
						appendStringInfoString(str, "(");
						_outIdList(str, (List *) e->arg);
						appendStringInfoString(str, ")");
					}
				}
			}
			appendStringInfoString(str, ")");
		}
	}

	if (node->whereClause)
	{
		appendStringInfoString(str, " WHERE ");
		_outNode(str, node->whereClause);
	}
}

static void
_outDeallocateStmt(StringInfo str, DeallocateStmt *node)
{
	appendStringInfoString(str, "DEALLOCATE \"");
	appendStringInfoString(str, node->name);
	appendStringInfoString(str, "\"");
}

static void
_outRenameStmt(StringInfo str, RenameStmt *node)
{
	ListCell   *lc;
	char		comma = 0;

	appendStringInfoString(str, "ALTER ");

	switch (node->renameType)
	{
		case OBJECT_AGGREGATE:
			appendStringInfoString(str, "AGGREGATE ");
			_outNode(str, node->object);
			appendStringInfoString(str, " (");
			appendStringInfoString(str, ") RENAME TO \"");
			appendStringInfoString(str, node->newname);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_CONVERSION:
			appendStringInfoString(str, "CONVERSION ");
			_outNode(str, node->object);
			appendStringInfoString(str, " RENAME TO \"");
			appendStringInfoString(str, node->newname);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_DATABASE:
			appendStringInfoString(str, "DATABASE \"");
			appendStringInfoString(str, node->subname);
			appendStringInfoString(str, "\" RENAME TO \"");
			appendStringInfoString(str, node->newname);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_FUNCTION:
			appendStringInfoString(str, "FUNCTION ");

			foreach(lc, castNode(List, node->object))
			{
				Node	   *n = lfirst(lc);

				if (IsA(n, String))
				{
					String	   *value = (String *) n;

					if (comma == 0)
						comma = 1;
					else
						appendStringInfoString(str, ".");
					appendStringInfoString(str, "\"");
					appendStringInfoString(str, value->sval);
					appendStringInfoString(str, "\"");
				}
				else
					_outNode(str, n);
			}

			appendStringInfoString(str, "(");
			appendStringInfoString(str, ")");
			appendStringInfoString(str, " RENAME TO \"");
			appendStringInfoString(str, node->newname);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_ROLE:
			appendStringInfoString(str, "ROLE \"");
			appendStringInfoString(str, node->subname);
			appendStringInfoString(str, "\" RENAME TO \"");
			appendStringInfoString(str, node->newname);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_LANGUAGE:
			appendStringInfoString(str, "LANGUAGE \"");
			appendStringInfoString(str, node->subname);
			appendStringInfoString(str, "\" RENAME TO \"");
			appendStringInfoString(str, node->newname);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_OPCLASS:
			appendStringInfoString(str, "OPERATOR CLASS ");
			_outNode(str, node->object);
			appendStringInfoString(str, " USING ");
			appendStringInfoString(str, node->subname);
			appendStringInfoString(str, " RENAME TO \"");
			appendStringInfoString(str, node->newname);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_SCHEMA:
			appendStringInfoString(str, "SCHEMA \"");
			appendStringInfoString(str, node->subname);
			appendStringInfoString(str, "\" RENAME TO \"");
			appendStringInfoString(str, node->newname);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_TABLE:
			appendStringInfoString(str, "TABLE ");
			_outNode(str, node->relation);
			appendStringInfoString(str, " RENAME TO \"");
			appendStringInfoString(str, node->newname);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_INDEX:
			appendStringInfoString(str, "INDEX ");
			_outNode(str, node->relation);
			appendStringInfoString(str, " RENAME TO \"");
			appendStringInfoString(str, node->newname);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_COLUMN:
			appendStringInfoString(str, "TABLE ");
			_outNode(str, node->relation);
			appendStringInfoString(str, " RENAME \"");
			appendStringInfoString(str, node->subname);
			appendStringInfoString(str, "\" TO \"");
			appendStringInfoString(str, node->newname);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_TRIGGER:
			appendStringInfoString(str, "TRIGGER \"");
			appendStringInfoString(str, node->subname);
			appendStringInfoString(str, "\" ON ");
			_outNode(str, node->relation);
			appendStringInfoString(str, " RENAME TO \"");
			appendStringInfoString(str, node->newname);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_TABLESPACE:
			appendStringInfoString(str, "TABLESPACE \"");
			appendStringInfoString(str, node->subname);
			appendStringInfoString(str, "\" RENAME TO \"");
			appendStringInfoString(str, node->newname);
			appendStringInfoString(str, "\"");
			break;

		default:
			break;
	}
}

static void
_outOptRoleList(StringInfo str, List *options)
{
	ListCell   *lc;

	foreach(lc, options)
	{
		DefElem    *elem = lfirst(lc);
		A_Const    *value = (A_Const *) elem->arg;

		if (strcmp(elem->defname, "password") == 0)
		{
			if (value == NULL)
				appendStringInfoString(str, " PASSWORD NULL");
			else
			{
				appendStringInfoString(str, " PASSWORD '");
				appendStringInfoString(str, value->val.sval.sval);
				appendStringInfoString(str, "'");
			}
		}
		else if (strcmp(elem->defname, "encryptedPassword") == 0)
		{
			appendStringInfoString(str, " ENCRYPTED PASSWORD '");
			appendStringInfoString(str, value->val.sval.sval);
			appendStringInfoString(str, "'");
		}
		else if (strcmp(elem->defname, "unencryptedPassword") == 0)
		{
			appendStringInfoString(str, " UNENCRYPTED PASSWORD '");
			appendStringInfoString(str, value->val.sval.sval);
			appendStringInfoString(str, "'");
		}
		else if (strcmp(elem->defname, "superuser") == 0)
		{
			if (value->val.boolval.boolval)
				appendStringInfoString(str, " SUPERUSER");
			else
				appendStringInfoString(str, " NOSUPERUSER");
		}
		else if (strcmp(elem->defname, "inherit") == 0)
		{
			if (value->val.boolval.boolval)
				appendStringInfoString(str, " INHERIT");
			else
				appendStringInfoString(str, " NOINHERIT");
		}
		else if (strcmp(elem->defname, "createdb") == 0)
		{
			if (value->val.boolval.boolval)
				appendStringInfoString(str, " CREATEDB");
			else
				appendStringInfoString(str, " NOCREATEDB");
		}
		else if (strcmp(elem->defname, "createrole") == 0)
		{
			if (value->val.boolval.boolval)
				appendStringInfoString(str, " CREATEROLE");
			else
				appendStringInfoString(str, " NOCREATEROLE");
		}
		else if (strcmp(elem->defname, "canlogin") == 0)
		{
			if (value->val.boolval.boolval)
				appendStringInfoString(str, " LOGIN");
			else
				appendStringInfoString(str, " NOLOGIN");
		}
		else if (strcmp(elem->defname, "connectionlimit") == 0)
		{
			char		buf[16];

			appendStringInfoString(str, " CONNECTION LIMIT ");
			snprintf(buf, 16, "%d", value->val.ival.ival);
			appendStringInfoString(str, buf);
		}
		else if (strcmp(elem->defname, "validUntil") == 0)
		{
			appendStringInfoString(str, " VALID UNTIL '");
			appendStringInfoString(str, value->val.sval.sval);
			appendStringInfoString(str, "'");
		}
		else if (strcmp(elem->defname, "rolemembers") == 0)
		{
			appendStringInfoString(str, " ROLE ");
			_outIdList(str, (List *) elem->arg);
		}
		else if (strcmp(elem->defname, "sysid") == 0)
		{
			char		buf[16];

			appendStringInfoString(str, " SYSID ");
			snprintf(buf, 16, "%d", value->val.ival.ival);
			appendStringInfoString(str, buf);
		}
		else if (strcmp(elem->defname, "adminmembers") == 0)
		{
			appendStringInfoString(str, " ADMIN ");
			_outIdList(str, (List *) elem->arg);
		}
		else if (strcmp(elem->defname, "addroleto") == 0)
		{
			appendStringInfoString(str, " IN ROLE ");
			_outIdList(str, (List *) elem->arg);
		}
	}
}

static void
_outCreateRoleStmt(StringInfo str, CreateRoleStmt *node)
{
	appendStringInfoString(str, "CREATE ");
	switch (node->stmt_type)
	{
		case ROLESTMT_ROLE:
			appendStringInfoString(str, "ROLE \"");
			break;

		case ROLESTMT_USER:
			appendStringInfoString(str, "USER \"");
			break;

		case ROLESTMT_GROUP:
			appendStringInfoString(str, "GROUP \"");
			break;
	}
	_outNode(str, node->role);
	appendStringInfoString(str, "\"");

	_outOptRoleList(str, node->options);
}

static void
_outAlterRoleStmt(StringInfo str, AlterRoleStmt *node)
{
	appendStringInfoString(str, "ALTER ROLE \"");
	_outNode(str, node->role);
	appendStringInfoString(str, "\"");

	if (node->options)
		_outOptRoleList(str, node->options);
}

static void
_outAlterRoleSetStmt(StringInfo str, AlterRoleSetStmt *node)
{
	appendStringInfoString(str, "ALTER ROLE \"");
	_outNode(str, node->role);
	appendStringInfoString(str, "\" ");

	if (node->setstmt)
	{
		_outNode(str, node->setstmt);
	}
}
static void
_outRoleSpec(StringInfo str, RoleSpec *node)
{
	appendStringInfoString(str, node->rolename);
}

static void
_outSetTransactionModeList(StringInfo str, List *list)
{
	ListCell   *lc;
	char		comma = 0;

	foreach(lc, list)
	{
		DefElem    *elem = lfirst(lc);

		if (comma == 0)
			comma = 1;
		else
			appendStringInfoString(str, ",");

		if (strcmp(elem->defname, "transaction_isolation") == 0)
		{
			A_Const    *v = (A_Const *) elem->arg;

			appendStringInfoString(str, " ISOLATION LEVEL ");
			appendStringInfoString(str, v->val.sval.sval);
		}
		else if (strcmp(elem->defname, "transaction_read_only") == 0)
		{
			A_Const    *n = (A_Const *) elem->arg;

			if (n->val.boolval.boolval)
				appendStringInfoString(str, "READ ONLY ");
			else
				appendStringInfoString(str, "READ WRITE ");
		}
	}
}


static void
_outSetRest(StringInfo str, VariableSetStmt *node)
{
	if (strcmp(node->name, "timezone") == 0)
	{
		appendStringInfoString(str, "TIME ZONE ");
		if (node->kind != VAR_RESET)
			_outNode(str, node->args);
	}
	else if (strcmp(node->name, "TRANSACTION") == 0)
	{
		appendStringInfoString(str, "TRANSACTION ");
		_outSetTransactionModeList(str, node->args);
	}
	else if (strcmp(node->name, "SESSION CHARACTERISTICS") == 0)
	{
		appendStringInfoString(str, "SESSION CHARACTERISTICS AS TRANSACTION ");
		_outSetTransactionModeList(str, node->args);
	}
	else if (strcmp(node->name, "role") == 0)
	{
		appendStringInfoString(str, "ROLE ");
		if (node->kind != VAR_RESET)
			_outNode(str, node->args);
	}
	else if (strcmp(node->name, "session_authorization") == 0)
	{
		appendStringInfoString(str, "SESSION AUTHORIZATION ");
		if (node->args == NIL && node->kind != VAR_RESET)
			appendStringInfoString(str, "DEFAULT");
		else
			_outNode(str, node->args);
	}
	else if (strcmp(node->name, "transaction_isolation") == 0)
	{
		appendStringInfoString(str, "TRANSACTION ISOLATION LEVEL");
		if (node->kind != VAR_RESET)
			_outSetTransactionModeList(str, node->args);
	}
	else if (strcmp(node->name, "xmloption") == 0)
	{
		A_Const    *v = linitial(node->args);

		appendStringInfoString(str, "XML OPTION ");
		appendStringInfoString(str, v->val.sval.sval);
	}
	else
	{
		appendStringInfoString(str, node->name);
		if (node->kind != VAR_RESET)
		{
			if (node->kind == VAR_SET_CURRENT)
			{
				appendStringInfoString(str, " FROM CURRENT");
			}
			else
			{
				appendStringInfoString(str, " TO ");
				if (node->args == NULL)
				{
					appendStringInfoString(str, "DEFAULT");
				}
				else
					_outNode(str, node->args);
			}
		}
	}
}

static void
_outDropRoleStmt(StringInfo str, DropRoleStmt *node)
{
	appendStringInfoString(str, "DROP ROLE ");
	if (node->missing_ok == TRUE)
		appendStringInfoString(str, "IF EXISTS ");
	_outIdList(str, node->roles);
}

static void
_outCreateSchemaStmt(StringInfo str, CreateSchemaStmt *node)
{
	appendStringInfoString(str, "CREATE SCHEMA \"");
	appendStringInfoString(str, node->schemaname);
	appendStringInfoString(str, "\"");
	if (node->authrole)
	{
		appendStringInfoString(str, "AUTHORIZATION \"");
		_outNode(str, node->authrole);
		appendStringInfoString(str, "\" ");
	}
	_outNode(str, node->schemaElts);
}

static void
_outVariableSetStmt(StringInfo str, VariableSetStmt *node)
{
	if (node->kind == VAR_RESET_ALL)
	{
		appendStringInfoString(str, "RESET ALL");
		return;
	}

	if (node->kind == VAR_RESET)
		appendStringInfoString(str, "RESET ");
	else
		appendStringInfoString(str, "SET ");

	if (node->is_local)
		appendStringInfoString(str, "LOCAL ");

	_outSetRest(str, node);
}

static void
_outVariableShowStmt(StringInfo str, VariableShowStmt *node)
{
	if (strcmp(node->name, "timezone") == 0)
		appendStringInfoString(str, "SHOW TIME ZONE");
	else if (strcmp(node->name, "transaction_isolation") == 0)
		appendStringInfoString(str, "SHOW TRANSACTION ISOLATION LEVEL");
	else if (strcmp(node->name, "session_authorization") == 0)
		appendStringInfoString(str, "SHOW SESSION AUTHORIZATION");
	else if (strcmp(node->name, "all") == 0)
		appendStringInfoString(str, "SHOW ALL");
	else
	{
		appendStringInfoString(str, "SHOW ");
		appendStringInfoString(str, node->name);
	}
}

static void
_outConstraintsSetStmt(StringInfo str, ConstraintsSetStmt *node)
{
	appendStringInfoString(str, "SET CONSTRAINTS ");

	if (node->constraints == NIL)
		appendStringInfoString(str, "ALL");
	else
		_outNode(str, node->constraints);

	appendStringInfoString(str, node->deferred == TRUE ? " DEFERRED" : " IMMEDIATE");
}

static void
_outAlterTableCmd(StringInfo str, AlterTableCmd *node)
{
	char		buf[16];

	switch (node->subtype)
	{
		case AT_AddColumn:
			appendStringInfoString(str, "ADD ");
			_outNode(str, node->def);
			break;

		case AT_ColumnDefault:
			appendStringInfoString(str, "ALTER \"");
			appendStringInfoString(str, node->name);
			appendStringInfoString(str, "\" ");
			if (node->def == NULL)
				appendStringInfoString(str, "DROP DEFAULT");
			else
			{
				appendStringInfoString(str, "SET DEFAULT ");
				_outNode(str, node->def);
			}
			break;

		case AT_DropNotNull:
			appendStringInfoString(str, "ALTER \"");
			appendStringInfoString(str, node->name);
			appendStringInfoString(str, "\" DROP NOT NULL");
			break;

		case AT_SetNotNull:
			appendStringInfoString(str, "ALTER \"");
			appendStringInfoString(str, node->name);
			appendStringInfoString(str, "\" SET NOT NULL");
			break;

		case AT_SetStatistics:
			appendStringInfoString(str, "ALTER \"");
			appendStringInfoString(str, node->name);
			appendStringInfoString(str, "\" SET STATISTICS ");
			snprintf(buf, 16, "%d", ((A_Const *) node->def)->val.ival.ival);
			appendStringInfoString(str, buf);
			break;

		case AT_SetStorage:
			appendStringInfoString(str, "ALTER \"");
			appendStringInfoString(str, node->name);
			appendStringInfoString(str, "\" SET STORAGE ");
			appendStringInfoString(str, ((A_Const *) node->def)->val.sval.sval);
			break;

		case AT_DropColumn:
			appendStringInfoString(str, "DROP \"");
			appendStringInfoString(str, node->name);
			appendStringInfoString(str, "\" ");
			if (node->behavior == DROP_CASCADE)
				appendStringInfoString(str, "CASCADE");
			break;

		case AT_AlterColumnType:
			appendStringInfoString(str, "ALTER \"");
			appendStringInfoString(str, node->name);
			appendStringInfoString(str, "\" TYPE ");
			_outNode(str, node->def);
			break;

		case AT_AddConstraint:
			appendStringInfoString(str, "ADD ");
			_outNode(str, node->def);
			break;

		case AT_DropConstraint:
			appendStringInfoString(str, "DROP CONSTRAINT \"");
			appendStringInfoString(str, node->name);
			appendStringInfoString(str, "\"");
			if (node->behavior == DROP_CASCADE)
				appendStringInfoString(str, " CASCADE");
			break;

		case AT_DropOids:
			appendStringInfoString(str, "SET WITHOUT OIDS");
			break;

		case AT_SetAccessMethod:
			appendStringInfoString(str, "SET ACCESS METHOD");
			break;

		case AT_ClusterOn:
			appendStringInfoString(str, "CLUSTER ON \"");
			appendStringInfoString(str, node->name);
			appendStringInfoString(str, "\"");
			break;

		case AT_DropCluster:
			appendStringInfoString(str, "SET WITHOUT CLUSTER");
			break;

		case AT_EnableTrig:
			appendStringInfoString(str, "ENABLE TRIGGER \"");
			appendStringInfoString(str, node->name);
			appendStringInfoString(str, "\"");
			break;

		case AT_EnableAlwaysTrig:
			/* not implemented */
			break;

		case AT_EnableReplicaTrig:
			/* not implemented */
			break;

		case AT_EnableTrigAll:
			appendStringInfoString(str, "ENABLE TRIGGER ALL");
			break;

		case AT_EnableTrigUser:
			appendStringInfoString(str, "ENABLE TRIGGER USER");
			break;

		case AT_DisableTrig:
			appendStringInfoString(str, "DISABLE TRIGGER \"");
			appendStringInfoString(str, node->name);
			appendStringInfoString(str, "\"");
			break;

		case AT_DisableTrigAll:
			appendStringInfoString(str, "DISABLE TRIGGER ALL");
			break;

		case AT_DisableTrigUser:
			appendStringInfoString(str, "DISABLE TRIGGER USER");
			break;

		case AT_EnableRule:
			/* not implemented */
			break;

		case AT_EnableReplicaRule:
			/* not implemented */
			break;

		case AT_EnableAlwaysRule:
			/* not implemented */
			break;

		case AT_DisableRule:
			/* not implemented */
			break;

		case AT_AddInherit:
			/* not implemented */
			break;

		case AT_ChangeOwner:
			appendStringInfoString(str, "OWNER TO \"");
			appendStringInfoString(str, node->name);
			appendStringInfoString(str, "\"");
			break;

		case AT_SetTableSpace:
			appendStringInfoString(str, "SET TABLESPACE \"");
			appendStringInfoString(str, node->name);
			appendStringInfoString(str, "\"");
			break;

		case AT_SetRelOptions:
			/* not implemented */
			break;

		case AT_ResetRelOptions:
			/* not implemented */
			break;

		default:
			break;
	}
}

static void
_outATAlterConstraint(StringInfo str, ATAlterConstraint *node)
{
}

static void
_outAlterTableStmt(StringInfo str, AlterTableStmt *node)
{
	if (node->objtype == OBJECT_TABLE)
		appendStringInfoString(str, "ALTER TABLE ");
	else
		appendStringInfoString(str, "ALTER INDEX ");

	_outNode(str, node->relation);
	appendStringInfoString(str, " ");
	_outNode(str, node->cmds);
}

static void
_outOptSeqList(StringInfo str, List *options)
{
	ListCell   *lc;

	foreach(lc, options)
	{
		DefElem    *e = lfirst(lc);
		A_Const    *v = (A_Const *) e->arg;
		char		buf[16];

		if (strcmp(e->defname, "cycle") == 0)
		{
			if (v->val.boolval.boolval)
				appendStringInfoString(str, " CYCLE");
			else
				appendStringInfoString(str, " NO CYCLE");
		}
		else if (strcmp(e->defname, "minvalue") == 0 && !v)
			appendStringInfoString(str, " NO MINVALUE");
		else if (strcmp(e->defname, "maxvalue") == 0 && !v)
			appendStringInfoString(str, " NO MAXVALUE");
		else if (strcmp(e->defname, "owned_by") == 0)
		{
			appendStringInfoString(str, " OWNED BY ");
			_outIdList(str, (List *) e->arg);
		}
		else
		{
			if (strcmp(e->defname, "cache") == 0)
				appendStringInfoString(str, " CACHE ");
			else if (strcmp(e->defname, "increment") == 0)
				appendStringInfoString(str, " INCREMENT ");
			else if (strcmp(e->defname, "maxvalue") == 0 && v)
				appendStringInfoString(str, " MAXVALUE ");
			else if (strcmp(e->defname, "minvalue") == 0 && v)
				appendStringInfoString(str, " MINVALUE ");
			else if (strcmp(e->defname, "start") == 0)
				appendStringInfoString(str, " START ");
			else if (strcmp(e->defname, "restart") == 0)
				appendStringInfoString(str, " RESTART ");

			if (IsA(e->arg, String))
				appendStringInfoString(str, v->val.sval.sval);
			else if (IsA(e->arg, Integer))
			{
				snprintf(buf, 16, "%d", v->val.ival.ival);
				appendStringInfoString(str, buf);
			}
		}
	}
}

static void
_outCreateSeqStmt(StringInfo str, CreateSeqStmt *node)
{
	appendStringInfoString(str, "CREATE ");

	if (node->sequence->relpersistence == RELPERSISTENCE_TEMP)
		appendStringInfoString(str, "TEMP ");
	else if (node->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
		appendStringInfoString(str, "UNLOGGED ");

	appendStringInfoString(str, "SEQUENCE ");

	if (node->if_not_exists)
		appendStringInfoString(str, "IF NOT EXISTS ");

	_outNode(str, node->sequence);

	_outOptSeqList(str, node->options);
}

static void
_outAlterSeqStmt(StringInfo str, AlterSeqStmt *node)
{
	appendStringInfoString(str, "ALTER SEQUENCE ");
	_outNode(str, node->sequence);
	_outOptSeqList(str, node->options);
}

static void
_outCreatePLangStmt(StringInfo str, CreatePLangStmt *node)
{
	appendStringInfoString(str, "CREATE ");
	if (node->pltrusted == true)
		appendStringInfoString(str, "TRUSTED ");
	appendStringInfoString(str, "LANGUAGE \"");
	appendStringInfoString(str, node->plname);
	appendStringInfoString(str, "\"");

	if (node->plhandler != NIL)
	{
		ListCell   *lc;
		char		dot = 0;

		appendStringInfoString(str, " HANDLER ");
		foreach(lc, node->plhandler)
		{
			A_Const    *v = (A_Const *) lfirst(lc);

			if (dot == 0)
				dot = 1;
			else
				appendStringInfoString(str, ".");

			appendStringInfoString(str, "\"");
			appendStringInfoString(str, v->val.sval.sval);
			appendStringInfoString(str, "\"");
		}
	}

	if (node->plvalidator != NIL)
	{
		ListCell   *lc;
		char		dot = 0;

		appendStringInfoString(str, " VALIDATOR ");
		foreach(lc, node->plvalidator)
		{
			A_Const    *v = (A_Const *) lfirst(lc);

			if (dot == 0)
				dot = 1;
			else
				appendStringInfoString(str, ".");

			appendStringInfoString(str, "\"");
			appendStringInfoString(str, v->val.sval.sval);
			appendStringInfoString(str, "\"");
		}
	}
}


static void
_outCreateTableSpaceStmt(StringInfo str, CreateTableSpaceStmt *node)
{
	appendStringInfoString(str, "CREATE TABLESPACE \"");
	appendStringInfoString(str, node->tablespacename);
	appendStringInfoString(str, "\" ");

	if (node->owner)
	{
		appendStringInfoString(str, "OWNER \"");
		_outNode(str, node->owner);
		appendStringInfoString(str, "\" ");
	}

	appendStringInfoString(str, "LOCATION '");
	appendStringInfoString(str, node->location);
	appendStringInfoString(str, "'");
}

static void
_outDropTableSpaceStmt(StringInfo str, DropTableSpaceStmt *node)
{
	appendStringInfoString(str, "DROP TABLESPACE \"");
	appendStringInfoString(str, node->tablespacename);
	appendStringInfoString(str, "\"");
}

static void
_outFuncName(StringInfo str, List *func_name)
{
	ListCell   *lc;
	char		dot = 0;

	if (func_name == NULL)
		return;

	foreach(lc, func_name)
	{
		A_Const    *v = (A_Const *) lfirst(lc);

		if (dot == 0)
			dot = 1;
		else
			appendStringInfoString(str, ".");

		if (IsA(&v->val, String))
		{
			appendStringInfoString(str, "\"");
			appendStringInfoString(str, v->val.sval.sval);
			appendStringInfoString(str, "\"");
		}
		else
		{
			_outNode(str, v);
		}
	}
}

static void
_outCreateTrigStmt(StringInfo str, CreateTrigStmt *node)
{
	bool		has_events = false;

	if (node->isconstraint == TRUE)
		appendStringInfoString(str, "CREATE CONSTRAINT TRIGGER \"");
	else
		appendStringInfoString(str, "CREATE TRIGGER \"");
	appendStringInfoString(str, node->trigname);
	appendStringInfoString(str, "\" ");

	if (TRIGGER_FOR_BEFORE(node->timing))
		appendStringInfoString(str, "BEFORE ");
	if (TRIGGER_FOR_AFTER(node->timing))
		appendStringInfoString(str, "AFTER ");

	if (node->events & TRIGGER_TYPE_INSERT)
	{
		appendStringInfoString(str, "INSERT ");
		has_events = true;
	}
	if (node->events & TRIGGER_TYPE_DELETE)
	{
		if (has_events)
			appendStringInfoString(str, "OR ");
		appendStringInfoString(str, "DELETE ");
		has_events = true;
	}
	if (node->events & TRIGGER_TYPE_UPDATE)
	{
		if (has_events)
			appendStringInfoString(str, "OR ");
		appendStringInfoString(str, "UPDATE ");
		has_events = true;
	}
	if (node->events & TRIGGER_TYPE_TRUNCATE)
	{
		if (has_events)
			appendStringInfoString(str, "OR ");
		appendStringInfoString(str, "TRUNCATE ");
		has_events = true;
	}

	appendStringInfoString(str, "ON ");
	_outNode(str, node->relation);

	if (node->constrrel)
	{
		appendStringInfoString(str, " FROM ");
		_outNode(str, node->constrrel);
	}

	if (node->deferrable)
		appendStringInfoString(str, " DEFERRABLE");
	if (node->initdeferred)
		appendStringInfoString(str, " INITIALLY DEFERRED");

	if (node->row == TRUE)
		appendStringInfoString(str, " FOR EACH ROW ");
	else
		appendStringInfoString(str, " FOR EACH STATEMENT ");

	appendStringInfoString(str, "EXECUTE PROCEDURE ");

	_outFuncName(str, node->funcname);
	appendStringInfoString(str, "(");
	_outNode(str, node->args);
	appendStringInfoString(str, ")");
}

static void
_outDefinition(StringInfo str, List *definition)
{
	ListCell   *lc;
	char		comma = 0;

	if (definition == NIL)
		return;

	appendStringInfoString(str, "(");
	foreach(lc, definition)
	{
		DefElem    *e = lfirst(lc);

		if (comma == 0)
			comma = 1;
		else
			appendStringInfoString(str, ", ");

		appendStringInfoString(str, "\"");
		appendStringInfoString(str, e->defname);
		appendStringInfoString(str, "\"");

		if (e->arg)
		{
			appendStringInfoString(str, "=");
			_outNode(str, e->arg);
		}
	}
	appendStringInfoString(str, ")");
}

static void
_outDefineStmt(StringInfo str, DefineStmt *node)
{
	ListCell   *lc;
	char		dot = 0;

	switch (node->kind)
	{
		case OBJECT_AGGREGATE:
			appendStringInfoString(str, "CREATE AGGREGATE ");
			_outFuncName(str, node->defnames);
			appendStringInfoString(str, " ");
			_outDefinition(str, node->definition);
			break;

		case OBJECT_OPERATOR:
			appendStringInfoString(str, "CREATE OPERATOR ");

			foreach(lc, node->defnames)
			{
				A_Const    *v = (A_Const *) lfirst(lc);

				if (dot == 0)
					dot = 1;
				else
					appendStringInfoString(str, ".");

				appendStringInfoString(str, v->val.sval.sval);
			}

			appendStringInfoString(str, " ");
			_outDefinition(str, node->definition);
			break;

		case OBJECT_TYPE:
			appendStringInfoString(str, "CREATE TYPE");
			_outFuncName(str, node->defnames);
			appendStringInfoString(str, " ");
			_outDefinition(str, node->definition);
			break;

		case OBJECT_TSPARSER:
			appendStringInfoString(str, "CREATE TEXT SEARCH PARSER ");
			_outIdList(str, node->defnames);
			_outDefinition(str, node->definition);
			break;

		case OBJECT_TSDICTIONARY:
			appendStringInfoString(str, "CREATE TEXT SEARCH DICTIONARY ");
			_outIdList(str, node->defnames);
			_outDefinition(str, node->definition);
			break;

		case OBJECT_TSTEMPLATE:
			appendStringInfoString(str, "CREATE TEXT SEARCH TEMPLATE ");
			_outIdList(str, node->defnames);
			_outDefinition(str, node->definition);
			break;

		case OBJECT_TSCONFIGURATION:
			appendStringInfoString(str, "CREATE TEXT SEARCH CONFIGURATION ");
			_outIdList(str, node->defnames);
			_outDefinition(str, node->definition);
			break;

		default:
			break;
	}
}

static void
_outOperatorName(StringInfo str, List *list)
{
	char		dot = 0;
	ListCell   *lc;

	foreach(lc, list)
	{
		A_Const    *v = (A_Const *) lfirst(lc);

		if (dot == 0)
			dot = 1;
		else
			appendStringInfoString(str, ".");

		appendStringInfoString(str, v->val.sval.sval);
	}
}

static void
_outCreateOpClassItem(StringInfo str, CreateOpClassItem *node)
{
	char		buf[16];

	switch (node->itemtype)
	{
		case OPCLASS_ITEM_OPERATOR:
			appendStringInfoString(str, "OPERATOR ");
			snprintf(buf, 16, "%d", node->number);
			appendStringInfoString(str, buf);
			appendStringInfoString(str, " ");
			_outOperatorName(str, node->name->objname);

			if (node->name->objname != NIL)
			{
				appendStringInfoString(str, "(");
				_outNode(str, node->name->objname);
				appendStringInfoString(str, ")");
			}

			/*
			 * if (node->recheck == TRUE) appendStringInfoString(str, "
			 * RECHECK");
			 */
			break;

		case OPCLASS_ITEM_FUNCTION:
			appendStringInfoString(str, "FUNCTION ");
			snprintf(buf, 16, "%d", node->number);
			appendStringInfoString(str, buf);
			appendStringInfoString(str, " ");
			_outFuncName(str, node->name->objname);
			appendStringInfoString(str, "(");
			_outNode(str, node->name->objname);
			appendStringInfoString(str, ")");
			break;

		case OPCLASS_ITEM_STORAGETYPE:
			appendStringInfoString(str, "STORAGE ");
			_outNode(str, node->storedtype);
			break;

		default:
			break;
	}

}

static void
_outCreateOpClassStmt(StringInfo str, CreateOpClassStmt *node)
{
	appendStringInfoString(str, "CREATE OPERATOR CLASS ");
	_outFuncName(str, node->opclassname);

	if (node->isDefault == TRUE)
		appendStringInfoString(str, " DEFAULT");

	appendStringInfoString(str, " FOR TYPE ");
	_outNode(str, node->datatype);
	appendStringInfoString(str, " USING ");
	appendStringInfoString(str, node->amname);
	appendStringInfoString(str, " AS ");
	_outNode(str, node->items);
}

/*
 * Handle drop statements. As of pgpool-II 3.3(derived parser from
 * PostgreSQL 9.2), following types are supported:
 *
 * DROP TABLE, DROP SEQUENCE, DROP VIEW, DROP INDEX, DROP FOREIGN
 * TABLE, DROP TYPE, DROP DOMAIN, DROP COLLATION, DROP CONVERSION,
 * DROP SCHEMA, DROP TEXT SEARCH PARSER, DROP TEXT SEARCH DICTIONARY,
 * DROP TEXT SEARCH CONFIGURATION, DROP LANGUAGE, DROP RULE, DROP
 * OPERATOR, DROP OPERATOR CLASS
 */

static void
add_function_like_objs(StringInfo str, DropStmt *node)
{
	ListCell   *lc;
	char		comma = 0;

	if (node->concurrent)
		appendStringInfoString(str, "CONCURRENTLY ");
	if (node->missing_ok)
		appendStringInfoString(str, "IF EXISTS ");

	foreach(lc, node->objects)
	{
		if (comma == 0)
			comma = 1;
		else
			appendStringInfoString(str, ", ");
		_outFuncName(str, lfirst(lc));
	}
}

static void
_outDropStmt(StringInfo str, DropStmt *node)
{
	List	   *objname;
	char	   *p;
	char	   *p1;
	List	   *l;

	appendStringInfoString(str, "DROP ");
	switch (node->removeType)
	{
		case OBJECT_TABLE:
			appendStringInfoString(str, "TABLE ");
			add_function_like_objs(str, node);
			break;

		case OBJECT_SEQUENCE:
			appendStringInfoString(str, "SEQUENCE ");
			add_function_like_objs(str, node);
			break;

		case OBJECT_VIEW:
			appendStringInfoString(str, "VIEW ");
			add_function_like_objs(str, node);
			break;

		case OBJECT_INDEX:
			appendStringInfoString(str, "INDEX ");
			add_function_like_objs(str, node);
			break;

		case OBJECT_FOREIGN_TABLE:
			appendStringInfoString(str, "FOREIGN TABLE ");
			add_function_like_objs(str, node);
			break;

		case OBJECT_FDW:
			appendStringInfoString(str, "FOREIGN DATA WRAPPER ");
			add_function_like_objs(str, node);
			break;

		case OBJECT_TYPE:
			appendStringInfoString(str, "TYPE ");
			add_function_like_objs(str, node);
			break;

		case OBJECT_DOMAIN:
			appendStringInfoString(str, "DOMAIN ");
			add_function_like_objs(str, node);
			break;

		case OBJECT_COLLATION:
			appendStringInfoString(str, "COLLATION ");
			add_function_like_objs(str, node);
			break;

		case OBJECT_CONVERSION:
			appendStringInfoString(str, "CONVERSION ");
			add_function_like_objs(str, node);
			break;

		case OBJECT_SCHEMA:
			appendStringInfoString(str, "SCHEMA ");
			add_function_like_objs(str, node);
			break;

		case OBJECT_EXTENSION:
			appendStringInfoString(str, "EXTENSION ");
			add_function_like_objs(str, node);
			break;

		case OBJECT_TSPARSER:
			appendStringInfoString(str, "TEXT SEARCH PARSER ");
			add_function_like_objs(str, node);
			break;

		case OBJECT_TSDICTIONARY:
			appendStringInfoString(str, "TEXT SEARCH DICTIONARY ");
			add_function_like_objs(str, node);
			break;

		case OBJECT_TSTEMPLATE:
			appendStringInfoString(str, "TEXT SEARCH TEMPLATE ");
			add_function_like_objs(str, node);
			break;

		case OBJECT_TSCONFIGURATION:
			appendStringInfoString(str, "TEXT SEARCH CONFIGURATION ");
			add_function_like_objs(str, node);
			break;

		case OBJECT_LANGUAGE:
			appendStringInfoString(str, "PROCEDURAL LANGUAGE ");
			add_function_like_objs(str, node);
			break;

		case OBJECT_RULE:
			appendStringInfoString(str, "DROP RULE ");
			if (node->missing_ok)
				appendStringInfoString(str, "IF EXISTS ");
			objname = lfirst(list_head(node->objects));
			appendStringInfoString(str, strVal(llast(objname)));
			appendStringInfoString(str, " ON ");
			l = list_truncate(list_copy(objname),
							  list_length(objname) - 1);
			p = NameListToString(l);
			appendStringInfoString(str, p);
			pfree(p);
			list_free(l);
			break;

		case OBJECT_OPERATOR:
			appendStringInfoString(str, "OPERATOR ");
			add_function_like_objs(str, node);
			break;

		case OBJECT_OPCLASS:
			appendStringInfoString(str, "DROP OPERATOR CLASS ");
			if (node->missing_ok)
				appendStringInfoString(str, "IF EXISTS ");
			objname = lfirst(list_head(node->objects));
			appendStringInfoString(str, strVal(llast(objname)));
			appendStringInfoString(str, " USING ");
			appendStringInfoString(str, "'");
			l = list_truncate(list_copy(objname),
							  list_length(objname) - 1);
			p = NameListToString(l);
			p1 = escape_string(p);
			appendStringInfoString(str, p1);
			pfree(p1);
			pfree(p);
			list_free(l);
			appendStringInfoString(str, "'");
			break;

		case OBJECT_CAST:
			appendStringInfoString(str, "DROP CAST ");
			if (node->missing_ok)
				appendStringInfoString(str, "IF EXISTS ");
			appendStringInfoString(str, "(");
			objname = linitial(node->objects);
			_outNode(str, linitial(objname));
			appendStringInfoString(str, " AS ");
			_outNode(str, linitial(objname));
			appendStringInfoString(str, ")");
			break;

		case OBJECT_OPFAMILY:
			appendStringInfoString(str, "OPERATOR FAMILY ");
			if (node->missing_ok)
				appendStringInfoString(str, "IF EXISTS ");
			objname = linitial(node->objects);
			_outIdList(str, objname);
			appendStringInfoString(str, " USING ");
			_outIdList(str, objname);
			break;

		default:
			break;
	}

	if (node->behavior == DROP_CASCADE)
		appendStringInfoString(str, " CASCADE");
}

static void
_outFetchStmt(StringInfo str, FetchStmt *node)
{
	char		buf[16];

	snprintf(buf, 16, "%ld", node->howMany);

	if (node->ismove == TRUE)
		appendStringInfoString(str, "MOVE ");
	else
		appendStringInfoString(str, "FETCH ");

	switch (node->direction)
	{
		case FETCH_FORWARD:
			appendStringInfoString(str, "FORWARD ");
			if (node->howMany == FETCH_ALL)
				appendStringInfoString(str, "ALL ");
			else
			{
				appendStringInfoString(str, buf);
				appendStringInfoString(str, " ");
			}
			break;

		case FETCH_BACKWARD:
			appendStringInfoString(str, "BACKWARD ");
			if (node->howMany == FETCH_ALL)
				appendStringInfoString(str, "ALL ");
			else
			{
				appendStringInfoString(str, buf);
				appendStringInfoString(str, " ");
			}
			break;

		case FETCH_ABSOLUTE:
			if (node->howMany == 1)
				appendStringInfoString(str, "FIRST ");
			else if (node->howMany == -1)
				appendStringInfoString(str, "LAST ");
			else
			{
				appendStringInfoString(str, "ABSOLUTE ");
				appendStringInfoString(str, buf);
				appendStringInfoString(str, " ");
			}
			break;

		case FETCH_RELATIVE:
			appendStringInfoString(str, "RELATIVE ");
			appendStringInfoString(str, buf);
			appendStringInfoString(str, " ");
			break;
	}

	appendStringInfoString(str, "IN \"");
	appendStringInfoString(str, node->portalname);
	appendStringInfoString(str, "\"");
}

static void
_outPrivilegeList(StringInfo str, List *list)
{
	ListCell   *lc;
	char		comma = 0;

	if (list == NIL)
		appendStringInfoString(str, "ALL");
	else
	{
		foreach(lc, list)
		{
			A_Const    *v = (A_Const *) lfirst(lc);

			if (comma == 0)
				comma = 1;
			else
				appendStringInfoString(str, ", ");

			appendStringInfoString(str, v->val.sval.sval);
		}
	}
}

static void
_outFunctionParameter(StringInfo str, FunctionParameter *node)
{
	switch (node->mode)
	{
		case FUNC_PARAM_OUT:
			appendStringInfoString(str, "OUT ");
			break;

		case FUNC_PARAM_INOUT:
			appendStringInfoString(str, "INOUT ");
			break;

		default:
			break;
	}

	/* function name */
	if (node->name)
	{
		appendStringInfoString(str, "\"");
		appendStringInfoString(str, node->name);
		appendStringInfoString(str, "\" ");
	}

	_outNode(str, node->argType);
}

static void
_outObjectWithArgs(StringInfo str, ObjectWithArgs *node)
{
	_outFuncName(str, node->objname);
	appendStringInfoString(str, "(");
	_outNode(str, node->objargs);
	appendStringInfoString(str, ")");
}

static void
_outGrantStmt(StringInfo str, GrantStmt *node)
{
	if (node->is_grant == true)
		appendStringInfoString(str, "GRANT ");
	else
	{
		appendStringInfoString(str, "REVOKE ");
		if (node->grant_option == true)
			appendStringInfoString(str, "GRANT OPTION FOR ");
	}

	_outPrivilegeList(str, node->privileges);

	appendStringInfoString(str, " ON ");

	switch (node->objtype)
	{
		case OBJECT_TABLE:
			_outNode(str, node->objects);
			break;

		case OBJECT_SEQUENCE:
			appendStringInfoString(str, "SEQUENCE ");
			_outNode(str, node->objects);
			break;

		case OBJECT_FUNCTION:
			appendStringInfoString(str, "FUNCTION ");
			_outNode(str, node->objects);
			break;

		case OBJECT_DATABASE:
			appendStringInfoString(str, "DATABASE ");
			_outIdList(str, node->objects);
			break;

		case OBJECT_LANGUAGE:
			appendStringInfoString(str, "LANGUAGE ");
			_outIdList(str, node->objects);
			break;

		case OBJECT_SCHEMA:
			appendStringInfoString(str, "SCHEMA ");
			_outIdList(str, node->objects);
			break;

		case OBJECT_TABLESPACE:
			appendStringInfoString(str, "TABLESPACE ");
			_outIdList(str, node->objects);
			break;

		case OBJECT_FDW:
			appendStringInfoString(str, "FOREIGN DATA WRAPPER ");
			_outIdList(str, node->objects);
			break;

		case OBJECT_FOREIGN_SERVER:
			appendStringInfoString(str, "FOREIGN SERVER ");
			_outIdList(str, node->objects);
			break;

		case OBJECT_COLUMN:
		case OBJECT_DOMAIN:
		case OBJECT_LARGEOBJECT:
		case OBJECT_TYPE:
		case OBJECT_ACCESS_METHOD:
		case OBJECT_AGGREGATE:
		case OBJECT_AMOP:
		case OBJECT_AMPROC:
		case OBJECT_ATTRIBUTE:
		case OBJECT_CAST:
		case OBJECT_COLLATION:
		case OBJECT_CONVERSION:
		case OBJECT_DEFAULT:
		case OBJECT_DEFACL:
		case OBJECT_DOMCONSTRAINT:
		case OBJECT_EVENT_TRIGGER:
		case OBJECT_EXTENSION:
		case OBJECT_FOREIGN_TABLE:
		case OBJECT_INDEX:
		case OBJECT_MATVIEW:
		case OBJECT_OPCLASS:
		case OBJECT_OPERATOR:
		case OBJECT_OPFAMILY:
		case OBJECT_PARAMETER_ACL:
		case OBJECT_POLICY:
		case OBJECT_PROCEDURE:
		case OBJECT_PUBLICATION:
		case OBJECT_PUBLICATION_NAMESPACE:
		case OBJECT_PUBLICATION_REL:
		case OBJECT_ROLE:
		case OBJECT_ROUTINE:
		case OBJECT_RULE:
		case OBJECT_SUBSCRIPTION:
		case OBJECT_STATISTIC_EXT:
		case OBJECT_TABCONSTRAINT:
		case OBJECT_TRANSFORM:
		case OBJECT_TRIGGER:
		case OBJECT_TSCONFIGURATION:
		case OBJECT_TSDICTIONARY:
		case OBJECT_TSPARSER:
		case OBJECT_TSTEMPLATE:
		case OBJECT_USER_MAPPING:
		case OBJECT_VIEW:
			break;
	}

	if (node->is_grant == true)
		appendStringInfoString(str, " TO ");
	else
		appendStringInfoString(str, " FROM ");
	_outNode(str, node->grantees);

	if (node->is_grant == true && node->grant_option == TRUE)
		appendStringInfoString(str, " WITH GRANT OPTION");

	if (node->behavior == DROP_CASCADE)
		appendStringInfoString(str, " CASCADE");
}

static void
_outGrantRoleStmt(StringInfo str, GrantRoleStmt *node)
{
	if (node->is_grant == true)
		appendStringInfoString(str, "GRANT ");
	else
	{
		appendStringInfoString(str, "REVOKE ");
	}

	_outIdList(str, node->granted_roles);

	appendStringInfoString(str, node->is_grant == true ? " TO " : " FROM ");

	_outIdList(str, node->grantee_roles);

	if (node->grantor != NULL)
	{
		appendStringInfoString(str, " GRANTED BY \"");
		_outNode(str, node->grantor);
		appendStringInfoString(str, "\"");
	}

	if (node->behavior == DROP_CASCADE)
		appendStringInfoString(str, " CASCADE");
}

static void
_outFuncOptList(StringInfo str, List *list)
{
	ListCell   *lc;

	foreach(lc, list)
	{
		DefElem    *e = lfirst(lc);
		A_Const    *v = (A_Const *) e->arg;

		if (strcmp(e->defname, "strict") == 0)
		{
			if (v->val.boolval.boolval)
				appendStringInfoString(str, " STRICT");
			else
				appendStringInfoString(str, " CALLED ON NULL INPUT");
		}
		else if (strcmp(e->defname, "volatility") == 0)
		{
			char	   *s = v->val.sval.sval;

			if (strcmp(s, "immutable") == 0)
				appendStringInfoString(str, " IMMUTABLE");
			else if (strcmp(s, "stable") == 0)
				appendStringInfoString(str, " STABLE");
			else if (strcmp(s, "volatile") == 0)
				appendStringInfoString(str, " VOLATILE");
		}
		else if (strcmp(e->defname, "security") == 0)
		{
			if (v->val.boolval.boolval)
				appendStringInfoString(str, " SECURITY DEFINER");
			else
				appendStringInfoString(str, " SECURITY INVOKER");
		}
		else if (strcmp(e->defname, "as") == 0)
		{
			appendStringInfoString(str, " AS ");
			_outNode(str, e->arg);
		}
		else if (strcmp(e->defname, "language") == 0)
		{
			appendStringInfoString(str, " LANGUAGE '");
			appendStringInfoString(str, v->val.sval.sval);
			appendStringInfoString(str, "'");
		}
	}
}

static void
_outCreateFunctionStmt(StringInfo str, CreateFunctionStmt *node)
{
	appendStringInfoString(str, "CREATE ");
	if (node->replace == true)
		appendStringInfoString(str, "OR REPLACE ");
	appendStringInfoString(str, "FUNCTION ");

	_outFuncName(str, node->funcname);

	appendStringInfoString(str, " (");
	_outNode(str, node->parameters);
	appendStringInfoString(str, ")");

	if (node->returnType)
	{
		appendStringInfoString(str, " RETURNS ");
		_outNode(str, node->returnType);
	}

	_outFuncOptList(str, node->options);
}

static void
_outAlterFunctionStmt(StringInfo str, AlterFunctionStmt *node)
{
	appendStringInfoString(str, "ALTER FUNCTION ");
	_outNode(str, node->func);
	_outFuncOptList(str, node->actions);
}


static void
_outCreateCastStmt(StringInfo str, CreateCastStmt *node)
{
	appendStringInfoString(str, "CREATE CAST (");
	_outNode(str, node->sourcetype);
	appendStringInfoString(str, " AS ");
	_outNode(str, node->targettype);
	appendStringInfoString(str, ") WITH FUNCTION ");
	_outNode(str, node->func);

	switch (node->context)
	{
		case COERCION_IMPLICIT:
			appendStringInfoString(str, " AS IMPLICIT");
			break;

		case COERCION_ASSIGNMENT:
			appendStringInfoString(str, " AS ASSIGNMENT");
			break;

		default:
			break;
	}
}

static void
_outReindexStmt(StringInfo str, ReindexStmt *node)
{
	appendStringInfoString(str, "REINDEX ");

	switch (node->kind)
	{
		case REINDEX_OBJECT_SYSTEM:
			appendStringInfoString(str, "SYSTEM ");
			break;

		case REINDEX_OBJECT_DATABASE:
			appendStringInfoString(str, "DATABASE ");
			break;

		case REINDEX_OBJECT_INDEX:
			appendStringInfoString(str, "INDEX ");
			break;

		case REINDEX_OBJECT_TABLE:
			appendStringInfoString(str, "TABLE ");
			break;

		default:
			break;
	}

	if (node->relation)
		_outNode(str, node->relation);

	if (node->name)
	{
		appendStringInfoString(str, "\"");
		appendStringInfoString(str, (char *) node->name);
		appendStringInfoString(str, "\"");
	}
}

static void
_outAlterObjectSchemaStmt(StringInfo str, AlterObjectSchemaStmt *node)
{
	appendStringInfoString(str, "ALTER ");

	ObjectWithArgs *owa = castNode(ObjectWithArgs, node);

	switch (node->objectType)
	{
		case OBJECT_AGGREGATE:
			appendStringInfoString(str, "AGGREGATE ");
			_outFuncName(str, owa->objname);
			appendStringInfoString(str, "(");
			if (lfirst(list_head(owa->objargs)) == NULL)
				appendStringInfoString(str, "*");
			else
				_outNode(str, lfirst(list_head(owa->objargs)));
			appendStringInfoString(str, ") SET SCHEME \"");
			appendStringInfoString(str, node->newschema);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_DOMAIN:
			appendStringInfoString(str, "DOMAIN ");
			_outFuncName(str, owa->objname);
			appendStringInfoString(str, " SET SCHEMA \"");
			appendStringInfoString(str, node->newschema);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_FUNCTION:
			appendStringInfoString(str, "FUNCTION ");
			_outFuncName(str, owa->objname);
			appendStringInfoString(str, "(");
			appendStringInfoString(str, ") SET SCHEMA \"");
			appendStringInfoString(str, node->newschema);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_SEQUENCE:
			appendStringInfoString(str, "SEQUENCE ");
			_outNode(str, node->relation);
			appendStringInfoString(str, " SET SCHEMA \"");
			appendStringInfoString(str, node->newschema);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_TABLE:
			appendStringInfoString(str, "TABLE ");
			_outNode(str, node->relation);
			appendStringInfoString(str, " SET SCHEMA \"");
			appendStringInfoString(str, node->newschema);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_TYPE:
			appendStringInfoString(str, "TYPE ");
			_outFuncName(str, owa->objname);
			appendStringInfoString(str, " SET SCHEMA \"");
			appendStringInfoString(str, node->newschema);
			appendStringInfoString(str, "\"");
			break;

		default:
			break;
	}
}

static void
_outAlterOwnerStmt(StringInfo str, AlterOwnerStmt *node)
{
	appendStringInfoString(str, "ALTER ");

	ObjectWithArgs *owa = castNode(ObjectWithArgs, node);

	switch (node->objectType)
	{
		case OBJECT_AGGREGATE:
			appendStringInfoString(str, "AGGREGATE ");
			_outFuncName(str, owa->objname);
			appendStringInfoString(str, "(");
			if (lfirst(list_head(owa->objargs)) == NULL)
				appendStringInfoString(str, "*");
			else
				_outNode(str, lfirst(list_head(owa->objargs)));
			appendStringInfoString(str, ") OWNER TO \"");
			_outNode(str, node->newowner);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_CONVERSION:
			appendStringInfoString(str, "CONVERSION \"");
			appendStringInfoString(str, strVal(linitial(owa->objname)));
			appendStringInfoString(str, "\" OWNER TO \"");
			_outNode(str, node->newowner);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_DATABASE:
			appendStringInfoString(str, "DATABASE \"");
			appendStringInfoString(str, strVal(linitial(owa->objname)));
			appendStringInfoString(str, "\" OWNER TO \"");
			_outNode(str, node->newowner);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_DOMAIN:
			appendStringInfoString(str, "DOMAIN \"");
			appendStringInfoString(str, strVal(linitial(owa->objname)));
			appendStringInfoString(str, "\" OWNER TO \"");
			_outNode(str, node->newowner);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_FUNCTION:
			appendStringInfoString(str, "FUNCTION ");
			_outFuncName(str, owa->objname);
			appendStringInfoString(str, "(");
			_outNode(str, owa->objargs);
			appendStringInfoString(str, ") OWNER TO \"");
			_outNode(str, node->newowner);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_LANGUAGE:
			appendStringInfoString(str, "LANGUAGE \"");
			appendStringInfoString(str, strVal(linitial(owa->objname)));
			appendStringInfoString(str, "\" OWNER TO \"");
			_outNode(str, node->newowner);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_OPERATOR:
			appendStringInfoString(str, "OPERATOR ");
			_outOperatorName(str, owa->objname);
			appendStringInfoString(str, "(");
			appendStringInfoString(str, ") OWNER TO \"");
			_outNode(str, node->newowner);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_OPCLASS:
			appendStringInfoString(str, "OPERATOR CLASS ");
			_outFuncName(str, owa->objname);
			appendStringInfoString(str, " USING ");
			appendStringInfoString(str, strVal(linitial(owa->objargs)));
			appendStringInfoString(str, " OWNER TO \"");
			_outNode(str, node->newowner);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_OPFAMILY:
			appendStringInfoString(str, "OPERATOR FAMILY ");
			_outFuncName(str, owa->objname);
			appendStringInfoString(str, " USING ");
			appendStringInfoString(str, strVal(linitial(owa->objargs)));
			appendStringInfoString(str, " OWNER TO \"");
			_outNode(str, node->newowner);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_SCHEMA:
			appendStringInfoString(str, "SCHEMA \"");
			appendStringInfoString(str, strVal(linitial(owa->objname)));
			appendStringInfoString(str, "\" OWNER TO \"");
			_outNode(str, node->newowner);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_TYPE:
			appendStringInfoString(str, "TYPE \"");
			appendStringInfoString(str, strVal(linitial(owa->objname)));
			appendStringInfoString(str, "\" OWNER TO \"");
			_outNode(str, node->newowner);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_TABLESPACE:
			appendStringInfoString(str, "TABLESPACE \"");
			appendStringInfoString(str, strVal(linitial(owa->objname)));
			appendStringInfoString(str, "\" OWNER TO \"");
			_outNode(str, node->newowner);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_TSDICTIONARY:
			appendStringInfoString(str, "TEXT SEARCH DICTIONARY \"");
			appendStringInfoString(str, strVal(linitial(owa->objname)));
			appendStringInfoString(str, "\" OWNER TO \"");
			_outNode(str, node->newowner);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_TSCONFIGURATION:
			appendStringInfoString(str, "TEXT SEARCH CONFIGURATION \"");
			appendStringInfoString(str, strVal(linitial(owa->objname)));
			appendStringInfoString(str, "\" OWNER TO \"");
			_outNode(str, node->newowner);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_FDW:
			appendStringInfoString(str, "FOREIGN DATA WRAPPER \"");
			appendStringInfoString(str, strVal(linitial(owa->objname)));
			appendStringInfoString(str, "\" OWNER TO \"");
			_outNode(str, node->newowner);
			appendStringInfoString(str, "\"");
			break;

		case OBJECT_FOREIGN_SERVER:
			appendStringInfoString(str, "SERVER \"");
			appendStringInfoString(str, strVal(linitial(owa->objname)));
			appendStringInfoString(str, "\" OWNER TO \"");
			_outNode(str, node->newowner);
			appendStringInfoString(str, "\"");
			break;

		default:
			break;
	}
}

static void
_outRuleStmt(StringInfo str, RuleStmt *node)
{
	appendStringInfoString(str, "CREATE ");
	if (node->replace)
		appendStringInfoString(str, "OR REPLACE ");
	appendStringInfoString(str, "RULE \"");
	appendStringInfoString(str, node->rulename);
	appendStringInfoString(str, "\" AS ON ");

	switch (node->event)
	{
		case CMD_SELECT:
			appendStringInfoString(str, "SELECT");
			break;

		case CMD_UPDATE:
			appendStringInfoString(str, "UPDATE");
			break;

		case CMD_DELETE:
			appendStringInfoString(str, "DELETE");
			break;

		case CMD_INSERT:
			appendStringInfoString(str, "INSERT");
			break;

		default:
			break;
	}

	appendStringInfoString(str, " TO ");
	_outNode(str, node->relation);

	if (node->whereClause)
	{
		appendStringInfoString(str, " WHERE ");
		_outNode(str, node->whereClause);
	}

	appendStringInfoString(str, " DO ");

	if (node->instead)
		appendStringInfoString(str, "INSTEAD ");

	if (node->actions == NIL)
		appendStringInfoString(str, "NOTHING");
	else if (list_length(node->actions) == 1)
		_outNode(str, linitial(node->actions));
	else
	{
		ListCell   *lc;
		char		semi = 0;

		appendStringInfoString(str, "(");

		foreach(lc, node->actions)
		{
			if (semi == 0)
				semi = 1;
			else
				appendStringInfoString(str, ";");

			_outNode(str, lfirst(lc));
		}

		appendStringInfoString(str, ")");
	}
}

static void
_outViewStmt(StringInfo str, ViewStmt *node)
{
	if (node->replace)
		appendStringInfoString(str, "CREATE OR REPLACE ");
	else
		appendStringInfoString(str, "CREATE ");

	if (node->view->relpersistence == RELPERSISTENCE_TEMP)
		appendStringInfoString(str, "TEMP ");

	appendStringInfoString(str, "VIEW ");
	_outNode(str, node->view);

	if (node->aliases)
	{
		appendStringInfoString(str, "(");
		_outIdList(str, node->aliases);
		appendStringInfoString(str, ")");
	}

	appendStringInfoString(str, " AS");
	_outNode(str, node->query);
}

static void
_outCreatedbOptList(StringInfo str, List *options)
{
	ListCell   *lc;

	foreach(lc, options)
	{
		DefElem    *e = lfirst(lc);
		A_Const    *v = (A_Const *) e->arg;

		/* keyword */
		if (strcmp(e->defname, "template") == 0)
			appendStringInfoString(str, " TEMPLATE ");
		else if (strcmp(e->defname, "location") == 0)
		{
			appendStringInfoString(str, " LOCATION ");
		}
		else if (strcmp(e->defname, "tablespace") == 0)
			appendStringInfoString(str, " TABLESPACE ");
		else if (strcmp(e->defname, "encoding") == 0)
		{
			appendStringInfoString(str, " ENCODING ");
		}
		else if (strcmp(e->defname, "owner") == 0)
			appendStringInfoString(str, " OWNER ");
		else if (strcmp(e->defname, "connectionlimit") == 0)
			appendStringInfoString(str, " CONNECTION LIMIT ");

		/* value */
		if (v == NULL)
			appendStringInfoString(str, "DEFAULT");
		else if (IsA(&v->val, String))
		{
			appendStringInfoString(str, "'");
			appendStringInfoString(str, v->val.sval.sval);
			appendStringInfoString(str, "'");
		}
		else if (IsA(&v->val, Integer))
		{
			char		buf[16];

			snprintf(buf, sizeof(buf), "%d", v->val.ival.ival);
			appendStringInfoString(str, buf);
		}
	}
}

static void
_outCreatedbStmt(StringInfo str, CreatedbStmt *node)
{
	appendStringInfoString(str, "CREATE DATABASE \"");
	appendStringInfoString(str, node->dbname);
	appendStringInfoString(str, "\"");

	_outCreatedbOptList(str, node->options);
}

static void
_outAlterDatabaseStmt(StringInfo str, AlterDatabaseStmt *node)
{
	appendStringInfoString(str, "ALTER DATABASE \"");
	appendStringInfoString(str, node->dbname);
	appendStringInfoString(str, "\" ");

	_outCreatedbOptList(str, node->options);
}

static void
_outAlterDatabaseSetStmt(StringInfo str, AlterDatabaseSetStmt *node)
{
	appendStringInfoString(str, "ALTER DATABASE \"");
	appendStringInfoString(str, node->dbname);
	appendStringInfoString(str, "\" ");

	_outNode(str, node->setstmt);
}

static void
_outDropdbStmt(StringInfo str, DropdbStmt *node)
{
	appendStringInfoString(str, "DROP DATABASE \"");
	appendStringInfoString(str, node->dbname);
	appendStringInfoString(str, "\"");
}

static void
_outCreateDomainStmt(StringInfo str, CreateDomainStmt *node)
{
	ListCell   *lc;

	appendStringInfoString(str, "CREATE DOMAIN ");
	_outFuncName(str, node->domainname);
	appendStringInfoString(str, " ");
	_outNode(str, node->typeName);


	foreach(lc, node->constraints)
	{
		appendStringInfoString(str, " ");
		_outNode(str, lfirst(lc));
	}
}

static void
_outAlterDomainStmt(StringInfo str, AlterDomainStmt *node)
{
	appendStringInfoString(str, "ALTER DOMAIN ");
	_outFuncName(str, node->typeName);

	switch (node->subtype)
	{
		case 'T':
			if (node->def)
			{
				appendStringInfoString(str, " SET DEFAULT ");
				_outNode(str, node->def);
			}
			else
				appendStringInfoString(str, " DROP DEFAULT");
			break;

		case 'N':
			appendStringInfoString(str, " DROP NOT NULL");
			break;

		case 'O':
			appendStringInfoString(str, " SET NOT NULL");
			break;

		case 'C':
			appendStringInfoString(str, " ADD ");
			_outNode(str, node->def);
			break;

		case 'X':
			appendStringInfoString(str, " DROP CONSTRAINT \"");
			appendStringInfoString(str, node->name);
			appendStringInfoString(str, "\"");
			if (node->behavior == DROP_CASCADE)
				appendStringInfoString(str, " CASCADE");
			break;
	}
}

static void
_outCreateConversionStmt(StringInfo str, CreateConversionStmt *node)
{
	appendStringInfoString(str, "CREATE ");

	if (node->def == TRUE)
		appendStringInfoString(str, "DEFAULT ");

	appendStringInfoString(str, "CONVERSION ");

	_outFuncName(str, node->conversion_name);

	appendStringInfoString(str, " FOR '");
	appendStringInfoString(str, node->for_encoding_name);
	appendStringInfoString(str, "' TO '");
	appendStringInfoString(str, node->to_encoding_name);
	appendStringInfoString(str, " FROM ");
	_outFuncName(str, node->func_name);
}

static void
_outPrepareStmt(StringInfo str, PrepareStmt *node)
{
	appendStringInfoString(str, "PREPARE \"");
	appendStringInfoString(str, node->name);
	appendStringInfoString(str, "\" ");

	if (node->argtypes != NIL)
	{
		appendStringInfoString(str, "(");
		_outNode(str, node->argtypes);
		appendStringInfoString(str, ") ");
	}

	appendStringInfoString(str, "AS ");
	_outNode(str, node->query);
}

static void
_outExecuteStmt(StringInfo str, ExecuteStmt *node)
{
	appendStringInfoString(str, "EXECUTE \"");
	appendStringInfoString(str, node->name);
	appendStringInfoString(str, "\" ");

	if (node->params != NIL)
	{
		appendStringInfoString(str, "(");
		_outNode(str, node->params);
		appendStringInfoString(str, ")");
	}
}

static void
_outLockStmt(StringInfo str, LockStmt *node)
{
	appendStringInfoString(str, "LOCK TABLE ");
	_outNode(str, node->relations);

	appendStringInfoString(str, " IN ");
	switch (node->mode)
	{
		case AccessShareLock:
			appendStringInfoString(str, "ACCESS SHARE ");
			break;

		case RowShareLock:
			appendStringInfoString(str, "ROW SHARE ");
			break;

		case RowExclusiveLock:
			appendStringInfoString(str, "ROW EXCLUSIVE ");
			break;

		case ShareUpdateExclusiveLock:
			appendStringInfoString(str, "SHARE UPDATE EXCLUSIVE ");
			break;

		case ShareLock:
			appendStringInfoString(str, "SHARE ");
			break;

		case ShareRowExclusiveLock:
			appendStringInfoString(str, "SHARE ROW EXCLUSIVE ");
			break;

		case ExclusiveLock:
			appendStringInfoString(str, "EXCLUSIVE ");
			break;

		case AccessExclusiveLock:
			appendStringInfoString(str, "ACCESS EXCLUSIVE ");
			break;
	}
	appendStringInfoString(str, "MODE");

	if (node->nowait == TRUE)
		appendStringInfoString(str, " NOWAIT");
}

static void
_outOperatorArgTypes(StringInfo str, List *args)
{
	TypeName   *left,
			   *right;

	left = linitial(args);
	right = lsecond(args);

	if (left)
		_outNode(str, left);
	else
		appendStringInfoString(str, "NONE");
	appendStringInfoString(str, ", ");
	if (right)
		_outNode(str, right);
	else
		appendStringInfoString(str, "NONE");
}

static void
_outCommentStmt(StringInfo str, CommentStmt *node)
{
	TypeName   *t;
	A_Const    *v;
	char		buf[16];

	appendStringInfoString(str, "COMMENT ON ");

	ObjectWithArgs *owa = castNode(ObjectWithArgs, node);

	switch (node->objtype)
	{
		case OBJECT_AGGREGATE:
			appendStringInfoString(str, "AGGREGATE ");
			_outFuncName(str, owa->objname);
			appendStringInfoString(str, "(");

			t = linitial(owa->objargs);
			if (t)
				_outNode(str, t);
			else
				appendStringInfoString(str, "*");
			appendStringInfoString(str, ")");
			break;

		case OBJECT_FUNCTION:
			appendStringInfoString(str, "FUNCTION ");
			_outFuncName(str, owa->objname);
			appendStringInfoString(str, "(");
			_outNode(str, owa->objargs);
			appendStringInfoString(str, ")");
			break;

		case OBJECT_OPERATOR:
			appendStringInfoString(str, "OPERATOR ");
			_outOperatorName(str, owa->objname);
			appendStringInfoString(str, "(");
			_outOperatorArgTypes(str, owa->objargs);
			appendStringInfoString(str, ")");
			break;

		case OBJECT_TABCONSTRAINT:
		case OBJECT_DOMCONSTRAINT:
			appendStringInfoString(str, "CONSTRAINT \"");
			v = lsecond(owa->objname);
			appendStringInfoString(str, v->val.sval.sval);
			appendStringInfoString(str, "\" ON ");
			_outFuncName(str, linitial(owa->objargs));
			break;

		case OBJECT_RULE:
			appendStringInfoString(str, "RULE \"");
			v = lsecond(owa->objname);
			appendStringInfoString(str, v->val.sval.sval);
			appendStringInfoString(str, "\" ON ");
			_outFuncName(str, linitial(owa->objargs));
			break;

		case OBJECT_TRIGGER:
			appendStringInfoString(str, "TRIGGER \"");
			v = lsecond(owa->objname);
			appendStringInfoString(str, v->val.sval.sval);
			appendStringInfoString(str, "\" ON ");
			_outFuncName(str, linitial(owa->objargs));
			break;

		case OBJECT_OPCLASS:
			appendStringInfoString(str, "OPERATOR CLASS ");
			_outFuncName(str, owa->objname);
			appendStringInfoString(str, " USING ");
			v = linitial(owa->objargs);
			appendStringInfoString(str, v->val.sval.sval);
			break;

		case OBJECT_LARGEOBJECT:
			appendStringInfoString(str, "LARGE OBJECT ");
			v = (A_Const *) linitial(owa->objname);
			if (IsA(&v->val, String))
				appendStringInfoString(str, v->val.sval.sval);
			else if (IsA(&v->val, Integer))
			{
				snprintf(buf, 16, "%d", v->val.ival.ival);
				appendStringInfoString(str, buf);
			}
			break;

		case OBJECT_CAST:
			appendStringInfoString(str, "CAST (");
			_outNode(str, linitial(owa->objname));
			appendStringInfoString(str, " AS ");
			_outNode(str, linitial(owa->objargs));
			appendStringInfoString(str, ")");
			break;

		case OBJECT_LANGUAGE:
			appendStringInfoString(str, "LANGUAGE ");
			_outFuncName(str, owa->objname);
			break;

		default:
			switch (node->objtype)
			{
				case OBJECT_COLUMN:
					appendStringInfoString(str, "COLUMN ");
					break;
				case OBJECT_DATABASE:
					appendStringInfoString(str, "DATABASE ");
					break;
				case OBJECT_SCHEMA:
					appendStringInfoString(str, "SCHEMA ");
					break;
				case OBJECT_INDEX:
					appendStringInfoString(str, "INDEX ");
					break;
				case OBJECT_SEQUENCE:
					appendStringInfoString(str, "SEQUENCE ");
					break;
				case OBJECT_TABLE:
					appendStringInfoString(str, "TABLE ");
					break;
				case OBJECT_DOMAIN:
					appendStringInfoString(str, "DOMAIN ");
					break;
				case OBJECT_TYPE:
					appendStringInfoString(str, "TYPE ");
					break;
				case OBJECT_VIEW:
					appendStringInfoString(str, "VIEW ");
					break;
				default:
					break;
			}
			_outFuncName(str, owa->objname);
			break;
	}

	appendStringInfoString(str, " IS ");
	if (node->comment)
	{
		appendStringInfoString(str, "'");
		appendStringInfoString(str, node->comment);
		appendStringInfoString(str, "'");
	}
	else
		appendStringInfoString(str, "NULL");
}

static void
_outRangeSubselect(StringInfo str, RangeSubselect *node)
{
	appendStringInfoString(str, "(");
	_outNode(str, node->subquery);
	appendStringInfoString(str, ")");

	_outNode(str, node->alias);
}

/*TODO*/
static void
_outRangeFunction(StringInfo str, RangeFunction *node)
{
	_outNode(str, node->functions);
	/* TODO */
	if (node->alias)
	{
		_outNode(str, node->alias);
	}

	if (node->coldeflist)
	{
		appendStringInfoString(str, " (");
		_outNode(str, node->coldeflist);
		appendStringInfoString(str, ")");
	}
}

static void
_outRangeTableSample(StringInfo str, RangeTableSample *node)
{
	_outNode(str, node->relation);
	appendStringInfoString(str, " TABLESAMPLE ");
	_outFuncName(str, node->method);
	appendStringInfoString(str, " (");
	_outNode(str, node->args);
	appendStringInfoString(str, ")");

	if (node->repeatable)
	{
		appendStringInfoString(str, " REPEATABLE (");
		_outNode(str, node->repeatable);
		appendStringInfoString(str, ")");
	}
}

static void
_outRangeTableFunc(StringInfo str, RangeTableFunc *node)
{
}

static void
_outRangeTableFuncCol(StringInfo str, RangeTableFuncCol *node)
{
}

static void
_outDiscardStmt(StringInfo str, DiscardStmt *node)
{
	switch (node->target)
	{
		case DISCARD_ALL:
			appendStringInfoString(str, "DISCARD ALL");
			break;

		case DISCARD_TEMP:
			appendStringInfoString(str, "DISCARD TEMP");
			break;

		case DISCARD_PLANS:
			appendStringInfoString(str, "DISCARD PLANS");
			break;

		default:
			break;
	}
}

static void
_outCreateOpFamilyStmt(StringInfo str, CreateOpFamilyStmt *node)
{
	appendStringInfoString(str, "CREATE OPERATOR FAMILY ");
	_outIdList(str, node->opfamilyname);
	appendStringInfoString(str, " USING \"");
	appendStringInfoString(str, node->amname);
	appendStringInfoString(str, "\"");
}

static void
_outAlterOpFamilyStmt(StringInfo str, AlterOpFamilyStmt *node)
{
}

static void
_outCreateEnumStmt(StringInfo str, CreateEnumStmt *node)
{
	appendStringInfoString(str, "CREATE TYPE ");
	_outIdList(str, node->typeName);
	appendStringInfoString(str, " AS ENUM (");
	_outNode(str, node->vals);
	appendStringInfoString(str, ")");
}

static void
_outDropOwnedStmt(StringInfo str, DropOwnedStmt *node)
{
	appendStringInfoString(str, "DROP OWNED BY ");
	_outIdList(str, node->roles);
	if (node->behavior == DROP_CASCADE)
		appendStringInfoString(str, " CASCADE");
}

static void
_outReassignOwnedStmt(StringInfo str, ReassignOwnedStmt *node)
{
	appendStringInfoString(str, "REASSIGN OWNED BY ");
	_outIdList(str, node->roles);
	appendStringInfoString(str, " TO \"");
	_outNode(str, node->newrole);
	appendStringInfoString(str, "\"");
}

static void
_outAlterTSDictionaryStmt(StringInfo str, AlterTSDictionaryStmt *node)
{
	appendStringInfoString(str, "ALTER TEXT SEARCH DICTIONARY ");
	_outIdList(str, node->dictname);
	appendStringInfoString(str, "(");
	_outNode(str, node->options);
	appendStringInfoString(str, ")");
}

static void
_outAlterTSConfigurationStmt(StringInfo str, AlterTSConfigurationStmt *node)
{
	appendStringInfoString(str, "ALTER TEXT SEARCH CONFIGURATION ");
	_outIdList(str, node->cfgname);
	if (node->override == false && node->replace == false)
	{
		appendStringInfoString(str, "ADD MAPPING FOR ");
		_outIdList(str, node->tokentype);
		appendStringInfoString(str, " WITH ");
		_outIdList(str, node->dicts);
	}
	else if (node->override == true && node->replace == false)
	{
		appendStringInfoString(str, "ALTER MAPPING FOR ");
		_outIdList(str, node->tokentype);
		appendStringInfoString(str, " WITH ");
		_outIdList(str, node->dicts);
	}
	else if (node->override == false && node->replace == true)
	{
		if (node->tokentype == NIL)
			appendStringInfoString(str, "ALTER MAPPING ");
		else
		{
			appendStringInfoString(str, "ALTER MAPPING FOR ");
			_outIdList(str, node->tokentype);
		}
		appendStringInfoString(str, "REPLACE ");
		_outNode(str, linitial(node->dicts));
		appendStringInfoString(str, " WITH ");
		_outNode(str, lsecond(node->dicts));
	}
	else if (node->missing_ok == false)
	{
		appendStringInfoString(str, " DROP MAPPING FOR ");
		_outIdList(str, node->tokentype);
	}
	else if (node->missing_ok == true)
	{
		appendStringInfoString(str, " DROP MAPPING IF EXISTS FOR ");
		_outIdList(str, node->tokentype);
	}
}

static void
_outXmlExpr(StringInfo str, XmlExpr *node)
{
	A_Const    *n;

	switch (node->op)
	{
		case IS_DOCUMENT:
			_outNode(str, node->args);
			appendStringInfoString(str, " IS DOCUMENT");
			break;

		case IS_XMLCONCAT:
			appendStringInfoString(str, "XMLCONCAT (");
			_outNode(str, node->args);
			appendStringInfoString(str, ")");
			break;

		case IS_XMLELEMENT:
			appendStringInfoString(str, "XMLELEMENT (");
			if (node->name)
			{
				appendStringInfoString(str, "NAME \"");
				appendStringInfoString(str, node->name);
				appendStringInfoString(str, "\"");
				if (node->named_args != NIL)
				{
					appendStringInfoString(str, ",");
					_outIdList(str, node->named_args);
				}
			}
			if (node->args != NIL)
			{
				appendStringInfoString(str, ",");
				_outNode(str, node->args);
			}
			appendStringInfoString(str, ")");
			break;

		case IS_XMLFOREST:
			appendStringInfoString(str, "XMLFOREST (");
			_outNode(str, node->named_args);
			appendStringInfoString(str, ")");
			break;

		case IS_XMLPARSE:
			appendStringInfoString(str, "XMLPARSE (");
			if (node->xmloption == XMLOPTION_DOCUMENT)
				appendStringInfoString(str, "DOCUMENT ");
			else
				appendStringInfoString(str, "CONTENT ");

			_outNode(str, linitial(node->args));
			n = lsecond(node->args);
			{
				Node	   *arg = ((TypeCast *) n)->arg;

				if (((A_Const *) arg)->val.sval.sval[0] == 't')
					appendStringInfoString(str, " PRESERVE WHITESPACE");
			}

			appendStringInfoString(str, ")");
			break;


		default:
			break;
	}
}


static void
_outJsonFormat(StringInfo str, const JsonFormat *node)
{
}

static void
_outJsonReturning(StringInfo str, const JsonReturning *node)
{
}

static void
_outJsonValueExpr(StringInfo str, const JsonValueExpr *node)
{
}

static void
_outJsonConstructorExpr(StringInfo str, const JsonConstructorExpr *node)
{
}

static void
_outJsonIsPredicate(StringInfo str, const JsonIsPredicate *node)
{
}

static void
_outJsonOutput(StringInfo str, const JsonOutput *node)
{
}

static void
_outJsonKeyValue(StringInfo str, const JsonKeyValue *node)
{
}

static void
_outJsonParseExpr(StringInfo str, const JsonParseExpr *node)
{
}

static void
_outJsonScalarExpr(StringInfo str, const JsonScalarExpr *node)
{
}

static void
_outJsonSerializeExpr(StringInfo str, const JsonSerializeExpr *node)
{
}

static void
_outJsonObjectConstructor(StringInfo str, const JsonObjectConstructor *node)
{
}

static void
_outJsonArrayConstructor(StringInfo str, const JsonArrayConstructor *node)
{
}


static void
_outJsonArrayQueryConstructor(StringInfo str, const JsonArrayQueryConstructor *node)
{
}

static void
_outJsonAggConstructor(StringInfo str, const JsonAggConstructor *node)
{
}

static void
_outJsonObjectAgg(StringInfo str, const JsonObjectAgg *node)
{
}

static void
_outJsonArrayAgg(StringInfo str, const JsonArrayAgg *node)
{
}

static void
_outXmlSerialize(StringInfo str, XmlSerialize *node)
{

}

static void
_outWithDefinition(StringInfo str, List *def_list)
{
	int			oid = 0;

	if (list_length(def_list) == 1)
	{
		DefElem    *elem;

		elem = linitial(def_list);
		if (strcmp(elem->defname, "oids") == 0)
		{
			Integer    *v = (Integer *) elem->arg;

			if (v->ival == 1)
				appendStringInfoString(str, " WITH OIDS ");
			else
				appendStringInfoString(str, " WITHOUT OIDS ");
			oid = 1;
		}
	}

	if (oid == 1)
		return;

	appendStringInfoString(str, " WITH ");
	_outDefinition(str, def_list);
}

static void
_outOnConflictClause(StringInfo str, OnConflictClause *node)
{
	appendStringInfoString(str, " ON CONFLICT ");

	if (node->infer)
	{
		if (node->infer->indexElems != NIL)
		{
			appendStringInfoString(str, " ( ");
			_outList(str, node->infer->indexElems);
			appendStringInfoString(str, " ) ");

			if (node->infer->whereClause)
			{
				appendStringInfoString(str, " WHERE ");
				_outNode(str, node->infer->whereClause);
			}
		}
		else
		{
			appendStringInfoString(str, " ON CONSTRAINT ");
			appendStringInfoString(str, node->infer->conname);
		}
	}

	switch (node->action)
	{
		case ONCONFLICT_UPDATE:
			appendStringInfoString(str, " DO UPDATE ");
			break;

		case ONCONFLICT_NOTHING:
			appendStringInfoString(str, " DO NOTHING ");
			break;

		default:
			break;
	}

	if (node->targetList)
	{
		_outSetClause(str, node->targetList);
	}

	if (node->whereClause)
	{
		appendStringInfoString(str, " WHERE ");
		_outNode(str, node->whereClause);
	}
}

static void
_outPartitionElem(StringInfo str, PartitionElem *node)
{
}

static void
_outPartitionSpec(StringInfo str, PartitionSpec *node)
{
}

static void
_outPartitionBoundSpec(StringInfo str, PartitionBoundSpec *node)
{
}

static void
_outPartitionRangeDatum(StringInfo str, PartitionRangeDatum *node)
{
}

/*
 * _outNode -
 *	  converts a Node into ascii string and append it to 'str'
 */
void
_outNode(StringInfo str, void *obj)
{
	/* Guard against stack overflow due to overly complex expressions */
	/*
	 * check_stack_depth();
	 */

	if (obj == NULL)
		return;
	else if (IsA(obj, List) || IsA(obj, IntList) || IsA(obj, OidList) ||
			 IsA(obj, XidList))
		_outList(str, obj);
	/* nodeRead does not want to see { } around these! */
	else if (IsA(obj, Integer))
		_outInteger(str, (Integer *) obj);
	else if (IsA(obj, Float))
		_outFloat(str, (Float *) obj);
	else if (IsA(obj, Boolean))
		_outBoolean(str, (Boolean *) obj);
	else if (IsA(obj, String))
		_outString(str, (String *) obj);
	else if (IsA(obj, BitString))
		_outBitString(str, (BitString *) obj);
	else if (IsA(obj, Bitmapset))
		_outBitmapset(str, (Bitmapset *) obj);
	else
	{
		switch (nodeTag(obj))
		{
			case T_Alias:
				_outAlias(str, obj);
				break;
			case T_RangeVar:
				_outRangeVar(str, obj);
				break;

				/*
				 * case T_IntoClause: _outIntoClause(str, obj); break;
				 */
			case T_Var:
				_outVar(str, obj);
				break;
			case T_Const:
				_outConst(str, obj);
				break;
			case T_Param:
				_outParam(str, obj);
				break;
			case T_Aggref:
				_outAggref(str, obj);
				break;
			case T_GroupingFunc:
				_outGroupingFunc(str, obj);
				break;

				/*
				 * case T_WindowFunc: _outWindowFunc(str, obj); break;
				 */
			case T_WindowFuncRunCondition:
				_outWindowFuncRunCondition(str, obj);
				break;
			case T_MergeSupportFunc:
				_outMergeSupportFunc(str, obj);
				break;
			case T_SubscriptingRef:
				_outSubscriptingRef(str, obj);
				break;
			case T_FuncExpr:
				_outFuncExpr(str, obj);
				break;
			case T_NamedArgExpr:
				_outNamedArgExpr(str, obj);
				break;
			case T_OpExpr:
				_outOpExpr(str, obj);
				break;
			case T_DistinctExpr:
				_outDistinctExpr(str, obj);
				break;
			case T_NullIfExpr:
				_outNullIfExpr(str, obj);
				break;
			case T_ScalarArrayOpExpr:
				_outScalarArrayOpExpr(str, obj);
				break;
			case T_BoolExpr:
				_outBoolExpr(str, obj);
				break;
			case T_SubLink:
				_outSubLink(str, obj);
				break;
			case T_SubPlan:
				_outSubPlan(str, obj);
				break;

				/*
				 * case T_AlternativeSubPlan: _outAlternativeSubPlan(str,
				 * obj); break;
				 */
			case T_FieldSelect:
				_outFieldSelect(str, obj);
				break;
			case T_FieldStore:
				_outFieldStore(str, obj);
				break;
			case T_RelabelType:
				_outRelabelType(str, obj);
				break;

				/*
				 * case T_CoerceViaIO: _outCoerceViaIO(str, obj); break; case
				 * T_ArrayCoerceExpr: _outArrayCoerceExpr(str, obj); break;
				 */
			case T_ConvertRowtypeExpr:
				_outConvertRowtypeExpr(str, obj);
				break;

				/*
				 * case T_CollateExpr: _outCollateExpr(str, obj); break;
				 */
			case T_CaseExpr:
				_outCaseExpr(str, obj);
				break;
			case T_CaseWhen:
				_outCaseWhen(str, obj);
				break;
			case T_CaseTestExpr:
				_outCaseTestExpr(str, obj);
				break;
			case T_ArrayExpr:
				_outArrayExpr(str, obj);
				break;
			case T_RowExpr:
				_outRowExpr(str, obj);
				break;

				/*
				 * case T_RowCompareExpr: _outRowCompareExpr(str, obj); break;
				 */
			case T_CoalesceExpr:
				_outCoalesceExpr(str, obj);
				break;
			case T_MinMaxExpr:
				_outMinMaxExpr(str, obj);
				break;

				/*
				 * case T_SQLValueFunction: _outSQLValueFunction(str, obj);
				 */
				break;
			case T_XmlExpr:
				_outXmlExpr(str, obj);
				break;
			case T_JsonFormat:
				_outJsonFormat(str, obj);
				break;
			case T_JsonReturning:
				_outJsonReturning(str, obj);
				break;
			case T_JsonValueExpr:
				_outJsonValueExpr(str, obj);
				break;
			case T_JsonConstructorExpr:
				_outJsonConstructorExpr(str, obj);
				break;
			case T_JsonIsPredicate:
				_outJsonIsPredicate(str, obj);
				break;
			case T_JsonBehavior:
				_outJsonBehavior(str, obj);
				break;
			case T_JsonExpr:
				_outJsonExpr(str, obj);
				break;
			case T_JsonTablePath:
				_outJsonTablePath(str, obj);
				break;
			case T_JsonTablePathScan:
				_outJsonTablePathScan(str, obj);
				break;
			case T_JsonTableSiblingJoin:
				_outJsonTableSiblingJoin(str, obj);
				break;
			case T_NullTest:
				_outNullTest(str, obj);
				break;
			case T_BooleanTest:
				_outBooleanTest(str, obj);
				break;
			case T_MergeAction:
				_outMergeAction(str, obj);
				break;
			case T_CoerceToDomain:
				_outCoerceToDomain(str, obj);
				break;
			case T_CoerceToDomainValue:
				_outCoerceToDomainValue(str, obj);
				break;
			case T_SetToDefault:
				_outSetToDefault(str, obj);
				break;
			case T_CurrentOfExpr:
				_outCurrentOfExpr(str, obj);
				break;

				/*
				 * case T_NextValueExpr: _outNextValueExpr(str, obj); break;
				 */
			case T_InferenceElem:
				_outInferenceElem(str, obj);
				break;
			case T_ReturningExpr:
				_outReturningExpr(str, obj);
				break;
			case T_TargetEntry:
				_outTargetEntry(str, obj);
				break;
			case T_RangeTblRef:
				_outRangeTblRef(str, obj);
				break;
			case T_JoinExpr:
				_outJoinExpr(str, obj);
				break;
			case T_FromExpr:
				_outFromExpr(str, obj);
				break;
			case T_OnConflictExpr:
				_outOnConflictExpr(str, obj);
				break;
#ifdef NOT_USED
			case T_ExtensibleNode:
				_outExtensibleNode(str, obj);
				break;
#endif
			case T_CreateStmt:
				_outCreateStmt(str, obj);
				break;
			case T_CreateTableAsStmt:
				_outCreateTableAsStmt(str, obj);
				break;
			case T_CreateForeignTableStmt:
				_outCreateForeignTableStmt(str, obj);
				break;
			case T_ImportForeignSchemaStmt:
				_outImportForeignSchemaStmt(str, obj);
				break;
			case T_IndexStmt:
				_outIndexStmt(str, obj);
				break;
			case T_CreateStatsStmt:
				_outCreateStatsStmt(str, obj);
				break;
			case T_AlterStatsStmt:
				_outAlterStatsStmt(str, obj);
				break;
			case T_NotifyStmt:
				_outNotifyStmt(str, obj);
				break;
			case T_DeclareCursorStmt:
				_outDeclareCursorStmt(str, obj);
				break;
			case T_SelectStmt:
				_outSelectStmt(str, obj);
				break;
			case T_ReturnStmt:
				_outReturnStmt(str, obj);
				break;
			case T_PLAssignStmt:
				_outPLAssignStmt(str, obj);
				break;
			case T_ColumnDef:
				_outColumnDef(str, obj);
				break;
			case T_TypeName:
				_outTypeName(str, obj);
				break;
			case T_TypeCast:
				_outTypeCast(str, obj);
				break;
			case T_CollateClause:
				_outCollateClause(str, obj);
				break;
			case T_IndexElem:
				_outIndexElem(str, obj);
				break;

				/*
				 * case T_Query: _outQuery(str, obj); break; case
				 * T_WithCheckOption: _outWithCheckOption(str, obj); break;
				 * case T_SortGroupClause: _outSortGroupClause(str, obj);
				 * break;
				 */
			case T_GroupingSet:
				_outGroupingSet(str, obj);
				break;

				/*
				 * case T_WindowClause: _outWindowClause(str, obj); break;
				 * case T_RowMarkClause: _outRowMarkClause(str, obj); break;
				 */
			case T_WithClause:
				_outWithClause(str, obj);
				break;
			case T_CTESearchClause:
				_outCTESearchClause(str, obj);
				break;
			case T_CTECycleClause:
				_outCTECycleClause(str, obj);
				break;
			case T_CommonTableExpr:
				_outCommonTableExpr(str, obj);
				break;
			case T_MergeWhenClause:
				_outMergeWhenClauses(str, obj);
				break;
			case T_ReturningOption:
				_outReturningOption(str, obj);
				break;
			case T_ReturningClause:
				_outReturningClause(str, obj);
				break;
			case T_SetOperationStmt:
				_outSetOperationStmt(str, obj);
				break;

				/*
				 * case T_RangeTblEntry: _outRangeTblEntry(str, obj); break;
				 */
			case T_TableSampleClause:
				_outTableSampleClause(str, obj);
				break;
			case T_A_Expr:
				_outA_Expr(str, obj);
				break;
			case T_ColumnRef:
				_outColumnRef(str, obj);
				break;
			case T_ParamRef:
				_outParamRef(str, obj);
				break;
			case T_RawStmt:
				_outRawStmt(str, obj);
				break;
			case T_A_Const:
				_outA_Const(str, obj);
				break;

				/*
				 * case T_A_Star: _outA_Star(str, obj); break;
				 */
			case T_A_Indices:
				_outA_Indices(str, obj);
				break;
			case T_A_Indirection:
				_outA_Indirection(str, obj);
				break;
			case T_A_ArrayExpr:
				_outA_ArrayExpr(str, obj);
				break;
			case T_ResTarget:
				_outResTarget(str, obj);
				break;
			case T_MultiAssignRef:
				_outMultiAssignRef(str, obj);
				break;
			case T_SortBy:
				_outSortBy(str, obj);
				break;
			case T_WindowDef:
				_outWindowDef(str, obj);
				break;
			case T_RangeSubselect:
				_outRangeSubselect(str, obj);
				break;
			case T_RangeFunction:
				_outRangeFunction(str, obj);
				break;
			case T_RangeTableSample:
				_outRangeTableSample(str, obj);
				break;
			case T_RangeTableFunc:
				_outRangeTableFunc(str, obj);
				break;
			case T_RangeTableFuncCol:
				_outRangeTableFuncCol(str, obj);
				break;
			case T_Constraint:
				_outConstraint(str, obj);
				break;
			case T_FuncCall:
				_outFuncCall(str, obj);
				break;
			case T_DefElem:
				_outDefElem(str, obj);
				break;

				/*
				 * case T_TableLikeClause: _outTableLikeClause(str, obj);
				 * break;
				 */
			case T_LockingClause:
				_outLockingClause(str, obj);
				break;
			case T_XmlSerialize:
				_outXmlSerialize(str, obj);
				break;
			case T_TriggerTransition:
				_outTriggerTransition(str, obj);
				break;
			case T_JsonOutput:
				_outJsonOutput(str, obj);
				break;
			case T_JsonArgument:
				_outJsonArgument(str, obj);
				break;
			case T_JsonFuncExpr:
				_outJsonFuncExpr(str, obj);
				break;
			case T_JsonTablePathSpec:
				_outJsonTablePathSpec(str, obj);
				break;
			case T_JsonTable:
				_outJsonTable(str, obj);
				break;
			case T_JsonTableColumn:
				_outJsonTableColumn(str, obj);
				break;
			case T_JsonKeyValue:
				_outJsonKeyValue(str, obj);
				break;
			case T_JsonParseExpr:
				_outJsonParseExpr(str, obj);
				break;
			case T_JsonScalarExpr:
				_outJsonScalarExpr(str, obj);
				break;
			case T_JsonSerializeExpr:
				_outJsonSerializeExpr(str, obj);
				break;
			case T_JsonObjectConstructor:
				_outJsonObjectConstructor(str, obj);
				break;
			case T_JsonArrayConstructor:
				_outJsonArrayConstructor(str, obj);
				break;
			case T_JsonArrayQueryConstructor:
				_outJsonArrayQueryConstructor(str, obj);
				break;
			case T_JsonAggConstructor:
				_outJsonAggConstructor(str, obj);
				break;
			case T_JsonObjectAgg:
				_outJsonObjectAgg(str, obj);
				break;
			case T_JsonArrayAgg:
				_outJsonArrayAgg(str, obj);
				break;
			case T_PartitionElem:
				_outPartitionElem(str, obj);
				break;
			case T_PartitionSpec:
				_outPartitionSpec(str, obj);
				break;
			case T_PartitionBoundSpec:
				_outPartitionBoundSpec(str, obj);
				break;
			case T_PartitionRangeDatum:
				_outPartitionRangeDatum(str, obj);
				break;

			case T_InsertStmt:
				_outInsertStmt(str, obj);
				break;

			case T_DeleteStmt:
				_outDeleteStmt(str, obj);
				break;

			case T_UpdateStmt:
				_outUpdateStmt(str, obj);
				break;

			case T_MergeStmt:
				_outMergeStmt(str, obj);
				break;

			case T_TransactionStmt:
				_outTransactionStmt(str, obj);
				break;

			case T_TruncateStmt:
				_outTruncateStmt(str, obj);
				break;

			case T_VacuumStmt:
				_outVacuumStmt(str, obj);
				break;

			case T_ExplainStmt:
				_outExplainStmt(str, obj);
				break;

			case T_ClusterStmt:
				_outClusterStmt(str, obj);
				break;

			case T_CheckPointStmt:
				_outCheckPointStmt(str, obj);
				break;

			case T_ClosePortalStmt:
				_outClosePortalStmt(str, obj);
				break;

			case T_ListenStmt:
				_outListenStmt(str, obj);
				break;

			case T_UnlistenStmt:
				_outUnlistenStmt(str, obj);
				break;

			case T_LoadStmt:
				_outLoadStmt(str, obj);
				break;

			case T_CopyStmt:
				_outCopyStmt(str, obj);
				break;

			case T_DeallocateStmt:
				_outDeallocateStmt(str, obj);
				break;

			case T_RenameStmt:
				_outRenameStmt(str, obj);
				break;

			case T_CreateRoleStmt:
				_outCreateRoleStmt(str, obj);
				break;

			case T_AlterRoleStmt:
				_outAlterRoleStmt(str, obj);
				break;

			case T_AlterRoleSetStmt:
				_outAlterRoleSetStmt(str, obj);
				break;

			case T_RoleSpec:
				_outRoleSpec(str, obj);
				break;

			case T_DropRoleStmt:
				_outDropRoleStmt(str, obj);
				break;

			case T_CreateSchemaStmt:
				_outCreateSchemaStmt(str, obj);
				break;

			case T_VariableSetStmt:
				_outVariableSetStmt(str, obj);
				break;

			case T_VariableShowStmt:
				_outVariableShowStmt(str, obj);
				break;

			case T_ConstraintsSetStmt:
				_outConstraintsSetStmt(str, obj);
				break;

			case T_AlterTableStmt:
				_outAlterTableStmt(str, obj);
				break;

			case T_AlterTableCmd:
				_outAlterTableCmd(str, obj);
				break;

			case T_ATAlterConstraint:
				_outATAlterConstraint(str, obj);
				break;

			case T_CreateSeqStmt:
				_outCreateSeqStmt(str, obj);
				break;

			case T_AlterSeqStmt:
				_outAlterSeqStmt(str, obj);
				break;

			case T_CreatePLangStmt:
				_outCreatePLangStmt(str, obj);
				break;

			case T_CreateTableSpaceStmt:
				_outCreateTableSpaceStmt(str, obj);
				break;

			case T_DropTableSpaceStmt:
				_outDropTableSpaceStmt(str, obj);
				break;

			case T_CreateTrigStmt:
				_outCreateTrigStmt(str, obj);
				break;

			case T_DefineStmt:
				_outDefineStmt(str, obj);
				break;

			case T_CreateOpClassStmt:
				_outCreateOpClassStmt(str, obj);
				break;

			case T_CreateOpClassItem:
				_outCreateOpClassItem(str, obj);
				break;

			case T_DropStmt:
				_outDropStmt(str, obj);
				break;

			case T_FetchStmt:
				_outFetchStmt(str, obj);
				break;

			case T_GrantStmt:
				_outGrantStmt(str, obj);
				break;

			case T_ObjectWithArgs:
				_outObjectWithArgs(str, obj);
				break;

			case T_FunctionParameter:
				_outFunctionParameter(str, obj);
				break;

			case T_GrantRoleStmt:
				_outGrantRoleStmt(str, obj);
				break;

			case T_CreateFunctionStmt:
				_outCreateFunctionStmt(str, obj);
				break;

			case T_AlterFunctionStmt:
				_outAlterFunctionStmt(str, obj);
				break;

			case T_CreateCastStmt:
				_outCreateCastStmt(str, obj);
				break;

			case T_ReindexStmt:
				_outReindexStmt(str, obj);
				break;

			case T_AlterObjectSchemaStmt:
				_outAlterObjectSchemaStmt(str, obj);
				break;

			case T_AlterOwnerStmt:
				_outAlterOwnerStmt(str, obj);
				break;

			case T_RuleStmt:
				_outRuleStmt(str, obj);
				break;

			case T_ViewStmt:
				_outViewStmt(str, obj);
				break;

			case T_CreatedbStmt:
				_outCreatedbStmt(str, obj);
				break;

			case T_AlterDatabaseStmt:
				_outAlterDatabaseStmt(str, obj);
				break;

			case T_AlterDatabaseSetStmt:
				_outAlterDatabaseSetStmt(str, obj);
				break;

			case T_DropdbStmt:
				_outDropdbStmt(str, obj);
				break;

			case T_CreateDomainStmt:
				_outCreateDomainStmt(str, obj);
				break;

			case T_AlterDomainStmt:
				_outAlterDomainStmt(str, obj);
				break;

			case T_CreateConversionStmt:
				_outCreateConversionStmt(str, obj);
				break;

			case T_PrepareStmt:
				_outPrepareStmt(str, obj);
				break;

			case T_ExecuteStmt:
				_outExecuteStmt(str, obj);
				break;

			case T_LockStmt:
				_outLockStmt(str, obj);
				break;

			case T_CommentStmt:
				_outCommentStmt(str, obj);
				break;

			case T_DiscardStmt:
				_outDiscardStmt(str, obj);
				break;

			case T_CreateOpFamilyStmt:
				_outCreateOpFamilyStmt(str, obj);
				break;

			case T_AlterOpFamilyStmt:
				_outAlterOpFamilyStmt(str, obj);
				break;

			case T_CreateEnumStmt:
				_outCreateEnumStmt(str, obj);
				break;

			case T_DropOwnedStmt:
				_outDropOwnedStmt(str, obj);
				break;

			case T_ReassignOwnedStmt:
				_outReassignOwnedStmt(str, obj);
				break;

			case T_AlterTSDictionaryStmt:
				_outAlterTSDictionaryStmt(str, obj);
				break;

			case T_AlterTSConfigurationStmt:
				_outAlterTSConfigurationStmt(str, obj);
				break;

			case T_OnConflictClause:
				_outOnConflictClause(str, obj);
				break;

			default:
				break;
		}
	}
}

/*
 * nodeToString -
 *	   returns the ascii representation of the Node as a palloc'd string
 *
 * write_loc_fields determines whether location fields are output with their
 * actual value rather than -1.  The actual value can be useful for debugging,
 * but for most uses, the actual value is not useful, since the original query
 * string is no longer available.
 */
static char *
nodeToStringInternal(const void *obj, bool write_loc_fields)
{
	StringInfoData str;
	bool		save_write_location_fields;

	save_write_location_fields = write_location_fields;
	write_location_fields = write_loc_fields;

	/* see stringinfo.h for an explanation of this maneuver */
	initStringInfo(&str);
	_outNode(&str, (void *) obj);

	write_location_fields = save_write_location_fields;

	return str.data;
}

/*
 * Externally visible entry points
 */
char *
nodeToString(const void *obj)
{
	return nodeToStringInternal(obj, false);
}
