/* Copyright (C) Olivier Bertrand 2004 - 2012 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /*************** Mycat CC Program Source Code File (.CC) ***************/ /* PROGRAM NAME: MYCAT */ /* ------------- */ /* Version 1.3 */ /* */ /* Author: Olivier Bertrand 2012 - 2013 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ /* This program are the DB description related routines. */ /***********************************************************************/ /***********************************************************************/ /* Include relevant MariaDB header file. */ /***********************************************************************/ #if defined(WIN32) //#include //#include #elif defined(UNIX) #include #include #include #include #include #endif #define DONT_DEFINE_VOID //#include #include "handler.h" #undef OFFSET /***********************************************************************/ /* Include application header files */ /* */ /* global.h is header containing all global declarations. */ /* plgdbsem.h is header containing DB application declarations. */ /* tabdos.h is header containing TDBDOS classes declarations. */ /* MYCAT.h is header containing DB description declarations. */ /***********************************************************************/ #if defined(UNIX) #include "osutil.h" #endif // UNIX #include "global.h" #include "plgdbsem.h" #include "reldef.h" #include "tabcol.h" #include "xtable.h" #include "filamtxt.h" #include "tabdos.h" #include "tabfmt.h" #include "tabvct.h" #include "tabsys.h" #if defined(WIN32) #include "tabmac.h" #include "tabwmi.h" #endif // WIN32 #include "tabtbl.h" #if defined(XML_SUPPORT) #include "tabxml.h" #endif // XML_SUPPORT #include "tabmul.h" #if defined(MYSQL_SUPPORT) #include "tabmysql.h" #endif // MYSQL_SUPPORT #if defined(ODBC_SUPPORT) #define NODBC #include "tabodbc.h" #endif // ODBC_SUPPORT #if defined(PIVOT_SUPPORT) #include "tabpivot.h" #endif // PIVOT_SUPPORT #include "ha_connect.h" #include "mycat.h" /**************************************************************************/ /* 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, ...); /**************************************************************************/ /* 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 --------------------------- */ /***********************************************************************/ /* CATALOG Constructor. */ /***********************************************************************/ CATALOG::CATALOG(void) { To_Desc= NULL; //*DescFile= '\0'; #if defined(WIN32) DataPath= ".\\"; #else // !WIN32 DataPath= "./"; #endif // !WIN32 Descp= NULL; //memset(&DescArea, 0, sizeof(AREADEF)); memset(&Ctb, 0, sizeof(CURTAB)); Cbuf= NULL; Cblen= 0; DefHuge= false; } // end of CATALOG constructor /* -------------------------- Class MYCAT ---------------------------- */ /***********************************************************************/ /* MYCAT Constructor. */ /***********************************************************************/ MYCAT::MYCAT(PHC hc) : CATALOG() { Hc= hc; To_Desc= NULL; DefHuge= false; SepIndex= true; // Temporay until we can store offet and size } // end of MYCAT constructor /***********************************************************************/ /* When using volatile storage, reset values pointing to Sarea. */ /***********************************************************************/ void MYCAT::Reset(void) { To_Desc= NULL; } // end of Reset /***********************************************************************/ /* This function sets the current database path. */ /***********************************************************************/ void MYCAT::SetPath(PGLOBAL g, LPCSTR *datapath, const char *path) { if (path) { size_t len= strlen(path) + (*path != '.' ? 4 : 1); char *buf= (char*)PlugSubAlloc(g, NULL, len); if (*path != '.') { #if defined(WIN32) char *s= "\\"; #else // !WIN32 char *s= "/"; #endif // !WIN32 strcat(strcat(strcat(strcpy(buf, "."), s), path), s); } else strcpy(buf, path); *datapath= buf; } // endif path } // end of SetDataPath /***********************************************************************/ /* This function sets an integer MYCAT information. */ /***********************************************************************/ bool MYCAT::SetIntCatInfo(LPCSTR name, PSZ what, int n) { return Hc->SetIntegerOption(what, n); } // end of SetIntCatInfo /***********************************************************************/ /* This function returns integer MYCAT information. */ /***********************************************************************/ int MYCAT::GetIntCatInfo(LPCSTR name, PSZ what, int idef) { int n= Hc->GetIntegerOption(what); return (n == NO_IVAL) ? idef : n; } // end of GetIntCatInfo /***********************************************************************/ /* This function returns Boolean MYCAT information. */ /***********************************************************************/ bool MYCAT::GetBoolCatInfo(LPCSTR name, PSZ what, bool bdef) { bool b= Hc->GetBooleanOption(what, bdef); return b; } // end of GetBoolCatInfo /***********************************************************************/ /* This function returns size catalog information. */ /***********************************************************************/ int MYCAT::GetSizeCatInfo(LPCSTR name, PSZ what, PSZ sdef) { char * s, c; int i, n= 0; if (!(s= Hc->GetStringOption(what))) s= sdef; if ((i= sscanf(s, " %d %c ", &n, &c)) == 2) switch (toupper(c)) { case 'M': n *= 1024; case 'K': n *= 1024; } // endswitch c return n; } // end of GetSizeCatInfo /***********************************************************************/ /* This function sets char MYCAT information in buf. */ /***********************************************************************/ int MYCAT::GetCharCatInfo(LPCSTR name, PSZ what, PSZ sdef, char *buf, int size) { char *s= Hc->GetStringOption(what); strncpy(buf, ((s) ? s : sdef), size); return size; } // end of GetCharCatInfo /***********************************************************************/ /* This function returns string MYCAT information. */ /* Default parameter is "*" to get the handler default. */ /***********************************************************************/ char *MYCAT::GetStringCatInfo(PGLOBAL g, PSZ name, PSZ what, PSZ sdef) { char *sval, *s= Hc->GetStringOption(what, sdef); if (s) { sval= (char*)PlugSubAlloc(g, NULL, strlen(s) + 1); strcpy(sval, s); } else sval = NULL; return sval; } // end of GetStringCatInfo /***********************************************************************/ /* This function returns column MYCAT information. */ /***********************************************************************/ int MYCAT::GetColCatInfo(PGLOBAL g, PTABDEF defp) { char *type= GetStringCatInfo(g, NULL, "Type", "DOS"); int i, loff, poff, nof, nlg; void *field= NULL; PCOLDEF cdp, lcdp= NULL, tocols= NULL; PCOLINFO pcf= (PCOLINFO)PlugSubAlloc(g, NULL, sizeof(COLINFO)); // Get a unique char identifier for type char tc= GetTypeID(type); // Take care of the column definitions i= poff= nof= nlg= 0; // Offsets of HTML and DIR tables start from 0, DBF at 1 loff= (tc == 'A') ? 1 : (tc == 'X' || tc == 'R') ? -1 : 0; while (true) { // Default Offset depends on table type switch (tc) { case 'D': // DOS case 'F': // FIX case 'B': // BIN case 'V': // VEC case 'A': // DBF poff= loff + nof; // Default next offset nlg= max(nlg, poff); // Default lrecl break; case 'C': // CSV case 'T': // FMT nlg+= nof; case 'R': // DIR case 'X': // XML poff= loff + 1; break; case 'I': // INI case 'M': // MAC case 'L': // TBL case 'E': // OEM poff = 0; // Offset represents an independant flag break; default: // VCT PLG ODBC MYSQL WMI... poff = 0; // NA break; } // endswitch tc do { field= Hc->GetColumnOption(field, pcf); } while (field && (*pcf->Name =='*' /*|| pcf->Flags & U_VIRTUAL*/)); if (tc == 'A' && pcf->Type == TYPE_DATE && !pcf->Datefmt) { // DBF date format defaults to 'YYYMMDD' pcf->Datefmt= "YYYYMMDD"; pcf->Length= 8; } // endif tc if (!field) break; // Allocate the column description block cdp= new(g) COLDEF; if ((nof= cdp->Define(g, NULL, pcf, poff)) < 0) return -1; // Error, probably unhandled type else if (nof) loff= cdp->GetOffset(); switch (tc) { case 'V': cdp->SetOffset(0); // Not to have shift case 'B': // BIN/VEC are packed by default if (nof) // Field width is the internal representation width // that can also depend on the column format switch (cdp->Fmt ? *cdp->Fmt : 'X') { case 'C': break; case 'R': case 'F': case 'L': case 'I': nof= 4; break; case 'D': nof= 8; break; case 'S': nof= 2; break; case 'T': nof= 1; break; default: nof= cdp->Clen; } // endswitch Fmt break; } // endswitch tc if (lcdp) lcdp->SetNext(cdp); else tocols= cdp; lcdp= cdp; i++; } // endwhile // Degree is the the number of defined columns (informational) if (i != defp->GetDegree()) defp->SetDegree(i); if (defp->GetDefType() == TYPE_AM_DOS) { int ending, recln= 0; PDOSDEF ddp= (PDOSDEF)defp; // Was commented because sometimes ending is 0 even when // not specified (for instance if quoted is specified) // if ((ending= Hc->GetIntegerOption("Ending")) < 0) { if ((ending= Hc->GetIntegerOption("Ending")) <= 0) { #if defined(WIN32) ending= 2; #else ending= 1; #endif Hc->SetIntegerOption("Ending", ending); } // endif ending // Calculate the default record size switch (tc) { case 'F': recln= nlg + ending; // + length of line ending break; case 'B': case 'V': recln= nlg; // if ((k= (pak < 0) ? 8 : pak) > 1) // See above for detailed comment // Round up lrecl to multiple of 8 or pak // recln= ((recln + k - 1) / k) * k; break; case 'D': case 'A': recln= nlg; break; 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 break; } // endswitch tc // lrecl must be at least recln to avoid buffer overflow recln= max(recln, Hc->GetIntegerOption("Lrecl")); Hc->SetIntegerOption("Lrecl", recln); ddp->SetLrecl(recln); } // endif Lrecl // Attach the column definition to the tabdef defp->SetCols(tocols); return poff; } // end of GetColCatInfo /***********************************************************************/ /* GetIndexInfo: retrieve index description from the table structure. */ /***********************************************************************/ bool MYCAT::GetIndexInfo(PGLOBAL g, PTABDEF defp) { PIXDEF xdp, pxd= NULL, toidx= NULL; // Now take care of the index definitions for (int n= 0; ; n++) { if (xtrace) printf("Getting index %d info\n", n + 1); if (!(xdp= Hc->GetIndexInfo(n))) break; if (pxd) pxd->SetNext(xdp); else toidx= xdp; pxd= xdp; } // endfor n // All is correct, attach new index(es) defp->SetIndx(toidx); return false; } // end of GetIndexInfo /***********************************************************************/ /* GetTableDesc: retrieve a table descriptor. */ /* Look for a table descriptor matching the name and type. If found */ /* in storage, return a pointer to it, else look in the XDB file. If */ /* found, make and add the descriptor and return a pointer to it. */ /***********************************************************************/ PRELDEF MYCAT::GetTableDesc(PGLOBAL g, LPCSTR name, LPCSTR am, PRELDEF *prp) { LPCSTR type; if (xtrace) printf("GetTableDesc: name=%s am=%s\n", name, SVP(am)); // Firstly check whether this table descriptor is in memory if (To_Desc) return To_Desc; // Here get the type of this table if (!(type= Hc->GetStringOption("Type"))) type= "DOS"; return MakeTableDesc(g, name, type); } // end of GetTableDesc /***********************************************************************/ /* MakeTableDesc: make a table/view description. */ /* Note: caller must check if name already exists before calling it. */ /***********************************************************************/ PRELDEF MYCAT::MakeTableDesc(PGLOBAL g, LPCSTR name, LPCSTR am) { char tc; PRELDEF tdp= NULL; if (xtrace) printf("MakeTableDesc: name=%s am=%s\n", name, SVP(am)); /*********************************************************************/ /* Get a unique char identifier for types. The letter used are: */ /* ABCDEF..IJKLM.OPQRSTUVWXYZ and Allocate table definition class */ /*********************************************************************/ 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; #if defined(XML_SUPPORT) case 'X': tdp= new(g) XMLDEF; break; #endif // XML_SUPPORT case 'V': tdp= new(g) VCTDEF; break; #if defined(ODBC_SUPPORT) case 'O': tdp= new(g) ODBCDEF; break; #endif // ODBC_SUPPORT #if defined(WIN32) case 'M': tdp= new(g) MACDEF; break; case 'W': tdp= new(g) WMIDEF; break; #endif // WIN32 case 'E': tdp= new(g) OEMDEF; break; case 'L': tdp= new(g) TBLDEF; break; #if defined(MYSQL_SUPPORT) case 'Y': tdp= new(g) MYSQLDEF; break; #endif // MYSQL_SUPPORT #if defined(PIVOT_SUPPORT) case 'P': tdp= new(g) PIVOTDEF; break; #endif // PIVOT_SUPPORT default: sprintf(g->Message, MSG(BAD_TABLE_TYPE), am, name); } // endswitch // Do make the table/view definition from XDB file information if (tdp && tdp->Define(g, this, name, am)) tdp= NULL; return tdp; } // end of MakeTableDesc /***********************************************************************/ /* Initialize a Table Description Block construction. */ /***********************************************************************/ PTDB MYCAT::GetTable(PGLOBAL g, PTABLE tablep, MODE mode) { PRELDEF tdp; PTDB tdbp= NULL; LPCSTR name= tablep->GetName(); if (xtrace) printf("GetTableDB: name=%s\n", name); // Look for the description of the requested table tdp= GetTableDesc(g, name, NULL); if (tdp) { if (xtrace) printf("tdb=%p type=%s\n", tdp, tdp->GetType()); if (tablep->GetQualifier()) SetPath(g, &tdp->Database, tablep->GetQualifier()); tdbp= tdp->GetTable(g, mode); } // endif tdp if (tdbp) { if (xtrace) printf("tdbp=%p name=%s amtype=%d\n", tdbp, tdbp->GetName(), tdbp->GetAmType()); tablep->SetTo_Tdb(tdbp); tdbp->SetTable(tablep); } // endif tdbp return (tdbp); } // end of GetTable /***********************************************************************/ /* ClearDB: Terminates Database usage. */ /***********************************************************************/ void MYCAT::ClearDB(PGLOBAL g) { To_Desc= NULL; } // end of ClearDB /* ------------------------ End of MYCAT --------------------------- */