代码拉取完成,页面将自动刷新
同步操作将从 openGauss/openGauss-connector-odbc 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
/*------
* Module: statement.c
*
* Description: This module contains functions related to creating
* and manipulating a statement.
*
* Classes: StatementClass (Functions prefix: "SC_")
*
* API functions: SQLAllocStmt, SQLFreeStmt
*
* Comments: See "readme.txt" for copyright and license information.
*-------
*/
#ifdef WIN_MULTITHREAD_SUPPORT
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0400
#endif /* _WIN32_WINNT */
#endif /* WIN_MULTITHREAD_SUPPORT */
#include "statement.h"
#include "misc.h" // strncpy_null
#include "bind.h"
#include "connection.h"
#include "multibyte.h"
#include "qresult.h"
#include "convert.h"
#include "environ.h"
#include "loadlib.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "pgapifunc.h"
#define IS_BLANK(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n')
/* Map sql commands to statement types */
static const struct
{
int type;
char *s;
} Statement_Type[] =
{
{
STMT_TYPE_SELECT, "SELECT"
}
,{
STMT_TYPE_INSERT, "INSERT"
}
,{
STMT_TYPE_UPDATE, "UPDATE"
}
,{
STMT_TYPE_DELETE, "DELETE"
}
,{
STMT_TYPE_PROCCALL, "{"
}
,{
STMT_TYPE_SET, "SET"
}
,{
STMT_TYPE_RESET, "RESET"
}
,{
STMT_TYPE_CREATE, "CREATE"
}
,{
STMT_TYPE_DECLARE, "DECLARE"
}
,{
STMT_TYPE_FETCH, "FETCH"
}
,{
STMT_TYPE_MOVE, "MOVE"
}
,{
STMT_TYPE_CLOSE, "CLOSE"
}
,{
STMT_TYPE_PREPARE, "PREPARE"
}
,{
STMT_TYPE_EXECUTE, "EXECUTE"
}
,{
STMT_TYPE_DEALLOCATE, "DEALLOCATE"
}
,{
STMT_TYPE_DROP, "DROP"
}
,{
STMT_TYPE_START, "BEGIN"
}
,{
STMT_TYPE_START, "START"
}
,{
STMT_TYPE_TRANSACTION, "SAVEPOINT"
}
,{
STMT_TYPE_TRANSACTION, "RELEASE"
}
,{
STMT_TYPE_TRANSACTION, "COMMIT"
}
,{
STMT_TYPE_TRANSACTION, "END"
}
,{
STMT_TYPE_TRANSACTION, "ROLLBACK"
}
,{
STMT_TYPE_TRANSACTION, "ABORT"
}
,{
STMT_TYPE_LOCK, "LOCK"
}
,{
STMT_TYPE_ALTER, "ALTER"
}
,{
STMT_TYPE_GRANT, "GRANT"
}
,{
STMT_TYPE_REVOKE, "REVOKE"
}
,{
STMT_TYPE_COPY, "COPY"
}
,{
STMT_TYPE_ANALYZE, "ANALYZE"
}
,{
STMT_TYPE_NOTIFY, "NOTIFY"
}
,{
STMT_TYPE_EXPLAIN, "EXPLAIN"
}
/*
* Special-commands that cannot be run in a transaction block. This isn't
* as granular as it could be. VACUUM can never be run in a transaction
* block, but some variants of REINDEX and CLUSTER can be. CHECKPOINT
* doesn't throw an error if you do, but it cannot be rolled back so
* there's no point in beginning a new transaction for it.
*/
,{
STMT_TYPE_SPECIAL, "VACUUM"
}
,{
STMT_TYPE_SPECIAL, "REINDEX"
}
,{
STMT_TYPE_SPECIAL, "CLUSTER"
}
,{
STMT_TYPE_SPECIAL, "CHECKPOINT"
}
,{
STMT_TYPE_WITH, "WITH"
}
,{
0, NULL
}
};
static QResultClass *libpq_bind_and_exec(StatementClass *stmt);
static void SC_set_errorinfo(StatementClass *self, QResultClass *res, int errkind);
static void SC_set_error_if_not_set(StatementClass *self, int errornumber, const char *errmsg, const char *func);
RETCODE SQL_API
PGAPI_AllocStmt(HDBC hdbc,
HSTMT * phstmt, UDWORD flag)
{
CSTR func = "PGAPI_AllocStmt";
ConnectionClass *conn = (ConnectionClass *) hdbc;
StatementClass *stmt;
ARDFields *ardopts;
MYLOG(0, "entering...\n");
if (!conn)
{
CC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
stmt = SC_Constructor(conn);
MYLOG(0, "**** : hdbc = %p, stmt = %p\n", hdbc, stmt);
if (!stmt)
{
CC_set_error(conn, CONN_STMT_ALLOC_ERROR, "No more memory to allocate a further SQL-statement", func);
*phstmt = SQL_NULL_HSTMT;
return SQL_ERROR;
}
if (!CC_add_statement(conn, stmt))
{
CC_set_error(conn, CONN_STMT_ALLOC_ERROR, "Maximum number of statements exceeded.", func);
SC_Destructor(stmt);
*phstmt = SQL_NULL_HSTMT;
return SQL_ERROR;
}
*phstmt = (HSTMT) stmt;
stmt->iflag = flag;
/* Copy default statement options based from Connection options */
if (0 != (PODBC_INHERIT_CONNECT_OPTIONS & flag))
{
stmt->options = stmt->options_orig = conn->stmtOptions;
stmt->ardi.ardf = conn->ardOptions;
}
else
{
InitializeStatementOptions(&stmt->options_orig);
stmt->options = stmt->options_orig;
InitializeARDFields(&stmt->ardi.ardf);
}
ardopts = SC_get_ARDF(stmt);
ARD_AllocBookmark(ardopts);
/* Save the handle for later */
stmt->phstmt = phstmt;
return SQL_SUCCESS;
}
RETCODE SQL_API
PGAPI_FreeStmt(HSTMT hstmt,
SQLUSMALLINT fOption)
{
CSTR func = "PGAPI_FreeStmt";
StatementClass *stmt = (StatementClass *) hstmt;
MYLOG(0, "entering...hstmt=%p, fOption=%hi\n", hstmt, fOption);
if (!stmt)
{
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
SC_clear_error(stmt);
if (fOption == SQL_DROP)
{
ConnectionClass *conn = stmt->hdbc;
/* Remove the statement from the connection's statement list */
if (conn)
{
QResultClass *res;
if (STMT_EXECUTING == stmt->status)
{
SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction.", func);
return SQL_ERROR; /* stmt may be executing a transaction */
}
if (conn->unnamed_prepared_stmt == stmt)
conn->unnamed_prepared_stmt = NULL;
/*
* Free any cursors and discard any result info.
* Don't detach the statement from the connection
* before freeing the associated cursors. Otherwise
* CC_cursor_count() would get wrong results.
*/
res = SC_get_Result(stmt);
QR_Destructor(res);
SC_init_Result(stmt);
if (!CC_remove_statement(conn, stmt))
{
SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction.", func);
return SQL_ERROR; /* stmt may be executing a
* transaction */
}
}
if (stmt->execute_delegate)
{
PGAPI_FreeStmt(stmt->execute_delegate, SQL_DROP);
stmt->execute_delegate = NULL;
}
if (stmt->execute_parent)
stmt->execute_parent->execute_delegate = 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)
*/
stmt->transition_status = STMT_TRANSITION_ALLOCATED;
if (stmt->execute_delegate)
{
PGAPI_FreeStmt(stmt->execute_delegate, SQL_DROP);
stmt->execute_delegate = NULL;
}
if (!SC_recycle_statement(stmt))
{
return SQL_ERROR;
}
SC_set_Curres(stmt, NULL);
}
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 PGAPI_FreeStmt.", func);
return SQL_ERROR;
}
return SQL_SUCCESS;
}
/*
* StatementClass implementation
*/
void
InitializeStatementOptions(StatementOptions *opt)
{
memset(opt, 0, sizeof(StatementOptions));
opt->scroll_concurrency = SQL_CONCUR_READ_ONLY;
opt->cursor_type = SQL_CURSOR_FORWARD_ONLY;
opt->retrieve_data = SQL_RD_ON;
opt->use_bookmarks = SQL_UB_OFF;
opt->metadata_id = SQL_FALSE;
}
static void SC_clear_parse_status(StatementClass *self, ConnectionClass *conn)
{
self->parse_status = STMT_PARSE_NONE;
}
static void SC_init_discard_output_params(StatementClass *self)
{
ConnectionClass *conn = SC_get_conn(self);
if (!conn) return;
self->discard_output_params = 0;
if (!conn->connInfo.use_server_side_prepare)
self->discard_output_params = 1;
}
static void SC_init_parse_method(StatementClass *self)
{
ConnectionClass *conn = SC_get_conn(self);
self->parse_method = 0;
if (!conn) return;
if (0 == (PODBC_EXTERNAL_STATEMENT & self->iflag)) return;
if (self->catalog_result) return;
if (conn->connInfo.drivers.parse)
SC_set_parse_forced(self);
}
StatementClass *
SC_Constructor(ConnectionClass *conn)
{
StatementClass *rv;
rv = (StatementClass *) malloc(sizeof(StatementClass));
if (rv)
{
rv->hdbc = conn;
rv->phstmt = NULL;
rv->result = NULL;
rv->curres = NULL;
rv->catalog_result = FALSE;
rv->prepare = NON_PREPARE_STATEMENT;
rv->prepared = NOT_YET_PREPARED;
rv->status = STMT_ALLOCATED;
rv->external = FALSE;
rv->iflag = 0;
rv->plan_name = NULL;
rv->transition_status = STMT_TRANSITION_UNALLOCATED;
rv->multi_statement = -1; /* unknown */
rv->num_params = -1; /* unknown */
rv->processed_statements = NULL;
rv->__error_message = NULL;
rv->__error_number = 0;
rv->pgerror = NULL;
rv->statement = NULL;
rv->stmt_with_params = NULL;
rv->load_statement = NULL;
rv->statement_type = STMT_TYPE_UNKNOWN;
rv->currTuple = -1;
rv->rowset_start = 0;
SC_set_rowset_start(rv, -1, FALSE);
rv->current_col = -1;
rv->bind_row = 0;
rv->from_pos = rv->load_from_pos = rv->where_pos = -1;
rv->last_fetch_count = rv->last_fetch_count_include_ommitted = 0;
rv->save_rowset_size = -1;
rv->data_at_exec = -1;
rv->current_exec_param = -1;
rv->exec_start_row = -1;
rv->exec_end_row = -1;
rv->exec_current_row = -1;
rv->put_data = FALSE;
rv->ref_CC_error = FALSE;
rv->join_info = 0;
rv->curr_param_result = 0;
SC_init_parse_method(rv);
rv->lobj_fd = -1;
INIT_NAME(rv->cursor_name);
/* Parse Stuff */
rv->ti = NULL;
rv->ntab = 0;
rv->num_key_fields = -1; /* unknown */
SC_clear_parse_status(rv, conn);
rv->proc_return = -1;
SC_init_discard_output_params(rv);
rv->cancel_info = 0;
/* Clear Statement Options -- defaults will be set in AllocStmt */
memset(&rv->options, 0, sizeof(StatementOptions));
InitializeEmbeddedDescriptor((DescriptorClass *)&(rv->ardi),
rv, SQL_ATTR_APP_ROW_DESC);
InitializeEmbeddedDescriptor((DescriptorClass *)&(rv->apdi),
rv, SQL_ATTR_APP_PARAM_DESC);
InitializeEmbeddedDescriptor((DescriptorClass *)&(rv->irdi),
rv, SQL_ATTR_IMP_ROW_DESC);
InitializeEmbeddedDescriptor((DescriptorClass *)&(rv->ipdi),
rv, SQL_ATTR_IMP_PARAM_DESC);
rv->miscinfo = 0;
rv->execinfo = 0;
rv->rb_or_tc = 0;
SC_reset_updatable(rv);
rv->diag_row_count = 0;
rv->stmt_time = 0;
rv->execute_delegate = NULL;
rv->execute_parent = NULL;
rv->allocated_callbacks = 0;
rv->num_callbacks = 0;
rv->callbacks = NULL;
GetDataInfoInitialize(SC_get_GDTI(rv));
PutDataInfoInitialize(SC_get_PDTI(rv));
rv->lock_CC_for_rb = FALSE;
INIT_STMT_CS(rv);
}
return rv;
}
char
SC_Destructor(StatementClass *self)
{
CSTR func = "SC_Destructor";
QResultClass *res = SC_get_Result(self);
MYLOG(0, "entering self=%p, self->result=%p, self->hdbc=%p\n", self, res, self->hdbc);
SC_clear_error(self);
if (STMT_EXECUTING == self->status)
{
SC_set_error(self, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction.", func);
return FALSE;
}
if (res)
{
if (!self->hdbc)
res->conn = NULL; /* prevent any dbase activity */
QR_Destructor(res);
}
SC_initialize_stmts(self, TRUE);
/* Free the parsed table information */
SC_initialize_cols_info(self, FALSE, TRUE);
NULL_THE_NAME(self->cursor_name);
/* Free the parsed field information */
DC_Destructor((DescriptorClass *) SC_get_ARDi(self));
DC_Destructor((DescriptorClass *) SC_get_APDi(self));
DC_Destructor((DescriptorClass *) SC_get_IRDi(self));
DC_Destructor((DescriptorClass *) SC_get_IPDi(self));
GDATA_unbind_cols(SC_get_GDTI(self), TRUE);
PDATA_free_params(SC_get_PDTI(self), STMT_FREE_PARAMS_ALL);
if (self->__error_message)
free(self->__error_message);
if (self->pgerror)
ER_Destructor(self->pgerror);
cancelNeedDataState(self);
if (self->callbacks)
free(self->callbacks);
DELETE_STMT_CS(self);
free(self);
MYLOG(0, "leaving\n");
return TRUE;
}
void
SC_init_Result(StatementClass *self)
{
self->result = self->curres = NULL;
self->curr_param_result = 0;
MYLOG(0, "leaving(%p)\n", self);
}
void
SC_set_Result(StatementClass *self, QResultClass *res)
{
if (res != self->result)
{
MYLOG(0, "(%p, %p)\n", self, res);
QR_Destructor(self->result);
self->result = self->curres = res;
if (NULL != res)
self->curr_param_result = 1;
}
}
/*
* Free parameters and free the memory from the
* data-at-execution parameters that was allocated in SQLPutData.
*/
void
SC_free_params(StatementClass *self, char option)
{
if (option != STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY)
{
APD_free_params(SC_get_APDF(self), option);
IPD_free_params(SC_get_IPDF(self), option);
}
PDATA_free_params(SC_get_PDTI(self), option);
self->data_at_exec = -1;
self->current_exec_param = -1;
self->put_data = FALSE;
if (option == STMT_FREE_PARAMS_ALL)
{
self->exec_start_row = -1;
self->exec_end_row = -1;
self->exec_current_row = -1;
}
}
int
statement_type(const char *statement)
{
int i;
/* ignore leading whitespace in query string */
while (*statement && (isspace((UCHAR) *statement) || *statement == '('))
statement++;
for (i = 0; Statement_Type[i].s; i++)
if (!strnicmp(statement, Statement_Type[i].s, strlen(Statement_Type[i].s)))
return Statement_Type[i].type;
return STMT_TYPE_OTHER;
}
void
SC_set_planname(StatementClass *stmt, const char *plan_name)
{
if (stmt->plan_name)
free(stmt->plan_name);
if (plan_name && plan_name[0])
stmt->plan_name = strdup(plan_name);
else
stmt->plan_name = NULL;
}
void
SC_set_rowset_start(StatementClass *stmt, SQLLEN start, BOOL valid_base)
{
QResultClass *res = SC_get_Curres(stmt);
SQLLEN incr = start - stmt->rowset_start;
MYLOG(DETAIL_LOG_LEVEL, "%p->SC_set_rowstart " FORMAT_LEN "->" FORMAT_LEN "(%s) ", stmt, stmt->rowset_start, start, valid_base ? "valid" : "unknown");
if (res != NULL)
{
BOOL valid = QR_has_valid_base(res);
MYPRINTF(DETAIL_LOG_LEVEL, ":(%p)QR is %s", res, QR_has_valid_base(res) ? "valid" : "unknown");
if (valid)
{
if (valid_base)
QR_inc_rowstart_in_cache(res, incr);
else
QR_set_no_valid_base(res);
}
else if (valid_base)
{
QR_set_has_valid_base(res);
if (start < 0)
QR_set_rowstart_in_cache(res, -1);
else
QR_set_rowstart_in_cache(res, start);
}
if (!QR_get_cursor(res))
res->key_base = start;
MYPRINTF(DETAIL_LOG_LEVEL, ":(%p)QR result=" FORMAT_LEN "(%s)", res, QR_get_rowstart_in_cache(res), QR_has_valid_base(res) ? "valid" : "unknown");
}
stmt->rowset_start = start;
MYPRINTF(DETAIL_LOG_LEVEL, ":stmt result=" FORMAT_LEN "\n", stmt->rowset_start);
}
void
SC_inc_rowset_start(StatementClass *stmt, SQLLEN inc)
{
SQLLEN start = stmt->rowset_start + inc;
SC_set_rowset_start(stmt, start, TRUE);
}
int
SC_set_current_col(StatementClass *stmt, int col)
{
if (col == stmt->current_col)
return col;
if (col >= 0)
reset_a_getdata_info(SC_get_GDTI(stmt), col + 1);
stmt->current_col = col;
return stmt->current_col;
}
void
SC_set_prepared(StatementClass *stmt, int prepared)
{
if (prepared == stmt->prepared)
;
else if (NOT_YET_PREPARED == prepared && PREPARED_PERMANENTLY == stmt->prepared)
{
ConnectionClass *conn = SC_get_conn(stmt);
if (conn)
{
ENTER_CONN_CS(conn);
if (CONN_CONNECTED == conn->status)
{
if (CC_is_in_error_trans(conn))
{
CC_mark_a_object_to_discard(conn, 's', stmt->plan_name);
}
else
{
QResultClass *res;
char dealloc_stmt[128];
SPRINTF_FIXED(dealloc_stmt, "DEALLOCATE \"%s\"", stmt->plan_name);
res = CC_send_query(conn, dealloc_stmt, NULL, IGNORE_ABORT_ON_CONN | ROLLBACK_ON_ERROR, NULL);
QR_Destructor(res);
}
}
LEAVE_CONN_CS(conn);
}
}
if (NOT_YET_PREPARED == prepared)
SC_set_planname(stmt, NULL);
stmt->prepared = prepared;
}
/*
* Initialize stmt_with_params and load_statement member pointer
* deallocating corresponding prepared plan. Also initialize
* statement member pointer if specified.
*/
RETCODE
SC_initialize_stmts(StatementClass *self, BOOL initializeOriginal)
{
ProcessedStmt *pstmt;
ProcessedStmt *next_pstmt;
if (self->lock_CC_for_rb)
{
LEAVE_CONN_CS(SC_get_conn(self));
self->lock_CC_for_rb = FALSE;
}
if (initializeOriginal)
{
if (self->statement)
{
free(self->statement);
self->statement = NULL;
}
pstmt = self->processed_statements;
while (pstmt)
{
if (pstmt->query)
free(pstmt->query);
next_pstmt = pstmt->next;
free(pstmt);
pstmt = next_pstmt;
}
self->processed_statements = NULL;
self->prepare = NON_PREPARE_STATEMENT;
SC_set_prepared(self, NOT_YET_PREPARED);
self->statement_type = STMT_TYPE_UNKNOWN; /* unknown */
self->multi_statement = -1; /* unknown */
self->num_params = -1; /* unknown */
self->proc_return = -1; /* unknown */
self->join_info = 0;
SC_init_parse_method(self);
SC_init_discard_output_params(self);
}
if (self->stmt_with_params)
{
free(self->stmt_with_params);
self->stmt_with_params = NULL;
}
if (self->load_statement)
{
free(self->load_statement);
self->load_statement = NULL;
}
return 0;
}
BOOL SC_opencheck(StatementClass *self, const char *func)
{
QResultClass *res;
if (!self)
return FALSE;
if (self->status == STMT_EXECUTING)
{
SC_set_error(self, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction.", func);
return TRUE;
}
/*
* We can dispose the result of Describe-only any time.
*/
if (self->prepare && self->status == STMT_DESCRIBED)
{
MYLOG(0, "self->prepare && self->status == STMT_DESCRIBED\n");
return FALSE;
}
if (res = SC_get_Curres(self), NULL != res)
{
if (QR_command_maybe_successful(res) && res->backend_tuples)
{
SC_set_error(self, STMT_SEQUENCE_ERROR, "The cursor is open.", func);
return TRUE;
}
}
return FALSE;
}
RETCODE
SC_initialize_and_recycle(StatementClass *self)
{
SC_initialize_stmts(self, TRUE);
if (!SC_recycle_statement(self))
return SQL_ERROR;
return SQL_SUCCESS;
}
void
SC_reset_result_for_rerun(StatementClass *self)
{
QResultClass *res;
ColumnInfoClass *flds;
if (!self) return;
if (res = SC_get_Result(self), NULL == res)
return;
flds = QR_get_fields(res);
if (NULL == flds ||
0 == CI_get_num_fields(flds))
SC_set_Result(self, NULL);
else
{
QR_reset_for_re_execute(res);
self->curr_param_result = 1;
SC_set_Curres(self, NULL);
}
}
/*
* Called from SQLPrepare if STMT_PREMATURE, or
* from SQLExecute if STMT_FINISHED, or
* from SQLFreeStmt(SQL_CLOSE)
*/
char
SC_recycle_statement(StatementClass *self)
{
CSTR func = "SC_recycle_statement";
ConnectionClass *conn;
MYLOG(0, "entering self=%p\n", self);
SC_clear_error(self);
/* This would not happen */
if (self->status == STMT_EXECUTING)
{
SC_set_error(self, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction.", func);
return FALSE;
}
if (SC_get_conn(self)->unnamed_prepared_stmt == self)
SC_get_conn(self)->unnamed_prepared_stmt = NULL;
conn = SC_get_conn(self);
switch (self->status)
{
case STMT_ALLOCATED:
/* this statement does not need to be recycled */
return TRUE;
case STMT_READY:
break;
case STMT_DESCRIBED:
break;
case STMT_FINISHED:
break;
default:
SC_set_error(self, STMT_INTERNAL_ERROR, "An internal error occured while recycling statements", func);
return FALSE;
}
switch (self->prepared)
{
case NOT_YET_PREPARED:
case PREPARED_TEMPORARILY:
/* Free the parsed table/field information */
SC_initialize_cols_info(self, TRUE, TRUE);
MYLOG(DETAIL_LOG_LEVEL, "SC_clear_parse_status\n");
SC_clear_parse_status(self, conn);
break;
}
/* Free any cursors */
if (SC_get_Result(self))
SC_set_Result(self, NULL);
self->miscinfo = 0;
self->execinfo = 0;
/* self->rbonerr = 0; Never clear the bits here */
/*
* Reset only parameters that have anything to do with results
*/
self->status = STMT_READY;
self->catalog_result = FALSE; /* not very important */
self->currTuple = -1;
SC_set_rowset_start(self, -1, FALSE);
SC_set_current_col(self, -1);
self->bind_row = 0;
MYLOG(DETAIL_LOG_LEVEL, "statement=%p ommitted=0\n", self);
self->last_fetch_count = self->last_fetch_count_include_ommitted = 0;
self->__error_message = NULL;
self->__error_number = 0;
self->lobj_fd = -1;
SC_free_params(self, STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY);
SC_initialize_stmts(self, FALSE);
cancelNeedDataState(self);
self->cancel_info = 0;
/*
* reset the current attr setting to the original one.
*/
self->options.scroll_concurrency = self->options_orig.scroll_concurrency;
self->options.cursor_type = self->options_orig.cursor_type;
self->options.keyset_size = self->options_orig.keyset_size;
self->options.maxLength = self->options_orig.maxLength;
self->options.maxRows = self->options_orig.maxRows;
return TRUE;
}
/*
* Scan the query wholly or partially (if the next_cmd param specified).
* Also count the number of parameters respectviely.
*/
void
SC_scanQueryAndCountParams(const char *query, const ConnectionClass *conn,
ssize_t *next_cmd, SQLSMALLINT * pcpar,
po_ind_t *multi_st, po_ind_t *proc_return)
{
const char *tstr, *tag = NULL;
size_t taglen = 0;
char tchar, bchar, escape_in_literal = '\0';
char in_literal = FALSE, in_ident_keyword = FALSE,
in_dquote_identifier = FALSE,
in_dollar_quote = FALSE, in_escape = FALSE,
in_line_comment = FALSE, del_found = FALSE;
int comment_level = 0;
po_ind_t multi = FALSE;
SQLSMALLINT num_p;
encoded_str encstr;
MYLOG(0, "entering...\n");
num_p = 0;
if (proc_return)
*proc_return = 0;
if (next_cmd)
*next_cmd = -1;
tstr = query;
make_encoded_str(&encstr, conn, tstr);
for (bchar = '\0', tchar = encoded_nextchar(&encstr); tchar; tchar = encoded_nextchar(&encstr))
{
if (MBCS_NON_ASCII(encstr)) /* multibyte char */
{
if ((UCHAR) tchar >= 0x80)
bchar = tchar;
if (in_dquote_identifier ||
in_literal ||
in_dollar_quote ||
in_escape ||
in_line_comment ||
comment_level > 0)
;
else
in_ident_keyword = TRUE;
continue;
}
if (!multi && del_found)
{
if (IS_NOT_SPACE(tchar))
{
multi = TRUE;
if (next_cmd)
break;
}
}
if (in_ident_keyword)
{
if (isalnum(tchar) ||
DOLLAR_QUOTE == tchar ||
'_' == tchar)
{
bchar = tchar;
continue;
}
in_ident_keyword = FALSE;
}
if (in_dollar_quote)
{
if (tchar == DOLLAR_QUOTE)
{
if (strncmp((const char *) ENCODE_PTR(encstr), tag, taglen) == 0)
{
in_dollar_quote = FALSE;
tag = NULL;
encoded_position_shift(&encstr, taglen - 1);
}
}
}
else if (in_literal)
{
if (in_escape)
in_escape = FALSE;
else if (tchar == escape_in_literal)
in_escape = TRUE;
else if (tchar == LITERAL_QUOTE)
in_literal = FALSE;
}
else if (in_dquote_identifier)
{
if (tchar == IDENTIFIER_QUOTE)
in_dquote_identifier = FALSE;
}
else if (in_line_comment)
{
if (PG_LINEFEED == tchar)
in_line_comment = FALSE;
}
else if (comment_level > 0)
{
if ('/' == tchar && '*' == ENCODE_PTR(encstr)[1])
{
tchar = encoded_nextchar(&encstr);
comment_level++;
}
else if ('*' == tchar && '/' == ENCODE_PTR(encstr)[1])
{
tchar = encoded_nextchar(&encstr);
comment_level--;
}
}
else if (isalnum(tchar))
in_ident_keyword = TRUE;
else
{
if (tchar == '?')
{
if (0 == num_p && bchar == '{')
{
if (proc_return)
*proc_return = 1;
}
num_p++;
}
else if (tchar == ';')
{
del_found = TRUE;
if (next_cmd)
*next_cmd = encstr.pos;
}
else if (tchar == DOLLAR_QUOTE)
{
const char *ptr = (const char *) ENCODE_PTR(encstr);
taglen = findTag(ptr, encstr.ccsc);
if (taglen > 0)
{
in_dollar_quote = TRUE;
tag = ptr;
encoded_position_shift(&encstr, taglen - 1);
}
}
else if (tchar == LITERAL_QUOTE)
{
in_literal = TRUE;
escape_in_literal = CC_get_escape(conn);
if (!escape_in_literal)
{
if (LITERAL_EXT == ENCODE_PTR(encstr)[-1])
escape_in_literal = ESCAPE_IN_LITERAL;
}
}
else if (tchar == IDENTIFIER_QUOTE)
in_dquote_identifier = TRUE;
else if ('-' == tchar)
{
if ('-' == ENCODE_PTR(encstr)[1])
{
tchar = encoded_nextchar(&encstr);
in_line_comment = TRUE;
}
}
else if ('/' == tchar)
{
if ('*' == ENCODE_PTR(encstr)[1])
{
tchar = encoded_nextchar(&encstr);
comment_level++;
}
}
if (IS_NOT_SPACE(tchar))
bchar = tchar;
}
}
if (pcpar)
*pcpar = num_p;
if (multi_st)
*multi_st = multi;
MYLOG(0, "leaving...num_p=%d multi=%d\n", num_p, multi);
}
/*
* Describe the result set a statement will produce (for
* SQLPrepare/SQLDescribeCol)
*
* returns # of fields if successful, -1 on error.
*/
Int4
SC_describe(StatementClass *self)
{
Int4 num_fields = -1;
QResultClass *res;
MYLOG(0, "entering status = %d\n", self->status);
res = SC_get_Curres(self);
if (NULL != res)
{
num_fields = QR_NumResultCols(res);
if (num_fields > 0 ||
NULL != QR_get_command(res))
return num_fields;
}
if (self->status == STMT_READY)
{
MYLOG(0, " preprocess: status = READY\n");
self->miscinfo = 0;
self->execinfo = 0;
decideHowToPrepare(self, FALSE);
switch (SC_get_prepare_method(self))
{
case NAMED_PARSE_REQUEST:
case PARSE_TO_EXEC_ONCE:
if (SQL_SUCCESS != prepareParameters(self, FALSE))
return num_fields;
break;
case PARSE_REQ_FOR_INFO:
if (SQL_SUCCESS != prepareParameters(self, FALSE))
return num_fields;
self->status = STMT_DESCRIBED;
break;
default:
if (SQL_SUCCESS != prepareParameters(self, TRUE))
return num_fields;
self->status = STMT_DESCRIBED;
break;
}
if (res = SC_get_Curres(self), NULL != res)
{
num_fields = QR_NumResultCols(res);
return num_fields;
}
}
return num_fields;
}
/* This is only called from SQLFreeStmt(SQL_UNBIND) */
char
SC_unbind_cols(StatementClass *self)
{
ARDFields *opts = SC_get_ARDF(self);
GetDataInfo *gdata = SC_get_GDTI(self);
BindInfoClass *bookmark;
ARD_unbind_cols(opts, FALSE);
GDATA_unbind_cols(gdata, FALSE);
if (bookmark = opts->bookmark, bookmark != NULL)
{
bookmark->buffer = NULL;
bookmark->used = NULL;
}
return 1;
}
void
SC_clear_error(StatementClass *self)
{
QResultClass *res;
self->__error_number = 0;
if (self->__error_message)
{
free(self->__error_message);
self->__error_message = NULL;
}
if (self->pgerror)
{
ER_Destructor(self->pgerror);
self->pgerror = NULL;
}
self->diag_row_count = 0;
if (res = SC_get_Curres(self), res)
{
QR_set_message(res, NULL);
QR_set_notice(res, NULL);
res->sqlstate[0] = '\0';
}
self->stmt_time = 0;
memset(&self->localtime, 0, sizeof(self->localtime));
self->localtime.tm_sec = -1;
SC_unref_CC_error(self);
}
/*
* This function creates an error info which is the concatenation
* of the result, statement, connection, and socket messages.
*/
/* Map sql commands to statement types */
static const struct
{
int number;
const char ver3str[6];
const char ver2str[6];
} Statement_sqlstate[] =
{
{ STMT_ERROR_IN_ROW, "01S01", "01S01" },
{ STMT_OPTION_VALUE_CHANGED, "01S02", "01S02" },
{ STMT_ROW_VERSION_CHANGED, "01001", "01001" }, /* data changed */
{ STMT_POS_BEFORE_RECORDSET, "01S06", "01S06" },
{ STMT_TRUNCATED, "01004", "01004" }, /* data truncated */
{ STMT_INFO_ONLY, "00000", "00000" }, /* just an information that is returned, no error */
{ STMT_OK, "00000", "00000" }, /* OK */
{ STMT_EXEC_ERROR, "HY000", "S1000" }, /* also a general error */
{ STMT_STATUS_ERROR, "HY010", "S1010" },
{ STMT_SEQUENCE_ERROR, "HY010", "S1010" }, /* Function sequence error */
{ STMT_NO_MEMORY_ERROR, "HY001", "S1001" }, /* memory allocation failure */
{ STMT_COLNUM_ERROR, "07009", "S1002" }, /* invalid column number */
{ STMT_NO_STMTSTRING, "HY001", "S1001" }, /* having no stmtstring is also a malloc problem */
{ STMT_ERROR_TAKEN_FROM_BACKEND, "HY000", "S1000" }, /* general error */
{ STMT_INTERNAL_ERROR, "HY000", "S1000" }, /* general error */
{ STMT_STILL_EXECUTING, "HY010", "S1010" },
{ STMT_NOT_IMPLEMENTED_ERROR, "HYC00", "S1C00" }, /* == 'driver not
* capable' */
{ STMT_BAD_PARAMETER_NUMBER_ERROR, "07009", "S1093" },
{ STMT_OPTION_OUT_OF_RANGE_ERROR, "HY092", "S1092" },
{ STMT_INVALID_COLUMN_NUMBER_ERROR, "07009", "S1002" },
{ STMT_RESTRICTED_DATA_TYPE_ERROR, "07006", "07006" },
{ STMT_INVALID_CURSOR_STATE_ERROR, "07005", "24000" },
{ STMT_CREATE_TABLE_ERROR, "42S01", "S0001" }, /* table already exists */
{ STMT_NO_CURSOR_NAME, "S1015", "S1015" },
{ STMT_INVALID_CURSOR_NAME, "34000", "34000" },
{ STMT_INVALID_ARGUMENT_NO, "HY024", "S1009" }, /* invalid argument value */
{ STMT_ROW_OUT_OF_RANGE, "HY107", "S1107" },
{ STMT_OPERATION_CANCELLED, "HY008", "S1008" },
{ STMT_INVALID_CURSOR_POSITION, "HY109", "S1109" },
{ STMT_VALUE_OUT_OF_RANGE, "HY019", "22003" },
{ STMT_OPERATION_INVALID, "HY011", "S1011" },
{ STMT_PROGRAM_TYPE_OUT_OF_RANGE, "?????", "?????" },
{ STMT_BAD_ERROR, "08S01", "08S01" }, /* communication link failure */
{ STMT_INVALID_OPTION_IDENTIFIER, "HY092", "HY092" },
{ STMT_RETURN_NULL_WITHOUT_INDICATOR, "22002", "22002" },
{ STMT_INVALID_DESCRIPTOR_IDENTIFIER, "HY091", "HY091" },
{ STMT_OPTION_NOT_FOR_THE_DRIVER, "HYC00", "HYC00" },
{ STMT_FETCH_OUT_OF_RANGE, "HY106", "S1106" },
{ STMT_COUNT_FIELD_INCORRECT, "07002", "07002" },
{ STMT_INVALID_NULL_ARG, "HY009", "S1009" },
{ STMT_NO_RESPONSE, "08S01", "08S01" },
{ STMT_COMMUNICATION_ERROR, "08S01", "08S01" }
};
static PG_ErrorInfo *
SC_create_errorinfo(const StatementClass *self, PG_ErrorInfo *pgerror_fail_safe)
{
QResultClass *res = SC_get_Curres(self);
ConnectionClass *conn = SC_get_conn(self);
Int4 errornum;
size_t pos;
BOOL resmsg = FALSE, detailmsg = FALSE, msgend = FALSE;
BOOL looponce, loopend;
char msg[4096], *wmsg;
char *ermsg = NULL, *sqlstate = NULL;
PG_ErrorInfo *pgerror;
if (self->pgerror)
return self->pgerror;
errornum = self->__error_number;
if (errornum == 0)
return NULL;
looponce = (SC_get_Result(self) != res);
msg[0] = '\0';
for (loopend = FALSE; (NULL != res) && !loopend; res = res->next)
{
if (looponce)
loopend = TRUE;
if ('\0' != res->sqlstate[0])
{
if (NULL != sqlstate && strnicmp(res->sqlstate, "00", 2) == 0)
continue;
sqlstate = res->sqlstate;
if ('0' != sqlstate[0] ||
'1' < sqlstate[1])
loopend = TRUE;
}
if (NULL != res->message)
{
STRCPY_FIXED(msg, res->message);
detailmsg = resmsg = TRUE;
}
else if (NULL != res->messageref)
{
STRCPY_FIXED(msg, res->messageref);
detailmsg = resmsg = TRUE;
}
if (msg[0])
ermsg = msg;
else if (QR_get_notice(res))
{
char *notice = QR_get_notice(res);
size_t len = strlen(notice);
if (len < sizeof(msg))
{
memcpy(msg, notice, len);
msg[len] = '\0';
ermsg = msg;
}
else
{
ermsg = notice;
msgend = TRUE;
}
}
}
if (!msgend && (wmsg = SC_get_errormsg(self)) && wmsg[0])
{
pos = strlen(msg);
snprintf(&msg[pos], sizeof(msg) - pos, "%s%s",
detailmsg ? ";\n" : "",
wmsg);
ermsg = msg;
detailmsg = TRUE;
}
if (!self->ref_CC_error)
msgend = TRUE;
if (conn && !msgend)
{
if (!resmsg && (wmsg = CC_get_errormsg(conn)) && wmsg[0] != '\0')
{
pos = strlen(msg);
snprintf(&msg[pos], sizeof(msg) - pos,
";\n%s", CC_get_errormsg(conn));
}
ermsg = msg;
}
pgerror = ER_Constructor(self->__error_number, ermsg);
if (!pgerror)
{
if (pgerror_fail_safe)
{
memset(pgerror_fail_safe, 0, sizeof(*pgerror_fail_safe));
pgerror = pgerror_fail_safe;
pgerror->status = self->__error_number;
pgerror->errorsize = sizeof(pgerror->__error_message);
STRCPY_FIXED(pgerror->__error_message, ermsg);
pgerror->recsize = -1;
}
else
return NULL;
}
if (sqlstate)
STRCPY_FIXED(pgerror->sqlstate, sqlstate);
else if (conn)
{
if (!msgend && conn->sqlstate[0])
STRCPY_FIXED(pgerror->sqlstate, conn->sqlstate);
else
{
EnvironmentClass *env = (EnvironmentClass *) CC_get_env(conn);
errornum -= LOWEST_STMT_ERROR;
if (errornum < 0 ||
errornum >= sizeof(Statement_sqlstate) / sizeof(Statement_sqlstate[0]))
{
errornum = 1 - LOWEST_STMT_ERROR;
}
STRCPY_FIXED(pgerror->sqlstate, EN_is_odbc3(env) ?
Statement_sqlstate[errornum].ver3str :
Statement_sqlstate[errornum].ver2str);
}
}
return pgerror;
}
StatementClass *SC_get_ancestor(StatementClass *stmt)
{
StatementClass *child = stmt, *parent;
MYLOG(DETAIL_LOG_LEVEL, "entering stmt=%p\n", stmt);
for (child = stmt, parent = child->execute_parent; parent; child = parent, parent = child->execute_parent)
{
MYLOG(DETAIL_LOG_LEVEL, "parent=%p\n", parent);
}
return child;
}
void SC_reset_delegate(RETCODE retcode, StatementClass *stmt)
{
StatementClass *delegate = stmt->execute_delegate;
if (!delegate)
return;
PGAPI_FreeStmt(delegate, SQL_DROP);
}
void
SC_set_error(StatementClass *self, int number, const char *message, const char *func)
{
if (self->__error_message)
free(self->__error_message);
self->__error_number = number;
self->__error_message = message ? strdup(message) : NULL;
if (func && number != STMT_OK && number != STMT_INFO_ONLY)
SC_log_error(func, "", self);
}
void
SC_set_errormsg(StatementClass *self, const char *message)
{
if (self->__error_message)
free(self->__error_message);
self->__error_message = message ? strdup(message) : NULL;
}
void
SC_replace_error_with_res(StatementClass *self, int number, const char *message, const QResultClass *from_res, BOOL check)
{
QResultClass *self_res;
BOOL repstate;
MYLOG(DETAIL_LOG_LEVEL, "entering %p->%p check=%i\n", from_res ,self, check);
if (check)
{
if (0 == number) return;
if (0 > number && /* SQL_SUCCESS_WITH_INFO */
0 < self->__error_number)
return;
}
if (!from_res)
return;
self->__error_number = number;
if (!check || message)
{
if (self->__error_message)
free(self->__error_message);
self->__error_message = message ? strdup(message) : NULL;
}
if (self->pgerror)
{
ER_Destructor(self->pgerror);
self->pgerror = NULL;
}
self_res = SC_get_Curres(self);
if (!self_res) return;
if (self_res == from_res) return;
QR_add_message(self_res, QR_get_message(from_res));
QR_add_notice(self_res, QR_get_notice(from_res));
repstate = FALSE;
if (!check)
repstate = TRUE;
else if (from_res->sqlstate[0])
{
if (!self_res->sqlstate[0] || strncmp(self_res->sqlstate, "00", 2) == 0)
repstate = TRUE;
else if (strncmp(from_res->sqlstate, "01", 2) >= 0)
repstate = TRUE;
}
if (repstate)
STRCPY_FIXED(self_res->sqlstate, from_res->sqlstate);
}
void
SC_error_copy(StatementClass *self, const StatementClass *from, BOOL check)
{
QResultClass *self_res, *from_res;
BOOL repstate;
MYLOG(DETAIL_LOG_LEVEL, "entering %p->%p check=%i\n", from ,self, check);
if (!from) return; /* for safety */
if (self == from) return; /* for safety */
if (check)
{
if (0 == from->__error_number) /* SQL_SUCCESS */
return;
if (0 > from->__error_number && /* SQL_SUCCESS_WITH_INFO */
0 < self->__error_number)
return;
}
self->__error_number = from->__error_number;
if (!check || from->__error_message)
{
if (self->__error_message)
free(self->__error_message);
self->__error_message = from->__error_message ? strdup(from->__error_message) : NULL;
}
if (self->pgerror)
{
ER_Destructor(self->pgerror);
self->pgerror = NULL;
}
self_res = SC_get_Curres(self);
from_res = SC_get_Curres(from);
if (!self_res || !from_res)
return;
QR_add_message(self_res, QR_get_message(from_res));
QR_add_notice(self_res, QR_get_notice(from_res));
repstate = FALSE;
if (!check)
repstate = TRUE;
else if (from_res->sqlstate[0])
{
if (!self_res->sqlstate[0] || strncmp(self_res->sqlstate, "00", 2) == 0)
repstate = TRUE;
else if (strncmp(from_res->sqlstate, "01", 2) >= 0)
repstate = TRUE;
}
if (repstate)
STRCPY_FIXED(self_res->sqlstate, from_res->sqlstate);
}
void
SC_full_error_copy(StatementClass *self, const StatementClass *from, BOOL allres)
{
PG_ErrorInfo *pgerror;
MYLOG(DETAIL_LOG_LEVEL, "entering %p->%p\n", from ,self);
if (!from) return; /* for safety */
if (self == from) return; /* for safety */
if (self->__error_message)
{
free(self->__error_message);
self->__error_message = NULL;
}
if (from->__error_message)
self->__error_message = strdup(from->__error_message);
self->__error_number = from->__error_number;
if (from->pgerror)
{
if (self->pgerror)
ER_Destructor(self->pgerror);
self->pgerror = ER_Dup(from->pgerror);
return;
}
else if (!allres)
return;
pgerror = SC_create_errorinfo(from, NULL);
if (!pgerror || !pgerror->__error_message[0])
{
ER_Destructor(pgerror);
return;
}
if (self->pgerror)
ER_Destructor(self->pgerror);
self->pgerror = pgerror;
}
/* Returns the next SQL error information. */
RETCODE SQL_API
PGAPI_StmtError(SQLHSTMT hstmt,
SQLSMALLINT RecNumber,
SQLCHAR * szSqlState,
SQLINTEGER * pfNativeError,
SQLCHAR * szErrorMsg,
SQLSMALLINT cbErrorMsgMax,
SQLSMALLINT * pcbErrorMsg,
UWORD flag)
{
/* CC: return an error of a hdesc */
PG_ErrorInfo *pgerror, error;
StatementClass *stmt = (StatementClass *) hstmt;
int errnum = SC_get_errornumber(stmt);
if (pgerror = SC_create_errorinfo(stmt, &error), NULL == pgerror)
return SQL_NO_DATA_FOUND;
if (pgerror != &error)
stmt->pgerror = pgerror;
if (STMT_NO_MEMORY_ERROR == errnum &&
!pgerror->__error_message[0])
STRCPY_FIXED(pgerror->__error_message, "Memory Allocation Error??");
return ER_ReturnError(pgerror, RecNumber, szSqlState,
pfNativeError, szErrorMsg, cbErrorMsgMax,
pcbErrorMsg, flag);
}
time_t
SC_get_time(StatementClass *stmt)
{
if (!stmt)
return time(NULL);
if (0 == stmt->stmt_time)
stmt->stmt_time = time(NULL);
return stmt->stmt_time;
}
struct tm *
SC_get_localtime(StatementClass *stmt)
{
#ifndef HAVE_LOCALTIME_R
struct tm * tim;
#endif /* HAVE_LOCALTIME_R */
if (stmt->localtime.tm_sec < 0)
{
SC_get_time(stmt);
#ifdef HAVE_LOCALTIME_R
localtime_r(&stmt->stmt_time, &(stmt->localtime));
#else
tim = localtime(&stmt->stmt_time);
stmt->localtime = *tim;
#endif /* HAVE_LOCALTIME_R */
}
return &(stmt->localtime);
}
RETCODE
SC_fetch(StatementClass *self)
{
CSTR func = "SC_fetch";
QResultClass *res = SC_get_Curres(self);
ARDFields *opts;
GetDataInfo *gdata;
int retval;
RETCODE result;
Int2 num_cols,
lf;
OID type;
int atttypmod;
char *value;
ColumnInfoClass *coli;
BindInfoClass *bookmark;
BOOL useCursor;
KeySet *keyset = NULL;
/* TupleField *tupleField; */
MYLOG(DETAIL_LOG_LEVEL, "entering statement=%p res=%p ommitted=0\n", self, res);
self->last_fetch_count = self->last_fetch_count_include_ommitted = 0;
if (!res)
return SQL_ERROR;
coli = QR_get_fields(res); /* the column info */
MYLOG(0, "fetch_cursor=%d, %p->total_read=" FORMAT_LEN "\n", SC_is_fetchcursor(self), res, res->num_total_read);
useCursor = (SC_is_fetchcursor(self) && (NULL != QR_get_cursor(res)));
if (!useCursor)
{
if (self->currTuple >= (Int4) QR_get_num_total_tuples(res) - 1 ||
(self->options.maxRows > 0 && self->currTuple == self->options.maxRows - 1))
{
/*
* if at the end of the tuples, return "no data found" and set
* the cursor past the end of the result set
*/
self->currTuple = QR_get_num_total_tuples(res);
return SQL_NO_DATA_FOUND;
}
MYLOG(0, "**** : non-cursor_result\n");
(self->currTuple)++;
}
else
{
/* read from the cache or the physical next tuple */
retval = QR_next_tuple(res, self);
if (retval < 0)
{
MYLOG(0, "**** : end_tuples\n");
if (QR_get_cursor(res) &&
SQL_CURSOR_FORWARD_ONLY == self->options.cursor_type &&
QR_once_reached_eof(res))
QR_close(res);
return SQL_NO_DATA_FOUND;
}
else if (retval > 0)
(self->currTuple)++; /* all is well */
else
{
SC_set_errorinfo(self, res, 1);
return SQL_ERROR;
}
}
if (QR_haskeyset(res))
{
SQLLEN kres_ridx;
kres_ridx = GIdx2KResIdx(self->currTuple, self, res);
if (kres_ridx >= 0 && kres_ridx < res->num_cached_keys)
{
UWORD pstatus = res->keyset[kres_ridx].status;
MYLOG(DETAIL_LOG_LEVEL, "SC_ pstatus[" FORMAT_LEN "]=%hx fetch_count=" FORMAT_LEN "\n", kres_ridx, pstatus, self->last_fetch_count);
if (0 != (pstatus & (CURS_SELF_DELETING | CURS_SELF_DELETED)))
return SQL_SUCCESS_WITH_INFO;
if (SQL_ROW_DELETED != (pstatus & KEYSET_INFO_PUBLIC) &&
0 != (pstatus & CURS_OTHER_DELETED))
{
return SQL_SUCCESS_WITH_INFO;
}
if (0 != (CURS_NEEDS_REREAD & pstatus))
{
UWORD qcount;
result = SC_pos_reload(self, self->currTuple, &qcount, 0);
if (SQL_ERROR == result)
return result;
pstatus &= ~CURS_NEEDS_REREAD;
}
keyset = res->keyset + kres_ridx;
}
}
num_cols = QR_NumPublicResultCols(res);
result = SQL_SUCCESS;
self->last_fetch_count++;
MYLOG(DETAIL_LOG_LEVEL, "stmt=%p ommitted++\n", self);
self->last_fetch_count_include_ommitted++;
opts = SC_get_ARDF(self);
/*
* If the bookmark column was bound then return a bookmark. Since this
* is used with SQLExtendedFetch, and the rowset size may be greater
* than 1, and an application can use row or column wise binding, use
* the code in copy_and_convert_field() to handle that.
*/
if ((bookmark = opts->bookmark) && bookmark->buffer)
{
SC_set_current_col(self, -1);
SC_Create_bookmark(self, bookmark, self->bind_row, self->currTuple, keyset);
}
if (self->options.retrieve_data == SQL_RD_OFF) /* data isn't required */
return SQL_SUCCESS;
/* The following adjustment would be needed after SQLMoreResults() */
if (opts->allocated < num_cols)
extend_column_bindings(opts, num_cols);
gdata = SC_get_GDTI(self);
if (gdata->allocated != opts->allocated)
extend_getdata_info(gdata, opts->allocated, TRUE);
for (lf = 0; lf < num_cols; lf++)
{
MYLOG(0, "fetch: cols=%d, lf=%d, opts = %p, opts->bindings = %p, buffer[] = %p\n", num_cols, lf, opts, opts->bindings, opts->bindings[lf].buffer);
/* reset for SQLGetData */
GETDATA_RESET(gdata->gdata[lf]);
if (NULL == opts->bindings)
continue;
if (opts->bindings[lf].buffer != NULL)
{
/* this column has a binding */
type = CI_get_oid(coli, lf); /* speed things up */
atttypmod = CI_get_atttypmod(coli, lf); /* speed things up */
MYLOG(0, "type = %d, atttypmod = %d\n", type, atttypmod);
if (useCursor)
value = QR_get_value_backend(res, lf);
else
{
SQLLEN curt = GIdx2CacheIdx(self->currTuple, self, res);
MYLOG(DETAIL_LOG_LEVEL, "%p->base=" FORMAT_LEN " curr=" FORMAT_LEN " st=" FORMAT_LEN " valid=%d\n", res, QR_get_rowstart_in_cache(res), self->currTuple, SC_get_rowset_start(self), QR_has_valid_base(res));
MYLOG(DETAIL_LOG_LEVEL, "curt=" FORMAT_LEN "\n", curt);
value = QR_get_value_backend_row(res, curt, lf);
}
MYLOG(0, "value = '%s'\n", (value == NULL) ? "<NULL>" : value);
retval = copy_and_convert_field_bindinfo(self, type, atttypmod, value, lf);
MYLOG(0, "copy_and_convert: retval = %d\n", retval);
switch (retval)
{
case COPY_OK:
break; /* OK, do next bound column */
case COPY_UNSUPPORTED_TYPE:
SC_set_error(self, STMT_RESTRICTED_DATA_TYPE_ERROR, "Received an unsupported type from Postgres.", func);
result = SQL_ERROR;
break;
case COPY_UNSUPPORTED_CONVERSION:
SC_set_error(self, STMT_RESTRICTED_DATA_TYPE_ERROR, "Couldn't handle the necessary data type conversion.", func);
result = SQL_ERROR;
break;
case COPY_RESULT_TRUNCATED:
SC_set_error(self, STMT_TRUNCATED, "Fetched item was truncated.", func);
MYLOG(DETAIL_LOG_LEVEL, "The %dth item was truncated\n", lf + 1);
MYLOG(DETAIL_LOG_LEVEL, "The buffer size = " FORMAT_LEN, opts->bindings[lf].buflen);
MYLOG(DETAIL_LOG_LEVEL, " and the value is '%s'\n", value);
result = SQL_SUCCESS_WITH_INFO;
break;
case COPY_INVALID_STRING_CONVERSION: /* invalid string */
SC_set_error(self, STMT_STRING_CONVERSION_ERROR, "invalid string conversion occured.", func);
result = SQL_ERROR;
break;
/* error msg already filled in */
case COPY_GENERAL_ERROR:
result = SQL_ERROR;
break;
/* This would not be meaningful in SQLFetch. */
case COPY_NO_DATA_FOUND:
break;
default:
SC_set_error(self, STMT_INTERNAL_ERROR, "Unrecognized return value from copy_and_convert_field.", func);
result = SQL_ERROR;
break;
}
}
}
return result;
}
#include "dlg_specific.h"
RETCODE
SC_execute(StatementClass *self)
{
CSTR func = "SC_execute";
ConnectionClass *conn;
IPDFields *ipdopts;
char was_ok, was_nonfatal;
QResultClass *res = NULL;
Int2 oldstatus,
numcols;
QueryInfo qi;
ConnInfo *ci;
unsigned int qflag = 0;
BOOL is_in_trans, issue_begin, has_out_para;
BOOL use_extended_protocol;
int func_cs_count = 0, i;
BOOL useCursor, isSelectType;
conn = SC_get_conn(self);
ci = &(conn->connInfo);
/* 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.
*/
#define return DONT_CALL_RETURN_FROM_HERE???
ENTER_INNER_CONN_CS(conn, func_cs_count);
oldstatus = conn->status;
if (CONN_EXECUTING == conn->status)
{
SC_set_error(self, STMT_SEQUENCE_ERROR, "Connection is already in use.", func);
MYLOG(0, "problem with connection\n");
goto cleanup;
}
is_in_trans = CC_is_in_trans(conn);
if ((useCursor = SC_is_fetchcursor(self)))
{
QResultClass *curres = SC_get_Curres(self);
if (NULL != curres &&
curres->dataFilled)
useCursor = (NULL != QR_get_cursor(curres));
}
/* issue BEGIN ? */
issue_begin = TRUE;
if (!self->external)
issue_begin = FALSE;
else if (is_in_trans)
{
issue_begin = FALSE;
if (STMT_TYPE_START == self->statement_type &&
CC_does_autocommit(conn))
{
CC_commit(conn);
is_in_trans = CC_is_in_trans(conn);
}
}
else if (CC_does_autocommit(conn) &&
(!useCursor
/* || SC_is_with_hold(self) thiw would lose the performance */
))
issue_begin = FALSE;
else if (self->statement_type == STMT_TYPE_SPECIAL)
{
/*
* Some utility commands like VACUUM cannot be run in a transaction
* block, so don't begin one even if auto-commit mode is disabled.
*
* An application should never issue an explicit BEGIN when
* auto-commit mode is disabled (probably not even when it's enabled,
* actually). We used to also suppress the implicit BEGIN when the
* statement was of STMT_TYPE_START type, ie. if the application
* issued an explicit BEGIN, but that actually seems like a bad idea.
* First of all, if you issue a BEGIN twice the backend will give a
* warning which can be helpful to spot mistakes in the application
* (because it shouldn't be doing that).
*/
issue_begin = FALSE;
}
if (issue_begin)
{
MYLOG(0, " about to begin a transaction on statement = %p\n", self);
qflag |= GO_INTO_TRANSACTION;
}
/*
* If the session query timeout setting differs from the statement one,
* change it.
*/
if (conn->stmt_timeout_in_effect != self->options.stmt_timeout)
{
char query[64];
SPRINTF_FIXED(query, "SET statement_timeout = %d",
(int) self->options.stmt_timeout * 1000);
res = CC_send_query(conn, query, NULL, 0, NULL);
if (QR_command_maybe_successful(res))
conn->stmt_timeout_in_effect = self->options.stmt_timeout;
QR_Destructor(res);
}
if (!SC_SetExecuting(self, TRUE))
{
SC_set_error(self, STMT_OPERATION_CANCELLED, "Cancel Reuest Accepted", func);
goto cleanup;
}
conn->status = CONN_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->stmt_with_params)
use_extended_protocol = FALSE;
else
{
use_extended_protocol = TRUE;
}
isSelectType = (SC_may_use_cursor(self) || self->statement_type == STMT_TYPE_PROCCALL);
if (use_extended_protocol)
{
if (issue_begin)
CC_begin(conn);
res = libpq_bind_and_exec(self);
if (!res)
{
if (SC_get_errornumber(self) <= 0)
{
SC_set_error(self, STMT_NO_RESPONSE, "Could not receive the response, communication down ??", func);
}
goto cleanup;
}
}
else if (isSelectType)
{
char fetch[128];
const char *appendq = NULL;
QueryInfo *qryi = NULL;
qflag |= (SQL_CONCUR_READ_ONLY != self->options.scroll_concurrency ? CREATE_KEYSET : 0);
MYLOG(0, " Sending SELECT statement on stmt=%p, cursor_name='%s' qflag=%d,%d\n", self, SC_cursor_name(self), qflag, self->options.scroll_concurrency);
/* send the declare/select */
if (useCursor)
{
qi.result_in = NULL;
qi.cursor = SC_cursor_name(self);
qi.fetch_size = qi.row_size = ci->drivers.fetch_max;
SPRINTF_FIXED(fetch,
"fetch " FORMAT_LEN " in \"%s\"",
qi.fetch_size, SC_cursor_name(self));
qryi = &qi;
appendq = fetch;
qflag &= (~READ_ONLY_QUERY); /* must be a SAVEPOINT after DECLARE */
}
res = SC_get_Result(self);
if (self->curr_param_result && res)
SC_set_Result(self, res->next);
res = CC_send_query_append(conn, self->stmt_with_params, qryi, qflag, SC_get_ancestor(self), appendq);
if (useCursor && QR_command_maybe_successful(res))
{
/*
* If we sent a DECLARE CURSOR + FETCH, throw away the result of
* the DECLARE CURSOR statement, and only return the result of the
* FETCH to the caller. However, if we received any NOTICEs as
* part of the DECLARE CURSOR, carry those over.
*/
if (appendq)
{
QResultClass *qres, *nres;
for (qres = res; qres;)
{
if (qres->command && strnicmp(qres->command, "fetch", 5) == 0)
{
break;
}
nres = qres->next;
if (nres && QR_get_notice(qres) != NULL)
{
if (QR_command_successful(nres) &&
QR_command_nonfatal(qres))
{
QR_set_rstatus(nres, PORES_NONFATAL_ERROR);
}
QR_add_notice(nres, QR_get_notice(qres));
}
qres->next = NULL;
QR_Destructor(qres);
qres = nres;
/*
* If we received fewer rows than requested, there are no
* more rows to fetch.
*/
if (qres->num_cached_rows < qi.row_size)
QR_set_reached_eof(qres);
}
res = qres;
}
if (res && SC_is_with_hold(self))
QR_set_withhold(res);
}
MYLOG(0, " done sending the query:\n");
}
else
{
/* not a SELECT statement so don't use a cursor */
MYLOG(0, " it's NOT a select statement: stmt=%p\n", self);
res = CC_send_query(conn, self->stmt_with_params, NULL, qflag, SC_get_ancestor(self));
}
if (!isSelectType)
{
/*
* 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 (CC_is_in_trans(conn))
{
if (!is_in_trans)
CC_set_in_manual_trans(conn);
if (self->external && CC_does_autocommit(conn) && !CC_is_in_manual_trans(conn))
CC_commit(conn);
}
}
if (CONN_DOWN != conn->status)
conn->status = oldstatus;
self->status = STMT_FINISHED;
LEAVE_INNER_CONN_CS(func_cs_count, conn);
/* Check the status of the result */
if (res)
{
was_ok = QR_command_successful(res);
was_nonfatal = QR_command_nonfatal(res);
if (0 < SC_get_errornumber(self))
;
else if (was_ok)
SC_set_errornumber(self, STMT_OK);
else if (was_nonfatal)
SC_set_errornumber(self, STMT_INFO_ONLY);
else
SC_set_errorinfo(self, res, 0);
/* set cursor before the first tuple in the list */
self->currTuple = -1;
SC_set_current_col(self, -1);
SC_set_rowset_start(self, -1, FALSE);
/* issue "ABORT" when query aborted */
if (QR_get_aborted(res))
{
}
else
{
QResultClass *tres;
/* see if the query did return any result columns */
for (tres = res, numcols = 0; !numcols && tres; tres = tres->next)
{
numcols = QR_NumResultCols(tres);
}
/* now allocate the array to hold the binding info */
if (numcols > 0)
{
ARDFields *opts = SC_get_ARDF(self);
extend_column_bindings(opts, numcols);
if (opts->bindings == NULL)
{
QR_Destructor(res);
SC_set_error(self, STMT_NO_MEMORY_ERROR,"Could not get enough free memory to store the binding information", func);
goto cleanup;
}
}
MYLOG(DETAIL_LOG_LEVEL, "!!%p->miscinfo=%x res=%p\n", self, self->miscinfo, res);
/*
* special handling of result for keyset driven cursors.
* Use the columns info of the 1st query and
* user the keyset info of the 2nd query.
*/
if (SQL_CURSOR_KEYSET_DRIVEN == self->options.cursor_type &&
SQL_CONCUR_READ_ONLY != self->options.scroll_concurrency &&
!useCursor)
{
if (tres = res->next, tres)
{
QR_set_fields(tres, QR_get_fields(res));
QR_set_fields(res, NULL);
tres->num_fields = res->num_fields;
res->next = NULL;
QR_Destructor(res);
SC_init_Result(self);
SC_set_Result(self, tres);
res = tres;
}
}
}
}
else
{
/* Bad Error -- The error message will be in the Connection */
if (!conn->pqconn)
SC_set_error(self, STMT_BAD_ERROR, CC_get_errormsg(conn), func);
else if (self->statement_type == STMT_TYPE_CREATE)
{
SC_set_error(self, STMT_CREATE_TABLE_ERROR, "Error creating the table", func);
/*
* 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, CC_get_errormsg(conn), func);
}
}
if (!SC_get_Result(self))
SC_set_Result(self, res);
else
{
QResultClass *last;
for (last = SC_get_Result(self); NULL != last->next; last = last->next)
{
if (last == res)
break;
}
if (last != res)
last->next = res;
self->curr_param_result = 1;
}
if (NULL == SC_get_Curres(self))
SC_set_Curres(self, SC_get_Result(self));
ipdopts = SC_get_IPDF(self);
has_out_para = FALSE;
if (self->statement_type == STMT_TYPE_PROCCALL &&
(SC_get_errornumber(self) == STMT_OK ||
SC_get_errornumber(self) == STMT_INFO_ONLY))
{
Int2 io, out;
has_out_para = (CountParameters(self, NULL, &io, &out) > 0);
/*
* I'm not sure if the following REFCUR_SUPPORT stuff is valuable
* or not.
*/
#ifdef REFCUR_SUPPORT
MYLOG(DETAIL_LOG_LEVEL, "!!! numfield=%d field_type=%u\n", QR_NumResultCols(res), QR_get_field_type(res, 0));
if (!has_out_para &&
0 < QR_NumResultCols(res) &&
PG_TYPE_REFCURSOR == QR_get_field_type(res, 0))
{
char fetch[128];
int stmt_type = self->statement_type;
STR_TO_NAME(self->cursor_name, QR_get_value_backend_text(res, 0, 0));
QR_Destructor(res);
SC_init_Result(self);
SC_set_fetchcursor(self);
qi.result_in = NULL;
qi.cursor = SC_cursor_name(self);
qi.cache_size = qi.row_size = ci->drivers.fetch_max;
SPRINTF_FIXED(fetch, "fetch " FORMAT_LEN " in \"%s\"", qi.fetch_size, SC_cursor_name(self));
res = CC_send_query(conn, fetch, &qi, qflag | READ_ONLY_QUERY, SC_get_ancestor(self));
if (NULL != res)
SC_set_Result(self, res);
}
#endif /* REFCUR_SUPPORT */
}
if (has_out_para)
{ /* get the return value of the procedure call */
RETCODE ret;
HSTMT hstmt = (HSTMT) self;
self->bind_row = 0;
ret = SC_fetch(hstmt);
MYLOG(DETAIL_LOG_LEVEL, "!!SC_fetch return =%d\n", ret);
if (SQL_SUCCEEDED(ret))
{
APDFields *apdopts = SC_get_APDF(self);
SQLULEN offset = apdopts->param_offset_ptr ? *apdopts->param_offset_ptr : 0;
ARDFields *ardopts = SC_get_ARDF(self);
const ParameterInfoClass *apara;
const ParameterImplClass *ipara;
int save_bind_size = ardopts->bind_size, gidx, num_p;
ardopts->bind_size = apdopts->param_bind_type;
num_p = self->num_params;
if (ipdopts->allocated < num_p)
num_p = ipdopts->allocated;
for (i = 0, gidx = 0; i < num_p; i++)
{
ipara = ipdopts->parameters + i;
if (ipara->paramType == SQL_PARAM_OUTPUT ||
ipara->paramType == SQL_PARAM_INPUT_OUTPUT)
{
apara = apdopts->parameters + i;
ret = PGAPI_GetData(hstmt, gidx + 1, apara->CType, apara->buffer + offset, apara->buflen, apara->used ? LENADDR_SHIFT(apara->used, offset) : NULL);
if (!SQL_SUCCEEDED(ret))
{
SC_set_error(self, STMT_EXEC_ERROR, "GetData to Procedure return failed.", func);
break;
}
gidx++;
}
}
ardopts->bind_size = save_bind_size; /* restore */
}
else
{
SC_set_error(self, STMT_EXEC_ERROR, "SC_fetch to get a Procedure return failed.", func);
}
}
cleanup:
#undef return
SC_SetExecuting(self, FALSE);
CLEANUP_FUNC_CONN_CS(func_cs_count, conn);
if (CONN_DOWN != conn->status)
conn->status = oldstatus;
/* self->status = STMT_FINISHED; */
if (SC_get_errornumber(self) == STMT_OK)
return SQL_SUCCESS;
else if (SC_get_errornumber(self) < STMT_OK)
return SQL_SUCCESS_WITH_INFO;
else
{
if (!SC_get_errormsg(self) || !SC_get_errormsg(self)[0])
{
if (STMT_NO_MEMORY_ERROR != SC_get_errornumber(self))
SC_set_errormsg(self, "Error while executing the query");
SC_log_error(func, NULL, self);
}
return SQL_ERROR;
}
}
#define CALLBACK_ALLOC_ONCE 4
int enqueueNeedDataCallback(StatementClass *stmt, NeedDataCallfunc func, void *data)
{
if (stmt->num_callbacks >= stmt->allocated_callbacks)
{
SC_REALLOC_return_with_error(stmt->callbacks, NeedDataCallback,
sizeof(NeedDataCallback) * (stmt->allocated_callbacks +
CALLBACK_ALLOC_ONCE), stmt, "NeedDataCallback enqueue error", 0);
stmt->allocated_callbacks += CALLBACK_ALLOC_ONCE;
}
stmt->callbacks[stmt->num_callbacks].func = func;
stmt->callbacks[stmt->num_callbacks].data = data;
stmt->num_callbacks++;
MYLOG(DETAIL_LOG_LEVEL, "stmt=%p, func=%p, count=%d\n", stmt, func, stmt->num_callbacks);
return stmt->num_callbacks;
}
RETCODE dequeueNeedDataCallback(RETCODE retcode, StatementClass *stmt)
{
RETCODE ret;
NeedDataCallfunc func;
void *data;
int i, cnt;
MYLOG(0, "entering ret=%d count=%d\n", retcode, stmt->num_callbacks);
if (SQL_NEED_DATA == retcode)
return retcode;
if (stmt->num_callbacks <= 0)
return retcode;
func = stmt->callbacks[0].func;
data = stmt->callbacks[0].data;
for (i = 1; i < stmt->num_callbacks; i++)
stmt->callbacks[i - 1] = stmt->callbacks[i];
cnt = --stmt->num_callbacks;
ret = (*func)(retcode, data);
free(data);
if (SQL_NEED_DATA != ret && cnt > 0)
ret = dequeueNeedDataCallback(ret, stmt);
return ret;
}
void cancelNeedDataState(StatementClass *stmt)
{
int cnt = stmt->num_callbacks, i;
stmt->num_callbacks = 0;
for (i = 0; i < cnt; i++)
{
if (stmt->callbacks[i].data)
free(stmt->callbacks[i].data);
stmt->callbacks[i].data = NULL;
}
SC_reset_delegate(SQL_ERROR, stmt);
}
void
SC_log_error(const char *func, const char *desc, const StatementClass *self)
{
const char *head;
#define NULLCHECK(a) (a ? a : "(NULL)")
if (self)
{
QResultClass *res = SC_get_Result(self);
const ARDFields *opts = SC_get_ARDF(self);
const APDFields *apdopts = SC_get_APDF(self);
SQLLEN rowsetSize;
const int level = 9;
rowsetSize = (STMT_TRANSITION_EXTENDED_FETCH == self->transition_status ? opts->size_of_rowset_odbc2 : opts->size_of_rowset);
if (SC_get_errornumber(self) <= 0)
head = "STATEMENT WARNING";
else
{
head = "STATEMENT ERROR";
QLOG(level, "%s: func=%s, desc='%s', errnum=%d, errmsg='%s'\n",head, func, desc, self->__error_number, NULLCHECK(self->__error_message));
}
MYLOG(0, "%s: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", head, func, desc, self->__error_number, NULLCHECK(self->__error_message));
if (SC_get_errornumber(self) > 0)
{
QLOG(level, " ------------------------------------------------------------\n");
QLOG(level, " hdbc=%p, stmt=%p, result=%p\n", self->hdbc, self, res);
QLOG(level, " prepare=%d, external=%d\n", self->prepare, self->external);
QLOG(level, " bindings=%p, bindings_allocated=%d\n", opts->bindings, opts->allocated);
QLOG(level, " parameters=%p, parameters_allocated=%d\n", apdopts->parameters, apdopts->allocated);
QLOG(level, " statement_type=%d, statement='%s'\n", self->statement_type, NULLCHECK(self->statement));
QLOG(level, " stmt_with_params='%s'\n", NULLCHECK(self->stmt_with_params));
QLOG(level, " data_at_exec=%d, current_exec_param=%d, put_data=%d\n", self->data_at_exec, self->current_exec_param, self->put_data);
QLOG(level, " currTuple=" FORMAT_LEN ", current_col=%d, lobj_fd=%d\n", self->currTuple, self->current_col, self->lobj_fd);
QLOG(level, " maxRows=" FORMAT_LEN ", rowset_size=" FORMAT_LEN ", keyset_size=" FORMAT_LEN ", cursor_type=%u, scroll_concurrency=%d\n", self->options.maxRows, rowsetSize, self->options.keyset_size, self->options.cursor_type, self->options.scroll_concurrency);
QLOG(level, " cursor_name='%s'\n", SC_cursor_name(self));
QLOG(level, " ----------------QResult Info -------------------------------\n");
if (res)
{
QLOG(level, " fields=%p, backend_tuples=%p, tupleField=%p, conn=%p\n", QR_get_fields(res), res->backend_tuples, res->tupleField, res->conn);
QLOG(level, " fetch_count=" FORMAT_LEN ", num_total_rows=" FORMAT_ULEN ", num_fields=%d, cursor='%s'\n", res->fetch_number, QR_get_num_total_tuples(res), res->num_fields, NULLCHECK(QR_get_cursor(res)));
QLOG(level, " message='%s', command='%s', notice='%s'\n", NULLCHECK(QR_get_message(res)), NULLCHECK(res->command), NULLCHECK(res->notice));
QLOG(level, " status=%d\n", QR_get_rstatus(res));
}
/* Log the connection error if there is one */
CC_log_error(func, desc, self->hdbc);
}
}
else
{
MYLOG(0, "INVALID STATEMENT HANDLE ERROR: func=%s, desc='%s'\n", func, desc);
}
}
/*
* Extended Query
*/
static BOOL
RequestStart(StatementClass *stmt, ConnectionClass *conn, const char *func)
{
BOOL ret = TRUE;
unsigned int svpopt = 0;
#ifdef _HANDLE_ENLIST_IN_DTC_
if (conn->asdum)
CALL_IsolateDtcConn(conn, TRUE);
#endif /* _HANDLE_ENLIST_IN_DTC_ */
if (NULL == conn->pqconn)
{
SC_set_error(stmt, STMT_COMMUNICATION_ERROR, "The connection has been lost", __FUNCTION__);
return SQL_ERROR;
}
if (CC_started_rbpoint(conn))
return TRUE;
if (SC_is_readonly(stmt))
svpopt |= SVPOPT_RDONLY;
if (SQL_ERROR == SetStatementSvp(stmt, svpopt))
{
char emsg[128];
SPRINTF_FIXED(emsg, "internal savepoint error in %s", func);
SC_set_error_if_not_set(stmt, STMT_INTERNAL_ERROR, emsg, func);
return FALSE;
}
/*
* In auto-commit mode, begin a new transaction implicitly if no
* transaction is in progress yet. However, some special statements like
* VACUUM and CLUSTER cannot be run in a transaction block.
*/
if (!CC_is_in_trans(conn) && CC_loves_visible_trans(conn) &&
stmt->statement_type != STMT_TYPE_SPECIAL)
{
ret = CC_begin(conn);
}
return ret;
}
static void log_params(int nParams, const Oid *paramTypes, const UCHAR * const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat)
{
int i, j;
BOOL isBinary;
for (i = 0; i < nParams; i++)
{
isBinary = paramFormats ? paramFormats[i] : FALSE;
if (!paramValues[i])
QLOG(TUPLE_LOG_LEVEL, "\t%c (null) OID=%u\n", isBinary ? 'b' : 't', paramTypes ? paramTypes[i] : 0);
else if (isBinary)
{
QLOG(TUPLE_LOG_LEVEL, "\tb '");
for (j = 0; j < paramLengths[i]; j++)
QPRINTF(TUPLE_LOG_LEVEL, "%02x", paramValues[i][j]);
QPRINTF(TUPLE_LOG_LEVEL, " OID=%u\n", paramTypes ? paramTypes[i] : 0);
}
else
QLOG(TUPLE_LOG_LEVEL, "\tt '%s' OID=%u\n", paramValues[i], paramTypes ? paramTypes[i] : 0);
}
}
static QResultClass *
libpq_bind_and_exec(StatementClass *stmt)
{
CSTR func = "libpq_bind_and_exec";
ConnectionClass *conn = SC_get_conn(stmt);
int nParams;
Oid *paramTypes = NULL;
char **paramValues = NULL;
int *paramLengths = NULL;
int *paramFormats = NULL;
int resultFormat;
PGresult *pgres = NULL;
int pgresstatus;
QResultClass *newres = NULL;
QResultClass *res = NULL;
char *cmdtag;
char *rowcount;
/* It will only be used under Batch Protocol. */
int nBatchCount = 1;
if (!RequestStart(stmt, conn, func))
return NULL;
#ifdef NOT_USED
if (CC_is_in_trans(conn) && !CC_started_rbpoint(conn))
{
if (SQL_ERROR == SetStatementSvp(stmt, 0))
{
SC_set_error_if_not_set(stmt, STMT_INTERNAL_ERROR, "internal savepoint error in build_libpq_bind_params", func);
return NULL;
}
}
#endif /* NOT_USED */
/* 1. Bind */
MYLOG(0, "bind stmt=%p\n", stmt);
if (SC_CanUseBatchProto(stmt))
{
if (!build_libpq_bind_params_batch(stmt,
&nParams,
&nBatchCount,
¶mTypes,
¶mValues,
¶mLengths, ¶mFormats,
&resultFormat))
{
goto cleanup;
}
}
else
{
if (!build_libpq_bind_params(stmt,
&nParams,
¶mTypes,
¶mValues,
¶mLengths, ¶mFormats,
&resultFormat))
{
if (SC_get_errornumber(stmt) <= 0)
SC_set_errornumber(stmt, STMT_NO_MEMORY_ERROR);
goto cleanup;
}
}
/* 2. Execute */
MYLOG(0, "execute stmt=%p\n", stmt);
if (!SC_is_fetchcursor(stmt))
{
if (stmt->prepared == NOT_YET_PREPARED ||
(stmt->prepared == PREPARED_TEMPORARILY && conn->unnamed_prepared_stmt != stmt))
{
SC_set_error(stmt, STMT_EXEC_ERROR, "about to execute a non-prepared statement", func);
goto cleanup;
}
}
/* 2.5 Prepare and Describe if needed */
if (stmt->prepared == PREPARING_TEMPORARILY ||
(stmt->prepared == PREPARED_TEMPORARILY && conn->unnamed_prepared_stmt != stmt))
{
ProcessedStmt *pstmt;
if (!stmt->processed_statements)
{
if (prepareParametersNoDesc(stmt, FALSE, EXEC_PARAM_CAST) == SQL_ERROR)
goto cleanup;
}
pstmt = stmt->processed_statements;
QLOG(0, "PQexecParams: %p '%s' nParams=%d\n", conn->pqconn, pstmt->query, nParams);
log_params(nParams, paramTypes, (const UCHAR * const *) paramValues, paramLengths, paramFormats, resultFormat);
if (SC_CanUseBatchProto(stmt))
{
pgres = PQexecParamsBatch(conn->pqconn,
pstmt->query,
nParams,
nBatchCount,
paramTypes,
(const char **) paramValues,
paramLengths,
paramFormats,
resultFormat);
}
else
{
pgres = PQexecParams(conn->pqconn,
pstmt->query,
nParams,
paramTypes,
(const char **) paramValues,
paramLengths,
paramFormats,
resultFormat);
}
}
else
{
const char *plan_name;
if (stmt->prepared == PREPARING_PERMANENTLY)
{
if (prepareParameters(stmt, FALSE) == SQL_ERROR)
goto cleanup;
}
/* prepareParameters() set plan name, so don't fetch this earlier */
plan_name = stmt->plan_name ? stmt->plan_name : NULL_STRING;
/* already prepared */
QLOG(0, "PQexecPrepared: %p plan=%s nParams=%d\n", conn->pqconn, plan_name, nParams);
log_params(nParams, paramTypes, (const UCHAR * const *) paramValues, paramLengths, paramFormats, resultFormat);
if (SC_CanUseBatchProto(stmt))
{
/* already prepared */
pgres = PQexecPreparedBatch(conn->pqconn,
plan_name, /* portal name == plan name */
nParams,
nBatchCount,
(const char **) paramValues, paramLengths, paramFormats,
resultFormat);
}
else
{
pgres = PQexecPrepared(conn->pqconn,
plan_name, /* portal name == plan name */
nParams,
(const char **) paramValues, paramLengths, paramFormats,
resultFormat);
}
}
if (stmt->curr_param_result)
{
for (res = SC_get_Result(stmt); NULL != res && NULL != res->next; res = res->next) ;
}
else
res = NULL;
if (!res)
{
newres = res = QR_Constructor();
if (!res)
{
SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Out of memory while allocating result set", func);
goto cleanup;
}
}
/* 3. Receive results */
MYLOG(DETAIL_LOG_LEVEL, "get_Result=%p %p %d\n", res, SC_get_Result(stmt), stmt->curr_param_result);
pgresstatus = PQresultStatus(pgres);
switch (pgresstatus)
{
case PGRES_COMMAND_OK:
/* portal query command, no tuples returned */
/* read in the return message from the backend */
cmdtag = PQcmdStatus(pgres);
QLOG(0, "\tok: - 'C' - %s\n", cmdtag);
QR_set_command(res, cmdtag);
if (QR_command_successful(res))
QR_set_rstatus(res, PORES_COMMAND_OK);
/* get rowcount */
rowcount = PQcmdTuples(pgres);
if (rowcount && rowcount[0])
res->recent_processed_row_count = atoi(rowcount);
else
res->recent_processed_row_count = -1;
/* Update the transaction status of connection. */
{
PGTransactionStatusType transStatus = PQtransactionStatus(conn->pqconn);
switch(transStatus)
{
case PQTRANS_INTRANS:
case PQTRANS_INERROR:
{
const char *trimedQuery = stmt->statement;
CC_set_in_trans(conn);
/* Trim blank prefix. */
while(*trimedQuery != '\0' && IS_BLANK(*trimedQuery))
trimedQuery ++;
if (strnicmp(trimedQuery, "begin", strlen("begin")) == 0)
{
CC_set_in_manual_trans(conn);
}
else if (strnicmp(trimedQuery, "start", strlen("start")) == 0 &&
IS_BLANK(*(trimedQuery + strlen("start"))))
{
trimedQuery += strlen("start");
while(*trimedQuery != '\0' && IS_BLANK(*trimedQuery))
trimedQuery ++;
if (strnicmp(trimedQuery, "transaction", strlen("transaction")) == 0)
CC_set_in_manual_trans(conn);
}
}
break;
default:
CC_set_no_trans(conn);
CC_set_no_manual_trans(conn);
break;
}
}
break;
case PGRES_EMPTY_QUERY:
/* We return the empty query */
QR_set_rstatus(res, PORES_EMPTY_QUERY);
break;
case PGRES_NONFATAL_ERROR:
handle_pgres_error(conn, pgres, "libpq_bind_and_exec", res, FALSE);
break;
case PGRES_BAD_RESPONSE:
case PGRES_FATAL_ERROR:
handle_pgres_error(conn, pgres, "libpq_bind_and_exec", res, TRUE);
break;
case PGRES_TUPLES_OK:
if (!QR_from_PGresult(res, stmt, conn, NULL, &pgres))
goto cleanup;
if (res->rstatus == PORES_TUPLES_OK && res->notice)
QR_set_rstatus(res, PORES_NONFATAL_ERROR);
break;
case PGRES_COPY_OUT:
case PGRES_COPY_IN:
case PGRES_COPY_BOTH:
default:
/* skip the unexpected response if possible */
QR_set_rstatus(res, PORES_BAD_RESPONSE);
CC_set_error(conn, CONNECTION_BACKEND_CRAZY, "Unexpected protocol character from backend (send_query)", func);
CC_on_abort(conn, CONN_DEAD);
QLOG(0, "PQexecXxxx error: - (%d) - %s\n", pgresstatus, CC_get_errormsg(conn));
break;
}
if (res != newres && NULL != newres)
QR_Destructor(newres);
if (SC_CanUseBatchProto(stmt))
stmt->exec_end_row =
stmt->exec_current_row = SC_get_APDF(stmt)->paramset_size;
cleanup:
if (pgres)
PQclear(pgres);
if (paramValues)
{
int i;
for (i = 0; i < nParams * nBatchCount; i++)
{
if (paramValues[i] != NULL)
free(paramValues[i]);
}
free(paramValues);
}
if (paramTypes)
free(paramTypes);
if (paramLengths)
free(paramLengths);
if (paramFormats)
free(paramFormats);
return res;
}
/*
* Parse a query using libpq.
*
* 'res' is only passed here for error reporting purposes. If an error is
* encountered, it is set in 'res', and the function returns FALSE.
*/
static BOOL
ParseWithLibpq(StatementClass *stmt, const char *plan_name,
const char *query,
Int2 num_params, const char *comment, QResultClass *res)
{
CSTR func = "ParseWithLibpq";
ConnectionClass *conn = SC_get_conn(stmt);
Int4 sta_pidx = -1, end_pidx = -1;
const char *cstatus;
Oid *paramTypes = NULL;
BOOL retval = FALSE;
PGresult *pgres = NULL;
MYLOG(0, "entering plan_name=%s query=%s\n", plan_name, query);
if (!RequestStart(stmt, conn, func))
return FALSE;
if (stmt->discard_output_params)
num_params = 0;
else if (num_params != 0)
{
#ifdef NOT_USED
sta_pidx += stmt->proc_return;
#endif /* NOT_USED */
int pidx;
sta_pidx = stmt->current_exec_param;
if (num_params < 0)
end_pidx = stmt->num_params - 1;
else
end_pidx = sta_pidx + num_params - 1;
#ifdef NOT_USED
num_params = end_pidx - sta_pidx + 1;
#endif /* NOT_USED */
for (num_params = 0, pidx = sta_pidx - 1;;)
{
SC_param_next(stmt, &pidx, NULL, NULL);
if (pidx > end_pidx)
break;
else if (pidx < end_pidx)
{
if (0 == num_params)
sta_pidx = pidx;
num_params++;
}
else
{
num_params++;
break;
}
}
MYLOG(0, "sta_pidx=%d end_pidx=%d num_p=%d\n", sta_pidx, end_pidx, num_params);
}
/*
* We let the server deduce the right datatype for the parameters, except
* for out parameters, which are sent as VOID.
*/
if (num_params > 0)
{
int i;
int j;
IPDFields *ipdopts = SC_get_IPDF(stmt);
paramTypes = malloc(sizeof(Oid) * num_params);
if (paramTypes == NULL)
{
SC_set_errornumber(stmt, STMT_NO_MEMORY_ERROR);
goto cleanup;
}
MYLOG(0, "ipdopts->allocated: %d\n", ipdopts->allocated);
j = 0;
for (i = sta_pidx; i <= end_pidx; i++)
{
if (i < ipdopts->allocated)
{
if (SQL_PARAM_OUTPUT == ipdopts->parameters[i].paramType)
paramTypes[j++] = PG_TYPE_VOID;
else
paramTypes[j++] = sqltype_to_bind_pgtype(conn,
ipdopts->parameters[i].SQLType);
}
else
{
/* Unknown type of parameter. Let the server decide */
paramTypes[j++] = 0;
}
}
}
if (plan_name == NULL || plan_name[0] == '\0')
conn->unnamed_prepared_stmt = NULL;
/* Prepare */
QLOG(0, "PQprepare: %p '%s' plan=%s nParams=%d\n", conn->pqconn, query, plan_name, num_params);
pgres = PQprepare(conn->pqconn, plan_name, query, num_params, paramTypes);
if (PQresultStatus(pgres) != PGRES_COMMAND_OK)
{
handle_pgres_error(conn, pgres, "ParseWithlibpq", res, TRUE);
goto cleanup;
}
cstatus = PQcmdStatus(pgres);
QLOG(0, "\tok: - 'C' - %s\n", cstatus);
if (stmt->plan_name)
SC_set_prepared(stmt, PREPARED_PERMANENTLY);
else
SC_set_prepared(stmt, PREPARED_TEMPORARILY);
if (plan_name == NULL || plan_name[0] == '\0')
conn->unnamed_prepared_stmt = stmt;
retval = TRUE;
cleanup:
if (paramTypes)
free(paramTypes);
if (pgres)
PQclear(pgres);
return retval;
}
/*
* Parse and describe a query using libpq.
*
* Returns an empty result set that has the column information, or error code
* and message, filled in. If 'res' is not NULL, it is the result set
* returned, otherwise a new one is allocated.
*
* NB: The caller must set stmt->current_exec_param before calling this
* function!
*/
QResultClass *
ParseAndDescribeWithLibpq(StatementClass *stmt, const char *plan_name,
const char *query_param,
Int2 num_params, const char *comment,
QResultClass *res)
{
CSTR func = "ParseAndDescribeWithLibpq";
ConnectionClass *conn = SC_get_conn(stmt);
PGresult *pgres = NULL;
int num_p;
Int2 num_discard_params;
IPDFields *ipdopts;
int pidx;
int i;
Oid oid;
SQLSMALLINT paramType;
MYLOG(0, "entering plan_name=%s query=%s\n", plan_name, query_param);
if (!RequestStart(stmt, conn, func))
return NULL;
if (!res)
res = QR_Constructor();
if (!res)
{
SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Couldn't allocate memory for query", func);
return NULL;
}
/*
* We need to do Prepare + Describe as two different round-trips to the
* server, while before we switched to use libpq, we used to send a Parse
* and Describe message followed by a single Sync.
*/
if (!ParseWithLibpq(stmt, plan_name, query_param, num_params, comment, res))
goto cleanup;
/* Describe */
QLOG(0, "\tPQdescribePrepared: %p plan_name=%s\n", conn->pqconn, plan_name);
pgres = PQdescribePrepared(conn->pqconn, plan_name);
switch (PQresultStatus(pgres))
{
case PGRES_COMMAND_OK:
QLOG(0, "\tok: - 'C' - %s\n", PQcmdStatus(pgres));
/* expected */
break;
case PGRES_NONFATAL_ERROR:
handle_pgres_error(conn, pgres, "ParseAndDescribeWithLibpq", res, FALSE);
goto cleanup;
case PGRES_FATAL_ERROR:
handle_pgres_error(conn, pgres, "ParseAndDescribeWithLibpq", res, TRUE);
goto cleanup;
default:
/* skip the unexpected response if possible */
CC_set_error(conn, CONNECTION_BACKEND_CRAZY, "Unexpected result from PQdescribePrepared", func);
CC_on_abort(conn, CONN_DEAD);
MYLOG(0, "PQdescribePrepared: error - %s\n", CC_get_errormsg(conn));
goto cleanup;
}
/* Extract parameter information from the result set */
num_p = PQnparams(pgres);
MYLOG(DETAIL_LOG_LEVEL, "num_params=%d info=%d\n", stmt->num_params, num_p);
if (get_qlog() > 0 || get_mylog() > 0)
{
int i;
QLOG(0, "\tnParams=%d", num_p);
for (i = 0; i < num_p; i++)
QPRINTF(0, " %u", PQparamtype(pgres, i));
QPRINTF(0, "\n");
}
num_discard_params = 0;
if (stmt->discard_output_params)
CountParameters(stmt, NULL, NULL, &num_discard_params);
if (num_discard_params < stmt->proc_return)
num_discard_params = stmt->proc_return;
if (num_p + num_discard_params != (int) stmt->num_params)
{
MYLOG(0, "ParamInfo unmatch num_params(=%d) != info(=%d)+discard(=%d)\n", stmt->num_params, num_p, num_discard_params);
/* stmt->num_params = (Int2) num_p + num_discard_params; it's possible in case of multi command queries */
}
ipdopts = SC_get_IPDF(stmt);
extend_iparameter_bindings(ipdopts, stmt->num_params);
pidx = stmt->current_exec_param;
if (pidx >= 0)
pidx--;
for (i = 0; i < num_p; i++)
{
SC_param_next(stmt, &pidx, NULL, NULL);
if (pidx >= stmt->num_params)
{
MYLOG(0, "%dth parameter's position(%d) is out of bound[%d]\n", i, pidx, stmt->num_params);
break;
}
oid = PQparamtype(pgres, i);
paramType = ipdopts->parameters[pidx].paramType;
if (SQL_PARAM_OUTPUT != paramType ||
PG_TYPE_VOID != oid)
PIC_set_pgtype(ipdopts->parameters[pidx], oid);
}
/* Extract Portal information */
QR_set_conn(res, conn);
if (CI_read_fields_from_pgres(QR_get_fields(res), pgres))
{
Int2 dummy1, dummy2;
int cidx;
int num_io_params;
QR_set_rstatus(res, PORES_FIELDS_OK);
res->num_fields = CI_get_num_fields(QR_get_fields(res));
if (QR_haskeyset(res))
res->num_fields -= res->num_key_fields;
num_io_params = CountParameters(stmt, NULL, &dummy1, &dummy2);
if (stmt->proc_return > 0 ||
num_io_params > 0)
{
ipdopts = SC_get_IPDF(stmt);
extend_iparameter_bindings(ipdopts, stmt->num_params);
for (i = 0, cidx = 0; i < stmt->num_params; i++)
{
if (i < stmt->proc_return)
ipdopts->parameters[i].paramType = SQL_PARAM_OUTPUT;
paramType =ipdopts->parameters[i].paramType;
if (SQL_PARAM_OUTPUT == paramType ||
SQL_PARAM_INPUT_OUTPUT == paramType)
{
MYLOG(DETAIL_LOG_LEVEL, "!![%d].PGType %u->%u\n", i, PIC_get_pgtype(ipdopts->parameters[i]), CI_get_oid(QR_get_fields(res), cidx));
PIC_set_pgtype(ipdopts->parameters[i], CI_get_oid(QR_get_fields(res), cidx));
cidx++;
}
}
}
}
else
{
if (NULL == QR_get_fields(res)->coli_array)
{
QR_set_rstatus(res, PORES_NO_MEMORY_ERROR);
QR_set_messageref(res, "Out of memory while reading field information");
}
else
{
QR_set_rstatus(res, PORES_BAD_RESPONSE);
QR_set_message(res, "Error reading field information");
}
}
cleanup:
if (pgres)
PQclear(pgres);
return res;
}
enum {
CancelRequestSet = 1L
,CancelRequestAccepted = (1L << 1)
,CancelCompleted = (1L << 2)
};
/* commonly used for short term lock */
#if defined(WIN_MULTITHREAD_SUPPORT)
extern CRITICAL_SECTION common_cs;
#elif defined(POSIX_MULTITHREAD_SUPPORT)
extern pthread_mutex_t common_cs;
#endif /* WIN_MULTITHREAD_SUPPORT */
BOOL SC_IsExecuting(const StatementClass *self)
{
BOOL ret;
ENTER_COMMON_CS; /* short time blocking */
ret = (STMT_EXECUTING == self->status);
LEAVE_COMMON_CS;
return ret;
}
BOOL SC_SetExecuting(StatementClass *self, BOOL on)
{
BOOL exeSet = FALSE;
ENTER_COMMON_CS; /* short time blocking */
if (on)
{
if (0 == (self->cancel_info & CancelRequestSet))
{
self->status = STMT_EXECUTING;
exeSet = TRUE;
}
}
else
{
self->cancel_info = 0;
self->status = STMT_FINISHED;
exeSet = TRUE;
}
LEAVE_COMMON_CS;
return exeSet;
}
#ifdef NOT_USED
BOOL SC_SetCancelRequest(StatementClass *self)
{
BOOL enteredCS = FALSE;
ENTER_COMMON_CS;
if (0 != (self->cancel_info & CancelCompleted))
;
else if (STMT_EXECUTING == self->status)
{
self->cancel_info |= CancelRequestSet;
}
else
{
/* try to acquire */
if (TRY_ENTER_STMT_CS(self))
enteredCS = TRUE;
else
self->cancel_info |= CancelRequestSet;
}
LEAVE_COMMON_CS;
return enteredCS;
}
#endif /* NOT_USED */
BOOL SC_AcceptedCancelRequest(const StatementClass *self)
{
BOOL shouldCancel = FALSE;
ENTER_COMMON_CS;
if (0 != (self->cancel_info & (CancelRequestSet | CancelRequestAccepted | CancelCompleted)))
shouldCancel = TRUE;
LEAVE_COMMON_CS;
return shouldCancel;
}
static void
SC_set_error_if_not_set(StatementClass *self, int errornumber, const char *errmsg, const char *func)
{
int errnum = SC_get_errornumber(self);
if (errnum <= 0)
{
const char *emsg = SC_get_errormsg(self);
if (emsg && 0 == errnum)
SC_set_errornumber(self, errornumber);
else
SC_set_error(self, errornumber, errmsg, func);
}
}
static void
SC_set_errorinfo(StatementClass *self, QResultClass *res, int errkind)
{
ConnectionClass *conn = SC_get_conn(self);
if (CC_not_connected(conn))
{
SC_set_error_if_not_set(self, STMT_COMMUNICATION_ERROR, "The connection has been lost", __FUNCTION__);
return;
}
switch (QR_get_rstatus(res))
{
case PORES_NO_MEMORY_ERROR:
SC_set_error_if_not_set(self, STMT_NO_MEMORY_ERROR, "memory allocation error???", __FUNCTION__);
break;
case PORES_BAD_RESPONSE:
SC_set_error_if_not_set(self, STMT_COMMUNICATION_ERROR, "communication error occured", __FUNCTION__);
break;
case PORES_INTERNAL_ERROR:
SC_set_error_if_not_set(self, STMT_INTERNAL_ERROR, "Internal error fetching next row", __FUNCTION__);
break;
default:
switch (errkind)
{
case 1:
SC_set_error_if_not_set(self, STMT_EXEC_ERROR, "Error while fetching the next result", __FUNCTION__);
break;
default:
SC_set_error_if_not_set(self, STMT_EXEC_ERROR, "Error while executing the query", __FUNCTION__);
break;
}
break;
}
}
/*
* Before 9.6, the driver offered very simple bookmark support -- it is just
* the current row number.
* Now the driver offers more verbose bookmarks which contain KeySet informations
* (CTID (+ OID)). Though they consume 12bytes(row number + CTID) or 16bytes
* (row number + CTID + OID), they are useful in declare/fetch mode.
*/
PG_BM SC_Resolve_bookmark(const ARDFields *opts, Int4 idx)
{
BindInfoClass *bookmark;
SQLLEN *used;
SQLULEN offset;
SQLUINTEGER bind_size;
size_t cpylen = sizeof(Int4);
PG_BM pg_bm;
bookmark = opts->bookmark;
offset = opts->row_offset_ptr ? *(opts->row_offset_ptr) : 0;
bind_size = opts->bind_size;
memset(&pg_bm, 0, sizeof(pg_bm));
if (used = bookmark->used, used != NULL)
{
used = LENADDR_SHIFT(used, offset);
if (bind_size > 0)
used = LENADDR_SHIFT(used, idx * bind_size);
else
used = LENADDR_SHIFT(used, idx * sizeof(SQLLEN));
if (*used >= sizeof(pg_bm))
cpylen = sizeof(pg_bm);
else if (*used >= 12)
cpylen = 12;
MYLOG(0, "used=" FORMAT_LEN " cpylen=" FORMAT_SIZE_T "\n", *used, cpylen);
}
memcpy(&pg_bm, CALC_BOOKMARK_ADDR(bookmark, offset, bind_size, idx), cpylen);
MYLOG(0, "index=%d block=%d off=%d\n", pg_bm.index, pg_bm.keys.blocknum, pg_bm.keys.offset);
pg_bm.index = SC_resolve_int4_bookmark(pg_bm.index);
return pg_bm;
}
int SC_Create_bookmark(StatementClass *self, BindInfoClass *bookmark, Int4 bind_row, Int4 currTuple, const KeySet *keyset)
{
ARDFields *opts = SC_get_ARDF(self);
SQLUINTEGER bind_size = opts->bind_size;
SQLULEN offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0;
size_t cvtlen = sizeof(Int4);
PG_BM pg_bm;
MYLOG(0, "entering type=%d buflen=" FORMAT_LEN " buf=%p\n", bookmark->returntype, bookmark->buflen, bookmark->buffer);
memset(&pg_bm, 0, sizeof(pg_bm));
if (SQL_C_BOOKMARK == bookmark->returntype)
;
else if (bookmark->buflen >= sizeof(pg_bm))
cvtlen = sizeof(pg_bm);
else if (bookmark->buflen >= 12)
cvtlen = 12;
pg_bm.index = SC_make_int4_bookmark(currTuple);
if (keyset)
pg_bm.keys = *keyset;
memcpy(CALC_BOOKMARK_ADDR(bookmark, offset, bind_size, bind_row), &pg_bm, cvtlen);
if (bookmark->used)
{
SQLLEN *used = LENADDR_SHIFT(bookmark->used, offset);
if (bind_size > 0)
used = LENADDR_SHIFT(used, bind_row * bind_size);
else
used = LENADDR_SHIFT(used, bind_row * sizeof(SQLLEN));
*used = cvtlen;
}
MYLOG(0, "leaving cvtlen=" FORMAT_SIZE_T " ix(bl,of)=%d(%d,%d)\n", cvtlen, pg_bm.index, pg_bm.keys.blocknum, pg_bm.keys.offset);
return COPY_OK;
}
/* For batch execution:
* - binded parameter sets count should be greater than 1.
* - binded parameter sets should not be all ignored.
* - INSERT/UPDATE/DELETE commands enabled.
*/
BOOL SC_CanUseBatchProto(const StatementClass *self)
{
Int2 st = STMT_TYPE_UNKNOWN;
APDFields *apdopts = NULL;
int usefulParamsCount = 0;
int i = 0;
/* Trigger for protocol is off. */
if (NULL == self->hdbc ||
/* Only exten protocol can trigger batch operations on. */
0 == self->hdbc->connInfo.use_server_side_prepare ||
0 == self->hdbc->connInfo.use_batch_protocol ||
1 != self->hdbc->connInfo.backend_support_batch_proto)
return FALSE;
apdopts = SC_get_APDF(self);
if (apdopts->paramset_size <= 1)
return FALSE;
usefulParamsCount = apdopts->paramset_size;
for (i = 0; i < apdopts->paramset_size; i++)
{
if (apdopts->param_operation_ptr &&
apdopts->param_operation_ptr[i] == SQL_PARAM_IGNORE)
usefulParamsCount --;
}
/* Only one(or zero?) parameter set not ignored by user? */
if (usefulParamsCount <= 1)
return FALSE;
if (self->statement &&
st == STMT_TYPE_UNKNOWN)
st = statement_type(self->statement);
if (st == STMT_TYPE_INSERT ||
st == STMT_TYPE_UPDATE ||
st == STMT_TYPE_DELETE)
return TRUE;
return FALSE;
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。