1 /* Copyright (C) Olivier Bertrand 2004 - 2017
2    Copyright (C) MariaDB Corporation Ab
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
17 
18 /***********************************************************************/
19 /*  Author Olivier BERTRAND  bertrandop@gmail.com         2004-2019    */
20 /*                                                                     */
21 /*  WHAT THIS PROGRAM DOES:                                            */
22 /*  -----------------------                                            */
23 /*  This program are the CONNECT general purpose semantic routines.    */
24 /***********************************************************************/
25 #ifdef USE_PRAGMA_IMPLEMENTATION
26 #pragma implementation        // gcc: Class implementation
27 #endif
28 
29 /***********************************************************************/
30 /*  Include application header files                                   */
31 /*                                                                     */
32 /*  global.h     is header containing all global declarations.         */
33 /*  plgdbsem.h   is header containing the DB applic. declarations.     */
34 /***********************************************************************/
35 #define DONT_DEFINE_VOID
36 #include <my_global.h>
37 #include "handler.h"
38 #undef  OFFSET
39 
40 #include "global.h"
41 #include "plgdbsem.h"
42 #include "xobject.h"
43 #include "connect.h"
44 #include "tabcol.h"
45 #include "catalog.h"
46 #include "ha_connect.h"
47 
48 #define my_strupr(p) my_caseup_str(default_charset_info, (p));
49 #define my_strlwr(p) my_casedn_str(default_charset_info, (p));
50 #define my_stricmp(a, b) my_strcasecmp(default_charset_info, (a), (b))
51 
52 /***********************************************************************/
53 /*  Routines called internally by semantic routines.                   */
54 /***********************************************************************/
55 void  CntEndDB(PGLOBAL);
56 RCODE EvalColumns(PGLOBAL g, PTDB tdbp, bool reset, bool mrr= false);
57 
58 /***********************************************************************/
59 /*  MySQL routines called externally by semantic routines.             */
60 /***********************************************************************/
61 int rename_file_ext(const char *from, const char *to,const char *ext);
62 
63 /***********************************************************************/
64 /*  CntExit: CONNECT termination routine.                              */
65 /***********************************************************************/
CntExit(PGLOBAL g)66 PGLOBAL CntExit(PGLOBAL g)
67   {
68   if (g) {
69     CntEndDB(g);
70 
71 		if (g->Activityp) {
72 			delete g->Activityp;
73 			g->Activityp = NULL;
74 		}	// endif Activityp
75 
76     g= PlugExit(g);
77     } // endif g
78 
79   return g;
80   } // end of CntExit
81 
82 /***********************************************************************/
83 /*  CntEndDB: DB termination semantic routine.                         */
84 /***********************************************************************/
CntEndDB(PGLOBAL g)85 void CntEndDB(PGLOBAL g)
86 {
87   PDBUSER dbuserp= PlgGetUser(g);
88 
89   if (dbuserp) {
90     if (dbuserp->Catalog)
91       delete dbuserp->Catalog;
92 
93     free(dbuserp);
94 
95 		if (trace(1))
96 			htrc("CntEndDB: Freeing Dup\n");
97 
98 		g->Activityp->Aptr = NULL;
99 	} // endif dbuserp
100 
101 } // end of CntEndDB
102 
103 /***********************************************************************/
104 /*  CntCheckDB: Initialize a DB application session.                   */
105 /*  Note: because MySQL does not call a storage handler when a user    */
106 /*  executes a use db command, a check must be done before an SQL      */
107 /*  command is executed to check whether we are still working on the   */
108 /*  current database, and if not to load the newly used database.      */
109 /***********************************************************************/
CntCheckDB(PGLOBAL g,PHC handler,const char * pathname)110 bool CntCheckDB(PGLOBAL g, PHC handler, const char *pathname)
111   {
112   bool    rc= false;
113   PDBUSER dbuserp= PlgGetUser(g);
114 
115   if (trace(1)) {
116     printf("CntCheckDB: dbuserp=%p\n", dbuserp);
117     } // endif trace
118 
119   if (!dbuserp || !handler)
120     return true;
121 
122   if (trace(1))
123     printf("cat=%p oldhandler=%p newhandler=%p\n", dbuserp->Catalog,
124     (dbuserp->Catalog) ? ((MYCAT*)dbuserp->Catalog)->GetHandler() : NULL,
125            handler);
126 
127   // Set the database path for this table
128 	if (handler->SetDataPath(g, pathname))
129 		return true;
130 
131   if (dbuserp->Catalog) {
132     return false;                       // Nothing else to do
133     } // endif Catalog
134 
135   // Copy new database name in dbuser block
136   strncpy(dbuserp->Name, "???", sizeof(dbuserp->Name) - 1);
137 
138   dbuserp->Vtdbno= 0;                      // Init of TDB numbers
139 
140   /*********************************************************************/
141   /*  Now allocate and initialize the Database Catalog.                */
142   /*********************************************************************/
143   dbuserp->Step= MSG(READY);
144 
145   if (!(dbuserp->Catalog= new MYCAT(handler)))
146     return true;
147 
148   /*********************************************************************/
149   /*  All is correct.                                                  */
150   /*********************************************************************/
151   sprintf(g->Message, MSG(DATABASE_LOADED), "???");
152 
153   if (trace(1))
154     printf("msg=%s\n", g->Message);
155 
156   return rc;
157   } // end of CntCheckDB
158 
159 /***********************************************************************/
160 /*  CntInfo: Get table info.                                           */
161 /*  Returns valid: true if this is a table info.                       */
162 /***********************************************************************/
CntInfo(PGLOBAL g,PTDB tp,PXF info)163 bool CntInfo(PGLOBAL g, PTDB tp, PXF info)
164 {
165   if (tp) {
166 		bool    b = (tp->GetFtype() == RECFM_NAF);
167 		PTDBDOS tdbp = b ? NULL : (PTDBDOS)tp;
168 
169 		info->data_file_length = (b) ? 0 : (ulonglong)tdbp->GetFileLength(g);
170 
171     if (b || info->data_file_length)
172       info->records= (unsigned)tp->Cardinality(g);
173 //    info->records= (unsigned)tp->GetMaxSize(g);
174     else
175       info->records= 0;
176 
177 //  info->mean_rec_length= tdbp->GetLrecl();
178     info->mean_rec_length= 0;
179     info->data_file_name= (b) ? NULL : (char*)tdbp->GetFile(g);
180     return true;
181   } else {
182     info->data_file_length= 0;
183     info->records= 0;
184     info->mean_rec_length= 0;
185     info->data_file_name= NULL;
186     return false;
187   } // endif tdbp
188 
189 } // end of CntInfo
190 
191 /***********************************************************************/
192 /*  GetTDB: Get the table description block of a CONNECT table.        */
193 /***********************************************************************/
CntGetTDB(PGLOBAL g,LPCSTR name,MODE mode,PHC h)194 PTDB CntGetTDB(PGLOBAL g, LPCSTR name, MODE mode, PHC h)
195 {
196 	PTDB    tdbp = NULL;
197 	PTABLE  tabp;
198 	PDBUSER dup = PlgGetUser(g);
199 	volatile PCATLG  cat = (dup) ? dup->Catalog : NULL;  // Safe over throw
200 
201 	if (trace(1))
202 		printf("CntGetTDB: name=%s mode=%d cat=%p\n", name, mode, cat);
203 
204 	if (!cat)
205 		return NULL;
206 
207 	try {
208 		// Get table object from the catalog
209 		tabp = new(g) XTAB(name);
210 
211 		if (trace(1))
212 			printf("CntGetTDB: tabp=%p\n", tabp);
213 
214 		// Perhaps this should be made thread safe
215 		((MYCAT*)cat)->SetHandler(h);
216 
217 		if (!(tdbp = cat->GetTable(g, tabp, mode)))
218 			printf("CntGetTDB: %s\n", g->Message);
219 
220 	} catch (int n) {
221 		if (trace(1))
222 			htrc("Exception %d: %s\n", n, g->Message);
223   } catch (const char *msg) {
224 		strcpy(g->Message, msg);
225 	}	// end catch
226 
227   if (trace(1))
228     printf("Returning tdbp=%p mode=%d\n", tdbp, mode);
229 
230   return tdbp;
231 } // end of CntGetTDB
232 
233 /***********************************************************************/
234 /*  OPENTAB: Open a Table.                                             */
235 /***********************************************************************/
CntOpenTable(PGLOBAL g,PTDB tdbp,MODE mode,char * c1,char * c2,bool del,PHC)236 bool CntOpenTable(PGLOBAL g, PTDB tdbp, MODE mode, char *c1, char *c2,
237                                         bool del, PHC)
238   {
239   char   *p;
240   int     i, n;
241   bool    rcop= true;
242   PCOL    colp;
243 //PCOLUMN cp;
244   PDBUSER dup= PlgGetUser(g);
245 
246   if (trace(1))
247     printf("CntOpenTable: tdbp=%p mode=%d\n", tdbp, mode);
248 
249   if (!tdbp) {
250     strcpy(g->Message, "Null tdbp");
251     printf("CntOpenTable: %s\n", g->Message);
252     return true;
253     } // endif tdbp
254 
255 	try {
256 		if (!c1) {
257 //		if (mode == MODE_INSERT)	 or CHECK TABLE
258 				// Allocate all column blocks for that table
259 				tdbp->ColDB(g, NULL, 0);
260 
261 		} else for (p = c1; *p; p += n) {
262 			// Allocate only used column blocks
263 			if (trace(1))
264 				printf("Allocating column %s\n", p);
265 
266 			g->Message[0] = 0;    // To check whether ColDB made an error message
267 			colp = tdbp->ColDB(g, p, 0);
268 
269 			if (!colp && !(mode == MODE_INSERT && tdbp->IsSpecial(p))) {
270 				if (g->Message[0] == 0)
271 					sprintf(g->Message, MSG(COL_ISNOT_TABLE), p, tdbp->GetName());
272 
273 				throw 1;
274 			} // endif colp
275 
276 			n = strlen(p) + 1;
277 		} // endfor p
278 
279 		for (i = 0, colp = tdbp->GetColumns(); colp; i++, colp = colp->GetNext()) {
280 			if (colp->InitValue(g))
281 				throw 2;
282 
283 			if (mode == MODE_INSERT)
284 				// Allow type conversion
285 				if (colp->SetBuffer(g, colp->GetValue(), true, false))
286 					throw 3;
287 
288 			colp->AddColUse(U_P);           // For PLG tables
289 		} // endfor colp
290 
291 		/*******************************************************************/
292 		/* In Update mode, the updated column blocks must be distinct from */
293 		/* the read column blocks. So make a copy of the TDB and allocate  */
294 		/* its column blocks in mode write (required by XML tables).       */
295 		/*******************************************************************/
296 		if (mode == MODE_UPDATE) {
297 			PTDBASE utp;
298 
299 			if (!(utp = (PTDBASE)tdbp->Duplicate(g))) {
300 				sprintf(g->Message, MSG(INV_UPDT_TABLE), tdbp->GetName());
301 				throw 4;
302 			} // endif tp
303 
304 			if (!c2)
305 				// Allocate all column blocks for that table
306 				utp->ColDB(g, NULL, 0);
307 			else for (p = c2; *p; p += n) {
308 				// Allocate only used column blocks
309 				colp = utp->ColDB(g, p, 0);
310 				n = strlen(p) + 1;
311 			} // endfor p
312 
313 			for (i = 0, colp = utp->GetColumns(); colp; i++, colp = colp->GetNext()) {
314 				if (colp->InitValue(g))
315 					throw 5;
316 
317 				if (colp->SetBuffer(g, colp->GetValue(), true, false))
318 					throw 6;
319 
320 			} // endfor colp
321 
322 		// Attach the updated columns list to the main table
323 			tdbp->SetSetCols(utp->GetColumns());
324 		} else if (tdbp && mode == MODE_INSERT)
325 			tdbp->SetSetCols(tdbp->GetColumns());
326 
327 		// Now do open the physical table
328 		if (trace(1))
329 			printf("Opening table %s in mode %d tdbp=%p\n",
330 				tdbp->GetName(), mode, tdbp);
331 
332 		//tdbp->SetMode(mode);
333 
334 		if (del/* && (tdbp->GetFtype() != RECFM_NAF*/) {
335 			// To avoid erasing the table when doing a partial delete
336 			// make a fake Next
337 //    PDOSDEF ddp= new(g) DOSDEF;
338 //    PTDB tp= new(g) TDBDOS(ddp, NULL);
339 			tdbp->SetNext((PTDB)1);
340 			dup->Check &= ~CHK_DELETE;
341 		} // endif del
342 
343 
344 		if (trace(1))
345 			printf("About to open the table: tdbp=%p\n", tdbp);
346 
347 		if (mode != MODE_ANY && mode != MODE_ALTER) {
348 			if (tdbp->OpenDB(g)) {
349 				printf("%s\n", g->Message);
350 				throw 7;
351 			} else
352 				tdbp->SetNext(NULL);
353 
354 		} // endif mode
355 
356 		rcop = false;
357 	} catch (int n) {
358 		if (trace(1))
359 			htrc("Exception %d: %s\n", n, g->Message);
360 	} catch (const char *msg) {
361 		strcpy(g->Message, msg);
362 	} // end catch
363 
364   return rcop;
365   } // end of CntOpenTable
366 
367 /***********************************************************************/
368 /*  Rewind a table by reopening it.                                    */
369 /***********************************************************************/
CntRewindTable(PGLOBAL g,PTDB tdbp)370 bool CntRewindTable(PGLOBAL g, PTDB tdbp)
371 {
372   if (!tdbp)
373     return true;
374 
375   tdbp->OpenDB(g);
376   return false;
377 } // end of CntRewindTable
378 
379 /***********************************************************************/
380 /*  Evaluate all columns after a record is read.                       */
381 /***********************************************************************/
EvalColumns(PGLOBAL g,PTDB tdbp,bool reset,bool mrr)382 RCODE EvalColumns(PGLOBAL g, PTDB tdbp, bool reset, bool mrr)
383 {
384   RCODE rc= RC_OK;
385   PCOL  colp;
386 
387 	try {
388 		for (colp = tdbp->GetColumns(); rc == RC_OK && colp;
389 			colp = colp->GetNext()) {
390 			xtrc(2, "Going to read column %s of table %s\n",
391 				colp->GetName(), tdbp->GetName());
392 
393 			if (reset)
394 				colp->Reset();
395 
396 			// Virtual columns are computed by MariaDB
397 			if (!colp->GetColUse(U_VIRTUAL) && (!mrr || colp->GetKcol()))
398 				if (colp->Eval(g))
399 					rc = RC_FX;
400 
401 		} // endfor colp
402 
403 	} catch (int n) {
404 		if (trace(1))
405 			printf("Error %d reading columns: %s\n", n, g->Message);
406 
407 		rc = RC_FX;
408 	} catch (const char *msg) {
409 		strcpy(g->Message, msg);
410 		rc = RC_NF;
411 	} // end catch
412 
413   return rc;
414 } // end of EvalColumns
415 
416 /***********************************************************************/
417 /*  ReadNext: Read next record sequentially.                           */
418 /***********************************************************************/
CntReadNext(PGLOBAL g,PTDB tdbp)419 RCODE CntReadNext(PGLOBAL g, PTDB tdbp)
420 {
421   RCODE rc;
422 
423   if (!tdbp)
424     return RC_FX;
425   else if (tdbp->GetKindex()) {
426     // Reading sequencially an indexed table. This happens after the
427     // handler function records_in_range was called and MySQL decides
428     // to quit using the index (!!!) Drop the index.
429 //  for (PCOL colp= tdbp->GetColumns(); colp; colp= colp->GetNext())
430 //    colp->SetKcol(NULL);
431 
432     ((PTDBASE)tdbp)->ResetKindex(g, NULL);
433     } // endif index
434 
435 	try {
436 		// Do it now to avoid double eval when filtering
437 		for (PCOL colp = tdbp->GetColumns(); colp; colp = colp->GetNext())
438 			colp->Reset();
439 
440 		do {
441 			if ((rc = (RCODE)tdbp->ReadDB(g)) == RC_OK)
442 				if (!ApplyFilter(g, tdbp->GetFilter()))
443 					rc = RC_NF;
444 
445 		} while (rc == RC_NF);
446 
447 		if (rc == RC_OK)
448 			rc = EvalColumns(g, tdbp, false);
449 
450 	} catch (int) {
451 	  rc = RC_FX;
452   } catch (const char *msg) {
453 	  strcpy(g->Message, msg);
454 		rc = RC_FX;
455   } // end catch
456 
457   return rc;
458 } // end of CntReadNext
459 
460 /***********************************************************************/
461 /*  WriteRow: Insert a new row into a table.                           */
462 /***********************************************************************/
CntWriteRow(PGLOBAL g,PTDB tdbp)463 RCODE  CntWriteRow(PGLOBAL g, PTDB tdbp)
464 {
465 	RCODE   rc;
466 	PCOL    colp;
467 	//PTDBASE tp= (PTDBASE)tdbp;
468 
469 	if (!tdbp)
470 		return RC_FX;
471 
472 	try {
473 		// Store column values in table write buffer(s)
474 		for (colp = tdbp->GetSetCols(); colp; colp = colp->GetNext())
475 			if (!colp->GetColUse(U_VIRTUAL))
476 				colp->WriteColumn(g);
477 
478 		if (tdbp->IsIndexed())
479 			// Index values must be sorted before updating
480 			rc = (RCODE)((PTDBDOS)tdbp)->GetTxfp()->StoreValues(g, true);
481 		else
482 			// Return result code from write operation
483 			rc = (RCODE)tdbp->WriteDB(g);
484 
485 	} catch (int n) {
486 		printf("Exception %d: %s\n", n, g->Message);
487 		rc = RC_FX;
488 	} catch (const char *msg) {
489 		strcpy(g->Message, msg);
490 		rc = RC_FX;
491 	} // end catch
492 
493 	return rc;
494 } // end of CntWriteRow
495 
496 /***********************************************************************/
497 /*  UpdateRow: Update a row into a table.                              */
498 /***********************************************************************/
CntUpdateRow(PGLOBAL g,PTDB tdbp)499 RCODE CntUpdateRow(PGLOBAL g, PTDB tdbp)
500   {
501   if (!tdbp || tdbp->GetMode() != MODE_UPDATE)
502     return RC_FX;
503 
504   // Return result code from write operation
505   return CntWriteRow(g, tdbp);
506   } // end of CntUpdateRow
507 
508 /***********************************************************************/
509 /*  DeleteRow: Delete a row from a table.                              */
510 /***********************************************************************/
CntDeleteRow(PGLOBAL g,PTDB tdbp,bool all)511 RCODE  CntDeleteRow(PGLOBAL g, PTDB tdbp, bool all)
512   {
513   RCODE   rc;
514 //PTDBASE tp= (PTDBASE)tdbp;
515 
516   if (!tdbp || tdbp->GetMode() != MODE_DELETE)
517     return RC_FX;
518   else if (tdbp->IsReadOnly())
519     return RC_NF;
520 
521   if (all) {
522     if (tdbp->GetDef()->Indexable())
523       ((PTDBDOS)tdbp)->Cardinal= 0;
524 
525     // Note: if all, this call will be done when closing the table
526     rc= (RCODE)tdbp->DeleteDB(g, RC_FX);
527 //} else if (tdbp->GetKindex() && !((PTDBASE)tdbp)->GetKindex()->IsSorted() &&
528 //           ((PTDBASE)tdbp)->Txfp->GetAmType() != TYPE_AM_DBF) {
529   } else if(tdbp->IsIndexed()) {
530     // Index values must be sorted before updating
531     rc= (RCODE)((PTDBDOS)tdbp)->GetTxfp()->StoreValues(g, false);
532   } else // Return result code from delete operation
533     rc= (RCODE)tdbp->DeleteDB(g, RC_OK);
534 
535   return rc;
536   } // end of CntDeleteRow
537 
538 /***********************************************************************/
539 /*  CLOSETAB: Close a table.                                           */
540 /***********************************************************************/
CntCloseTable(PGLOBAL g,PTDB tdbp,bool nox,bool abort)541 int CntCloseTable(PGLOBAL g, PTDB tdbp, bool nox, bool abort)
542 {
543 	int     rc = RC_OK;
544 	//TDBASE *tbxp= (PTDBASE)tdbp;
545 
546 	if (!tdbp)
547 		return rc;                           // Nothing to do
548 	else if (tdbp->GetUse() != USE_OPEN) {
549 		if (tdbp->GetAmType() == TYPE_AM_XML)
550 			tdbp->CloseDB(g);                  // Opened by GetMaxSize
551 
552 		return rc;
553 	} // endif !USE_OPEN
554 
555 	if (trace(1))
556 		printf("CntCloseTable: tdbp=%p mode=%d nox=%d abort=%d\n",
557 			tdbp, tdbp->GetMode(), nox, abort);
558 
559 	if (tdbp->GetMode() == MODE_DELETE && tdbp->GetUse() == USE_OPEN) {
560 		if (tdbp->IsIndexed())
561 			rc = ((PTDBDOS)tdbp)->GetTxfp()->DeleteSortedRows(g);
562 
563 		if (!rc)
564 			rc = tdbp->DeleteDB(g, RC_EF);    // Specific A.M. delete routine
565 
566 	} else if (tdbp->GetMode() == MODE_UPDATE && tdbp->IsIndexed())
567 		rc = ((PTDBDOS)tdbp)->GetTxfp()->UpdateSortedRows(g);
568 
569 	switch (rc) {
570 		case RC_FX:
571 			abort = true;
572 			break;
573 		case RC_INFO:
574 			PushWarning(g, tdbp);
575 			break;
576 	} // endswitch rc
577 
578 	try {
579 		//  This will close the table file(s) and also finalize write
580 		//  operations such as Insert, Update, or Delete.
581 		tdbp->SetAbort(abort);
582 		tdbp->CloseDB(g);
583 		tdbp->SetAbort(false);
584 
585 		if (trace(2))
586 			printf("Table %s closed\n", tdbp->GetName());
587 
588 		if (!nox && tdbp->GetMode() != MODE_READ && tdbp->GetMode() != MODE_ANY) {
589 			if (trace(2))
590 				printf("About to reset opt\n");
591 
592 			if (!tdbp->IsRemote()) {
593 				// Make all the eventual indexes
594 				PTDBDOS tbxp = (PTDBDOS)tdbp;
595 				tbxp->ResetKindex(g, NULL);
596 				tbxp->SetKey_Col(NULL);
597 				rc = tbxp->ResetTableOpt(g, true, tbxp->GetDef()->Indexable() == 1);
598 			} // endif remote
599 
600 		} // endif nox
601 
602 	} catch (int) {
603 		rc = RC_FX;
604 	} catch (const char *msg) {
605 		strcpy(g->Message, msg);
606 		rc = RC_FX;
607 	} // end catch
608 
609 	if (trace(2))
610 		htrc("Done rc=%d\n", rc);
611 
612 	return (rc == RC_OK || rc == RC_INFO) ? 0 : rc;
613 } // end of CntCloseTable
614 
615 /***********************************************************************/
616 /*  Load and initialize the use of an index.                           */
617 /*  This is the condition(s) for doing indexing.                       */
618 /*  Note: FIX table are not reset here to Nrec= 1.                     */
619 /***********************************************************************/
CntIndexInit(PGLOBAL g,PTDB ptdb,int id,bool sorted)620 int CntIndexInit(PGLOBAL g, PTDB ptdb, int id, bool sorted)
621   {
622   PIXDEF  xdp;
623   PTDBDOS tdbp;
624   DOSDEF *dfp;
625 
626   if (!ptdb)
627     return -1;
628   else if (!ptdb->GetDef()->Indexable()) {
629     sprintf(g->Message, MSG(TABLE_NO_INDEX), ptdb->GetName());
630     return 0;
631   } else if (ptdb->GetDef()->Indexable() == 3) {
632     return 1;
633   } else
634     tdbp= (PTDBDOS)ptdb;
635 
636   dfp= (DOSDEF*)tdbp->GetDef();
637 
638 //if (!(k= colp->GetKey()))
639 //  if (colp->GetOpt() >= 2) {
640 //    strcpy(g->Message, "Not a valid indexed column");
641 //    return -1;
642 //  } else
643       // This is a pseudo indexed sorted block optimized column
644 //    return 0;
645 
646   if (tdbp->GetKindex())
647     if (((XXBASE*)tdbp->GetKindex())->GetID() == id) {
648       tdbp->GetKindex()->Reset();                // Same index
649       return (tdbp->GetKindex()->IsMul()) ? 2 : 1;
650     } else {
651       tdbp->GetKindex()->Close();
652       tdbp->SetKindex(NULL);
653     } // endif colp
654 
655   for (xdp= dfp->GetIndx(); xdp; xdp= xdp->GetNext())
656     if (xdp->GetID() == id)
657       break;
658 
659   if (!xdp) {
660     sprintf(g->Message, "Wrong index ID %d", id);
661     return 0;
662     } // endif xdp
663 
664 #if 0
665   if (xdp->IsDynamic()) {
666     // This is a dynamically created index (KINDEX)
667     // It should not be created now, if called by index range
668     tdbp->SetXdp(xdp);
669     return (xdp->IsUnique()) ? 1 : 2;
670     } // endif dynamic
671 #endif // 0
672 
673   // Static indexes must be initialized now for records_in_range
674   if (tdbp->InitialyzeIndex(g, xdp, sorted))
675     return 0;
676 
677   return (tdbp->GetKindex()->IsMul()) ? 2 : 1;
678   } // end of CntIndexInit
679 
680 #if defined(WORDS_BIGENDIAN)
681 /***********************************************************************/
682 /*  Swap bytes of the key that are written in little endian order.     */
683 /***********************************************************************/
SetSwapValue(PVAL valp,char * kp)684 static void SetSwapValue(PVAL valp, char *kp)
685 {
686   if (valp->IsTypeNum() && valp->GetType() != TYPE_DECIM) {
687     uchar buf[8];
688     int   i, k= valp->GetClen();
689 
690     for (i = 0; k > 0;)
691       buf[i++]= kp[--k];
692 
693 
694 
695     valp->SetBinValue((void*)buf);
696   } else
697     valp->SetBinValue((void*)kp);
698 
699 } // end of SetSwapValue
700 #endif   // WORDS_BIGENDIAN
701 
702 /***********************************************************************/
703 /*  IndexRead: fetch a record having the index value.                  */
704 /***********************************************************************/
CntIndexRead(PGLOBAL g,PTDB ptdb,OPVAL op,const key_range * kr,bool mrr)705 RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op,
706                    const key_range *kr, bool mrr)
707   {
708   int     n, x;
709   RCODE   rc;
710   XXBASE *xbp;
711 	PTDBDOS tdbp;
712 
713   if (!ptdb)
714     return RC_FX;
715   else
716     x= ptdb->GetDef()->Indexable();
717 
718   if (!x) {
719     sprintf(g->Message, MSG(TABLE_NO_INDEX), ptdb->GetName());
720     return RC_FX;
721   } else if (x == 2) {
722     // Remote index. Only used in read mode
723     if ((ptdb->GetMode() == MODE_READ || ptdb->GetMode() == MODE_READX)
724 			               && op != OP_SAME && ptdb->ReadKey(g, op, kr))
725       return RC_FX;
726 
727     goto rnd;
728   } else if (x == 3) {
729     if (kr)
730       ((PTDBASE)ptdb)->SetRecpos(g, *(int*)kr->key);
731 
732     if (op == OP_SAME)
733       return RC_NF;
734 
735     goto rnd;
736   } else
737     tdbp= (PTDBDOS)ptdb;
738 
739   // Set reference values and index operator
740   if (!tdbp->GetLink() || !tdbp->GetKindex()) {
741 //  if (!tdbp->To_Xdp) {
742       sprintf(g->Message, "Index not initialized for table %s", tdbp->GetName());
743       return RC_FX;
744 #if 0
745       } // endif !To_Xdp
746     // Now it's time to make the dynamic index
747     if (tdbp->InitialyzeIndex(g, NULL, false)) {
748       sprintf(g->Message, "Fail to make dynamic index %s",
749                           tdbp->To_Xdp->GetName());
750       return RC_FX;
751       } // endif MakeDynamicIndex
752 #endif // 0
753     } // endif !To_Kindex
754 
755   xbp= (XXBASE*)tdbp->GetKindex();
756 
757   if (kr) {
758 		char   *kp= (char*)kr->key;
759 		int     len= kr->length;
760 		short   lg;
761 		bool    rcb;
762 		PVAL    valp;
763 		PCOL    colp;
764 
765     for (n= 0; n < tdbp->GetKnum(); n++) {
766       colp= (PCOL)tdbp->Key(n);
767 
768       if (colp->GetColUse(U_NULLS))
769         kp++;                   // Skip null byte
770 
771       valp= tdbp->Link(n)->GetValue();
772 
773       if (!valp->IsTypeNum()) {
774         if (colp->GetColUse(U_VAR)) {
775 #if defined(WORDS_BIGENDIAN)
776           ((char*)&lg)[0]= ((char*)kp)[1];
777           ((char*)&lg)[1]= ((char*)kp)[0];
778 #else   // !WORDS_BIGENDIAN
779           lg= *(short*)kp;
780 #endif   //!WORDS_BIGENDIAN
781           kp+= sizeof(short);
782           rcb= valp->SetValue_char(kp, (int)lg);
783         } else
784           rcb= valp->SetValue_char(kp, valp->GetClen());
785 
786         if (rcb) {
787           if (tdbp->RowNumber(g))
788             sprintf(g->Message, "Out of range value for column %s at row %d",
789                     colp->GetName(), tdbp->RowNumber(g));
790           else
791             sprintf(g->Message, "Out of range value for column %s",
792                     colp->GetName());
793 
794           PushWarning(g, tdbp);
795           } // endif b
796 
797       } else
798 #if defined(WORDS_BIGENDIAN)
799         SetSwapValue(valp, kp);
800 #else   // !WORDS_BIGENDIAN
801         valp->SetBinValue((void*)kp);
802 #endif   //!WORDS_BIGENDIAN
803 
804       kp+= valp->GetClen();
805 
806       if (len == kp - (char*)kr->key) {
807         n++;
808         break;
809       } else if (len < kp - (char*)kr->key) {
810         strcpy(g->Message, "Key buffer is too small");
811         return RC_FX;
812       } // endif len
813 
814       } // endfor n
815 
816     xbp->SetNval(n);
817     } // endif key
818 
819   xbp->SetOp(op);
820   xbp->SetNth(0);
821 
822  rnd:
823   if ((rc= (RCODE)ptdb->ReadDB(g)) == RC_OK)
824     rc= EvalColumns(g, ptdb, true, mrr);
825 
826   return rc;
827   } // end of CntIndexRead
828 
829 /***********************************************************************/
830 /*  Return the number of rows matching given values.                   */
831 /***********************************************************************/
CntIndexRange(PGLOBAL g,PTDB ptdb,const uchar ** key,uint * len,bool * incl,key_part_map * kmap)832 int CntIndexRange(PGLOBAL g, PTDB ptdb, const uchar* *key, uint *len,
833                    bool *incl, key_part_map *kmap)
834   {
835   const uchar *p, *kp;
836   int     i, n, x, k[2];
837   short   lg;
838   bool    b, rcb;
839   PVAL    valp;
840   PCOL    colp;
841   PTDBDOS tdbp;
842   XXBASE *xbp;
843 
844   if (!ptdb)
845     return -1;
846 
847   x= ptdb->GetDef()->Indexable();
848 
849   if (!x) {
850     sprintf(g->Message, MSG(TABLE_NO_INDEX), ptdb->GetName());
851     DBUG_PRINT("Range", ("%s", g->Message));
852     return -1;
853   } else if (x == 2) {
854     // Remote index
855     return 2;
856   } else if (x == 3) {
857     // Virtual index
858     for (i= 0; i < 2; i++)
859       if (key[i])
860         k[i] = *(int*)key[i] + (incl[i] ? 0 : 1 - 2 * i);
861       else
862         k[i] = (i) ? ptdb->Cardinality(g) : 1;
863 
864     return k[1] - k[0] + 1;
865   } else
866     tdbp= (PTDBDOS)ptdb;
867 
868   if (!tdbp->GetKindex() || !tdbp->GetLink()) {
869     if (!tdbp->GetXdp()) {
870       sprintf(g->Message, "Index not initialized for table %s", tdbp->GetName());
871       DBUG_PRINT("Range", ("%s", g->Message));
872       return -1;
873     } else       // Dynamic index
874       return tdbp->GetXdp()->GetMaxSame();     // TODO a better estimate
875 
876   } else
877     xbp= (XXBASE*)tdbp->GetKindex();
878 
879   for (b= false, i= 0; i < 2; i++) {
880     p= kp= key[i];
881 
882     if (kp) {
883       for (n= 0; n < tdbp->GetKnum(); n++) {
884         if (kmap[i] & (key_part_map)(1 << n)) {
885           if (b == true)
886             // Cannot do indexing with missing intermediate key
887             return -1;
888 
889           colp= (PCOL)tdbp->Key(n);
890 
891           if (colp->GetColUse(U_NULLS))
892             p++;                   // Skip null byte  ???
893 
894           valp= tdbp->Link(n)->GetValue();
895 
896           if (!valp->IsTypeNum()) {
897             if (colp->GetColUse(U_VAR)) {
898 #if defined(WORDS_BIGENDIAN)
899               ((char*)&lg)[0]= ((char*)p)[1];
900               ((char*)&lg)[1]= ((char*)p)[0];
901 #else   // !WORDS_BIGENDIAN
902               lg= *(short*)p;
903 #endif   //!WORDS_BIGENDIAN
904               p+= sizeof(short);
905               rcb= valp->SetValue_char((char*)p, (int)lg);
906             } else
907               rcb= valp->SetValue_char((char*)p, valp->GetClen());
908 
909           if (rcb) {
910             if (tdbp->RowNumber(g))
911               sprintf(g->Message,
912                       "Out of range value for column %s at row %d",
913                       colp->GetName(), tdbp->RowNumber(g));
914             else
915               sprintf(g->Message, "Out of range value for column %s",
916                       colp->GetName());
917 
918             PushWarning(g, tdbp);
919             } // endif b
920 
921           } else
922 #if defined(WORDS_BIGENDIAN)
923             SetSwapValue(valp, (char*)p);
924 #else   // !WORDS_BIGENDIAN
925             valp->SetBinValue((void*)p);
926 #endif  // !WORDS_BIGENDIAN
927 
928           if (trace(1)) {
929             char bf[32];
930             printf("i=%d n=%d key=%s\n", i, n, valp->GetCharString(bf));
931             } // endif trace
932 
933           p+= valp->GetClen();
934 
935           if (len[i] == (unsigned)(p - kp)) {
936             n++;
937             break;
938           } else if (len[i] < (unsigned)(p - kp)) {
939             strcpy(g->Message, "Key buffer is too small");
940             return -1;
941           } // endif len
942 
943         } else
944           b= true;
945 
946         } // endfor n
947 
948       xbp->SetNval(n);
949 
950       if (trace(1))
951         printf("xbp=%p Nval=%d i=%d incl=%d\n", xbp, n, i, incl[i]);
952 
953       k[i]= xbp->Range(g, i + 1, incl[i]);
954     } else
955       k[i]= (i) ? xbp->GetNum_K() : 0;
956 
957     } // endfor i
958 
959   if (trace(1))
960     printf("k1=%d k0=%d\n", k[1], k[0]);
961 
962   return k[1] - k[0];
963   } // end of CntIndexRange
964