/************* 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 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"
#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. */ /***********************************************************************/ typedefclass 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
typedefstruct 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)
{ staticint 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}; staticunsignedint 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
// Missing column can be null for (xcp = fxcp; xcp; xcp = xcp->Next) {
xcp->Cbn |= !xcp->Found;
xcp->Found = false;
} // endfor xcp
} // endfor 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);
/*********************************************************************/ /* 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 ---------------- */
// Note that if no support is specified, the default is MS-DOM // on Windows and libxml2 otherwise if (*buf == '*') #ifdefined(_WIN32)
Usedom = true; #else// !_WIN32
Usedom = false; #endif// !_WIN32 else
Usedom = (toupper(*buf) == 'M' || toupper(*buf) == 'D');
// Specific for zipped files if ((Zipped = GetBoolCatInfo("Zipped", false)))
Mulentries = ((Entry = GetStringCatInfo(g, "Entry", NULL)))
? strchr(Entry, '*') || strchr(Entry, '?')
: GetBoolCatInfo("Mulentries", false);
returnfalse;
} // end of DefineAM
/***********************************************************************/ /* GetTable: makes a new TDB of the proper type. */ /***********************************************************************/
PTDB XMLDEF::GetTable(PGLOBAL g, MODE m)
{ if (Catfunc == FNC_COL) returnnew(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 ---------------------------- */
// 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
returnnew(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;
// 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, eventually before */ /* the columns are known (from TBL for instance) */ /***********************************************************************/ bool TDBXML::Initialize(PGLOBAL g)
{ int rc;
PXMLCOL colp;
if (Void) returnfalse;
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)) returntrue;
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
} elseif (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 (!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 #ifdefined(_WIN32)
} catch (_com_error e) { // We come here if a DOM command threw an error char buf[128];
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);
/***********************************************************************/ /* 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) return10;
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];
} d;
d.Rpos = htonl(Irow);
d.Spos[0] = (BYTE)Nsub; return d.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())); return0; // 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();
returnfalse;
} // endif use
/*********************************************************************/ /* OpenDB: initialize the XML file processing. */ /*********************************************************************/
Write = (Mode == MODE_INSERT || Mode == MODE_UPDATE);
if (Initialize(g)) returntrue;
NewRow = (Mode == MODE_INSERT);
Nsub = 0;
Use = USE_OPEN; // Do it now in case we are recursively called returnfalse;
} // 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];
} d;
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 return RC_OK; default:
d.Rpos = recpos;
Nsub = d.Spos[0];
d.Spos[0] = 0;
if (Irow != (signed)ntohl(d.Rpos)) {
Irow = ntohl(d.Rpos);
same = false;
} else
same = true;
// 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
/***********************************************************************/ /* 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)); returntrue;
} // endif Rowname
}
/***********************************************************************/ /* 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);
/***********************************************************************/ /* 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
/***********************************************************************/ /* 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) returnfalse; // 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); returntrue;
} 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)); returntrue;
} 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); returntrue;
} 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); returntrue;
} elseif (*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;
} elseif (Type == 2) { // HTML like table, columns are retrieved by position new(this) XPOSCOL(Value); // Change the class of this column
Inod = -1;
} elseif (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;
auto oLong = Long; new(this) XMULCOL(Value); // Change the class of this column Long = oLong;
} // 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; returnfalse;
} // 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); returntrue;
} elseif (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;
} elseif (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())); returntrue;
} // endif check
newval: if (InitValue(g)) // Allocate the matching value block returntrue;
} // 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 returntrue;
} // endif GetOrig
// Set the Column
Status = (ok) ? BUF_EMPTY : BUF_NO; returnfalse;
} // 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
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 elseif (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
/*********************************************************************/ /* Get the string representation of Value according to column type. */ /*********************************************************************/
p = Value->GetCharString(buf);
/*********************************************************************/ /* 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);
/***********************************************************************/ /* 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
} elseif (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();
/*********************************************************************/ /* Get the string representation of Value according to column type. */ /*********************************************************************/
p = Value->GetCharString(buf);
/*********************************************************************/ /* 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);
/***********************************************************************/ /* 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 ((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
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);
/*********************************************************************/ /* 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 ---------------------------- */
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.53 Sekunden
(vorverarbeitet am 2026-10-08)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.