diff options
Diffstat (limited to 'storage/connect/myconn.cpp')
-rw-r--r-- | storage/connect/myconn.cpp | 1098 |
1 files changed, 1098 insertions, 0 deletions
diff --git a/storage/connect/myconn.cpp b/storage/connect/myconn.cpp new file mode 100644 index 00000000..bb6f72de --- /dev/null +++ b/storage/connect/myconn.cpp @@ -0,0 +1,1098 @@ +/************** MyConn C++ Program Source Code File (.CPP) **************/ +/* PROGRAM NAME: MYCONN */ +/* ------------- */ +/* Version 1.9 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to the author Olivier BERTRAND 2007-2017 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* Implements a connection to MySQL. */ +/* It can optionally use the embedded MySQL library. */ +/* */ +/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */ +/* -------------------------------------- */ +/* */ +/* REQUIRED FILES: */ +/* --------------- */ +/* MYCONN.CPP - Source code */ +/* MYCONN.H - MYCONN class declaration file */ +/* GLOBAL.H - Global declaration file */ +/* */ +/* REQUIRED LIBRARIES: */ +/* ------------------- */ +/* Large model C library */ +/* */ +/* REQUIRED PROGRAMS: */ +/* ------------------ */ +/* IBM, Borland, GNU or Microsoft C++ Compiler and Linker */ +/* */ +/************************************************************************/ +#include "my_global.h" +#if !defined(MYSQL_PREPARED_STATEMENTS) +#include "my_sys.h" +#include "mysqld_error.h" +#endif // !MYSQL_PREPARED_STATEMENTS +#if defined(_WIN32) +//#include <windows.h> +#else // !_WIN32 +#include "osutil.h" +#endif // !_WIN32 + +#include "global.h" +#include "plgdbsem.h" +#include "plgcnx.h" // For DB types +#include "resource.h" +//#include "value.h" +//#include "valblk.h" +#include "xobject.h" +#define DLL_EXPORT // Items are exported from this DLL +#include "myconn.h" + +//extern "C" int zconv; +int GetConvSize(void); +extern MYSQL_PLUGIN_IMPORT uint mysqld_port; +extern MYSQL_PLUGIN_IMPORT char *mysqld_unix_port; + +DllExport void PushWarning(PGLOBAL, THD*, int level = 1); + +// Returns the current used port +uint GetDefaultPort(void) +{ + return mysqld_port; +} // end of GetDefaultPort + +#if !defined(MYSQL_PREPARED_STATEMENTS) +/************************************************************************** + Alloc struct for use with unbuffered reads. Data is fetched by domand + when calling to mysql_fetch_row. + mysql_data_seek is a noop. + + No other queries may be specified with the same MYSQL handle. + There shouldn't be much processing per row because mysql server shouldn't + have to wait for the client (and will not wait more than 30 sec/packet). + NOTE: copied from client.c cli_use_result +**************************************************************************/ +static MYSQL_RES *connect_use_result(MYSQL *mysql) +{ + MYSQL_RES *result; + DBUG_ENTER("connect_use_result"); + + if (!mysql->fields) + DBUG_RETURN(NULL); + + if (mysql->status != MYSQL_STATUS_GET_RESULT) { + my_message(ER_UNKNOWN_ERROR, "Command out of sync", MYF(0)); + DBUG_RETURN(NULL); + } // endif status + + if (!(result = (MYSQL_RES*) my_malloc(PSI_NOT_INSTRUMENTED, + sizeof(*result) + sizeof(ulong) * mysql->field_count, + MYF(MY_WME | MY_ZEROFILL)))) + DBUG_RETURN(NULL); + + result->lengths = (ulong*)(result+1); + result->methods = mysql->methods; + + /* Ptrs: to one row */ + if (!(result->row = (MYSQL_ROW)my_malloc(PSI_NOT_INSTRUMENTED, + sizeof(result->row[0]) * (mysql->field_count+1), MYF(MY_WME)))) { + my_free(result); + DBUG_RETURN(NULL); + } // endif row + + result->fields = mysql->fields; + result->field_alloc = mysql->field_alloc; + result->field_count = mysql->field_count; + result->current_field = 0; + result->handle = mysql; + result->current_row = 0; + mysql->fields = 0; /* fields is now in result */ + clear_alloc_root(&mysql->field_alloc); + mysql->status = MYSQL_STATUS_USE_RESULT; + mysql->unbuffered_fetch_owner = &result->unbuffered_fetch_cancelled; + DBUG_RETURN(result); /* Data is ready to be fetched */ +} // end of connect_use_result +#endif // !MYSQL_PREPARED_STATEMENTS + +/************************************************************************/ +/* MyColumns: constructs the result blocks containing all columns */ +/* of a MySQL table or view. */ +/* info = TRUE to get catalog column information. */ +/************************************************************************/ +PQRYRES MyColumns(PGLOBAL g, THD *thd, const char *host, const char *db, + const char *user, const char *pwd, + const char *table, const char *colpat, + int port, bool info) + { + int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING, TYPE_INT, + TYPE_STRING, TYPE_SHORT, TYPE_SHORT, TYPE_SHORT, + TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_STRING, + TYPE_STRING}; + XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME, FLD_PREC, + FLD_KEY, FLD_SCALE, FLD_RADIX, FLD_NULL, + FLD_REM, FLD_NO, FLD_DEFAULT, FLD_EXTRA, + FLD_CHARSET}; + //unsigned int length[] = {0, 4, 16, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0}; + unsigned int length[] = {0, 4, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0}; + PCSZ fmt; + char *fld, *colname, *chset, v, buf[128], uns[16], zero[16]; + int i, n, nf = 0, ncol = sizeof(buftyp) / sizeof(int); + int len, type, prec, rc; + bool b; + PQRYRES qrp; + PCOLRES crp; + MYSQLC myc; + + if (!port) + port = mysqld_port; + + if (!info) { + /********************************************************************/ + /* Open the connection with the MySQL server. */ + /********************************************************************/ + if (myc.Open(g, host, db, user, pwd, port)) + return NULL; + + /********************************************************************/ + /* Do an evaluation of the result size. */ + /********************************************************************/ + STRING cmd(g, 64, "SHOW FULL COLUMNS FROM "); + b = cmd.Append('`'); + b |= cmd.Append((PSZ)table); + b |= cmd.Append('`'); + + b |= cmd.Append(" FROM "); + b |= cmd.Append((PSZ)(db ? db : PlgGetUser(g)->DBName)); + + if (colpat) { + b |= cmd.Append(" LIKE "); + b |= cmd.Append((PSZ)colpat); + } // endif colpat + + if (b) { + strcpy(g->Message, "Out of memory"); + return NULL; + } // endif b + + if (trace(1)) + htrc("MyColumns: cmd='%s'\n", cmd.GetStr()); + + if ((n = myc.GetResultSize(g, cmd.GetStr())) < 0) { + myc.Close(); + return NULL; + } // endif n + + /********************************************************************/ + /* Get the size of the name and default columns. */ + /********************************************************************/ + length[0] = myc.GetFieldLength(0); +// length[10] = myc.GetFieldLength(5); + } else { + n = 0; + length[0] = 128; + } // endif info + + /**********************************************************************/ + /* Allocate the structures used to refer to the result set. */ + /**********************************************************************/ + if (!(qrp = PlgAllocResult(g, ncol, n, IDS_COLUMNS + 3, + buftyp, fldtyp, length, false, true))) + return NULL; + + // Some columns must be renamed + for (i = 0, crp = qrp->Colresp; crp; crp = crp->Next) + switch (++i) { + case 2: crp->Nulls = (char*)PlugSubAlloc(g, NULL, n); break; + case 4: crp->Name = "Length"; break; + case 5: crp->Name = "Key"; break; + case 10: crp->Name = "Date_fmt"; break; + case 11: crp->Name = "Default"; break; + case 12: crp->Name = "Extra"; break; + case 13: crp->Name = "Collation"; break; + } // endswitch i + + if (info) + return qrp; + + /**********************************************************************/ + /* Now get the results into blocks. */ + /**********************************************************************/ + for (i = 0; i < n; /*i++*/) { + if ((rc = myc.Fetch(g, -1)) == RC_FX) { + myc.Close(); + return NULL; + } else if (rc == RC_EF) + break; + + // Get column name + colname = myc.GetCharField(0); + crp = qrp->Colresp; // Column_Name + crp->Kdata->SetValue(colname, i); + + // Get type, type name, precision, unsigned and zerofill + chset = myc.GetCharField(2); + fld = myc.GetCharField(1); + prec = 0; + len = 0; +// v = (chset && !strcmp(chset, "binary")) ? 'B' : 0; + v = 0; + *uns = 0; + *zero = 0; + b = false; + + if (!strnicmp(fld, "enum", 4)) { + char *p2, *p1 = fld + 6; // to skip enum(' + + while (true) { + p2 = strchr(p1, '\''); + len = MY_MAX(len, (int)(p2 - p1)); + if (*++p2 != ',') break; + p1 = p2 + 2; + } // endwhile + + v = (len > 255) ? 'V' : 0; + strcpy(buf, "enum"); + b = true; + } else if (!strnicmp(fld, "set", 3)) { + len = (int)strlen(fld) - 2; + v = 'V'; + strcpy(buf, "set"); + b = true; + } else switch ((nf = sscanf(fld, "%[^(](%d,%d", buf, &len, &prec))) { + case 3: + nf = sscanf(fld, "%[^(](%d,%d) %s %s", buf, &len, &prec, uns, zero); + break; + case 2: + nf = sscanf(fld, "%[^(](%d) %s %s", buf, &len, uns, zero) + 1; + break; + case 1: + nf = sscanf(fld, "%s %s %s", buf, uns, zero) + 2; + break; + default: + snprintf(g->Message, sizeof(g->Message), MSG(BAD_FIELD_TYPE), fld); + myc.Close(); + return NULL; + } // endswitch nf + + if ((type = MYSQLtoPLG(buf, &v)) == TYPE_ERROR) { + if (v == 'K') { + // Skip this column + snprintf(g->Message, sizeof(g->Message), "Column %s skipped (unsupported type %s)", + colname, buf); + PushWarning(g, thd); + continue; + } // endif v + + snprintf(g->Message, sizeof(g->Message), "Column %s unsupported type %s", colname, buf); + myc.Close(); + return NULL; + } else if (type == TYPE_STRING) { + if (v == 'X') { + len = GetConvSize(); + snprintf(g->Message, sizeof(g->Message), "Column %s converted to varchar(%d)", + colname, len); + PushWarning(g, thd); + v = 'V'; + } else + len = MY_MIN(len, 4096); + + } // endif type + + qrp->Nblin++; + crp = crp->Next; // Data_Type + crp->Kdata->SetValue(type, i); + + switch (nf) { + case 5: crp->Nulls[i] = 'Z'; break; + case 4: crp->Nulls[i] = 'U'; break; + default: crp->Nulls[i] = v; break; + } // endswitch nf + + if (b) // enum or set + nf = sscanf(fld, "%s ", buf); // get values + + crp = crp->Next; // Type_Name + crp->Kdata->SetValue(buf, i); + + if (type == TYPE_DATE) { + // When creating tables we do need info about date columns + fmt = MyDateFmt(buf); + len = strlen(fmt); + } else + fmt = NULL; + + crp = crp->Next; // Precision + crp->Kdata->SetValue(len, i); + + crp = crp->Next; // key (was Length) + fld = myc.GetCharField(4); + crp->Kdata->SetValue(fld, i); + + crp = crp->Next; // Scale + crp->Kdata->SetValue(prec, i); + + crp = crp->Next; // Radix + crp->Kdata->SetValue(0, i); + + crp = crp->Next; // Nullable + fld = myc.GetCharField(3); + crp->Kdata->SetValue((toupper(*fld) == 'Y') ? 1 : 0, i); + + crp = crp->Next; // Remark + fld = myc.GetCharField(8); + crp->Kdata->SetValue(fld, i); + + crp = crp->Next; // Date format +// crp->Kdata->SetValue((fmt) ? fmt : (char*) "", i); + crp->Kdata->SetValue(fmt, i); + + crp = crp->Next; // New (default) + fld = myc.GetCharField(5); + crp->Kdata->SetValue(fld, i); + + crp = crp->Next; // New (extra) + fld = myc.GetCharField(6); + crp->Kdata->SetValue(fld, i); + + crp = crp->Next; // New (charset) + fld = chset; + crp->Kdata->SetValue(fld, i); + + i++; // Can be skipped + } // endfor i + +#if 0 + if (k > 1) { + // Multicolumn primary key + PVBLK vbp = qrp->Colresp->Next->Next->Next->Next->Kdata; + + for (i = 0; i < n; i++) + if (vbp->GetIntValue(i)) + vbp->SetValue(k, i); + + } // endif k +#endif // 0 + + /**********************************************************************/ + /* Close MySQL connection. */ + /**********************************************************************/ + myc.Close(); + + /**********************************************************************/ + /* Return the result pointer for use by GetData routines. */ + /**********************************************************************/ + return qrp; + } // end of MyColumns + +/************************************************************************/ +/* SrcColumns: constructs the result blocks containing all columns */ +/* resulting from an SQL source definition query execution. */ +/************************************************************************/ +PQRYRES SrcColumns(PGLOBAL g, const char *host, const char *db, + const char *user, const char *pwd, + const char *srcdef, int port) + { + char *query; + int w; + MYSQLC myc; + PQRYRES qrp = NULL; + const char *p; + + if (!port) + port = mysqld_port; + + if (!strnicmp(srcdef, "select ", 7) || strstr(srcdef, "%s")) { + size_t query_sz = strlen(srcdef) + 10; + query = (char *)PlugSubAlloc(g, NULL, query_sz); + + if ((p= strstr(srcdef, "%s"))) + { + /* Replace %s with 1=1 */ + snprintf(query, query_sz, "%.*s1=1%s", + (int) (p - srcdef), srcdef, p + 2); // dummy where clause + } + else + safe_strcpy(query, query_sz, srcdef); + + if (!strnicmp(srcdef, "select ", 7)) + safe_strcat(query, query_sz, " LIMIT 0"); + + } else + query = (char *)srcdef; + + // Open a MySQL connection for this table + if (myc.Open(g, host, db, user, pwd, port)) + return NULL; + + // Send the source command to MySQL + if (myc.ExecSQL(g, query, &w) == RC_OK) + qrp = myc.GetResult(g, true); + + myc.Close(); + return qrp; + } // end of SrcColumns + +/* -------------------------- Class MYSQLC --------------------------- */ + +/***********************************************************************/ +/* Implementation of the MYSQLC class. */ +/***********************************************************************/ +MYSQLC::MYSQLC(void) + { + m_DB = NULL; +#if defined (MYSQL_PREPARED_STATEMENTS) + m_Stmt = NULL; +#endif // MYSQL_PREPARED_STATEMENTS + m_Res = NULL; + m_Rows = -1; + m_Row = NULL; + m_Fields = -1; + N = 0; + m_Use = false; + } // end of MYSQLC constructor + +/***********************************************************************/ +/* Get the number of lines of the result set. */ +/* Currently we send the Select command and return m_Rows */ +/* Perhaps should we use Select count(*) ... (?????) */ +/* No because here we execute only one query instead of two */ +/* (the select count(*) plus the normal query) */ +/***********************************************************************/ +int MYSQLC::GetResultSize(PGLOBAL g, PSZ sql) + { + if (m_Rows < 0) + if (ExecSQL(g, sql) != RC_OK) + return -1; + + return m_Rows; + } // end of GetResultSize + +/***********************************************************************/ +/* Open a MySQL (remote) connection. */ +/***********************************************************************/ +int MYSQLC::Open(PGLOBAL g, const char *host, const char *db, + const char *user, const char *pwd, + int pt, const char *csname) + { + const char *pipe = NULL; + //uint cto = 10, nrt = 20; + my_bool my_true= 1; + + m_DB = mysql_init(NULL); + + if (!m_DB) { + strcpy(g->Message, "mysql_init failed: no memory"); + return RC_FX; + } // endif m_DB + + if (trace(1)) + htrc("MYSQLC Open: m_DB=%.4X size=%d\n", m_DB, (int)sizeof(*m_DB)); + + // Removed to do like FEDERATED does +//mysql_options(m_DB, MYSQL_READ_DEFAULT_GROUP, "client-mariadb"); +//mysql_options(m_DB, MYSQL_OPT_USE_REMOTE_CONNECTION, NULL); +//mysql_options(m_DB, MYSQL_OPT_CONNECT_TIMEOUT, &cto); +//mysql_options(m_DB, MYSQL_OPT_READ_TIMEOUT, &nrt); +//mysql_options(m_DB, MYSQL_OPT_WRITE_TIMEOUT, ...); + +#if defined(_WIN32) + if (!strcmp(host, ".")) { + mysql_options(m_DB, MYSQL_OPT_NAMED_PIPE, NULL); + pipe = mysqld_unix_port; + } // endif host +#else // !_WIN32 + if (!strcmp(host, "localhost")) + pipe = mysqld_unix_port; +#endif // !_WIN32 + +#if 0 + if (pwd && !strcmp(pwd, "*")) { + if (GetPromptAnswer(g, "*Enter password:")) { + m_DB = NULL; + return RC_FX; + } else + pwd = g->Message; + + } // endif pwd +#endif // 0 + +/***********************************************************************/ +/* BUG# 17044 Federated Storage Engine is not UTF8 clean */ +/* Add set names to whatever charset the table is at open of table */ +/* this sets the csname like 'set names utf8'. */ +/***********************************************************************/ + if (csname) + mysql_options(m_DB, MYSQL_SET_CHARSET_NAME, csname); + + // Don't know what this one do but FEDERATED does it + mysql_options(m_DB, MYSQL_OPT_USE_THREAD_SPECIFIC_MEMORY, + (char*)&my_true); + + if (!mysql_real_connect(m_DB, host, user, pwd, db, pt, pipe, + CLIENT_MULTI_RESULTS | CLIENT_REMEMBER_OPTIONS)) { +#if defined(_DEBUG) + snprintf(g->Message, sizeof(g->Message), "mysql_real_connect failed: (%d) %s", + mysql_errno(m_DB), mysql_error(m_DB)); +#else // !_DEBUG + snprintf(g->Message, sizeof(g->Message), "(%d) %s", mysql_errno(m_DB), mysql_error(m_DB)); +#endif // !_DEBUG + mysql_close(m_DB); + m_DB = NULL; + return RC_FX; + } // endif mysql_real_connect + + return RC_OK; + } // end of Open + +/***********************************************************************/ +/* Returns true if the connection is still alive. */ +/***********************************************************************/ +bool MYSQLC::Connected(void) + { +//int rc; + + if (!m_DB) + return FALSE; +//else if ((rc = mysql_ping(m_DB)) == CR_SERVER_GONE_ERROR) +// return FALSE; + else + return TRUE; + + } // end of Connected + +#if 0 // Not used +/***********************************************************************/ +/* Returns the thread ID of the current MySQL connection. */ +/***********************************************************************/ +ulong MYSQLC::GetThreadID(void) + { + return (m_DB) ? mysql_thread_id(m_DB) : 0; + } // end of GetThreadID + +/***********************************************************************/ +/* Returns a string that represents the server version number. */ +/***********************************************************************/ +const char *MYSQLC::ServerInfo(void) + { + return (m_DB) ? mysql_get_server_info(m_DB) : NULL; + } // end of ServerInfo + +/***********************************************************************/ +/* Returns the version number of the server as a number that */ +/* represents the MySQL server version in this format: */ +/* major_version*10000 + minor_version *100 + sub_version */ +/***********************************************************************/ +ulong MYSQLC::ServerVersion(void) + { + return (m_DB) ? mysql_get_server_version(m_DB) : 0; + } // end of ServerVersion +#endif // 0 + +/**************************************************************************/ +/* KillQuery: Send MySQL a Kill Query command. */ +/**************************************************************************/ +int MYSQLC::KillQuery(ulong id) + { + char kill[20]; + + sprintf(kill, "KILL QUERY %u", (unsigned int) id); +//return (m_DB) ? mysql_query(m_DB, kill) : 1; + return (m_DB) ? mysql_real_query(m_DB, kill, strlen(kill)) : 1; + } // end of KillQuery + +#if defined (MYSQL_PREPARED_STATEMENTS) +/***********************************************************************/ +/* Prepare the SQL statement used to insert into a MySQL table. */ +/***********************************************************************/ +int MYSQLC::PrepareSQL(PGLOBAL g, const char *stmt) + { + if (!m_DB) { + strcpy(g->Message, "MySQL not connected"); + return -4; + } else if (m_Stmt) + return -1; // should not append + +#if defined(ALPHA) + if (!(m_Stmt = mysql_prepare(m_DB, stmt, strlen(stmt)))) { + + snprintf(g->Message, sizeof(g->Message), "mysql_prepare failed: %s [%s]", + mysql_error(m_DB), stmt); + return -1; + } // endif m_Stmt + + // Return the parameter count from the statement + return mysql_param_count(m_Stmt); +#else // !ALPHA + if (!(m_Stmt = mysql_stmt_init(m_DB))) { + strcpy(g->Message, "mysql_stmt_init(), out of memory"); + return -2; + } // endif m_Stmt + + if (mysql_stmt_prepare(m_Stmt, stmt, strlen(stmt))) { + snprintf(g->Message, sizeof(g->Message), "mysql_stmt_prepare() failed: (%d) %s", + mysql_stmt_errno(m_Stmt), mysql_stmt_error(m_Stmt)); + return -3; + } // endif prepare + + // Return the parameter count from the statement + return mysql_stmt_param_count(m_Stmt); +#endif // !ALPHA + } // end of PrepareSQL + +/***********************************************************************/ +/* Bind the parameter buffers. */ +/***********************************************************************/ +int MYSQLC::BindParams(PGLOBAL g, MYSQL_BIND *bind) + { + if (!m_DB) { + strcpy(g->Message, "MariaDB not connected"); + return RC_FX; + } else + assert(m_Stmt); + +#if defined(ALPHA) + if (mysql_bind_param(m_Stmt, bind)) { + snprintf(g->Message, sizeof(g->Message), "mysql_bind_param() failed: %s", + mysql_stmt_error(m_Stmt)); +#else // !ALPHA + if (mysql_stmt_bind_param(m_Stmt, bind)) { + snprintf(g->Message, sizeof(g->Message), "mysql_stmt_bind_param() failed: %s", + mysql_stmt_error(m_Stmt)); +#endif // !ALPHA + return RC_FX; + } // endif bind + + return RC_OK; + +/***********************************************************************/ +/* Execute a prepared statement. */ +/***********************************************************************/ +int MYSQLC::ExecStmt(PGLOBAL g) + { + if (!m_DB) { + strcpy(g->Message, "MySQL not connected"); + return RC_FX; + } // endif m_DB + +#if defined(ALPHA) + if (mysql_execute(m_Stmt)) { + snprintf(g->Message, sizeof(g->Message), "mysql_execute() failed: %s", + mysql_stmt_error(m_Stmt)); + return RC_FX; + } // endif execute +#else // !ALPHA + if (mysql_stmt_execute(m_Stmt)) { + snprintf(g->Message, sizeof(g->Message), "mysql_stmt_execute() failed: %s", + mysql_stmt_error(m_Stmt)); + return RC_FX; + } // endif execute +#endif // !ALPHA + + // Check the total number of affected rows + if (mysql_stmt_affected_rows(m_Stmt) != 1) { + snprintf(g->Message, sizeof(g->Message), "Invalid affected rows by MySQL"); + return RC_FX; + } // endif affected_rows + + return RC_OK; + } // end of ExecStmt +#endif // MYSQL_PREPARED_STATEMENTS + +/***********************************************************************/ +/* Exec the Select SQL command and get back the result size in rows. */ +/***********************************************************************/ +int MYSQLC::ExecSQL(PGLOBAL g, const char *query, int *w) + { + int rc = RC_OK; + + if (!m_DB) { + strcpy(g->Message, "MySQL not connected"); + return RC_FX; + } // endif m_DB + + if (w) + *w = 0; + + if (m_Rows >= 0) + return RC_OK; // Already done + +//if (mysql_query(m_DB, query) != 0) { + if (mysql_real_query(m_DB, query, strlen(query))) { + char *msg = (char*)PlugSubAlloc(g, NULL, 512 + strlen(query)); + + sprintf(msg, "(%d) %s [%s]", mysql_errno(m_DB), + mysql_error(m_DB), query); + strncpy(g->Message, msg, sizeof(g->Message) - 1); + g->Message[sizeof(g->Message) - 1] = 0; + rc = RC_FX; +//} else if (mysql_field_count(m_DB) > 0) { + } else if (m_DB->field_count > 0) { + if (m_Use) +#if defined(MYSQL_PREPARED_STATEMENTS) + m_Res = mysql_use_result(m_DB); +#else // !MYSQL_PREPARED_STATEMENTS) + m_Res = connect_use_result(m_DB); +#endif // !MYSQL_PREPARED_STATEMENTS + else + m_Res = mysql_store_result(m_DB); + + if (!m_Res) { + char *msg = (char*)PlugSubAlloc(g, NULL, 512 + strlen(query)); + + sprintf(msg, "mysql_store_result failed: %s", mysql_error(m_DB)); + strncpy(g->Message, msg, sizeof(g->Message) - 1); + g->Message[sizeof(g->Message) - 1] = 0; + rc = RC_FX; + } else { + m_Fields = mysql_num_fields(m_Res); + m_Rows = (!m_Use) ? (int)mysql_num_rows(m_Res) : 0; + + if (trace(1)) + htrc("ExecSQL: m_Res=%.4X size=%d m_Fields=%d m_Rows=%d\n", + m_Res, sizeof(*m_Res), m_Fields, m_Rows); + + } // endif m_Res + + } else { +// m_Rows = (int)mysql_affected_rows(m_DB); + m_Rows = (int)m_DB->affected_rows; + snprintf(g->Message, sizeof(g->Message), "Affected rows: %d\n", m_Rows); + rc = RC_NF; + } // endif field count + + if (w) +// *w = mysql_warning_count(m_DB); + *w = m_DB->warning_count; + + return rc; + } // end of ExecSQL + +/***********************************************************************/ +/* Get table size by executing "select count(*) from table_name". */ +/***********************************************************************/ +int MYSQLC::GetTableSize(PGLOBAL g __attribute__((unused)), PSZ query) + { + if (mysql_real_query(m_DB, query, strlen(query))) { +#if defined(_DEBUG) + char *msg = (char*)PlugSubAlloc(g, NULL, 512 + strlen(query)); + + sprintf(msg, "(%d) %s [%s]", mysql_errno(m_DB), + mysql_error(m_DB), query); + strncpy(g->Message, msg, sizeof(g->Message) - 1); + g->Message[sizeof(g->Message) - 1] = 0; +#endif // _DEBUG + return -2; + } // endif mysql_real_query + + if (!(m_Res = mysql_store_result(m_DB))) + return -3; + + // Get the resulting count value + m_Rows = (int)mysql_num_rows(m_Res); // Should be 1 + + if (m_Rows && (m_Row = mysql_fetch_row(m_Res))) + return atoi(*m_Row); + + return -4; + } // end of GetTableSize + +/***********************************************************************/ +/* Move to a specific row and column */ +/***********************************************************************/ +void MYSQLC::DataSeek(my_ulonglong row) + { + MYSQL_ROWS *tmp = 0; +//DBUG_PRINT("info",("mysql_data_seek(%ld)",(long) row)); + + if (m_Res->data) + for (tmp = m_Res->data->data; row-- && tmp; tmp = tmp->next) ; + + m_Res->current_row = 0; + m_Res->data_cursor = tmp; + } // end of DataSeek + +/***********************************************************************/ +/* Fetch one result line from the query result set. */ +/***********************************************************************/ +int MYSQLC::Fetch(PGLOBAL g, int pos) + { + if (!m_DB) { + strcpy(g->Message, "MySQL not connected"); + return RC_FX; + } // endif m_DB + + if (!m_Res) { + // Result set was not initialized + strcpy(g->Message, MSG(FETCH_NO_RES)); + return RC_FX; + } else + N++; + + if (pos >= 0) +// mysql_data_seek(m_Res, (my_ulonglong)pos); + DataSeek((my_ulonglong)pos); + + m_Row = mysql_fetch_row(m_Res); + return (m_Row) ? RC_OK : RC_EF; + } // end of Fetch + +/***********************************************************************/ +/* Get one field of the current row. */ +/***********************************************************************/ +char *MYSQLC::GetCharField(int i) + { + if (m_Res && m_Row) { +#if defined(_DEBUG) +// MYSQL_FIELD *fld = mysql_fetch_field_direct(m_Res, i); +#endif // _DEBUG + MYSQL_ROW row = m_Row + i; + + return (row) ? (char*)*row : (char*)"<null>"; + } else + return NULL; + + } // end of GetCharField + +/***********************************************************************/ +/* Get the max length of the field. */ +/***********************************************************************/ +int MYSQLC::GetFieldLength(int i) + { + if (m_Res) { +// MYSQL_FIELD *fld = mysql_fetch_field_direct(m_Res, i); +// return fld->max_length; + return (m_Res)->fields[i].max_length; + } else + return 0; + + } // end of GetFieldLength + +/***********************************************************************/ +/* Return next field of the query results. */ +/***********************************************************************/ +MYSQL_FIELD *MYSQLC::GetNextField(void) + { + return (m_Res->current_field >= m_Res->field_count) ? NULL + : &m_Res->fields[m_Res->current_field++]; + } // end of GetNextField + +/***********************************************************************/ +/* Make a CONNECT result structure from the MySQL result. */ +/***********************************************************************/ +PQRYRES MYSQLC::GetResult(PGLOBAL g, bool pdb) + { + PCSZ fmt; + char *name, v= 0; + int n; + bool uns; + PCOLRES *pcrp, crp; + PQRYRES qrp; + MYSQL_FIELD *fld; + MYSQL_ROW row; + + if (!m_Res || !m_Fields) { + snprintf(g->Message, sizeof(g->Message), "%s result", (m_Res) ? "Void" : "No"); + return NULL; + } // endif m_Res + + /*********************************************************************/ + /* Put the result in storage for future retrieval. */ + /*********************************************************************/ + qrp = (PQRYRES)PlugSubAlloc(g, NULL, sizeof(QRYRES)); + pcrp = &qrp->Colresp; + qrp->Continued = FALSE; + qrp->Truncated = FALSE; + qrp->Info = FALSE; + qrp->Suball = TRUE; + qrp->BadLines = 0; + qrp->Maxsize = m_Rows; + qrp->Maxres = m_Rows; + qrp->Nbcol = 0; + qrp->Nblin = 0; + qrp->Cursor = 0; + +//for (fld = mysql_fetch_field(m_Res); fld; +// fld = mysql_fetch_field(m_Res)) { + for (fld = GetNextField(); fld; fld = GetNextField()) { + *pcrp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES)); + crp = *pcrp; + pcrp = &crp->Next; + memset(crp, 0, sizeof(COLRES)); + crp->Ncol = ++qrp->Nbcol; + + name = (char*)PlugSubAlloc(g, NULL, fld->name_length + 1); + strcpy(name, fld->name); + crp->Name = name; + + if ((crp->Type = MYSQLtoPLG(fld->type, &v)) == TYPE_ERROR) { + snprintf(g->Message, sizeof(g->Message), "Type %d not supported for column %s", + fld->type, crp->Name); + return NULL; + } else if (crp->Type == TYPE_DATE && !pdb) + // For direct MySQL connection, display the MySQL date string + crp->Type = TYPE_STRING; + else + crp->Var = v; + + crp->Prec = (crp->Type == TYPE_DOUBLE || crp->Type == TYPE_DECIM) + ? fld->decimals : 0; + CHARSET_INFO *cs= get_charset(fld->charsetnr, MYF(0)); + crp->Clen = GetTypeSize(crp->Type, fld->length); + crp->Length = fld->length / (cs ? cs->mbmaxlen : 1); + uns = (fld->flags & (UNSIGNED_FLAG | ZEROFILL_FLAG)) ? true : false; + + if (!(crp->Kdata = AllocValBlock(g, NULL, crp->Type, m_Rows, + crp->Clen, 0, FALSE, TRUE, uns))) { + snprintf(g->Message, sizeof(g->Message), MSG(INV_RESULT_TYPE), + GetFormatType(crp->Type)); + return NULL; + } else if (crp->Type == TYPE_DATE) { + fmt = MyDateFmt(fld->type); + crp->Kdata->SetFormat(g, fmt, strlen(fmt)); + } // endif's + + if (fld->flags & NOT_NULL_FLAG) + crp->Nulls = NULL; + else { + if (m_Rows) { + crp->Nulls = (char*)PlugSubAlloc(g, NULL, m_Rows); + memset(crp->Nulls, ' ', m_Rows); + } // endif m_Rows + + crp->Kdata->SetNullable(true); + } // endelse fld->flags + + } // endfor fld + + *pcrp = NULL; + assert(qrp->Nbcol == m_Fields); + + /*********************************************************************/ + /* Now fill the allocated result structure. */ + /*********************************************************************/ + for (n = 0; n < m_Rows; n++) { + if (!(m_Row = mysql_fetch_row(m_Res))) { + snprintf(g->Message, sizeof(g->Message), "Missing row %d from result", n + 1); + return NULL; + } // endif m_Row + + for (crp = qrp->Colresp; crp; crp = crp->Next) { + if ((row = m_Row + (crp->Ncol - 1))) { + if (*row) + crp->Kdata->SetValue((PSZ)*row, n); + else { + if (!*row && crp->Nulls) + crp->Nulls[n] = '*'; // Null value + + crp->Kdata->Reset(n); + } // endelse *row + } + + } // endfor crp + + } // endfor n + + qrp->Nblin = n; + return qrp; + } // end of GetResult + +/***********************************************************************/ +/* Free the current result. */ +/***********************************************************************/ +void MYSQLC::FreeResult(void) + { + if (m_Res) { + mysql_free_result(m_Res); + m_Res = NULL; + } // endif m_Res + + // Reset the connection + m_Row = NULL; + m_Rows = -1; + m_Fields = -1; + N = 0; + } // end of FreeResult + +/***********************************************************************/ +/* Place the cursor at the beginning of the result set. */ +/***********************************************************************/ +int MYSQLC::Rewind(PGLOBAL g, PSZ sql) + { + int rc = RC_OK; + + if (m_Res) + DataSeek(0); + else if (sql) + rc = ExecSQL(g, sql); + + return rc; + } // end of Rewind + +/***********************************************************************/ +/* Exec the Select SQL command and return ncol or afrws (TDBMYEXC). */ +/***********************************************************************/ +int MYSQLC::ExecSQLcmd(PGLOBAL g, const char *query, int *w) + { + int rc = RC_OK; + + if (!m_DB) { + strcpy(g->Message, "MySQL not connected"); + return RC_FX; + } else + *w = 0; + + if (!stricmp(query, "Warning") || !stricmp(query, "Note") + || !stricmp(query, "Error")) + return RC_INFO; + else + m_Afrw = 0; + +//if (mysql_query(m_DB, query) != 0) { + if (mysql_real_query(m_DB, query, strlen(query))) { + m_Afrw = (int)mysql_errno(m_DB); + snprintf(g->Message, sizeof(g->Message), "Remote: %s", mysql_error(m_DB)); + rc = RC_FX; +//} else if (!(m_Fields = mysql_field_count(m_DB))) { + } else if (!(m_Fields = (int)m_DB->field_count)) { +// m_Afrw = (int)mysql_affected_rows(m_DB); + m_Afrw = (int)m_DB->affected_rows; + rc = RC_NF; + } // endif's + +//*w = mysql_warning_count(m_DB); + *w = m_DB->warning_count; + return rc; + } // end of ExecSQLcmd + +/***********************************************************************/ +/* Close the connection. */ +/***********************************************************************/ +void MYSQLC::Close(void) + { + FreeResult(); + + if (trace(1)) + htrc("MYSQLC Close: m_DB=%.4X\n", m_DB); + + mysql_close(m_DB); + m_DB = NULL; + } // end of Close + +#if 0 // not used yet +/***********************************************************************/ +/* Discard additional results from a stored procedure. */ +/***********************************************************************/ +void MYSQLC::DiscardResults(void) + { + MYSQL_RES *res; + + while (!mysql_next_result(m_DB)) { + res = mysql_store_result(m_DB); + mysql_free_result(res); + } // endwhile next result + + } // end of DiscardResults +#endif // 0 |