mariadb/innobase/odbc/odbc0odbc.c
jani@hynda.mysql.fi d0e8306203 Added xml patch to mysqldump.
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
2001-11-05 23:48:03 +02:00

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);
}