Example #1
0
RETCODE SQL_API PG_SQLFreeStmt(HSTMT     hstmt,
			    UWORD     fOption)
{
static char* const func="SQLFreeStmt";
StatementClass *stmt = (StatementClass *) hstmt;

	mylog("%s: entering...hstmt=%u, fOption=%d\n", func, hstmt, fOption);

	if ( ! stmt) {
		SC_log_error(func, "", NULL);
		return SQL_INVALID_HANDLE;
	}

	if (fOption == SQL_DROP) {
		ConnectionClass *conn = stmt->hdbc;

		/* Remove the statement from the connection's statement list */
		if ( conn) {
			if ( ! CC_remove_statement(conn, stmt)) {
				SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction.");
				SC_log_error(func, "", stmt);
				return SQL_ERROR;  /* stmt may be executing a transaction */
			}

			/*	Free any cursors and discard any result info */
			if (stmt->result) {
				QR_Destructor(stmt->result);
				stmt->result = NULL;
			}
		}

		/* Destroy the statement and free any results, cursors, etc. */
		SC_Destructor(stmt);

    } else if (fOption == SQL_UNBIND) {
		SC_unbind_cols(stmt);

    } else if (fOption == SQL_CLOSE) {
		/* this should discard all the results, but leave the statement */
		/* itself in place (it can be executed again) */
	if (!SC_recycle_statement(stmt)) {
		/*	errormsg passed in above */
			SC_log_error(func, "", stmt);
	    return SQL_ERROR;
		}

    } else if(fOption == SQL_RESET_PARAMS) {
		SC_free_params(stmt, STMT_FREE_PARAMS_ALL);

    } else {
	SC_set_error(stmt, STMT_OPTION_OUT_OF_RANGE_ERROR, "Invalid option passed to SQLFreeStmt.");
	SC_log_error(func, "", stmt);
	return SQL_ERROR;
    }

    return SQL_SUCCESS;
}
Example #2
0
char
SC_Destructor(StatementClass *self)
{

	mylog("SC_Destructor: self=%u, self->result=%u, self->hdbc=%u\n", self, self->result, self->hdbc);
	if (STMT_EXECUTING == self->status) {
		SC_set_error(self, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction.");
		return FALSE;
	}

	if (self->result) {
		if ( ! self->hdbc)
			self->result->conn = NULL;  /* prevent any dbase activity */

		QR_Destructor(self->result);
	}

	if (self->statement)
		free(self->statement);

	SC_free_params(self, STMT_FREE_PARAMS_ALL);

	/* the memory pointed to by the bindings is not deallocated by the driver */
	/* by by the application that uses that driver, so we don't have to care  */
	/* about that here. */
	if (self->bindings)
		free(self->bindings);


	/*	Free the parsed table information */
	if (self->ti) {
		int i;
		for (i = 0; i < self->ntab; i++) {
			free(self->ti[i]);
		}

		free(self->ti);
	}

	/*	Free the parsed field information */
	if (self->fi) {
		int i;
		for (i = 0; i < self->nfld; i++) {
			free(self->fi[i]);
		}
		free(self->fi);
	}

	SC_set_errormsg(self, NULL);

	free(self);

	mylog("SC_Destructor: EXIT\n");

	return TRUE;
}
Example #3
0
static char *
CC_lookup_cs_new(ConnectionClass *self)
{
	char		*encstr = NULL;
	QResultClass	*res;

	res = CC_send_query(self, "select pg_client_encoding()", NULL, IGNORE_ABORT_ON_CONN | ROLLBACK_ON_ERROR, NULL);
	if (QR_command_maybe_successful(res))
	{
		const char 	*enc = QR_get_value_backend_text(res, 0, 0);

		if (enc)
			encstr = strdup(enc);
	}
	QR_Destructor(res);
	return encstr;
}
Example #4
0
RETCODE
DiscardStatementSvp(StatementClass *stmt, RETCODE ret, BOOL errorOnly)
{
	CSTR	func = "DiscardStatementSvp";
	char	esavepoint[32], cmd[64];
	ConnectionClass	*conn = SC_get_conn(stmt);
	QResultClass *res;
	BOOL	cmd_success, start_stmt = FALSE;	

inolog("%s:%p->accessed=%d is_in=%d is_rb=%d is_tc=%d\n", func, stmt, SC_accessed_db(stmt),
CC_is_in_trans(conn), SC_is_rb_stmt(stmt), SC_is_tc_stmt(stmt));
	switch (ret)
	{
		case SQL_NEED_DATA:
			break;
		case SQL_ERROR:
			start_stmt = TRUE;
			break;
		default:
			if (!errorOnly)
				start_stmt = TRUE;
			break;
	}
	if (!SC_accessed_db(stmt) || !CC_is_in_trans(conn))
		goto cleanup;
	if (!SC_is_rb_stmt(stmt) && !SC_is_tc_stmt(stmt))
		goto cleanup;
	sprintf(esavepoint, "_EXEC_SVP_%p", stmt);
	if (SQL_ERROR == ret)
	{
		if (SC_started_rbpoint(stmt))
		{
			snprintf(cmd, sizeof(cmd), "ROLLBACK to %s", esavepoint);
			res = CC_send_query(conn, cmd, NULL, IGNORE_ABORT_ON_CONN, NULL);
			cmd_success = QR_command_maybe_successful(res);
			QR_Destructor(res);
			if (!cmd_success)
			{
				SC_set_error(stmt, STMT_INTERNAL_ERROR, "internal ROLLBACK failed", func);
				CC_abort(conn);
				goto cleanup;
			}
		}
		else
		{
			CC_abort(conn);
			goto cleanup;
		}
	}
	else if (errorOnly)
		return ret;
inolog("ret=%d\n", ret);
	if (SQL_NEED_DATA != ret && SC_started_rbpoint(stmt))
	{
		snprintf(cmd, sizeof(cmd), "RELEASE %s", esavepoint);
		res = CC_send_query(conn, cmd, NULL, IGNORE_ABORT_ON_CONN, NULL);
		cmd_success = QR_command_maybe_successful(res);
		QR_Destructor(res);
		if (!cmd_success)
		{
			SC_set_error(stmt, STMT_INTERNAL_ERROR, "internal RELEASE failed", func);
			CC_abort(conn);
			ret = SQL_ERROR;
		}
	}
cleanup:
	if (SQL_NEED_DATA != ret)
		SC_forget_unnamed(stmt); /* unnamed plan is no longer reliable */
	if (!SC_is_prepare_statement(stmt) && ONCE_DESCRIBED == stmt->prepared)
		SC_set_prepared(stmt, NOT_YET_PREPARED);
	if (start_stmt || SQL_ERROR == ret)
	{
		if (stmt->lock_CC_for_rb > 0)
		{
			LEAVE_CONN_CS(conn);
			stmt->lock_CC_for_rb--;
		}
		SC_start_stmt(stmt);
	}
	return ret;
}
Example #5
0
/*
 *	Must be in a transaction or the subsequent execution
 *	invokes a transaction.
 */
RETCODE
SetStatementSvp(StatementClass *stmt)
{
	CSTR	func = "SetStatementSvp";
	char	esavepoint[32], cmd[64];
	ConnectionClass	*conn = SC_get_conn(stmt);
	QResultClass *res;
	RETCODE	ret = SQL_SUCCESS_WITH_INFO;

	if (CC_is_in_error_trans(conn))
		return ret;

	if (0 == stmt->lock_CC_for_rb)
	{
		ENTER_CONN_CS(conn);
		stmt->lock_CC_for_rb++;
	}
	switch (stmt->statement_type)
	{
		case STMT_TYPE_SPECIAL:
		case STMT_TYPE_TRANSACTION:
			return ret;
	}
	if (!SC_accessed_db(stmt))
	{
		BOOL	need_savep = FALSE;

		if (stmt->internal)
		{
			if (PG_VERSION_GE(conn, 8.0))
				SC_start_rb_stmt(stmt);
			else
				SC_start_tc_stmt(stmt);
		}
		if (SC_is_rb_stmt(stmt))
		{
			if (CC_is_in_trans(conn))
			{
				need_savep = TRUE;
			}
		}
		if (need_savep)
		{
			sprintf(esavepoint, "_EXEC_SVP_%p", stmt);
			snprintf(cmd, sizeof(cmd), "SAVEPOINT %s", esavepoint);
			res = CC_send_query(conn, cmd, NULL, 0, NULL);
			if (QR_command_maybe_successful(res))
			{
				SC_set_accessed_db(stmt);
				SC_start_rbpoint(stmt);
				ret = SQL_SUCCESS;
			}
			else
			{
				SC_set_error(stmt, STMT_INTERNAL_ERROR, "internal SAVEPOINT failed", func);
				ret = SQL_ERROR;
			}
			QR_Destructor(res);
		}
		else
			SC_set_accessed_db(stmt);
	}
inolog("%s:%p->accessed=%d\n", func, stmt, SC_accessed_db(stmt));
	return ret;
}
Example #6
0
/*
 *	The execution after all parameters were resolved.
 */
static
RETCODE	Exec_with_parameters_resolved(StatementClass *stmt, BOOL *exec_end)
{
	CSTR func = "Exec_with_parameters_resolved";
	RETCODE		retval;
	SQLLEN		end_row;
	SQLINTEGER	cursor_type, scroll_concurrency;
	ConnectionClass	*conn;
	QResultClass	*res;
	APDFields	*apdopts;
	IPDFields	*ipdopts;
	BOOL		prepare_before_exec = FALSE;

	*exec_end = FALSE;
	conn = SC_get_conn(stmt);
	mylog("%s: copying statement params: trans_status=%d, len=%d, stmt='%s'\n", func, conn->transact_status, strlen(stmt->statement), stmt->statement);

	/* save the cursor's info before the execution */
	cursor_type = stmt->options.cursor_type;
	scroll_concurrency = stmt->options.scroll_concurrency;
	/* Prepare the statement if possible at backend side */
	if (!stmt->inaccurate_result)
	{
		if (HowToPrepareBeforeExec(stmt, FALSE) >= allowParse)
			prepare_before_exec = TRUE;
	}
inolog("prepare_before_exec=%d srv=%d\n", prepare_before_exec, conn->connInfo.use_server_side_prepare);
	/* Create the statement with parameters substituted. */
	retval = copy_statement_with_parameters(stmt, prepare_before_exec);
	stmt->current_exec_param = -1;
	if (retval != SQL_SUCCESS)
	{
		stmt->exec_current_row = -1;
		*exec_end = TRUE;
		return retval; /* error msg is passed from the above */
	}

	mylog("   stmt_with_params = '%s'\n", stmt->stmt_with_params);

	/*
	 *	Dummy exection to get the column info.
	 */ 
	if (stmt->inaccurate_result && SC_is_parse_tricky(stmt))
	{
		BOOL		in_trans = CC_is_in_trans(conn);
		BOOL		issued_begin = FALSE;
		QResultClass *curres;

		stmt->exec_current_row = -1;
		*exec_end = TRUE;
		if (!SC_is_pre_executable(stmt))
			return SQL_SUCCESS;
		if (strnicmp(stmt->stmt_with_params, "BEGIN;", 6) == 0)
		{
			/* do nothing */
		}
		else if (!in_trans)
		{
			if (issued_begin = CC_begin(conn), !issued_begin)
			{
				SC_set_error(stmt, STMT_EXEC_ERROR,  "Handle prepare error", func);
				return SQL_ERROR;
			}
		}
		/* we are now in a transaction */
		res = CC_send_query(conn, stmt->stmt_with_params, NULL, 0, SC_get_ancestor(stmt));
		if (!QR_command_maybe_successful(res))
		{
#ifndef	_LEGACY_MODE_
			if (PG_VERSION_LT(conn, 8.0))
				CC_abort(conn);
#endif /* LEGACY_MODE_ */
			SC_set_error(stmt, STMT_EXEC_ERROR, "Handle prepare error", func);
			QR_Destructor(res);
			return SQL_ERROR;
		}
		SC_set_Result(stmt, res);
		for (curres = res; !curres->num_fields; curres = curres->next)
			;
		SC_set_Curres(stmt, curres);
		if (CC_does_autocommit(conn))
		{
			if (issued_begin)
				CC_commit(conn);
		}
		stmt->status = STMT_FINISHED;
		return SQL_SUCCESS;
	}
	/*
	 *	The real execution.
	 */
mylog("about to begin SC_execute\n");
	retval = SC_execute(stmt);
	if (retval == SQL_ERROR)
	{
		stmt->exec_current_row = -1;
		*exec_end = TRUE;
		return retval;
	}
	res = SC_get_Result(stmt);
	/* special handling of result for keyset driven cursors */
	if (SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type &&
	    SQL_CONCUR_READ_ONLY != stmt->options.scroll_concurrency)
	{
		QResultClass	*kres;

		if (kres = res->next, kres)
		{
			if (kres->fields)
				CI_Destructor(kres->fields);
			kres->fields = res->fields;
			res->fields = NULL;
			kres->num_fields = res->num_fields;
			res->next = NULL;
			SC_set_Result(stmt, kres);
			res = kres;
		}
	}
#ifdef	NOT_USED
	else if (SC_is_concat_prepare_exec(stmt))
	{
		if (res && QR_command_maybe_successful(res))
		{
			QResultClass	*kres;
		
			kres = res->next;
inolog("res->next=%p\n", kres);
			res->next = NULL;
			SC_set_Result(stmt, kres);
			res = kres;
			SC_set_prepared(stmt, PREPARED_PERMANENTLY);
		}
		else
		{
			retval = SQL_ERROR;
			if (stmt->execute_statement)
				free(stmt->execute_statement);
			stmt->execute_statement = NULL;
		}
	}
#endif /* NOT_USED */
#if (ODBCVER >= 0x0300)
	ipdopts = SC_get_IPDF(stmt);
	if (ipdopts->param_status_ptr)
	{
		switch (retval)
		{
			case SQL_SUCCESS: 
				ipdopts->param_status_ptr[stmt->exec_current_row] = SQL_PARAM_SUCCESS;
				break;
			case SQL_SUCCESS_WITH_INFO: 
				ipdopts->param_status_ptr[stmt->exec_current_row] = SQL_PARAM_SUCCESS_WITH_INFO;
				break;
			default: 
				ipdopts->param_status_ptr[stmt->exec_current_row] = SQL_PARAM_ERROR;
				break;
		}
	}
#endif /* ODBCVER */
	if (end_row = stmt->exec_end_row, end_row < 0)
	{
		apdopts = SC_get_APDF(stmt);
		end_row = (SQLINTEGER) apdopts->paramset_size - 1;
	}
	if (stmt->inaccurate_result ||
	    stmt->exec_current_row >= end_row)
	{
		*exec_end = TRUE;
		stmt->exec_current_row = -1;
	}
	else
		stmt->exec_current_row++;
	if (res)
	{
#if (ODBCVER >= 0x0300)
		EnvironmentClass *env = (EnvironmentClass *) CC_get_env(conn);
		const char *cmd = QR_get_command(res);
		SQLLEN	start_row;
		if (start_row = stmt->exec_start_row, start_row < 0)
			start_row = 0;

		if (retval == SQL_SUCCESS &&
		    NULL != cmd &&
		    start_row >= end_row &&
		    NULL != env &&
		    EN_is_odbc3(env))
		{
			int     count;

			if (sscanf(cmd , "UPDATE %d", &count) == 1)
				;
			else if (sscanf(cmd , "DELETE %d", &count) == 1)
				;
			else
				count = -1;
			if (0 == count)
				retval = SQL_NO_DATA;
		}
#endif /* ODBCVER */
		stmt->diag_row_count = res->recent_processed_row_count;
	}
	/*
	 *	The cursor's info was changed ?
	 */
	if (retval == SQL_SUCCESS &&
	    (stmt->options.cursor_type != cursor_type ||
	     stmt->options.scroll_concurrency != scroll_concurrency))
	{
		SC_set_error(stmt, STMT_OPTION_VALUE_CHANGED, "cursor updatability changed", func);
		retval = SQL_SUCCESS_WITH_INFO;
	}
	return retval;
}
Example #7
0
void
CC_lookup_characterset(ConnectionClass *self)
{
	char	*encspec = NULL, *currenc = NULL, *tencstr;
	CSTR func = "CC_lookup_characterset";

	mylog("%s: entering...\n", func);
	if (self->original_client_encoding)
		encspec = strdup(self->original_client_encoding);
	if (self->current_client_encoding)
		currenc = strdup(self->current_client_encoding);
	else if (PG_VERSION_LT(self, 7.2))
		currenc = CC_lookup_cs_old(self);
	else
		currenc = CC_lookup_cs_new(self);
	tencstr = encspec ? encspec : currenc;
	if (self->original_client_encoding)
	{
		if (stricmp(self->original_client_encoding, tencstr))
		{
			char msg[256];

			snprintf(msg, sizeof(msg), "The client_encoding '%s' was changed to '%s'", self->original_client_encoding, tencstr);
			CC_set_error(self, CONN_OPTION_VALUE_CHANGED, msg, func);
		}
		free(self->original_client_encoding);
	}
#ifndef	UNICODE_SUPPORT
	else
	{
		const char *wenc = get_environment_encoding(self, encspec, currenc, FALSE);
		if (wenc && (!tencstr || stricmp(tencstr, wenc)))
		{
			QResultClass	*res;
			char		query[64];
			int		errnum = CC_get_errornumber(self);
			BOOL		cmd_success;

			sprintf(query, "set client_encoding to '%s'", wenc);
			res = CC_send_query(self, query, NULL, IGNORE_ABORT_ON_CONN | ROLLBACK_ON_ERROR, NULL);
			cmd_success = QR_command_maybe_successful(res);
			QR_Destructor(res);
			CC_set_errornumber(self, errnum);
			if (cmd_success)
			{
				self->original_client_encoding = strdup(wenc);
				self->ccsc = pg_CS_code(self->original_client_encoding);
				if (encspec)
					free(encspec);
				if (currenc)
					free(currenc);
				return;
			}
		}
	}
#endif /* UNICODE_SUPPORT */
	if (tencstr)
	{
		self->original_client_encoding = tencstr;
		if (encspec && currenc)
			free(currenc);
		self->ccsc = pg_CS_code(tencstr);
		qlog("    [ Client encoding = '%s' (code = %d) ]\n", self->original_client_encoding, self->ccsc);
		if (self->ccsc < 0)
		{
			char msg[256];

			snprintf(msg, sizeof(msg), "would handle the encoding '%s' like ASCII", tencstr);
			CC_set_error(self, CONN_OPTION_VALUE_CHANGED, msg, func); 
		}
	}
	else
	{
		self->ccsc = SQL_ASCII;
		self->original_client_encoding = NULL;
	}
	self->mb_maxbyte_per_char = pg_mb_maxlen(self->ccsc);
}
Example #8
0
RETCODE SC_execute(StatementClass *self)
{
static char* const func="SC_execute";
ConnectionClass *conn;
QResultClass *res;
char ok, was_ok, was_nonfatal;
Int2 oldstatus, numcols;
QueryInfo qi;


	conn = SC_get_conn(self);

	/*	Begin a transaction if one is not already in progress */
	/*
	 * Basically we don't have to begin a transaction in autocommit mode
	 * because Postgres backend runs in autocomit mode. We issue "BEGIN"
	 * in the following cases. 1) we use declare/fetch and the statement
	 * is SELECT (because declare/fetch must be called in a transaction).
	 * 2) we are in autocommit off state and the statement isn't of type
	 * OTHER.
	 */
	if (!self->internal && !CC_is_in_trans(conn) &&
		((globals.use_declarefetch && self->statement_type == STMT_TYPE_SELECT) || (!CC_is_in_autocommit(conn) && self->statement_type != STMT_TYPE_OTHER)))
	{
		mylog("   about to begin a transaction on statement = %u\n", self);
		res = CC_send_query(conn, "BEGIN", NULL);
		if (QR_aborted(res)) {
			SC_set_error(self, STMT_EXEC_ERROR, "Could not begin a transaction");
			SC_log_error(func, "", self);
			return SQL_ERROR;
		}

		ok = QR_command_successful(res);

		mylog("SQLExecute: ok = %d, status = %d\n", ok, QR_get_status(res));

		QR_Destructor(res);

		if (!ok) {
			SC_set_error(self, STMT_EXEC_ERROR, "Could not begin a transaction");
			SC_log_error(func, "", self);
			return SQL_ERROR;
		}
		else
			CC_set_in_trans(conn);
	}



	oldstatus = conn->status;
	conn->status = CONN_EXECUTING;
	self->status = STMT_EXECUTING;


	/*	If it's a SELECT statement, use a cursor. */
	/*	Note that the declare cursor has already been prepended to the statement */
	/*	in copy_statement... */
	if (self->statement_type == STMT_TYPE_SELECT) {

		char fetch[128];

		mylog("       Sending SELECT statement on stmt=%u, cursor_name='%s'\n", self, self->cursor_name);


		/*	send the declare/select */
		self->result = CC_send_query(conn, self->stmt_with_params, NULL);

		if (globals.use_declarefetch && self->result != NULL &&
			QR_command_successful(self->result)) {

			QR_Destructor(self->result);

			/*	That worked, so now send the fetch to start getting data back */
			qi.result_in = NULL;
			qi.cursor = self->cursor_name;
			qi.row_size = globals.fetch_max;

			/*	Most likely the rowset size will not be set by the application until
				after the statement	is executed, so might as well use the cache size.
				The qr_next_tuple() function will correct for any discrepancies in
				sizes and adjust the cache accordingly.
			*/

			sprintf(fetch, "fetch %d in %s", qi.row_size, self->cursor_name);

			self->result = CC_send_query( conn, fetch, &qi);
		}

		mylog("     done sending the query:\n");



	}
	else  { /* not a SELECT statement so don't use a cursor */
		mylog("      it's NOT a select statement: stmt=%u\n", self);
		self->result = CC_send_query(conn, self->stmt_with_params, NULL);

		/*	We shouldn't send COMMIT. Postgres backend does the
			autocommit if neccessary. (Zoltan, 04/26/2000)
		*/
		/*	Above seems wrong.
			Even in case of autocommit, started transactions
			must be committed. (Hiroshi, 02/11/2001)
		 */ 
		if ( ! self->internal && CC_is_in_autocommit(conn) && CC_is_in_trans(conn))
		 {                   
			res = CC_send_query(conn, "COMMIT", NULL); 
			QR_Destructor(res);
			CC_set_no_trans(conn);
		} 
	
	}

	conn->status = oldstatus;
	self->status = STMT_FINISHED;

	/*	Check the status of the result */
	if (self->result) {

		was_ok = QR_command_successful(self->result);
		was_nonfatal = QR_command_nonfatal(self->result);

		if ( was_ok)
			SC_set_errornumber(self, STMT_OK);
		else
			SC_set_errornumber(self, was_nonfatal ? STMT_INFO_ONLY : STMT_ERROR_TAKEN_FROM_BACKEND);

		self->currTuple = -1; /* set cursor before the first tuple in the list */
		self->current_col = -1;
		self->rowset_start = -1;

		/* see if the query did return any result columns */
		numcols = QR_NumResultCols(self->result);

		/* now allocate the array to hold the binding info */
		if (numcols > 0) {
			extend_bindings(self, numcols);
			if (self->bindings == NULL) {
				SC_set_error(self, STMT_NO_MEMORY_ERROR, "Could not get enough free memory to store the binding information");
				SC_log_error(func, "", self);
				return SQL_ERROR;
			}
		}
		/* issue "ABORT" when query aborted */ 
		if (QR_get_aborted(self->result) && ! self->internal )                   
			CC_abort(conn);
	} else {		/* Bad Error -- The error message will be in the Connection */

		if (self->statement_type == STMT_TYPE_CREATE) {
			SC_set_error(self, STMT_CREATE_TABLE_ERROR, "Error creating the table");
			/*	This would allow the table to already exists, thus appending
				rows to it.  BUT, if the table didn't have the same attributes,
				it would fail.
				return SQL_SUCCESS_WITH_INFO;
			*/
		}
		else {
			SC_set_error(self, STMT_EXEC_ERROR, "Error while executing the query");
		}

		if ( ! self->internal)
			CC_abort(conn);
	}

	if (SC_get_errornumber(self) == STMT_OK)
		return SQL_SUCCESS;

	else {
		/* Modified, 2000-04-29, Zoltan */
		if (SC_get_errornumber(self) == STMT_INFO_ONLY)
		    SC_set_errormsg(self, "Error while executing the query (non-fatal)");
		else
		    SC_set_errormsg(self, "Unknown error");
		SC_log_error(func, "", self);
		return SQL_ERROR;
	}
}
Example #9
0
/*	Called from SQLPrepare if STMT_PREMATURE, or
	from SQLExecute if STMT_FINISHED, or
	from SQLFreeStmt(SQL_CLOSE)
 */
char
SC_recycle_statement(StatementClass *self)
{
ConnectionClass *conn;

mylog("recycle statement: self= %u\n", self);

	/*	This would not happen */
	if (self->status == STMT_EXECUTING) {
		SC_set_error(self, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction.");
		return FALSE;
	}

	SC_set_errormsg(self, NULL);
	SC_clear_error(self);

	switch (self->status) {
	case STMT_ALLOCATED:
		/* this statement does not need to be recycled */
		return TRUE;

	case STMT_READY:
		break;

	case STMT_PREMATURE:
		/*	Premature execution of the statement might have caused the start of a transaction.
			If so, we have to rollback that transaction.
		*/
		conn = SC_get_conn(self);
		if ( ! CC_is_in_autocommit(conn) && CC_is_in_trans(conn)) {

			CC_send_query(conn, "ABORT", NULL);
			CC_set_no_trans(conn);
		}
		break;

	case STMT_FINISHED:
		break;

	default:
		SC_set_error(self, STMT_INTERNAL_ERROR, "An internal error occured while recycling statements");
		return FALSE;
	}

	/*	Free the parsed table information */
	if (self->ti) {
		int i;
		for (i = 0; i < self->ntab; i++) {
			free(self->ti[i]);
		}

		free(self->ti);
		self->ti = NULL;
		self->ntab = 0;
	}

	/*	Free the parsed field information */
	if (self->fi) {
		int i;
		for (i = 0; i < self->nfld; i++) {
			free(self->fi[i]);
		}
		free(self->fi);
		self->fi = NULL;
		self->nfld = 0;
	}
	self->parse_status = STMT_PARSE_NONE;

	/*	Free any cursors */
	if (self->result) {
		QR_Destructor(self->result);
		self->result = NULL;
	}

	/****************************************************************/
	/*	Reset only parameters that have anything to do with results */
	/****************************************************************/

	self->status = STMT_READY;
	self->manual_result = FALSE;	/* very important */

	self->currTuple = -1;
	self->rowset_start = -1;
	self->current_col = -1;
	self->bind_row = 0;
	self->last_fetch_count = 0;

	SC_set_errormsg(self, NULL);
	SC_clear_error(self);

	self->lobj_fd = -1;

	/*	Free any data at exec params before the statement is executed */
	/*	again.  If not, then there will be a memory leak when */
	/*	the next SQLParamData/SQLPutData is called. */
	SC_free_params(self, STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY);

	return TRUE;
}