1 /************* TabTbl C++ Program Source Code File (.CPP) **************/
2 /* PROGRAM NAME: TABTBL                                                */
3 /* -------------                                                       */
4 /*  Version 1.9                                                        */
5 /*                                                                     */
6 /*  Author: Olivier BERTRAND                              2008-2018    */
7 /*                                                                     */
8 /* WHAT THIS PROGRAM DOES:                                             */
9 /* -----------------------                                             */
10 /*  This program are the TDBTBL class DB routines.                     */
11 /*                                                                     */
12 /* WHAT YOU NEED TO COMPILE THIS PROGRAM:                              */
13 /* --------------------------------------                              */
14 /*                                                                     */
15 /*  REQUIRED FILES:                                                    */
16 /*  ---------------                                                    */
17 /*    TABTBL.CPP     - Source code                                     */
18 /*    PLGDBSEM.H     - DB application declaration file                 */
19 /*    TABDOS.H       - TABDOS classes declaration file                 */
20 /*    TABTBL.H       - TABTBL classes declaration file                 */
21 /*    GLOBAL.H       - Global declaration file                         */
22 /*                                                                     */
23 /*  REQUIRED LIBRARIES:                                                */
24 /*  -------------------                                                */
25 /*    Large model C library                                            */
26 /*                                                                     */
27 /*  REQUIRED PROGRAMS:                                                 */
28 /*  ------------------                                                 */
29 /*    IBM, Borland, GNU or Microsoft C++ Compiler and Linker           */
30 /*                                                                     */
31 /***********************************************************************/
32 
33 /***********************************************************************/
34 /*  Include relevant section of system dependant header files.         */
35 /***********************************************************************/
36 //#include "sql_base.h"
37 #include "my_global.h"
38 #include "table.h"       // MySQL table definitions
39 #if defined(_WIN32)
40 #include <stdlib.h>
41 #include <stdio.h>
42 #if defined(__BORLANDC__)
43 #define __MFC_COMPAT__                   // To define min/max as macro
44 #endif
45 //#include <windows.h>
46 #else
47 #if defined(UNIX)
48 #include <fnmatch.h>
49 #include <errno.h>
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <string.h>
53 #include "osutil.h"
54 #else
55 //#include <io.h>
56 #endif
57 //#include <fcntl.h>
58 #endif
59 
60 /***********************************************************************/
61 /*  Include application header files:                                  */
62 /***********************************************************************/
63 #include "global.h"      // global declarations
64 #include "plgdbsem.h"    // DB application declarations
65 #include "reldef.h"      // DB definition declares
66 #include "filamtxt.h"
67 #include "tabcol.h"
68 #include "tabdos.h"      // TDBDOS and DOSCOL class dcls
69 #include "tabtbl.h"
70 #include "tabext.h"
71 #include "tabmysql.h"
72 #include "ha_connect.h"
73 
74 #if defined(_WIN32)
75 #if defined(__BORLANDC__)
76 #define SYSEXIT void _USERENTRY
77 #else
78 #define SYSEXIT void
79 #endif
80 #else   // !_WIN32
81 #define SYSEXIT void *
82 #endif  // !_WIN32
83 
84 extern pthread_mutex_t tblmut;
85 
86 /* ---------------------------- Class TBLDEF ---------------------------- */
87 
88 /**************************************************************************/
89 /*  Constructor.                                                          */
90 /**************************************************************************/
TBLDEF(void)91 TBLDEF::TBLDEF(void)
92   {
93 //To_Tables = NULL;
94   Accept = false;
95   Thread = false;
96   Maxerr = 0;
97   Ntables = 0;
98   Pseudo = 3;
99   } // end of TBLDEF constructor
100 
101 /**************************************************************************/
102 /*  DefineAM: define specific AM block values from XDB file.              */
103 /**************************************************************************/
DefineAM(PGLOBAL g,LPCSTR,int)104 bool TBLDEF::DefineAM(PGLOBAL g, LPCSTR, int)
105   {
106   char   *tablist, *dbname, *def = NULL;
107 
108   Desc = "Table list table";
109   tablist = GetStringCatInfo(g, "Tablist", "");
110   dbname = GetStringCatInfo(g, "Dbname", "*");
111   def = GetStringCatInfo(g, "Srcdef", NULL);
112   Ntables = 0;
113 
114   if (*tablist) {
115     char  *p, *pn, *pdb;
116     PTABLE tbl;
117 
118     for (pdb = tablist; ;) {
119       if ((p = strchr(pdb, ',')))
120         *p = 0;
121 
122       // Analyze the table name, it may have the format:
123       // [dbname.]tabname
124       if ((pn = strchr(pdb, '.'))) {
125         *pn++ = 0;
126       } else {
127         pn = pdb;
128         pdb = dbname;
129       } // endif p
130 
131       // Allocate the TBLIST block for that table
132       tbl = new(g) XTAB(pn, def);
133       tbl->SetSchema(pdb);
134 
135       if (trace(1))
136         htrc("TBL: Name=%s db=%s\n", tbl->GetName(), tbl->GetSchema());
137 
138       // Link the blocks
139       if (Tablep)
140         Tablep->Link(tbl);
141       else
142         Tablep = tbl;
143 
144       Ntables++;
145 
146       if (p)
147         pdb = pn + strlen(pn) + 1;
148       else
149         break;
150 
151       } // endfor pdb
152 
153     Maxerr = GetIntCatInfo("Maxerr", 0);
154     Accept = GetBoolCatInfo("Accept", false);
155     Thread = GetBoolCatInfo("Thread", false);
156     } // endif tablist
157 
158   return FALSE;
159   } // end of DefineAM
160 
161 /***********************************************************************/
162 /*  GetTable: makes a new Table Description Block.                     */
163 /***********************************************************************/
GetTable(PGLOBAL g,MODE)164 PTDB TBLDEF::GetTable(PGLOBAL g, MODE)
165   {
166   if (Catfunc == FNC_COL)
167     return new(g) TDBTBC(this);
168 	else if (Thread) {
169 #if defined(DEVELOPMENT)
170 		return new(g) TDBTBM(this);
171 #else
172 		strcpy(g->Message, "Option THREAD is no more supported");
173 		return NULL;
174 #endif   // DEVELOPMENT
175 	} else
176     return new(g) TDBTBL(this);
177 
178   } // end of GetTable
179 
180 /* ------------------------- Class TDBTBL ---------------------------- */
181 
182 /***********************************************************************/
183 /*  TDBTBL constructors.                                               */
184 /***********************************************************************/
TDBTBL(PTBLDEF tdp)185 TDBTBL::TDBTBL(PTBLDEF tdp) : TDBPRX(tdp)
186   {
187   Tablist = NULL;
188   CurTable = NULL;
189 //Tdbp = NULL;
190   Accept = tdp->Accept;
191   Maxerr = tdp->Maxerr;
192   Nbc = 0;
193   Rows = 0;
194   Crp = 0;
195 //  NTables = 0;
196 //  iTable = 0;
197   } // end of TDBTBL standard constructor
198 
199 /***********************************************************************/
200 /*  Allocate TBL column description block.                             */
201 /***********************************************************************/
MakeCol(PGLOBAL g,PCOLDEF cdp,PCOL cprec,int n)202 PCOL TDBTBL::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
203   {
204   return new(g) PRXCOL(cdp, this, cprec, n);
205   } // end of MakeCol
206 
207 /***********************************************************************/
208 /*  InsertSpecialColumn: Put a special column ahead of the column list.*/
209 /***********************************************************************/
InsertSpecialColumn(PCOL scp)210 PCOL TDBTBL::InsertSpecialColumn(PCOL scp)
211   {
212   PCOL colp;
213 
214   if (!scp->IsSpecial())
215     return NULL;
216 
217   if (scp->GetAmType() == TYPE_AM_TABID)
218     // This special column is handled locally
219     colp = new((TIDBLK*)scp) TBTBLK(scp->GetValue());
220   else  // Other special columns are treated normally
221     colp = scp;
222 
223   colp->SetNext(Columns);
224   Columns = colp;
225   return colp;
226   } // end of InsertSpecialColumn
227 
228 /***********************************************************************/
229 /*  Initializes the table table list.                                  */
230 /***********************************************************************/
InitTableList(PGLOBAL g)231 bool TDBTBL::InitTableList(PGLOBAL g)
232   {
233   int     n;
234   uint    sln;
235   const char   *scs;
236   PTABLE  tp, tabp;
237   PCOL    colp;
238   PTBLDEF tdp = (PTBLDEF)To_Def;
239   PCATLG  cat = To_Def->GetCat();
240   PHC     hc = ((MYCAT*)cat)->GetHandler();
241 
242   scs = hc->get_table()->s->connect_string.str;
243   sln = hc->get_table()->s->connect_string.length;
244 //  PlugSetPath(filename, Tdbp->GetFile(g), Tdbp->GetPath());
245 
246   for (n = 0, tp = tdp->Tablep; tp; tp = tp->GetNext()) {
247     if (TestFil(g, To_CondFil, tp)) {
248       tabp = new(g) XTAB(tp);
249 
250       if (tabp->GetSrc()) {
251         // Table list is a list of connections
252         hc->get_table()->s->connect_string.str = (char*)tabp->GetName();
253         hc->get_table()->s->connect_string.length = strlen(tabp->GetName());
254         } // endif Src
255 
256       // Get the table description block of this table
257       if (!(Tdbp = GetSubTable(g, tabp))) {
258         if (++Nbc > Maxerr)
259           return TRUE;               // Error return
260         else
261           continue;                  // Skip this table
262 
263       } else
264         RemoveNext(tabp);            // To avoid looping
265 
266       // We must allocate subtable columns before GetMaxSize is called
267       // because some (PLG, ODBC?) need to have their columns attached.
268       // Real initialization will be done later.
269       for (colp = Columns; colp; colp = colp->GetNext())
270         if (!colp->IsSpecial())
271           if (((PPRXCOL)colp)->Init(g, NULL) && !Accept)
272             return TRUE;
273 
274       if (Tablist)
275         Tablist->Link(tabp);
276       else
277         Tablist = tabp;
278 
279       n++;
280       } // endif filp
281 
282     } // endfor tp
283 
284   hc->get_table()->s->connect_string.str = (char*)scs;
285   hc->get_table()->s->connect_string.length = sln;
286 
287 //NumTables = n;
288   To_CondFil = NULL;        // To avoid doing it several times
289   return FALSE;
290   } // end of InitTableList
291 
292 /***********************************************************************/
293 /*  Test the tablename against the pseudo "local" filter.              */
294 /***********************************************************************/
TestFil(PGLOBAL g,PCFIL filp,PTABLE tabp)295 bool TDBTBL::TestFil(PGLOBAL g, PCFIL filp, PTABLE tabp)
296   {
297   char *body, *fil, op[8], tn[NAME_LEN];
298   bool  neg;
299 
300   if (!filp)
301     return TRUE;
302   else
303     body = filp->Body;
304 
305   if (strstr(body, " OR ") || strstr(body, " AND "))
306     return TRUE;               // Not handled yet
307   else
308     fil = body + (*body == '(' ? 1 : 0);
309 
310   if (sscanf(fil, "TABID %s", op) != 1)
311     return TRUE;               // ignore invalid filter
312 
313   if ((neg = !strcmp(op, "NOT")))
314     strcpy(op, "IN");
315 
316   if (!strcmp(op, "=")) {
317     // Temporarily, filter must be "TABID = 'value'" only
318     if (sscanf(fil, "TABID = '%[^']'", tn) != 1)
319       return TRUE;             // ignore invalid filter
320 
321     return !stricmp(tn, tabp->GetName());
322   } else if (!strcmp(op, "IN")) {
323     char *p, *tnl = (char*)PlugSubAlloc(g, NULL, strlen(fil) - 10);
324     int   n;
325 
326     if (neg)
327       n = sscanf(fil, "TABID NOT IN (%[^)])", tnl);
328     else
329       n = sscanf(fil, "TABID IN (%[^)])", tnl);
330 
331     if (n != 1)
332       return TRUE;             // ignore invalid filter
333 
334     while (tnl) {
335       if ((p = strchr(tnl, ',')))
336         *p++ = 0;
337 
338       if (sscanf(tnl, "'%[^']'", tn) != 1)
339         return TRUE;           // ignore invalid filter
340       else if (!stricmp(tn, tabp->GetName()))
341         return !neg;           // Found
342 
343       tnl = p;
344       } // endwhile
345 
346     return neg;                // Not found
347   } // endif op
348 
349   return TRUE;                 // invalid operator
350   } // end of TestFil
351 
352 /***********************************************************************/
353 /*  Sum up the cardinality of all sub-tables.                          */
354 /***********************************************************************/
Cardinality(PGLOBAL g)355 int TDBTBL::Cardinality(PGLOBAL g)
356   {
357   if (!g)
358     return 0;                 // Cannot make the table list
359   else if (Cardinal < 0) {
360     int tsz;
361 
362     if (!Tablist && InitTableList(g))
363       return 0;               // Cannot be calculated at this stage
364 
365     Cardinal = 0;
366 
367     for (PTABLE tabp = Tablist; tabp; tabp = tabp->GetNext()) {
368       if ((tsz = tabp->GetTo_Tdb()->Cardinality(g)) < 0) {
369         Cardinal = -1;
370         return tsz;
371         } // endif mxsz
372 
373       Cardinal += tsz;
374       } // endfor i
375 
376     } // endif Cardinal
377 
378   return Cardinal;
379   } // end of Cardinality
380 
381 /***********************************************************************/
382 /*  Sum up the maximum sizes of all sub-tables.                        */
383 /***********************************************************************/
GetMaxSize(PGLOBAL g)384 int TDBTBL::GetMaxSize(PGLOBAL g)
385   {
386   if (MaxSize < 0) {
387     int mxsz;
388 
389     if (!Tablist && InitTableList(g))
390       return 0;               // Cannot be calculated at this stage
391 
392     MaxSize = 0;
393 
394     for (PTABLE tabp = Tablist; tabp; tabp = tabp->GetNext()) {
395       if ((mxsz = tabp->GetTo_Tdb()->GetMaxSize(g)) < 0) {
396         MaxSize = -1;
397         return mxsz;
398         } // endif mxsz
399 
400       MaxSize += mxsz;
401       } // endfor i
402 
403     } // endif MaxSize
404 
405   return MaxSize;
406   } // end of GetMaxSize
407 
408 /***********************************************************************/
409 /*  Reset read/write position values.                                  */
410 /***********************************************************************/
ResetDB(void)411 void TDBTBL::ResetDB(void)
412   {
413   for (PCOL colp = Columns; colp; colp = colp->GetNext())
414     if (colp->GetAmType() == TYPE_AM_TABID ||
415         colp->GetAmType() == TYPE_AM_SRVID)
416       colp->COLBLK::Reset();
417 
418   for (PTABLE tabp = Tablist; tabp; tabp = tabp->GetNext())
419     tabp->GetTo_Tdb()->ResetDB();
420 
421   Tdbp = Tablist->GetTo_Tdb();
422   Crp = 0;
423   } // end of ResetDB
424 
425 /***********************************************************************/
426 /*  Returns RowId if b is false or Rownum if b is true.                */
427 /***********************************************************************/
RowNumber(PGLOBAL g,bool b)428 int TDBTBL::RowNumber(PGLOBAL g, bool b)
429   {
430   return Tdbp->RowNumber(g) + ((b) ? 0 : Rows);
431   } // end of RowNumber
432 
433 /***********************************************************************/
434 /*  TBL Access Method opening routine.                                 */
435 /*  Open first file, other will be opened sequencially when reading.   */
436 /***********************************************************************/
OpenDB(PGLOBAL g)437 bool TDBTBL::OpenDB(PGLOBAL g)
438   {
439   if (trace(1))
440     htrc("TBL OpenDB: tdbp=%p tdb=R%d use=%d key=%p mode=%d\n",
441                       this, Tdb_No, Use, To_Key_Col, Mode);
442 
443   if (Use == USE_OPEN) {
444     /*******************************************************************/
445     /*  Table already open, replace it at its beginning.               */
446     /*******************************************************************/
447     ResetDB();
448     return Tdbp->OpenDB(g);  // Re-open fist table
449     } // endif use
450 
451   /*********************************************************************/
452   /*  When GetMaxsize was called, To_CondFil was not set yet.          */
453   /*********************************************************************/
454   if (To_CondFil && Tablist) {
455     Tablist = NULL;
456     Nbc = 0;
457     } // endif To_CondFil
458 
459   /*********************************************************************/
460   /*  Open the first table of the list.                                */
461   /*********************************************************************/
462   if (!Tablist && InitTableList(g))     //  done in GetMaxSize
463     return TRUE;
464 
465   if ((CurTable = Tablist)) {
466     Tdbp = CurTable->GetTo_Tdb();
467 //  Tdbp->SetMode(Mode);
468 //  Tdbp->ResetDB();
469 //  Tdbp->ResetSize();
470 
471     // Check and initialize the subtable columns
472     for (PCOL cp = Columns; cp; cp = cp->GetNext())
473       if (cp->GetAmType() == TYPE_AM_TABID)
474         cp->COLBLK::Reset();
475       else if (((PPRXCOL)cp)->Init(g, NULL) && !Accept)
476         return TRUE;
477 
478     if (trace(1))
479       htrc("Opening subtable %s\n", Tdbp->GetName());
480 
481     // Now we can safely open the table
482     if (Tdbp->OpenDB(g))
483       return TRUE;
484 
485     } // endif *Tablist
486 
487   Use = USE_OPEN;
488   return FALSE;
489   } // end of OpenDB
490 
491 /***********************************************************************/
492 /*  ReadDB: Data Base read routine for MUL access method.              */
493 /***********************************************************************/
ReadDB(PGLOBAL g)494 int TDBTBL::ReadDB(PGLOBAL g)
495   {
496   int rc;
497 
498   if (!CurTable)
499     return RC_EF;
500   else if (To_Kindex) {
501     /*******************************************************************/
502     /*  Reading is by an index table.                                  */
503     /*******************************************************************/
504     strcpy(g->Message, MSG(NO_INDEX_READ));
505     rc = RC_FX;
506   } else {
507     /*******************************************************************/
508     /*  Now start the reading process.                                 */
509     /*******************************************************************/
510    retry:
511     rc = Tdbp->ReadDB(g);
512 
513     if (rc == RC_EF) {
514       // Total number of rows met so far
515       Rows += Tdbp->RowNumber(g) - 1;
516       Crp += Tdbp->GetProgMax(g);
517 
518       if ((CurTable = CurTable->GetNext())) {
519         /***************************************************************/
520         /*  Continue reading from next table file.                     */
521         /***************************************************************/
522         Tdbp->CloseDB(g);
523         Tdbp = CurTable->GetTo_Tdb();
524 
525         // Check and initialize the subtable columns
526         for (PCOL cp = Columns; cp; cp = cp->GetNext())
527           if (cp->GetAmType() == TYPE_AM_TABID ||
528               cp->GetAmType() == TYPE_AM_SRVID)
529             cp->COLBLK::Reset();
530           else if (((PPRXCOL)cp)->Init(g, NULL) && !Accept)
531             return RC_FX;
532 
533         if (trace(1))
534           htrc("Opening subtable %s\n", Tdbp->GetName());
535 
536         // Now we can safely open the table
537         if (Tdbp->OpenDB(g))     // Open next table
538           return RC_FX;
539 
540         goto retry;
541         } // endif iFile
542 
543     } else if (rc == RC_FX)
544       strcat(strcat(strcat(g->Message, " ("), Tdbp->GetName()), ")");
545 
546   } // endif To_Kindex
547 
548   return rc;
549   } // end of ReadDB
550 
551 /* ---------------------------- TBTBLK ------------------------------- */
552 
553 /***********************************************************************/
554 /*  ReadColumn:                                                        */
555 /***********************************************************************/
ReadColumn(PGLOBAL)556 void TBTBLK::ReadColumn(PGLOBAL)
557   {
558   if (trace(1))
559     htrc("TBT ReadColumn: name=%s\n", Name);
560 
561   Value->SetValue_psz((char*)((PTDBTBL)To_Tdb)->Tdbp->GetName());
562 
563   } // end of ReadColumn
564 
565 #if defined(DEVELOPMENT)
566 /* ------------------------- Class TDBTBM ---------------------------- */
567 
568 /***********************************************************************/
569 /*  Thread routine that check and open one remote connection.          */
570 /***********************************************************************/
ThreadOpen(void * p)571 pthread_handler_t ThreadOpen(void *p)
572   {
573   PTBMT cmp = (PTBMT)p;
574 
575   if (!my_thread_init()) {
576     set_current_thd(cmp->Thd);
577 
578 		if (trace(1))
579 			htrc("ThreadOpen: Thd=%d\n", cmp->Thd);
580 
581     // Try to open the connection
582 		pthread_mutex_lock(&tblmut);
583 
584 		if (!cmp->Tap->GetTo_Tdb()->OpenDB(cmp->G)) {
585 //		pthread_mutex_lock(&tblmut);
586 			if (trace(1))
587 				htrc("Table %s ready\n", cmp->Tap->GetName());
588 
589 			cmp->Ready = true;
590 //		pthread_mutex_unlock(&tblmut);
591 		} else {
592 //		pthread_mutex_lock(&tblmut);
593 			if (trace(1))
594 				htrc("Opening %s failed\n", cmp->Tap->GetName());
595 
596 			cmp->Rc = RC_FX;
597 //		pthread_mutex_unlock(&tblmut);
598 		}	// endif OpenDB
599 
600 		pthread_mutex_unlock(&tblmut);
601 		my_thread_end();
602   } else
603     cmp->Rc = RC_FX;
604 
605   return NULL;
606   } // end of ThreadOpen
607 
608 /***********************************************************************/
609 /*  TDBTBM constructors.                                               */
610 /***********************************************************************/
TDBTBM(PTBLDEF tdp)611 TDBTBM::TDBTBM(PTBLDEF tdp) : TDBTBL(tdp)
612   {
613   Tmp = NULL;              // To data table TBMT structures
614   Cmp = NULL;              // Current data table TBMT
615   Bmp = NULL;              // To bad (unconnected) TBMT structures
616   Done = false;            // TRUE after first GetAllResults
617   Nrc = 0;                 // Number of remote connections
618   Nlc = 0;                 // Number of local connections
619   } // end of TDBTBL standard constructor
620 
621 /***********************************************************************/
622 /*  Reset read/write position values.                                  */
623 /***********************************************************************/
ResetDB(void)624 void TDBTBM::ResetDB(void)
625   {
626   for (PCOL colp = Columns; colp; colp = colp->GetNext())
627     if (colp->GetAmType() == TYPE_AM_TABID)
628       colp->COLBLK::Reset();
629 
630 	// Local tables
631   for (PTABLE tabp = Tablist; tabp; tabp = tabp->GetNext())
632     tabp->GetTo_Tdb()->ResetDB();
633 
634 	// Remote tables
635 	for (PTBMT tp = Tmp; tp; tp = tp->Next)
636 		tp->Tap->GetTo_Tdb()->ResetDB();
637 
638   Tdbp = (Tablist) ? Tablist->GetTo_Tdb() : NULL;
639   Crp = 0;
640   } // end of ResetDB
641 
642 /***********************************************************************/
643 /*  Returns RowId if b is false or Rownum if b is true.                */
644 /***********************************************************************/
RowNumber(PGLOBAL g,bool b)645 int TDBTBM::RowNumber(PGLOBAL g, bool b)
646   {
647   return Tdbp->RowNumber(g) + ((b) ? 0 : Rows);
648   } // end of RowNumber
649 
650 /***********************************************************************/
651 /*  Returns true if this MYSQL table refers to a local table.          */
652 /***********************************************************************/
IsLocal(PTABLE tbp)653 bool TDBTBM::IsLocal(PTABLE tbp)
654 {
655 	TDBMYSQL *tdbp = (TDBMYSQL*)tbp->GetTo_Tdb();
656 
657 	return ((!stricmp(tdbp->Host, "localhost") ||
658 		       !strcmp(tdbp->Host, "127.0.0.1")) &&
659                        (int) tdbp->Port == (int)GetDefaultPort());
660 }	// end of IsLocal
661 
662 /***********************************************************************/
663 /*  Initialyze table parallel processing.                              */
664 /***********************************************************************/
OpenTables(PGLOBAL g)665 bool TDBTBM::OpenTables(PGLOBAL g)
666   {
667   int    k;
668   THD   *thd = current_thd;
669   PTABLE tabp, *ptabp = &Tablist;
670   PTBMT  tp, *ptp = &Tmp;
671 
672   // Allocates the TBMT blocks for the tables
673   for (tabp = Tablist; tabp; tabp = tabp->Next)
674     if (tabp->GetTo_Tdb()->GetAmType() == TYPE_AM_MYSQL && !IsLocal(tabp)) {
675       // Remove remote table from the local list
676       *ptabp = tabp->Next;
677 
678 			if (trace(1))
679 				htrc("=====> New remote table %s\n", tabp->GetName());
680 
681       // Make the remote table block
682       tp = (PTBMT)PlugSubAlloc(g, NULL, sizeof(TBMT));
683       memset(tp, 0, sizeof(TBMT));
684       tp->G = g;
685 			tp->Ready = false;
686       tp->Tap = tabp;
687       tp->Thd = thd;
688 
689       // Create the thread that will do the table opening.
690       pthread_attr_init(&tp->attr);
691 //    pthread_attr_setdetachstate(&tp->attr, PTHREAD_CREATE_JOINABLE);
692 
693       if ((k = pthread_create(&tp->Tid, &tp->attr, ThreadOpen, tp))) {
694         sprintf(g->Message, "pthread_create error %d", k);
695         Nbc++;
696         continue;
697         } // endif k
698 
699       // Add it to the remote list
700       *ptp = tp;
701       ptp = &tp->Next;
702       Nrc++;         // Number of remote connections
703     } else {
704 			if (trace(1))
705 				htrc("=====> Local table %s\n", tabp->GetName());
706 
707 			ptabp = &tabp->Next;
708       Nlc++;         // Number of local connections
709     } // endif Type
710 
711   return false;
712   } // end of OpenTables
713 
714 /***********************************************************************/
715 /*  TBL Access Method opening routine.                                 */
716 /*  Open first file, other will be opened sequencially when reading.   */
717 /***********************************************************************/
OpenDB(PGLOBAL g)718 bool TDBTBM::OpenDB(PGLOBAL g)
719   {
720   if (trace(1))
721     htrc("TBM OpenDB: tdbp=%p tdb=R%d use=%d key=%p mode=%d\n",
722                       this, Tdb_No, Use, To_Key_Col, Mode);
723 
724   if (Use == USE_OPEN) {
725     /*******************************************************************/
726     /*  Table already open, replace it at its beginning.               */
727     /*******************************************************************/
728     ResetDB();
729     return (Tdbp) ? Tdbp->OpenDB(g) : false;  // Re-open fist table
730     } // endif use
731 
732 #if 0
733   /*********************************************************************/
734   /*  When GetMaxsize was called, To_CondFil was not set yet.          */
735   /*********************************************************************/
736   if (To_CondFil && Tablist) {
737     Tablist = NULL;
738     Nbc = 0;
739     } // endif To_CondFil
740 #endif // 0
741 
742   /*********************************************************************/
743   /*  Make the table list.                                             */
744   /*********************************************************************/
745   if (/*!Tablist &&*/ InitTableList(g))
746     return TRUE;
747 
748   /*********************************************************************/
749   /*  Open all remote tables of the list.                              */
750   /*********************************************************************/
751   if (OpenTables(g))
752     return TRUE;
753 
754   /*********************************************************************/
755   /*  Proceed with local tables.                                       */
756   /*********************************************************************/
757   if ((CurTable = Tablist)) {
758     Tdbp = CurTable->GetTo_Tdb();
759 //  Tdbp->SetMode(Mode);
760 
761     // Check and initialize the subtable columns
762     for (PCOL cp = Columns; cp; cp = cp->GetNext())
763       if (cp->GetAmType() == TYPE_AM_TABID)
764         cp->COLBLK::Reset();
765       else if (((PPRXCOL)cp)->Init(g, NULL) && !Accept)
766         return TRUE;
767 
768     if (trace(1))
769       htrc("Opening subtable %s\n", Tdbp->GetName());
770 
771     // Now we can safely open the table
772     if (Tdbp->OpenDB(g))
773       return TRUE;
774 
775     } // endif *Tablist
776 
777   Use = USE_OPEN;
778   return FALSE;
779   } // end of OpenDB
780 
781 /***********************************************************************/
782 /*  ReadDB: Data Base read routine for MUL access method.              */
783 /***********************************************************************/
ReadDB(PGLOBAL g)784 int TDBTBM::ReadDB(PGLOBAL g)
785   {
786   int rc;
787 
788   if (!Done) {
789     // Get result from local tables
790     if ((rc = TDBTBL::ReadDB(g)) != RC_EF)
791       return rc;
792     else if ((rc = ReadNextRemote(g)) != RC_OK)
793       return rc;
794 
795     Done = true;
796     } // endif Done
797 
798   /*********************************************************************/
799   /*  Now start the reading process of remote tables.                  */
800   /*********************************************************************/
801  retry:
802   rc = Tdbp->ReadDB(g);
803 
804   if (rc == RC_EF) {
805     // Total number of rows met so far
806     Rows += Tdbp->RowNumber(g) - 1;
807     Crp += Tdbp->GetProgMax(g);
808     Cmp->Complete = true;
809 
810     if ((rc = ReadNextRemote(g)) == RC_OK)
811       goto retry;
812 
813   } else if (rc == RC_FX)
814     strcat(strcat(strcat(g->Message, " ("), Tdbp->GetName()), ")");
815 
816   return rc;
817   } // end of ReadDB
818 
819 /***********************************************************************/
820 /*  ReadNext: Continue reading from next table.                        */
821 /***********************************************************************/
ReadNextRemote(PGLOBAL g)822 int TDBTBM::ReadNextRemote(PGLOBAL g)
823   {
824   bool b;
825 
826   if (Tdbp)
827     Tdbp->CloseDB(g);
828 
829   Cmp = NULL;
830 
831  retry:
832 	b = false;
833 
834 	// Search for a remote table having its result set
835 	pthread_mutex_lock(&tblmut);
836 	for (PTBMT  tp = Tmp; tp; tp = tp->Next)
837 		if (tp->Rc != RC_FX) {
838 			if (tp->Ready) {
839 				if (!tp->Complete) {
840 					Cmp = tp;
841 					break;
842 				}	// endif Complete
843 
844 			} else
845 				b = true;
846 
847 		}	// endif Rc
848 
849 	pthread_mutex_unlock(&tblmut);
850 
851   if (!Cmp) {
852     if (b) {          // more result to come
853 //    sleep(20);
854       goto retry;
855     } else
856       return RC_EF;
857 
858     } // endif Curtable
859 
860   Tdbp = Cmp->Tap->GetTo_Tdb();
861 
862   // Check and initialize the subtable columns
863   for (PCOL cp = Columns; cp; cp = cp->GetNext())
864     if (cp->GetAmType() == TYPE_AM_TABID)
865       cp->COLBLK::Reset();
866     else if (((PPRXCOL)cp)->Init(g, NULL) && !Accept)
867       return RC_FX;
868 
869   if (trace(1))
870     htrc("Reading subtable %s\n", Tdbp->GetName());
871 
872   return RC_OK;
873   } // end of ReadNextRemote
874 #endif   // DEVELOPMENT
875 
876 /* ------------------------------------------------------------------- */
877