/*-------------------------------------------------------------------------
 * pgut-spi.c
 *
 * Portions Copyright (c) 2008-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
 * Portions Copyright (c) 2011, Itagaki Takahiro
 * Portions Copyright (c) 2012-2015, The Reorg Development Team
 *-------------------------------------------------------------------------
 */

#include "postgres.h"
#include "pgut-spi.h"
#include "lib/stringinfo.h"

#define EXEC_FAILED(ret, expected) \
	(((expected) > 0 && (ret) != (expected)) || (ret) < 0)

static void
appendStringInfoVA_s(StringInfo str, const char *fmt, va_list args)
__attribute__((format(printf, 2, 0)));

static void
termStringInfo(StringInfo str)
{
	if (str && str->data)
		pfree(str->data);
}

/* appendStringInfoVA + automatic buffer extension */
static void
appendStringInfoVA_s(StringInfo str, const char *fmt, va_list args)
{
#if PG_VERSION_NUM >= 90400
	int needed;
	while ((needed = appendStringInfoVA(str, fmt, args)) > 0)
	{
		/* Double the buffer size and try again. */
		enlargeStringInfo(str, needed);
	}
#else
	while (!appendStringInfoVA(str, fmt, args))
	{
		/* Double the buffer size and try again. */
		enlargeStringInfo(str, str->maxlen);
	}
#endif
}

/* simple execute */
void
execute(int expected, const char *sql)
{
	int ret = SPI_execute(sql, false, 0);
	if EXEC_FAILED(ret, expected)
		elog(ERROR, "query failed: (sql=%s, code=%d, expected=%d)", sql, ret, expected);
}

/* execute prepared plan */
void
execute_plan(int expected, SPIPlanPtr plan, Datum *values, const char *nulls)
{
	int	ret = SPI_execute_plan(plan, values, nulls, false, 0);
	if EXEC_FAILED(ret, expected)
		elog(ERROR, "query failed: (code=%d, expected=%d)", ret, expected);
}

/* execute sql with format */
void
execute_with_format(int expected, const char *format, ...)
{
	va_list			ap;
	StringInfoData	sql;
	int				ret;

	initStringInfo(&sql);
	va_start(ap, format);
	appendStringInfoVA_s(&sql, format, ap);
	va_end(ap);

	if (sql.len == 0)
		elog(WARNING, "execute_with_format(%s)", format);
	ret = SPI_exec(sql.data, 0);
	if EXEC_FAILED(ret, expected)
		elog(ERROR, "query failed: (sql=%s, code=%d, expected=%d)", sql.data, ret, expected);

	termStringInfo(&sql);
}

void
execute_with_args(int expected, const char *src, int nargs, Oid argtypes[], Datum values[], const bool nulls[])
{
	int		ret;
	int		i;
	char	c_nulls[FUNC_MAX_ARGS];

	for (i = 0; i < nargs; i++)
		c_nulls[i] = (nulls[i] ? 'n' : ' ');

	ret = SPI_execute_with_args(src, nargs, argtypes, values, c_nulls, false, 0);
	if EXEC_FAILED(ret, expected)
		elog(ERROR, "query failed: (sql=%s, code=%d, expected=%d)", src, ret, expected);
}

void
execute_with_format_args(int expected, const char *format, int nargs, Oid argtypes[], Datum values[], const bool nulls[], ...)
{
	va_list			ap;
	StringInfoData	sql;

	initStringInfo(&sql);
	va_start(ap, nulls);
	appendStringInfoVA_s(&sql, format, ap);
	va_end(ap);

	execute_with_args(expected, sql.data, nargs, argtypes, values, nulls);

	termStringInfo(&sql);
}
