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