diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:00:34 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:00:34 +0000 |
commit | 3f619478f796eddbba6e39502fe941b285dd97b1 (patch) | |
tree | e2c7b5777f728320e5b5542b6213fd3591ba51e2 /storage/connect/tabxml.cpp | |
parent | Initial commit. (diff) | |
download | mariadb-3f619478f796eddbba6e39502fe941b285dd97b1.tar.xz mariadb-3f619478f796eddbba6e39502fe941b285dd97b1.zip |
Adding upstream version 1:10.11.6.upstream/1%10.11.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'storage/connect/tabxml.cpp')
-rw-r--r-- | storage/connect/tabxml.cpp | 2300 |
1 files changed, 2300 insertions, 0 deletions
diff --git a/storage/connect/tabxml.cpp b/storage/connect/tabxml.cpp new file mode 100644 index 00000000..d5302759 --- /dev/null +++ b/storage/connect/tabxml.cpp @@ -0,0 +1,2300 @@ +/************* Tabxml C++ Program Source Code File (.CPP) **************/ +/* PROGRAM NAME: TABXML */ +/* ------------- */ +/* Version 3.0 */ +/* */ +/* Author Olivier BERTRAND 2007 - 2020 */ +/* */ +/* This program are the XML tables classes using MS-DOM or libxml2. */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include required compiler header files. */ +/***********************************************************************/ +#include "my_global.h" +#include <m_string.h> +#include <fcntl.h> +#if defined(_WIN32) +#include <io.h> +#include <winsock2.h> +//#include <windows.h> +#include <comdef.h> +#else // !_WIN32 +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <unistd.h> +//#include <ctype.h> +#include "osutil.h" +#define _O_RDONLY O_RDONLY +#endif // !_WIN32 +#include "resource.h" // for IDS_COLUMNS + +#define INCLUDE_TDBXML +#define NODE_TYPE_LIST + +/***********************************************************************/ +/* Include application header files: */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB application declarations. */ +/* tabdos.h is header containing the TABDOS class declarations. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +//#include "reldef.h" +#include "xtable.h" +#include "colblk.h" +#include "mycat.h" +#include "xindex.h" +#include "plgxml.h" +#include "tabxml.h" +#include "tabmul.h" + +extern "C" char version[]; + +#if defined(_WIN32) && defined(DOMDOC_SUPPORT) +#define XMLSUP "MS-DOM" +#else // !_WIN32 +#define XMLSUP "libxml2" +#endif // !_WIN32 + +#define TYPE_UNKNOWN 12 /* Must be greater than other types */ +#define XLEN(M) sizeof(M) - strlen(M) - 1 /* To avoid overflow*/ + +int GetDefaultDepth(void); + +/***********************************************************************/ +/* Class and structure used by XMLColumns. */ +/***********************************************************************/ +typedef class XMCOL *PXCL; + +class XMCOL : public BLOCK { + public: + // Constructors + XMCOL(void) {Next = NULL; + Name[0] = 0; + Fmt = NULL; + Type = 1; + Len = Scale = 0; + Cbn = false; + Found = true;} + XMCOL(PGLOBAL g, PXCL xp, char *fmt, int i) { + Next = NULL; + strcpy(Name, xp->Name); + Fmt = (*fmt) ? PlugDup(g, fmt) : NULL; + Type = xp->Type; + Len = xp->Len; + Scale = xp->Scale; + Cbn = (xp->Cbn || i > 1); + Found = true;} + + // Members + PXCL Next; + char Name[64]; + char *Fmt; + int Type; + int Len; + int Scale; + bool Cbn; + bool Found; +}; // end of class XMCOL + +typedef struct LVL { + PXNODE pn; + PXLIST nl; + PXATTR atp; + bool b; + long k; + int m, n; +} *PLVL; + +/***********************************************************************/ +/* XMLColumns: construct the result blocks containing the description */ +/* of all the columns of a table contained inside an XML file. */ +/***********************************************************************/ +PQRYRES XMLColumns(PGLOBAL g, char *db, char *tab, PTOS topt, bool info) +{ + static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING, TYPE_INT, + TYPE_INT, TYPE_SHORT, TYPE_SHORT, TYPE_STRING}; + static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME, FLD_PREC, + FLD_LENGTH, FLD_SCALE, FLD_NULL, FLD_FORMAT}; + static unsigned int length[] = {0, 6, 8, 10, 10, 6, 6, 0}; + char colname[65], fmt[129], buf[512]; + int i, j, lvl, n = 0; + int ncol = sizeof(buftyp) / sizeof(int); + bool ok = true; + PCSZ fn, op; + PXCL xcol, xcp, fxcp = NULL, pxcp = NULL; + PLVL *lvlp, vp; + PXNODE node = NULL; + PXMLDEF tdp; + PTDBXML txmp; + PQRYRES qrp; + PCOLRES crp; + + if (info) { + length[0] = 128; + length[7] = 256; + goto skipit; + } // endif info + + if (GetIntegerTableOption(g, topt, "Multiple", 0)) { + snprintf(g->Message, sizeof(g->Message), "Cannot find column definition for multiple table"); + return NULL; + } // endif Multiple + + /*********************************************************************/ + /* Open the input file. */ + /*********************************************************************/ + if (!(fn = GetStringTableOption(g, topt, "Filename", NULL))) { + if (topt->http) // REST table can have default filename + fn = GetStringTableOption(g, topt, "Subtype", NULL); + + if (!fn) { + snprintf(g->Message, sizeof(g->Message), MSG(MISSING_FNAME)); + return NULL; + } else + topt->subtype = NULL; + + } // endif fn + + lvl = GetIntegerTableOption(g, topt, "Level", GetDefaultDepth()); + lvl = GetIntegerTableOption(g, topt, "Depth", lvl); + lvl = (lvl < 0) ? 0 : (lvl > 16) ? 16 : lvl; + + if (trace(1)) + htrc("File %s lvl=%d\n", topt->filename, lvl); + + tdp = new(g) XMLDEF; + tdp->Fn = fn; + + if (!(tdp->Database = SetPath(g, db))) + return NULL; + + tdp->Tabname = tab; + tdp->Tabname = (char*)GetStringTableOption(g, topt, "Tabname", tab); + tdp->Rowname = (char*)GetStringTableOption(g, topt, "Rownode", NULL); + tdp->Zipped = GetBooleanTableOption(g, topt, "Zipped", false); + tdp->Entry = GetStringTableOption(g, topt, "Entry", NULL); + tdp->Skip = GetBooleanTableOption(g, topt, "Skipnull", false); + + if (!(op = GetStringTableOption(g, topt, "Xmlsup", NULL))) +#if defined(_WIN32) + tdp->Usedom = true; +#else // !_WIN32 + tdp->Usedom = false; +#endif // !_WIN32 + else + tdp->Usedom = (toupper(*op) == 'M' || toupper(*op) == 'D'); + + txmp = new(g) TDBXML(tdp); + + if (txmp->Initialize(g)) + goto err; + + xcol = new(g) XMCOL; + colname[64] = 0; + fmt[128] = 0; + lvlp = (PLVL*)PlugSubAlloc(g, NULL, sizeof(PLVL) * (lvl + 1)); + + for (j = 0; j <= lvl; j++) + lvlp[j] = (PLVL)PlugSubAlloc(g, NULL, sizeof(LVL)); + + /*********************************************************************/ + /* Analyse the XML tree and define columns. */ + /*********************************************************************/ + for (i = 1; ; i++) { + // Get next row + switch (txmp->ReadDB(g)) { + case RC_EF: + vp = NULL; + break; + case RC_FX: + goto err; + default: + vp = lvlp[0]; + vp->pn = txmp->RowNode; + vp->atp = vp->pn->GetAttribute(g, NULL); + vp->nl = vp->pn->GetChildElements(g); + vp->b = true; + vp->k = 0; + vp->m = vp->n = 0; + j = 0; + } // endswitch ReadDB + + if (!vp) + break; + + while (true) { + if (!vp->atp && + !(node = (vp->nl) ? vp->nl->GetItem(g, vp->k++, tdp->Usedom ? + node : NULL) + : NULL)) + { + if (j) { + vp = lvlp[--j]; + + if (!tdp->Usedom) // nl was destroyed + vp->nl = vp->pn->GetChildElements(g); + + if (!lvlp[j+1]->b) { + vp->k--; + ok = false; + } // endif b + + continue; + } else + break; + } + xcol->Name[vp->n] = 0; + fmt[vp->m] = 0; + + more: + if (vp->atp) { + size_t z = sizeof(colname) - 1; + size_t xlen= strlen(xcol->Name); + strmake(colname, vp->atp->GetName(g), z); + strmake(xcol->Name + xlen, colname, + sizeof(xcol->Name) - 1 - xlen); + + switch (vp->atp->GetText(g, buf, sizeof(buf))) { + case RC_INFO: + PushWarning(g, txmp); + /* falls through */ + case RC_OK: + strncat(fmt, "@", XLEN(fmt)); + break; + default: + goto err; + } // enswitch rc + + if (j) + strncat(fmt, colname, XLEN(fmt)); + + } else { + size_t xlen; + if (tdp->Usedom && node->GetType() != 1) + continue; + + xlen= strlen(xcol->Name); + strmake(colname, node->GetName(g), sizeof(colname)-1); + strmake(xcol->Name + xlen, colname, sizeof(xcol->Name) - 1 - xlen); + + if (j) + strncat(fmt, colname, XLEN(fmt)); + + if (j < lvl && ok) { + vp = lvlp[j+1]; + vp->k = 0; + vp->pn = node; + vp->atp = node->GetAttribute(g, NULL); + vp->nl = node->GetChildElements(g); + + if (tdp->Usedom && vp->nl->GetLength() == 1) { + node = vp->nl->GetItem(g, 0, node); + vp->b = (node->GetType() == 1); // Must be ab element + } else + vp->b = (vp->nl && vp->nl->GetLength()); + + if (vp->atp || vp->b) { + if (!vp->atp) + node = vp->nl->GetItem(g, vp->k++, tdp->Usedom ? node : NULL); + + if (!j) + strncat(fmt, colname, XLEN(fmt)); + + strncat(fmt, "/", XLEN(fmt)); + strncat(xcol->Name, "_", XLEN(xcol->Name)); + j++; + vp->n = (int)strlen(xcol->Name); + vp->m = (int)strlen(fmt); + goto more; + } else { + vp = lvlp[j]; + + if (!tdp->Usedom) // nl was destroyed + vp->nl = vp->pn->GetChildElements(g); + + } // endif vp + + } else + ok = true; + + switch (node->GetContent(g, buf, sizeof(buf))) { + case RC_INFO: + PushWarning(g, txmp); + /* falls through */ + case RC_OK: + xcol->Cbn = !strlen(buf); + break; + default: + goto err; + } // enswitch rc + + } // endif atp; + + xcol->Len = strlen(buf); + + // Check whether this column was already found + for (xcp = fxcp; xcp; xcp = xcp->Next) + if (!strcmp(xcol->Name, xcp->Name)) + break; + + if (xcp) { + if (xcp->Type != xcol->Type) + xcp->Type = TYPE_STRING; + + if (*fmt && (!xcp->Fmt || strlen(xcp->Fmt) < strlen(fmt))) { + xcp->Fmt = PlugDup(g, fmt); + length[7] = MY_MAX(length[7], strlen(fmt)); + } // endif *fmt + + xcp->Len = MY_MAX(xcp->Len, xcol->Len); + xcp->Scale = MY_MAX(xcp->Scale, xcol->Scale); + xcp->Cbn |= (xcol->Cbn || !xcol->Len); + xcp->Found = true; + } else if(xcol->Len || !tdp->Skip) { + // New column + xcp = new(g) XMCOL(g, xcol, fmt, i); + length[0] = MY_MAX(length[0], strlen(xcol->Name)); + length[7] = MY_MAX(length[7], strlen(fmt)); + + if (pxcp) { + xcp->Next = pxcp->Next; + pxcp->Next = xcp; + } else + fxcp = xcp; + + n++; + } // endif xcp + + if (xcp) + pxcp = xcp; + + if (vp->atp) + vp->atp = vp->atp->GetNext(g); + + } // endwhile + + // Missing column can be null + for (xcp = fxcp; xcp; xcp = xcp->Next) { + xcp->Cbn |= !xcp->Found; + xcp->Found = false; + } // endfor xcp + + } // endor i + + txmp->CloseDB(g); + + skipit: + if (trace(1)) + htrc("XMLColumns: n=%d len=%d\n", n, length[0]); + + /*********************************************************************/ + /* Allocate the structures used to refer to the result set. */ + /*********************************************************************/ + qrp = PlgAllocResult(g, ncol, n, IDS_COLUMNS + 3, + buftyp, fldtyp, length, false, false); + + crp = qrp->Colresp->Next->Next->Next->Next->Next->Next; + crp->Name = "Nullable"; + crp->Next->Name = "Xpath"; + + if (info || !qrp) + return qrp; + + qrp->Nblin = n; + + /*********************************************************************/ + /* Now get the results into blocks. */ + /*********************************************************************/ + for (i = 0, xcp = fxcp; xcp; i++, xcp = xcp->Next) { + if (xcp->Type == TYPE_UNKNOWN) // Void column + xcp->Type = TYPE_STRING; + + crp = qrp->Colresp; // Column Name + crp->Kdata->SetValue(xcp->Name, i); + crp = crp->Next; // Data Type + crp->Kdata->SetValue(xcp->Type, i); + crp = crp->Next; // Type Name + crp->Kdata->SetValue(GetTypeName(xcp->Type), i); + crp = crp->Next; // Precision + crp->Kdata->SetValue(xcp->Len, i); + crp = crp->Next; // Length + crp->Kdata->SetValue(xcp->Len, i); + crp = crp->Next; // Scale (precision) + crp->Kdata->SetValue(xcp->Scale, i); + crp = crp->Next; // Nullable + crp->Kdata->SetValue(xcp->Cbn ? 1 : 0, i); + crp = crp->Next; // Field format + + if (crp->Kdata) + crp->Kdata->SetValue(xcp->Fmt, i); + + } // endfor i + + /*********************************************************************/ + /* Return the result pointer. */ + /*********************************************************************/ + return qrp; + +err: + txmp->CloseDB(g); + return NULL; + } // end of XMLColumns + +/* -------------- Implementation of the XMLDEF class ---------------- */ + +/***********************************************************************/ +/* Constructor. */ +/***********************************************************************/ +XMLDEF::XMLDEF(void) + { + Pseudo = 3; + Fn = NULL; + Encoding = NULL; + Tabname = NULL; + Rowname = NULL; + Colname = NULL; + Mulnode = NULL; + XmlDB = NULL; + Nslist = NULL; + DefNs = NULL; + Attrib = NULL; + Hdattr = NULL; + Entry = NULL; + Coltype = 1; + Limit = 0; + Header = 0; + Xpand = false; + Usedom = false; + Zipped = false; + Mulentries = false; + Skip = false; + } // end of XMLDEF constructor + +/***********************************************************************/ +/* DefineAM: define specific AM block values from XDB file. */ +/***********************************************************************/ +bool XMLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) + { + PCSZ defrow, defcol; + char buf[10]; + + Fn = GetStringCatInfo(g, "Filename", NULL); + Encoding = GetStringCatInfo(g, "Encoding", "UTF-8"); + + if (*Fn == '?') { + snprintf(g->Message, sizeof(g->Message), MSG(MISSING_FNAME)); + return true; + } // endif fn + + if ((signed)GetIntCatInfo("Flag", -1) != -1) { + snprintf(g->Message, sizeof(g->Message), MSG(DEPREC_FLAG)); + return true; + } // endif flag + + defrow = defcol = NULL; + GetCharCatInfo("Coltype", "", buf, sizeof(buf)); + + switch (toupper(*buf)) { + case 'A': // Attribute + case '@': + case '0': + Coltype = 0; + break; + case '\0': // Default + case 'T': // Tag + case 'N': // Node + case '1': + Coltype = 1; + break; + case 'C': // Column + case 'P': // Position + case 'H': // HTML + case '2': + Coltype = 2; + defrow = "TR"; + defcol = "TD"; + break; + default: + snprintf(g->Message, sizeof(g->Message), MSG(INV_COL_TYPE), buf); + return true; + } // endswitch typname + + Tabname = GetStringCatInfo(g, "Name", Name); // Deprecated + Tabname = GetStringCatInfo(g, "Table_name", Tabname); // Deprecated + Tabname = GetStringCatInfo(g, "Tabname", Tabname); + Rowname = GetStringCatInfo(g, "Rownode", defrow); + Colname = GetStringCatInfo(g, "Colnode", defcol); + Mulnode = GetStringCatInfo(g, "Mulnode", NULL); + XmlDB = GetStringCatInfo(g, "XmlDB", NULL); + Nslist = GetStringCatInfo(g, "Nslist", NULL); + DefNs = GetStringCatInfo(g, "DefNs", NULL); + Limit = GetIntCatInfo("Limit", 50); + Xpand = GetBoolCatInfo("Expand", false); + Header = GetIntCatInfo("Header", 0); + GetCharCatInfo("Xmlsup", "*", buf, sizeof(buf)); + + // Note that if no support is specified, the default is MS-DOM + // on Windows and libxml2 otherwise + if (*buf == '*') +#if defined(_WIN32) + Usedom = true; +#else // !_WIN32 + Usedom = false; +#endif // !_WIN32 + else + Usedom = (toupper(*buf) == 'M' || toupper(*buf) == 'D'); + + // Get eventual table node attribute + Attrib = GetStringCatInfo(g, "Attribute", NULL); + Hdattr = GetStringCatInfo(g, "HeadAttr", NULL); + + // Specific for zipped files + if ((Zipped = GetBoolCatInfo("Zipped", false))) + Mulentries = ((Entry = GetStringCatInfo(g, "Entry", NULL))) + ? strchr(Entry, '*') || strchr(Entry, '?') + : GetBoolCatInfo("Mulentries", false); + + return false; + } // end of DefineAM + +/***********************************************************************/ +/* GetTable: makes a new TDB of the proper type. */ +/***********************************************************************/ +PTDB XMLDEF::GetTable(PGLOBAL g, MODE m) + { + if (Catfunc == FNC_COL) + return new(g) TDBXCT(this); + + if (Zipped && !(m == MODE_READ || m == MODE_ANY)) { + snprintf(g->Message, sizeof(g->Message), "ZIpped XML tables are read only"); + return NULL; + } // endif Zipped + + PTDBASE tdbp = new(g) TDBXML(this); + + if (Multiple) + tdbp = new(g) TDBMUL(tdbp); + + return tdbp; + } // end of GetTable + +/* ------------------------- TDBXML Class ---------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBXML constuctor. */ +/***********************************************************************/ +TDBXML::TDBXML(PXMLDEF tdp) : TDBASE(tdp) + { + Docp = NULL; + Root = NULL; + Curp = NULL; + DBnode = NULL; + TabNode = NULL; + RowNode = NULL; + ColNode = NULL; + Nlist = NULL; + Clist = NULL; + To_Xb = NULL; + Colp = NULL; + Xfile = tdp->Fn; + Enc = tdp->Encoding; + Tabname = tdp->Tabname; +#if 0 // why all these? + Rowname = (tdp->Rowname) ? tdp->Rowname : NULL; + Colname = (tdp->Colname) ? tdp->Colname : NULL; + Mulnode = (tdp->Mulnode) ? tdp->Mulnode : NULL; + XmlDB = (tdp->XmlDB) ? tdp->XmlDB : NULL; + Nslist = (tdp->Nslist) ? tdp->Nslist : NULL; + DefNs = (tdp->DefNs) ? tdp->DefNs : NULL; + Attrib = (tdp->Attrib) ? tdp->Attrib : NULL; + Hdattr = (tdp->Hdattr) ? tdp->Hdattr : NULL; +#endif // 0 + Rowname = tdp->Rowname; + Colname = tdp->Colname; + Mulnode = tdp->Mulnode; + XmlDB = tdp->XmlDB; + Nslist = tdp->Nslist; + DefNs = tdp->DefNs; + Attrib = tdp->Attrib; + Hdattr = tdp->Hdattr; + Entry = tdp->Entry; + Coltype = tdp->Coltype; + Limit = tdp->Limit; + Xpand = tdp->Xpand; + Zipped = tdp->Zipped; + Mulentries = tdp->Mulentries; + Changed = false; + Checked = false; + NextSame = false; + NewRow = false; + Hasnod = false; + Write = false; + Bufdone = false; + Nodedone = false; + Void = false; + Usedom = tdp->Usedom; + Header = tdp->Header; + Multiple = tdp->Multiple; + Nrow = -1; + Irow = Header - 1; + Nsub = 0; + N = 0; + } // end of TDBXML constructor + +TDBXML::TDBXML(PTDBXML tdbp) : TDBASE(tdbp) + { + Docp = tdbp->Docp; + Root = tdbp->Root; + Curp = tdbp->Curp; + DBnode = tdbp->DBnode; + TabNode = tdbp->TabNode; + RowNode = tdbp->RowNode; + ColNode = tdbp->ColNode; + Nlist = tdbp->Nlist; + Clist = tdbp->Clist; + To_Xb = tdbp->To_Xb; + Colp = tdbp->Colp; + Xfile = tdbp->Xfile; + Enc = tdbp->Enc; + Tabname = tdbp->Tabname; + Rowname = tdbp->Rowname; + Colname = tdbp->Colname; + Mulnode = tdbp->Mulnode; + XmlDB = tdbp->XmlDB; + Nslist = tdbp->Nslist; + DefNs = tdbp->DefNs; + Attrib = tdbp->Attrib; + Hdattr = tdbp->Hdattr; + Entry = tdbp->Entry; + Coltype = tdbp->Coltype; + Limit = tdbp->Limit; + Xpand = tdbp->Xpand; + Zipped = tdbp->Zipped; + Mulentries = tdbp->Mulentries; + Changed = tdbp->Changed; + Checked = tdbp->Checked; + NextSame = tdbp->NextSame; + NewRow = tdbp->NewRow; + Hasnod = tdbp->Hasnod; + Write = tdbp->Write; + Void = tdbp->Void; + Usedom = tdbp->Usedom; + Header = tdbp->Header; + Multiple = tdbp->Multiple; + Nrow = tdbp->Nrow; + Irow = tdbp->Irow; + Nsub = tdbp->Nsub; + N = tdbp->N; + } // end of TDBXML copy constructor + +// Used for update +PTDB TDBXML::Clone(PTABS t) + { + PTDB tp; + PXMLCOL cp1, cp2; + PGLOBAL g = t->G; + + tp = new(g) TDBXML(this); + + for (cp1 = (PXMLCOL)Columns; cp1; cp1 = (PXMLCOL)cp1->GetNext()) { + cp2 = new(g) XMLCOL(cp1, tp); // Make a copy + NewPointer(t, cp1, cp2); + } // endfor cp1 + + return tp; + } // end of Clone + +/***********************************************************************/ +/* Must not be in tabxml.h because of OEM tables */ +/***********************************************************************/ +const CHARSET_INFO *TDBXML::data_charset() +{ + return &my_charset_utf8mb3_general_ci; +} // end of data_charset + +/***********************************************************************/ +/* Allocate XML column description block. */ +/***********************************************************************/ +PCOL TDBXML::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) + { + if (trace(1)) + htrc("TDBXML: MakeCol %s n=%d\n", (cdp) ? cdp->GetName() : "<null>", n); + + return new(g) XMLCOL(cdp, this, cprec, n); + } // end of MakeCol + +/***********************************************************************/ +/* InsertSpecialColumn: Put a special column ahead of the column list.*/ +/***********************************************************************/ +PCOL TDBXML::InsertSpecialColumn(PCOL colp) + { + if (!colp->IsSpecial()) + return NULL; + +//if (Xpand && ((SPCBLK*)colp)->GetRnm()) +// colp->SetKey(0); // Rownum is no more a key + + colp->SetNext(Columns); + Columns = colp; + return colp; + } // end of InsertSpecialColumn + +/***********************************************************************/ +/* LoadTableFile: Load and parse an XML file. */ +/***********************************************************************/ +int TDBXML::LoadTableFile(PGLOBAL g, char *filename) + { + int rc = RC_OK, type = (Usedom) ? TYPE_FB_XML : TYPE_FB_XML2; + PFBLOCK fp = NULL; + PDBUSER dup = (PDBUSER)g->Activityp->Aptr; + + if (Docp) + return rc; // Already done + + if (trace(1)) + htrc("TDBXML: loading %s\n", filename); + + /*********************************************************************/ + /* Firstly we check whether this file have been already loaded. */ + /*********************************************************************/ + if ((Mode == MODE_READ || Mode == MODE_ANY) && !Zipped) + for (fp = dup->Openlist; fp; fp = fp->Next) + if (fp->Type == type && fp->Length && fp->Count) + if (!stricmp(fp->Fname, filename)) + break; + + if (fp) { + /*******************************************************************/ + /* File already loaded. Just increment use count and get pointer. */ + /*******************************************************************/ + fp->Count++; + Docp = (Usedom) ? GetDomDoc(g, Nslist, DefNs, Enc, fp) + : GetLibxmlDoc(g, Nslist, DefNs, Enc, fp); + } else { + /*******************************************************************/ + /* Parse the XML file. */ + /*******************************************************************/ + if (!(Docp = (Usedom) ? GetDomDoc(g, Nslist, DefNs, Enc) + : GetLibxmlDoc(g, Nslist, DefNs, Enc))) + return RC_FX; + + // Initialize the implementation + if (Docp->Initialize(g, Entry, Zipped)) { + snprintf(g->Message, sizeof(g->Message), MSG(INIT_FAILED), (Usedom) ? "DOM" : "libxml2"); + return RC_FX; + } // endif init + + if (trace(1)) + htrc("TDBXML: parsing %s rc=%d\n", filename, rc); + + // Parse the XML file + if (Docp->ParseFile(g, filename)) { + // Does the file exist? + int h= global_open(g, MSGID_NONE, filename, _O_RDONLY); + + if (h != -1) { + rc = (!_filelength(h)) ? RC_EF : RC_INFO; + close(h); + } else + rc = (errno == ENOENT) ? RC_NF : RC_INFO; + + // Cannot make a Xblock until document is made + return rc; + } // endif Docp + + /*******************************************************************/ + /* Link a Xblock. This make possible to reuse already opened docs */ + /* and also to automatically close them in case of error g->jump. */ + /*******************************************************************/ + fp = Docp->LinkXblock(g, Mode, rc, filename); + } // endif xp + + To_Xb = fp; // Useful when closing + return rc; + } // end of LoadTableFile + +/***********************************************************************/ +/* Initialize the processing of the XML file. */ +/* Note: this function can be called several times, eventally before */ +/* the columns are known (from TBL for instance) */ +/***********************************************************************/ +bool TDBXML::Initialize(PGLOBAL g) + { + int rc; + PXMLCOL colp; + + if (Void) + return false; + + if (Columns) { + // Allocate the buffers that will contain node values + for (colp = (PXMLCOL)Columns; colp; colp = (PXMLCOL)colp->GetNext()) + if (!colp->IsSpecial()) { // Not a pseudo column + if (!Bufdone && colp->AllocBuf(g, Mode == MODE_INSERT)) + return true; + + colp->Nx = colp->Sx = -1; + } // endif Special + + Bufdone = true; + } // endif Bufdone + +#if !defined(UNIX) + if (!Root) try { +#else + if (!Root) { +#endif + char tabpath[64], filename[_MAX_PATH]; + + // We used the file name relative to recorded datapath + PlugSetPath(filename, Xfile, GetPath()); + + // Load or re-use the table file + rc = LoadTableFile(g, filename); + + if (rc == RC_OK) { + // Get root node + if (!(Root = Docp->GetRoot(g))) { + // This should never happen as load should have failed + snprintf(g->Message, sizeof(g->Message), MSG(EMPTY_DOC)); + goto error; + } // endif Root + + // If tabname is not an Xpath, + // construct one that will find it anywhere + if (!strchr(Tabname, '/')) + snprintf(tabpath, sizeof(tabpath), "//%s", Tabname); + else + snprintf(tabpath, sizeof(tabpath), "%s", Tabname); + + // Evaluate table xpath + if ((TabNode = Root->SelectSingleNode(g, tabpath))) { + if (TabNode->GetType() != XML_ELEMENT_NODE) { + snprintf(g->Message, sizeof(g->Message), MSG(BAD_NODE_TYPE), TabNode->GetType()); + goto error; + } // endif Type + + } else if (Mode == MODE_INSERT && XmlDB) { + // We are adding a new table to a multi-table file + + // If XmlDB is not an Xpath, + // construct one that will find it anywhere + if (!strchr(XmlDB, '/')) + strcat(strcpy(tabpath, "//"), XmlDB); + else + strcpy(tabpath, XmlDB); + + if (!(DBnode = Root->SelectSingleNode(g, tabpath))) { + // DB node does not exist yet; we cannot create it + // because we don't know where it should be placed + snprintf(g->Message, sizeof(g->Message), MSG(MISSING_NODE), XmlDB, Xfile); + goto error; + } // endif DBnode + + if (!(TabNode = DBnode->AddChildNode(g, Tabname))) { + snprintf(g->Message, sizeof(g->Message), MSG(FAIL_ADD_NODE), Tabname); + goto error; + } // endif TabNode + + DBnode->AddText(g, "\n"); + } else { + TabNode = Root; // Try this ? + Tabname = TabNode->GetName(g); + } // endif's + + } else if (rc == RC_NF || rc == RC_EF) { + // The XML file does not exist or is void + if (Mode == MODE_INSERT) { + // New Document + char buf[64]; + + // Create the XML node + if (Docp->NewDoc(g, "1.0")) { + snprintf(g->Message, sizeof(g->Message), MSG(NEW_DOC_FAILED)); + goto error; + } // endif NewDoc + + // Now we can link the Xblock + To_Xb = Docp->LinkXblock(g, Mode, rc, filename); + + // Add a CONNECT comment node + snprintf(buf, sizeof(buf), " Created by the MariaDB CONNECT Storage Engine"); + Docp->AddComment(g, buf); + + if (XmlDB) { + // This is a multi-table file + DBnode = Root = Docp->NewRoot(g, XmlDB); + DBnode->AddText(g, "\n"); + TabNode = DBnode->AddChildNode(g, Tabname); + DBnode->AddText(g, "\n"); + } else + TabNode = Root = Docp->NewRoot(g, Tabname); + + if (TabNode == NULL || Root == NULL) { + snprintf(g->Message, sizeof(g->Message), MSG(XML_INIT_ERROR)); + goto error; + } else if (SetTabNode(g)) + goto error; + + } else { + snprintf(g->Message, sizeof(g->Message), MSG(FILE_UNFOUND), Xfile); + + if (Mode == MODE_READ) { + PushWarning(g, this); + Void = true; + } // endif Mode + + goto error; + } // endif Mode + + } else if (rc == RC_INFO) { + // Loading failed + snprintf(g->Message, sizeof(g->Message), MSG(LOADING_FAILED), Xfile); + goto error; + } else // (rc == RC_FX) + goto error; + + if (!Rowname) { + for (PXNODE n = TabNode->GetChild(g); n; n = n->GetNext(g)) + if (n->GetType() == XML_ELEMENT_NODE) { + Rowname = n->GetName(g); + break; + } // endif Type + + if (!Rowname) + Rowname = TabNode->GetName(g); + } // endif Rowname + + // Get row node list + if (strcmp(Rowname, Tabname)) + Nlist = TabNode->SelectNodes(g, Rowname); + else + Nrow = 1; + + + Docp->SetNofree(true); // For libxml2 +#if defined(_WIN32) + } catch (_com_error e) { + // We come here if a DOM command threw an error + char buf[128]; + + rc = WideCharToMultiByte(CP_ACP, 0, e.Description(), -1, + buf, sizeof(buf), NULL, NULL); + + if (rc) + snprintf(g->Message, sizeof(g->Message), "%s: %s", MSG(COM_ERROR), buf); + else + snprintf(g->Message, sizeof(g->Message), "%s hr=%x", MSG(COM_ERROR), e.Error()); + + goto error; +#endif // _WIN32 +#if !defined(UNIX) + } catch(...) { + // Other errors + snprintf(g->Message, sizeof(g->Message), MSG(XMLTAB_INIT_ERR)); + goto error; +#endif + } // end of try-catches + + if (Root && Columns && (Multiple || !Nodedone)) { + // Allocate class nodes to avoid dynamic allocation + for (colp = (PXMLCOL)Columns; colp; colp = (PXMLCOL)colp->GetNext()) + if (!colp->IsSpecial()) // Not a pseudo column + colp->AllocNodes(g, Docp); + + Nodedone = true; + } // endif Nodedone + + if (Nrow < 0) + Nrow = (Nlist) ? Nlist->GetLength() : 0; + + // Init is Ok + return false; + +error: + if (Docp) + Docp->CloseDoc(g, To_Xb); + + return !Void; +} // end of Initialize + +/***********************************************************************/ +/* Set TabNode attributes or header. */ +/***********************************************************************/ +bool TDBXML::SetTabNode(PGLOBAL g) + { + assert(Mode == MODE_INSERT); + + if (Attrib) + SetNodeAttr(g, Attrib, TabNode); + + if (Header) { + PCOLDEF cdp; + PXNODE rn, cn; + + if (Rowname) { + TabNode->AddText(g, "\n\t"); + rn = TabNode->AddChildNode(g, Rowname, NULL); + } else { + snprintf(g->Message, sizeof(g->Message), MSG(NO_ROW_NODE)); + return true; + } // endif Rowname + + if (Hdattr) + SetNodeAttr(g, Hdattr, rn); + + for (cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext()) { + rn->AddText(g, "\n\t\t"); + cn = rn->AddChildNode(g, "TH", NULL); + cn->SetContent(g, (char *)cdp->GetName(), + strlen(cdp->GetName()) + 1); + } // endfor cdp + + rn->AddText(g, "\n\t"); + } // endif ColType + + return false; + } // end of SetTabNode + +/***********************************************************************/ +/* Set attributes of a table or header node. */ +/***********************************************************************/ +void TDBXML::SetNodeAttr(PGLOBAL g, char *attr, PXNODE node) + { + char *p, *pa, *pn = attr; + PXATTR an; + + do { + if ((p = strchr(pn, '='))) { + pa = pn; + *p++ = 0; + + if ((pn = strchr(p, ';'))) + *pn++ = 0; + + an = node->AddProperty(g, pa, NULL); + an->SetText(g, p, strlen(p) + 1); + } else + break; + + } while (pn); + + } // end of SetNodeAttr + +/***********************************************************************/ +/* XML Cardinality: returns table cardinality in number of rows. */ +/* This function can be called with a null argument to test the */ +/* availability of Cardinality implementation (1 yes, 0 no). */ +/***********************************************************************/ +int TDBXML::Cardinality(PGLOBAL g) + { + if (!g) + return (Multiple || Xpand || Coltype == 2) ? 0 : 1; + + if (Multiple) + return 10; + + if (Nrow < 0) + if (Initialize(g)) + return -1; + + return (Void) ? 0 : Nrow - Header; + } // end of Cardinality + +/***********************************************************************/ +/* XML GetMaxSize: returns the number of tables in the database. */ +/***********************************************************************/ +int TDBXML::GetMaxSize(PGLOBAL g) + { + if (MaxSize < 0) { + if (!Multiple) + MaxSize = Cardinality(g) * ((Xpand) ? Limit : 1); + else + MaxSize = 10; + + } // endif MaxSize + + return MaxSize; + } // end of GetMaxSize + +/***********************************************************************/ +/* Return the position in the table. */ +/***********************************************************************/ +int TDBXML::GetRecpos(void) + { + union { + uint Rpos; + BYTE Spos[4]; + }; + + Rpos = htonl(Irow); + Spos[0] = (BYTE)Nsub; + return Rpos; + } // end of GetRecpos + +/***********************************************************************/ +/* RowNumber: return the ordinal number of the current row. */ +/***********************************************************************/ +int TDBXML::RowNumber(PGLOBAL g, bool b) + { + if (To_Kindex && (Xpand || Coltype == 2) && !b) { + /*******************************************************************/ + /* Don't know how to retrieve RowID for expanded XML tables. */ + /*******************************************************************/ + snprintf(g->Message, sizeof(g->Message), MSG(NO_ROWID_FOR_AM), + GetAmName(g, GetAmType())); + return 0; // Means error + } else + return (b || !(Xpand || Coltype == 2)) ? Irow - Header + 1 : N; + + } // end of RowNumber + +/***********************************************************************/ +/* XML Access Method opening routine. */ +/***********************************************************************/ +bool TDBXML::OpenDB(PGLOBAL g) + { + if (Use == USE_OPEN) { + /*******************************************************************/ + /* Table already open replace it at its beginning. */ + /*******************************************************************/ + if (!To_Kindex) { + Irow = Header - 1; + Nsub = 0; + } else + /*****************************************************************/ + /* Table is to be accessed through a sorted index table. */ + /*****************************************************************/ + To_Kindex->Reset(); + + return false; + } // endif use + + /*********************************************************************/ + /* OpenDB: initialize the XML file processing. */ + /*********************************************************************/ + Write = (Mode == MODE_INSERT || Mode == MODE_UPDATE); + + if (Initialize(g)) + return true; + + NewRow = (Mode == MODE_INSERT); + Nsub = 0; + Use = USE_OPEN; // Do it now in case we are recursively called + return false; + } // end of OpenDB + +/***********************************************************************/ +/* Data Base read routine for XML access method. */ +/***********************************************************************/ +int TDBXML::ReadDB(PGLOBAL g) + { + bool same; + + if (Void) + return RC_EF; + + /*********************************************************************/ + /* Now start the pseudo reading process. */ + /*********************************************************************/ + if (To_Kindex) { + /*******************************************************************/ + /* Reading is by an index table. */ + /*******************************************************************/ + union { + uint Rpos; + BYTE Spos[4]; + }; + + int recpos = To_Kindex->Fetch(g); + + switch (recpos) { + case -1: // End of file reached + return RC_EF; + case -2: // No match for join + return RC_NF; + case -3: // Same record as last non null one + same = true; + return RC_OK; + default: + Rpos = recpos; + Nsub = Spos[0]; + Spos[0] = 0; + + if (Irow != (signed)ntohl(Rpos)) { + Irow = ntohl(Rpos); + same = false; + } else + same = true; + + } // endswitch recpos + + } else { + if (trace(1)) + htrc("TDBXML ReadDB: Irow=%d Nrow=%d\n", Irow, Nrow); + + // This is to force the table to be expanded when constructing + // an index for which the expand column is not specified. + if (Colp && Irow >= Header) { + Colp->Eval(g); + Colp->Reset(); + } // endif Colp + + if (!NextSame) { + if (++Irow == Nrow) + return RC_EF; + + same = false; + Nsub = 0; + } else { + // Not sure the multiple column read will be called + NextSame = false; + same = true; + Nsub++; + } // endif NextSame + + N++; // RowID + } // endif To_Kindex + + if (!same) { + if (trace(2)) + htrc("TDBXML ReadDB: Irow=%d RowNode=%p\n", Irow, RowNode); + + // Get the new row node + if (Nlist) { + if ((RowNode = Nlist->GetItem(g, Irow, RowNode)) == NULL) { + snprintf(g->Message, sizeof(g->Message), MSG(MISSING_ROWNODE), Irow); + return RC_FX; + } // endif RowNode + + } else + RowNode = TabNode; + + if (Colname && Coltype == 2) + Clist = RowNode->SelectNodes(g, Colname, Clist); + + } // endif same + + return RC_OK; + } // end of ReadDB + +/***********************************************************************/ +/* CheckRow: called On Insert and Update. Must create the Row node */ +/* if it does not exist (Insert) and update the Clist if called by */ +/* a column having an Xpath because it can use an existing node that */ +/* was added while inserting or Updating this row. */ +/***********************************************************************/ +bool TDBXML::CheckRow(PGLOBAL g, bool b) + { + if (NewRow && Mode == MODE_INSERT) + { + if (Rowname) { + TabNode->AddText(g, "\n\t"); + RowNode = TabNode->AddChildNode(g, Rowname, RowNode); + } else { + snprintf(g->Message, sizeof(g->Message), MSG(NO_ROW_NODE)); + return true; + } // endif Rowname + } + + if (Colname && (NewRow || b)) + Clist = RowNode->SelectNodes(g, Colname, Clist); + + return NewRow = false; + } // end of CheckRow + +/***********************************************************************/ +/* WriteDB: Data Base write routine for XDB access methods. */ +/***********************************************************************/ +int TDBXML::WriteDB(PGLOBAL g) + { + if (Mode == MODE_INSERT) { + if (Hasnod) + RowNode->AddText(g, "\n\t"); + + NewRow = true; + } // endif Mode + + // Something was changed in the document + Changed = true; + return RC_OK; + } // end of WriteDB + +/***********************************************************************/ +/* Data Base delete line routine for XDB access methods. */ +/***********************************************************************/ +int TDBXML::DeleteDB(PGLOBAL g, int irc) + { + // TODO: Handle null Nlist + if (irc == RC_FX) { + // Delete all rows + for (Irow = 0; Irow < Nrow; Irow++) + if ((RowNode = Nlist->GetItem(g, Irow, RowNode)) == NULL) { + snprintf(g->Message, sizeof(g->Message), MSG(MISSING_ROWNODE), Irow); + return RC_FX; + } else { + TabNode->DeleteChild(g, RowNode); + + if (Nlist->DropItem(g, Irow)) + return RC_FX; + + } // endif RowNode + + Changed = true; + } else if (irc != RC_EF) { + TabNode->DeleteChild(g, RowNode); + + if (Nlist->DropItem(g, Irow)) + return RC_FX; + + Changed = true; + } // endif's irc + + return RC_OK; + } // end of DeleteDB + +/***********************************************************************/ +/* Data Base close routine for XDB access methods. */ +/***********************************************************************/ +void TDBXML::CloseDB(PGLOBAL g) + { + if (Docp) { + if (Changed) { + char filename[_MAX_PATH]; + + // We used the file name relative to recorded datapath + PlugSetPath(filename, Xfile, GetPath()); + + if (Mode == MODE_INSERT) + TabNode->AddText(g, "\n"); + + // Save the modified document + if (Docp->DumpDoc(g, filename)) { + PushWarning(g, this); + Docp->CloseDoc(g, To_Xb); + + // This causes a crash in Diagnostics_area::set_error_status +// throw (int)TYPE_AM_XML; + } // endif DumpDoc + + } // endif Changed + + // Free the document and terminate XML processing + Docp->CloseDoc(g, To_Xb); + } // endif docp + + if (Multiple) { + // Reset all constants to start a new parse + Docp = NULL; + Root = NULL; + Curp = NULL; + DBnode = NULL; + TabNode = NULL; + RowNode = NULL; + ColNode = NULL; + Nlist = NULL; + Clist = NULL; + To_Xb = NULL; + Colp = NULL; + Changed = false; + Checked = false; + NextSame = false; + NewRow = false; + Hasnod = false; + Write = false; + Nodedone = false; + Void = false; + Nrow = -1; + Irow = Header - 1; + Nsub = 0; + N = 0; + } // endif Multiple + + } // end of CloseDB + +// ------------------------ XMLCOL functions ---------------------------- + +/***********************************************************************/ +/* XMLCOL public constructor. */ +/***********************************************************************/ +XMLCOL::XMLCOL(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 XML access method information for column. + Tdbp = (PTDBXML)tdbp; + Nl = NULL; + Nlx = NULL; + ColNode = NULL; + ValNode = NULL; + Cxnp = NULL; + Vxnp = NULL; + Vxap = NULL; + AttNode = NULL; + Nodes = NULL; + Nod = 0; + Inod = -1; + Mul = false; + Checked = false; + Xname = cdp->GetFmt(); + Long = cdp->GetLong(); + Rank = cdp->GetOffset(); + Type = Tdbp->Coltype; + Nx = -1; + Sx = -1; + N = 0; + Valbuf = NULL; + To_Val = NULL; + } // end of XMLCOL constructor + +/***********************************************************************/ +/* XMLCOL constructor used for copying columns. */ +/* tdbp is the pointer to the new table descriptor. */ +/***********************************************************************/ +XMLCOL::XMLCOL(XMLCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp) + { + Tdbp = col1->Tdbp; + Nl = col1->Nl; + Nlx = col1->Nlx; + ColNode = col1->ColNode; + ValNode = col1->ValNode; + Cxnp = col1->Cxnp; + Vxnp = col1->Vxnp; + Vxap = col1->Vxap; + AttNode = col1->AttNode; + Nodes = col1->Nodes; + Nod = col1->Nod; + Inod = col1->Inod; + Mul = col1->Mul; + Checked = col1->Checked; + Xname = col1->Xname; + Valbuf = col1->Valbuf; + Long = col1->Long; + Rank = col1->Rank; + Nx = col1->Nx; + Sx = col1->Sx; + N = col1->N; + Type = col1->Type; + To_Val = col1->To_Val; + } // end of XMLCOL copy constructor + +/***********************************************************************/ +/* Allocate a buffer of the proper size. */ +/***********************************************************************/ +bool XMLCOL::AllocBuf(PGLOBAL g, bool mode) + { + if (Valbuf) + return false; // Already done + + return ParseXpath(g, mode); + } // end of AllocBuf + +/***********************************************************************/ +/* Parse the eventual passed Xpath information. */ +/* This information can be specified in the Xpath (or Fieldfmt) */ +/* column option when creating the table. It permits to indicate the */ +/* position of the node corresponding to that column in a Xpath-like */ +/* language (but not a truly compliant one). */ +/***********************************************************************/ +bool XMLCOL::ParseXpath(PGLOBAL g, bool mode) + { + char *p, *p2, *pbuf = NULL; + int i, n = 1, len = strlen(Name); + + len += ((Tdbp->Colname) ? strlen(Tdbp->Colname) : 0); + len += ((Xname) ? strlen(Xname) : 0); + pbuf = (char*)PlugSubAlloc(g, NULL, len + 3); + *pbuf = '\0'; + + if (!mode) + // Take care of an eventual extra column node a la html + if (Tdbp->Colname) { + char *p = strstr(Tdbp->Colname, "%d"); + if (p) + snprintf(pbuf, len + 3, "%.*s%d%s/", (int) (p - Tdbp->Colname), Tdbp->Colname, + Rank + (Tdbp->Usedom ? 0 : 1), p + 2); + else + snprintf(pbuf, len + 3, "%s/", Tdbp->Colname); + } // endif Colname + + if (Xname) { + if (Type == 2) { + snprintf(g->Message, sizeof(g->Message), MSG(BAD_COL_XPATH), Name, Tdbp->Name); + return true; + } else + strcat(pbuf, Xname); + + if (trace(1)) + htrc("XMLCOL: pbuf=%s\n", pbuf); + + // For Update or Insert the Xpath must be analyzed + if (mode) { + for (i = 0, p = pbuf; (p = strchr(p, '/')); i++, p++) + Nod++; // One path node found + + if (Nod) + Nodes = (char**)PlugSubAlloc(g, NULL, Nod * sizeof(char*)); + + } // endif mode + + // Analyze the Xpath for this column + for (i = 0, p = pbuf; (p2 = strchr(p, '/')); i++, p = p2 + 1) { + if (Tdbp->Mulnode && !strncmp(p, Tdbp->Mulnode, p2 - p)) + { + if (!Tdbp->Xpand && mode) { + snprintf(g->Message, sizeof(g->Message), MSG(CONCAT_SUBNODE)); + return true; + } else + Inod = i; // Index of multiple node + } + + if (mode) { + // For Update or Insert the Xpath must be explicit + if (strchr("@/.*", *p)) { + snprintf(g->Message, sizeof(g->Message), MSG(XPATH_NOT_SUPP), Name); + return true; + } else + Nodes[i] = p; + + *p2 = '\0'; + } // endif mode + + } // endfor i, p + + if (*p == '/' || *p == '.') { + snprintf(g->Message, sizeof(g->Message), MSG(XPATH_NOT_SUPP), Name); + return true; + } else if (*p == '@') { + p++; // Remove the @ if mode + Type = 0; // Column is an attribute + } else + Type = 1; // Column is a node + + if (!*p) + strcpy(p, Name); // Xname is column name + + if (Type && Tdbp->Mulnode && !strcmp(p, Tdbp->Mulnode)) + Inod = Nod; // Index of multiple node + + if (mode) // Prepare Xname + pbuf = p; + + } else if (Type == 2) { + // HTML like table, columns are retrieved by position + new(this) XPOSCOL(Value); // Change the class of this column + Inod = -1; + } else if (Type == 0 && !mode) { + strcat(strcat(pbuf, "@"), Name); + } else { // Type == 1 + if (Tdbp->Mulnode && !strcmp(Name, Tdbp->Mulnode)) + Inod = 0; // Nod + + strcat(pbuf, Name); + } // endif,s + + if (Inod >= 0) { + Tdbp->Colp = this; // To force expand + + if (Tdbp->Xpand) + n = Tdbp->Limit; + + new(this) XMULCOL(Value); // Change the class of this column + } // endif Inod + + Valbuf = (char*)PlugSubAlloc(g, NULL, n * (Long + 1)); + + for (i = 0; i < n; i++) + Valbuf[Long + (i * (Long + 1))] = '\0'; + + if (Type || Nod) + Tdbp->Hasnod = true; + + if (trace(1)) + htrc("XMLCOL: Xname=%s\n", pbuf); + + // Save the calculated Xpath + Xname = pbuf; + return false; + } // end of ParseXpath + +/***********************************************************************/ +/* SetBuffer: prepare a column block for write operation. */ +/***********************************************************************/ +bool XMLCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check) + { + if (!(To_Val = value)) { + snprintf(g->Message, sizeof(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) { + snprintf(g->Message, sizeof(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(); + Tdbp = (PTDBXML)To_Tdb; // Specific of XMLCOL + + // Allocate the XML buffer + if (AllocBuf(g, true)) // In Write mode + return true; + + } // endif GetOrig + + // Set the Column + Status = (ok) ? BUF_EMPTY : BUF_NO; + return false; + } // end of SetBuffer + +/***********************************************************************/ +/* Alloc the nodes that will be used during the whole process. */ +/***********************************************************************/ +void XMLCOL::AllocNodes(PGLOBAL g, PXDOC dp) +{ + Cxnp = dp->NewPnode(g); + Vxnp = dp->NewPnode(g); + Vxap = dp->NewPattr(g); +} // end of AllocNodes + +/***********************************************************************/ +/* ReadColumn: what this routine does is to access the column node */ +/* from the corresponding table, extract from it the node text and */ +/* convert it to the column type. */ +/***********************************************************************/ +void XMLCOL::ReadColumn(PGLOBAL g) + { + if (Nx == Tdbp->Irow) + return; // Same row than the last read + + ValNode = Tdbp->RowNode->SelectSingleNode(g, Xname, Vxnp); + + if (ValNode) { + if (ValNode->GetType() != XML_ELEMENT_NODE && + ValNode->GetType() != XML_ATTRIBUTE_NODE) { + snprintf(g->Message, sizeof(g->Message), MSG(BAD_VALNODE), ValNode->GetType(), Name); + throw (int)TYPE_AM_XML; + } // endif type + + // Get the Xname value from the XML file + switch (ValNode->GetContent(g, Valbuf, Long + 1)) { + case RC_OK: + break; + case RC_INFO: + PushWarning(g, Tdbp); + break; + default: + throw (int)TYPE_AM_XML; + } // endswitch + + Value->SetValue_psz(Valbuf); + } else { + if (Nullable) + Value->SetNull(true); + + Value->Reset(); // Null value + } // endif ValNode + + Nx = Tdbp->Irow; + } // end of ReadColumn + +/***********************************************************************/ +/* WriteColumn: what this routine does is to access the last row of */ +/* the corresponding table, and rewrite the content corresponding */ +/* to this column node from the column buffer and type. */ +/***********************************************************************/ +void XMLCOL::WriteColumn(PGLOBAL g) + { + char *p, buf[16]; + int done = 0; + int i, n, k = 0; + PXNODE TopNode = NULL; + + if (trace(2)) + htrc("XML WriteColumn: col %s R%d coluse=%.4X status=%.4X\n", + Name, Tdbp->GetTdb_No(), ColUse, Status); + + /*********************************************************************/ + /* Check whether this node must be written. */ + /*********************************************************************/ + if (Value != To_Val) + Value->SetValue_pval(To_Val, false); // Convert the updated value + + /*********************************************************************/ + /* If a check pass was done while updating, all node contruction */ + /* has been already one. */ + /*********************************************************************/ + if (Status && Tdbp->Checked && !Value->IsNull()) { + assert (ColNode != NULL); + assert ((Type ? (void *)ValNode : (void *)AttNode) != NULL); + goto fin; + } // endif Checked + + /*********************************************************************/ + /* On Insert, a Row node must be created for each row; */ + /* For columns having an Xpath, the Clist must be updated. */ + /*********************************************************************/ + if (Tdbp->CheckRow(g, Nod || Tdbp->Colname)) + throw (int)TYPE_AM_XML; + + /*********************************************************************/ + /* Null values are represented by no node. */ + /*********************************************************************/ + if (Value->IsNull()) + return; + + /*********************************************************************/ + /* Find the column and value nodes to update or insert. */ + /*********************************************************************/ + if (Tdbp->Clist) { + n = Tdbp->Clist->GetLength(); + ColNode = NULL; + } else { + n = 1; + ColNode = Tdbp->RowNode->Clone(g, ColNode); + } // endif Clist + + ValNode = NULL; + + for (i = 0; i < n; i++) { + if (Tdbp->Clist) + ColNode = Tdbp->Clist->GetItem(g, i, Cxnp); + + /*******************************************************************/ + /* Check whether an Xpath was provided to go to the column node. */ + /*******************************************************************/ + for (k = 0; k < Nod; k++) + if ((ColNode = ColNode->SelectSingleNode(g, Nodes[k], Cxnp))) + TopNode = ColNode; + else + break; + + if (ColNode) + { + if (Type) + ValNode = ColNode->SelectSingleNode(g, Xname, Vxnp); + else + AttNode = ColNode->GetAttribute(g, Xname, Vxap); + } + + if (TopNode || ValNode || AttNode) + break; // We found the good column + else if (Tdbp->Clist) + ColNode = NULL; + + // refresh CList in case its Listp was freed in SelectSingleNode above + if (Tdbp->Clist) + Tdbp->RowNode->SelectNodes(g, Tdbp->Colname, Tdbp->Clist); + } // endfor i + + /*********************************************************************/ + /* Create missing nodes. */ + /*********************************************************************/ + if (ColNode == NULL) { + if (TopNode == NULL) + { + if (Tdbp->Clist) { + Tdbp->RowNode->AddText(g, "\n\t\t"); + ColNode = Tdbp->RowNode->AddChildNode(g, Tdbp->Colname); + done = 2; + TopNode = ColNode; + } else + TopNode = Tdbp->RowNode; + } + for (; k < Nod && TopNode; k++) { + if (!done) { + TopNode->AddText(g, "\n\t\t"); + done = 1; + } // endif done + + ColNode = TopNode->AddChildNode(g, Nodes[k], Cxnp); + TopNode = ColNode; + } // endfor k + + if (ColNode == NULL) { + snprintf(g->Message, sizeof(g->Message), MSG(COL_ALLOC_ERR)); + throw (int)TYPE_AM_XML; + } // endif ColNode + + } // endif ColNode + + if (Type == 1) { + if (ValNode == NULL) { + if (done < 2) + ColNode->AddText(g, "\n\t\t"); + + ValNode = ColNode->AddChildNode(g, Xname, Vxnp); + } // endif ValNode + + } else // (Type == 0) + if (AttNode == NULL) + AttNode = ColNode->AddProperty(g, Xname, Vxap); + + if (ValNode == NULL && AttNode == NULL) { + snprintf(g->Message, sizeof(g->Message), MSG(VAL_ALLOC_ERR)); + throw (int)TYPE_AM_XML; + } // endif ValNode + + /*********************************************************************/ + /* Get the string representation of Value according to column type. */ + /*********************************************************************/ + p = Value->GetCharString(buf); + + if (strlen(p) > (unsigned)Long) { + snprintf(g->Message, sizeof(g->Message), MSG(VALUE_TOO_LONG), p, Name, Long); + throw (int)TYPE_AM_XML; + } else + strcpy(Valbuf, p); + + /*********************************************************************/ + /* Updating must be done only when not in checking pass. */ + /*********************************************************************/ + fin: + if (Status) { + if (Type) { + ValNode->SetContent(g, Valbuf, Long); + } else + AttNode->SetText(g, Valbuf, Long); + + } // endif Status + + } // end of WriteColumn + +// ------------------------ XMULCOL functions --------------------------- + +/***********************************************************************/ +/* ReadColumn: what this routine does is to access the column node */ +/* from the corresponding table, extract from it the node text and */ +/* convert it to the column type. */ +/***********************************************************************/ +void XMULCOL::ReadColumn(PGLOBAL g) + { + char *p; + int i, len; + bool b = Tdbp->Xpand; + + if (Nx != Tdbp->Irow) { // New row + Nl = Tdbp->RowNode->SelectNodes(g, Xname, Nl); + + if ((N = Nl->GetLength())) { + *(p = Valbuf) = '\0'; + len = Long; + + if (N > Tdbp->Limit) { + N = Tdbp->Limit; + snprintf(g->Message, sizeof(g->Message), "Multiple values limited to %d", Tdbp->Limit); + PushWarning(g, Tdbp); + } // endif N + + for (i = 0; i < N; i++) { + ValNode = Nl->GetItem(g, i, Vxnp); + + if (ValNode->GetType() != XML_ELEMENT_NODE && + ValNode->GetType() != XML_ATTRIBUTE_NODE) { + snprintf(g->Message, sizeof(g->Message), MSG(BAD_VALNODE), ValNode->GetType(), Name); + throw (int)TYPE_AM_XML; + } // endif type + + // Get the Xname value from the XML file + switch (ValNode->GetContent(g, p, (b ? Long : len))) { + case RC_OK: + break; + case RC_INFO: + PushWarning(g, Tdbp); + break; + default: + throw (int)TYPE_AM_XML; + } // endswitch + + if (!b) { + // Concatenate all values + if (N - i > 1) + strncat(Valbuf, ", ", len - strlen(p)); + + if ((len -= strlen(p)) <= 0) + break; + + p += strlen(p); + } else // Xpand + p += (Long + 1); + + } // endfor i + + Value->SetValue_psz(Valbuf); + } else { + if (Nullable) + Value->SetNull(true); + + Value->Reset(); // Null value + } // endif ValNode + + } else if (Sx == Tdbp->Nsub) + return; // Same row + else // Expanded value + Value->SetValue_psz(Valbuf + (Tdbp->Nsub * (Long + 1))); + + Nx = Tdbp->Irow; + Sx = Tdbp->Nsub; + Tdbp->NextSame = (Tdbp->Xpand && N - Sx > 1); + } // end of ReadColumn + +/***********************************************************************/ +/* WriteColumn: what this routine does is to access the last line */ +/* read from the corresponding table, and rewrite the field */ +/* corresponding to this column from the column buffer and type. */ +/***********************************************************************/ +void XMULCOL::WriteColumn(PGLOBAL g) + { + char *p, buf[16]; + int done = 0; + int i, n, len, k = 0; + PXNODE TopNode = NULL; + + if (trace(1)) + htrc("XML WriteColumn: col %s R%d coluse=%.4X status=%.4X\n", + Name, Tdbp->GetTdb_No(), ColUse, Status); + + /*********************************************************************/ + /* Check whether this node must be written. */ + /*********************************************************************/ + if (Value != To_Val) + Value->SetValue_pval(To_Val, false); // Convert the updated value + + if (Value->IsNull()) + return; + + /*********************************************************************/ + /* If a check pass was done while updating, all node contruction */ + /* has been already one. */ + /*********************************************************************/ + if (Status && Tdbp->Checked) { + assert (ColNode); + assert ((Type ? (void *)ValNode : (void *)AttNode) != NULL); + goto fin; + } // endif Checked + + /*********************************************************************/ + /* On Insert, a Row node must be created for each row; */ + /* For columns having an Xpath, the Clist must be updated. */ + /*********************************************************************/ + if (Tdbp->CheckRow(g, Nod)) + throw (int)TYPE_AM_XML; + + /*********************************************************************/ + /* Find the column and value nodes to update or insert. */ + /*********************************************************************/ + if (Tdbp->Clist) { + n = Tdbp->Clist->GetLength(); + ColNode = NULL; + } else { + n = 1; + ColNode = Tdbp->RowNode->Clone(g, ColNode); + } // endif Clist + + ValNode = NULL; + + for (i = 0; i < n; i++) { + if (Tdbp->Clist) + ColNode = Tdbp->Clist->GetItem(g, i, Cxnp); + + /*******************************************************************/ + /* Check whether an Xpath was provided to go to the column node. */ + /*******************************************************************/ + for (k = 0; k < Nod; k++) { + if (k == Inod) { + // This is the multiple node + Nlx = ColNode->SelectNodes(g, Nodes[k], Nlx); + ColNode = Nlx->GetItem(g, Tdbp->Nsub, Cxnp); + } else + ColNode = ColNode->SelectSingleNode(g, Nodes[k], Cxnp); + + if (ColNode == NULL) + break; + + TopNode = ColNode; + } // endfor k + + if (ColNode) + { + if (Inod == Nod) { + /***************************************************************/ + /* The node value can be multiple. */ + /***************************************************************/ + assert (Type); + + // Get the value Node from the XML list + Nlx = ColNode->SelectNodes(g, Xname, Nlx); + len = Nlx->GetLength(); + + if (len > 1 && !Tdbp->Xpand) { + snprintf(g->Message, sizeof(g->Message), MSG(BAD_VAL_UPDATE), Name); + throw (int)TYPE_AM_XML; + } else + ValNode = Nlx->GetItem(g, Tdbp->Nsub, Vxnp); + + } else // Inod != Nod + { + if (Type) + ValNode = ColNode->SelectSingleNode(g, Xname, Vxnp); + else + AttNode = ColNode->GetAttribute(g, Xname, Vxap); + } + } + if (TopNode || ValNode || AttNode) + break; // We found the good column + else if (Tdbp->Clist) + ColNode = NULL; + + } // endfor i + + /*********************************************************************/ + /* Create missing nodes. */ + /*********************************************************************/ + if (ColNode == NULL) { + if (TopNode == NULL) + { + if (Tdbp->Clist) { + Tdbp->RowNode->AddText(g, "\n\t\t"); + ColNode = Tdbp->RowNode->AddChildNode(g, Tdbp->Colname); + done = 2; + TopNode = ColNode; + } else + TopNode = Tdbp->RowNode; + } + + for (; k < Nod && TopNode; k++) { + if (!done) { + TopNode->AddText(g, "\n\t\t"); + done = 1; + } // endif done + + ColNode = TopNode->AddChildNode(g, Nodes[k], Cxnp); + TopNode = ColNode; + } // endfor k + + if (ColNode == NULL) { + snprintf(g->Message, sizeof(g->Message), MSG(COL_ALLOC_ERR)); + throw (int)TYPE_AM_XML; + } // endif ColNode + + } // endif ColNode + + if (Type == 1) { + if (ValNode == NULL) { + if (done < 2) + ColNode->AddText(g, "\n\t\t"); + + ValNode = ColNode->AddChildNode(g, Xname, Vxnp); + } // endif ValNode + + } else // (Type == 0) + if (AttNode == NULL) + AttNode = ColNode->AddProperty(g, Xname, Vxap); + + if (ValNode == NULL && AttNode == NULL) { + snprintf(g->Message, sizeof(g->Message), MSG(VAL_ALLOC_ERR)); + throw (int)TYPE_AM_XML; + } // endif ValNode + + /*********************************************************************/ + /* Get the string representation of Value according to column type. */ + /*********************************************************************/ + p = Value->GetCharString(buf); + + if (strlen(p) > (unsigned)Long) { + snprintf(g->Message, sizeof(g->Message), MSG(VALUE_TOO_LONG), p, Name, Long); + throw (int)TYPE_AM_XML; + } else + strcpy(Valbuf, p); + + /*********************************************************************/ + /* Updating must be done only when not in checking pass. */ + /*********************************************************************/ + fin: + if (Status) { + if (Type) { + ValNode->SetContent(g, Valbuf, Long); + } else + AttNode->SetText(g, Valbuf, Long); + + } // endif Status + + } // end of WriteColumn + +/* ------------------------ XPOSCOL functions ------------------------ */ + +/***********************************************************************/ +/* ReadColumn: what this routine does is to access the column node */ +/* from the corresponding table, extract from it the node text and */ +/* convert it to the column type. */ +/***********************************************************************/ +void XPOSCOL::ReadColumn(PGLOBAL g) + { + if (Nx == Tdbp->Irow) + return; // Same row than the last read + + if (Tdbp->Clist == NULL) { + snprintf(g->Message, sizeof(g->Message), MSG(MIS_TAG_LIST)); + throw (int)TYPE_AM_XML; + } // endif Clist + + if ((ValNode = Tdbp->Clist->GetItem(g, Rank, Vxnp))) { + // Get the column value from the XML file + switch (ValNode->GetContent(g, Valbuf, Long + 1)) { + case RC_OK: + break; + case RC_INFO: + PushWarning(g, Tdbp); + break; + default: + throw (int)TYPE_AM_XML; + } // endswitch + + Value->SetValue_psz(Valbuf); + } else { + if (Nullable) + Value->SetNull(true); + + Value->Reset(); // Null value + } // endif ValNode + + Nx = Tdbp->Irow; + } // end of ReadColumn + +/***********************************************************************/ +/* WriteColumn: what this routine does is to access the last line */ +/* read from the corresponding table, and rewrite the field */ +/* corresponding to this column from the column buffer and type. */ +/***********************************************************************/ +void XPOSCOL::WriteColumn(PGLOBAL g) + { + char *p, buf[16]; + int i, k, n; + + if (trace(1)) + htrc("XML WriteColumn: col %s R%d coluse=%.4X status=%.4X\n", + Name, Tdbp->GetTdb_No(), ColUse, Status); + + /*********************************************************************/ + /* Check whether this node must be written. */ + /*********************************************************************/ + if (Value != To_Val) + Value->SetValue_pval(To_Val, false); // Convert the updated value + + if (Value->IsNull()) + return; + + /*********************************************************************/ + /* If a check pass was done while updating, all node contruction */ + /* has been already one. */ + /*********************************************************************/ + if (Status && Tdbp->Checked) { + assert (ValNode); + goto fin; + } // endif Checked + + /*********************************************************************/ + /* On Insert, a Row node must be created for each row; */ + /* For all columns the Clist must be updated. */ + /*********************************************************************/ + if (Tdbp->CheckRow(g, true)) + throw (int)TYPE_AM_XML; + + /*********************************************************************/ + /* Find the column and value nodes to update or insert. */ + /*********************************************************************/ + if (Tdbp->Clist == NULL) { + snprintf(g->Message, sizeof(g->Message), MSG(MIS_TAG_LIST)); + throw (int)TYPE_AM_XML; + } // endif Clist + + n = Tdbp->Clist->GetLength(); + k = Rank; + + if (!(ValNode = Tdbp->Clist->GetItem(g, k, Vxnp))) { + /*******************************************************************/ + /* Create missing column nodes. */ + /*******************************************************************/ + Tdbp->RowNode->AddText(g, "\n\t\t"); + + for (i = n; i <= k; i++) + ValNode = Tdbp->RowNode->AddChildNode(g, Tdbp->Colname, Vxnp); + + assert (ValNode); + } // endif ValNode + + /*********************************************************************/ + /* Get the string representation of Value according to column type. */ + /*********************************************************************/ + p = Value->GetCharString(buf); + + if (strlen(p) > (unsigned)Long) { + snprintf(g->Message, sizeof(g->Message), MSG(VALUE_TOO_LONG), p, Name, Long); + throw (int)TYPE_AM_XML; + } else + strcpy(Valbuf, p); + + /*********************************************************************/ + /* Updating must be done only when not in checking pass. */ + /*********************************************************************/ + fin: + if (Status) + ValNode->SetContent(g, Valbuf, Long); + + } // end of WriteColumn + +/* ---------------------------TDBXCT class --------------------------- */ + +/***********************************************************************/ +/* TDBXCT class constructor. */ +/***********************************************************************/ +TDBXCT::TDBXCT(PXMLDEF tdp) : TDBCAT(tdp) + { + Topt = tdp->GetTopt(); + //Db = (char*)tdp->GetDB(); + Db = (char*)tdp->Schema; + Tabn = tdp->Tabname; + } // end of TDBXCT constructor + +/***********************************************************************/ +/* GetResult: Get the list the JSON file columns. */ +/***********************************************************************/ +PQRYRES TDBXCT::GetResult(PGLOBAL g) + { + return XMLColumns(g, Db, Tabn, Topt, false); + } // end of GetResult + +/* ------------------------ End of Tabxml ---------------------------- */ |