コード例 #1
0
ファイル: plpy_spi.c プロジェクト: pguyot/postgres
void
PLy_spi_subtransaction_abort(MemoryContext oldcontext, ResourceOwner oldowner)
{
	ErrorData  *edata;
	PLyExceptionEntry *entry;
	PyObject   *exc;

	/* Save error info */
	MemoryContextSwitchTo(oldcontext);
	edata = CopyErrorData();
	FlushErrorState();

	/* Abort the inner transaction */
	RollbackAndReleaseCurrentSubTransaction();
	MemoryContextSwitchTo(oldcontext);
	CurrentResourceOwner = oldowner;

	/*
	 * If AtEOSubXact_SPI() popped any SPI context of the subxact, it will have
	 * left us in a disconnected state.  We need this hack to return to
	 * connected state.
	 */
	SPI_restore_connection();

	/* Look up the correct exception */
	entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
						HASH_FIND, NULL);
	/* We really should find it, but just in case have a fallback */
	Assert(entry != NULL);
	exc = entry ? entry->exc : PLy_exc_spi_error;
	/* Make Python raise the exception */
	PLy_spi_exception_set(exc, edata);
	FreeErrorData(edata);
}
コード例 #2
0
ファイル: plluaspi.c プロジェクト: mauzo/pllua
static void luaP_throwsqlerr (lua_State *L, ErrorData *ed) {
  SPI_restore_connection();
  lua_pushstring(L, ed->message);
  FreeErrorData(ed);
  lua_pushlightuserdata(L, (void *)PLLUA_TXNABORT);
  lua_pushvalue(L, -2);
  lua_rawset(L, LUA_REGISTRYINDEX);
  lua_error(L);
}
コード例 #3
0
ファイル: ErrorData.c プロジェクト: TwentyOneSolutions/pljava
/*
 * Class:     org_postgresql_pljava_internal_ErrorData
 * Method:    _free
 * Signature: (J)V
 */
JNIEXPORT void JNICALL
Java_org_postgresql_pljava_internal_ErrorData__1free(JNIEnv* env, jobject _this, jlong pointer)
{
	BEGIN_NATIVE_NO_ERRCHECK
	Ptr2Long p2l;
	p2l.longVal = pointer;
	FreeErrorData(p2l.ptrVal);
	END_NATIVE
}
コード例 #4
0
ファイル: execute.c プロジェクト: mpihlak/plproxy-dev
static int geterrcode(void)
{
	/* switch context to work around Assert() in CopyErrorData() */
	MemoryContext ctx = MemoryContextSwitchTo(TopMemoryContext);
	ErrorData *edata = CopyErrorData();
	int code = edata->sqlerrcode;
	FreeErrorData(edata);
	MemoryContextSwitchTo(ctx);
	return code;
}
コード例 #5
0
ファイル: netStubs.cpp プロジェクト: errorcodexero/porting
int FNC::SetErrorData( const char *errors, int errorsLength, int wait_ms )
{
    Synchronized sync(m_dataMutex);
    
    if (errors && errorsLength) {
	// discard any unsent data
	FreeErrorData();
	// copy new data to local storage until we can send it to DS
	m_userErrMsg = new char[errorsLength];
	memcpy(m_userErrMsg, errors, errorsLength);
	m_userErrMsgLength = errorsLength;
    }

    return OK;
}
コード例 #6
0
ファイル: Backend.c プロジェクト: greenplum-db/pljava
/*
 * A function having everything to do with logging, which ought to be factored
 * out one day to make a start on the Thoughts-on-logging wiki ideas.
 */
static void reLogWithChangedLevel(int level)
{
	ErrorData *edata = CopyErrorData();
	int sqlstate = edata->sqlerrcode;
	int category = ERRCODE_TO_CATEGORY(sqlstate);
	FlushErrorState();
	if ( WARNING > level )
	{
		if ( ERRCODE_SUCCESSFUL_COMPLETION != category )
			sqlstate = ERRCODE_SUCCESSFUL_COMPLETION;
	}
	else if ( WARNING == level )
	{
		if ( ERRCODE_WARNING != category && ERRCODE_NO_DATA != category )
			sqlstate = ERRCODE_WARNING;
	}
	else if ( ERRCODE_WARNING == category || ERRCODE_NO_DATA == category ||
		ERRCODE_SUCCESSFUL_COMPLETION == category )
		sqlstate = ERRCODE_INTERNAL_ERROR;
#if PG_VERSION_NUM >= 90500
	edata->elevel = level;
	edata->sqlerrcode = sqlstate;
	PG_TRY();
	{
		ThrowErrorData(edata);
	}
	PG_CATCH();
	{
		FreeErrorData(edata); /* otherwise this wouldn't happen in ERROR case */
		PG_RE_THROW();
	}
	PG_END_TRY();
	FreeErrorData(edata);
#else
	if (!errstart(level, edata->filename, edata->lineno,
				  edata->funcname, NULL))
	{
		FreeErrorData(edata);
		return;
	}

	errcode(sqlstate);
	if (edata->message)
		errmsg("%s", edata->message);
	if (edata->detail)
		errdetail("%s", edata->detail);
	if (edata->detail_log)
		errdetail_log("%s", edata->detail_log);
	if (edata->hint)
		errhint("%s", edata->hint);
	if (edata->context)
		errcontext("%s", edata->context); /* this may need to be trimmed */
#if PG_VERSION_NUM >= 90300
	if (edata->schema_name)
		err_generic_string(PG_DIAG_SCHEMA_NAME, edata->schema_name);
	if (edata->table_name)
		err_generic_string(PG_DIAG_TABLE_NAME, edata->table_name);
	if (edata->column_name)
		err_generic_string(PG_DIAG_COLUMN_NAME, edata->column_name);
	if (edata->datatype_name)
		err_generic_string(PG_DIAG_DATATYPE_NAME, edata->datatype_name);
	if (edata->constraint_name)
		err_generic_string(PG_DIAG_CONSTRAINT_NAME, edata->constraint_name);
#endif
	if (edata->internalquery)
		internalerrquery(edata->internalquery);

	FreeErrorData(edata);
	errfinish(0);
#endif
}
コード例 #7
0
ファイル: reader.c プロジェクト: gatehouse/pg_bulkload
/**
 * @brief Read the next tuple from parser.
 * @param rd  [in/out] reader
 * @return type
 */
HeapTuple
ReaderNext(Reader *rd)
{
	HeapTuple		tuple;
	MemoryContext	ccxt;
	bool			eof;
	Parser		   *parser = rd->parser;

	ccxt = CurrentMemoryContext;

	eof = false;
	do
	{
		tuple = NULL;
		parser->parsing_field = -1;

		PG_TRY();
		{
			tuple = ParserRead(parser, &rd->checker);
			if (tuple == NULL)
				eof = true;
			else
			{
				tuple = CheckerTuple(&rd->checker, tuple,
									 &parser->parsing_field);
				CheckerConstraints(&rd->checker, tuple, &parser->parsing_field);
			}
		}
		PG_CATCH();
		{
			ErrorData	   *errdata;
			MemoryContext	ecxt;
			char		   *message;
			StringInfoData	buf;

			if (parser->parsing_field < 0)
				PG_RE_THROW();	/* should not ignore */

			ecxt = MemoryContextSwitchTo(ccxt);
			errdata = CopyErrorData();

			/* We cannot ignore query aborts. */
			switch (errdata->sqlerrcode)
			{
				case ERRCODE_ADMIN_SHUTDOWN:
				case ERRCODE_QUERY_CANCELED:
					MemoryContextSwitchTo(ecxt);
					PG_RE_THROW();
					break;
			}

			/* Absorb parse errors. */
			rd->parse_errors++;
			if (errdata->message)
				message = pstrdup(errdata->message);
			else
				message = "<no error message>";
			FlushErrorState();
			FreeErrorData(errdata);

			initStringInfo(&buf);
			appendStringInfo(&buf, "Parse error Record " int64_FMT
				": Input Record " int64_FMT ": Rejected",
				rd->parse_errors, parser->count);

			if (parser->parsing_field > 0)
				appendStringInfo(&buf, " - column %d", parser->parsing_field);

			appendStringInfo(&buf, ". %s\n", message);

			LoggerLog(WARNING, buf.data);

			/* Terminate if PARSE_ERRORS has been reached. */
			if (rd->parse_errors > rd->max_parse_errors)
			{
				eof = true;
				LoggerLog(WARNING,
					"Maximum parse error count exceeded - " int64_FMT
					" error(s) found in input file\n",
					rd->parse_errors);
			}

			/* output parse bad file. */
			if (rd->parse_fp == NULL)
				if ((rd->parse_fp = AllocateFile(rd->parse_badfile, "w")) == NULL)
					ereport(ERROR,
							(errcode_for_file_access(),
							 errmsg("could not open parse bad file \"%s\": %m",
									rd->parse_badfile)));

			ParserDumpRecord(parser, rd->parse_fp, rd->parse_badfile);

			MemoryContextReset(ccxt);
			// Without the below line, the regression tests shows the different result on debug-build mode.
			tuple = NULL;
		}
		PG_END_TRY();

	} while (!eof && !tuple);

	BULKLOAD_PROFILE(&prof_reader_parser);
	return tuple;
}
コード例 #8
0
ファイル: plpy_plpymodule.c プロジェクト: cconvey/postgres
static PyObject *
PLy_output(volatile int level, PyObject *self, PyObject *args, PyObject *kw)
{
	int			sqlstate = 0;
	char	   *volatile sqlstatestr = NULL;
	char	   *volatile message = NULL;
	char	   *volatile detail = NULL;
	char	   *volatile hint = NULL;
	char	   *volatile column_name = NULL;
	char	   *volatile constraint_name = NULL;
	char	   *volatile datatype_name = NULL;
	char	   *volatile table_name = NULL;
	char	   *volatile schema_name = NULL;
	volatile MemoryContext oldcontext;
	PyObject   *key,
			   *value;
	PyObject   *volatile so;
	Py_ssize_t	pos = 0;

	if (PyTuple_Size(args) == 1)
	{
		/*
		 * Treat single argument specially to avoid undesirable ('tuple',)
		 * decoration.
		 */
		PyObject   *o;

		if (!PyArg_UnpackTuple(args, "plpy.elog", 1, 1, &o))
			PLy_elog(ERROR, "could not unpack arguments in plpy.elog");
		so = PyObject_Str(o);
	}
	else
		so = PyObject_Str(args);

	if (so == NULL || ((message = PyString_AsString(so)) == NULL))
	{
		level = ERROR;
		message = dgettext(TEXTDOMAIN, "could not parse error message in plpy.elog");
	}
	message = pstrdup(message);

	Py_XDECREF(so);

	if (kw != NULL)
	{
		while (PyDict_Next(kw, &pos, &key, &value))
		{
			char	   *keyword = PyString_AsString(key);

			if (strcmp(keyword, "message") == 0)
			{
				/* the message should not be overwriten */
				if (PyTuple_Size(args) != 0)
				{
					PLy_exception_set(PyExc_TypeError, "Argument 'message' given by name and position");
					return NULL;
				}

				if (message)
					pfree(message);
				message = object_to_string(value);
			}
			else if (strcmp(keyword, "detail") == 0)
				detail = object_to_string(value);
			else if (strcmp(keyword, "hint") == 0)
				hint = object_to_string(value);
			else if (strcmp(keyword, "sqlstate") == 0)
				sqlstatestr = object_to_string(value);
			else if (strcmp(keyword, "schema_name") == 0)
				schema_name = object_to_string(value);
			else if (strcmp(keyword, "table_name") == 0)
				table_name = object_to_string(value);
			else if (strcmp(keyword, "column_name") == 0)
				column_name = object_to_string(value);
			else if (strcmp(keyword, "datatype_name") == 0)
				datatype_name = object_to_string(value);
			else if (strcmp(keyword, "constraint_name") == 0)
				constraint_name = object_to_string(value);
			else
			{
				PLy_exception_set(PyExc_TypeError,
					 "'%s' is an invalid keyword argument for this function",
								  keyword);
				return NULL;
			}
		}
	}

	if (sqlstatestr != NULL)
	{
		if (strlen(sqlstatestr) != 5)
		{
			PLy_exception_set(PyExc_ValueError, "invalid SQLSTATE code");
			return NULL;
		}

		if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
		{
			PLy_exception_set(PyExc_ValueError, "invalid SQLSTATE code");
			return NULL;
		}

		sqlstate = MAKE_SQLSTATE(sqlstatestr[0],
								 sqlstatestr[1],
								 sqlstatestr[2],
								 sqlstatestr[3],
								 sqlstatestr[4]);
	}

	oldcontext = CurrentMemoryContext;
	PG_TRY();
	{
		if (message != NULL)
			pg_verifymbstr(message, strlen(message), false);
		if (detail != NULL)
			pg_verifymbstr(detail, strlen(detail), false);
		if (hint != NULL)
			pg_verifymbstr(hint, strlen(hint), false);
		if (schema_name != NULL)
			pg_verifymbstr(schema_name, strlen(schema_name), false);
		if (table_name != NULL)
			pg_verifymbstr(table_name, strlen(table_name), false);
		if (column_name != NULL)
			pg_verifymbstr(column_name, strlen(column_name), false);
		if (datatype_name != NULL)
			pg_verifymbstr(datatype_name, strlen(datatype_name), false);
		if (constraint_name != NULL)
			pg_verifymbstr(constraint_name, strlen(constraint_name), false);

		ereport(level,
				((sqlstate != 0) ? errcode(sqlstate) : 0,
				 (message != NULL) ? errmsg_internal("%s", message) : 0,
				 (detail != NULL) ? errdetail_internal("%s", detail) : 0,
				 (hint != NULL) ? errhint("%s", hint) : 0,
				 (column_name != NULL) ?
				 err_generic_string(PG_DIAG_COLUMN_NAME, column_name) : 0,
				 (constraint_name != NULL) ?
			err_generic_string(PG_DIAG_CONSTRAINT_NAME, constraint_name) : 0,
				 (datatype_name != NULL) ?
				 err_generic_string(PG_DIAG_DATATYPE_NAME, datatype_name) : 0,
				 (table_name != NULL) ?
				 err_generic_string(PG_DIAG_TABLE_NAME, table_name) : 0,
				 (schema_name != NULL) ?
				 err_generic_string(PG_DIAG_SCHEMA_NAME, schema_name) : 0));
	}
	PG_CATCH();
	{
		ErrorData  *edata;

		MemoryContextSwitchTo(oldcontext);
		edata = CopyErrorData();
		FlushErrorState();

		PLy_exception_set_with_details(PLy_exc_error, edata);
		FreeErrorData(edata);

		return NULL;
	}
	PG_END_TRY();

	/*
	 * return a legal object so the interpreter will continue on its merry way
	 */
	Py_INCREF(Py_None);
	return Py_None;
}
コード例 #9
0
ファイル: pgsynck.c プロジェクト: jconway/pgsynck
Datum
pgsynck(PG_FUNCTION_ARGS)
{
/*	List			   *raw_parsetree_list = NULL; */
	char			   *query_string = NULL;
	char			   *oneq = NULL;
	char			   *q;
	ErrorData		   *edata = NULL;
	MemoryContext		oldcontext = CurrentMemoryContext;
	MemoryContext		per_query_ctx;
	ReturnSetInfo	   *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
	TupleDesc			tupdesc;
	Tuplestorestate	   *tupstore;

	/* check to see if caller supports us returning a tuplestore */
	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("set-valued function called in context that cannot accept a set")));
	if (!(rsinfo->allowedModes & SFRM_Materialize))
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("materialize mode required, but it is not " \
						"allowed in this context")));

	/* Build a tuple descriptor for our result type */
	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
		elog(ERROR, "return type must be a row type");

	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
	oldcontext = MemoryContextSwitchTo(per_query_ctx);

	tupstore = tuplestore_begin_heap(true, false, work_mem);
	rsinfo->returnMode = SFRM_Materialize;
	rsinfo->setResult = tupstore;
	rsinfo->setDesc = tupdesc;

	MemoryContextSwitchTo(oldcontext);

	q = query_string = text_to_cstring(PG_GETARG_TEXT_PP(0));
	oneq = get_one_query(&q);

	while (oneq != NULL)
	{
		int			j = 0;
		Datum		values[PGSYNCK_COLS];
		bool		nulls[PGSYNCK_COLS];

		memset(values, 0, sizeof(values));
		memset(nulls, 0, sizeof(nulls));

		/* sql */
		values[j++] = CStringGetTextDatum(oneq);

		PG_TRY();
		{
			raw_parser(oneq);

			/* cursorpos */
			values[j++] = Int32GetDatum(0);

			/* sqlerrcode */
			values[j++] = Int32GetDatum(0);

			/* message - primary error message */
			values[j++] = CStringGetTextDatum("");

			/* hint - hint message */
			values[j++] = CStringGetTextDatum("");

			tuplestore_putvalues(tupstore, tupdesc, values, nulls);
		}
		PG_CATCH();
		{
			/* Save error info */
			MemoryContextSwitchTo(oldcontext);
			edata = CopyErrorData();
			FlushErrorState();

			/* cursorpos - cursor index into query string */
			values[j++] = Int32GetDatum(edata->cursorpos);

			/* sqlerrcode - encoded ERRSTATE */
			values[j++] = Int32GetDatum(edata->sqlerrcode);

			/* message - primary error message */
			values[j++] = CStringGetTextDatum(edata->message ? edata->message:"");

			/* hint - hint message */
			values[j++] = CStringGetTextDatum(edata->hint ? edata->hint:"");

			tuplestore_putvalues(tupstore, tupdesc, values, nulls);
			FreeErrorData(edata);
		}
		PG_END_TRY();

		oneq = get_one_query(&q);
	}

	/* clean up and return the tuplestore */
	tuplestore_donestoring(tupstore);

	return (Datum) 0;
}
コード例 #10
0
ファイル: netStubs.cpp プロジェクト: errorcodexero/porting
int FNC::Send()
{
    Synchronized sync(m_dataMutex);

    memset(&m_sendPkt, 0, sizeof m_sendPkt);

    // send our current control mode back to DS
    // keep the same bit ordering sent from the DS
    m_sendPkt.control = m_recvPkt.ctrl.control;

    // battery voltage - BCD scaled integer
    int vbat = (int)(m_battery * 100.0);
    m_sendPkt.battery[0] = (((vbat / 1000) % 10) << 4) | ((vbat / 100) % 10);
    m_sendPkt.battery[1] = (((vbat / 10) % 10) << 4) | (vbat % 10);

    // 8 bits of "digital outputs" displayed on DS
    m_sendPkt.dsDigitalOut = m_dsDigitalOut;

    // copy team ID from DS control packet
    m_sendPkt.teamID = htons(m_recvData.teamID);

    // fake the cRIO MAC address - does the DS care about this?
    memcpy(m_sendPkt.macAddr, fakeMAC, 6);

    // fake the FRC_NetworkCommunication version
    strncpy(m_sendPkt.versionData, commVersion, sizeof m_sendPkt.versionData);

    // robot operating mode
    m_sendPkt.mode = htons(m_mode);

    // copy last received packet number from DS control packet
    m_sendPkt.packetIndex = htons(m_recvData.packetIndex);

    // dashboard status update number
    m_sendPkt.updateNumber = m_updateNumber;

    // dashboard data...include as much as will fit in the allowed space
    //   high-priority data length (4 bytes)
    //   high-priority data bytes  (variable-length)
    //   error message length (4 bytes)
    //   error message string (variable-length)
    //   low-priority data length (4 bytes)
    //   low-priority data bytes (variable-length)
    unsigned int udlen = 0;
    const unsigned int udMaxLen = sizeof m_sendPkt.userData;
    if (udlen + sizeof (uint32_t) + m_userDataHighLength <= udMaxLen) {
	uint32_t udhl = htonl(m_userDataHighLength);
	memcpy(&m_sendPkt.userData[udlen], &udhl, sizeof udhl);
	udlen += sizeof udhl;
	if (m_userDataHigh) {
	    memcpy(&m_sendPkt.userData[udlen], m_userDataHigh,
		    m_userDataHighLength);
	    udlen += m_userDataHighLength;
	    // now that the message has been sent, free the local copy
	    FreeUserDataHigh();
	}
    }

    if (udlen + sizeof (uint32_t) + m_userErrMsgLength <= udMaxLen) {
	uint32_t erl = htonl(m_userErrMsgLength);
	memcpy(&m_sendPkt.userData[udlen], &erl, sizeof erl);
	udlen += sizeof erl;
	if (m_userErrMsg) {
	    memcpy(&m_sendPkt.userData[udlen], m_userErrMsg,
		    m_userErrMsgLength);
	    udlen += m_userErrMsgLength;
	    // now that the message has been sent, free the local copy
	    FreeErrorData();
	}
    }

    if (udlen + sizeof (uint32_t) + m_userDataLowLength <= udMaxLen) {
	uint32_t udll = htonl(m_userDataLowLength);
	memcpy(&m_sendPkt.userData[udlen], &udll, sizeof udll);
	udlen += sizeof udll;
	if (m_userDataLow) {
	    memcpy(&m_sendPkt.userData[udlen], m_userDataLow,
		    m_userDataLowLength);
	    udlen += m_userDataLowLength;
	    // now that the message has been sent, free the local copy
	    FreeUserDataLow();
	}
    }

#if 0
    // this is a hack
    // the real robot code updates this data once/second
    // and it's not clear how the numbers are generated
    // (or why it needs to be 40 bytes long!)
    clock_gettime(CLOCK_REALTIME, (timespec *)m_sendPkt.sysstat);
#endif

    // DS Enhanced I/O configuration
    if (m_eioConfig) {
	memcpy(m_sendPkt.eioConfig, m_eioConfig, m_eioConfigLength);
	if (m_eioConfigLength >= 26 && m_sendPkt.eioConfig[1] ==
	  kFRC_NetworkCommunication_DynamicType_DSEnhancedIO_Output) {
	    SwapDSEIOOutput((output_t *)&m_sendPkt.eioConfig[2]);
	}
	FreeEIOConfig();
    }

    // calculate CRC on the first 1024 bytes
    uint32_t crc = crc32(0, Z_NULL, 0);
    crc = crc32(crc, (uint8_t *)&m_sendPkt, 1024);
    m_sendPkt.crc = htonl(crc);

    m_sendPktLength = 1024;
    if (m_lcdData) {
	if (m_lcdDataLength <= sizeof m_sendPkt.lcdData) {
	    memcpy(m_sendPkt.lcdData, m_lcdData, m_lcdDataLength);
	    m_sendPktLength += m_lcdDataLength;
	}
	FreeLCDData();
    }

    m_toAddr.sin_family = AF_INET;
    m_toAddr.sin_addr.s_addr = m_fromAddr.sin_addr.s_addr;
    m_toAddr.sin_port = htons(DS_REPLY_PORT);

    int n = sendto(m_sendSocket, (char *)&m_sendPkt, m_sendPktLength, 0,
		    (struct sockaddr *)&m_toAddr, sizeof m_toAddr);
    if (n == -1) {
	perror("FRC_NetworkCommunication::Recv::sendto");
	return ERROR;
    }

    m_pcap->write_record(&m_sendAddr, &m_toAddr, (char *)&m_sendPkt, n);

    return OK;
}
コード例 #11
0
/**
 * @brief Entry point of the user-defined function for pg_bulkload.
 * @return Returns number of loaded tuples.  If the case of errors, -1 will be
 * returned.
 */
Datum
pg_bulkload(PG_FUNCTION_ARGS)
{
	Reader		   *rd = NULL;
	Writer		   *wt = NULL;
	Datum			options;
	MemoryContext	ctx;
	MemoryContext	ccxt;
	PGRUsage		ru0;
	PGRUsage		ru1;
	int64			count;
	int64			parse_errors;
	int64			skip;
	WriterResult	ret;
	char		   *start;
	char		   *end;
	float8			system;
	float8			user;
	float8			duration;
	TupleDesc		tupdesc;
	Datum			values[PG_BULKLOAD_COLS];
	bool			nulls[PG_BULKLOAD_COLS];
	HeapTuple		result;

	/* Build a tuple descriptor for our result type */
	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
		elog(ERROR, "return type must be a row type");

	BULKLOAD_PROFILE_PUSH();

	pg_rusage_init(&ru0);

	/* must be the super user */
	if (!superuser())
		ereport(ERROR,
			(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
			 errmsg("must be superuser to use pg_bulkload")));

	options = PG_GETARG_DATUM(0);

	ccxt = CurrentMemoryContext;

	/*
	 * STEP 1: Initialization
	 */

	/* parse options and create reader and writer */
	ParseOptions(options, &rd, &wt, ru0.tv.tv_sec);

	/* initialize reader */
	ReaderInit(rd);

	/*
	 * We need to split PG_TRY block because gcc optimizes if-branches with
	 * longjmp codes too much. Local variables initialized in either branch
	 * cannot be handled another branch.
	 */
	PG_TRY();
	{
		/* truncate heap */
		if (wt->truncate)
			TruncateTable(wt->relid);

		/* initialize writer */
		WriterInit(wt);

		/* initialize checker */
		CheckerInit(&rd->checker, wt->rel, wt->tchecker);

		/* initialize parser */
		ParserInit(rd->parser, &rd->checker, rd->infile, wt->desc,
				   wt->multi_process, PG_GET_COLLATION());
	}
	PG_CATCH();
	{
		if (rd)
			ReaderClose(rd, true);
		if (wt)
			WriterClose(wt, true);
		PG_RE_THROW();
	}
	PG_END_TRY();

	/* No throwable codes here! */

	PG_TRY();
	{
		/* create logger */
		CreateLogger(rd->logfile, wt->verbose, rd->infile[0] == ':');

		start = timeval_to_cstring(ru0.tv);
		LoggerLog(INFO, "\npg_bulkload %s on %s\n\n",
				   PG_BULKLOAD_VERSION, start);

		ReaderDumpParams(rd);
		WriterDumpParams(wt);
		LoggerLog(INFO, "\n");

		BULKLOAD_PROFILE(&prof_init);

		/*
		 * STEP 2: Build heap
		 */

		/* Switch into its memory context */
		Assert(wt->context);
		ctx = MemoryContextSwitchTo(wt->context);

		/* Loop for each input file record. */
		while (wt->count < rd->limit)
		{
			HeapTuple	tuple;

			CHECK_FOR_INTERRUPTS();

			/* read tuple */
			BULKLOAD_PROFILE_PUSH();
			tuple = ReaderNext(rd);
			BULKLOAD_PROFILE_POP();
			BULKLOAD_PROFILE(&prof_reader);
			if (tuple == NULL)
				break;

			/* write tuple */
			BULKLOAD_PROFILE_PUSH();
			WriterInsert(wt, tuple);
			wt->count += 1;
			BULKLOAD_PROFILE_POP();
			BULKLOAD_PROFILE(&prof_writer);

			MemoryContextReset(wt->context);
			BULKLOAD_PROFILE(&prof_reset);
		}

		MemoryContextSwitchTo(ctx);

		/*
		 * STEP 3: Finalize heap and merge indexes
		 */

		count = wt->count;
		parse_errors = rd->parse_errors;

		/*
		 * close writer first and reader second because shmem_exit callback
		 * is managed by a simple stack.
		 */
		ret = WriterClose(wt, false);
		wt = NULL;
		skip = ReaderClose(rd, false);
		rd = NULL;
	}
	PG_CATCH();
	{
		ErrorData	   *errdata;
		MemoryContext	ecxt;

		ecxt = MemoryContextSwitchTo(ccxt);
		errdata = CopyErrorData();
		LoggerLog(INFO, "%s\n", errdata->message);
		FreeErrorData(errdata);

		/* close writer first, and reader second */
		if (wt)
			WriterClose(wt, true);
		if (rd)
			ReaderClose(rd, true);

		MemoryContextSwitchTo(ecxt);
		PG_RE_THROW();
	}
	PG_END_TRY();

	count -= ret.num_dup_new;

	LoggerLog(INFO, "\n"
			  "  " int64_FMT " Rows skipped.\n"
			  "  " int64_FMT " Rows successfully loaded.\n"
			  "  " int64_FMT " Rows not loaded due to parse errors.\n"
			  "  " int64_FMT " Rows not loaded due to duplicate errors.\n"
			  "  " int64_FMT " Rows replaced with new rows.\n\n",
			  skip, count, parse_errors, ret.num_dup_new, ret.num_dup_old);

	pg_rusage_init(&ru1);
	system = diffTime(ru1.ru.ru_stime, ru0.ru.ru_stime);
	user = diffTime(ru1.ru.ru_utime, ru0.ru.ru_utime);
	duration = diffTime(ru1.tv, ru0.tv);
	end = timeval_to_cstring(ru1.tv);

	memset(nulls, 0, sizeof(nulls));
	values[0] = Int64GetDatum(skip);
	values[1] = Int64GetDatum(count);
	values[2] = Int64GetDatum(parse_errors);
	values[3] = Int64GetDatum(ret.num_dup_new);
	values[4] = Int64GetDatum(ret.num_dup_old);
	values[5] = Float8GetDatumFast(system);
	values[6] = Float8GetDatumFast(user);
	values[7] = Float8GetDatumFast(duration);

	LoggerLog(INFO,
		"Run began on %s\n"
		"Run ended on %s\n\n"
		"CPU %.2fs/%.2fu sec elapsed %.2f sec\n",
		start, end, system, user, duration);

	LoggerClose();

	result = heap_form_tuple(tupdesc, values, nulls);

	BULKLOAD_PROFILE(&prof_fini);
	BULKLOAD_PROFILE_POP();
	BULKLOAD_PROFILE_PRINT();

	PG_RETURN_DATUM(HeapTupleGetDatum(result));
}