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 | /***********************************************************************/ |
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 | /***********************************************************************/ |
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 | 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 | /***********************************************************************/ |
86 | void 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 | /***********************************************************************/ |
111 | bool 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 | /***********************************************************************/ |
164 | bool 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 | /***********************************************************************/ |
195 | PTDB 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 | /***********************************************************************/ |
237 | bool 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 | /***********************************************************************/ |
372 | bool 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 | /***********************************************************************/ |
384 | RCODE 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 | /***********************************************************************/ |
418 | RCODE 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 | /***********************************************************************/ |
462 | RCODE 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 | /***********************************************************************/ |
498 | RCODE 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 | /***********************************************************************/ |
510 | RCODE 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 | /***********************************************************************/ |
540 | int 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 | /***********************************************************************/ |
619 | int 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 | /***********************************************************************/ |
683 | static 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 | /***********************************************************************/ |
704 | RCODE 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 | /***********************************************************************/ |
831 | int 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 | |