summaryrefslogtreecommitdiff
path: root/innobase/odbc/odbc0odbc.c
diff options
context:
space:
mode:
Diffstat (limited to 'innobase/odbc/odbc0odbc.c')
-rw-r--r--innobase/odbc/odbc0odbc.c714
1 files changed, 714 insertions, 0 deletions
diff --git a/innobase/odbc/odbc0odbc.c b/innobase/odbc/odbc0odbc.c
new file mode 100644
index 00000000000..640a88a2503
--- /dev/null
+++ b/innobase/odbc/odbc0odbc.c
@@ -0,0 +1,714 @@
+/******************************************************
+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);
+}