mirror of
https://github.com/MariaDB/server.git
synced 2025-01-19 21:42:35 +01:00
d0e8306203
Made innodb to compile more cleanly with debugging options enabled. Fixed a few bugs and found a few possible bugs, which I hope Heikki will check. Comments needs to be fixed too. Some while() functions should be changed to do ... until for documenting purposes, because some of them must and will be processed at least once, or a variable would be used uninitialized. Regards, Jani
714 lines
16 KiB
C
714 lines
16 KiB
C
/******************************************************
|
|
Innobase ODBC client library
|
|
|
|
(c) 1998 Innobase Oy
|
|
|
|
Created 2/22/1998 Heikki Tuuri
|
|
*******************************************************/
|
|
|
|
#include "odbc0odbc.h"
|
|
|
|
#include "mem0mem.h"
|
|
#include "com0com.h"
|
|
#include "usr0sess.h"
|
|
|
|
#define ODBC_STAT_INITIAL 1
|
|
#define ODBC_STAT_PREPARED 2
|
|
#define ODBC_STAT_EXECUTED 3
|
|
|
|
|
|
typedef struct odbc_conn_struct odbc_conn_t;
|
|
typedef struct odbc_env_struct odbc_env_t;
|
|
|
|
/* ODBC parameter description struct */
|
|
|
|
typedef struct odbc_param_struct odbc_param_t;
|
|
struct odbc_param_struct{
|
|
ulint data_type; /* SQL_CHAR, ... */
|
|
ibool is_input; /* TRUE if an input parameter of a stored
|
|
procedure, FALSE if an output parameter */
|
|
byte* buf; /* buffer where the value is stored before
|
|
SQLExecute, or where it comes after SQLExecute
|
|
in the case of an output parameter */
|
|
lint* data_len; /* pointer to where the data len or the value
|
|
SQL_NULL_DATA is stored */
|
|
ulint buf_len; /* buffer size */
|
|
};
|
|
|
|
/* ODBC statement data structure */
|
|
|
|
typedef struct odbc_stat_struct odbc_stat_t;
|
|
struct odbc_stat_struct{
|
|
ulint state; /* ODBC_STAT_INITIAL,
|
|
ODBC_STAT_PREPARED,
|
|
ODBC_STAT_EXECUTED */
|
|
ulint id; /* statement id */
|
|
ulint n_params; /* number of parameters */
|
|
odbc_param_t* params; /* pointer to an array describing
|
|
the parameters, if any */
|
|
ulint n_params_bound; /* number of parameters that have
|
|
been bound: the statement cannot be
|
|
executed before n_params_bound
|
|
== n_params */
|
|
byte* error_msg; /* possible error message, or NULL;
|
|
allocated separately from dynamic
|
|
memory */
|
|
ulint error_msg_len; /* error mesage length if it is
|
|
non-NULL */
|
|
odbc_conn_t* conn; /* connection */
|
|
UT_LIST_NODE_T(odbc_stat_t)
|
|
stat_list; /* list of the statements of the
|
|
connection */
|
|
};
|
|
|
|
/* ODBC connection data structure */
|
|
|
|
struct odbc_conn_struct{
|
|
ibool connected; /* TRUE if connected */
|
|
char* server_name; /* server name where connected
|
|
(= server address) */
|
|
ulint server_name_len;/* length of the server name */
|
|
com_endpoint_t* com_endpoint; /* connection endpoint for this client
|
|
connection */
|
|
dulint out_msg_count; /* count of outgoing messages */
|
|
byte* out_datagram_buf;/* buffer for outgoing datagrams to
|
|
the server */
|
|
byte* in_datagram_buf;/* buffer for incoming datagrams from
|
|
the server */
|
|
byte* addr_buf; /* buffer for the address from which
|
|
an incoming datagram came; in practice,
|
|
this will be the server address */
|
|
dulint sess_id; /* user session id, once the
|
|
connection to the server is
|
|
established */
|
|
odbc_env_t* env; /* environment */
|
|
UT_LIST_BASE_NODE_T(odbc_stat_t)
|
|
stat_list; /* list of the statements of the
|
|
connection */
|
|
UT_LIST_NODE_T(odbc_conn_t)
|
|
conn_list; /* list of the connections of the
|
|
environment */
|
|
};
|
|
|
|
/* ODBC environment data structure */
|
|
|
|
struct odbc_env_struct{
|
|
UT_LIST_BASE_NODE_T(odbc_conn_t) conn_list;
|
|
/* list of the connections of the
|
|
environment */
|
|
};
|
|
|
|
/**************************************************************************
|
|
Gets the nth parameter description struct for a statement. */
|
|
UNIV_INLINE
|
|
odbc_param_t*
|
|
stat_get_nth_param(
|
|
/*===============*/
|
|
/* out: nth parameter */
|
|
odbc_stat_t* stat, /* in: pointer to statement handle */
|
|
ulint i) /* in: parameter index */
|
|
{
|
|
ut_ad(stat->n_params > i);
|
|
|
|
return(stat->params + i);
|
|
}
|
|
|
|
/**************************************************************************
|
|
Allocates an SQL environment. */
|
|
|
|
RETCODE
|
|
SQLAllocEnv(
|
|
/*========*/
|
|
/* out: SQL_SUCCESS */
|
|
HENV* phenv) /* out: pointer to an environment handle */
|
|
{
|
|
odbc_env_t* env;
|
|
|
|
if (!sync_initialized) {
|
|
sync_init();
|
|
mem_init(2000000);
|
|
}
|
|
|
|
env = mem_alloc(sizeof(odbc_env_t));
|
|
|
|
UT_LIST_INIT(env->conn_list);
|
|
|
|
*phenv = env;
|
|
|
|
return(SQL_SUCCESS);
|
|
}
|
|
|
|
/**************************************************************************
|
|
Allocates an SQL connection. */
|
|
|
|
RETCODE
|
|
SQLAllocConnect(
|
|
/*============*/
|
|
/* out: SQL_SUCCESS */
|
|
HENV henv, /* in: pointer to an environment handle */
|
|
HDBC* phdbc) /* out: pointer to a connection handle */
|
|
{
|
|
odbc_conn_t* conn;
|
|
odbc_env_t* env;
|
|
|
|
ut_a(henv);
|
|
|
|
env = henv;
|
|
conn = mem_alloc(sizeof(odbc_conn_t));
|
|
|
|
conn->connected = FALSE;
|
|
conn->env = env;
|
|
|
|
UT_LIST_INIT(conn->stat_list);
|
|
|
|
UT_LIST_ADD_LAST(conn_list, env->conn_list, conn);
|
|
|
|
*phdbc = conn;
|
|
|
|
return(SQL_SUCCESS);
|
|
}
|
|
|
|
/**************************************************************************
|
|
Allocates an SQL statement. */
|
|
|
|
RETCODE
|
|
SQLAllocStmt(
|
|
/*=========*/
|
|
HDBC hdbc, /* in: SQL connection */
|
|
HSTMT* phstmt) /* out: pointer to a statement handle */
|
|
{
|
|
odbc_conn_t* conn;
|
|
odbc_stat_t* stat;
|
|
|
|
ut_a(hdbc);
|
|
|
|
conn = hdbc;
|
|
|
|
stat = mem_alloc(sizeof(odbc_stat_t));
|
|
|
|
stat->state = ODBC_STAT_INITIAL;
|
|
stat->error_msg = NULL;
|
|
stat->conn = conn;
|
|
|
|
UT_LIST_ADD_LAST(stat_list, conn->stat_list, stat);
|
|
|
|
*phstmt = stat;
|
|
|
|
return(SQL_SUCCESS);
|
|
}
|
|
|
|
/**************************************************************************
|
|
Sends the message in datagram_buf to the server. */
|
|
static
|
|
void
|
|
odbc_send_cli_msg(
|
|
/*==============*/
|
|
odbc_conn_t* conn, /* in: connection, does not have to be
|
|
connected yet */
|
|
ulint len) /* in: message length (excluding the standard
|
|
header of size SESS_CLI_MSG_DATA) */
|
|
{
|
|
ulint ret;
|
|
ulint fold;
|
|
byte* msg;
|
|
|
|
ut_a(len + SESS_CLI_MSG_DATA <= ODBC_DATAGRAM_SIZE);
|
|
|
|
msg = conn->out_datagram_buf;
|
|
|
|
mach_write_to_8(msg + SESS_CLI_MSG_NO, conn->out_msg_count);
|
|
|
|
UT_DULINT_INC(conn->out_msg_count);
|
|
|
|
fold = ut_fold_binary(msg + 4, len + SESS_CLI_MSG_DATA - 4);
|
|
|
|
ut_ad(SESS_CLI_MSG_CHECKSUM == 0);
|
|
|
|
mach_write_to_4(msg + SESS_CLI_MSG_CHECKSUM, fold);
|
|
|
|
ret = com_sendto(conn->com_endpoint, msg, SESS_CLI_MSG_DATA + len,
|
|
conn->server_name, conn->server_name_len);
|
|
ut_a(ret == 0);
|
|
}
|
|
|
|
/**************************************************************************
|
|
Receives a message in datagram_buf from the server. */
|
|
static
|
|
void
|
|
odbc_recv_srv_msg(
|
|
/*==============*/
|
|
odbc_conn_t* conn, /* in: connection, does not have to be
|
|
connected yet */
|
|
ulint* len) /* out: received message length (excluding the
|
|
standard header of size SESS_SRV_MSG_DATA) */
|
|
{
|
|
ulint total_len;
|
|
ulint addr_len;
|
|
ulint ret;
|
|
|
|
ret = com_recvfrom(conn->com_endpoint, conn->in_datagram_buf,
|
|
ODBC_DATAGRAM_SIZE, &total_len, (char*)conn->addr_buf,
|
|
ODBC_ADDRESS_SIZE, &addr_len);
|
|
ut_a(ret == 0);
|
|
ut_a(total_len >= SESS_SRV_MSG_DATA);
|
|
|
|
*len = total_len - SESS_SRV_MSG_DATA;
|
|
}
|
|
|
|
/**************************************************************************
|
|
Connects to a database server process (establishes a connection and a
|
|
session). */
|
|
|
|
RETCODE
|
|
SQLConnect(
|
|
/*=======*/
|
|
/* out: SQL_SUCCESS */
|
|
HDBC hdbc, /* in: SQL connection handle */
|
|
UCHAR* szDSN, /* in: data source name (server name) */
|
|
SWORD cbDSN, /* in: data source name length */
|
|
UCHAR* szUID, /* in: user name */
|
|
SWORD cbUID, /* in: user name length */
|
|
UCHAR* szAuthStr, /* in: password */
|
|
SWORD cbAuthStr) /* in: password length */
|
|
{
|
|
com_endpoint_t* ep;
|
|
odbc_conn_t* conn;
|
|
ulint err;
|
|
ulint size;
|
|
byte* msg;
|
|
ulint len;
|
|
UCHAR catenated_name[100];
|
|
|
|
ut_a(hdbc && szDSN);
|
|
|
|
UT_NOT_USED(szUID);
|
|
UT_NOT_USED(cbUID);
|
|
UT_NOT_USED(szAuthStr);
|
|
UT_NOT_USED(cbAuthStr);
|
|
|
|
conn = hdbc;
|
|
|
|
ut_a(!conn->connected);
|
|
|
|
conn->server_name = mem_alloc(cbDSN);
|
|
ut_memcpy(conn->server_name, szDSN, cbDSN);
|
|
|
|
conn->server_name_len = cbDSN;
|
|
|
|
ep = com_endpoint_create(COM_SHM);
|
|
|
|
ut_a(ep);
|
|
|
|
conn->com_endpoint = ep;
|
|
|
|
conn->out_msg_count = ut_dulint_zero;
|
|
|
|
size = ODBC_DATAGRAM_SIZE;
|
|
|
|
err = com_endpoint_set_option(ep, COM_OPT_MAX_DGRAM_SIZE,
|
|
(byte*)&size, 4);
|
|
ut_a(err == 0);
|
|
|
|
/* Make the data source name catenated to user name as the
|
|
address of the communications endpoint */
|
|
|
|
ut_a((ulint)cbDSN + (ulint)cbUID < 100);
|
|
|
|
ut_memcpy(catenated_name, szDSN, (ulint)cbDSN);
|
|
ut_memcpy(catenated_name + (ulint)cbDSN, szUID, (ulint)cbUID);
|
|
|
|
err = com_bind(ep, (char*)catenated_name, (ulint)cbDSN
|
|
+ (ulint)cbUID);
|
|
ut_a(err == 0);
|
|
|
|
conn->in_datagram_buf = mem_alloc(ODBC_DATAGRAM_SIZE);
|
|
|
|
msg = mem_alloc(ODBC_DATAGRAM_SIZE);
|
|
|
|
conn->out_datagram_buf = msg;
|
|
conn->addr_buf = mem_alloc(ODBC_ADDRESS_SIZE);
|
|
|
|
/* Set the session id to dulint 0 as we are not yet connected */
|
|
|
|
sess_cli_msg_set_sess(msg, ut_dulint_zero);
|
|
sess_cli_msg_set_type(msg, SESS_CLI_CONNECT);
|
|
|
|
/*------------------------------------------*/
|
|
|
|
odbc_send_cli_msg(conn, 0);
|
|
|
|
odbc_recv_srv_msg(conn, &len);
|
|
|
|
/*------------------------------------------*/
|
|
|
|
ut_a(len == 0);
|
|
ut_a(sess_srv_msg_get_type(conn->in_datagram_buf)
|
|
== SESS_SRV_ACCEPT_CONNECT);
|
|
|
|
conn->sess_id = mach_read_from_8(conn->in_datagram_buf
|
|
+ SESS_SRV_MSG_SESS_ID);
|
|
|
|
/* Write the session id to out_datagram_buf: it will not be rewritten
|
|
until the connection is closed, as the session id will stay the same */
|
|
|
|
sess_cli_msg_set_sess(msg, conn->sess_id);
|
|
|
|
/* We currently only send single part messages: the following will
|
|
stay 0 during the connection */
|
|
|
|
mach_write_to_4(msg + SESS_CLI_MSG_CONTINUE, 0);
|
|
mach_write_to_4(msg + SESS_CLI_MSG_CONT_SIZE, 0);
|
|
|
|
conn->connected = TRUE;
|
|
|
|
return(SQL_SUCCESS);
|
|
}
|
|
|
|
/**************************************************************************
|
|
Stores an error message to a statement handle, so that it can be later
|
|
queried with SQLError. */
|
|
static
|
|
void
|
|
odbc_stat_store_error_msg(
|
|
/*======================*/
|
|
odbc_stat_t* stat, /* in: statement handle */
|
|
byte* msg, /* in: error message sent by the server */
|
|
ulint len) /* in: length of msg */
|
|
{
|
|
if (stat->error_msg) {
|
|
mem_free(stat->error_msg);
|
|
}
|
|
|
|
stat->error_msg_len = len;
|
|
|
|
len += SESS_SRV_MSG_DATA;
|
|
|
|
stat->error_msg = mem_alloc(len);
|
|
ut_memcpy(stat->error_msg, msg, len);
|
|
}
|
|
|
|
/**************************************************************************
|
|
Queries an error message. */
|
|
|
|
RETCODE
|
|
SQLError(
|
|
/*=====*/
|
|
/* out: SQL_SUCCESS or SQL_NO_DATA_FOUND */
|
|
HENV henv, /* in: SQL_NULL_HENV */
|
|
HDBC hdbc, /* in: SQL_NULL_HDBC */
|
|
HSTMT hstmt, /* in: statement handle */
|
|
UCHAR* szSqlState, /* in/out: SQLSTATE as a null-terminated string,
|
|
(currently, always == "S1000") */
|
|
SDWORD* pfNativeError, /* out: native error code */
|
|
UCHAR* szErrorMsg, /* in/out: buffer for an error message as a
|
|
null-terminated string */
|
|
SWORD cbErrorMsgMax, /* in: buffer size for szErrorMsg */
|
|
SWORD* pcbErrorMsg) /* out: error message length */
|
|
{
|
|
odbc_stat_t* stat;
|
|
ulint len;
|
|
|
|
ut_a(henv == SQL_NULL_HENV);
|
|
ut_a(hdbc == SQL_NULL_HDBC);
|
|
ut_a(hstmt);
|
|
ut_a(cbErrorMsgMax > 1);
|
|
|
|
stat = hstmt;
|
|
|
|
if (stat->error_msg == NULL) {
|
|
|
|
return(SQL_NO_DATA_FOUND);
|
|
}
|
|
|
|
*pfNativeError = 0;
|
|
ut_memcpy(szSqlState, (char *) "S1000", 6);
|
|
|
|
len = (ulint)cbErrorMsgMax - 1;
|
|
|
|
if (stat->error_msg_len < len) {
|
|
len = stat->error_msg_len;
|
|
}
|
|
|
|
ut_memcpy(szErrorMsg, stat->error_msg + SESS_SRV_MSG_DATA, len);
|
|
|
|
*(szErrorMsg + len) = '\0';
|
|
|
|
*pcbErrorMsg = (SWORD)len;
|
|
|
|
if (stat->error_msg) {
|
|
mem_free(stat->error_msg);
|
|
stat->error_msg = NULL;
|
|
}
|
|
|
|
return(SQL_SUCCESS);
|
|
}
|
|
|
|
/**************************************************************************
|
|
Makes the server to parse and optimize an SQL string. */
|
|
|
|
RETCODE
|
|
SQLPrepare(
|
|
/*=======*/
|
|
/* out: SQL_SUCCESS or SQL_ERROR */
|
|
HSTMT hstmt, /* in: statement handle */
|
|
UCHAR* szSqlStr, /* in: SQL string */
|
|
SDWORD cbSqlStr) /* in: SQL string length */
|
|
{
|
|
odbc_stat_t* stat;
|
|
odbc_conn_t* conn;
|
|
odbc_param_t* param;
|
|
ulint len;
|
|
byte* msg;
|
|
ulint i;
|
|
|
|
stat = hstmt;
|
|
conn = stat->conn;
|
|
|
|
if (stat->error_msg) {
|
|
mem_free(stat->error_msg);
|
|
stat->error_msg = NULL;
|
|
}
|
|
|
|
ut_memcpy(conn->out_datagram_buf + SESS_CLI_MSG_DATA, szSqlStr,
|
|
1 + (ulint)cbSqlStr);
|
|
|
|
sess_cli_msg_set_type(conn->out_datagram_buf, SESS_CLI_PREPARE);
|
|
|
|
/* The client message will be decoded in sess_receive_prepare */
|
|
|
|
/*------------------------------------------*/
|
|
|
|
odbc_send_cli_msg(conn, 1 + (ulint)cbSqlStr);
|
|
|
|
odbc_recv_srv_msg(conn, &len);
|
|
|
|
/*------------------------------------------*/
|
|
|
|
/* The server message was coded in sess_receive_prepare */
|
|
|
|
ut_a(len >= 8);
|
|
|
|
msg = conn->in_datagram_buf;
|
|
|
|
if (sess_srv_msg_get_type(msg) != SESS_SRV_SUCCESS) {
|
|
|
|
ut_a(sess_srv_msg_get_type(msg) == SESS_SRV_ERROR);
|
|
|
|
odbc_stat_store_error_msg(stat, msg, len);
|
|
|
|
return(SQL_ERROR);
|
|
}
|
|
|
|
stat->id = mach_read_from_4(msg + SESS_SRV_MSG_DATA);
|
|
|
|
stat->n_params = mach_read_from_4(msg + SESS_SRV_MSG_DATA + 4);
|
|
|
|
stat->n_params_bound = 0;
|
|
|
|
ut_a(len == 8 + stat->n_params);
|
|
|
|
if (stat->n_params > 0) {
|
|
|
|
stat->params = mem_alloc(stat->n_params
|
|
* sizeof(odbc_param_t));
|
|
for (i = 0; i < stat->n_params; i++) {
|
|
param = stat_get_nth_param(stat, i);
|
|
|
|
param->is_input = mach_read_from_1(
|
|
msg + SESS_SRV_MSG_DATA + 8 + i);
|
|
/* Set buf to NULL so that we know when the parameter
|
|
has been bound */
|
|
|
|
param->buf = NULL;
|
|
}
|
|
}
|
|
|
|
stat->state = ODBC_STAT_PREPARED;
|
|
|
|
return(SQL_SUCCESS);
|
|
}
|
|
|
|
/**************************************************************************
|
|
Binds a parameter in a prepared statement. */
|
|
|
|
RETCODE
|
|
SQLBindParameter(
|
|
/*=============*/
|
|
/* out: SQL_SUCCESS */
|
|
HSTMT hstmt, /* in: statement handle */
|
|
UWORD ipar, /* in: parameter index, starting from 1 */
|
|
SWORD fParamType, /* in: SQL_PARAM_INPUT or SQL_PARAM_OUTPUT */
|
|
SWORD fCType, /* in: SQL_C_CHAR, ... */
|
|
SWORD fSqlType, /* in: SQL_CHAR, ... */
|
|
UDWORD cbColDef, /* in: precision: ignored */
|
|
SWORD ibScale, /* in: scale: ignored */
|
|
PTR rgbValue, /* in: pointer to a buffer for the data */
|
|
SDWORD cbValueMax, /* in: buffer size */
|
|
SDWORD* pcbValue) /* in: pointer to a buffer for the data
|
|
length or SQL_NULL_DATA */
|
|
{
|
|
odbc_stat_t* stat;
|
|
odbc_param_t* param;
|
|
|
|
stat = hstmt;
|
|
|
|
ut_a(stat->state != ODBC_STAT_INITIAL);
|
|
ut_a(rgbValue);
|
|
ut_a(ipar <= stat->n_params);
|
|
ut_a(ipar > 0);
|
|
ut_a(cbValueMax >= 0);
|
|
ut_a(pcbValue);
|
|
|
|
UT_NOT_USED(ibScale);
|
|
UT_NOT_USED(fCType);
|
|
UT_NOT_USED(cbColDef);
|
|
|
|
if (stat->error_msg) {
|
|
mem_free(stat->error_msg);
|
|
stat->error_msg = NULL;
|
|
}
|
|
|
|
param = stat_get_nth_param(stat, ipar - 1);
|
|
|
|
if (param->buf == NULL) {
|
|
stat->n_params_bound++;
|
|
}
|
|
|
|
param->data_type = fSqlType;
|
|
|
|
ut_a((fParamType != SQL_PARAM_INPUT) || param->is_input);
|
|
ut_a((fParamType == SQL_PARAM_INPUT) || !param->is_input);
|
|
|
|
param->buf = rgbValue;
|
|
param->buf_len = cbValueMax;
|
|
param->data_len = pcbValue;
|
|
|
|
return(SQL_SUCCESS);
|
|
}
|
|
|
|
/**************************************************************************
|
|
Executes a prepared statement where all parameters have been bound. */
|
|
|
|
RETCODE
|
|
SQLExecute(
|
|
/*=======*/
|
|
/* out: SQL_SUCCESS or SQL_ERROR */
|
|
HSTMT hstmt) /* in: statement handle */
|
|
{
|
|
odbc_stat_t* stat;
|
|
odbc_conn_t* conn;
|
|
odbc_param_t* param;
|
|
lint len;
|
|
ulint msg_len;
|
|
byte* msg;
|
|
byte* ptr;
|
|
lint int_val;
|
|
ulint i;
|
|
|
|
stat = hstmt;
|
|
|
|
ut_a(stat->state != ODBC_STAT_INITIAL);
|
|
ut_a(stat->n_params == stat->n_params_bound);
|
|
|
|
if (stat->error_msg) {
|
|
mem_free(stat->error_msg);
|
|
stat->error_msg = NULL;
|
|
}
|
|
|
|
conn = stat->conn;
|
|
msg = conn->out_datagram_buf;
|
|
|
|
sess_cli_msg_set_type(msg, SESS_CLI_EXECUTE);
|
|
|
|
ptr = msg + SESS_CLI_MSG_DATA;
|
|
|
|
mach_write_to_4(ptr, stat->id);
|
|
|
|
ptr += 4;
|
|
|
|
for (i = 0; i < stat->n_params; i++) {
|
|
|
|
param = stat_get_nth_param(stat, i);
|
|
|
|
if (param->is_input) {
|
|
/* Copy its length and data to the message buffer */
|
|
|
|
len = *(param->data_len);
|
|
|
|
mach_write_to_4(ptr, (ulint)len);
|
|
|
|
ptr += 4;
|
|
|
|
if (len != SQL_NULL_DATA) {
|
|
if (param->data_type == SQL_INTEGER) {
|
|
ut_ad(len == 4);
|
|
int_val = *((lint*)(param->buf));
|
|
|
|
mach_write_to_4(ptr, (ulint)int_val);
|
|
} else {
|
|
ut_memcpy(ptr, param->buf, len);
|
|
}
|
|
|
|
ptr += len;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* The client message will be decoded in sess_receive_command */
|
|
|
|
/*------------------------------------------*/
|
|
|
|
odbc_send_cli_msg(conn, ptr - (msg + SESS_CLI_MSG_DATA));
|
|
|
|
odbc_recv_srv_msg(conn, &msg_len);
|
|
|
|
/*------------------------------------------*/
|
|
|
|
/* The server message was coded in sess_command_completed_message */
|
|
|
|
msg = conn->in_datagram_buf;
|
|
|
|
if (sess_srv_msg_get_type(msg) != SESS_SRV_SUCCESS) {
|
|
|
|
ut_a(sess_srv_msg_get_type(msg) == SESS_SRV_ERROR);
|
|
|
|
odbc_stat_store_error_msg(stat, msg, msg_len);
|
|
|
|
return(SQL_ERROR);
|
|
}
|
|
|
|
ptr = msg + SESS_SRV_MSG_DATA;
|
|
|
|
for (i = 0; i < stat->n_params; i++) {
|
|
|
|
param = stat_get_nth_param(stat, i);
|
|
|
|
if (!param->is_input) {
|
|
/* Copy its length and data from the message buffer */
|
|
|
|
len = (lint)mach_read_from_4(ptr);
|
|
|
|
ptr += 4;
|
|
|
|
*(param->data_len) = len;
|
|
|
|
if (len != SQL_NULL_DATA) {
|
|
if (param->data_type == SQL_INTEGER) {
|
|
ut_ad(len == 4);
|
|
|
|
int_val = (lint)mach_read_from_4(ptr);
|
|
|
|
*((lint*)(param->buf)) = int_val;
|
|
} else {
|
|
ut_memcpy(param->buf, ptr, (ulint)len);
|
|
}
|
|
|
|
ptr += len;
|
|
}
|
|
}
|
|
}
|
|
|
|
ut_ad(msg + SESS_SRV_MSG_DATA + msg_len == ptr);
|
|
|
|
return(SQL_SUCCESS);
|
|
}
|