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