diff options
Diffstat (limited to 'storage/connect/tabext.cpp')
-rw-r--r-- | storage/connect/tabext.cpp | 720 |
1 files changed, 720 insertions, 0 deletions
diff --git a/storage/connect/tabext.cpp b/storage/connect/tabext.cpp new file mode 100644 index 00000000..212d27f0 --- /dev/null +++ b/storage/connect/tabext.cpp @@ -0,0 +1,720 @@ +/************* Tabext C++ Functions Source Code File (.CPP) ************/ +/* Name: TABEXT.CPP Version 1.1 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2017 - 2019 */ +/* */ +/* 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(_WIN32) +#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) +{ + if (g->Createas) { + strcpy(g->Message, + "Multiple-table UPDATE/DELETE commands are not supported"); + return true; + } // endif multi + + 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_utf8mb3_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 = 0, *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; + 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 + +/***********************************************************************/ +/* Remove the NAME_CONST functions that are added by procedures. */ +/***********************************************************************/ +void TDBEXT::RemoveConst(PGLOBAL g, char *stmt) +{ + char *p, *p2; + char val[1025], nval[1025]; + int n, nc; + + while ((p = strstr(stmt, "NAME_CONST"))) + { + if ((n = sscanf(p, "%*[^,],%1024[^)])%n", val, &nc))) { + if (trace(33)) + htrc("p=%s\nn=%d val=%s nc=%d\n", p, n, val, nc); + + *p = 0; + + if ((p2 = strstr(val, "'"))) { + if ((n = sscanf(p2, "%*['\\]%1024[^'\\]", nval))) { + if (trace(33)) + htrc("p2=%s\nn=%d nval=%s\n", p2, n, nval); + + strcat(strcat(strcat(strcat(stmt, "'"), nval), "'"), p + nc); + } else + break; + + } else + strcat(strcat(strcat(strcat(stmt, "("), val), ")"), p + nc); + + if (trace(33)) + htrc("stmt=%s\n", stmt); + + } else + break; + } + return; +} // end of RemoveConst + +/***********************************************************************/ +/* 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++]); + + RemoveConst(g, stmt); + + 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 + |