/************* Tabutil cpp Declares Source Code File (.CPP) ************/ /* Name: TABUTIL.CPP Version 1.2 */ /* */ /* (C) Copyright to the author Olivier BERTRAND 2013 - 2017 */ /* */ /* Utility function used by the PROXY, XCOL, OCCUR, and TBL tables. */ /***********************************************************************/ /***********************************************************************/ /* Include relevant section of system dependant header files. */ /***********************************************************************/ #define MYSQL_SERVER 1 #include #include "sql_class.h" #include "table.h" #include "field.h" #if defined(__WIN__) #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 "valblk.h" #include "resource.h" //#include "reldef.h" #include "xtable.h" #include "tabext.h" #include "tabmysql.h" #include "tabcol.h" #include "tabutil.h" #include "ha_connect.h" int GetConvSize(void); /************************************************************************/ /* Used by MYSQL tables to get MySQL parameters from the calling proxy */ /* table (PROXY, TBL, XCL, or OCCUR) when used by one of these. */ /************************************************************************/ TABLE_SHARE *Remove_tshp(PCATLG cat) { TABLE_SHARE *s = ((MYCAT*)cat)->GetHandler()->tshp; ((MYCAT*)cat)->GetHandler()->tshp = NULL; return s; } // end of Remove_thsp /************************************************************************/ /* 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 Restore_tshp(PCATLG cat, TABLE_SHARE *s) { ((MYCAT*)cat)->GetHandler()->tshp = s; } // end of Restore_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_SHARE *s; 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 if (!open_table_def(thd, s, GTS_TABLE | GTS_VIEW)) { if (!s->is_view) { if (stricmp(plugin_name(s->db_plugin)->str, "connect")) mysql = true; else mysql = false; } else mysql = true; } else { if (thd->is_error()) thd->clear_error(); // Avoid stopping info commands 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 */ /* description of the object table that will be retrieved by discovery.*/ /************************************************************************/ PQRYRES TabColumns(PGLOBAL g, THD *thd, const char *db, const char *name, bool& info) { int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING, TYPE_INT, TYPE_INT, TYPE_SHORT, TYPE_SHORT, TYPE_SHORT, TYPE_STRING, TYPE_STRING, TYPE_STRING}; XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME, FLD_PREC, FLD_LENGTH, FLD_SCALE, FLD_RADIX, FLD_NULL, FLD_REM, FLD_NO, FLD_CHARSET}; unsigned int length[] = {0, 4, 16, 4, 4, 4, 4, 4, 0, 32, 32}; PCSZ fmt; char *pn, *tn, *fld, *colname, v; // *chset int i, n, ncol = sizeof(buftyp) / sizeof(int); int prec, len, type, scale; int zconv = GetConvSize(); bool mysql; TABLE_SHARE *s = NULL; Field* *field; Field *fp; PQRYRES qrp; PCOLRES crp; if (!info) { // Analyze the table name, it may have the format: [dbname.]tabname if (strchr((char*)name, '.')) { tn = (char*)PlugDup(g, name); pn = strchr(tn, '.'); *pn++ = 0; db = tn; name = pn; } // endif pn 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. */ /**********************************************************************/ 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 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; field++) { fp= *field; // Get column name crp = qrp->Colresp; // Column_Name colname = (char *)fp->field_name.str; crp->Kdata->SetValue(colname, i); // chset = (char *)fp->charset()->name; // v = (!strcmp(chset, "binary")) ? 'B' : 0; v = 0; if ((type = MYSQLtoPLG(fp->type(), &v)) == TYPE_ERROR) { if (v == 'K') { // Skip this column sprintf(g->Message, "Column %s skipped (unsupported type)", colname); push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message); continue; } // endif v sprintf(g->Message, "Column %s unsupported type", colname); qrp = NULL; break; } // endif type if (v == 'X') { len = zconv; sprintf(g->Message, "Column %s converted to varchar(%d)", colname, len); push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message); } // endif v crp = crp->Next; // Data_Type crp->Kdata->SetValue(type, i); if (fp->flags & ZEROFILL_FLAG) crp->Nulls[i] = 'Z'; else if (fp->flags & UNSIGNED_FLAG) crp->Nulls[i] = 'U'; else // X means TEXT field crp->Nulls[i] = (v == 'X') ? 'V' : v; crp = crp->Next; // Type_Name crp->Kdata->SetValue(GetTypeName(type), i); fmt = NULL; if (type == TYPE_DATE) { // When creating tables we do need info about date columns if (mysql) { fmt = MyDateFmt(fp->type()); prec = len = strlen(fmt); } else { fmt = (PCSZ)fp->option_struct->dateformat; prec = len = fp->field_length; } // endif mysql } else if (v != 'X') { if (type == TYPE_DECIM) prec = ((Field_new_decimal*)fp)->precision; else prec = fp->field_length; // prec = (prec(???) == NOT_FIXED_DEC) ? 0 : fp->field_length; len = fp->char_length(); } else prec = len = zconv; crp = crp->Next; // Precision crp->Kdata->SetValue(prec, i); crp = crp->Next; // Length crp->Kdata->SetValue(len, i); crp = crp->Next; // Scale scale = (type == TYPE_DOUBLE || type == TYPE_DECIM) ? fp->decimals() : 0; crp->Kdata->SetValue(scale, 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 = (char*) fp->comment.str)) crp->Kdata->SetValue(fld, fp->comment.length, i); else crp->Kdata->Reset(i); crp = crp->Next; // New (date format) 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++; i++; // Can be skipped } // 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, int) { char *pn, *db, *tab, *def = NULL; db = GetStringCatInfo(g, "Dbname", "*"); def = GetStringCatInfo(g, "Srcdef", NULL); if (!(tab = GetStringCatInfo(g, "Tabname", NULL))) { if (!def) { strcpy(g->Message, "Missing object table definition"); return true; } else tab = PlugDup(g, "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->SetSchema(db); return false; } // end of DefineAM /***********************************************************************/ /* GetTable: makes a new TDB of the proper type. */ /***********************************************************************/ PTDB PRXDEF::GetTable(PGLOBAL g, 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 TDBPRX::TDBPRX(PTDBPRX tdbp) : TDBASE(tdbp) { Tdbp = tdbp->Tdbp; } // end of TDBPRX copy constructor // Method PTDB TDBPRX::Clone(PTABS t) { PTDB tp; PPRXCOL cp1, cp2; PGLOBAL g = t->G; tp = new(g) TDBPRX(this); for (cp1 = (PPRXCOL)Columns; cp1; cp1 = (PPRXCOL)cp1->GetNext()) { cp2 = new(g) PRXCOL(cp1, tp); // Make a copy NewPointer(t, cp1, cp2); } // endfor cp1 return tp; } // end of Clone /***********************************************************************/ /* Get the PTDB of the sub-table. */ /***********************************************************************/ PTDB 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->GetSchema() ? tabp->GetSchema() : curdb); name = (char*)tabp->GetName(); // Check for eventual loop for (PTABLE tp = To_Table; tp; tp = tp->Next) { cdb = (tp->Schema) ? tp->Schema : 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) { // Access sub-table via MySQL API if (!(tdbp= cat->GetTable(g, tabp, Mode, "MYPRX"))) { char buf[MAX_STR]; strcpy(buf, g->Message); snprintf(g->Message, MAX_STR, "Error accessing %s.%s: %s", db, name, buf); hc->tshp = NULL; goto err; } // endif Define if (db) ((PTDBMY)tdbp)->SetDatabase(tabp->GetSchema()); if (Mode == MODE_UPDATE || Mode == MODE_DELETE) tdbp->SetName(Name); // For Make_Command } else { // Sub-table is a CONNECT table tabp->Next = To_Table; // For loop checking tdbp = cat->GetTable(g, tabp, Mode); } // 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(1) && tdbp) htrc("Subtable %s in %s\n", name, SVP(tdbp->GetDef()->GetDB())); err: if (s) free_table_share(s); return 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; // Tdbp->SetMode(Mode); } // 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 Cardinality: returns the number of rows in the table. */ /***********************************************************************/ int TDBPRX::Cardinality(PGLOBAL g) { if (Cardinal < 0) { if (InitTable(g)) return 0; Cardinal = Tdbp->Cardinality(g); } // endif MaxSize return Cardinal; } // end of GetMaxSize /***********************************************************************/ /* 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 (InitTable(g)) return true; else if (Mode != MODE_READ && (Read_Only || Tdbp->IsReadOnly())) { strcpy(g->Message, "Cannot modify a read only table"); return true; } // endif tp /*********************************************************************/ /* Check and initialize the subtable columns. */ /*********************************************************************/ for (PCOL cp = Columns; cp; cp = cp->GetNext()) if (((PPRXCOL)cp)->Init(g, Tdbp)) return true; /*********************************************************************/ /* In Update mode, the updated column blocks must be distinct from */ /* the read column blocks. So make a copy of the TDB and allocate */ /* its column blocks in mode write (required by XML tables). */ /*********************************************************************/ if (Mode == MODE_UPDATE) { PTDB utp; if (!(utp= Tdbp->Duplicate(g))) { sprintf(g->Message, MSG(INV_UPDT_TABLE), Tdbp->GetName()); return true; } // endif tp for (PCOL cp = To_SetCols; cp; cp = cp->GetNext()) if (((PPRXCOL)cp)->Init(g, utp)) return true; } else if (Mode == MODE_DELETE) Tdbp->SetNext(Next); /*********************************************************************/ /* Physically open the object table. */ /*********************************************************************/ if (Tdbp->OpenDB(g)) return true; Tdbp->SetNext(NULL); Use = USE_OPEN; 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) { return Tdbp->WriteDB(g); } // end of WriteDB /***********************************************************************/ /* Data Base delete line routine for PROXY access methods. */ /***********************************************************************/ int TDBPRX::DeleteDB(PGLOBAL g, int irc) { return Tdbp->DeleteDB(g, irc); } // 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, PCSZ 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(1)) htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this); } // end of PRXCOL constructor /***********************************************************************/ /* PRXCOL constructor used for copying columns. */ /* tdbp is the pointer to the new table descriptor. */ /***********************************************************************/ PRXCOL::PRXCOL(PRXCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp) { Colp = col1->Colp; To_Val = col1->To_Val; Pseudo = col1->Pseudo; Colnum = col1->Colnum; } // end of PRXCOL copy constructor /***********************************************************************/ /* Convert an UTF-8 name to latin characters. */ /***********************************************************************/ char *PRXCOL::Decode(PGLOBAL g, const char *cnm) { char *buf= (char*)PlugSubAlloc(g, NULL, strlen(cnm) + 1); uint dummy_errors; uint32 len= copy_and_convert(buf, strlen(cnm) + 1, &my_charset_latin1, cnm, strlen(cnm), &my_charset_utf8_general_ci, &dummy_errors); buf[len]= '\0'; return buf; } // end of Decode /***********************************************************************/ /* PRXCOL initialization routine. */ /* Look for the matching column in the object table. */ /***********************************************************************/ bool PRXCOL::Init(PGLOBAL g, PTDB tp) { if (!tp) tp = ((PTDBPRX)To_Tdb)->Tdbp; if (!(Colp = tp->ColDB(g, Name, 0)) && Colnum) Colp = tp->ColDB(g, NULL, Colnum); if (Colp) { MODE mode = To_Tdb->GetMode(); // Needed for MYSQL subtables ((COLBLK*)Colp)->SetName(Decode(g, Colp->GetName())); // May not have been done elsewhere Colp->InitValue(g); To_Val = Colp->GetValue(); if (mode == MODE_INSERT || mode == MODE_UPDATE) if (Colp->SetBuffer(g, Colp->GetValue(), true, false)) return true; // this may be needed by some tables (which?) Colp->SetColUse(ColUse); } else { sprintf(g->Message, MSG(NO_MATCHING_COL), Name, tp->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(2)) 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()); } else { Value->Reset(); // Set null when applicable if (Nullable) Value->SetNull(true); } // endif Colp } // end of ReadColumn /***********************************************************************/ /* WriteColumn: */ /***********************************************************************/ void PRXCOL::WriteColumn(PGLOBAL g) { if (trace(2)) htrc("PRX WriteColumn: name=%s\n", Name); if (Colp) { To_Val->SetValue_pval(Value); Colp->WriteColumn(g); } // endif Colp } // end of WriteColumn /* ---------------------------TDBTBC class --------------------------- */ /***********************************************************************/ /* TDBTBC class constructor. */ /***********************************************************************/ TDBTBC::TDBTBC(PPRXDEF tdp) : TDBCAT(tdp) { Db = (PSZ)tdp->Tablep->GetSchema(); 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