mirror of
https://github.com/MariaDB/server.git
synced 2025-01-19 05:22:25 +01:00
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, "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);
|
|
}
|