/************* Tabutil cpp Declares Source Code File (.CPP) ************/ /* Name: TABUTIL.CPP Version 1.0 */ /* */ /* (C) Copyright to the author Olivier BERTRAND 2013 */ /* */ /* Utility function used by the PROXY, XCOL, OCCUR, and TBL tables. */ /***********************************************************************/ /***********************************************************************/ /* Include relevant section of system dependant header files. */ /***********************************************************************/ #include "my_global.h" #include "sql_class.h" #include "table.h" #include "field.h" #if defined(WIN32) #include #include #if defined(__BORLANDC__) #define __MFC_COMPAT__ // To define min/max as macro #endif //#include #else #if defined(UNIX) #include #include #include #include #include #include "osutil.h" #else //#include #endif //#include #endif /***********************************************************************/ /* Include application header files: */ /***********************************************************************/ #include "table.h" // MySQL table definitions #include "global.h" #include "plgdbsem.h" #include "plgcnx.h" // For DB types #include "myutil.h" #include "mycat.h" #include "valblk.h" #include "resource.h" #include "reldef.h" #include "xtable.h" #if defined(MYSQL_SUPPORT) #include "tabmysql.h" #endif // MYSQL_SUPPORT #include "tabcol.h" #include "tabutil.h" #include "ha_connect.h" extern "C" int trace; /************************************************************************/ /* Used by MYSQL tables to get MySQL parameters from the calling proxy */ /* table (PROXY, TBL, XCL, or OCCUR) when used by one of these. */ /************************************************************************/ void Remove_tshp(PCATLG cat) { ((MYCAT*)cat)->GetHandler()->tshp = NULL; } // end of Remove_thsp /************************************************************************/ /* GetTableShare: allocates and open a table share. */ /************************************************************************/ TABLE_SHARE *GetTableShare(PGLOBAL g, THD *thd, const char *db, const char *name, bool& mysql) { char key[256]; uint k; //TABLE_LIST table_list; TABLE_SHARE *s; //table_list.init_one_table(db, strlen(db), name, strlen(name), // NULL, TL_IGNORE); k = sprintf(key, "%s", db) + 1; k += sprintf(key + k, "%s", name); key[++k] = 0; if (!(s = alloc_table_share(db, name, key, ++k))) { strcpy(g->Message, "Error allocating share\n"); return NULL; } // endif s // 1 2 4 8 //flags = GTS_TABLE | GTS_VIEW | GTS_NOLOCK | GTS_FORCE_DISCOVERY; if (!open_table_def(thd, s, GTS_TABLE | GTS_VIEW)) { if (!s->is_view) { if (stricmp(plugin_name(s->db_plugin)->str, "connect")) { #if defined(MYSQL_SUPPORT) mysql = true; #else // !MYSQL_SUPPORT sprintf(g->Message, "%s.%s is not a CONNECT table", db, name); return NULL; #endif // MYSQL_SUPPORT } else mysql = false; } else { mysql = true; } // endif is_view } else { sprintf(g->Message, "Error %d opening share\n", s->error); free_table_share(s); return NULL; } // endif open_table_def return s; } // end of GetTableShare /************************************************************************/ /* TabColumns: constructs the result blocks containing all the columns */ /* of the object table that will be retrieved by GetData commands. */ /************************************************************************/ PQRYRES TabColumns(PGLOBAL g, THD *thd, const char *db, const char *name, bool& info) { static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING, TYPE_INT, TYPE_INT, TYPE_SHORT, TYPE_SHORT, TYPE_SHORT, TYPE_STRING, TYPE_STRING, TYPE_STRING}; static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME, FLD_PREC, FLD_LENGTH, FLD_SCALE, FLD_RADIX, FLD_NULL, FLD_REM, FLD_NO, FLD_CHARSET}; static unsigned int length[] = {0, 4, 16, 4, 4, 4, 4, 4, 256, 32, 32}; char *fld, *fmt; int i, n, ncol = sizeof(buftyp) / sizeof(int); int len, type, prec; bool mysql; TABLE_SHARE *s = NULL; Field* *field; Field *fp; PQRYRES qrp; PCOLRES crp; if (!info) { if (!(s = GetTableShare(g, thd, db, name, mysql))) { return NULL; } else if (s->is_view) { strcpy(g->Message, "Use MYSQL type to see columns from a view"); info = true; // To tell caller name is a view free_table_share(s); return NULL; } else n = s->fieldnames.count; } else { n = 0; length[0] = 128; } // endif info /**********************************************************************/ /* Allocate the structures used to refer to the result set. */ /**********************************************************************/ qrp = PlgAllocResult(g, ncol, n, IDS_COLUMNS + 3, buftyp, fldtyp, length, false, true); // Some columns must be renamed for (i = 0, crp = qrp->Colresp; crp; crp = crp->Next) switch (++i) { case 10: crp->Name = "Date_fmt"; break; case 11: crp->Name = "Collation"; break; } // endswitch i if (info) return qrp; /**********************************************************************/ /* Now get the results into blocks. */ /**********************************************************************/ for (i = 0, field= s->field; *field; i++, field++) { fp= *field; // Get column name crp = qrp->Colresp; // Column_Name fld = (char *)fp->field_name; crp->Kdata->SetValue(fld, i); if ((type = MYSQLtoPLG(fp->type())) == TYPE_ERROR) { sprintf(g->Message, "Unsupported column type %s", GetTypeName(type)); qrp = NULL; break; } // endif type crp = crp->Next; // Data_Type crp->Kdata->SetValue(type, i); crp = crp->Next; // Type_Name crp->Kdata->SetValue(GetTypeName(type), i); if (type == TYPE_DATE) { // When creating tables we do need info about date columns if (mysql) { fmt = MyDateFmt(fp->type()); len = strlen(fmt); } else { fmt = (char*)fp->option_struct->dateformat; len = fp->field_length; } // endif mysql } else { fmt = NULL; len = fp->char_length(); } // endif type crp = crp->Next; // Precision crp->Kdata->SetValue(len, i); crp = crp->Next; // Length len = fp->field_length; crp->Kdata->SetValue(len, i); prec = (type == TYPE_FLOAT) ? fp->decimals() : 0; crp = crp->Next; // Scale crp->Kdata->SetValue(prec, i); crp = crp->Next; // Radix crp->Kdata->SetValue(0, i); crp = crp->Next; // Nullable crp->Kdata->SetValue((fp->null_ptr != 0) ? 1 : 0, i); crp = crp->Next; // Remark // For Valgrind if (fp->comment.length > 0 && (fld = fp->comment.str)) crp->Kdata->SetValue(fld, fp->comment.length, i); else crp->Kdata->Reset(i); crp = crp->Next; // New crp->Kdata->SetValue((fmt) ? fmt : (char*) "", i); crp = crp->Next; // New (charset) fld = (char *)fp->charset()->name; crp->Kdata->SetValue(fld, i); // Add this item qrp->Nblin++; } // endfor field /**********************************************************************/ /* Return the result pointer for use by GetData routines. */ /**********************************************************************/ if (s) free_table_share(s); return qrp; } // end of TabColumns /* -------------- Implementation of the PROXY classes ---------------- */ /***********************************************************************/ /* PRXDEF constructor. */ /***********************************************************************/ PRXDEF::PRXDEF(void) { Tablep = NULL; Pseudo = 3; } // end of PRXDEF constructor /***********************************************************************/ /* DefineAM: define specific AM block values from XCOL file. */ /***********************************************************************/ bool PRXDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) { char *pn, *db, *tab, *def = NULL; db = Cat->GetStringCatInfo(g, "Dbname", "*"); def = Cat->GetStringCatInfo(g, "Srcdef", NULL); if (!(tab = Cat->GetStringCatInfo(g, "Tabname", NULL))) { if (!def) { strcpy(g->Message, "Missing object table definition"); return TRUE; } else tab = "Noname"; } else // Analyze the table name, it may have the format: [dbname.]tabname if ((pn = strchr(tab, '.'))) { *pn++ = 0; db = tab; tab = pn; } // endif pn Tablep = new(g) XTAB(tab, def); Tablep->SetQualifier(db); return FALSE; } // end of DefineAM /***********************************************************************/ /* GetTable: makes a new TDB of the proper type. */ /***********************************************************************/ PTDB PRXDEF::GetTable(PGLOBAL g, MODE mode) { if (Catfunc == FNC_COL) return new(g) TDBTBC(this); else return new(g) TDBPRX(this); } // end of GetTable /* ------------------------------------------------------------------- */ /***********************************************************************/ /* Implementation of the TDBPRX class. */ /***********************************************************************/ TDBPRX::TDBPRX(PPRXDEF tdp) : TDBASE(tdp) { Tdbp = NULL; // The object table } // end of TDBPRX constructor /***********************************************************************/ /* Get the PTDB of the sub-table. */ /***********************************************************************/ PTDBASE TDBPRX::GetSubTable(PGLOBAL g, PTABLE tabp, bool b) { const char *sp = NULL; char *db, *name; bool mysql = true; PTDB tdbp = NULL; TABLE_SHARE *s = NULL; Field* *fp = NULL; PCATLG cat = To_Def->GetCat(); PHC hc = ((MYCAT*)cat)->GetHandler(); LPCSTR cdb, curdb = hc->GetDBName(NULL); THD *thd = (hc->GetTable())->in_use; db = (char*)tabp->GetQualifier(); name = (char*)tabp->GetName(); // Check for eventual loop for (PTABLE tp = To_Table; tp; tp = tp->Next) { cdb = (tp->Qualifier) ? tp->Qualifier : curdb; if (!stricmp(name, tp->Name) && !stricmp(db, cdb)) { sprintf(g->Message, "Table %s.%s pointing on itself", db, name); return NULL; } // endif } // endfor tp if (!tabp->GetSrc()) { if (!(s = GetTableShare(g, thd, db, name, mysql))) return NULL; if (s->is_view && !b) s->field = hc->get_table()->s->field; hc->tshp = s; } else if (b) { // Don't use caller's columns fp = hc->get_table()->field; hc->get_table()->field = NULL; // Make caller use the source definition sp = hc->get_table()->s->option_struct->srcdef; hc->get_table()->s->option_struct->srcdef = tabp->GetSrc(); } // endif srcdef if (mysql) { #if defined(MYSQL_SUPPORT) // Access sub-table via MySQL API if (!(tdbp= cat->GetTable(g, tabp, MODE_READ, "MYPRX"))) { char buf[MAX_STR]; strcpy(buf, g->Message); sprintf(g->Message, "Error accessing %s.%s: %s", db, name, buf); hc->tshp = NULL; goto err; } // endif Define if (db) ((PTDBMY)tdbp)->SetDatabase(tabp->GetQualifier()); #else // !MYSQL_SUPPORT sprintf(g->Message, "%s.%s is not a CONNECT table", db, tblp->Name); goto err; #endif // MYSQL_SUPPORT } else { // Sub-table is a CONNECT table tabp->Next = To_Table; // For loop checking tdbp = cat->GetTable(g, tabp); } // endif mysql if (s) { if (s->is_view && !b) s->field = NULL; hc->tshp = NULL; } else if (b) { // Restore s structure that can be in cache hc->get_table()->field = fp; hc->get_table()->s->option_struct->srcdef = sp; } // endif s if (trace && tdbp) htrc("Subtable %s in %s\n", name, SVP(((PTDBASE)tdbp)->GetDef()->GetDB())); err: if (s) free_table_share(s); return (PTDBASE)tdbp; } // end of GetSubTable /***********************************************************************/ /* Initializes the table. */ /***********************************************************************/ bool TDBPRX::InitTable(PGLOBAL g) { if (!Tdbp) { // Get the table description block of this table if (!(Tdbp = GetSubTable(g, ((PPRXDEF)To_Def)->Tablep))) return TRUE; } // endif Tdbp return FALSE; } // end of InitTable /***********************************************************************/ /* Allocate PRX column description block. */ /***********************************************************************/ PCOL TDBPRX::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) { return new(g) PRXCOL(cdp, this, cprec, n); } // end of MakeCol /***********************************************************************/ /* PRX GetMaxSize: returns the maximum number of rows in the table. */ /***********************************************************************/ int TDBPRX::GetMaxSize(PGLOBAL g) { if (MaxSize < 0) { if (InitTable(g)) return 0; MaxSize = Tdbp->GetMaxSize(g); } // endif MaxSize return MaxSize; } // end of GetMaxSize /***********************************************************************/ /* In this sample, ROWID will be the (virtual) row number, */ /* while ROWNUM will be the occurence rank in the multiple column. */ /***********************************************************************/ int TDBPRX::RowNumber(PGLOBAL g, bool b) { return Tdbp->RowNumber(g, b); } // end of RowNumber /***********************************************************************/ /* PROXY Access Method opening routine. */ /***********************************************************************/ bool TDBPRX::OpenDB(PGLOBAL g) { if (Use == USE_OPEN) { /*******************************************************************/ /* Table already open, just replace it at its beginning. */ /*******************************************************************/ return Tdbp->OpenDB(g); } // endif use if (Mode != MODE_READ) { /*******************************************************************/ /* Currently XCOL tables cannot be modified. */ /*******************************************************************/ strcpy(g->Message, "PROXY tables are read only"); return TRUE; } // endif Mode if (InitTable(g)) return TRUE; /*********************************************************************/ /* Check and initialize the subtable columns. */ /*********************************************************************/ for (PCOL cp = Columns; cp; cp = cp->GetNext()) if (((PPRXCOL)cp)->Init(g)) return TRUE; /*********************************************************************/ /* Physically open the object table. */ /*********************************************************************/ if (Tdbp->OpenDB(g)) return TRUE; return FALSE; } // end of OpenDB /***********************************************************************/ /* Data Base read routine for PROY access method. */ /***********************************************************************/ int TDBPRX::ReadDB(PGLOBAL g) { /*********************************************************************/ /* Now start the reading process. */ /*********************************************************************/ return Tdbp->ReadDB(g); } // end of ReadDB /***********************************************************************/ /* WriteDB: Data Base write routine for PROXY access methods. */ /***********************************************************************/ int TDBPRX::WriteDB(PGLOBAL g) { sprintf(g->Message, "%s tables are read only", To_Def->GetType()); return RC_FX; } // end of WriteDB /***********************************************************************/ /* Data Base delete line routine for PROXY access methods. */ /***********************************************************************/ int TDBPRX::DeleteDB(PGLOBAL g, int irc) { sprintf(g->Message, "Delete not enabled for %s tables", To_Def->GetType()); return RC_FX; } // end of DeleteDB /***********************************************************************/ /* Used by the TBL tables. */ /***********************************************************************/ void TDBPRX::RemoveNext(PTABLE tp) { tp->Next = NULL; } // end of RemoveNext /* ---------------------------- PRXCOL ------------------------------- */ /***********************************************************************/ /* PRXCOL public constructor. */ /***********************************************************************/ PRXCOL::PRXCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am) : COLBLK(cdp, tdbp, i) { if (cprec) { Next = cprec->GetNext(); cprec->SetNext(this); } else { Next = tdbp->GetColumns(); tdbp->SetColumns(this); } // endif cprec // Set additional Dos access method information for column. Long = cdp->GetLong(); // Useful ??? //strcpy(F_Date, cdp->F_Date); Colp = NULL; To_Val = NULL; Pseudo = FALSE; Colnum = cdp->GetOffset(); // If columns are retrieved by number if (trace) htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this); } // end of PRXCOL constructor /***********************************************************************/ /* PRXCOL initialization routine. */ /* Look for the matching column in the object table. */ /***********************************************************************/ bool PRXCOL::Init(PGLOBAL g) { PTDBPRX tdbp = (PTDBPRX)To_Tdb; if (!(Colp = tdbp->Tdbp->ColDB(g, Name, 0)) && Colnum) Colp = tdbp->Tdbp->ColDB(g, NULL, Colnum); if (Colp) { // May not have been done elsewhere Colp->InitValue(g); To_Val = Colp->GetValue(); // this may be needed by some tables (which?) Colp->SetColUse(ColUse); } else { sprintf(g->Message, MSG(NO_MATCHING_COL), Name, tdbp->Tdbp->GetName()); return TRUE; } // endif Colp return FALSE; } // end of Init /***********************************************************************/ /* Reset the column descriptor to non evaluated yet. */ /***********************************************************************/ void PRXCOL::Reset(void) { if (Colp) Colp->Reset(); Status &= ~BUF_READ; } // end of Reset /***********************************************************************/ /* ReadColumn: */ /***********************************************************************/ void PRXCOL::ReadColumn(PGLOBAL g) { if (trace) htrc("PRX ReadColumn: name=%s\n", Name); if (Colp) { Colp->Eval(g); Value->SetValue_pval(To_Val); // Set null when applicable if (Nullable) Value->SetNull(Value->IsNull()); } // endif Colp } // end of ReadColumn /* ---------------------------TDBTBC class --------------------------- */ /***********************************************************************/ /* TDBTBC class constructor. */ /***********************************************************************/ TDBTBC::TDBTBC(PPRXDEF tdp) : TDBCAT(tdp) { Db = (PSZ)tdp->Tablep->GetQualifier(); Tab = (PSZ)tdp->Tablep->GetName(); } // end of TDBTBC constructor /***********************************************************************/ /* GetResult: Get the list the MYSQL table columns. */ /***********************************************************************/ PQRYRES TDBTBC::GetResult(PGLOBAL g) { bool b = false; return TabColumns(g, current_thd, Db, Tab, b); } // end of GetResult