/************* Tabext C++ Functions Source Code File (.CPP) ************/ /* Name: TABEXT.CPP Version 1.0 */ /* */ /* (C) Copyright to the author Olivier BERTRAND 2017 */ /* */ /* This file contains the TBX, TDB and OPJOIN classes functions. */ /***********************************************************************/ /***********************************************************************/ /* Include relevant MariaDB header file. */ /***********************************************************************/ #define MYSQL_SERVER 1 #include "my_global.h" #include "sql_class.h" #include "sql_servers.h" #include "sql_string.h" #if !defined(__WIN__) #include "osutil.h" #endif /***********************************************************************/ /* Include required application header files */ /* global.h is header containing all global Plug declarations. */ /* plgdbsem.h is header containing the DB applic. declarations. */ /* xobject.h is header containing XOBJECT derived classes declares. */ /***********************************************************************/ #include "global.h" #include "plgdbsem.h" #include "xtable.h" #include "tabext.h" #include "ha_connect.h" /* -------------------------- Class CONDFIL -------------------------- */ /***********************************************************************/ /* CONDFIL Constructor. */ /***********************************************************************/ CONDFIL::CONDFIL(uint idx, AMT type) { //Cond = cond; Idx = idx; Type = type; Op = OP_XX; Cmds = NULL; Alist = NULL; All = true; Bd = false; Hv = false; Body = NULL, Having = NULL; } // end of CONDFIL constructor /***********************************************************************/ /* Make and allocate the alias list. */ /***********************************************************************/ int CONDFIL::Init(PGLOBAL g, PHC hc) { PTOS options = hc->GetTableOptionStruct(); char *p, *cn, *cal, *alt = NULL; int rc = RC_OK; bool h; if (options) alt = (char*)GetListOption(g, "Alias", options->oplist, NULL); while (alt) { if (!(p = strchr(alt, '='))) { strcpy(g->Message, "Invalid alias list"); rc = RC_FX; break; } // endif !p cal = alt; // Alias *p++ = 0; if ((h = *p == '*')) { rc = RC_INFO; p++; } // endif h cn = p; // Remote column name if ((alt = strchr(p, ';'))) *alt++ = 0; if (*cn == 0) cn = alt; Alist = new(g) ALIAS(Alist, cn, cal, h); } // endwhile alt return rc; } // end of Init /***********************************************************************/ /* Make and allocate the alias list. */ /***********************************************************************/ const char *CONDFIL::Chk(const char *fln, bool *h) { for (PAL pal = Alist; pal; pal = pal->Next) if (!stricmp(fln, pal->Alias)) { *h = pal->Having; return pal->Name; } // endif fln *h = false; return fln; } // end of Chk /* --------------------------- Class EXTDEF -------------------------- */ /***********************************************************************/ /* EXTDEF Constructor. */ /***********************************************************************/ EXTDEF::EXTDEF(void) { Tabname = Tabschema = Username = Password = Tabcat = Tabtyp = NULL; Colpat = Srcdef = Qchar = Qrystr = Sep = Phpos = NULL; Options = Cto = Qto = Quoted = Maxerr = Maxres = Memory = 0; Scrollable = Xsrc = false; } // end of EXTDEF constructor /***********************************************************************/ /* DefineAM: define specific AM block values from XDB file. */ /***********************************************************************/ bool EXTDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) { Desc = NULL; Tabname = GetStringCatInfo(g, "Name", (Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name); Tabname = GetStringCatInfo(g, "Tabname", Tabname); Tabschema = GetStringCatInfo(g, "Dbname", NULL); Tabschema = GetStringCatInfo(g, "Schema", Tabschema); Tabcat = GetStringCatInfo(g, "Qualifier", NULL); Tabcat = GetStringCatInfo(g, "Catalog", Tabcat); Username = GetStringCatInfo(g, "User", NULL); Password = GetStringCatInfo(g, "Password", NULL); if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL))) Read_Only = true; Qrystr = GetStringCatInfo(g, "Query_String", "?"); Sep = GetStringCatInfo(g, "Separator", NULL); //Alias = GetStringCatInfo(g, "Alias", NULL); Phpos = GetStringCatInfo(g, "Phpos", NULL); Xsrc = GetBoolCatInfo("Execsrc", FALSE); Maxerr = GetIntCatInfo("Maxerr", 0); Maxres = GetIntCatInfo("Maxres", 0); Quoted = GetIntCatInfo("Quoted", 0); Options = 0; Cto = 0; Qto = 0; if ((Scrollable = GetBoolCatInfo("Scrollable", false)) && !Elemt) Elemt = 1; // Cannot merge SQLFetch and SQLExtendedFetch if (Catfunc == FNC_COL) Colpat = GetStringCatInfo(g, "Colpat", NULL); if (Catfunc == FNC_TABLE) Tabtyp = GetStringCatInfo(g, "Tabtype", NULL); // Memory was Boolean, it is now integer if (!(Memory = GetIntCatInfo("Memory", 0))) Memory = GetBoolCatInfo("Memory", false) ? 1 : 0; Pseudo = 2; // FILID is Ok but not ROWID return false; } // end of DefineAM /* ---------------------------TDBEXT class --------------------------- */ /***********************************************************************/ /* Implementation of the TDBEXT class. */ /***********************************************************************/ TDBEXT::TDBEXT(EXTDEF *tdp) : TDB(tdp) { Qrp = NULL; if (tdp) { TableName = tdp->Tabname; Schema = tdp->Tabschema; User = tdp->Username; Pwd = tdp->Password; Catalog = tdp->Tabcat; Srcdef = tdp->Srcdef; Qrystr = tdp->Qrystr; Sep = tdp->GetSep(); Options = tdp->Options; Cto = tdp->Cto; Qto = tdp->Qto; Quoted = MY_MAX(0, tdp->GetQuoted()); Rows = tdp->GetElemt(); Memory = tdp->Memory; Scrollable = tdp->Scrollable; } else { TableName = NULL; Schema = NULL; User = NULL; Pwd = NULL; Catalog = NULL; Srcdef = NULL; Qrystr = NULL; Sep = 0; Options = 0; Cto = 0; Qto = 0; Quoted = 0; Rows = 0; Memory = 0; Scrollable = false; } // endif tdp Quote = NULL; Query = NULL; Count = NULL; //Where = NULL; MulConn = NULL; DBQ = NULL; Qrp = NULL; Fpos = 0; Curpos = 0; AftRows = 0; CurNum = 0; Rbuf = 0; BufSize = 0; Nparm = 0; Ncol = 0; Placed = false; } // end of TDBEXT constructor TDBEXT::TDBEXT(PTDBEXT tdbp) : TDB(tdbp) { Qrp = tdbp->Qrp; TableName = tdbp->TableName; Schema = tdbp->Schema; User = tdbp->User; Pwd = tdbp->Pwd; Catalog = tdbp->Catalog; Srcdef = tdbp->Srcdef; Qrystr = tdbp->Qrystr; Sep = tdbp->Sep; Options = tdbp->Options; Cto = tdbp->Cto; Qto = tdbp->Qto; Quoted = tdbp->Quoted; Rows = tdbp->Rows; Memory = tdbp->Memory; Scrollable = tdbp->Scrollable; Quote = tdbp->Quote; Query = tdbp->Query; Count = tdbp->Count; //Where = tdbp->Where; MulConn = tdbp->MulConn; DBQ = tdbp->DBQ; Fpos = 0; Curpos = 0; AftRows = 0; CurNum = 0; Rbuf = 0; BufSize = tdbp->BufSize; Nparm = tdbp->Nparm; Ncol = tdbp->Ncol; Placed = false; } // end of TDBEXT copy constructor /******************************************************************/ /* Convert an UTF-8 string to latin characters. */ /******************************************************************/ int TDBEXT::Decode(PCSZ txt, char *buf, size_t n) { uint dummy_errors; uint32 len = copy_and_convert(buf, n, &my_charset_latin1, txt, strlen(txt), &my_charset_utf8_general_ci, &dummy_errors); buf[len] = '\0'; return 0; } // end of Decode /***********************************************************************/ /* MakeSrcdef: make the SQL statement from SRDEF option. */ /***********************************************************************/ bool TDBEXT::MakeSrcdef(PGLOBAL g) { char *catp = strstr(Srcdef, "%s"); if (catp) { char *fil1, *fil2; PCSZ ph = ((EXTDEF*)To_Def)->Phpos; if (!ph) ph = (strstr(catp + 2, "%s")) ? "WH" : "W"; if (stricmp(ph, "H")) { fil1 = (To_CondFil && *To_CondFil->Body) ? To_CondFil->Body : PlugDup(g, "1=1"); } // endif ph if (stricmp(ph, "W")) { fil2 = (To_CondFil && To_CondFil->Having && *To_CondFil->Having) ? To_CondFil->Having : PlugDup(g, "1=1"); } // endif ph if (!stricmp(ph, "W")) { Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil1)); Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil1)); } else if (!stricmp(ph, "WH")) { Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil1) + strlen(fil2)); Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil1, fil2)); } else if (!stricmp(ph, "H")) { Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil2)); Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil2)); } else if (!stricmp(ph, "HW")) { Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil1) + strlen(fil2)); Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil2, fil1)); } else { strcpy(g->Message, "MakeSQL: Wrong place holders specification"); return true; } // endif's ph } else Query = new(g)STRING(g, 0, Srcdef); return false; } // end of MakeSrcdef /***********************************************************************/ /* MakeSQL: make the SQL statement use with remote connection. */ /* TODO: when implementing remote filtering, column only used in */ /* local filter should be removed from column list. */ /***********************************************************************/ bool TDBEXT::MakeSQL(PGLOBAL g, bool cnt) { PCSZ schmp = NULL; char *catp = NULL, buf[NAM_LEN * 3]; int len; bool first = true; PTABLE tablep = To_Table; PCOL colp; if (Srcdef) return MakeSrcdef(g); // Allocate the string used to contain the Query Query = new(g)STRING(g, 1023, "SELECT "); if (!cnt) { if (Columns) { // Normal SQL statement to retrieve results for (colp = Columns; colp; colp = colp->GetNext()) if (!colp->IsSpecial()) { if (!first) Query->Append(", "); else first = false; // Column name can be encoded in UTF-8 Decode(colp->GetName(), buf, sizeof(buf)); if (Quote) { // Put column name between identifier quotes in case in contains blanks Query->Append(Quote); Query->Append(buf); Query->Append(Quote); } else Query->Append(buf); ((PEXTCOL)colp)->SetRank(++Ncol); } // endif colp } else // !Columns can occur for queries such that sql count(*) from... // for which we will count the rows from sql * from... Query->Append('*'); } else // SQL statement used to retrieve the size of the result Query->Append("count(*)"); Query->Append(" FROM "); if (Catalog && *Catalog) catp = Catalog; //if (tablep->GetSchema()) // schmp = (char*)tablep->GetSchema(); //else if (Schema && *Schema) schmp = Schema; if (catp) { Query->Append(catp); if (schmp) { Query->Append('.'); Query->Append(schmp); } // endif schmp Query->Append('.'); } else if (schmp) { Query->Append(schmp); Query->Append('.'); } // endif schmp // Table name can be encoded in UTF-8 Decode(TableName, buf, sizeof(buf)); if (Quote) { // Put table name between identifier quotes in case in contains blanks Query->Append(Quote); Query->Append(buf); Query->Append(Quote); } else Query->Append(buf); len = Query->GetLength(); if (To_CondFil) { if (Mode == MODE_READ) { Query->Append(" WHERE "); Query->Append(To_CondFil->Body); len = Query->GetLength() + 1; } else len += (strlen(To_CondFil->Body) + 256); } else len += ((Mode == MODE_READX) ? 256 : 1); if (Query->IsTruncated()) { strcpy(g->Message, "MakeSQL: Out of memory"); return true; } else Query->Resize(len); if (trace(33)) htrc("Query=%s\n", Query->GetStr()); return false; } // end of MakeSQL /***********************************************************************/ /* MakeCommand: make the Update or Delete statement to send to the */ /* MySQL server. Limited to remote values and filtering. */ /***********************************************************************/ bool TDBEXT::MakeCommand(PGLOBAL g) { PCSZ schmp = NULL; char *p, *stmt, name[132], *body = NULL; char *qrystr = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 1); bool qtd = Quoted > 0; char q = qtd ? *Quote : ' '; int i = 0, k = 0; // Make a lower case copy of the originale query and change // back ticks to the data source identifier quoting character do { qrystr[i] = (Qrystr[i] == '`') ? q : tolower(Qrystr[i]); } while (Qrystr[i++]); if (To_CondFil && (p = strstr(qrystr, " where "))) { p[7] = 0; // Remove where clause Qrystr[(p - qrystr) + 7] = 0; body = To_CondFil->Body; stmt = (char*)PlugSubAlloc(g, NULL, strlen(qrystr) + strlen(body) + 64); } else stmt = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64); // Check whether the table name is equal to a keyword // If so, it must be quoted in the original query strlwr(strcat(strcat(strcpy(name, " "), Name), " ")); if (strstr(" update delete low_priority ignore quick from ", name)) { if (Quote) { strlwr(strcat(strcat(strcpy(name, Quote), Name), Quote)); k += 2; } else { strcpy(g->Message, "Quoted must be specified"); return true; } // endif Quote } else strlwr(strcpy(name, Name)); // Not a keyword if ((p = strstr(qrystr, name))) { for (i = 0; i < p - qrystr; i++) stmt[i] = (Qrystr[i] == '`') ? q : Qrystr[i]; stmt[i] = 0; k += i + (int)strlen(Name); if (Schema && *Schema) schmp = Schema; if (qtd && *(p - 1) == ' ') { if (schmp) strcat(strcat(stmt, schmp), "."); strcat(strcat(strcat(stmt, Quote), TableName), Quote); } else { if (schmp) { if (qtd && *(p - 1) != ' ') { stmt[i - 1] = 0; strcat(strcat(strcat(stmt, schmp), "."), Quote); } else strcat(strcat(stmt, schmp), "."); } // endif schmp strcat(stmt, TableName); } // endif's i = (int)strlen(stmt); do { stmt[i++] = (Qrystr[k] == '`') ? q : Qrystr[k]; } while (Qrystr[k++]); if (body) strcat(stmt, body); } else { sprintf(g->Message, "Cannot use this %s command", (Mode == MODE_UPDATE) ? "UPDATE" : "DELETE"); return true; } // endif p if (trace(33)) htrc("Command=%s\n", stmt); Query = new(g)STRING(g, 0, stmt); return (!Query->GetSize()); } // end of MakeCommand /***********************************************************************/ /* GetRecpos: return the position of last read record. */ /***********************************************************************/ int TDBEXT::GetRecpos(void) { return Fpos; } // end of GetRecpos /***********************************************************************/ /* ODBC GetMaxSize: returns table size estimate in number of lines. */ /***********************************************************************/ int TDBEXT::GetMaxSize(PGLOBAL g) { if (MaxSize < 0) { if (Mode == MODE_DELETE) // Return 0 in mode DELETE in case of delete all. MaxSize = 0; else if (!Cardinality(NULL)) MaxSize = 10; // To make MySQL happy else if ((MaxSize = Cardinality(g)) < 0) MaxSize = 12; // So we can see an error occurred } // endif MaxSize return MaxSize; } // end of GetMaxSize /***********************************************************************/ /* Return max size value. */ /***********************************************************************/ int TDBEXT::GetProgMax(PGLOBAL g) { return GetMaxSize(g); } // end of GetProgMax /* ---------------------------EXTCOL class --------------------------- */ /***********************************************************************/ /* EXTCOL public constructor. */ /***********************************************************************/ EXTCOL::EXTCOL(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 if (trace(1)) htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this); // Set additional remote access method information for column. Crp = NULL; Long = Precision; To_Val = NULL; Bufp = NULL; Blkp = NULL; Rank = 0; // Not known yet } // end of JDBCCOL constructor /***********************************************************************/ /* EXTCOL private constructor. */ /***********************************************************************/ EXTCOL::EXTCOL(void) : COLBLK() { Crp = NULL; Buf_Type = TYPE_INT; // This is a count(*) column // Set additional Dos access method information for column. Long = sizeof(int); To_Val = NULL; Bufp = NULL; Blkp = NULL; Rank = 1; } // end of EXTCOL constructor /***********************************************************************/ /* EXTCOL constructor used for copying columns. */ /* tdbp is the pointer to the new table descriptor. */ /***********************************************************************/ EXTCOL::EXTCOL(PEXTCOL col1, PTDB tdbp) : COLBLK(col1, tdbp) { Crp = col1->Crp; Long = col1->Long; To_Val = col1->To_Val; Bufp = col1->Bufp; Blkp = col1->Blkp; Rank = col1->Rank; } // end of JDBCCOL copy constructor /***********************************************************************/ /* SetBuffer: prepare a column block for write operation. */ /***********************************************************************/ bool EXTCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check) { if (!(To_Val = value)) { sprintf(g->Message, MSG(VALUE_ERROR), Name); return true; } else if (Buf_Type == value->GetType()) { // Values are of the (good) column type if (Buf_Type == TYPE_DATE) { // If any of the date values is formatted // output format must be set for the receiving table if (GetDomain() || ((DTVAL *)value)->IsFormatted()) goto newval; // This will make a new value; } else if (Buf_Type == TYPE_DOUBLE) // Float values must be written with the correct (column) precision // Note: maybe this should be forced by ShowValue instead of this ? value->SetPrec(GetScale()); Value = value; // Directly access the external value } else { // Values are not of the (good) column type if (check) { sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name, GetTypeName(Buf_Type), GetTypeName(value->GetType())); return true; } // endif check newval: if (InitValue(g)) // Allocate the matching value block return true; } // endif's Value, Buf_Type // Because Colblk's have been made from a copy of the original TDB in // case of Update, we must reset them to point to the original one. if (To_Tdb->GetOrig()) To_Tdb = (PTDB)To_Tdb->GetOrig(); // Set the Column Status = (ok) ? BUF_EMPTY : BUF_NO; return false; } // end of SetBuffer