From 37465ea2fed5e60f94c41d5a4076252783b84236 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Tue, 22 Jan 2013 18:14:34 +0400 Subject: [PATCH] Adding an Olivier's changeset: pre_create function and one is now able to create table without giving the column specifications for tables of types: DBF, ODBC, MYSQL, CSV, and WMI (on Windows) modified: sql/handler.h sql/sql_table.cc storage/connect/ha_connect.cc storage/connect/ha_connect.h storage/connect/mycat.cc storage/connect/odbconn.cpp storage/connect/plgcnx.h storage/connect/tabfmt.cpp storage/connect/tabmysql.cpp storage/connect/tabwmi.cpp --- sql/handler.h | 6 +- sql/sql_table.cc | 14 +- storage/connect/ha_connect.cc | 160 ++++++++++++++--- storage/connect/ha_connect.h | 4 +- storage/connect/mycat.cc | 136 ++++++++++++--- storage/connect/odbconn.cpp | 301 +++++++++++++++----------------- storage/connect/plgcnx.h | 2 +- storage/connect/tabfmt.cpp | 298 +++++++++++++++++++++++++++++++- storage/connect/tabmysql.cpp | 16 +- storage/connect/tabwmi.cpp | 315 +++++++++++++++++++++++++++++++++- 10 files changed, 1027 insertions(+), 225 deletions(-) diff --git a/sql/handler.h b/sql/handler.h index 559648da37c..7e33fd23527 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1930,7 +1930,11 @@ public: cached_table_flags= table_flags(); } /* ha_ methods: pubilc wrappers for private virtual API */ - + + /* Added by O. Bertrand */ + virtual bool pre_create(THD *thd, void *crt_info, void *alt_info) + {return true;} + int ha_open(TABLE *table, const char *name, int mode, uint test_if_locked); int ha_index_init(uint idx, bool sorted) { diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 31e4110c4b1..6c75f975d23 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4135,12 +4135,6 @@ bool mysql_create_table_no_lock(THD *thd, /* Check for duplicate fields and check type of table to create */ - if (!alter_info->create_list.elements) - { - my_message(ER_TABLE_MUST_HAVE_COLUMNS, ER(ER_TABLE_MUST_HAVE_COLUMNS), - MYF(0)); - DBUG_RETURN(TRUE); - } if (check_engine(thd, db, table_name, create_info)) DBUG_RETURN(TRUE); @@ -4320,6 +4314,14 @@ bool mysql_create_table_no_lock(THD *thd, } #endif + // Added by O. Bertrand + if (!alter_info->create_list.elements && + file->pre_create(thd, create_info, alter_info)) + { + my_message(ER_TABLE_MUST_HAVE_COLUMNS, ER(ER_TABLE_MUST_HAVE_COLUMNS), + MYF(0)); + DBUG_RETURN(TRUE); + } if (mysql_prepare_create_table(thd, create_info, alter_info, internal_tmp_table, &db_options, file, diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc index 8cda906c4e7..1e9c6f827b2 100644 --- a/storage/connect/ha_connect.cc +++ b/storage/connect/ha_connect.cc @@ -126,6 +126,9 @@ #define my_strlwr(p) my_casedn_str(default_charset_info, (p)); #define my_stricmp(a, b) my_strcasecmp(default_charset_info, (a), (b)) +#if defined (WIN32) +typedef struct _WMIutil *PWMIUT; /* Used to call WMIColumns */ +#endif /****************************************************************************/ /* CONNECT functions called externally. */ /****************************************************************************/ @@ -152,6 +155,14 @@ void XmlCleanupParserLib(void); /* Functions called externally by pre_parser. */ /****************************************************************************/ PQRYRES DBFColumns(PGLOBAL g, char *fn, BOOL info); +PQRYRES MyODBCCols(PGLOBAL g, char *tab, char *dsn); +PQRYRES CSVColumns(PGLOBAL g, char *fn, char sep, char q, int hdr, int mxr); +PQRYRES MyColumns(PGLOBAL g, char *host, char *db, char *user, char *pwd, + char *table, char *colpat, int port, bool key); +#if defined(WIN32) +PQRYRES WMIColumns(PGLOBAL g, char *nsp, char *classname, PWMIUT wp= NULL); +#endif // WIN32 +char GetTypeID(char *type); enum enum_field_types PLGtoMYSQL(int type, bool gdf); bool check_string_char_length(LEX_STRING *str, const char *err_msg, uint max_char_length, CHARSET_INFO *cs, @@ -388,17 +399,19 @@ static int connect_init_func(void *p) static int connect_done_func(void *p) { int error= 0; + PCONNECT pc, pn; DBUG_ENTER("connect_done_func"); if (connect_open_tables.records) error= 1; - for (PCONNECT p= user_connect::to_users; p; p= p->next) { - if (p->g) - PlugCleanup(p->g, true); + for (pc= user_connect::to_users; pc; pc= pn) { + if (pc->g) + PlugCleanup(pc->g, true); - delete p; - } // endfor p + pn= pc->next; + delete pc; + } // endfor pc my_hash_free(&connect_open_tables); mysql_mutex_destroy(&connect_mutex); @@ -613,11 +626,11 @@ PGLOBAL ha_connect::GetPlug(THD *thd) /****************************************************************************/ /* Return the value of an option specified in the option list. */ /****************************************************************************/ -char *ha_connect::GetListOption(char *opname, const char *oplist) +char *ha_connect::GetListOption(char *opname, const char *oplist, char *def) { char key[16], val[256]; char *pk, *pv, *pn; - char *opval= NULL; + char *opval= def; int n; for (pk= (char*)oplist; ; pk= ++pn) { @@ -3244,26 +3257,99 @@ bool ha_connect::add_fields(THD *thd, void *alt_info, @note Not really implemented yet. */ -bool ha_connect::pre_create(THD *thd, void *alter_info) +bool ha_connect::pre_create(THD *thd, void *crt_info, void *alt_info) { - char *ttp= "DOS" , *fn= NULL; + char ttp= '?', spc= ',', qch= 0, *typn= "DOS"; + char *fn, *dsn, *tab, *db, *host, *user, *pwd, *prt, *sep; +#if defined(WIN32) + char *nsp= NULL, *cls= NULL; +#endif // WIN32 + int port= MYSQL_PORT, hdr= 0, mxr= 0; + bool ok= false; LEX *lex= thd->lex; + HA_CREATE_INFO *create_info= (HA_CREATE_INFO *)crt_info; engine_option_value *pov; PQRYRES qrp; PCOLRES crp; PGLOBAL g= GetPlug(thd); - if (!g) + fn= dsn= tab= db= host= user= pwd= prt= sep= NULL; + + if (g) { + // Set default values + tab= (char*)create_info->alias; + db= thd->db; + } else return true; - for (pov= lex->create_info.option_list; pov; pov= pov->next) - if (!stricmp(pov->name.str, "table_type")) - ttp= pov->value.str; - else if (!stricmp(pov->name.str, "file_name")) + // Get the useful create options + for (pov= create_info->option_list; pov; pov= pov->next) + if (!stricmp(pov->name.str, "table_type")) { + typn= pov->value.str; + ttp= GetTypeID(typn); + } else if (!stricmp(pov->name.str, "file_name")) { fn= pov->value.str; + } else if (!stricmp(pov->name.str, "tabname")) { + tab= pov->value.str; + } else if (!stricmp(pov->name.str, "db_name")) { + db= pov->value.str; + } else if (!stricmp(pov->name.str, "sep_char")) { + sep= pov->value.str; + spc= (!strcmp(sep, "\\t")) ? '\t' : *sep; + } else if (!stricmp(pov->name.str, "qchar")) { + qch= *pov->value.str; + } else if (!stricmp(pov->name.str, "quoted")) { + if (!qch) + qch= '"'; - if (!stricmp(ttp, "DBF") && fn) { - char *length, *decimals, *nm; + } else if (!stricmp(pov->name.str, "header")) { + hdr= atoi(pov->value.str); + } else if (!stricmp(pov->name.str, "option_list")) { + host= GetListOption("host", pov->value.str, "localhost"); + user= GetListOption("user", pov->value.str, "root"); + pwd= GetListOption("password", pov->value.str); + prt= GetListOption("port", pov->value.str); + port= (prt) ? atoi(prt) : MYSQL_PORT; +#if defined(WIN32) + nsp= GetListOption("namespace", pov->value.str); + cls= GetListOption("class", pov->value.str); +#endif + mxr= atoi(GetListOption("maxerr", pov->value.str, "0")); + } // endelse option_list + + switch (ttp) { + case 'O': // ODBC + if (!(dsn= create_info->connect_string.str)) + sprintf(g->Message, "Missing %s connection string", typn); + else + ok= true; + + break; + case 'A': // DBF + case 'C': // CSV + if (!fn) + sprintf(g->Message, "Missing %s file name", typn); + else + ok= true; + + break; + case 'Y': // MYSQL + if (!user) + user= "root"; // Avoid crash + + ok= true; + break; +#if defined(WIN32) + case 'W': // WMI + ok= true; + break; +#endif // WIN32 + default: + sprintf(g->Message, "Cannot get column info for table type %s", typn); + } // endif ttp + + if (ok) { + char *length, *decimals, *nm, *rem; int i, len, dec; bool b; LEX_STRING *comment, *name; @@ -3274,13 +3360,32 @@ bool ha_connect::pre_create(THD *thd, void *alter_info) if (cat) cat->SetDataPath(g, thd->db); else - return true; + return true; // Should never happen - if (!(qrp= DBFColumns(g, fn, false))) - return true; + switch (ttp) { + case 'A': + qrp= DBFColumns(g, fn, false); + break; + case 'O': + qrp= MyODBCCols(g, tab, dsn); + break; + case 'Y': + qrp= MyColumns(g, host, db, user, pwd, tab, NULL, port, false); + break; + case 'C': + qrp= CSVColumns(g, fn, spc, qch, hdr, mxr); + break; +#if defined(WIN32) + case 'W': + qrp= WMIColumns(g, nsp, cls); + break; +#endif // WIN32 + } // endswitch ttp - comment= (LEX_STRING *)PlugSubAlloc(g, NULL, sizeof(LEX_STRING)); - memset(comment, 0, sizeof(LEX_STRING)); + if (!qrp) { + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, g->Message); + return true; + } // endif qrp for (i= 0; i < qrp->Nblin; i++) { crp = qrp->Colresp; // Column Name @@ -3289,11 +3394,11 @@ bool ha_connect::pre_create(THD *thd, void *alter_info) crp = crp->Next; // Data Type type= PLGtoMYSQL(crp->Kdata->GetIntValue(i), true); crp = crp->Next; // Type Name - crp = crp->Next; // Precision - crp = crp->Next; // Length + crp = crp->Next; // Precision (length) len= crp->Kdata->GetIntValue(i); length= (char*)PlugSubAlloc(g, NULL, 8); sprintf(length, "%d", len); + crp = crp->Next; // Length crp = crp->Next; // Scale (precision) if ((dec= crp->Kdata->GetIntValue(i))) { @@ -3302,18 +3407,23 @@ bool ha_connect::pre_create(THD *thd, void *alter_info) } else decimals= NULL; - comment= thd->make_lex_string(NULL, "", 0, true); + if ((crp= crp->Next) && // Remark (comment) + (rem= crp->Kdata->GetCharValue(i))) + comment= thd->make_lex_string(NULL, rem, strlen(rem), true); + else + comment= thd->make_lex_string(NULL, "", 0, true); // Now add the field // b= add_field_to_list(thd, &name, type, length, decimals, // 0, NULL, NULL, comment, NULL, NULL, NULL, 0, NULL, NULL); - b= add_fields(thd, alter_info, name, type, length, decimals, + b= add_fields(thd, alt_info, name, type, length, decimals, 0, comment, NULL, NULL, NULL); } // endfor i return false; } // endif ttp + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, g->Message); return true; } // end of pre_create diff --git a/storage/connect/ha_connect.h b/storage/connect/ha_connect.h index 5422beda96c..3aaa8aeef4b 100644 --- a/storage/connect/ha_connect.h +++ b/storage/connect/ha_connect.h @@ -335,7 +335,7 @@ char *GetValStr(OPVAL vop, bool neg); ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key); int delete_table(const char *from); - bool pre_create(THD *thd, void *alter_info); + bool pre_create(THD *thd, void *crt_info, void *alt_info); int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info); ///< required bool check_if_incompatible_data(HA_CREATE_INFO *info, @@ -346,7 +346,7 @@ char *GetValStr(OPVAL vop, bool neg); int optimize(THD* thd, HA_CHECK_OPT* check_opt); protected: - char *GetListOption(char *opname, const char *oplist); + char *GetListOption(char *opname, const char *oplist, char *def= NULL); bool add_fields(THD *thd, void *alter_info, LEX_STRING *field_name, enum_field_types type, diff --git a/storage/connect/mycat.cc b/storage/connect/mycat.cc index a041ffd1a90..073c16f18ec 100644 --- a/storage/connect/mycat.cc +++ b/storage/connect/mycat.cc @@ -16,7 +16,9 @@ /*************** Mycat CC Program Source Code File (.CC) ***************/ /* PROGRAM NAME: MYCAT */ /* ------------- */ -/* Version 1.2 */ +/* Version 1.3 */ +/* */ +/* Author: Olivier Bertrand 2012 - 2013 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ @@ -84,16 +86,113 @@ #include "ha_connect.h" #include "mycat.h" -/***********************************************************************/ -/* General DB routines. */ -/***********************************************************************/ +/**************************************************************************/ +/* Extern static variables. */ +/**************************************************************************/ +#if defined(WIN32) +extern "C" HINSTANCE s_hModule; // Saved module handle +#endif // !WIN32 + +extern int xtrace; + +/**************************************************************************/ +/* General DB routines. */ +/**************************************************************************/ //bool PlugCheckPattern(PGLOBAL, LPCSTR, LPCSTR); #if !defined(WIN32) extern "C" int GetRcString(int id, char *buf, int bufsize); #endif // !WIN32 //void ptrc(char const *fmt, ...); -extern int xtrace; +/**************************************************************************/ +/* Allocate the result structure that will contain result data. */ +/**************************************************************************/ +PQRYRES PlgAllocResult(PGLOBAL g, int ncol, int maxres, int ids, + int *dbtype, int *buftyp, unsigned int *length, + bool blank = false, bool nonull = false) + { + char cname[NAM_LEN+1]; + int i; + PCOLRES *pcrp, crp; + PQRYRES qrp; + + /************************************************************************/ + /* Allocate the structure used to contain the result set. */ + /************************************************************************/ + qrp = (PQRYRES)PlugSubAlloc(g, NULL, sizeof(QRYRES)); + pcrp = &qrp->Colresp; + qrp->Continued = false; + qrp->Truncated = false; + qrp->Info = false; + qrp->Suball = true; + qrp->Maxres = maxres; + qrp->Maxsize = 0; + qrp->Nblin = 0; + qrp->Nbcol = 0; // will be ncol + qrp->Cursor = 0; + qrp->BadLines = 0; + + for (i = 0; i < ncol; i++) { + *pcrp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES)); + crp = *pcrp; + pcrp = &crp->Next; + crp->Colp = NULL; + crp->Ncol = ++qrp->Nbcol; + crp->Type = buftyp[i]; + crp->Length = length[i]; + crp->Clen = GetTypeSize(crp->Type, length[i]); + crp->Prec = 0; + crp->DBtype = dbtype[i]; + + if (ids > 0) { +#if defined(XMSG) + // Get header from message file + strncpy(cname, PlugReadMessage(g, ids + crp->Ncol, NULL), NAM_LEN); + cname[NAM_LEN] = 0; // for truncated long names +#elif defined(WIN32) + // Get header from ressource file + LoadString(s_hModule, ids + crp->Ncol, cname, sizeof(cname)); +#else // !WIN32 + GetRcString(ids + crp->Ncol, cname, sizeof(cname)); +#endif // !WIN32 + crp->Name = (PSZ)PlugSubAlloc(g, NULL, strlen(cname) + 1); + strcpy(crp->Name, cname); + } else + crp->Name = NULL; // Will be set by caller + + // Allocate the Value Block that will contain data + if (crp->Length || nonull) + crp->Kdata = AllocValBlock(g, NULL, crp->Type, maxres, + crp->Length, 0, true, blank); + else + crp->Kdata = NULL; + + if (g->Trace) + htrc("Column(%d) %s type=%d len=%d value=%p\n", + crp->Ncol, crp->Name, crp->Type, crp->Length, crp->Kdata); + + } // endfor i + + *pcrp = NULL; + + return qrp; + } // end of PlgAllocResult + +/***********************************************************************/ +/* Get a unique char identifier for types. The letter used are: */ +/* ABCDEF..I.KLM.O..R.T.VWXY.. */ +/***********************************************************************/ +char GetTypeID(char *type) + { + return (!type) ? 'D' // DOS (default) + : (!stricmp(type, "FMT")) ? 'T' // CSV + : (!stricmp(type, "DIR")) ? 'R' // diR + : (!stricmp(type, "DBF")) ? 'A' // dbAse + : (!stricmp(type, "SYS")) ? 'I' // INI + : (!stricmp(type, "TBL")) ? 'L' // tbL + : (!stricmp(type, "MYSQL")) ? 'Y' // mYsql + : (!stricmp(type, "OEM")) ? 'E' : toupper(*type); + } // end of GetTypeID /* ------------------------- Class CATALOG --------------------------- */ @@ -252,15 +351,8 @@ int MYCAT::GetColCatInfo(PGLOBAL g, PTABDEF defp) PCOLDEF cdp, lcdp= NULL, tocols= NULL; PCOLINFO pcf= (PCOLINFO)PlugSubAlloc(g, NULL, sizeof(COLINFO)); - /*********************************************************************/ - /* Get a unique char identifier for types. The letter used are: */ - /* ABCDEF..IJKLM.OPQRSTUV.XYZ */ - /*********************************************************************/ - char tc= (!stricmp(type, "FMT")) ? 'T' // fmT - : (!stricmp(type, "DBF")) ? 'A' // dbAse - : (!stricmp(type, "TBL")) ? 'L' // tbL - : (!stricmp(type, "OEM")) ? 'E' // oEm - : (!stricmp(type, "DIR")) ? 'R' : toupper(*type); + // Get a unique char identifier for type + char tc= GetTypeID(type); // Take care of the column definitions i= poff= nof= nlg= 0; @@ -389,8 +481,8 @@ int MYCAT::GetColCatInfo(PGLOBAL g, PTABDEF defp) case 'A': recln= nlg; break; - case 'C': case 'T': + case 'C': // The number of separators (assuming an extra one can exist) // recln= poff * ((qotd) ? 3 : 1); to be investigated recln= nlg + poff * 3; // To be safe @@ -467,6 +559,7 @@ PRELDEF MYCAT::GetTableDesc(PGLOBAL g, LPCSTR name, /***********************************************************************/ PRELDEF MYCAT::MakeTableDesc(PGLOBAL g, LPCSTR name, LPCSTR am) { + char tc; PRELDEF tdp= NULL; if (xtrace) @@ -476,17 +569,14 @@ PRELDEF MYCAT::MakeTableDesc(PGLOBAL g, LPCSTR name, LPCSTR am) /* Get a unique char identifier for types. The letter used are: */ /* ABCDEF..IJKLM.OPQRSTUVWXYZ and Allocate table definition class */ /*********************************************************************/ - switch ((!am) ? 'D' : (!stricmp(am, "FMT")) ? 'C' // CSV - : (!stricmp(am, "DIR")) ? 'R' - : (!stricmp(am, "SYS")) ? 'I' // INI -// : (!stricmp(am, "DUMMY")) ? 'U' - : (!stricmp(am, "TBL")) ? 'L' -// : (!stricmp(am, "PLG")) ? 'S' // Compatibility - : (!stricmp(am, "MYSQL")) ? 'Y' // mYsql - : (!stricmp(am, "OEM")) ? 'E' : toupper(*am)) { + tc= GetTypeID((char*)am); + + switch (tc) { case 'F': case 'B': + case 'A': case 'D': tdp= new(g) DOSDEF; break; + case 'T': case 'C': tdp= new(g) CSVDEF; break; case 'I': tdp= new(g) INIDEF; break; case 'R': tdp= new(g) DIRDEF; break; diff --git a/storage/connect/odbconn.cpp b/storage/connect/odbconn.cpp index b45db78ab5a..8fef995473a 100644 --- a/storage/connect/odbconn.cpp +++ b/storage/connect/odbconn.cpp @@ -1,13 +1,13 @@ /************ Odbconn C++ Functions Source Code File (.CPP) ************/ -/* Name: ODBCONN.CPP Version 1.5 */ +/* Name: ODBCONN.CPP Version 1.6 */ /* */ -/* (C) Copyright to the author Olivier BERTRAND 1998-2012 */ +/* (C) Copyright to the author Olivier BERTRAND 1998-2013 */ /* */ /* This file contains the ODBC connection classes functions. */ /***********************************************************************/ /***********************************************************************/ -/* Include relevant MariaDB header file. */ +/* Include relevant MariaDB header file. */ /***********************************************************************/ #include "my_global.h" #if defined(WIN32) @@ -73,74 +73,7 @@ extern "C" int GetRcString(int id, char *buf, int bufsize); /**************************************************************************/ PQRYRES PlgAllocResult(PGLOBAL g, int ncol, int maxres, int ids, int *dbtype, int *buftyp, unsigned int *length, - bool blank = false, bool nonull = false) - { - char cname[NAM_LEN+1]; - int i; - PCOLRES *pcrp, crp; - PQRYRES qrp; - - /************************************************************************/ - /* Allocate the structure used to contain the result set. */ - /************************************************************************/ - qrp = (PQRYRES)PlugSubAlloc(g, NULL, sizeof(QRYRES)); - pcrp = &qrp->Colresp; - qrp->Continued = false; - qrp->Truncated = false; - qrp->Info = false; - qrp->Suball = true; - qrp->Maxres = maxres; - qrp->Maxsize = 0; - qrp->Nblin = 0; - qrp->Nbcol = 0; // will be ncol - qrp->Cursor = 0; - qrp->BadLines = 0; - - for (i = 0; i < ncol; i++) { - *pcrp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES)); - crp = *pcrp; - pcrp = &crp->Next; - crp->Colp = NULL; - crp->Ncol = ++qrp->Nbcol; - crp->Type = buftyp[i]; - crp->Length = length[i]; - crp->Clen = GetTypeSize(crp->Type, length[i]); - crp->Prec = 0; - crp->DBtype = dbtype[i]; - - if (ids > 0) { -#if defined(XMSG) - // Get header from message file - strncpy(cname, PlugReadMessage(g, ids + crp->Ncol, NULL), NAM_LEN); - cname[NAM_LEN] = 0; // for truncated long names -#elif defined(WIN32) - // Get header from ressource file - LoadString(s_hModule, ids + crp->Ncol, cname, sizeof(cname)); -#else // !WIN32 - GetRcString(ids + crp->Ncol, cname, sizeof(cname)); -#endif // !WIN32 - crp->Name = (PSZ)PlugSubAlloc(g, NULL, strlen(cname) + 1); - strcpy(crp->Name, cname); - } else - crp->Name = NULL; // Will be set by caller - - // Allocate the Value Block that will contain data - if (crp->Length || nonull) - crp->Kdata = AllocValBlock(g, NULL, crp->Type, maxres, - crp->Length, 0, true, blank); - else - crp->Kdata = NULL; - - if (g->Trace) - htrc("Column(%d) %s type=%d len=%d value=%p\n", - crp->Ncol, crp->Name, crp->Type, crp->Length, crp->Kdata); - - } // endfor i - - *pcrp = NULL; - - return qrp; - } // end of PlgAllocResult + bool blank = true, bool nonull = true); /***********************************************************************/ /* Allocate the structure used to refer to the result set. */ @@ -192,6 +125,141 @@ void ResetNullValues(CATPARM *cap) } // end of ResetNullValues +/***********************************************************************/ +/* ODBCColumns: constructs the result blocks containing all columns */ +/* of an ODBC table that will be retrieved by GetData commands. */ +/* Note: The first two columns (Qualifier, Owner) are ignored. */ +/***********************************************************************/ +PQRYRES ODBCColumns(PGLOBAL g, ODBConn *op, char *dsn, char *table, + char *colpat) + { + static int dbtype[] = {DB_CHAR, DB_CHAR, + DB_CHAR, DB_SHORT, DB_CHAR, + DB_INT, DB_INT, DB_SHORT, + DB_SHORT, DB_SHORT, DB_CHAR}; + static int buftyp[] = {TYPE_STRING, TYPE_STRING, + TYPE_STRING, TYPE_SHORT, TYPE_STRING, + TYPE_INT, TYPE_INT, TYPE_SHORT, + TYPE_SHORT, TYPE_SHORT, TYPE_STRING}; + static unsigned int length[] = {0, 0, 0, 6, 20, 10, 10, 6, 6, 6, 128}; + int n, ncol = 11; + int maxres; + PQRYRES qrp; + CATPARM *cap; + ODBConn *ocp = op; + + if (!op) { + /**********************************************************************/ + /* Open the connection with the ODBC data source. */ + /**********************************************************************/ + ocp = new(g) ODBConn(g, NULL); + + if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly + return NULL; + + } // endif op + + /************************************************************************/ + /* Do an evaluation of the result size. */ + /************************************************************************/ + n = ocp->GetMaxValue(SQL_MAX_COLUMNS_IN_TABLE); + maxres = (n) ? (int)n : 250; + n = ocp->GetMaxValue(SQL_MAX_USER_NAME_LEN); + length[0] = (n) ? (n + 1) : 128; + n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN); + length[1] = (n) ? (n + 1) : 128; + n = ocp->GetMaxValue(SQL_MAX_COLUMN_NAME_LEN); + length[2] = (n) ? (n + 1) : 128; + +#ifdef DEBTRACE + htrc("ODBCColumns: max=%d len=%d,%d,%d\n", + maxres, length[0], length[1], length[2]); +#endif + + /************************************************************************/ + /* Allocate the structures used to refer to the result set. */ + /************************************************************************/ + qrp = PlgAllocResult(g, ncol, maxres, IDS_COLUMNS + 1, + dbtype, buftyp, length); + +#ifdef DEBTRACE + htrc("Getting col results ncol=%d\n", qrp->Nbcol); +#endif + + cap = AllocCatInfo(g, CAT_COL, table, qrp); + cap->Pat = (PUCHAR)colpat; + + /************************************************************************/ + /* Now get the results into blocks. */ + /************************************************************************/ + if ((n = ocp->GetCatInfo(cap)) >= 0) { + qrp->Nblin = n; + ResetNullValues(cap); + +#ifdef DEBTRACE + htrc("Columns: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin); +#endif + } else + qrp = NULL; + + /************************************************************************/ + /* Close any local connection. */ + /************************************************************************/ + if (!op) + ocp->Close(); + + /************************************************************************/ + /* Return the result pointer for use by GetData routines. */ + /************************************************************************/ + return qrp; + } // end of ODBCColumns + +/**************************************************************************/ +/* MyODBCCols: returns column info as required by ha_connect::pre_create. */ +/**************************************************************************/ +PQRYRES MyODBCCols(PGLOBAL g, char *tab, char *dsn) + { + int n; + PCOLRES crp; + PQRYRES qrp; + ODBConn *ocp = new(g) ODBConn(g, NULL); + + /**********************************************************************/ + /* Open the connection with the ODBC data source. */ + /**********************************************************************/ + if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly + return NULL; + + /**********************************************************************/ + /* Get the information about the ODBC table columns. */ + /**********************************************************************/ + if ((qrp = ODBCColumns(g, ocp, dsn, tab, NULL))) + dsn = ocp->GetConnect(); // Complete connect string + else + return NULL; + + /************************************************************************/ + /* Close the local connection. */ + /************************************************************************/ + ocp->Close(); + + /************************************************************************/ + /* Keep only the info used by ha_connect::pre_create. */ + /************************************************************************/ + qrp->Colresp = qrp->Colresp->Next->Next; // Skip Owner and Table names + crp = qrp->Colresp->Next; // DB type + + // Types must be PLG types, not SQL types + for (int i = 0; i < qrp->Nblin; i++) + crp->Kdata->SetValue(TranslateSQLType(crp->Kdata->GetIntValue(i),0,n),i); + + crp = crp->Next->Next->Next->Next; // Should be Radix + crp->Next = crp->Next->Next->Next; // Should be Remark + qrp->Nbcol = 7; // Was 11, skipped 4 + return qrp; + } // end of MyODBCCols + +#if 0 // Currently not used by CONNECT /***********************************************************************/ /* ODBCTables: constructs the result blocks containing all tables in */ /* an ODBC database that will be retrieved by GetData commands. */ @@ -273,95 +341,6 @@ PQRYRES ODBCTables(PGLOBAL g, ODBConn *op, char *dsn, char *tabpat, return qrp; } // end of ODBCTables -/***********************************************************************/ -/* ODBCColumns: constructs the result blocks containing all columns */ -/* of an ODBC table that will be retrieved by GetData commands. */ -/* Note: The first two columns (Qualifier, Owner) are ignored. */ -/***********************************************************************/ -PQRYRES ODBCColumns(PGLOBAL g, ODBConn *op, char *dsn, char *table, - char *colpat) - { - static int dbtype[] = {DB_CHAR, DB_CHAR, - DB_CHAR, DB_SHORT, DB_CHAR, - DB_INT, DB_INT, DB_SHORT, - DB_SHORT, DB_SHORT, DB_CHAR}; - static int buftyp[] = {TYPE_STRING, TYPE_STRING, - TYPE_STRING, TYPE_SHORT, TYPE_STRING, - TYPE_INT, TYPE_INT, TYPE_SHORT, - TYPE_SHORT, TYPE_SHORT, TYPE_STRING}; - static unsigned int length[] = {0, 0, 0, 6, 20, 10, 10, 6, 6, 6, 128}; - int n, ncol = 11; - int maxres; - PQRYRES qrp; - CATPARM *cap; - ODBConn *ocp = op; - - if (!op) { - /**********************************************************************/ - /* Open the connection with the ODBC data source. */ - /**********************************************************************/ - ocp = new(g) ODBConn(g, NULL); - - if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly - return NULL; - - } // endif op - - /************************************************************************/ - /* Do an evaluation of the result size. */ - /************************************************************************/ - n = ocp->GetMaxValue(SQL_MAX_COLUMNS_IN_TABLE); - maxres = (n) ? (int)n : 250; - n = ocp->GetMaxValue(SQL_MAX_USER_NAME_LEN); - length[0] = (n) ? (n + 1) : 128; - n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN); - length[1] = (n) ? (n + 1) : 128; - n = ocp->GetMaxValue(SQL_MAX_COLUMN_NAME_LEN); - length[2] = (n) ? (n + 1) : 128; - -#ifdef DEBTRACE - htrc("ODBCColumns: max=%d len=%d,%d,%d\n", - maxres, length[0], length[1], length[2]); -#endif - - /************************************************************************/ - /* Allocate the structures used to refer to the result set. */ - /************************************************************************/ - qrp = PlgAllocResult(g, ncol, maxres, IDS_COLUMNS + 1, - dbtype, buftyp, length); - -#ifdef DEBTRACE - htrc("Getting col results ncol=%d\n", qrp->Nbcol); -#endif - - cap = AllocCatInfo(g, CAT_COL, table, qrp); - cap->Pat = (PUCHAR)colpat; - - /************************************************************************/ - /* Now get the results into blocks. */ - /************************************************************************/ - if ((n = ocp->GetCatInfo(cap)) >= 0) { - qrp->Nblin = n; - ResetNullValues(cap); - -#ifdef DEBTRACE - htrc("Columns: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin); -#endif - } else - qrp = NULL; - - /************************************************************************/ - /* Close any local connection. */ - /************************************************************************/ - if (!op) - ocp->Close(); - - /************************************************************************/ - /* Return the result pointer for use by GetData routines. */ - /************************************************************************/ - return qrp; - } // end of ODBCColumns - /**************************************************************************/ /* PrimaryKeys: constructs the result blocks containing all the */ /* ODBC catalog information concerning primary keys. */ @@ -606,7 +585,7 @@ PQRYRES GetColumnInfo(PGLOBAL g, char*& dsn, return qrpc; } // end of GetColumnInfo - +#endif // 0 /***********************************************************************/ /* Implementation of DBX class. */ diff --git a/storage/connect/plgcnx.h b/storage/connect/plgcnx.h index 840dfdceaf6..d37ea3594b8 100644 --- a/storage/connect/plgcnx.h +++ b/storage/connect/plgcnx.h @@ -56,7 +56,7 @@ enum XDBTYPE {DB_ERROR = 0, /* Unknown or wrong type */ DB_STRING = 1, /* Null terminated string */ DB_CHAR = 2, /* Character array */ DB_SHORT = 3, /* Used by some catalog functions */ - DB_INT = 4, /* Long integer array */ + DB_INT = 4, /* Long integer array */ DB_DOUBLE = 5, /* Double float array */ DB_DATE = 6}; /* Datetime value array */ diff --git a/storage/connect/tabfmt.cpp b/storage/connect/tabfmt.cpp index 21f4e456711..d15abe81455 100644 --- a/storage/connect/tabfmt.cpp +++ b/storage/connect/tabfmt.cpp @@ -1,11 +1,11 @@ /************* TabFmt C++ Program Source Code File (.CPP) **************/ /* PROGRAM NAME: TABFMT */ /* ------------- */ -/* Version 3.6 */ +/* Version 3.7 */ /* */ /* COPYRIGHT: */ /* ---------- */ -/* (C) Copyright to the author Olivier BERTRAND 2001 - 2012 */ +/* (C) Copyright to the author Olivier BERTRAND 2001 - 2013 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ @@ -74,6 +74,300 @@ extern "C" int trace; PQRYRES PlgAllocResult(PGLOBAL, int, int, int, int *, int *, unsigned int *, bool blank = true, bool nonull = false); +/***********************************************************************/ +/* CSVColumns: constructs the result blocks containing the description */ +/* of all the columns of a CSV file that will be retrieved by #GetData.*/ +/* Note: the algorithm to set the type is based on the internal values */ +/* of types (TYPE_STRING < TYPE_FLOAT < TYPE_INT) (1 < 2 < 7). */ +/* If these values are changed, this will have to be revisited. */ +/***********************************************************************/ +PQRYRES CSVColumns(PGLOBAL g, char *fn, char sep, char q, int hdr, int mxr) + { + static int dbtype[] = {DB_CHAR, DB_SHORT, DB_CHAR, + DB_INT, DB_INT, DB_SHORT}; + static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING, + TYPE_INT, TYPE_INT, TYPE_SHORT}; + static unsigned int length[] = {6, 6, 8, 10, 10, 6}; + char *p, *colname[MAXCOL], dechar, filename[_MAX_PATH], buf[4096]; + int i, imax, hmax, n, nerr, phase, blank, digit, dec, type; + int ncol = sizeof(dbtype) / sizeof(int); + int num_read = 0, num_max = 10000000; // Statistics + int len[MAXCOL], typ[MAXCOL], prc[MAXCOL]; + FILE *infile; + PQRYRES qrp; + PCOLRES crp; + +// num_max = atoi(p+1); // Max num of record to test +#if defined(WIN32) + if (strnicmp(setlocale(LC_NUMERIC, NULL), "French", 6)) + dechar = '.'; + else + dechar = ','; +#else // !WIN32 + dechar = '.'; +#endif // !WIN32 + + if (trace) + htrc("File %s sep=%c q=%c hdr=%d mxr=%d\n", + SVP(fn), sep, q, hdr, mxr); + + if (!fn) { + strcpy(g->Message, MSG(MISSING_FNAME)); + return NULL; + } // endif fn + + imax = hmax = nerr = 0; + mxr = max(0, mxr); + + for (i = 0; i < MAXCOL; i++) { + colname[i] = NULL; + len[i] = 0; + typ[i] = TYPE_UNKNOWN; + prc[i] = 0; + } // endfor i + + /*********************************************************************/ + /* Open the input file. */ + /*********************************************************************/ + PlugSetPath(filename, fn, PlgGetDataPath(g)); + + if (!(infile = fopen(filename, "r"))) { + sprintf(g->Message, MSG(CANNOT_OPEN), filename); + return NULL; + } // endif infile + + if (hdr) { + /*******************************************************************/ + /* Make the column names from the first line. */ + /*******************************************************************/ + phase = 0; + + if (fgets(buf, sizeof(buf), infile)) { + n = strlen(buf) + 1; + buf[n - 2] = '\0'; + p = (char*)PlugSubAlloc(g, NULL, n); + memcpy(p, buf, n); + + //skip leading blanks + for (; *p == ' '; p++) ; + + if (q && *p == q) { + // Header is quoted + p++; + phase = 1; + } // endif q + + colname[0] = p; + } else { + sprintf(g->Message, MSG(FILE_IS_EMPTY), fn); + goto err; + } // endif's + + for (i = 1; *p; p++) + if (phase == 1 && *p == q) { + *p = '\0'; + phase = 0; + } else if (*p == sep && !phase) { + *p = '\0'; + + //skip leading blanks + for (; *(p+1) == ' '; p++) ; + + if (q && *(p+1) == q) { + // Header is quoted + p++; + phase = 1; + } // endif q + + colname[i++] = p + 1; + } // endif sep + + num_read++; + imax = hmax = i; + + for (i = 0; i < hmax; i++) + length[0] = max(length[0], strlen(colname[i])); + + } // endif hdr + + for (num_read++; num_read <= num_max; num_read++) { + /*******************************************************************/ + /* Now start the reading process. Read one line. */ + /*******************************************************************/ + if (fgets(buf, sizeof(buf), infile)) { + buf[strlen(buf) - 1] = '\0'; + } else if (feof(infile)) { + sprintf(g->Message, MSG(EOF_AFTER_LINE), num_read -1); + break; + } else { + sprintf(g->Message, MSG(ERR_READING_REC), num_read, fn); + goto err; + } // endif's + + /*******************************************************************/ + /* Make the test for field lengths. */ + /*******************************************************************/ + i = n = phase = blank = digit = dec = 0; + + for (p = buf; *p; p++) + if (*p == sep) { + if (phase != 1) { + if (i == MAXCOL - 1) { + sprintf(g->Message, MSG(TOO_MANY_FIELDS), num_read, fn); + goto err; + } // endif i + + if (n) { + len[i] = max(len[i], n); + type = (digit || (dec && n == 1)) ? TYPE_STRING + : (dec) ? TYPE_FLOAT : TYPE_INT; + typ[i] = min(type, typ[i]); + prc[i] = max((typ[i] == TYPE_FLOAT) ? (dec - 1) : 0, prc[i]); + } // endif n + + i++; + n = phase = blank = digit = dec = 0; + } else // phase == 1 + n++; + + } else if (*p == ' ') { + if (phase < 2) + n++; + + if (blank) + digit = 1; + + } else if (*p == q) { + if (phase == 0) { + if (blank) + if (++nerr > mxr) { + sprintf(g->Message, MSG(MISPLACED_QUOTE), num_read); + goto err; + } else + goto skip; + + n = 0; + phase = digit = 1; + } else if (phase == 1) { + if (*(p+1) == q) { + // This is currently not implemented for CSV tables +// if (++nerr > mxr) { +// sprintf(g->Message, MSG(QUOTE_IN_QUOTE), num_read); +// goto err; +// } else +// goto skip; + + p++; + n++; + } else + phase = 2; + + } else if (++nerr > mxr) { // phase == 2 + sprintf(g->Message, MSG(MISPLACED_QUOTE), num_read); + goto err; + } else + goto skip; + + } else { + if (phase == 2) + if (++nerr > mxr) { + sprintf(g->Message, MSG(MISPLACED_QUOTE), num_read); + goto err; + } else + goto skip; + + // isdigit cannot be used here because of debug assert + if (!strchr("0123456789", *p)) { + if (!digit && *p == dechar) + dec = 1; // Decimal point found + else if (blank || !(*p == '-' || *p == '+')) + digit = 1; + + } else if (dec) + dec++; // More decimals + + n++; + blank = 1; + } // endif's *p + + if (phase == 1) + if (++nerr > mxr) { + sprintf(g->Message, MSG(UNBALANCE_QUOTE), num_read); + goto err; + } else + goto skip; + + if (n) { + len[i] = max(len[i], n); + type = (digit || n == 0 || (dec && n == 1)) ? TYPE_STRING + : (dec) ? TYPE_FLOAT : TYPE_INT; + typ[i] = min(type, typ[i]); + prc[i] = max((typ[i] == TYPE_FLOAT) ? (dec - 1) : 0, prc[i]); + } // endif n + + imax = max(imax, i+1); + skip: ; // Skip erroneous line + } // endfor num_read + + if (trace) { + htrc("imax=%d Lengths:", imax); + + for (i = 0; i < imax; i++) + htrc(" %d", len[i]); + + htrc("\n"); + } // endif trace + + fclose(infile); + + if (trace) + htrc("CSVColumns: imax=%d hmax=%d len=%d\n", + imax, hmax, length[0]); + + /*********************************************************************/ + /* Allocate the structures used to refer to the result set. */ + /*********************************************************************/ + qrp = PlgAllocResult(g, ncol, imax, IDS_COLUMNS + 3, + dbtype, buftyp, length); + qrp->Nblin = imax; + + /*********************************************************************/ + /* Now get the results into blocks. */ + /*********************************************************************/ + for (i = 0; i < imax; i++) { + if (i >= hmax) { + sprintf(buf, "COL%.3d", i+1); + p = buf; + } else + p = colname[i]; + + if (typ[i] == TYPE_UNKNOWN) // Void column + typ[i] = TYPE_STRING; + + crp = qrp->Colresp; // Column Name + crp->Kdata->SetValue(p, i); + crp = crp->Next; // Data Type + crp->Kdata->SetValue(typ[i], i); + crp = crp->Next; // Type Name + crp->Kdata->SetValue(GetTypeName(typ[i]), i); + crp = crp->Next; // Precision + crp->Kdata->SetValue(len[i], i); + crp = crp->Next; // Length + crp->Kdata->SetValue(len[i], i); + crp = crp->Next; // Scale (precision) + crp->Kdata->SetValue(prc[i], i); + } // endfor i + + /*********************************************************************/ + /* Return the result pointer for use by GetData routines. */ + /*********************************************************************/ + return qrp; + + err: + fclose(infile); + return NULL; + } // end of CSVCColumns + /* --------------------------- Class CSVDEF -------------------------- */ /***********************************************************************/ diff --git a/storage/connect/tabmysql.cpp b/storage/connect/tabmysql.cpp index ee56ae3e3ff..686c6b666b3 100644 --- a/storage/connect/tabmysql.cpp +++ b/storage/connect/tabmysql.cpp @@ -67,7 +67,13 @@ void PrintResult(PGLOBAL, PSEM, PQRYRES); extern "C" int trace; -#if 0 +/**************************************************************************/ +/* Allocate the result structure that will contain result data. */ +/**************************************************************************/ +PQRYRES PlgAllocResult(PGLOBAL g, int ncol, int maxres, int ids, + int *dbtype, int *buftyp, unsigned int *length, + bool blank = true, bool nonull = true); + /************************************************************************/ /* MyColumns: constructs the result blocks containing all columns */ /* of a MySQL table that will be retrieved by GetData commands. */ @@ -152,8 +158,11 @@ PQRYRES MyColumns(PGLOBAL g, char *host, char *db, char *user, char *pwd, } else qrp->Nblin++; + if ((type = MYSQLtoPLG(cmd)) == TYPE_ERROR) { + sprintf(g->Message, "Unsupported column type %s", cmd); + return NULL; + } // endif type - type = MYSQLtoPLG(cmd); crp = crp->Next; crp->Kdata->SetValue(type, i); crp = crp->Next; @@ -227,6 +236,7 @@ PQRYRES MyColumns(PGLOBAL g, char *host, char *db, char *user, char *pwd, return qrp; } // end of MyColumns +#if 0 /**************************************************************************/ /* SemMySQLColumns: analyze a MySQL table for column format. */ /**************************************************************************/ @@ -327,7 +337,7 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) Database = Cat->GetStringCatInfo(g, Name, "Database", "*"); Tabname = Cat->GetStringCatInfo(g, Name, "Name", Name); // Deprecated Tabname = Cat->GetStringCatInfo(g, Name, "Tabname", Tabname); - Username = Cat->GetStringCatInfo(g, Name, "User", NULL); + Username = Cat->GetStringCatInfo(g, Name, "User", "root"); Password = Cat->GetStringCatInfo(g, Name, "Password", NULL); Portnumber = Cat->GetIntCatInfo(Name, "Port", MYSQL_PORT); Bind = !!Cat->GetIntCatInfo(Name, "Bind", 0); diff --git a/storage/connect/tabwmi.cpp b/storage/connect/tabwmi.cpp index 2b30f0ac3f3..5d33e06ff76 100644 --- a/storage/connect/tabwmi.cpp +++ b/storage/connect/tabwmi.cpp @@ -1,5 +1,5 @@ /***********************************************************************/ -/* TABWMI: Author Olivier Bertrand -- PlugDB -- 2012 */ +/* TABWMI: Author Olivier Bertrand -- PlugDB -- 2012 - 2013 */ /* TABWMI: Virtual table to get WMI information. */ /***********************************************************************/ #if !defined(WIN32) @@ -21,6 +21,319 @@ #include "plgcnx.h" // For DB types #include "resource.h" +extern "C" int trace; + +/**************************************************************************/ +/* Allocate the result structure that will contain result data. */ +/**************************************************************************/ +PQRYRES PlgAllocResult(PGLOBAL g, int ncol, int maxres, int ids, + int *dbtype, int *buftyp, unsigned int *length, + bool blank = true, bool nonull = true); + +/* ------------------- Functions WMI Column info --------------------- */ + +/***********************************************************************/ +/* Structure used by WMI column info functions. */ +/***********************************************************************/ +typedef struct _WMIutil { + IWbemServices *Svc; + IWbemClassObject *Cobj; +} WMIUTIL, *PWMIUT; + +/***********************************************************************/ +/* Initialize WMI operations. */ +/***********************************************************************/ +PWMIUT InitWMI(PGLOBAL g, char *nsp, char *classname) +{ + IWbemLocator *loc; + char *p; + HRESULT res; + PWMIUT wp = (PWMIUT)PlugSubAlloc(g, NULL, sizeof(WMIUTIL)); + + if (trace) + htrc("WMIColumns class %s space %s\n", SVP(classname), SVP(nsp)); + + /*********************************************************************/ + /* Set default values for the namespace and class name. */ + /*********************************************************************/ + if (!nsp) + nsp = "root\\cimv2"; + + if (!classname) { + if (!stricmp(nsp, "root\\cimv2")) + classname = "ComputerSystemProduct"; + else if (!stricmp(nsp, "root\\cli")) + classname = "Msft_CliAlias"; + else { + strcpy(g->Message, "Missing class name"); + return NULL; + } // endif classname + + } // endif classname + + /*********************************************************************/ + /* Initialize WMI. */ + /*********************************************************************/ +//res = CoInitializeEx(NULL, COINIT_MULTITHREADED); + res = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + if (FAILED(res)) { + sprintf(g->Message, "Failed to initialize COM library. " + "Error code = %p", res); + return NULL; + } // endif res + +#if 0 // irrelevant for a DLL + res = CoInitializeSecurity(NULL, -1, NULL, NULL, + RPC_C_AUTHN_LEVEL_CONNECT, + RPC_C_IMP_LEVEL_IMPERSONATE, + NULL, EOAC_NONE, NULL); + + if (res != RPC_E_TOO_LATE && FAILED(res)) { + sprintf(g->Message, "Failed to initialize security. " + "Error code = %p", res); + CoUninitialize(); + return NULL; + } // endif Res +#endif // 0 + + res = CoCreateInstance(CLSID_WbemLocator, NULL, + CLSCTX_INPROC_SERVER, IID_IWbemLocator, + (void**) &loc); + if (FAILED(res)) { + sprintf(g->Message, "Failed to create Locator. " + "Error code = %p", res); + CoUninitialize(); + return NULL; + } // endif res + + res = loc->ConnectServer(_bstr_t(nsp), + NULL, NULL, NULL, 0, NULL, NULL, &wp->Svc); + + if (FAILED(res)) { + sprintf(g->Message, "Could not connect. Error code = %p", res); + loc->Release(); + CoUninitialize(); + return NULL; + } // endif res + + loc->Release(); + + if (trace) + htrc("Successfully connected to namespace.\n"); + + /*********************************************************************/ + /* Perform a full class object retrieval. */ + /*********************************************************************/ + p = (char*)PlugSubAlloc(g, NULL, strlen(classname) + 7); + + if (strchr(classname, '_')) + strcpy(p, classname); + else + strcat(strcpy(p, "Win32_"), classname); + + res = wp->Svc->GetObject(bstr_t(p), 0, 0, &wp->Cobj, 0); + + if (FAILED(res)) { + sprintf(g->Message, "failed GetObject %s in %s\n", classname, nsp); + wp->Svc->Release(); + wp->Svc = NULL; // MUST be set to NULL (why?) + return NULL; + } // endif res + + return wp; +} // end of InitWMI + +/***********************************************************************/ +/* WMIColumns: constructs the result blocks containing the description */ +/* of all the columns of a WMI table of a specified class. */ +/***********************************************************************/ +PQRYRES WMIColumns(PGLOBAL g, char *nsp, char *classname, PWMIUT wp) + { + static int dbtype[] = {DB_CHAR, DB_SHORT, DB_CHAR, + DB_INT, DB_INT, DB_SHORT}; + static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING, + TYPE_INT, TYPE_INT, TYPE_SHORT}; + static unsigned int len, length[] = {0, 6, 8, 10, 10, 6}; + int i = 0, n = 0, ncol = sizeof(dbtype) / sizeof(int); + int lng, typ, prec; + LONG low, upp; + BOOL b1, b2 = TRUE; + BSTR propname; + VARIANT val; + CIMTYPE type; + HRESULT res; + SAFEARRAY *prnlist = NULL; + PQRYRES qrp = NULL; + PCOLRES crp; + + /*********************************************************************/ + /* Initialize WMI if not done yet. */ + /*********************************************************************/ + if ((b1 = !wp) && !(wp = InitWMI(g, nsp, classname))) + return NULL; + + /*********************************************************************/ + /* Get the number of properties to return. */ + /*********************************************************************/ + res = wp->Cobj->Get(bstr_t("__Property_Count"), 0, &val, NULL, NULL); + + if (FAILED(res)) { + sprintf(g->Message, "failed Get(__Property_Count) res=%d\n", res); + goto err; + } // endif res + + if (!(n = val.lVal)) { + sprintf(g->Message, "Class %s in %s has no properties\n", + classname, nsp); + goto err; + } // endif res + + /*********************************************************************/ + /* Get max property name length. */ + /*********************************************************************/ + res = wp->Cobj->GetNames(NULL, + WBEM_FLAG_ALWAYS | WBEM_FLAG_NONSYSTEM_ONLY, + NULL, &prnlist); + + if (FAILED(res)) { + sprintf(g->Message, "failed GetNames res=%d\n", res); + goto err; + } // endif res + + res = SafeArrayGetLBound(prnlist, 1, &low); + res = SafeArrayGetUBound(prnlist, 1, &upp); + + for (long i = low; i <= upp; i++) { + // Get this property name. + res = SafeArrayGetElement(prnlist, &i, &propname); + + if (FAILED(res)) { + sprintf(g->Message, "failed GetArrayElement res=%d\n", res); + goto err; + } // endif res + + len = (unsigned)SysStringLen(propname); + length[0] = max(length[0], len); + } // enfor i + + res = SafeArrayDestroy(prnlist); + + /*********************************************************************/ + /* Allocate the structures used to refer to the result set. */ + /*********************************************************************/ + qrp = PlgAllocResult(g, ncol, n, IDS_COLUMNS + 3, + dbtype, buftyp, length); + + /*********************************************************************/ + /* Now get the results into blocks. */ + /*********************************************************************/ + res = wp->Cobj->BeginEnumeration(WBEM_FLAG_NONSYSTEM_ONLY); + + if (FAILED(res)) { + sprintf(g->Message, "failed BeginEnumeration hr=%d\n", res); + qrp = NULL; + goto err; + } // endif hr + + while (TRUE) { + res = wp->Cobj->Next(0, &propname, &val, &type, NULL); + + if (FAILED(res)) { + sprintf(g->Message, "failed getting Next hr=%d\n", res); + qrp = NULL; + goto err; + } else if (res == WBEM_S_NO_MORE_DATA) { + VariantClear(&val); + break; + } // endif res + + if (i >= n) + break; // Should never happen + else + prec = 0; + + switch (type) { + case CIM_STRING: + typ = TYPE_STRING; + lng = 255; + prec = 1; // Case insensitive + break; + case CIM_SINT32: + case CIM_UINT32: + case CIM_BOOLEAN: + typ = TYPE_INT; + lng = 9; + break; + case CIM_SINT8: + case CIM_UINT8: + case CIM_SINT16: + case CIM_UINT16: + typ = TYPE_SHORT; + lng = 6; + break; + case CIM_REAL64: + case CIM_REAL32: + prec = 2; + case CIM_SINT64: + case CIM_UINT64: + typ = TYPE_FLOAT; + lng = 15; + break; + case CIM_DATETIME: + typ = TYPE_DATE; + lng = 19; + break; + case CIM_CHAR16: + typ = TYPE_STRING; + lng = 16; + break; + case CIM_EMPTY: + typ = TYPE_STRING; + lng = 24; // ??? + break; + default: + qrp->BadLines++; + goto suite; + } // endswitch type + + crp = qrp->Colresp; // Column Name + crp->Kdata->SetValue(_com_util::ConvertBSTRToString(propname), i); + crp = crp->Next; // Data Type + crp->Kdata->SetValue(typ, i); + crp = crp->Next; // Type Name + crp->Kdata->SetValue(GetTypeName(typ), i); + crp = crp->Next; // Precision + crp->Kdata->SetValue(lng, i); + crp = crp->Next; // Length + crp->Kdata->SetValue(lng, i); + crp = crp->Next; // Scale (precision) + crp->Kdata->SetValue(prec, i); + i++; + + suite: + SysFreeString(propname); + VariantClear(&val); + } // endfor i + + qrp->Nblin = i; + b2 = b1; + + err: + if (b2) { + // Cleanup + wp->Cobj->Release(); + wp->Svc->Release(); + wp->Svc = NULL; // MUST be set to NULL (why?) + CoUninitialize(); + } // endif b + + /*********************************************************************/ + /* Return the result pointer for use by GetData routines. */ + /*********************************************************************/ + return qrp; + } // end of WMIColumns + /* -------------- Implementation of the WMI classes ------------------ */ /***********************************************************************/