1/* Copyright (C) MariaDB Corporation Ab
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 of the License.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software
14 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
15
16/**
17 @file ha_connect.cc
18
19 @brief
20 The ha_connect engine is a stubbed storage engine that enables to create tables
21 based on external data. Principally they are based on plain files of many
22 different types, but also on collections of such files, collection of tables,
23 local or remote MySQL/MariaDB tables retrieved via MySQL API,
24 ODBC/JDBC tables retrieving data from other DBMS having an ODBC/JDBC server,
25 and even virtual tables.
26
27 @details
28 ha_connect will let you create/open/delete tables, the created table can be
29 done specifying an already existing file, the drop table command will just
30 suppress the table definition but not the eventual data file.
31 Indexes are not supported for all table types but data can be inserted,
32 updated or deleted.
33
34 You can enable the CONNECT storage engine in your build by doing the
35 following during your build process:<br> ./configure
36 --with-connect-storage-engine
37
38 You can install the CONNECT handler as all other storage handlers.
39
40 Once this is done, MySQL will let you create tables with:<br>
41 CREATE TABLE <table name> (...) ENGINE=CONNECT;
42
43 The example storage engine does not use table locks. It
44 implements an example "SHARE" that is inserted into a hash by table
45 name. This is not used yet.
46
47 Please read the object definition in ha_connect.h before reading the rest
48 of this file.
49
50 @note
51 This MariaDB CONNECT handler is currently an adaptation of the XDB handler
52 that was written for MySQL version 4.1.2-alpha. Its overall design should
53 be enhanced in the future to meet MariaDB requirements.
54
55 @note
56 It was written also from the Brian's ha_example handler and contains parts
57 of it that are there, such as table and system variables.
58
59 @note
60 When you create an CONNECT table, the MySQL Server creates a table .frm
61 (format) file in the database directory, using the table name as the file
62 name as is customary with MySQL.
63 For file based tables, if a file name is not specified, this is an inward
64 table. An empty file is made in the current data directory that you can
65 populate later like for other engine tables. This file modified on ALTER
66 and is deleted when dropping the table.
67 If a file name is specified, this in an outward table. The specified file
68 will be used as representing the table data and will not be modified or
69 deleted on command such as ALTER or DROP.
70 To get an idea of what occurs, here is an example select that would do
71 a scan of an entire table:
72
73 @code
74 ha-connect::open
75 ha_connect::store_lock
76 ha_connect::external_lock
77 ha_connect::info
78 ha_connect::rnd_init
79 ha_connect::extra
80 ENUM HA_EXTRA_CACHE Cache record in HA_rrnd()
81 ha_connect::rnd_next
82 ha_connect::rnd_next
83 ha_connect::rnd_next
84 ha_connect::rnd_next
85 ha_connect::rnd_next
86 ha_connect::rnd_next
87 ha_connect::rnd_next
88 ha_connect::rnd_next
89 ha_connect::rnd_next
90 ha_connect::extra
91 ENUM HA_EXTRA_NO_CACHE End caching of records (def)
92 ha_connect::external_lock
93 ha_connect::extra
94 ENUM HA_EXTRA_RESET Reset database to after open
95 @endcode
96
97 Here you see that the connect storage engine has 9 rows called before
98 rnd_next signals that it has reached the end of its data. Calls to
99 ha_connect::extra() are hints as to what will be occuring to the request.
100
101 Author Olivier Bertrand
102 */
103
104#ifdef USE_PRAGMA_IMPLEMENTATION
105#pragma implementation // gcc: Class implementation
106#endif
107
108#define MYSQL_SERVER 1
109#define DONT_DEFINE_VOID
110#include <my_global.h>
111#include "sql_parse.h"
112#include "sql_base.h"
113#include "sql_partition.h"
114#undef OFFSET
115
116#define NOPARSE
117#define NJDBC
118#if defined(UNIX)
119#include "osutil.h"
120#endif // UNIX
121#include "global.h"
122#include "plgdbsem.h"
123#include "xtable.h"
124#include "tabext.h"
125#if defined(ODBC_SUPPORT)
126#include "odbccat.h"
127#endif // ODBC_SUPPORT
128#if defined(JAVA_SUPPORT)
129#include "tabjdbc.h"
130#include "jdbconn.h"
131#endif // JAVA_SUPPORT
132#if defined(CMGO_SUPPORT)
133#include "cmgoconn.h"
134#endif // CMGO_SUPPORT
135#include "tabmysql.h"
136#include "filamdbf.h"
137#include "tabxcl.h"
138#include "tabfmt.h"
139//#include "reldef.h"
140#include "tabcol.h"
141#include "xindex.h"
142#if defined(__WIN__)
143#include <io.h>
144#include "tabwmi.h"
145#endif // __WIN__
146#include "connect.h"
147#include "user_connect.h"
148#include "ha_connect.h"
149#include "myutil.h"
150#include "preparse.h"
151#include "inihandl.h"
152#if defined(LIBXML2_SUPPORT)
153#include "libdoc.h"
154#endif // LIBXML2_SUPPORT
155#include "taboccur.h"
156#include "tabpivot.h"
157#include "tabfix.h"
158
159#define my_strupr(p) my_caseup_str(default_charset_info, (p));
160#define my_strlwr(p) my_casedn_str(default_charset_info, (p));
161#define my_stricmp(a,b) my_strcasecmp(default_charset_info, (a), (b))
162
163
164/***********************************************************************/
165/* Initialize the ha_connect static members. */
166/***********************************************************************/
167#define SZCONV 8192
168#define SZWORK 67108864 // Default work area size 64M
169#define SZWMIN 4194304 // Minimum work area size 4M
170#define JSONMAX 10 // JSON Default max grp size
171
172extern "C" {
173 char version[]= "Version 1.06.0007 March 11, 2018";
174#if defined(__WIN__)
175 char compver[]= "Version 1.06.0007 " __DATE__ " " __TIME__;
176 char slash= '\\';
177#else // !__WIN__
178 char slash= '/';
179#endif // !__WIN__
180} // extern "C"
181
182#if MYSQL_VERSION_ID > 100200
183#define stored_in_db stored_in_db()
184#endif // MYSQL_VERSION_ID
185
186#if defined(XMAP)
187 my_bool xmap= false;
188#endif // XMAP
189
190ulong ha_connect::num= 0;
191
192#if defined(XMSG)
193extern "C" {
194 char *msg_path;
195} // extern "C"
196#endif // XMSG
197
198#if defined(JAVA_SUPPORT)
199 char *JvmPath;
200 char *ClassPath;
201#endif // JAVA_SUPPORT
202
203pthread_mutex_t parmut;
204pthread_mutex_t usrmut;
205pthread_mutex_t tblmut;
206
207/***********************************************************************/
208/* Utility functions. */
209/***********************************************************************/
210PQRYRES OEMColumns(PGLOBAL g, PTOS topt, char *tab, char *db, bool info);
211PQRYRES VirColumns(PGLOBAL g, bool info);
212PQRYRES JSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info);
213PQRYRES XMLColumns(PGLOBAL g, char *db, char *tab, PTOS topt, bool info);
214#if defined(JAVA_SUPPORT)
215PQRYRES MGOColumns(PGLOBAL g, PCSZ db, PCSZ url, PTOS topt, bool info);
216#endif // JAVA_SUPPORT
217int TranslateJDBCType(int stp, char *tn, int prec, int& len, char& v);
218void PushWarning(PGLOBAL g, THD *thd, int level);
219bool CheckSelf(PGLOBAL g, TABLE_SHARE *s, PCSZ host, PCSZ db,
220 PCSZ tab, PCSZ src, int port);
221bool ZipLoadFile(PGLOBAL, PCSZ, PCSZ, PCSZ, bool, bool);
222bool ExactInfo(void);
223#if defined(CMGO_SUPPORT)
224//void mongo_init(bool);
225#endif // CMGO_SUPPORT
226USETEMP UseTemp(void);
227int GetConvSize(void);
228TYPCONV GetTypeConv(void);
229char *GetJsonNull(void);
230uint GetJsonGrpSize(void);
231char *GetJavaWrapper(void);
232uint GetWorkSize(void);
233void SetWorkSize(uint);
234extern "C" const char *msglang(void);
235
236static void PopUser(PCONNECT xp);
237static PCONNECT GetUser(THD *thd, PCONNECT xp);
238static PGLOBAL GetPlug(THD *thd, PCONNECT& lxp);
239
240static handler *connect_create_handler(handlerton *hton,
241 TABLE_SHARE *table,
242 MEM_ROOT *mem_root);
243
244static int connect_assisted_discovery(handlerton *hton, THD* thd,
245 TABLE_SHARE *table_s,
246 HA_CREATE_INFO *info);
247
248/****************************************************************************/
249/* Return str as a zero terminated string. */
250/****************************************************************************/
251static char *strz(PGLOBAL g, LEX_CSTRING &ls)
252{
253 char *str= (char*)PlugSubAlloc(g, NULL, ls.length + 1);
254
255 memcpy(str, ls.str, ls.length);
256 str[ls.length]= 0;
257 return str;
258} // end of strz
259
260/***********************************************************************/
261/* CONNECT session variables definitions. */
262/***********************************************************************/
263// Tracing: 0 no, 1 yes, 2 more, 4 index... 511 all
264const char *xtrace_names[] =
265{
266 "YES", "MORE", "INDEX", "MEMORY", "SUBALLOC",
267 "QUERY", "STMT", "HANDLER", "BLOCK", "MONGO", NullS
268};
269
270TYPELIB xtrace_typelib =
271{
272 array_elements(xtrace_names) - 1, "xtrace_typelib",
273 xtrace_names, NULL
274};
275
276static MYSQL_THDVAR_SET(
277 xtrace, // name
278 PLUGIN_VAR_RQCMDARG, // opt
279 "Trace values.", // comment
280 NULL, // check
281 NULL, // update function
282 0, // def (NO)
283 &xtrace_typelib); // typelib
284
285// Getting exact info values
286static MYSQL_THDVAR_BOOL(exact_info, PLUGIN_VAR_RQCMDARG,
287 "Getting exact info values",
288 NULL, NULL, 0);
289
290// Enabling cond_push
291static MYSQL_THDVAR_BOOL(cond_push, PLUGIN_VAR_RQCMDARG,
292 "Enabling cond_push",
293 NULL, NULL, 1); // YES by default
294
295/**
296 Temporary file usage:
297 no: Not using temporary file
298 auto: Using temporary file when needed
299 yes: Allways using temporary file
300 force: Force using temporary file (no MAP)
301 test: Reserved
302*/
303const char *usetemp_names[]=
304{
305 "NO", "AUTO", "YES", "FORCE", "TEST", NullS
306};
307
308TYPELIB usetemp_typelib=
309{
310 array_elements(usetemp_names) - 1, "usetemp_typelib",
311 usetemp_names, NULL
312};
313
314static MYSQL_THDVAR_ENUM(
315 use_tempfile, // name
316 PLUGIN_VAR_RQCMDARG, // opt
317 "Temporary file use.", // comment
318 NULL, // check
319 NULL, // update function
320 1, // def (AUTO)
321 &usetemp_typelib); // typelib
322
323// Size used for g->Sarea_Size
324static MYSQL_THDVAR_UINT(work_size,
325 PLUGIN_VAR_RQCMDARG,
326 "Size of the CONNECT work area.",
327 NULL, NULL, SZWORK, SZWMIN, UINT_MAX, 1);
328
329// Size used when converting TEXT columns to VARCHAR
330static MYSQL_THDVAR_INT(conv_size,
331 PLUGIN_VAR_RQCMDARG, // opt
332 "Size used when converting TEXT columns.",
333 NULL, NULL, SZCONV, 0, 65500, 8192);
334
335/**
336 Type conversion:
337 no: Unsupported types -> TYPE_ERROR
338 yes: TEXT -> VARCHAR
339 force: Do it also for ODBC BINARY and BLOBs
340 skip: skip unsupported type columns in Discovery
341*/
342const char *xconv_names[]=
343{
344 "NO", "YES", "FORCE", "SKIP", NullS
345};
346
347TYPELIB xconv_typelib=
348{
349 array_elements(xconv_names) - 1, "xconv_typelib",
350 xconv_names, NULL
351};
352
353static MYSQL_THDVAR_ENUM(
354 type_conv, // name
355 PLUGIN_VAR_RQCMDARG, // opt
356 "Unsupported types conversion.", // comment
357 NULL, // check
358 NULL, // update function
359 1, // def (yes)
360 &xconv_typelib); // typelib
361
362// Null representation for JSON values
363static MYSQL_THDVAR_STR(json_null,
364 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
365 "Representation of Json null values",
366 // check_json_null, update_json_null,
367 NULL, NULL, "<null>");
368
369// Estimate max number of rows for JSON aggregate functions
370static MYSQL_THDVAR_UINT(json_grp_size,
371 PLUGIN_VAR_RQCMDARG, // opt
372 "max number of rows for JSON aggregate functions.",
373 NULL, NULL, JSONMAX, 1, INT_MAX, 1);
374
375#if defined(JAVA_SUPPORT)
376// Default java wrapper to use with JDBC tables
377static MYSQL_THDVAR_STR(java_wrapper,
378 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
379 "Java wrapper class name",
380 // check_java_wrapper, update_java_wrapper,
381 NULL, NULL, "wrappers/JdbcInterface");
382#endif // JAVA_SUPPORT
383
384// This is apparently not acceptable for a plugin so it is undocumented
385#if defined(JAVA_SUPPORT) || defined(CMGO_SUPPORT)
386// Enabling MONGO table type
387#if defined(MONGO_SUPPORT) || (MYSQL_VERSION_ID > 100200)
388static MYSQL_THDVAR_BOOL(enable_mongo, PLUGIN_VAR_RQCMDARG,
389 "Enabling the MongoDB access", NULL, NULL, 1);
390#else // !version 2,3
391static MYSQL_THDVAR_BOOL(enable_mongo, PLUGIN_VAR_RQCMDARG,
392 "Enabling the MongoDB access", NULL, NULL, 0);
393#endif // !version 2,3
394#endif // JAVA_SUPPORT || CMGO_SUPPORT
395
396#if defined(XMSG) || defined(NEWMSG)
397const char *language_names[]=
398{
399 "default", "english", "french", NullS
400};
401
402TYPELIB language_typelib=
403{
404 array_elements(language_names) - 1, "language_typelib",
405 language_names, NULL
406};
407
408static MYSQL_THDVAR_ENUM(
409 msg_lang, // name
410 PLUGIN_VAR_RQCMDARG, // opt
411 "Message language", // comment
412 NULL, // check
413 NULL, // update
414 1, // def (ENGLISH)
415 &language_typelib); // typelib
416#endif // XMSG || NEWMSG
417
418/***********************************************************************/
419/* The CONNECT handlerton object. */
420/***********************************************************************/
421handlerton *connect_hton= NULL;
422
423/***********************************************************************/
424/* Function to export session variable values to other source files. */
425/***********************************************************************/
426uint GetTraceValue(void)
427 {return (uint)(connect_hton ? THDVAR(current_thd, xtrace) : 0);}
428bool ExactInfo(void) {return THDVAR(current_thd, exact_info);}
429static bool CondPushEnabled(void) {return THDVAR(current_thd, cond_push);}
430USETEMP UseTemp(void) {return (USETEMP)THDVAR(current_thd, use_tempfile);}
431int GetConvSize(void) {return THDVAR(current_thd, conv_size);}
432TYPCONV GetTypeConv(void) {return (TYPCONV)THDVAR(current_thd, type_conv);}
433char *GetJsonNull(void)
434 {return connect_hton ? THDVAR(current_thd, json_null) : NULL;}
435uint GetJsonGrpSize(void)
436 {return connect_hton ? THDVAR(current_thd, json_grp_size) : 10;}
437uint GetWorkSize(void) {return THDVAR(current_thd, work_size);}
438void SetWorkSize(uint)
439{
440 // Changing the session variable value seems to be impossible here
441 // and should be done in a check function
442 push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0,
443 "Work size too big, try setting a smaller value");
444} // end of SetWorkSize
445
446#if defined(JAVA_SUPPORT)
447char *GetJavaWrapper(void)
448{return connect_hton ? THDVAR(current_thd, java_wrapper) : (char*)"wrappers/JdbcInterface";}
449#endif // JAVA_SUPPORT
450
451#if defined(JAVA_SUPPORT) || defined(CMGO_SUPPORT)
452bool MongoEnabled(void) {return THDVAR(current_thd, enable_mongo);}
453#endif // JAVA_SUPPORT || CMGO_SUPPORT
454
455#if defined(XMSG) || defined(NEWMSG)
456extern "C" const char *msglang(void)
457 {return language_names[THDVAR(current_thd, msg_lang)];}
458#else // !XMSG && !NEWMSG
459extern "C" const char *msglang(void)
460{
461#if defined(FRENCH)
462 return "french";
463#else // DEFAULT
464 return "english";
465#endif // DEFAULT
466} // end of msglang
467#endif // !XMSG && !NEWMSG
468
469#if 0
470/***********************************************************************/
471/* Global variables update functions. */
472/***********************************************************************/
473static void update_connect_zconv(MYSQL_THD thd,
474 struct st_mysql_sys_var *var,
475 void *var_ptr, const void *save)
476{
477 zconv= *(int *)var_ptr= *(int *)save;
478} // end of update_connect_zconv
479
480static void update_connect_xconv(MYSQL_THD thd,
481 struct st_mysql_sys_var *var,
482 void *var_ptr, const void *save)
483{
484 xconv= (int)(*(ulong *)var_ptr= *(ulong *)save);
485} // end of update_connect_xconv
486
487#if defined(XMAP)
488static void update_connect_xmap(MYSQL_THD thd,
489 struct st_mysql_sys_var *var,
490 void *var_ptr, const void *save)
491{
492 xmap= (my_bool)(*(my_bool *)var_ptr= *(my_bool *)save);
493} // end of update_connect_xmap
494#endif // XMAP
495#endif // 0
496
497#if 0 // (was XMSG) Unuseful because not called for default value
498static void update_msg_path(MYSQL_THD thd,
499 struct st_mysql_sys_var *var,
500 void *var_ptr, const void *save)
501{
502 char *value= *(char**)save;
503 char *old= *(char**)var_ptr;
504
505 if (value)
506 *(char**)var_ptr= my_strdup(value, MYF(0));
507 else
508 *(char**)var_ptr= 0;
509
510 my_free(old);
511} // end of update_msg_path
512
513static int check_msg_path (MYSQL_THD thd, struct st_mysql_sys_var *var,
514 void *save, struct st_mysql_value *value)
515{
516 const char *path;
517 char buff[512];
518 int len= sizeof(buff);
519
520 path= value->val_str(value, buff, &len);
521
522 if (path && *path != '*') {
523 /* Save a pointer to the name in the
524 'file_format_name_map' constant array. */
525 *(char**)save= my_strdup(path, MYF(0));
526 return(0);
527 } else {
528 push_warning_printf(thd,
529 Sql_condition::WARN_LEVEL_WARN,
530 ER_WRONG_ARGUMENTS,
531 "CONNECT: invalid message path");
532 } // endif path
533
534 *(char**)save= NULL;
535 return(1);
536} // end of check_msg_path
537#endif // 0
538
539/**
540 CREATE TABLE option list (table options)
541
542 These can be specified in the CREATE TABLE:
543 CREATE TABLE ( ... ) {...here...}
544*/
545ha_create_table_option connect_table_option_list[]=
546{
547 HA_TOPTION_STRING("TABLE_TYPE", type),
548 HA_TOPTION_STRING("FILE_NAME", filename),
549 HA_TOPTION_STRING("XFILE_NAME", optname),
550//HA_TOPTION_STRING("CONNECT_STRING", connect),
551 HA_TOPTION_STRING("TABNAME", tabname),
552 HA_TOPTION_STRING("TABLE_LIST", tablist),
553 HA_TOPTION_STRING("DBNAME", dbname),
554 HA_TOPTION_STRING("SEP_CHAR", separator),
555 HA_TOPTION_STRING("QCHAR", qchar),
556 HA_TOPTION_STRING("MODULE", module),
557 HA_TOPTION_STRING("SUBTYPE", subtype),
558 HA_TOPTION_STRING("CATFUNC", catfunc),
559 HA_TOPTION_STRING("SRCDEF", srcdef),
560 HA_TOPTION_STRING("COLIST", colist),
561 HA_TOPTION_STRING("FILTER", filter),
562 HA_TOPTION_STRING("OPTION_LIST", oplist),
563 HA_TOPTION_STRING("DATA_CHARSET", data_charset),
564 HA_TOPTION_NUMBER("LRECL", lrecl, 0, 0, INT_MAX32, 1),
565 HA_TOPTION_NUMBER("BLOCK_SIZE", elements, 0, 0, INT_MAX32, 1),
566//HA_TOPTION_NUMBER("ESTIMATE", estimate, 0, 0, INT_MAX32, 1),
567 HA_TOPTION_NUMBER("MULTIPLE", multiple, 0, 0, 3, 1),
568 HA_TOPTION_NUMBER("HEADER", header, 0, 0, 3, 1),
569 HA_TOPTION_NUMBER("QUOTED", quoted, (ulonglong) -1, 0, 3, 1),
570 HA_TOPTION_NUMBER("ENDING", ending, (ulonglong) -1, 0, INT_MAX32, 1),
571 HA_TOPTION_NUMBER("COMPRESS", compressed, 0, 0, 2, 1),
572 HA_TOPTION_BOOL("MAPPED", mapped, 0),
573 HA_TOPTION_BOOL("HUGE", huge, 0),
574 HA_TOPTION_BOOL("SPLIT", split, 0),
575 HA_TOPTION_BOOL("READONLY", readonly, 0),
576 HA_TOPTION_BOOL("SEPINDEX", sepindex, 0),
577 HA_TOPTION_BOOL("ZIPPED", zipped, 0),
578 HA_TOPTION_END
579};
580
581
582/**
583 CREATE TABLE option list (field options)
584
585 These can be specified in the CREATE TABLE per field:
586 CREATE TABLE ( field ... {...here...}, ... )
587*/
588ha_create_table_option connect_field_option_list[]=
589{
590 HA_FOPTION_NUMBER("FLAG", offset, (ulonglong) -1, 0, INT_MAX32, 1),
591 HA_FOPTION_NUMBER("MAX_DIST", freq, 0, 0, INT_MAX32, 1), // BLK_INDX
592 HA_FOPTION_NUMBER("FIELD_LENGTH", fldlen, 0, 0, INT_MAX32, 1),
593 HA_FOPTION_STRING("DATE_FORMAT", dateformat),
594 HA_FOPTION_STRING("FIELD_FORMAT", fieldformat),
595 HA_FOPTION_STRING("SPECIAL", special),
596 HA_FOPTION_ENUM("DISTRIB", opt, "scattered,clustered,sorted", 0),
597 HA_FOPTION_END
598};
599
600/*
601 CREATE TABLE option list (index options)
602
603 These can be specified in the CREATE TABLE per index:
604 CREATE TABLE ( field ..., .., INDEX .... *here*, ... )
605*/
606ha_create_table_option connect_index_option_list[]=
607{
608 HA_IOPTION_BOOL("DYNAM", dynamic, 0),
609 HA_IOPTION_BOOL("MAPPED", mapped, 0),
610 HA_IOPTION_END
611};
612
613/***********************************************************************/
614/* Push G->Message as a MySQL warning. */
615/***********************************************************************/
616bool PushWarning(PGLOBAL g, PTDB tdbp, int level)
617{
618 PHC phc;
619 THD *thd;
620 MYCAT *cat= (MYCAT*)tdbp->GetDef()->GetCat();
621
622 if (!cat || !(phc= cat->GetHandler()) || !phc->GetTable() ||
623 !(thd= (phc->GetTable())->in_use))
624 return true;
625
626 PushWarning(g, thd, level);
627 return false;
628} // end of PushWarning
629
630void PushWarning(PGLOBAL g, THD *thd, int level)
631 {
632 if (thd) {
633 Sql_condition::enum_warning_level wlvl;
634
635 wlvl= (Sql_condition::enum_warning_level)level;
636 push_warning(thd, wlvl, 0, g->Message);
637 } else
638 htrc("%s\n", g->Message);
639
640 } // end of PushWarning
641
642#ifdef HAVE_PSI_INTERFACE
643static PSI_mutex_key con_key_mutex_CONNECT_SHARE_mutex;
644
645static PSI_mutex_info all_connect_mutexes[]=
646{
647 { &con_key_mutex_CONNECT_SHARE_mutex, "CONNECT_SHARE::mutex", 0}
648};
649
650static void init_connect_psi_keys()
651{
652 const char* category= "connect";
653 int count;
654
655 if (PSI_server == NULL)
656 return;
657
658 count= array_elements(all_connect_mutexes);
659 PSI_server->register_mutex(category, all_connect_mutexes, count);
660}
661#else
662static void init_connect_psi_keys() {}
663#endif
664
665
666DllExport LPCSTR PlugSetPath(LPSTR to, LPCSTR name, LPCSTR dir)
667{
668 const char *res= PlugSetPath(to, mysql_data_home, name, dir);
669 return res;
670}
671
672
673/**
674 @brief
675 If frm_error() is called then we will use this to determine
676 the file extensions that exist for the storage engine. This is also
677 used by the default rename_table and delete_table method in
678 handler.cc.
679
680 For engines that have two file name extentions (separate meta/index file
681 and data file), the order of elements is relevant. First element of engine
682 file name extentions array should be meta/index file extention. Second
683 element - data file extention. This order is assumed by
684 prepare_for_repair() when REPAIR TABLE ... USE_FRM is issued.
685
686 @see
687 rename_table method in handler.cc and
688 delete_table method in handler.cc
689*/
690static const char *ha_connect_exts[]= {
691 ".dos", ".fix", ".csv", ".bin", ".fmt", ".dbf", ".xml", ".json", ".ini",
692 ".vec", ".dnx", ".fnx", ".bnx", ".vnx", ".dbx", ".dop", ".fop", ".bop",
693 ".vop", NULL};
694
695/**
696 @brief
697 Plugin initialization
698*/
699static int connect_init_func(void *p)
700{
701 DBUG_ENTER("connect_init_func");
702
703// added from Sergei mail
704#if 0 // (defined(LINUX))
705 Dl_info dl_info;
706 if (dladdr(&connect_hton, &dl_info))
707 {
708 if (dlopen(dl_info.dli_fname, RTLD_NOLOAD | RTLD_NOW | RTLD_GLOBAL) == 0)
709 {
710 sql_print_information("CONNECT: dlopen() failed, OEM table type is not supported");
711 sql_print_information("CONNECT: %s", dlerror());
712 }
713 }
714 else
715 {
716 sql_print_information("CONNECT: dladdr() failed, OEM table type is not supported");
717 sql_print_information("CONNECT: %s", dlerror());
718 }
719#endif // 0 (LINUX)
720
721#if defined(__WIN__)
722 sql_print_information("CONNECT: %s", compver);
723#else // !__WIN__
724 sql_print_information("CONNECT: %s", version);
725#endif // !__WIN__
726 pthread_mutex_init(&parmut, NULL);
727 pthread_mutex_init(&usrmut, NULL);
728 pthread_mutex_init(&tblmut, NULL);
729
730#if defined(LIBXML2_SUPPORT)
731 XmlInitParserLib();
732#endif // LIBXML2_SUPPORT
733
734#if 0 //defined(CMGO_SUPPORT)
735 mongo_init(true);
736#endif // CMGO_SUPPORT
737
738 init_connect_psi_keys();
739
740 connect_hton= (handlerton *)p;
741 connect_hton->state= SHOW_OPTION_YES;
742 connect_hton->create= connect_create_handler;
743 connect_hton->flags= HTON_TEMPORARY_NOT_SUPPORTED;
744 connect_hton->table_options= connect_table_option_list;
745 connect_hton->field_options= connect_field_option_list;
746 connect_hton->index_options= connect_index_option_list;
747 connect_hton->tablefile_extensions= ha_connect_exts;
748 connect_hton->discover_table_structure= connect_assisted_discovery;
749
750 if (trace(128))
751 sql_print_information("connect_init: hton=%p", p);
752
753 DTVAL::SetTimeShift(); // Initialize time zone shift once for all
754 BINCOL::SetEndian(); // Initialize host endian setting
755#if defined(JAVA_SUPPORT)
756 JAVAConn::SetJVM();
757#endif // JAVA_SUPPORT
758 DBUG_RETURN(0);
759} // end of connect_init_func
760
761
762/**
763 @brief
764 Plugin clean up
765*/
766static int connect_done_func(void *)
767{
768 int error= 0;
769 PCONNECT pc, pn;
770 DBUG_ENTER("connect_done_func");
771
772#ifdef LIBXML2_SUPPORT
773 XmlCleanupParserLib();
774#endif // LIBXML2_SUPPORT
775
776#if defined(CMGO_SUPPORT)
777 CMgoConn::mongo_init(false);
778#endif // CMGO_SUPPORT
779
780#ifdef JAVA_SUPPORT
781 JAVAConn::ResetJVM();
782#endif // JAVA_SUPPORT
783
784#if !defined(__WIN__)
785 PROFILE_End();
786#endif // !__WIN__
787
788 pthread_mutex_lock(&usrmut);
789 for (pc= user_connect::to_users; pc; pc= pn) {
790 if (pc->g)
791 PlugCleanup(pc->g, true);
792
793 pn= pc->next;
794 delete pc;
795 } // endfor pc
796
797 pthread_mutex_unlock(&usrmut);
798
799 pthread_mutex_destroy(&usrmut);
800 pthread_mutex_destroy(&parmut);
801 pthread_mutex_destroy(&tblmut);
802 connect_hton= NULL;
803 DBUG_RETURN(error);
804} // end of connect_done_func
805
806
807/**
808 @brief
809 Example of simple lock controls. The "share" it creates is a
810 structure we will pass to each CONNECT handler. Do you have to have
811 one of these? Well, you have pieces that are used for locking, and
812 they are needed to function.
813*/
814
815CONNECT_SHARE *ha_connect::get_share()
816{
817 CONNECT_SHARE *tmp_share;
818
819 lock_shared_ha_data();
820
821 if (!(tmp_share= static_cast<CONNECT_SHARE*>(get_ha_share_ptr()))) {
822 tmp_share= new CONNECT_SHARE;
823 if (!tmp_share)
824 goto err;
825 mysql_mutex_init(con_key_mutex_CONNECT_SHARE_mutex,
826 &tmp_share->mutex, MY_MUTEX_INIT_FAST);
827 set_ha_share_ptr(static_cast<Handler_share*>(tmp_share));
828 } // endif tmp_share
829
830 err:
831 unlock_shared_ha_data();
832 return tmp_share;
833} // end of get_share
834
835
836static handler* connect_create_handler(handlerton *hton,
837 TABLE_SHARE *table,
838 MEM_ROOT *mem_root)
839{
840 handler *h= new (mem_root) ha_connect(hton, table);
841
842 if (trace(128))
843 htrc("New CONNECT %p, table: %.*s\n", h,
844 table ? table->table_name.length : 6,
845 table ? table->table_name.str : "<null>");
846
847 return h;
848} // end of connect_create_handler
849
850/****************************************************************************/
851/* ha_connect constructor. */
852/****************************************************************************/
853ha_connect::ha_connect(handlerton *hton, TABLE_SHARE *table_arg)
854 :handler(hton, table_arg)
855{
856 hnum= ++num;
857 xp= (table) ? GetUser(ha_thd(), NULL) : NULL;
858 if (xp)
859 xp->SetHandler(this);
860#if defined(__WIN__)
861 datapath= ".\\";
862#else // !__WIN__
863 datapath= "./";
864#endif // !__WIN__
865 tdbp= NULL;
866 sdvalin1= sdvalin2= sdvalin3= sdvalin4= NULL;
867 sdvalout= NULL;
868 xmod= MODE_ANY;
869 istable= false;
870 memset(partname, 0, sizeof(partname));
871 bzero((char*) &xinfo, sizeof(XINFO));
872 valid_info= false;
873 valid_query_id= 0;
874 creat_query_id= (table && table->in_use) ? table->in_use->query_id : 0;
875 stop= false;
876 alter= false;
877 mrr= false;
878 nox= true;
879 abort= false;
880 indexing= -1;
881 locked= 0;
882 part_id= NULL;
883 data_file_name= NULL;
884 index_file_name= NULL;
885 enable_activate_all_index= 0;
886 int_table_flags= (HA_NO_TRANSACTIONS | HA_NO_PREFIX_CHAR_KEYS);
887 ref_length= sizeof(int);
888 share= NULL;
889 tshp= NULL;
890} // end of ha_connect constructor
891
892
893/****************************************************************************/
894/* ha_connect destructor. */
895/****************************************************************************/
896ha_connect::~ha_connect(void)
897{
898 if (trace(128))
899 htrc("Delete CONNECT %p, table: %.*s, xp=%p count=%d\n", this,
900 table ? table->s->table_name.length : 6,
901 table ? table->s->table_name.str : "<null>",
902 xp, xp ? xp->count : 0);
903
904 PopUser(xp);
905} // end of ha_connect destructor
906
907
908/****************************************************************************/
909/* Check whether this user can be removed. */
910/****************************************************************************/
911static void PopUser(PCONNECT xp)
912{
913 if (xp) {
914 pthread_mutex_lock(&usrmut);
915 xp->count--;
916
917 if (!xp->count) {
918 PCONNECT p;
919
920 for (p= user_connect::to_users; p; p= p->next)
921 if (p == xp)
922 break;
923
924 if (p) {
925 if (p->next)
926 p->next->previous= p->previous;
927
928 if (p->previous)
929 p->previous->next= p->next;
930 else
931 user_connect::to_users= p->next;
932
933 } // endif p
934
935 PlugCleanup(xp->g, true);
936 delete xp;
937 } // endif count
938
939 pthread_mutex_unlock(&usrmut);
940 } // endif xp
941
942} // end of PopUser
943
944
945/****************************************************************************/
946/* Get a pointer to the user of this handler. */
947/****************************************************************************/
948static PCONNECT GetUser(THD *thd, PCONNECT xp)
949{
950 if (!thd)
951 return NULL;
952
953 if (xp) {
954 if (thd == xp->thdp)
955 return xp;
956
957 PopUser(xp); // Avoid memory leak
958 } // endif xp
959
960 pthread_mutex_lock(&usrmut);
961
962 for (xp= user_connect::to_users; xp; xp= xp->next)
963 if (thd == xp->thdp)
964 break;
965
966 if (xp)
967 xp->count++;
968
969 pthread_mutex_unlock(&usrmut);
970
971 if (!xp) {
972 xp = new user_connect(thd);
973
974 if (xp->user_init()) {
975 delete xp;
976 xp = NULL;
977 } // endif user_init
978
979 } // endif xp
980
981 //} else
982 // xp->count++;
983
984 return xp;
985} // end of GetUser
986
987/****************************************************************************/
988/* Get the global pointer of the user of this handler. */
989/****************************************************************************/
990static PGLOBAL GetPlug(THD *thd, PCONNECT& lxp)
991{
992 lxp= GetUser(thd, lxp);
993 return (lxp) ? lxp->g : NULL;
994} // end of GetPlug
995
996/****************************************************************************/
997/* Get the implied table type. */
998/****************************************************************************/
999TABTYPE ha_connect::GetRealType(PTOS pos)
1000{
1001 TABTYPE type;
1002
1003 if (pos || (pos= GetTableOptionStruct())) {
1004 type= GetTypeID(pos->type);
1005
1006 if (type == TAB_UNDEF)
1007 type= pos->srcdef ? TAB_MYSQL : pos->tabname ? TAB_PRX : TAB_DOS;
1008
1009 } else
1010 type= TAB_UNDEF;
1011
1012 return type;
1013} // end of GetRealType
1014
1015/** @brief
1016 The name of the index type that will be used for display.
1017 Don't implement this method unless you really have indexes.
1018 */
1019const char *ha_connect::index_type(uint inx)
1020{
1021 switch (GetIndexType(GetRealType())) {
1022 case 1:
1023 if (table_share)
1024 return (GetIndexOption(&table_share->key_info[inx], "Dynamic"))
1025 ? "KINDEX" : "XINDEX";
1026 else
1027 return "XINDEX";
1028
1029 case 2: return "REMOTE";
1030 case 3: return "VIRTUAL";
1031 } // endswitch
1032
1033 return "Unknown";
1034} // end of index_type
1035
1036/** @brief
1037 This is a bitmap of flags that indicates how the storage engine
1038 implements indexes. The current index flags are documented in
1039 handler.h. If you do not implement indexes, just return zero here.
1040
1041 @details
1042 part is the key part to check. First key part is 0.
1043 If all_parts is set, MySQL wants to know the flags for the combined
1044 index, up to and including 'part'.
1045*/
1046//ong ha_connect::index_flags(uint inx, uint part, bool all_parts) const
1047ulong ha_connect::index_flags(uint, uint, bool) const
1048{
1049 ulong flags= HA_READ_NEXT | HA_READ_RANGE |
1050 HA_KEYREAD_ONLY | HA_KEY_SCAN_NOT_ROR;
1051 ha_connect *hp= (ha_connect*)this;
1052 PTOS pos= hp->GetTableOptionStruct();
1053
1054 if (pos) {
1055 TABTYPE type= hp->GetRealType(pos);
1056
1057 switch (GetIndexType(type)) {
1058 case 1: flags|= (HA_READ_ORDER | HA_READ_PREV); break;
1059 case 2: flags|= HA_READ_AFTER_KEY; break;
1060 } // endswitch
1061
1062 } // endif pos
1063
1064 return flags;
1065} // end of index_flags
1066
1067/** @brief
1068 This is a list of flags that indicate what functionality the storage
1069 engine implements. The current table flags are documented in handler.h
1070*/
1071ulonglong ha_connect::table_flags() const
1072{
1073 ulonglong flags= HA_CAN_VIRTUAL_COLUMNS | HA_REC_NOT_IN_SEQ |
1074 HA_NO_AUTO_INCREMENT | HA_NO_PREFIX_CHAR_KEYS |
1075 HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE |
1076 HA_PARTIAL_COLUMN_READ | HA_FILE_BASED |
1077// HA_NULL_IN_KEY | not implemented yet
1078// HA_FAST_KEY_READ | causes error when sorting (???)
1079 HA_NO_TRANSACTIONS | HA_DUPLICATE_KEY_NOT_IN_ORDER |
1080 HA_NO_BLOBS | HA_MUST_USE_TABLE_CONDITION_PUSHDOWN;
1081 ha_connect *hp= (ha_connect*)this;
1082 PTOS pos= hp->GetTableOptionStruct();
1083
1084 if (pos) {
1085 TABTYPE type= hp->GetRealType(pos);
1086
1087 if (IsFileType(type))
1088 flags|= HA_FILE_BASED;
1089
1090 if (IsExactType(type))
1091 flags|= (HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT);
1092
1093 // No data change on ALTER for outward tables
1094 if (!IsFileType(type) || hp->FileExists(pos->filename, true))
1095 flags|= HA_NO_COPY_ON_ALTER;
1096
1097 } // endif pos
1098
1099 return flags;
1100} // end of table_flags
1101
1102/****************************************************************************/
1103/* Return the value of an option specified in an option list. */
1104/****************************************************************************/
1105PCSZ GetListOption(PGLOBAL g, PCSZ opname, PCSZ oplist, PCSZ def)
1106{
1107 if (!oplist)
1108 return (char*)def;
1109
1110 char key[16], val[256];
1111 char *pv, *pn, *pk = (char*)oplist;
1112 PCSZ opval = def;
1113 int n;
1114
1115 while (*pk == ' ')
1116 pk++;
1117
1118 for (; pk; pk = pn) {
1119 pn = strchr(pk, ',');
1120 pv = strchr(pk, '=');
1121
1122 if (pv && (!pn || pv < pn)) {
1123 n = MY_MIN(static_cast<size_t>(pv - pk), sizeof(key) - 1);
1124 memcpy(key, pk, n);
1125
1126 while (n && key[n - 1] == ' ')
1127 n--;
1128
1129 key[n] = 0;
1130
1131 while (*(++pv) == ' ');
1132
1133 n = MY_MIN((pn ? pn - pv : strlen(pv)), sizeof(val) - 1);
1134 memcpy(val, pv, n);
1135
1136 while (n && val[n - 1] == ' ')
1137 n--;
1138
1139 val[n] = 0;
1140 } else {
1141 n = MY_MIN((pn ? pn - pk : strlen(pk)), sizeof(key) - 1);
1142 memcpy(key, pk, n);
1143
1144 while (n && key[n - 1] == ' ')
1145 n--;
1146
1147 key[n] = 0;
1148 val[0] = 0;
1149 } // endif pv
1150
1151 if (!stricmp(opname, key)) {
1152 opval = PlugDup(g, val);
1153 break;
1154 } else if (!pn)
1155 break;
1156
1157 while (*(++pn) == ' ');
1158 } // endfor pk
1159
1160 return opval;
1161} // end of GetListOption
1162
1163/****************************************************************************/
1164/* Return the value of a string option or NULL if not specified. */
1165/****************************************************************************/
1166PCSZ GetStringTableOption(PGLOBAL g, PTOS options, PCSZ opname, PCSZ sdef)
1167{
1168 PCSZ opval= NULL;
1169
1170 if (!options)
1171 return sdef;
1172 else if (!stricmp(opname, "Type"))
1173 opval= options->type;
1174 else if (!stricmp(opname, "Filename"))
1175 opval= options->filename;
1176 else if (!stricmp(opname, "Optname"))
1177 opval= options->optname;
1178 else if (!stricmp(opname, "Tabname"))
1179 opval= options->tabname;
1180 else if (!stricmp(opname, "Tablist"))
1181 opval= options->tablist;
1182 else if (!stricmp(opname, "Database") ||
1183 !stricmp(opname, "DBname"))
1184 opval= options->dbname;
1185 else if (!stricmp(opname, "Separator"))
1186 opval= options->separator;
1187 else if (!stricmp(opname, "Qchar"))
1188 opval= options->qchar;
1189 else if (!stricmp(opname, "Module"))
1190 opval= options->module;
1191 else if (!stricmp(opname, "Subtype"))
1192 opval= options->subtype;
1193 else if (!stricmp(opname, "Catfunc"))
1194 opval= options->catfunc;
1195 else if (!stricmp(opname, "Srcdef"))
1196 opval= options->srcdef;
1197 else if (!stricmp(opname, "Colist"))
1198 opval= options->colist;
1199 else if (!stricmp(opname, "Filter"))
1200 opval = options->filter;
1201 else if (!stricmp(opname, "Data_charset"))
1202 opval= options->data_charset;
1203
1204 if (!opval && options->oplist)
1205 opval= GetListOption(g, opname, options->oplist);
1206
1207 return opval ? (char*)opval : sdef;
1208} // end of GetStringTableOption
1209
1210/****************************************************************************/
1211/* Return the value of a Boolean option or bdef if not specified. */
1212/****************************************************************************/
1213bool GetBooleanTableOption(PGLOBAL g, PTOS options, PCSZ opname, bool bdef)
1214{
1215 bool opval= bdef;
1216 PCSZ pv;
1217
1218 if (!options)
1219 return bdef;
1220 else if (!stricmp(opname, "Mapped"))
1221 opval= options->mapped;
1222 else if (!stricmp(opname, "Huge"))
1223 opval= options->huge;
1224 else if (!stricmp(opname, "Split"))
1225 opval= options->split;
1226 else if (!stricmp(opname, "Readonly"))
1227 opval= options->readonly;
1228 else if (!stricmp(opname, "SepIndex"))
1229 opval= options->sepindex;
1230 else if (!stricmp(opname, "Header"))
1231 opval= (options->header != 0); // Is Boolean for some table types
1232 else if (!stricmp(opname, "Zipped"))
1233 opval = options->zipped;
1234 else if (options->oplist)
1235 if ((pv= GetListOption(g, opname, options->oplist)))
1236 opval= (!*pv || *pv == 'y' || *pv == 'Y' || atoi(pv) != 0);
1237
1238 return opval;
1239} // end of GetBooleanTableOption
1240
1241/****************************************************************************/
1242/* Return the value of an integer option or NO_IVAL if not specified. */
1243/****************************************************************************/
1244int GetIntegerTableOption(PGLOBAL g, PTOS options, PCSZ opname, int idef)
1245{
1246 ulonglong opval= (ulonglong) NO_IVAL;
1247
1248 if (!options)
1249 return idef;
1250 else if (!stricmp(opname, "Lrecl"))
1251 opval= options->lrecl;
1252 else if (!stricmp(opname, "Elements"))
1253 opval= options->elements;
1254 else if (!stricmp(opname, "Multiple"))
1255 opval= options->multiple;
1256 else if (!stricmp(opname, "Header"))
1257 opval= options->header;
1258 else if (!stricmp(opname, "Quoted"))
1259 opval= options->quoted;
1260 else if (!stricmp(opname, "Ending"))
1261 opval= options->ending;
1262 else if (!stricmp(opname, "Compressed"))
1263 opval= (options->compressed);
1264
1265 if ((ulonglong) opval == (ulonglong)NO_IVAL) {
1266 PCSZ pv;
1267
1268 if ((pv= GetListOption(g, opname, options->oplist)))
1269 opval= CharToNumber((char*)pv, strlen(pv), ULONGLONG_MAX, true);
1270 else
1271 return idef;
1272
1273 } // endif opval
1274
1275 return (int)opval;
1276} // end of GetIntegerTableOption
1277
1278/****************************************************************************/
1279/* Return the table option structure. */
1280/****************************************************************************/
1281PTOS ha_connect::GetTableOptionStruct(TABLE_SHARE *s)
1282{
1283 TABLE_SHARE *tsp= (tshp) ? tshp : (s) ? s : table_share;
1284
1285 return (tsp && (!tsp->db_plugin ||
1286 !stricmp(plugin_name(tsp->db_plugin)->str, "connect") ||
1287 !stricmp(plugin_name(tsp->db_plugin)->str, "partition")))
1288 ? tsp->option_struct : NULL;
1289} // end of GetTableOptionStruct
1290
1291/****************************************************************************/
1292/* Return the string eventually formatted with partition name. */
1293/****************************************************************************/
1294char *ha_connect::GetRealString(PCSZ s)
1295{
1296 char *sv;
1297
1298 if (IsPartitioned() && s && partname && *partname) {
1299 sv= (char*)PlugSubAlloc(xp->g, NULL, 0);
1300 sprintf(sv, s, partname);
1301 PlugSubAlloc(xp->g, NULL, strlen(sv) + 1);
1302 } else
1303 sv= (char*)s;
1304
1305 return sv;
1306} // end of GetRealString
1307
1308/****************************************************************************/
1309/* Return the value of a string option or sdef if not specified. */
1310/****************************************************************************/
1311PCSZ ha_connect::GetStringOption(PCSZ opname, PCSZ sdef)
1312{
1313 PCSZ opval= NULL;
1314 PTOS options= GetTableOptionStruct();
1315
1316 if (!stricmp(opname, "Connect")) {
1317 LEX_CSTRING cnc= (tshp) ? tshp->connect_string
1318 : table->s->connect_string;
1319
1320 if (cnc.length)
1321 opval= strz(xp->g, cnc);
1322 else
1323 opval= GetListOption(xp->g, opname, options->oplist);
1324
1325 } else if (!stricmp(opname, "Query_String")) {
1326// This escapes everything and returns a wrong query
1327// opval = thd_query_string(table->in_use)->str;
1328 opval = (PCSZ)PlugSubAlloc(xp->g, NULL,
1329 thd_query_string(table->in_use)->length + 1);
1330 strcpy((char*)opval, thd_query_string(table->in_use)->str);
1331// sprintf((char*)opval, "%s", thd_query_string(table->in_use)->str);
1332 } else if (!stricmp(opname, "Partname"))
1333 opval= partname;
1334 else if (!stricmp(opname, "Table_charset")) {
1335 const CHARSET_INFO *chif= (tshp) ? tshp->table_charset
1336 : table->s->table_charset;
1337
1338 if (chif)
1339 opval= (char*)chif->csname;
1340
1341 } else
1342 opval= GetStringTableOption(xp->g, options, opname, NULL);
1343
1344 if (opval && (!stricmp(opname, "connect")
1345 || !stricmp(opname, "tabname")
1346 || !stricmp(opname, "filename")
1347 || !stricmp(opname, "optname")
1348 || !stricmp(opname, "entry")))
1349 opval = GetRealString(opval);
1350
1351 if (!opval) {
1352 if (sdef && !strcmp(sdef, "*")) {
1353 // Return the handler default value
1354 if (!stricmp(opname, "Dbname") || !stricmp(opname, "Database"))
1355 opval= (char*)GetDBName(NULL); // Current database
1356 else if (!stricmp(opname, "Type")) // Default type
1357 opval= (!options) ? NULL :
1358 (options->srcdef) ? (char*)"MYSQL" :
1359 (options->tabname) ? (char*)"PROXY" : (char*)"DOS";
1360 else if (!stricmp(opname, "User")) // Connected user
1361 opval= (char *) "root";
1362 else if (!stricmp(opname, "Host")) // Connected user host
1363 opval= (char *) "localhost";
1364 else
1365 opval= sdef; // Caller default
1366
1367 } else
1368 opval= sdef; // Caller default
1369
1370 } // endif !opval
1371
1372 return opval;
1373} // end of GetStringOption
1374
1375/****************************************************************************/
1376/* Return the value of a Boolean option or bdef if not specified. */
1377/****************************************************************************/
1378bool ha_connect::GetBooleanOption(PCSZ opname, bool bdef)
1379{
1380 bool opval;
1381 PTOS options= GetTableOptionStruct();
1382
1383 if (!stricmp(opname, "View"))
1384 opval= (tshp) ? tshp->is_view : table_share->is_view;
1385 else
1386 opval= GetBooleanTableOption(xp->g, options, opname, bdef);
1387
1388 return opval;
1389} // end of GetBooleanOption
1390
1391/****************************************************************************/
1392/* Set the value of the opname option (does not work for oplist options) */
1393/* Currently used only to set the Sepindex value. */
1394/****************************************************************************/
1395bool ha_connect::SetBooleanOption(PCSZ opname, bool b)
1396{
1397 PTOS options= GetTableOptionStruct();
1398
1399 if (!options)
1400 return true;
1401
1402 if (!stricmp(opname, "SepIndex"))
1403 options->sepindex= b;
1404 else
1405 return true;
1406
1407 return false;
1408} // end of SetBooleanOption
1409
1410/****************************************************************************/
1411/* Return the value of an integer option or NO_IVAL if not specified. */
1412/****************************************************************************/
1413int ha_connect::GetIntegerOption(PCSZ opname)
1414{
1415 int opval;
1416 PTOS options= GetTableOptionStruct();
1417 TABLE_SHARE *tsp= (tshp) ? tshp : table_share;
1418
1419 if (!stricmp(opname, "Avglen"))
1420 opval= (int)tsp->avg_row_length;
1421 else if (!stricmp(opname, "Estimate"))
1422 opval= (int)tsp->max_rows;
1423 else
1424 opval= GetIntegerTableOption(xp->g, options, opname, NO_IVAL);
1425
1426 return opval;
1427} // end of GetIntegerOption
1428
1429/****************************************************************************/
1430/* Set the value of the opname option (does not work for oplist options) */
1431/* Currently used only to set the Lrecl value. */
1432/****************************************************************************/
1433bool ha_connect::SetIntegerOption(PCSZ opname, int n)
1434{
1435 PTOS options= GetTableOptionStruct();
1436
1437 if (!options)
1438 return true;
1439
1440 if (!stricmp(opname, "Lrecl"))
1441 options->lrecl= n;
1442 else if (!stricmp(opname, "Elements"))
1443 options->elements= n;
1444//else if (!stricmp(opname, "Estimate"))
1445// options->estimate= n;
1446 else if (!stricmp(opname, "Multiple"))
1447 options->multiple= n;
1448 else if (!stricmp(opname, "Header"))
1449 options->header= n;
1450 else if (!stricmp(opname, "Quoted"))
1451 options->quoted= n;
1452 else if (!stricmp(opname, "Ending"))
1453 options->ending= n;
1454 else if (!stricmp(opname, "Compressed"))
1455 options->compressed= n;
1456 else
1457 return true;
1458//else if (options->oplist)
1459// SetListOption(opname, options->oplist, n);
1460
1461 return false;
1462} // end of SetIntegerOption
1463
1464/****************************************************************************/
1465/* Return a field option structure. */
1466/****************************************************************************/
1467PFOS ha_connect::GetFieldOptionStruct(Field *fdp)
1468{
1469 return fdp->option_struct;
1470} // end of GetFildOptionStruct
1471
1472/****************************************************************************/
1473/* Returns the column description structure used to make the column. */
1474/****************************************************************************/
1475void *ha_connect::GetColumnOption(PGLOBAL g, void *field, PCOLINFO pcf)
1476{
1477 const char *cp;
1478 char *chset, v = 0;
1479 ha_field_option_struct *fop;
1480 Field* fp;
1481 Field* *fldp;
1482
1483 // Double test to be on the safe side
1484 if (!table)
1485 return NULL;
1486
1487 // Find the column to describe
1488 if (field) {
1489 fldp= (Field**)field;
1490 fldp++;
1491 } else
1492 fldp= (tshp) ? tshp->field : table->field;
1493
1494 if (!fldp || !(fp= *fldp))
1495 return NULL;
1496
1497 // Get the CONNECT field options structure
1498 fop= GetFieldOptionStruct(fp);
1499 pcf->Flags= 0;
1500
1501 // Now get column information
1502 pcf->Name= (char*)fp->field_name.str;
1503
1504 if (fop && fop->special) {
1505 pcf->Fieldfmt= (char*)fop->special;
1506 pcf->Flags= U_SPECIAL;
1507 return fldp;
1508 } // endif special
1509
1510 pcf->Scale= 0;
1511 pcf->Opt= (fop) ? (int)fop->opt : 0;
1512
1513 if ((pcf->Length= fp->field_length) < 0)
1514 pcf->Length= 256; // BLOB?
1515
1516 pcf->Precision= pcf->Length;
1517
1518 if (fop) {
1519 pcf->Offset= (int)fop->offset;
1520 pcf->Freq= (int)fop->freq;
1521 pcf->Datefmt= (char*)fop->dateformat;
1522 pcf->Fieldfmt= (char*)fop->fieldformat;
1523 } else {
1524 pcf->Offset= -1;
1525 pcf->Freq= 0;
1526 pcf->Datefmt= NULL;
1527 pcf->Fieldfmt= NULL;
1528 } // endif fop
1529
1530 chset = (char *)fp->charset()->name;
1531
1532 switch (fp->type()) {
1533 case MYSQL_TYPE_BLOB:
1534 case MYSQL_TYPE_VARCHAR:
1535 case MYSQL_TYPE_VAR_STRING:
1536 pcf->Flags |= U_VAR;
1537 // fall through
1538 default:
1539 pcf->Type= MYSQLtoPLG(fp->type(), &v);
1540 break;
1541 } // endswitch SQL type
1542
1543 switch (pcf->Type) {
1544 case TYPE_STRING:
1545 case TYPE_BIN:
1546 // Do something for case
1547 cp= chset;
1548
1549 // Find if collation name ends by _ci
1550 if (!strcmp(cp + strlen(cp) - 3, "_ci")) {
1551 pcf->Scale= 1; // Case insensitive
1552 pcf->Opt= 0; // Prevent index opt until it is safe
1553 } // endif ci
1554
1555 break;
1556 case TYPE_DOUBLE:
1557 pcf->Scale= MY_MAX(MY_MIN(fp->decimals(), ((unsigned)pcf->Length - 2)), 0);
1558 break;
1559 case TYPE_DECIM:
1560 pcf->Precision= ((Field_new_decimal*)fp)->precision;
1561 pcf->Length= pcf->Precision;
1562 pcf->Scale= fp->decimals();
1563 break;
1564 case TYPE_DATE:
1565 // Field_length is only used for DATE columns
1566 if (fop && fop->fldlen)
1567 pcf->Length= (int)fop->fldlen;
1568 else {
1569 int len;
1570
1571 if (pcf->Datefmt) {
1572 // Find the (max) length produced by the date format
1573 char buf[256];
1574 PGLOBAL g= GetPlug(table->in_use, xp);
1575 PDTP pdtp= MakeDateFormat(g, pcf->Datefmt, false, true, 0);
1576 struct tm datm;
1577 bzero(&datm, sizeof(datm));
1578 datm.tm_mday= 12;
1579 datm.tm_mon= 11;
1580 datm.tm_year= 112;
1581 mktime(&datm); // set other fields get proper day name
1582 len= strftime(buf, 256, pdtp->OutFmt, &datm);
1583 } else
1584 len= 0;
1585
1586 // 11 is for signed numeric representation of the date
1587 pcf->Length= (len) ? len : 11;
1588 } // endelse
1589
1590 // For Value setting
1591 pcf->Precision= MY_MAX(pcf->Precision, pcf->Length);
1592 break;
1593 default:
1594 break;
1595 } // endswitch type
1596
1597 if (fp->flags & UNSIGNED_FLAG)
1598 pcf->Flags |= U_UNSIGNED;
1599
1600 if (fp->flags & ZEROFILL_FLAG)
1601 pcf->Flags |= U_ZEROFILL;
1602
1603 // This is used to skip null bit
1604 if (fp->real_maybe_null())
1605 pcf->Flags |= U_NULLS;
1606
1607 // Mark virtual columns as such
1608 if (fp->vcol_info && !fp->stored_in_db)
1609 pcf->Flags |= U_VIRTUAL;
1610
1611 pcf->Key= 0; // Not used when called from MySQL
1612
1613 // Get the comment if any
1614 if (fp->comment.str && fp->comment.length)
1615 pcf->Remark= strz(g, fp->comment);
1616 else
1617 pcf->Remark= NULL;
1618
1619 return fldp;
1620} // end of GetColumnOption
1621
1622/****************************************************************************/
1623/* Return an index option structure. */
1624/****************************************************************************/
1625PXOS ha_connect::GetIndexOptionStruct(KEY *kp)
1626{
1627 return kp->option_struct;
1628} // end of GetIndexOptionStruct
1629
1630/****************************************************************************/
1631/* Return a Boolean index option or false if not specified. */
1632/****************************************************************************/
1633bool ha_connect::GetIndexOption(KEY *kp, PCSZ opname)
1634{
1635 bool opval= false;
1636 PXOS options= GetIndexOptionStruct(kp);
1637
1638 if (options) {
1639 if (!stricmp(opname, "Dynamic"))
1640 opval= options->dynamic;
1641 else if (!stricmp(opname, "Mapped"))
1642 opval= options->mapped;
1643
1644 } else if (kp->comment.str && kp->comment.length) {
1645 PCSZ pv, oplist= strz(xp->g, kp->comment);
1646
1647 if ((pv= GetListOption(xp->g, opname, oplist)))
1648 opval= (!*pv || *pv == 'y' || *pv == 'Y' || atoi(pv) != 0);
1649
1650 } // endif comment
1651
1652 return opval;
1653} // end of GetIndexOption
1654
1655/****************************************************************************/
1656/* Returns the index description structure used to make the index. */
1657/****************************************************************************/
1658bool ha_connect::IsUnique(uint n)
1659{
1660 TABLE_SHARE *s= (table) ? table->s : NULL;
1661 KEY kp= s->key_info[n];
1662
1663 return (kp.flags & 1) != 0;
1664} // end of IsUnique
1665
1666/****************************************************************************/
1667/* Returns the index description structure used to make the index. */
1668/****************************************************************************/
1669PIXDEF ha_connect::GetIndexInfo(TABLE_SHARE *s)
1670{
1671 char *name, *pn;
1672 bool unique;
1673 PIXDEF xdp, pxd=NULL, toidx= NULL;
1674 PKPDEF kpp, pkp;
1675 KEY kp;
1676 PGLOBAL& g= xp->g;
1677
1678 if (!s)
1679 s= table->s;
1680
1681 for (int n= 0; (unsigned)n < s->keynames.count; n++) {
1682 if (trace(1))
1683 htrc("Getting created index %d info\n", n + 1);
1684
1685 // Find the index to describe
1686 kp= s->key_info[n];
1687
1688 // Now get index information
1689 pn= (char*)s->keynames.type_names[n];
1690 name= PlugDup(g, pn);
1691 unique= (kp.flags & 1) != 0;
1692 pkp= NULL;
1693
1694 // Allocate the index description block
1695 xdp= new(g) INDEXDEF(name, unique, n);
1696
1697 // Get the the key parts info
1698 for (int k= 0; (unsigned)k < kp.user_defined_key_parts; k++) {
1699 pn= (char*)kp.key_part[k].field->field_name.str;
1700 name= PlugDup(g, pn);
1701
1702 // Allocate the key part description block
1703 kpp= new(g) KPARTDEF(name, k + 1);
1704 kpp->SetKlen(kp.key_part[k].length);
1705
1706#if 0 // NIY
1707 // Index on auto increment column can be an XXROW index
1708 if (kp.key_part[k].field->flags & AUTO_INCREMENT_FLAG &&
1709 kp.uder_defined_key_parts == 1) {
1710 char *type= GetStringOption("Type", "DOS");
1711 TABTYPE typ= GetTypeID(type);
1712
1713 xdp->SetAuto(IsTypeFixed(typ));
1714 } // endif AUTO_INCREMENT
1715#endif // 0
1716
1717 if (pkp)
1718 pkp->SetNext(kpp);
1719 else
1720 xdp->SetToKeyParts(kpp);
1721
1722 pkp= kpp;
1723 } // endfor k
1724
1725 xdp->SetNParts(kp.user_defined_key_parts);
1726 xdp->Dynamic= GetIndexOption(&kp, "Dynamic");
1727 xdp->Mapped= GetIndexOption(&kp, "Mapped");
1728
1729 if (pxd)
1730 pxd->SetNext(xdp);
1731 else
1732 toidx= xdp;
1733
1734 pxd= xdp;
1735 } // endfor n
1736
1737 return toidx;
1738} // end of GetIndexInfo
1739
1740/****************************************************************************/
1741/* Returns the index description structure used to make the index. */
1742/****************************************************************************/
1743bool ha_connect::CheckVirtualIndex(TABLE_SHARE *s)
1744{
1745
1746 char *rid;
1747 KEY kp;
1748 Field *fp;
1749 PGLOBAL& g= xp->g;
1750
1751 if (!s)
1752 s= table->s;
1753
1754 for (int n= 0; (unsigned)n < s->keynames.count; n++) {
1755 kp= s->key_info[n];
1756
1757 // Now get index information
1758
1759 // Get the the key parts info
1760 for (int k= 0; (unsigned)k < kp.user_defined_key_parts; k++) {
1761 fp= kp.key_part[k].field;
1762 rid= (fp->option_struct) ? fp->option_struct->special : NULL;
1763
1764 if (!rid || (stricmp(rid, "ROWID") && stricmp(rid, "ROWNUM"))) {
1765 strcpy(g->Message, "Invalid virtual index");
1766 return true;
1767 } // endif rowid
1768
1769 } // endfor k
1770
1771 } // endfor n
1772
1773 return false;
1774} // end of CheckVirtualIndex
1775
1776bool ha_connect::IsPartitioned(void)
1777{
1778#ifdef WITH_PARTITION_STORAGE_ENGINE
1779 if (tshp)
1780 return tshp->partition_info_str_len > 0;
1781 else if (table && table->part_info)
1782 return true;
1783 else
1784#endif
1785 return false;
1786
1787} // end of IsPartitioned
1788
1789PCSZ ha_connect::GetDBName(PCSZ name)
1790{
1791 return (name) ? name : table->s->db.str;
1792} // end of GetDBName
1793
1794const char *ha_connect::GetTableName(void)
1795{
1796 return tshp ? tshp->table_name.str : table_share->table_name.str;
1797} // end of GetTableName
1798
1799char *ha_connect::GetPartName(void)
1800{
1801 return (IsPartitioned()) ? partname : (char*)GetTableName();
1802} // end of GetTableName
1803
1804#if 0
1805/****************************************************************************/
1806/* Returns the column real or special name length of a field. */
1807/****************************************************************************/
1808int ha_connect::GetColNameLen(Field *fp)
1809{
1810 int n;
1811 PFOS fop= GetFieldOptionStruct(fp);
1812
1813 // Now get the column name length
1814 if (fop && fop->special)
1815 n= strlen(fop->special) + 1;
1816 else
1817 n= fp->field_name.length;
1818
1819 return n;
1820} // end of GetColNameLen
1821
1822/****************************************************************************/
1823/* Returns the column real or special name of a field. */
1824/****************************************************************************/
1825char *ha_connect::GetColName(Field *fp)
1826{
1827 PFOS fop= GetFieldOptionStruct(fp);
1828
1829 return (fop && fop->special) ? fop->special : (char*)fp->field_name.str;
1830} // end of GetColName
1831
1832/****************************************************************************/
1833/* Adds the column real or special name of a field to a string. */
1834/****************************************************************************/
1835void ha_connect::AddColName(char *cp, Field *fp)
1836{
1837 PFOS fop= GetFieldOptionStruct(fp);
1838
1839 // Now add the column name
1840 if (fop && fop->special)
1841 // The prefix * mark the column as "special"
1842 strcat(strcpy(cp, "*"), strupr(fop->special));
1843 else
1844 strcpy(cp, fp->field_name.str);
1845
1846} // end of AddColName
1847#endif // 0
1848
1849/***********************************************************************/
1850/* This function sets the current database path. */
1851/***********************************************************************/
1852bool ha_connect::SetDataPath(PGLOBAL g, PCSZ path)
1853{
1854 return (!(datapath= SetPath(g, path)));
1855} // end of SetDataPath
1856
1857/****************************************************************************/
1858/* Get the table description block of a CONNECT table. */
1859/****************************************************************************/
1860PTDB ha_connect::GetTDB(PGLOBAL g)
1861{
1862 const char *table_name;
1863 PTDB tp;
1864
1865 // Double test to be on the safe side
1866 if (!g || !table)
1867 return NULL;
1868
1869 table_name= GetTableName();
1870
1871 if (!xp->CheckQuery(valid_query_id) && tdbp
1872 && !stricmp(tdbp->GetName(), table_name)
1873 && (tdbp->GetMode() == xmod
1874 || (tdbp->GetMode() == MODE_READ && xmod == MODE_READX)
1875 || tdbp->GetAmType() == TYPE_AM_XML)) {
1876 tp= tdbp;
1877 tp->SetMode(xmod);
1878 } else if ((tp= CntGetTDB(g, table_name, xmod, this))) {
1879 valid_query_id= xp->last_query_id;
1880// tp->SetMode(xmod);
1881 } else
1882 htrc("GetTDB: %s\n", g->Message);
1883
1884 return tp;
1885} // end of GetTDB
1886
1887/****************************************************************************/
1888/* Open a CONNECT table, restricting column list if cols is true. */
1889/****************************************************************************/
1890int ha_connect::OpenTable(PGLOBAL g, bool del)
1891{
1892 bool rc= false;
1893 char *c1= NULL, *c2=NULL;
1894
1895 // Double test to be on the safe side
1896 if (!g || !table) {
1897 htrc("OpenTable logical error; g=%p table=%p\n", g, table);
1898 return HA_ERR_INITIALIZATION;
1899 } // endif g
1900
1901 if (!(tdbp= GetTDB(g)))
1902 return RC_FX;
1903 else if (tdbp->IsReadOnly())
1904 switch (xmod) {
1905 case MODE_WRITE:
1906 case MODE_INSERT:
1907 case MODE_UPDATE:
1908 case MODE_DELETE:
1909 strcpy(g->Message, MSG(READ_ONLY));
1910 return HA_ERR_TABLE_READONLY;
1911 default:
1912 break;
1913 } // endswitch xmode
1914
1915 if (xmod != MODE_INSERT || tdbp->GetAmType() == TYPE_AM_MYSQL
1916 || tdbp->GetAmType() == TYPE_AM_ODBC
1917 || tdbp->GetAmType() == TYPE_AM_JDBC) {
1918 // Get the list of used fields (columns)
1919 char *p;
1920 unsigned int k1, k2, n1, n2;
1921 Field* *field;
1922 Field* fp;
1923 MY_BITMAP *map= (xmod == MODE_INSERT) ? table->write_set : table->read_set;
1924 MY_BITMAP *ump= (xmod == MODE_UPDATE) ? table->write_set : NULL;
1925
1926 k1= k2= 0;
1927 n1= n2= 1; // 1 is space for final null character
1928
1929 for (field= table->field; fp= *field; field++) {
1930 if (bitmap_is_set(map, fp->field_index)) {
1931 n1+= (fp->field_name.length + 1);
1932 k1++;
1933 } // endif
1934
1935 if (ump && bitmap_is_set(ump, fp->field_index)) {
1936 n2+= (fp->field_name.length + 1);
1937 k2++;
1938 } // endif
1939
1940 } // endfor field
1941
1942 if (k1) {
1943 p= c1= (char*)PlugSubAlloc(g, NULL, n1);
1944
1945 for (field= table->field; fp= *field; field++)
1946 if (bitmap_is_set(map, fp->field_index)) {
1947 strcpy(p, fp->field_name.str);
1948 p+= (fp->field_name.length + 1);
1949 } // endif used field
1950
1951 *p= '\0'; // mark end of list
1952 } // endif k1
1953
1954 if (k2) {
1955 p= c2= (char*)PlugSubAlloc(g, NULL, n2);
1956
1957 for (field= table->field; fp= *field; field++)
1958 if (bitmap_is_set(ump, fp->field_index)) {
1959 strcpy(p, fp->field_name.str);
1960
1961 if (part_id && bitmap_is_set(part_id, fp->field_index)) {
1962 // Trying to update a column used for partitioning
1963 // This cannot be currently done because it may require
1964 // a row to be moved in another partition.
1965 sprintf(g->Message,
1966 "Cannot update column %s because it is used for partitioning",
1967 p);
1968 return HA_ERR_INTERNAL_ERROR;
1969 } // endif part_id
1970
1971 p+= (strlen(p) + 1);
1972 } // endif used field
1973
1974 *p= '\0'; // mark end of list
1975 } // endif k2
1976
1977 } // endif xmod
1978
1979 // Open the table
1980 if (!(rc= CntOpenTable(g, tdbp, xmod, c1, c2, del, this))) {
1981 istable= true;
1982// strmake(tname, table_name, sizeof(tname)-1);
1983
1984 // We may be in a create index query
1985 if (xmod == MODE_ANY && *tdbp->GetName() != '#') {
1986 // The current indexes
1987 PIXDEF oldpix= GetIndexInfo();
1988 } // endif xmod
1989
1990 } else
1991 htrc("OpenTable: %s\n", g->Message);
1992
1993 if (rc) {
1994 tdbp= NULL;
1995 valid_info= false;
1996 } // endif rc
1997
1998 return (rc) ? HA_ERR_INITIALIZATION : 0;
1999} // end of OpenTable
2000
2001
2002/****************************************************************************/
2003/* CheckColumnList: check that all bitmap columns do exist. */
2004/****************************************************************************/
2005bool ha_connect::CheckColumnList(PGLOBAL g)
2006{
2007 // Check the list of used fields (columns)
2008 bool brc= false;
2009 PCOL colp;
2010 Field* *field;
2011 Field* fp;
2012 MY_BITMAP *map= table->read_set;
2013
2014 try {
2015 for (field= table->field; fp= *field; field++)
2016 if (bitmap_is_set(map, fp->field_index)) {
2017 if (!(colp= tdbp->ColDB(g, (PSZ)fp->field_name.str, 0))) {
2018 sprintf(g->Message, "Column %s not found in %s",
2019 fp->field_name.str, tdbp->GetName());
2020 throw 1;
2021 } // endif colp
2022
2023 if ((brc= colp->InitValue(g)))
2024 throw 2;
2025
2026 colp->AddColUse(U_P); // For PLG tables
2027 } // endif
2028
2029 } catch (int n) {
2030 if (trace(1))
2031 htrc("Exception %d: %s\n", n, g->Message);
2032 brc = true;
2033 } catch (const char *msg) {
2034 strcpy(g->Message, msg);
2035 brc = true;
2036 } // end catch
2037
2038 return brc;
2039} // end of CheckColumnList
2040
2041
2042/****************************************************************************/
2043/* IsOpened: returns true if the table is already opened. */
2044/****************************************************************************/
2045bool ha_connect::IsOpened(void)
2046{
2047 return (!xp->CheckQuery(valid_query_id) && tdbp
2048 && tdbp->GetUse() == USE_OPEN);
2049} // end of IsOpened
2050
2051
2052/****************************************************************************/
2053/* Close a CONNECT table. */
2054/****************************************************************************/
2055int ha_connect::CloseTable(PGLOBAL g)
2056{
2057 int rc= CntCloseTable(g, tdbp, nox, abort);
2058 tdbp= NULL;
2059 sdvalin1= sdvalin2= sdvalin3= sdvalin4= NULL;
2060 sdvalout=NULL;
2061 valid_info= false;
2062 indexing= -1;
2063 nox= true;
2064 abort= false;
2065 return rc;
2066} // end of CloseTable
2067
2068
2069/***********************************************************************/
2070/* Make a pseudo record from current row values. Specific to MySQL. */
2071/***********************************************************************/
2072int ha_connect::MakeRecord(char *buf)
2073{
2074 PCSZ fmt;
2075 char *p, val[32];
2076 int rc= 0;
2077 Field* *field;
2078 Field *fp;
2079 my_bitmap_map *org_bitmap;
2080 CHARSET_INFO *charset= tdbp->data_charset();
2081//MY_BITMAP readmap;
2082 MY_BITMAP *map;
2083 PVAL value;
2084 PCOL colp= NULL;
2085 DBUG_ENTER("ha_connect::MakeRecord");
2086
2087 if (trace(2))
2088 htrc("Maps: read=%08X write=%08X vcol=%08X defr=%08X defw=%08X\n",
2089 *table->read_set->bitmap, *table->write_set->bitmap,
2090 (table->vcol_set) ? *table->vcol_set->bitmap : 0,
2091 *table->def_read_set.bitmap, *table->def_write_set.bitmap);
2092
2093 // Avoid asserts in field::store() for columns that are not updated
2094 org_bitmap= dbug_tmp_use_all_columns(table, table->write_set);
2095
2096 // This is for variable_length rows
2097 memset(buf, 0, table->s->null_bytes);
2098
2099 // When sorting read_set selects all columns, so we use def_read_set
2100 map= (MY_BITMAP *)&table->def_read_set;
2101
2102 // Make the pseudo record from field values
2103 for (field= table->field; *field && !rc; field++) {
2104 fp= *field;
2105
2106 if (fp->vcol_info && !fp->stored_in_db)
2107 continue; // This is a virtual column
2108
2109 if (bitmap_is_set(map, fp->field_index) || alter) {
2110 // This is a used field, fill the buffer with value
2111 for (colp= tdbp->GetColumns(); colp; colp= colp->GetNext())
2112 if ((!mrr || colp->GetKcol()) &&
2113 !stricmp(colp->GetName(), fp->field_name.str))
2114 break;
2115
2116 if (!colp) {
2117 if (mrr)
2118 continue;
2119
2120 htrc("Column %s not found\n", fp->field_name.str);
2121 dbug_tmp_restore_column_map(table->write_set, org_bitmap);
2122 DBUG_RETURN(HA_ERR_WRONG_IN_RECORD);
2123 } // endif colp
2124
2125 value= colp->GetValue();
2126 p= NULL;
2127
2128 // All this was better optimized
2129 if (!value->IsNull()) {
2130 switch (value->GetType()) {
2131 case TYPE_DATE:
2132 if (!sdvalout)
2133 sdvalout= AllocateValue(xp->g, TYPE_STRING, 20);
2134
2135 switch (fp->type()) {
2136 case MYSQL_TYPE_DATE:
2137 fmt= "%Y-%m-%d";
2138 break;
2139 case MYSQL_TYPE_TIME:
2140 fmt= "%H:%M:%S";
2141 break;
2142 case MYSQL_TYPE_YEAR:
2143 fmt= "%Y";
2144 break;
2145 default:
2146 fmt= "%Y-%m-%d %H:%M:%S";
2147 break;
2148 } // endswitch type
2149
2150 // Get date in the format required by MySQL fields
2151 value->FormatValue(sdvalout, fmt);
2152 p= sdvalout->GetCharValue();
2153 rc= fp->store(p, strlen(p), charset, CHECK_FIELD_WARN);
2154 break;
2155 case TYPE_STRING:
2156 case TYPE_DECIM:
2157 p= value->GetCharString(val);
2158 charset= tdbp->data_charset();
2159 rc= fp->store(p, strlen(p), charset, CHECK_FIELD_WARN);
2160 break;
2161 case TYPE_BIN:
2162 p = value->GetCharValue();
2163 charset = &my_charset_bin;
2164 rc = fp->store(p, strlen(p), charset, CHECK_FIELD_WARN);
2165 break;
2166 case TYPE_DOUBLE:
2167 rc= fp->store(value->GetFloatValue());
2168 break;
2169 default:
2170 rc= fp->store(value->GetBigintValue(), value->IsUnsigned());
2171 break;
2172 } // endswitch Type
2173
2174 // Store functions returns 1 on overflow and -1 on fatal error
2175 if (rc > 0) {
2176 char buf[256];
2177 THD *thd= ha_thd();
2178
2179 sprintf(buf, "Out of range value %.140s for column '%s' at row %ld",
2180 value->GetCharString(val),
2181 fp->field_name.str,
2182 thd->get_stmt_da()->current_row_for_warning());
2183
2184 push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, buf);
2185 DBUG_PRINT("MakeRecord", ("%s", buf));
2186 rc= 0;
2187 } else if (rc < 0)
2188 rc= HA_ERR_WRONG_IN_RECORD;
2189
2190 fp->set_notnull();
2191 } else
2192 fp->set_null();
2193
2194 } // endif bitmap
2195
2196 } // endfor field
2197
2198 // This is sometimes required for partition tables because the buf
2199 // can be different from the table->record[0] buffer
2200 if (buf != (char*)table->record[0])
2201 memcpy(buf, table->record[0], table->s->stored_rec_length);
2202
2203 // This is copied from ha_tina and is necessary to avoid asserts
2204 dbug_tmp_restore_column_map(table->write_set, org_bitmap);
2205 DBUG_RETURN(rc);
2206} // end of MakeRecord
2207
2208
2209/***********************************************************************/
2210/* Set row values from a MySQL pseudo record. Specific to MySQL. */
2211/***********************************************************************/
2212int ha_connect::ScanRecord(PGLOBAL g, const uchar *)
2213{
2214 char attr_buffer[1024];
2215 char data_buffer[1024];
2216 PCSZ fmt;
2217 int rc= 0;
2218 PCOL colp;
2219 PVAL value, sdvalin;
2220 Field *fp;
2221//PTDBASE tp= (PTDBASE)tdbp;
2222 String attribute(attr_buffer, sizeof(attr_buffer),
2223 table->s->table_charset);
2224 my_bitmap_map *bmap= dbug_tmp_use_all_columns(table, table->read_set);
2225 const CHARSET_INFO *charset= tdbp->data_charset();
2226 String data_charset_value(data_buffer, sizeof(data_buffer), charset);
2227
2228 // Scan the pseudo record for field values and set column values
2229 for (Field **field=table->field ; *field ; field++) {
2230 fp= *field;
2231
2232 if ((fp->vcol_info && !fp->stored_in_db) ||
2233 fp->option_struct->special)
2234 continue; // Is a virtual column possible here ???
2235
2236 if ((xmod == MODE_INSERT && tdbp->GetAmType() != TYPE_AM_MYSQL
2237 && tdbp->GetAmType() != TYPE_AM_ODBC
2238 && tdbp->GetAmType() != TYPE_AM_JDBC) ||
2239 bitmap_is_set(table->write_set, fp->field_index)) {
2240 for (colp= tdbp->GetSetCols(); colp; colp= colp->GetNext())
2241 if (!stricmp(colp->GetName(), fp->field_name.str))
2242 break;
2243
2244 if (!colp) {
2245 htrc("Column %s not found\n", fp->field_name.str);
2246 rc= HA_ERR_WRONG_IN_RECORD;
2247 goto err;
2248 } else
2249 value= colp->GetValue();
2250
2251 // This is a used field, fill the value from the row buffer
2252 // All this could be better optimized
2253 if (fp->is_null()) {
2254 if (colp->IsNullable())
2255 value->SetNull(true);
2256
2257 value->Reset();
2258 } else switch (value->GetType()) {
2259 case TYPE_DOUBLE:
2260 value->SetValue(fp->val_real());
2261 break;
2262 case TYPE_DATE:
2263 // Get date in the format produced by MySQL fields
2264 switch (fp->type()) {
2265 case MYSQL_TYPE_DATE:
2266 if (!sdvalin2) {
2267 sdvalin2= (DTVAL*)AllocateValue(xp->g, TYPE_DATE, 19);
2268 fmt= "YYYY-MM-DD";
2269 ((DTVAL*)sdvalin2)->SetFormat(g, fmt, strlen(fmt));
2270 } // endif sdvalin1
2271
2272 sdvalin= sdvalin2;
2273 break;
2274 case MYSQL_TYPE_TIME:
2275 if (!sdvalin3) {
2276 sdvalin3= (DTVAL*)AllocateValue(xp->g, TYPE_DATE, 19);
2277 fmt= "hh:mm:ss";
2278 ((DTVAL*)sdvalin3)->SetFormat(g, fmt, strlen(fmt));
2279 } // endif sdvalin1
2280
2281 sdvalin= sdvalin3;
2282 break;
2283 case MYSQL_TYPE_YEAR:
2284 if (!sdvalin4) {
2285 sdvalin4= (DTVAL*)AllocateValue(xp->g, TYPE_DATE, 19);
2286 fmt= "YYYY";
2287 ((DTVAL*)sdvalin4)->SetFormat(g, fmt, strlen(fmt));
2288 } // endif sdvalin1
2289
2290 sdvalin= sdvalin4;
2291 break;
2292 default:
2293 if (!sdvalin1) {
2294 sdvalin1= (DTVAL*)AllocateValue(xp->g, TYPE_DATE, 19);
2295 fmt= "YYYY-MM-DD hh:mm:ss";
2296 ((DTVAL*)sdvalin1)->SetFormat(g, fmt, strlen(fmt));
2297 } // endif sdvalin1
2298
2299 sdvalin= sdvalin1;
2300 } // endswitch type
2301
2302 sdvalin->SetNullable(colp->IsNullable());
2303 fp->val_str(&attribute);
2304 sdvalin->SetValue_psz(attribute.c_ptr_safe());
2305 value->SetValue_pval(sdvalin);
2306 break;
2307 default:
2308 fp->val_str(&attribute);
2309
2310 if (charset != &my_charset_bin) {
2311 // Convert from SQL field charset to DATA_CHARSET
2312 uint cnv_errors;
2313
2314 data_charset_value.copy(attribute.ptr(), attribute.length(),
2315 attribute.charset(), charset, &cnv_errors);
2316 value->SetValue_psz(data_charset_value.c_ptr_safe());
2317 } else
2318 value->SetValue_psz(attribute.c_ptr_safe());
2319
2320 break;
2321 } // endswitch Type
2322
2323#ifdef NEWCHANGE
2324 } else if (xmod == MODE_UPDATE) {
2325 PCOL cp;
2326
2327 for (cp= tdbp->GetColumns(); cp; cp= cp->GetNext())
2328 if (!stricmp(colp->GetName(), cp->GetName()))
2329 break;
2330
2331 if (!cp) {
2332 rc= HA_ERR_WRONG_IN_RECORD;
2333 goto err;
2334 } // endif cp
2335
2336 value->SetValue_pval(cp->GetValue());
2337 } else // mode Insert
2338 value->Reset();
2339#else
2340 } // endif bitmap_is_set
2341#endif
2342
2343 } // endfor field
2344
2345 err:
2346 dbug_tmp_restore_column_map(table->read_set, bmap);
2347 return rc;
2348} // end of ScanRecord
2349
2350
2351/***********************************************************************/
2352/* Check change in index column. Specific to MySQL. */
2353/* Should be elaborated to check for real changes. */
2354/***********************************************************************/
2355int ha_connect::CheckRecord(PGLOBAL g, const uchar *, const uchar *newbuf)
2356{
2357 return ScanRecord(g, newbuf);
2358} // end of dummy CheckRecord
2359
2360
2361/***********************************************************************/
2362/* Return true if this field is used in current indexing. */
2363/***********************************************************************/
2364bool ha_connect::IsIndexed(Field *fp)
2365{
2366 if (active_index < MAX_KEY) {
2367 KEY_PART_INFO *kpart;
2368 KEY *kfp= &table->key_info[active_index];
2369 uint rem= kfp->user_defined_key_parts;
2370
2371 for (kpart= kfp->key_part; rem; rem--, kpart++)
2372 if (kpart->field == fp)
2373 return true;
2374
2375 } // endif active_index
2376
2377 return false;
2378} // end of IsIndexed
2379
2380
2381/***********************************************************************/
2382/* Return the where clause for remote indexed read. */
2383/***********************************************************************/
2384bool ha_connect::MakeKeyWhere(PGLOBAL g, PSTRG qry, OPVAL vop, char q,
2385 const key_range *kr)
2386{
2387 const uchar *ptr;
2388//uint i, rem, len, klen, stlen;
2389 uint i, rem, len, stlen;
2390 bool nq, both, oom;
2391 OPVAL op;
2392 Field *fp;
2393 const key_range *ranges[2];
2394 my_bitmap_map *old_map;
2395 KEY *kfp;
2396 KEY_PART_INFO *kpart;
2397
2398 if (active_index == MAX_KEY)
2399 return false;
2400
2401 ranges[0]= kr;
2402 ranges[1]= (end_range && !eq_range) ? &save_end_range : NULL;
2403
2404 if (!ranges[0] && !ranges[1]) {
2405 strcpy(g->Message, "MakeKeyWhere: No key");
2406 return true;
2407 } else
2408 both= ranges[0] && ranges[1];
2409
2410 kfp= &table->key_info[active_index];
2411 old_map= dbug_tmp_use_all_columns(table, table->write_set);
2412
2413 for (i = 0; i <= 1; i++) {
2414 if (ranges[i] == NULL)
2415 continue;
2416
2417 if (both && i > 0)
2418 qry->Append(") AND (");
2419 else
2420 qry->Append(" WHERE (");
2421
2422// klen= len= ranges[i]->length;
2423 len= ranges[i]->length;
2424 rem= kfp->user_defined_key_parts;
2425 ptr= ranges[i]->key;
2426
2427 for (kpart= kfp->key_part; rem; rem--, kpart++) {
2428 fp= kpart->field;
2429 stlen= kpart->store_length;
2430 nq= fp->str_needs_quotes();
2431
2432 if (kpart != kfp->key_part)
2433 qry->Append(" AND ");
2434
2435 if (q) {
2436 qry->Append(q);
2437 qry->Append((PSZ)fp->field_name.str);
2438 qry->Append(q);
2439 } else
2440 qry->Append((PSZ)fp->field_name.str);
2441
2442 switch (ranges[i]->flag) {
2443 case HA_READ_KEY_EXACT:
2444// op= (stlen >= len || !nq || fp->result_type() != STRING_RESULT)
2445// ? OP_EQ : OP_LIKE;
2446 op= OP_EQ;
2447 break;
2448 case HA_READ_AFTER_KEY:
2449 op= (stlen >= len || i > 0) ? (i > 0 ? OP_LE : OP_GT) : OP_GE;
2450 break;
2451 case HA_READ_KEY_OR_NEXT:
2452 op= OP_GE;
2453 break;
2454 case HA_READ_BEFORE_KEY:
2455 op= (stlen >= len) ? OP_LT : OP_LE;
2456 break;
2457 case HA_READ_KEY_OR_PREV:
2458 op= OP_LE;
2459 break;
2460 default:
2461 sprintf(g->Message, "cannot handle flag %d", ranges[i]->flag);
2462 goto err;
2463 } // endswitch flag
2464
2465 qry->Append((PSZ)GetValStr(op, false));
2466
2467 if (nq)
2468 qry->Append('\'');
2469
2470 if (kpart->key_part_flag & HA_VAR_LENGTH_PART) {
2471 String varchar;
2472 uint var_length= uint2korr(ptr);
2473
2474 varchar.set_quick((char*)ptr + HA_KEY_BLOB_LENGTH,
2475 var_length, &my_charset_bin);
2476 qry->Append(varchar.ptr(), varchar.length(), nq);
2477 } else {
2478 char strbuff[MAX_FIELD_WIDTH];
2479 String str(strbuff, sizeof(strbuff), kpart->field->charset()), *res;
2480
2481 res= fp->val_str(&str, ptr);
2482 qry->Append(res->ptr(), res->length(), nq);
2483 } // endif flag
2484
2485 if (nq)
2486 qry->Append('\'');
2487
2488 if (stlen >= len)
2489 break;
2490
2491 len-= stlen;
2492
2493 /* For nullable columns, null-byte is already skipped before, that is
2494 ptr was incremented by 1. Since store_length still counts null-byte,
2495 we need to subtract 1 from store_length. */
2496 ptr+= stlen - MY_TEST(kpart->null_bit);
2497 } // endfor kpart
2498
2499 } // endfor i
2500
2501 qry->Append(')');
2502
2503 if ((oom= qry->IsTruncated()))
2504 strcpy(g->Message, "Out of memory");
2505
2506 dbug_tmp_restore_column_map(table->write_set, old_map);
2507 return oom;
2508
2509err:
2510 dbug_tmp_restore_column_map(table->write_set, old_map);
2511 return true;
2512} // end of MakeKeyWhere
2513
2514
2515/***********************************************************************/
2516/* Return the string representing an operator. */
2517/***********************************************************************/
2518const char *ha_connect::GetValStr(OPVAL vop, bool neg)
2519{
2520 const char *val;
2521
2522 switch (vop) {
2523 case OP_EQ:
2524 val= " = ";
2525 break;
2526 case OP_NE:
2527 val= " <> ";
2528 break;
2529 case OP_GT:
2530 val= " > ";
2531 break;
2532 case OP_GE:
2533 val= " >= ";
2534 break;
2535 case OP_LT:
2536 val= " < ";
2537 break;
2538 case OP_LE:
2539 val= " <= ";
2540 break;
2541 case OP_IN:
2542 val= (neg) ? " NOT IN (" : " IN (";
2543 break;
2544 case OP_NULL:
2545 val= (neg) ? " IS NOT NULL" : " IS NULL";
2546 break;
2547 case OP_LIKE:
2548 val= (neg) ? " NOT LIKE " : " LIKE ";
2549 break;
2550 case OP_XX:
2551 val= (neg) ? " NOT BETWEEN " : " BETWEEN ";
2552 break;
2553 case OP_EXIST:
2554 val= (neg) ? " NOT EXISTS " : " EXISTS ";
2555 break;
2556 case OP_AND:
2557 val= " AND ";
2558 break;
2559 case OP_OR:
2560 val= " OR ";
2561 break;
2562 case OP_NOT:
2563 val= " NOT ";
2564 break;
2565 case OP_CNC:
2566 val= " || ";
2567 break;
2568 case OP_ADD:
2569 val= " + ";
2570 break;
2571 case OP_SUB:
2572 val= " - ";
2573 break;
2574 case OP_MULT:
2575 val= " * ";
2576 break;
2577 case OP_DIV:
2578 val= " / ";
2579 break;
2580 default:
2581 val= " ? ";
2582 break;
2583 } /* endswitch */
2584
2585 return val;
2586} // end of GetValStr
2587
2588#if 0
2589/***********************************************************************/
2590/* Check the WHERE condition and return a CONNECT filter. */
2591/***********************************************************************/
2592PFIL ha_connect::CheckFilter(PGLOBAL g)
2593{
2594 return CondFilter(g, (Item *)pushed_cond);
2595} // end of CheckFilter
2596#endif // 0
2597
2598/***********************************************************************/
2599/* Check the WHERE condition and return a CONNECT filter. */
2600/***********************************************************************/
2601PFIL ha_connect::CondFilter(PGLOBAL g, Item *cond)
2602{
2603 unsigned int i;
2604 bool ismul= false;
2605 OPVAL vop= OP_XX;
2606 PFIL filp= NULL;
2607
2608 if (!cond)
2609 return NULL;
2610
2611 if (trace(1))
2612 htrc("Cond type=%d\n", cond->type());
2613
2614 if (cond->type() == COND::COND_ITEM) {
2615 PFIL fp;
2616 Item_cond *cond_item= (Item_cond *)cond;
2617
2618 if (trace(1))
2619 htrc("Cond: Ftype=%d name=%s\n", cond_item->functype(),
2620 cond_item->func_name());
2621
2622 switch (cond_item->functype()) {
2623 case Item_func::COND_AND_FUNC: vop= OP_AND; break;
2624 case Item_func::COND_OR_FUNC: vop= OP_OR; break;
2625 default: return NULL;
2626 } // endswitch functype
2627
2628 List<Item>* arglist= cond_item->argument_list();
2629 List_iterator<Item> li(*arglist);
2630 Item *subitem;
2631
2632 for (i= 0; i < arglist->elements; i++)
2633 if ((subitem= li++)) {
2634 if (!(fp= CondFilter(g, subitem))) {
2635 if (vop == OP_OR)
2636 return NULL;
2637 } else
2638 filp= (filp) ? MakeFilter(g, filp, vop, fp) : fp;
2639
2640 } else
2641 return NULL;
2642
2643 } else if (cond->type() == COND::FUNC_ITEM) {
2644 unsigned int i;
2645 bool iscol, neg= FALSE;
2646 PCOL colp[2]= {NULL,NULL};
2647 PPARM pfirst= NULL, pprec= NULL;
2648 POPER pop;
2649 Item_func *condf= (Item_func *)cond;
2650 Item* *args= condf->arguments();
2651
2652 if (trace(1))
2653 htrc("Func type=%d argnum=%d\n", condf->functype(),
2654 condf->argument_count());
2655
2656 switch (condf->functype()) {
2657 case Item_func::EQUAL_FUNC:
2658 case Item_func::EQ_FUNC: vop= OP_EQ; break;
2659 case Item_func::NE_FUNC: vop= OP_NE; break;
2660 case Item_func::LT_FUNC: vop= OP_LT; break;
2661 case Item_func::LE_FUNC: vop= OP_LE; break;
2662 case Item_func::GE_FUNC: vop= OP_GE; break;
2663 case Item_func::GT_FUNC: vop= OP_GT; break;
2664 case Item_func::IN_FUNC: vop= OP_IN; /* fall through */
2665 case Item_func::BETWEEN:
2666 ismul= true;
2667 neg= ((Item_func_opt_neg *)condf)->negated;
2668 break;
2669 default: return NULL;
2670 } // endswitch functype
2671
2672 pop= (POPER)PlugSubAlloc(g, NULL, sizeof(OPER));
2673 pop->Name= NULL;
2674 pop->Val=vop;
2675 pop->Mod= 0;
2676
2677 if (condf->argument_count() < 2)
2678 return NULL;
2679
2680 for (i= 0; i < condf->argument_count(); i++) {
2681 if (trace(1))
2682 htrc("Argtype(%d)=%d\n", i, args[i]->type());
2683
2684 if (i >= 2 && !ismul) {
2685 if (trace(1))
2686 htrc("Unexpected arg for vop=%d\n", vop);
2687
2688 continue;
2689 } // endif i
2690
2691 if ((iscol= args[i]->type() == COND::FIELD_ITEM)) {
2692 Item_field *pField= (Item_field *)args[i];
2693
2694 // IN and BETWEEN clauses should be col VOP list
2695 if (i && ismul)
2696 return NULL;
2697
2698 if (pField->field->table != table ||
2699 !(colp[i]= tdbp->ColDB(g, (PSZ)pField->field->field_name.str, 0)))
2700 return NULL; // Column does not belong to this table
2701
2702 // These types are not yet implemented (buggy)
2703 switch (pField->field->type()) {
2704 case MYSQL_TYPE_TIMESTAMP:
2705 case MYSQL_TYPE_DATE:
2706 case MYSQL_TYPE_TIME:
2707 case MYSQL_TYPE_DATETIME:
2708 case MYSQL_TYPE_YEAR:
2709 case MYSQL_TYPE_NEWDATE:
2710 return NULL;
2711 default:
2712 break;
2713 } // endswitch type
2714
2715 if (trace(1)) {
2716 htrc("Field index=%d\n", pField->field->field_index);
2717 htrc("Field name=%s\n", pField->field->field_name.str);
2718 } // endif trace
2719
2720 } else {
2721 char buff[256];
2722 String *res, tmp(buff, sizeof(buff), &my_charset_bin);
2723 Item_basic_constant *pval= (Item_basic_constant *)args[i];
2724 PPARM pp= (PPARM)PlugSubAlloc(g, NULL, sizeof(PARM));
2725
2726 // IN and BETWEEN clauses should be col VOP list
2727 if (!i && (ismul))
2728 return NULL;
2729
2730 switch (args[i]->real_type()) {
2731 case COND::STRING_ITEM:
2732 res= pval->val_str(&tmp);
2733 pp->Value= PlugSubAllocStr(g, NULL, res->ptr(), res->length());
2734 pp->Type= (pp->Value) ? TYPE_STRING : TYPE_ERROR;
2735 break;
2736 case COND::INT_ITEM:
2737 pp->Type= TYPE_INT;
2738 pp->Value= PlugSubAlloc(g, NULL, sizeof(int));
2739 *((int*)pp->Value)= (int)pval->val_int();
2740 break;
2741 case COND::DATE_ITEM:
2742 pp->Type= TYPE_DATE;
2743 pp->Value= PlugSubAlloc(g, NULL, sizeof(int));
2744 *((int*)pp->Value)= (int)pval->val_int_from_date();
2745 break;
2746 case COND::REAL_ITEM:
2747 pp->Type= TYPE_DOUBLE;
2748 pp->Value= PlugSubAlloc(g, NULL, sizeof(double));
2749 *((double*)pp->Value)= pval->val_real();
2750 break;
2751 case COND::DECIMAL_ITEM:
2752 pp->Type= TYPE_DOUBLE;
2753 pp->Value= PlugSubAlloc(g, NULL, sizeof(double));
2754 *((double*)pp->Value)= pval->val_real_from_decimal();
2755 break;
2756 case COND::CACHE_ITEM: // Possible ???
2757 case COND::NULL_ITEM: // TODO: handle this
2758 default:
2759 return NULL;
2760 } // endswitch type
2761
2762 if (trace(1))
2763 htrc("Value type=%hd\n", pp->Type);
2764
2765 // Append the value to the argument list
2766 if (pprec)
2767 pprec->Next= pp;
2768 else
2769 pfirst= pp;
2770
2771 pp->Domain= i;
2772 pp->Next= NULL;
2773 pprec= pp;
2774 } // endif type
2775
2776 } // endfor i
2777
2778 filp= MakeFilter(g, colp, pop, pfirst, neg);
2779 } else {
2780 if (trace(1))
2781 htrc("Unsupported condition\n");
2782
2783 return NULL;
2784 } // endif's type
2785
2786 return filp;
2787} // end of CondFilter
2788
2789/***********************************************************************/
2790/* Check the WHERE condition and return a MYSQL/ODBC/JDBC/WQL filter. */
2791/***********************************************************************/
2792PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond)
2793{
2794 AMT tty = filp->Type;
2795 char *body= filp->Body;
2796 char *havg= filp->Having;
2797 unsigned int i;
2798 bool ismul= false, x= (tty == TYPE_AM_MYX || tty == TYPE_AM_XDBC);
2799 bool nonul= ((tty == TYPE_AM_ODBC || tty == TYPE_AM_JDBC) &&
2800 (tdbp->GetMode() == MODE_INSERT || tdbp->GetMode() == MODE_DELETE));
2801 OPVAL vop= OP_XX;
2802
2803 if (!cond)
2804 return NULL;
2805
2806 if (trace(1))
2807 htrc("Cond type=%d\n", cond->type());
2808
2809 if (cond->type() == COND::COND_ITEM) {
2810 char *pb0, *pb1, *pb2, *ph0= 0, *ph1= 0, *ph2= 0;
2811 bool bb = false, bh = false;
2812 Item_cond *cond_item= (Item_cond *)cond;
2813
2814 if (x)
2815 return NULL;
2816 else
2817 pb0= pb1= pb2= ph0= ph1= ph2= NULL;
2818
2819 if (trace(1))
2820 htrc("Cond: Ftype=%d name=%s\n", cond_item->functype(),
2821 cond_item->func_name());
2822
2823 switch (cond_item->functype()) {
2824 case Item_func::COND_AND_FUNC: vop= OP_AND; break;
2825 case Item_func::COND_OR_FUNC: vop= OP_OR; break;
2826 default: return NULL;
2827 } // endswitch functype
2828
2829 List<Item>* arglist= cond_item->argument_list();
2830 List_iterator<Item> li(*arglist);
2831 const Item *subitem;
2832
2833 pb0= pb1= body + strlen(body);
2834 strcpy(pb0, "(");
2835 pb2= pb1 + 1;
2836
2837 if (havg) {
2838 ph0= ph1= havg + strlen(havg);
2839 strcpy(ph0, "(");
2840 ph2= ph1 + 1;
2841 } // endif havg
2842
2843 for (i= 0; i < arglist->elements; i++)
2844 if ((subitem= li++)) {
2845 if (!CheckCond(g, filp, subitem)) {
2846 if (vop == OP_OR || nonul)
2847 return NULL;
2848 else {
2849 *pb2= 0;
2850 if (havg) *ph2= 0;
2851 } // endelse
2852
2853 } else {
2854 if (filp->Bd) {
2855 pb1= pb2 + strlen(pb2);
2856 strcpy(pb1, GetValStr(vop, false));
2857 pb2= pb1 + strlen(pb1);
2858 } // endif Bd
2859
2860 if (filp->Hv) {
2861 ph1= ph2 + strlen(ph2);
2862 strcpy(ph1, GetValStr(vop, false));
2863 ph2= ph1 + strlen(ph1);
2864 } // endif Hv
2865
2866 } // endif CheckCond
2867
2868 bb |= filp->Bd;
2869 bh |= filp->Hv;
2870 filp->Bd = filp->Hv = false;
2871 } else
2872 return NULL;
2873
2874 if (bb) {
2875 strcpy(pb1, ")");
2876 filp->Bd = bb;
2877 } else
2878 *pb0= 0;
2879
2880 if (havg) {
2881 if (bb && bh && vop == OP_OR) {
2882 // Cannot or'ed a where clause with a having clause
2883 bb= bh= 0;
2884 *pb0 = 0;
2885 *ph0 = 0;
2886 } else if (bh) {
2887 strcpy(ph1, ")");
2888 filp->Hv= bh;
2889 } else
2890 *ph0 = 0;
2891
2892 } // endif havg
2893
2894 if (!bb && !bh)
2895 return NULL;
2896
2897 } else if (cond->type() == COND::FUNC_ITEM) {
2898 unsigned int i;
2899 bool iscol, ishav= false, neg= false;
2900 Item_func *condf= (Item_func *)cond;
2901 Item* *args= condf->arguments();
2902
2903 filp->Bd = filp->Hv = false;
2904
2905 if (trace(1))
2906 htrc("Func type=%d argnum=%d\n", condf->functype(),
2907 condf->argument_count());
2908
2909 switch (condf->functype()) {
2910 case Item_func::EQUAL_FUNC:
2911 case Item_func::EQ_FUNC: vop= OP_EQ; break;
2912 case Item_func::NE_FUNC: vop= OP_NE; break;
2913 case Item_func::LT_FUNC: vop= OP_LT; break;
2914 case Item_func::LE_FUNC: vop= OP_LE; break;
2915 case Item_func::GE_FUNC: vop= OP_GE; break;
2916 case Item_func::GT_FUNC: vop= OP_GT; break;
2917 case Item_func::LIKE_FUNC:
2918 vop= OP_LIKE;
2919 neg = ((Item_func_opt_neg *)condf)->negated;
2920 break;
2921 case Item_func::ISNOTNULL_FUNC:
2922 neg = true;
2923 // fall through
2924 case Item_func::ISNULL_FUNC: vop= OP_NULL; break;
2925 case Item_func::IN_FUNC: vop= OP_IN; /* fall through */
2926 case Item_func::BETWEEN:
2927 ismul= true;
2928 neg= ((Item_func_opt_neg *)condf)->negated;
2929 break;
2930 default: return NULL;
2931 } // endswitch functype
2932
2933 if (condf->argument_count() < 2)
2934 return NULL;
2935 else if (ismul && tty == TYPE_AM_WMI)
2936 return NULL; // Not supported by WQL
2937
2938 if (x && (neg || !(vop == OP_EQ || vop == OP_IN || vop == OP_NULL)))
2939 return NULL;
2940
2941 for (i= 0; i < condf->argument_count(); i++) {
2942 if (trace(1))
2943 htrc("Argtype(%d)=%d\n", i, args[i]->type());
2944
2945 if (i >= 2 && !ismul) {
2946 if (trace(1))
2947 htrc("Unexpected arg for vop=%d\n", vop);
2948
2949 continue;
2950 } // endif i
2951
2952 if ((iscol= args[i]->type() == COND::FIELD_ITEM)) {
2953 const char *fnm;
2954 ha_field_option_struct *fop;
2955 Item_field *pField= (Item_field *)args[i];
2956
2957 // IN and BETWEEN clauses should be col VOP list
2958 if (i && (x || ismul))
2959 return NULL; // IN and BETWEEN clauses should be col VOP list
2960 else if (pField->field->table != table)
2961 return NULL; // Field does not belong to this table
2962 else if (tty != TYPE_AM_WMI && IsIndexed(pField->field))
2963 return NULL; // Will be handled by ReadKey
2964 else
2965 fop= GetFieldOptionStruct(pField->field);
2966
2967 if (fop && fop->special) {
2968 if (tty == TYPE_AM_TBL && !stricmp(fop->special, "TABID"))
2969 fnm= "TABID";
2970 else if (tty == TYPE_AM_PLG)
2971 fnm= fop->special;
2972 else
2973 return NULL;
2974
2975 } else if (tty == TYPE_AM_TBL) {
2976 return NULL;
2977 } else {
2978 bool h;
2979
2980 fnm = filp->Chk(pField->field->field_name.str, &h);
2981
2982 if (h && i && !ishav)
2983 return NULL; // Having should be col VOP arg
2984 else
2985 ishav = h;
2986
2987 } // endif's
2988
2989 if (trace(1)) {
2990 htrc("Field index=%d\n", pField->field->field_index);
2991 htrc("Field name=%s\n", pField->field->field_name.str);
2992 htrc("Field type=%d\n", pField->field->type());
2993 htrc("Field_type=%d\n", args[i]->field_type());
2994 } // endif trace
2995
2996 strcat((ishav ? havg : body), fnm);
2997 } else if (args[i]->type() == COND::FUNC_ITEM) {
2998 if (tty == TYPE_AM_MYSQL) {
2999 if (!CheckCond(g, filp, args[i]))
3000 return NULL;
3001
3002 } else
3003 return NULL;
3004
3005 } else {
3006 char buff[256];
3007 String *res, tmp(buff, sizeof(buff), &my_charset_bin);
3008 Item_basic_constant *pval= (Item_basic_constant *)args[i];
3009 Item::Type type= args[i]->real_type();
3010
3011 switch (type) {
3012 case COND::STRING_ITEM:
3013 case COND::INT_ITEM:
3014 case COND::REAL_ITEM:
3015 case COND::NULL_ITEM:
3016 case COND::DECIMAL_ITEM:
3017 case COND::DATE_ITEM:
3018 case COND::CACHE_ITEM:
3019 break;
3020 default:
3021 return NULL;
3022 } // endswitch type
3023
3024 if ((res= pval->val_str(&tmp)) == NULL)
3025 return NULL; // To be clarified
3026
3027 if (trace(1))
3028 htrc("Value=%.*s\n", res->length(), res->ptr());
3029
3030 // IN and BETWEEN clauses should be col VOP list
3031 if (!i && (x || ismul))
3032 return NULL;
3033
3034 if (!x) {
3035 const char *p;
3036 char *s = (ishav) ? havg : body;
3037 uint j, k, n;
3038
3039 // Append the value to the filter
3040 switch (args[i]->field_type()) {
3041 case MYSQL_TYPE_TIMESTAMP:
3042 case MYSQL_TYPE_DATETIME:
3043 if (tty == TYPE_AM_ODBC) {
3044 strcat(s, "{ts '");
3045 strncat(s, res->ptr(), res->length());
3046
3047 if (res->length() < 19)
3048 strcat(s, "1970-01-01 00:00:00" + res->length());
3049
3050 strcat(s, "'}");
3051 break;
3052 } // endif ODBC
3053
3054 // fall through
3055 case MYSQL_TYPE_DATE:
3056 if (tty == TYPE_AM_ODBC) {
3057 strcat(s, "{d '");
3058 strcat(strncat(s, res->ptr(), res->length()), "'}");
3059 break;
3060 } // endif ODBC
3061
3062 case MYSQL_TYPE_TIME:
3063 if (tty == TYPE_AM_ODBC) {
3064 strcat(s, "{t '");
3065 strcat(strncat(s, res->ptr(), res->length()), "'}");
3066 break;
3067 } // endif ODBC
3068
3069 case MYSQL_TYPE_VARCHAR:
3070 if (tty == TYPE_AM_ODBC && i) {
3071 switch (args[0]->field_type()) {
3072 case MYSQL_TYPE_TIMESTAMP:
3073 case MYSQL_TYPE_DATETIME:
3074 strcat(s, "{ts '");
3075 strncat(s, res->ptr(), res->length());
3076
3077 if (res->length() < 19)
3078 strcat(s, "1970-01-01 00:00:00" + res->length());
3079
3080 strcat(s, "'}");
3081 break;
3082 case MYSQL_TYPE_DATE:
3083 strcat(s, "{d '");
3084 strncat(s, res->ptr(), res->length());
3085 strcat(s, "'}");
3086 break;
3087 case MYSQL_TYPE_TIME:
3088 strcat(s, "{t '");
3089 strncat(s, res->ptr(), res->length());
3090 strcat(s, "'}");
3091 break;
3092 default:
3093 j = strlen(s);
3094 s[j++] = '\'';
3095 p = res->ptr();
3096 n = res->length();
3097
3098 for (k = 0; k < n; k++) {
3099 if (p[k] == '\'')
3100 s[j++] = '\'';
3101
3102 s[j++] = p[k];
3103 } // endfor k
3104
3105 s[j++] = '\'';
3106 s[j] = 0;
3107 } // endswitch field type
3108
3109 } else {
3110 j = strlen(s);
3111 s[j++] = '\'';
3112 p = res->ptr();
3113 n = res->length();
3114
3115 for (k = 0; k < n; k++) {
3116 if (p[k] == '\'')
3117 s[j++] = '\'';
3118
3119 s[j++] = p[k];
3120 } // endfor k
3121
3122 s[j++] = '\'';
3123 s[j] = 0;
3124 } // endif tty
3125
3126 break;
3127 default:
3128 strncat(s, res->ptr(), res->length());
3129 } // endswitch field type
3130
3131 } else {
3132 if (args[i]->field_type() == MYSQL_TYPE_VARCHAR) {
3133 // Add the command to the list
3134 PCMD *ncp, cmdp= new(g) CMD(g, (char*)res->c_ptr());
3135
3136 for (ncp= &filp->Cmds; *ncp; ncp= &(*ncp)->Next) ;
3137
3138 *ncp= cmdp;
3139 } else
3140 return NULL;
3141
3142 } // endif x
3143
3144 } // endif's Type
3145
3146 if (!x) {
3147 char *s = (ishav) ? havg : body;
3148
3149 if (!i)
3150 strcat(s, GetValStr(vop, neg));
3151 else if (vop == OP_XX && i == 1)
3152 strcat(s, " AND ");
3153 else if (vop == OP_IN)
3154 strcat(s, (i == condf->argument_count() - 1) ? ")" : ",");
3155
3156 } // endif x
3157
3158 } // endfor i
3159
3160 if (x)
3161 filp->Op = vop;
3162 else if (ishav)
3163 filp->Hv = true;
3164 else
3165 filp->Bd = true;
3166
3167 } else {
3168 if (trace(1))
3169 htrc("Unsupported condition\n");
3170
3171 return NULL;
3172 } // endif's type
3173
3174 return filp;
3175} // end of CheckCond
3176
3177
3178 /**
3179 Push condition down to the table handler.
3180
3181 @param cond Condition to be pushed. The condition tree must not be
3182 modified by the caller.
3183
3184 @return
3185 The 'remainder' condition that caller must use to filter out records.
3186 NULL means the handler will not return rows that do not match the
3187 passed condition.
3188
3189 @note
3190 CONNECT handles the filtering only for table types that construct
3191 an SQL or WQL query, but still leaves it to MySQL because only some
3192 parts of the filter may be relevant.
3193 The first suballocate finds the position where the string will be
3194 constructed in the sarea. The second one does make the suballocation
3195 with the proper length.
3196 */
3197const COND *ha_connect::cond_push(const COND *cond)
3198{
3199 DBUG_ENTER("ha_connect::cond_push");
3200
3201 if (tdbp && CondPushEnabled()) {
3202 PGLOBAL& g= xp->g;
3203 AMT tty= tdbp->GetAmType();
3204 bool x= (tty == TYPE_AM_MYX || tty == TYPE_AM_XDBC);
3205 bool b= (tty == TYPE_AM_WMI || tty == TYPE_AM_ODBC ||
3206 tty == TYPE_AM_TBL || tty == TYPE_AM_MYSQL ||
3207 tty == TYPE_AM_PLG || tty == TYPE_AM_JDBC || x);
3208
3209 // This should never happen but is done to avoid crashing
3210 try {
3211 if (b) {
3212 PCFIL filp;
3213 int rc;
3214
3215 if ((filp = tdbp->GetCondFil()) && tdbp->GetCond() == cond &&
3216 filp->Idx == active_index && filp->Type == tty)
3217 goto fin;
3218
3219 filp = new(g) CONDFIL(active_index, tty);
3220 rc = filp->Init(g, this);
3221
3222 if (rc == RC_INFO) {
3223 filp->Having = (char*)PlugSubAlloc(g, NULL, 256);
3224 *filp->Having = 0;
3225 } else if (rc == RC_FX)
3226 goto fin;
3227
3228 filp->Body = (char*)PlugSubAlloc(g, NULL, (x) ? 128 : 0);
3229 *filp->Body = 0;
3230
3231 if (CheckCond(g, filp, cond)) {
3232 if (filp->Having && strlen(filp->Having) > 255)
3233 goto fin; // Memory collapse
3234
3235 if (trace(1))
3236 htrc("cond_push: %s\n", filp->Body);
3237
3238 tdbp->SetCond(cond);
3239
3240 if (!x)
3241 PlugSubAlloc(g, NULL, strlen(filp->Body) + 1);
3242 else
3243 cond = NULL; // Does this work?
3244
3245 tdbp->SetCondFil(filp);
3246 } else if (x && cond)
3247 tdbp->SetCondFil(filp); // Wrong filter
3248
3249 } else if (tdbp->CanBeFiltered()) {
3250 if (!tdbp->GetCond() || tdbp->GetCond() != cond) {
3251 tdbp->SetFilter(CondFilter(g, (Item *)cond));
3252
3253 if (tdbp->GetFilter())
3254 tdbp->SetCond(cond);
3255
3256 } // endif cond
3257
3258 } // endif tty
3259
3260 } catch (int n) {
3261 if (trace(1))
3262 htrc("Exception %d: %s\n", n, g->Message);
3263 } catch (const char *msg) {
3264 strcpy(g->Message, msg);
3265 } // end catch
3266
3267 fin:;
3268 } // endif tdbp
3269
3270 // Let MySQL do the filtering
3271 DBUG_RETURN(cond);
3272} // end of cond_push
3273
3274/**
3275 Number of rows in table. It will only be called if
3276 (table_flags() & (HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT)) != 0
3277*/
3278ha_rows ha_connect::records()
3279{
3280 if (!valid_info)
3281 info(HA_STATUS_VARIABLE);
3282
3283 if (tdbp)
3284 return stats.records;
3285 else
3286 return HA_POS_ERROR;
3287
3288} // end of records
3289
3290
3291/**
3292 Return an error message specific to this handler.
3293
3294 @param error error code previously returned by handler
3295 @param buf pointer to String where to add error message
3296
3297 @return
3298 Returns true if this is a temporary error
3299*/
3300bool ha_connect::get_error_message(int error, String* buf)
3301{
3302 DBUG_ENTER("ha_connect::get_error_message");
3303
3304 if (xp && xp->g) {
3305 PGLOBAL g= xp->g;
3306 char msg[3072]; // MAX_STR * 3
3307 uint dummy_errors;
3308 uint32 len= copy_and_convert(msg, strlen(g->Message) * 3,
3309 system_charset_info,
3310 g->Message, strlen(g->Message),
3311 &my_charset_latin1,
3312 &dummy_errors);
3313
3314 if (trace(1))
3315 htrc("GEM(%d): len=%u %s\n", error, len, g->Message);
3316
3317 msg[len]= '\0';
3318 buf->copy(msg, (uint)strlen(msg), system_charset_info);
3319 } else
3320 buf->copy("Cannot retrieve msg", 19, system_charset_info);
3321
3322 DBUG_RETURN(false);
3323} // end of get_error_message
3324
3325/**
3326 Convert a filename partition name to system
3327*/
3328static char *decode(PGLOBAL g, const char *pn)
3329 {
3330 char *buf= (char*)PlugSubAlloc(g, NULL, strlen(pn) + 1);
3331 uint dummy_errors;
3332 uint32 len= copy_and_convert(buf, strlen(pn) + 1,
3333 system_charset_info,
3334 pn, strlen(pn),
3335 &my_charset_filename,
3336 &dummy_errors);
3337 buf[len]= '\0';
3338 return buf;
3339 } // end of decode
3340
3341/**
3342 @brief
3343 Used for opening tables. The name will be the name of the file.
3344
3345 @details
3346 A table is opened when it needs to be opened; e.g. when a request comes in
3347 for a SELECT on the table (tables are not open and closed for each request,
3348 they are cached).
3349
3350 Called from handler.cc by handler::ha_open(). The server opens all tables by
3351 calling ha_open() which then calls the handler specific open().
3352
3353 @note
3354 For CONNECT no open can be done here because field information is not yet
3355 updated. >>>>> TO BE CHECKED <<<<<
3356 (Thread information could be get by using 'ha_thd')
3357
3358 @see
3359 handler::ha_open() in handler.cc
3360*/
3361int ha_connect::open(const char *name, int mode, uint test_if_locked)
3362{
3363 int rc= 0;
3364 DBUG_ENTER("ha_connect::open");
3365
3366 if (trace(1))
3367 htrc("open: name=%s mode=%d test=%u\n", name, mode, test_if_locked);
3368
3369 if (!(share= get_share()))
3370 DBUG_RETURN(1);
3371
3372 thr_lock_data_init(&share->lock,&lock,NULL);
3373
3374 // Try to get the user if possible
3375 xp= GetUser(ha_thd(), xp);
3376 PGLOBAL g= (xp) ? xp->g : NULL;
3377
3378 // Try to set the database environment
3379 if (g) {
3380 rc= (CntCheckDB(g, this, name)) ? (-2) : 0;
3381
3382 if (g->Mrr) {
3383 // This should only happen for the mrr secondary handler
3384 mrr= true;
3385 g->Mrr= false;
3386 } else
3387 mrr= false;
3388
3389#if defined(WITH_PARTITION_STORAGE_ENGINE)
3390 if (table->part_info) {
3391 if (GetStringOption("Filename") || GetStringOption("Tabname")
3392 || GetStringOption("Connect")) {
3393 strncpy(partname, decode(g, strrchr(name, '#') + 1), sizeof(partname) - 1);
3394// strcpy(partname, table->part_info->curr_part_elem->partition_name);
3395// part_id= &table->part_info->full_part_field_set;
3396 } else // Inward table
3397 strncpy(partname, strrchr(name, slash) + 1, sizeof(partname) - 1);
3398
3399 part_id= &table->part_info->full_part_field_set; // Temporary
3400 } // endif part_info
3401#endif // WITH_PARTITION_STORAGE_ENGINE
3402 } else
3403 rc= HA_ERR_INTERNAL_ERROR;
3404
3405 DBUG_RETURN(rc);
3406} // end of open
3407
3408/**
3409 @brief
3410 Make the indexes for this table
3411*/
3412int ha_connect::optimize(THD* thd, HA_CHECK_OPT*)
3413{
3414 int rc= 0;
3415 PGLOBAL& g= xp->g;
3416 PDBUSER dup= PlgGetUser(g);
3417
3418 try {
3419 // Ignore error on the opt file
3420 dup->Check &= ~CHK_OPT;
3421 tdbp = GetTDB(g);
3422 dup->Check |= CHK_OPT;
3423
3424 if (tdbp && !tdbp->IsRemote()) {
3425 bool dop = IsTypeIndexable(GetRealType(NULL));
3426 bool dox = (tdbp->GetDef()->Indexable() == 1);
3427
3428 if ((rc = ((PTDBASE)tdbp)->ResetTableOpt(g, dop, dox))) {
3429 if (rc == RC_INFO) {
3430 push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
3431 rc = 0;
3432 } else
3433 rc = HA_ERR_INTERNAL_ERROR;
3434
3435 } // endif rc
3436
3437 } else if (!tdbp)
3438 rc = HA_ERR_INTERNAL_ERROR;
3439
3440 } catch (int n) {
3441 if (trace(1))
3442 htrc("Exception %d: %s\n", n, g->Message);
3443 rc = HA_ERR_INTERNAL_ERROR;
3444 } catch (const char *msg) {
3445 strcpy(g->Message, msg);
3446 rc = HA_ERR_INTERNAL_ERROR;
3447 } // end catch
3448
3449 return rc;
3450} // end of optimize
3451
3452/**
3453 @brief
3454 Closes a table.
3455
3456 @details
3457 Called from sql_base.cc, sql_select.cc, and table.cc. In sql_select.cc it is
3458 only used to close up temporary tables or during the process where a
3459 temporary table is converted over to being a myisam table.
3460
3461 For sql_base.cc look at close_data_tables().
3462
3463 @see
3464 sql_base.cc, sql_select.cc and table.cc
3465*/
3466int ha_connect::close(void)
3467{
3468 int rc= 0;
3469 DBUG_ENTER("ha_connect::close");
3470
3471 // If this is called by a later query, the table may have
3472 // been already closed and the tdbp is not valid anymore.
3473 if (tdbp && xp->last_query_id == valid_query_id)
3474 rc= CloseTable(xp->g);
3475
3476 DBUG_RETURN(rc);
3477} // end of close
3478
3479
3480/**
3481 @brief
3482 write_row() inserts a row. No extra() hint is given currently if a bulk load
3483 is happening. buf() is a byte array of data. You can use the field
3484 information to extract the data from the native byte array type.
3485
3486 @details
3487 Example of this would be:
3488 @code
3489 for (Field **field=table->field ; *field ; field++)
3490 {
3491 ...
3492 }
3493 @endcode
3494
3495 See ha_tina.cc for an example of extracting all of the data as strings.
3496 ha_berekly.cc has an example of how to store it intact by "packing" it
3497 for ha_berkeley's own native storage type.
3498
3499 See the note for update_row() on auto_increments and timestamps. This
3500 case also applies to write_row().
3501
3502 Called from item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc,
3503 sql_insert.cc, sql_select.cc, sql_table.cc, sql_udf.cc, and sql_update.cc.
3504
3505 @see
3506 item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc,
3507 sql_insert.cc, sql_select.cc, sql_table.cc, sql_udf.cc and sql_update.cc
3508*/
3509int ha_connect::write_row(uchar *buf)
3510{
3511 int rc= 0;
3512 PGLOBAL& g= xp->g;
3513 DBUG_ENTER("ha_connect::write_row");
3514
3515 // This is not tested yet
3516 if (xmod == MODE_ALTER) {
3517 if (IsPartitioned() && GetStringOption("Filename", NULL))
3518 // Why does this happen now that check_if_supported_inplace_alter is called?
3519 DBUG_RETURN(0); // Alter table on an outward partition table
3520
3521 xmod= MODE_INSERT;
3522 } else if (xmod == MODE_ANY)
3523 DBUG_RETURN(0); // Probably never met
3524
3525 // Open the table if it was not opened yet (locked)
3526 if (!IsOpened() || xmod != tdbp->GetMode()) {
3527 if (IsOpened())
3528 CloseTable(g);
3529
3530 if ((rc= OpenTable(g)))
3531 DBUG_RETURN(rc);
3532
3533 } // endif isopened
3534
3535#if 0 // AUTO_INCREMENT NIY
3536 if (table->next_number_field && buf == table->record[0]) {
3537 int error;
3538
3539 if ((error= update_auto_increment()))
3540 return error;
3541
3542 } // endif nex_number_field
3543#endif // 0
3544
3545 // Set column values from the passed pseudo record
3546 if ((rc= ScanRecord(g, buf)))
3547 DBUG_RETURN(rc);
3548
3549 // Return result code from write operation
3550 if (CntWriteRow(g, tdbp)) {
3551 DBUG_PRINT("write_row", ("%s", g->Message));
3552 htrc("write_row: %s\n", g->Message);
3553 rc= HA_ERR_INTERNAL_ERROR;
3554 } else // Table is modified
3555 nox= false; // Indexes to be remade
3556
3557 DBUG_RETURN(rc);
3558} // end of write_row
3559
3560
3561/**
3562 @brief
3563 Yes, update_row() does what you expect, it updates a row. old_data will have
3564 the previous row record in it, while new_data will have the newest data in it.
3565 Keep in mind that the server can do updates based on ordering if an ORDER BY
3566 clause was used. Consecutive ordering is not guaranteed.
3567
3568 @details
3569 Currently new_data will not have an updated auto_increament record, or
3570 and updated timestamp field. You can do these for example by doing:
3571 @code
3572 if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
3573 table->timestamp_field->set_time();
3574 if (table->next_number_field && record == table->record[0])
3575 update_auto_increment();
3576 @endcode
3577
3578 Called from sql_select.cc, sql_acl.cc, sql_update.cc, and sql_insert.cc.
3579
3580 @see
3581 sql_select.cc, sql_acl.cc, sql_update.cc and sql_insert.cc
3582*/
3583int ha_connect::update_row(const uchar *old_data, const uchar *new_data)
3584{
3585 int rc= 0;
3586 PGLOBAL& g= xp->g;
3587 DBUG_ENTER("ha_connect::update_row");
3588
3589 if (trace(2))
3590 htrc("update_row: old=%s new=%s\n", old_data, new_data);
3591
3592 // Check values for possible change in indexed column
3593 if ((rc= CheckRecord(g, old_data, new_data)))
3594 DBUG_RETURN(rc);
3595
3596 if (CntUpdateRow(g, tdbp)) {
3597 DBUG_PRINT("update_row", ("%s", g->Message));
3598 htrc("update_row CONNECT: %s\n", g->Message);
3599 rc= HA_ERR_INTERNAL_ERROR;
3600 } else
3601 nox= false; // Table is modified
3602
3603 DBUG_RETURN(rc);
3604} // end of update_row
3605
3606
3607/**
3608 @brief
3609 This will delete a row. buf will contain a copy of the row to be deleted.
3610 The server will call this right after the current row has been called (from
3611 either a previous rnd_nexT() or index call).
3612
3613 @details
3614 If you keep a pointer to the last row or can access a primary key it will
3615 make doing the deletion quite a bit easier. Keep in mind that the server does
3616 not guarantee consecutive deletions. ORDER BY clauses can be used.
3617
3618 Called in sql_acl.cc and sql_udf.cc to manage internal table
3619 information. Called in sql_delete.cc, sql_insert.cc, and
3620 sql_select.cc. In sql_select it is used for removing duplicates
3621 while in insert it is used for REPLACE calls.
3622
3623 @see
3624 sql_acl.cc, sql_udf.cc, sql_delete.cc, sql_insert.cc and sql_select.cc
3625*/
3626int ha_connect::delete_row(const uchar *)
3627{
3628 int rc= 0;
3629 DBUG_ENTER("ha_connect::delete_row");
3630
3631 if (CntDeleteRow(xp->g, tdbp, false)) {
3632 rc= HA_ERR_INTERNAL_ERROR;
3633 htrc("delete_row CONNECT: %s\n", xp->g->Message);
3634 } else
3635 nox= false; // To remake indexes
3636
3637 DBUG_RETURN(rc);
3638} // end of delete_row
3639
3640
3641/****************************************************************************/
3642/* We seem to come here at the begining of an index use. */
3643/****************************************************************************/
3644int ha_connect::index_init(uint idx, bool sorted)
3645{
3646 int rc;
3647 PGLOBAL& g= xp->g;
3648 DBUG_ENTER("index_init");
3649
3650 if (trace(1))
3651 htrc("index_init: this=%p idx=%u sorted=%d\n", this, idx, sorted);
3652
3653 if (GetIndexType(GetRealType()) == 2) {
3654 if (xmod == MODE_READ)
3655 // This is a remote index
3656 xmod= MODE_READX;
3657
3658 if (!(rc= rnd_init(0))) {
3659// if (xmod == MODE_READX) {
3660 active_index= idx;
3661 indexing= IsUnique(idx) ? 1 : 2;
3662// } else {
3663// active_index= MAX_KEY;
3664// indexing= 0;
3665// } // endif xmod
3666
3667 } //endif rc
3668
3669 DBUG_RETURN(rc);
3670 } // endif index type
3671
3672 if ((rc= rnd_init(0)))
3673 DBUG_RETURN(rc);
3674
3675 if (locked == 2) {
3676 // Indexes are not updated in lock write mode
3677 active_index= MAX_KEY;
3678 indexing= 0;
3679 DBUG_RETURN(0);
3680 } // endif locked
3681
3682 indexing= CntIndexInit(g, tdbp, (signed)idx, sorted);
3683
3684 if (indexing <= 0) {
3685 DBUG_PRINT("index_init", ("%s", g->Message));
3686 htrc("index_init CONNECT: %s\n", g->Message);
3687 active_index= MAX_KEY;
3688 rc= HA_ERR_INTERNAL_ERROR;
3689 } else if (tdbp->GetKindex()) {
3690 if (((PTDBDOX)tdbp)->To_Kindex->GetNum_K()) {
3691 if (tdbp->GetFtype() != RECFM_NAF)
3692 ((PTDBDOX)tdbp)->GetTxfp()->ResetBuffer(g);
3693
3694 active_index= idx;
3695// } else { // Void table
3696// active_index= MAX_KEY;
3697// indexing= 0;
3698 } // endif Num
3699
3700 rc= 0;
3701 } // endif indexing
3702
3703 if (trace(1))
3704 htrc("index_init: rc=%d indexing=%d active_index=%d\n",
3705 rc, indexing, active_index);
3706
3707 DBUG_RETURN(rc);
3708} // end of index_init
3709
3710/****************************************************************************/
3711/* We seem to come here at the end of an index use. */
3712/****************************************************************************/
3713int ha_connect::index_end()
3714{
3715 DBUG_ENTER("index_end");
3716 active_index= MAX_KEY;
3717 ds_mrr.dsmrr_close();
3718 DBUG_RETURN(rnd_end());
3719} // end of index_end
3720
3721
3722/****************************************************************************/
3723/* This is internally called by all indexed reading functions. */
3724/****************************************************************************/
3725int ha_connect::ReadIndexed(uchar *buf, OPVAL op, const key_range *kr)
3726{
3727 int rc;
3728
3729//statistic_increment(ha_read_key_count, &LOCK_status);
3730
3731 switch (CntIndexRead(xp->g, tdbp, op, kr, mrr)) {
3732 case RC_OK:
3733 xp->fnd++;
3734 rc= MakeRecord((char*)buf);
3735 break;
3736 case RC_EF: // End of file
3737 rc= HA_ERR_END_OF_FILE;
3738 break;
3739 case RC_NF: // Not found
3740 xp->nfd++;
3741 rc= (op == OP_SAME) ? HA_ERR_END_OF_FILE : HA_ERR_KEY_NOT_FOUND;
3742 break;
3743 default: // Read error
3744 DBUG_PRINT("ReadIndexed", ("%s", xp->g->Message));
3745 htrc("ReadIndexed: %s\n", xp->g->Message);
3746 rc= HA_ERR_INTERNAL_ERROR;
3747 break;
3748 } // endswitch RC
3749
3750 if (trace(2))
3751 htrc("ReadIndexed: op=%d rc=%d\n", op, rc);
3752
3753 table->status= (rc == RC_OK) ? 0 : STATUS_NOT_FOUND;
3754 return rc;
3755} // end of ReadIndexed
3756
3757
3758#ifdef NOT_USED
3759/**
3760 @brief
3761 Positions an index cursor to the index specified in the handle. Fetches the
3762 row if available. If the key value is null, begin at the first key of the
3763 index.
3764*/
3765int ha_connect::index_read_map(uchar *buf, const uchar *key,
3766 key_part_map keypart_map __attribute__((unused)),
3767 enum ha_rkey_function find_flag
3768 __attribute__((unused)))
3769{
3770 DBUG_ENTER("ha_connect::index_read");
3771 DBUG_RETURN(HA_ERR_WRONG_COMMAND);
3772}
3773#endif // NOT_USED
3774
3775
3776/****************************************************************************/
3777/* This is called by handler::index_read_map. */
3778/****************************************************************************/
3779int ha_connect::index_read(uchar * buf, const uchar * key, uint key_len,
3780 enum ha_rkey_function find_flag)
3781{
3782 int rc;
3783 OPVAL op= OP_XX;
3784 DBUG_ENTER("ha_connect::index_read");
3785
3786 switch(find_flag) {
3787 case HA_READ_KEY_EXACT: op= OP_EQ; break;
3788 case HA_READ_AFTER_KEY: op= OP_GT; break;
3789 case HA_READ_KEY_OR_NEXT: op= OP_GE; break;
3790 default: DBUG_RETURN(-1); break;
3791 } // endswitch find_flag
3792
3793 if (trace(2))
3794 htrc("%p index_read: op=%d\n", this, op);
3795
3796 if (indexing > 0) {
3797 start_key.key= key;
3798 start_key.length= key_len;
3799 start_key.flag= find_flag;
3800 start_key.keypart_map= 0;
3801
3802 rc= ReadIndexed(buf, op, &start_key);
3803
3804 if (rc == HA_ERR_INTERNAL_ERROR) {
3805 nox= true; // To block making indexes
3806 abort= true; // Don't rename temp file
3807 } // endif rc
3808
3809 } else
3810 rc= HA_ERR_INTERNAL_ERROR; // HA_ERR_KEY_NOT_FOUND ?
3811
3812 DBUG_RETURN(rc);
3813} // end of index_read
3814
3815
3816/**
3817 @brief
3818 Used to read forward through the index.
3819*/
3820int ha_connect::index_next(uchar *buf)
3821{
3822 int rc;
3823 DBUG_ENTER("ha_connect::index_next");
3824 //statistic_increment(ha_read_next_count, &LOCK_status);
3825
3826 if (indexing > 0)
3827 rc= ReadIndexed(buf, OP_NEXT);
3828 else if (!indexing)
3829 rc= rnd_next(buf);
3830 else
3831 rc= HA_ERR_INTERNAL_ERROR;
3832
3833 DBUG_RETURN(rc);
3834} // end of index_next
3835
3836
3837/**
3838 @brief
3839 Used to read backwards through the index.
3840*/
3841int ha_connect::index_prev(uchar *buf)
3842{
3843 DBUG_ENTER("ha_connect::index_prev");
3844 int rc;
3845
3846 if (indexing > 0) {
3847 rc= ReadIndexed(buf, OP_PREV);
3848 } else
3849 rc= HA_ERR_WRONG_COMMAND;
3850
3851 DBUG_RETURN(rc);
3852} // end of index_prev
3853
3854
3855/**
3856 @brief
3857 index_first() asks for the first key in the index.
3858
3859 @details
3860 Called from opt_range.cc, opt_sum.cc, sql_handler.cc, and sql_select.cc.
3861
3862 @see
3863 opt_range.cc, opt_sum.cc, sql_handler.cc and sql_select.cc
3864*/
3865int ha_connect::index_first(uchar *buf)
3866{
3867 int rc;
3868 DBUG_ENTER("ha_connect::index_first");
3869
3870 if (indexing > 0)
3871 rc= ReadIndexed(buf, OP_FIRST);
3872 else if (indexing < 0)
3873 rc= HA_ERR_INTERNAL_ERROR;
3874 else if (CntRewindTable(xp->g, tdbp)) {
3875 table->status= STATUS_NOT_FOUND;
3876 rc= HA_ERR_INTERNAL_ERROR;
3877 } else
3878 rc= rnd_next(buf);
3879
3880 DBUG_RETURN(rc);
3881} // end of index_first
3882
3883
3884/**
3885 @brief
3886 index_last() asks for the last key in the index.
3887
3888 @details
3889 Called from opt_range.cc, opt_sum.cc, sql_handler.cc, and sql_select.cc.
3890
3891 @see
3892 opt_range.cc, opt_sum.cc, sql_handler.cc and sql_select.cc
3893*/
3894int ha_connect::index_last(uchar *buf)
3895{
3896 DBUG_ENTER("ha_connect::index_last");
3897 int rc;
3898
3899 if (indexing <= 0) {
3900 rc= HA_ERR_INTERNAL_ERROR;
3901 } else
3902 rc= ReadIndexed(buf, OP_LAST);
3903
3904 DBUG_RETURN(rc);
3905}
3906
3907
3908/****************************************************************************/
3909/* This is called to get more rows having the same index value. */
3910/****************************************************************************/
3911//t ha_connect::index_next_same(uchar *buf, const uchar *key, uint keylen)
3912int ha_connect::index_next_same(uchar *buf, const uchar *, uint)
3913{
3914 int rc;
3915 DBUG_ENTER("ha_connect::index_next_same");
3916//statistic_increment(ha_read_next_count, &LOCK_status);
3917
3918 if (!indexing)
3919 rc= rnd_next(buf);
3920 else if (indexing > 0)
3921 rc= ReadIndexed(buf, OP_SAME);
3922 else
3923 rc= HA_ERR_INTERNAL_ERROR;
3924
3925 DBUG_RETURN(rc);
3926} // end of index_next_same
3927
3928
3929/**
3930 @brief
3931 rnd_init() is called when the system wants the storage engine to do a table
3932 scan. See the example in the introduction at the top of this file to see when
3933 rnd_init() is called.
3934
3935 @details
3936 Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc,
3937 and sql_update.cc.
3938
3939 @note
3940 We always call open and extern_lock/start_stmt before comming here.
3941
3942 @see
3943 filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc and sql_update.cc
3944*/
3945int ha_connect::rnd_init(bool scan)
3946{
3947 PGLOBAL g= ((table && table->in_use) ? GetPlug(table->in_use, xp) :
3948 (xp) ? xp->g : NULL);
3949 DBUG_ENTER("ha_connect::rnd_init");
3950
3951 // This is not tested yet
3952 if (xmod == MODE_ALTER) {
3953 xmod= MODE_READ;
3954 alter= 1;
3955 } // endif xmod
3956
3957 if (trace(1))
3958 htrc("rnd_init: this=%p scan=%d xmod=%d alter=%d\n",
3959 this, scan, xmod, alter);
3960
3961 if (!g || !table || xmod == MODE_INSERT)
3962 DBUG_RETURN(HA_ERR_INITIALIZATION);
3963
3964 // Do not close the table if it was opened yet (locked?)
3965 if (IsOpened()) {
3966 if (IsPartitioned() && xmod != MODE_INSERT)
3967 if (CheckColumnList(g)) // map can have been changed
3968 DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
3969
3970 if (tdbp->OpenDB(g)) // Rewind table
3971 DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
3972 else
3973 DBUG_RETURN(0);
3974
3975 } else if (xp->CheckQuery(valid_query_id))
3976 tdbp= NULL; // Not valid anymore
3977
3978 // When updating, to avoid skipped update, force the table
3979 // handler to retrieve write-only fields to be able to compare
3980 // records and detect data change.
3981 if (xmod == MODE_UPDATE)
3982 bitmap_union(table->read_set, table->write_set);
3983
3984 if (OpenTable(g, xmod == MODE_DELETE))
3985 DBUG_RETURN(HA_ERR_INITIALIZATION);
3986
3987 xp->nrd= xp->fnd= xp->nfd= 0;
3988 xp->tb1= my_interval_timer();
3989 DBUG_RETURN(0);
3990} // end of rnd_init
3991
3992/**
3993 @brief
3994 Not described.
3995
3996 @note
3997 The previous version said:
3998 Stop scanning of table. Note that this may be called several times during
3999 execution of a sub select.
4000 =====> This has been moved to external lock to avoid closing subselect tables.
4001*/
4002int ha_connect::rnd_end()
4003{
4004 int rc= 0;
4005 DBUG_ENTER("ha_connect::rnd_end");
4006
4007 // If this is called by a later query, the table may have
4008 // been already closed and the tdbp is not valid anymore.
4009// if (tdbp && xp->last_query_id == valid_query_id)
4010// rc= CloseTable(xp->g);
4011
4012 ds_mrr.dsmrr_close();
4013 DBUG_RETURN(rc);
4014} // end of rnd_end
4015
4016
4017/**
4018 @brief
4019 This is called for each row of the table scan. When you run out of records
4020 you should return HA_ERR_END_OF_FILE. Fill buff up with the row information.
4021 The Field structure for the table is the key to getting data into buf
4022 in a manner that will allow the server to understand it.
4023
4024 @details
4025 Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc,
4026 and sql_update.cc.
4027
4028 @see
4029 filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc and sql_update.cc
4030*/
4031int ha_connect::rnd_next(uchar *buf)
4032{
4033 int rc;
4034 DBUG_ENTER("ha_connect::rnd_next");
4035//statistic_increment(ha_read_rnd_next_count, &LOCK_status);
4036
4037 if (tdbp->GetMode() == MODE_ANY) {
4038 // We will stop on next read
4039 if (!stop) {
4040 stop= true;
4041 DBUG_RETURN(RC_OK);
4042 } else
4043 DBUG_RETURN(HA_ERR_END_OF_FILE);
4044
4045 } // endif Mode
4046
4047 switch (CntReadNext(xp->g, tdbp)) {
4048 case RC_OK:
4049 rc= MakeRecord((char*)buf);
4050 break;
4051 case RC_EF: // End of file
4052 rc= HA_ERR_END_OF_FILE;
4053 break;
4054 case RC_NF: // Not found
4055 rc= HA_ERR_RECORD_DELETED;
4056 break;
4057 default: // Read error
4058 htrc("rnd_next CONNECT: %s\n", xp->g->Message);
4059 rc= (records()) ? HA_ERR_INTERNAL_ERROR : HA_ERR_END_OF_FILE;
4060 break;
4061 } // endswitch RC
4062
4063 if (trace(2) && (rc || !(xp->nrd++ % 16384))) {
4064 ulonglong tb2= my_interval_timer();
4065 double elapsed= (double) (tb2 - xp->tb1) / 1000000000ULL;
4066 DBUG_PRINT("rnd_next", ("rc=%d nrd=%u fnd=%u nfd=%u sec=%.3lf\n",
4067 rc, (uint)xp->nrd, (uint)xp->fnd,
4068 (uint)xp->nfd, elapsed));
4069 htrc("rnd_next: rc=%d nrd=%u fnd=%u nfd=%u sec=%.3lf\n",
4070 rc, (uint)xp->nrd, (uint)xp->fnd,
4071 (uint)xp->nfd, elapsed);
4072 xp->tb1= tb2;
4073 xp->fnd= xp->nfd= 0;
4074 } // endif nrd
4075
4076 table->status= (!rc) ? 0 : STATUS_NOT_FOUND;
4077 DBUG_RETURN(rc);
4078} // end of rnd_next
4079
4080
4081/**
4082 @brief
4083 position() is called after each call to rnd_next() if the data needs
4084 to be ordered. You can do something like the following to store
4085 the position:
4086 @code
4087 my_store_ptr(ref, ref_length, current_position);
4088 @endcode
4089
4090 @details
4091 The server uses ref to store data. ref_length in the above case is
4092 the size needed to store current_position. ref is just a byte array
4093 that the server will maintain. If you are using offsets to mark rows, then
4094 current_position should be the offset. If it is a primary key like in
4095 BDB, then it needs to be a primary key.
4096
4097 Called from filesort.cc, sql_select.cc, sql_delete.cc, and sql_update.cc.
4098
4099 @see
4100 filesort.cc, sql_select.cc, sql_delete.cc and sql_update.cc
4101*/
4102void ha_connect::position(const uchar *)
4103{
4104 DBUG_ENTER("ha_connect::position");
4105 my_store_ptr(ref, ref_length, (my_off_t)tdbp->GetRecpos());
4106
4107 if (trace(2))
4108 htrc("position: pos=%d\n", tdbp->GetRecpos());
4109
4110 DBUG_VOID_RETURN;
4111} // end of position
4112
4113
4114/**
4115 @brief
4116 This is like rnd_next, but you are given a position to use
4117 to determine the row. The position will be of the type that you stored in
4118 ref. You can use my_get_ptr(pos,ref_length) to retrieve whatever key
4119 or position you saved when position() was called.
4120
4121 @details
4122 Called from filesort.cc, records.cc, sql_insert.cc, sql_select.cc, and sql_update.cc.
4123
4124 @note
4125 Is this really useful? It was never called even when sorting.
4126
4127 @see
4128 filesort.cc, records.cc, sql_insert.cc, sql_select.cc and sql_update.cc
4129*/
4130int ha_connect::rnd_pos(uchar *buf, uchar *pos)
4131{
4132 int rc;
4133//PTDBASE tp= (PTDBASE)tdbp;
4134 DBUG_ENTER("ha_connect::rnd_pos");
4135
4136 if (!tdbp->SetRecpos(xp->g, (int)my_get_ptr(pos, ref_length))) {
4137 if (trace(1))
4138 htrc("rnd_pos: %d\n", tdbp->GetRecpos());
4139
4140 tdbp->SetFilter(NULL);
4141 rc= rnd_next(buf);
4142 } else {
4143 PGLOBAL g = GetPlug((table) ? table->in_use : NULL, xp);
4144 strcpy(g->Message, "Not supported by this table type");
4145 my_message(ER_ILLEGAL_HA, g->Message, MYF(0));
4146 rc= HA_ERR_INTERNAL_ERROR;
4147 } // endif SetRecpos
4148
4149 DBUG_RETURN(rc);
4150} // end of rnd_pos
4151
4152
4153/**
4154 @brief
4155 ::info() is used to return information to the optimizer. See my_base.h for
4156 the complete description.
4157
4158 @details
4159 Currently this table handler doesn't implement most of the fields really needed.
4160 SHOW also makes use of this data.
4161
4162 You will probably want to have the following in your code:
4163 @code
4164 if (records < 2)
4165 records= 2;
4166 @endcode
4167 The reason is that the server will optimize for cases of only a single
4168 record. If, in a table scan, you don't know the number of records, it
4169 will probably be better to set records to two so you can return as many
4170 records as you need. Along with records, a few more variables you may wish
4171 to set are:
4172 records
4173 deleted
4174 data_file_length
4175 index_file_length
4176 delete_length
4177 check_time
4178 Take a look at the public variables in handler.h for more information.
4179
4180 Called in filesort.cc, ha_heap.cc, item_sum.cc, opt_sum.cc, sql_delete.cc,
4181 sql_delete.cc, sql_derived.cc, sql_select.cc, sql_select.cc, sql_select.cc,
4182 sql_select.cc, sql_select.cc, sql_show.cc, sql_show.cc, sql_show.cc, sql_show.cc,
4183 sql_table.cc, sql_union.cc, and sql_update.cc.
4184
4185 @see
4186 filesort.cc, ha_heap.cc, item_sum.cc, opt_sum.cc, sql_delete.cc, sql_delete.cc,
4187 sql_derived.cc, sql_select.cc, sql_select.cc, sql_select.cc, sql_select.cc,
4188 sql_select.cc, sql_show.cc, sql_show.cc, sql_show.cc, sql_show.cc, sql_table.cc,
4189 sql_union.cc and sql_update.cc
4190*/
4191int ha_connect::info(uint flag)
4192{
4193 bool pure= false;
4194 PGLOBAL g= GetPlug((table) ? table->in_use : NULL, xp);
4195
4196 DBUG_ENTER("ha_connect::info");
4197
4198 if (!g) {
4199 my_message(ER_UNKNOWN_ERROR, "Cannot get g pointer", MYF(0));
4200 DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
4201 } // endif g
4202
4203 if (trace(1))
4204 htrc("%p In info: flag=%u valid_info=%d\n", this, flag, valid_info);
4205
4206 // tdbp must be available to get updated info
4207 if (xp->CheckQuery(valid_query_id) || !tdbp) {
4208 PDBUSER dup= PlgGetUser(g);
4209 PCATLG cat= (dup) ? dup->Catalog : NULL;
4210
4211 if (xmod == MODE_ANY || xmod == MODE_ALTER) {
4212 // Pure info, not a query
4213 pure= true;
4214 xp->CheckCleanup(xmod == MODE_ANY && valid_query_id == 0);
4215 } // endif xmod
4216
4217 // This is necessary for getting file length
4218 if (table) {
4219 if (SetDataPath(g, table->s->db.str)) {
4220 my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
4221 DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
4222 } // endif SetDataPath
4223
4224 } else
4225 DBUG_RETURN(HA_ERR_INTERNAL_ERROR); // Should never happen
4226
4227 if (!(tdbp = GetTDB(g))) {
4228 my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
4229 DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
4230 } // endif tdbp
4231
4232 valid_info = false;
4233 } // endif tdbp
4234
4235 if (!valid_info) {
4236 valid_info= CntInfo(g, tdbp, &xinfo);
4237
4238 if (((signed)xinfo.records) < 0)
4239 DBUG_RETURN(HA_ERR_INITIALIZATION); // Error in Cardinality
4240
4241 } // endif valid_info
4242
4243 if (flag & HA_STATUS_VARIABLE) {
4244 stats.records= xinfo.records;
4245 stats.deleted= 0;
4246 stats.data_file_length= xinfo.data_file_length;
4247 stats.index_file_length= 0;
4248 stats.delete_length= 0;
4249 stats.check_time= 0;
4250 stats.mean_rec_length= xinfo.mean_rec_length;
4251 } // endif HA_STATUS_VARIABLE
4252
4253 if (flag & HA_STATUS_CONST) {
4254 // This is imported from the previous handler and must be reconsidered
4255 stats.max_data_file_length= 4294967295LL;
4256 stats.max_index_file_length= 4398046510080LL;
4257 stats.create_time= 0;
4258 data_file_name= xinfo.data_file_name;
4259 index_file_name= NULL;
4260// sortkey= (uint) - 1; // Table is not sorted
4261 ref_length= sizeof(int); // Pointer size to row
4262 table->s->db_options_in_use= 03;
4263 stats.block_size= 1024;
4264 table->s->keys_in_use.set_prefix(table->s->keys);
4265 table->s->keys_for_keyread= table->s->keys_in_use;
4266// table->s->keys_for_keyread.subtract(table->s->read_only_keys);
4267 table->s->db_record_offset= 0;
4268 } // endif HA_STATUS_CONST
4269
4270 if (flag & HA_STATUS_ERRKEY) {
4271 errkey= 0;
4272 } // endif HA_STATUS_ERRKEY
4273
4274 if (flag & HA_STATUS_TIME)
4275 stats.update_time= 0;
4276
4277 if (flag & HA_STATUS_AUTO)
4278 stats.auto_increment_value= 1;
4279
4280 if (tdbp && pure)
4281 CloseTable(g); // Not used anymore
4282
4283 DBUG_RETURN(0);
4284} // end of info
4285
4286
4287/**
4288 @brief
4289 extra() is called whenever the server wishes to send a hint to
4290 the storage engine. The myisam engine implements the most hints.
4291 ha_innodb.cc has the most exhaustive list of these hints.
4292
4293 @note
4294 This is not yet implemented for CONNECT.
4295
4296 @see
4297 ha_innodb.cc
4298*/
4299int ha_connect::extra(enum ha_extra_function /*operation*/)
4300{
4301 DBUG_ENTER("ha_connect::extra");
4302 DBUG_RETURN(0);
4303} // end of extra
4304
4305
4306/**
4307 @brief
4308 Used to delete all rows in a table, including cases of truncate and cases where
4309 the optimizer realizes that all rows will be removed as a result of an SQL statement.
4310
4311 @details
4312 Called from item_sum.cc by Item_func_group_concat::clear(),
4313 Item_sum_count_distinct::clear(), and Item_func_group_concat::clear().
4314 Called from sql_delete.cc by mysql_delete().
4315 Called from sql_select.cc by JOIN::reinit().
4316 Called from sql_union.cc by st_select_lex_unit::exec().
4317
4318 @see
4319 Item_func_group_concat::clear(), Item_sum_count_distinct::clear() and
4320 Item_func_group_concat::clear() in item_sum.cc;
4321 mysql_delete() in sql_delete.cc;
4322 JOIN::reinit() in sql_select.cc and
4323 st_select_lex_unit::exec() in sql_union.cc.
4324*/
4325int ha_connect::delete_all_rows()
4326{
4327 int rc= 0;
4328 PGLOBAL g= xp->g;
4329 DBUG_ENTER("ha_connect::delete_all_rows");
4330
4331 if (tdbp && tdbp->GetUse() == USE_OPEN &&
4332 tdbp->GetAmType() != TYPE_AM_XML &&
4333 tdbp->GetFtype() != RECFM_NAF)
4334 // Close and reopen the table so it will be deleted
4335 rc= CloseTable(g);
4336
4337 if (!(rc= OpenTable(g))) {
4338 if (CntDeleteRow(g, tdbp, true)) {
4339 htrc("%s\n", g->Message);
4340 rc= HA_ERR_INTERNAL_ERROR;
4341 } else
4342 nox= false;
4343
4344 } // endif rc
4345
4346 DBUG_RETURN(rc);
4347} // end of delete_all_rows
4348
4349
4350bool ha_connect::check_privileges(THD *thd, PTOS options, const char *dbn, bool quick)
4351{
4352 const char *db= (dbn && *dbn) ? dbn : NULL;
4353 TABTYPE type=GetRealType(options);
4354
4355 switch (type) {
4356 case TAB_UNDEF:
4357// case TAB_CATLG:
4358 case TAB_PLG:
4359 case TAB_JCT:
4360 case TAB_DMY:
4361 case TAB_NIY:
4362 my_printf_error(ER_UNKNOWN_ERROR,
4363 "Unsupported table type %s", MYF(0), options->type);
4364 return true;
4365
4366 case TAB_DOS:
4367 case TAB_FIX:
4368 case TAB_BIN:
4369 case TAB_CSV:
4370 case TAB_FMT:
4371 case TAB_DBF:
4372 case TAB_XML:
4373 case TAB_INI:
4374 case TAB_VEC:
4375 case TAB_JSON:
4376 if (options->filename && *options->filename) {
4377 if (!quick) {
4378 char path[FN_REFLEN], dbpath[FN_REFLEN];
4379
4380 strcpy(dbpath, mysql_real_data_home);
4381
4382 if (db)
4383#if defined(__WIN__)
4384 strcat(strcat(dbpath, db), "\\");
4385#else // !__WIN__
4386 strcat(strcat(dbpath, db), "/");
4387#endif // !__WIN__
4388
4389 (void)fn_format(path, options->filename, dbpath, "",
4390 MY_RELATIVE_PATH | MY_UNPACK_FILENAME);
4391
4392 if (!is_secure_file_path(path)) {
4393 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--secure-file-priv");
4394 return true;
4395 } // endif path
4396
4397 } // endif !quick
4398
4399 } else
4400 return false;
4401
4402 // Fall through
4403 case TAB_MYSQL:
4404 case TAB_DIR:
4405 case TAB_ZIP:
4406 case TAB_OEM:
4407#ifdef NO_EMBEDDED_ACCESS_CHECKS
4408 return false;
4409 #endif
4410
4411 /*
4412 Check FILE_ACL
4413 If table or table->mdl_ticket is NULL - it's a DLL, e.g. CREATE TABLE.
4414 if the table has an MDL_EXCLUSIVE lock - it's a DDL too, e.g. the
4415 insert step of CREATE ... SELECT.
4416
4417 Otherwise it's a DML, the table was normally opened, locked,
4418 privilege were already checked, and table->grant.privilege is set.
4419 With SQL SECURITY DEFINER, table->grant.privilege has definer's privileges.
4420
4421 Unless we're in prelocking mode, in this case table->grant.privilege
4422 is only checked in start_stmt(), not in external_lock().
4423 */
4424 if (!table || !table->mdl_ticket || table->mdl_ticket->get_type() == MDL_EXCLUSIVE)
4425 return check_access(thd, FILE_ACL, db, NULL, NULL, 0, 0);
4426
4427 if ((!quick && thd->lex->requires_prelocking()) || table->grant.privilege & FILE_ACL)
4428 return false;
4429
4430 status_var_increment(thd->status_var.access_denied_errors);
4431 my_error(access_denied_error_code(thd->password), MYF(0),
4432 thd->security_ctx->priv_user, thd->security_ctx->priv_host,
4433 (thd->password ? ER(ER_YES) : ER(ER_NO)));
4434 return true;
4435 case TAB_ODBC:
4436 case TAB_JDBC:
4437 case TAB_MONGO:
4438 case TAB_MAC:
4439 case TAB_WMI:
4440 return false;
4441 case TAB_TBL:
4442 case TAB_XCL:
4443 case TAB_PRX:
4444 case TAB_OCCUR:
4445 case TAB_PIVOT:
4446 case TAB_VIR:
4447 // This is temporary until a solution is found
4448 return false;
4449 } // endswitch type
4450
4451 my_printf_error(ER_UNKNOWN_ERROR, "check_privileges failed", MYF(0));
4452 return true;
4453} // end of check_privileges
4454
4455// Check that two indexes are equivalent
4456bool ha_connect::IsSameIndex(PIXDEF xp1, PIXDEF xp2)
4457{
4458 bool b= true;
4459 PKPDEF kp1, kp2;
4460
4461 if (stricmp(xp1->Name, xp2->Name))
4462 b= false;
4463 else if (xp1->Nparts != xp2->Nparts ||
4464 xp1->MaxSame != xp2->MaxSame ||
4465 xp1->Unique != xp2->Unique)
4466 b= false;
4467 else for (kp1= xp1->ToKeyParts, kp2= xp2->ToKeyParts;
4468 b && (kp1 || kp2);
4469 kp1= kp1->Next, kp2= kp2->Next)
4470 if (!kp1 || !kp2)
4471 b= false;
4472 else if (stricmp(kp1->Name, kp2->Name))
4473 b= false;
4474 else if (kp1->Klen != kp2->Klen)
4475 b= false;
4476
4477 return b;
4478} // end of IsSameIndex
4479
4480MODE ha_connect::CheckMode(PGLOBAL g, THD *thd,
4481 MODE newmode, bool *chk, bool *cras)
4482{
4483#if defined(DEVELOPMENT)
4484 if (true) {
4485#else
4486 if (trace(65)) {
4487#endif
4488 LEX_STRING *query_string= thd_query_string(thd);
4489 htrc("%p check_mode: cmdtype=%d\n", this, thd_sql_command(thd));
4490 htrc("Cmd=%.*s\n", (int) query_string->length, query_string->str);
4491 } // endif trace
4492
4493 // Next code is temporarily replaced until sql_command is set
4494 stop= false;
4495
4496 if (newmode == MODE_WRITE) {
4497 switch (thd_sql_command(thd)) {
4498 case SQLCOM_LOCK_TABLES:
4499 locked= 2; // fall through
4500 case SQLCOM_CREATE_TABLE:
4501 case SQLCOM_INSERT:
4502 case SQLCOM_LOAD:
4503 case SQLCOM_INSERT_SELECT:
4504 newmode= MODE_INSERT;
4505 break;
4506// case SQLCOM_REPLACE:
4507// case SQLCOM_REPLACE_SELECT:
4508// newmode= MODE_UPDATE; // To be checked
4509// break;
4510 case SQLCOM_DELETE:
4511 case SQLCOM_DELETE_MULTI:
4512 case SQLCOM_TRUNCATE:
4513 newmode= MODE_DELETE;
4514 break;
4515 case SQLCOM_UPDATE:
4516 case SQLCOM_UPDATE_MULTI:
4517 newmode= MODE_UPDATE;
4518 break;
4519 case SQLCOM_SELECT:
4520 case SQLCOM_OPTIMIZE:
4521 newmode= MODE_READ;
4522 break;
4523 case SQLCOM_FLUSH:
4524 locked= 0;
4525 case SQLCOM_DROP_TABLE:
4526 case SQLCOM_RENAME_TABLE:
4527 newmode= MODE_ANY;
4528 break;
4529 case SQLCOM_CREATE_VIEW:
4530 case SQLCOM_DROP_VIEW:
4531 newmode= MODE_ANY;
4532 break;
4533 case SQLCOM_ALTER_TABLE:
4534 newmode= MODE_ALTER;
4535 break;
4536 case SQLCOM_DROP_INDEX:
4537 case SQLCOM_CREATE_INDEX:
4538// if (!IsPartitioned()) {
4539 newmode= MODE_ANY;
4540 break;
4541// } // endif partitioned
4542
4543 default:
4544 htrc("Unsupported sql_command=%d\n", thd_sql_command(thd));
4545 strcpy(g->Message, "CONNECT Unsupported command");
4546 my_message(ER_NOT_ALLOWED_COMMAND, g->Message, MYF(0));
4547 newmode= MODE_ERROR;
4548 break;
4549 } // endswitch newmode
4550
4551 } else if (newmode == MODE_READ) {
4552 switch (thd_sql_command(thd)) {
4553 case SQLCOM_CREATE_TABLE:
4554 *chk= true;
4555 *cras= true;
4556 case SQLCOM_INSERT:
4557 case SQLCOM_LOAD:
4558 case SQLCOM_INSERT_SELECT:
4559// case SQLCOM_REPLACE:
4560// case SQLCOM_REPLACE_SELECT:
4561 case SQLCOM_DELETE:
4562 case SQLCOM_DELETE_MULTI:
4563 case SQLCOM_TRUNCATE:
4564 case SQLCOM_UPDATE:
4565 case SQLCOM_UPDATE_MULTI:
4566 case SQLCOM_SELECT:
4567 case SQLCOM_OPTIMIZE:
4568 case SQLCOM_SET_OPTION:
4569 break;
4570 case SQLCOM_LOCK_TABLES:
4571 locked= 1;
4572 break;
4573 case SQLCOM_DROP_TABLE:
4574 case SQLCOM_RENAME_TABLE:
4575 newmode= MODE_ANY;
4576 break;
4577 case SQLCOM_CREATE_VIEW:
4578 case SQLCOM_DROP_VIEW:
4579 newmode= MODE_ANY;
4580 break;
4581 case SQLCOM_ALTER_TABLE:
4582 *chk= true;
4583 newmode= MODE_ALTER;
4584 break;
4585 case SQLCOM_DROP_INDEX:
4586 case SQLCOM_CREATE_INDEX:
4587// if (!IsPartitioned()) {
4588 *chk= true;
4589 newmode= MODE_ANY;
4590 break;
4591// } // endif partitioned
4592
4593 case SQLCOM_CHECK: // TODO implement it
4594 case SQLCOM_END: // Met in procedures: IF(EXISTS(SELECT...
4595 newmode= MODE_READ;
4596 break;
4597 default:
4598 htrc("Unsupported sql_command=%d\n", thd_sql_command(thd));
4599 strcpy(g->Message, "CONNECT Unsupported command");
4600 my_message(ER_NOT_ALLOWED_COMMAND, g->Message, MYF(0));
4601 newmode= MODE_ERROR;
4602 break;
4603 } // endswitch newmode
4604
4605 } // endif's newmode
4606
4607 if (trace(1))
4608 htrc("New mode=%d\n", newmode);
4609
4610 return newmode;
4611} // end of check_mode
4612
4613int ha_connect::start_stmt(THD *thd, thr_lock_type lock_type)
4614{
4615 int rc= 0;
4616 bool chk=false, cras= false;
4617 MODE newmode;
4618 PGLOBAL g= GetPlug(thd, xp);
4619 DBUG_ENTER("ha_connect::start_stmt");
4620
4621 if (check_privileges(thd, GetTableOptionStruct(), table->s->db.str, true))
4622 DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
4623
4624 // Action will depend on lock_type
4625 switch (lock_type) {
4626 case TL_WRITE_ALLOW_WRITE:
4627 case TL_WRITE_CONCURRENT_INSERT:
4628 case TL_WRITE_DELAYED:
4629 case TL_WRITE_DEFAULT:
4630 case TL_WRITE_LOW_PRIORITY:
4631 case TL_WRITE:
4632 case TL_WRITE_ONLY:
4633 newmode= MODE_WRITE;
4634 break;
4635 case TL_READ:
4636 case TL_READ_WITH_SHARED_LOCKS:
4637 case TL_READ_HIGH_PRIORITY:
4638 case TL_READ_NO_INSERT:
4639 case TL_READ_DEFAULT:
4640 newmode= MODE_READ;
4641 break;
4642 case TL_UNLOCK:
4643 default:
4644 newmode= MODE_ANY;
4645 break;
4646 } // endswitch mode
4647
4648 xmod= CheckMode(g, thd, newmode, &chk, &cras);
4649 DBUG_RETURN((xmod == MODE_ERROR) ? HA_ERR_INTERNAL_ERROR : 0);
4650} // end of start_stmt
4651
4652/**
4653 @brief
4654 This create a lock on the table. If you are implementing a storage engine
4655 that can handle transacations look at ha_berkely.cc to see how you will
4656 want to go about doing this. Otherwise you should consider calling flock()
4657 here. Hint: Read the section "locking functions for mysql" in lock.cc to understand
4658 this.
4659
4660 @details
4661 Called from lock.cc by lock_external() and unlock_external(). Also called
4662 from sql_table.cc by copy_data_between_tables().
4663
4664 @note
4665 Following what we did in the MySQL XDB handler, we use this call to actually
4666 physically open the table. This could be reconsider when finalizing this handler
4667 design, which means we have a better understanding of what MariaDB does.
4668
4669 @see
4670 lock.cc by lock_external() and unlock_external() in lock.cc;
4671 the section "locking functions for mysql" in lock.cc;
4672 copy_data_between_tables() in sql_table.cc.
4673*/
4674int ha_connect::external_lock(THD *thd, int lock_type)
4675{
4676 int rc= 0;
4677 bool xcheck=false, cras= false;
4678 MODE newmode;
4679 PTOS options= GetTableOptionStruct();
4680 PGLOBAL g= GetPlug(thd, xp);
4681 DBUG_ENTER("ha_connect::external_lock");
4682
4683 DBUG_ASSERT(thd == current_thd);
4684
4685 if (trace(1))
4686 htrc("external_lock: this=%p thd=%p xp=%p g=%p lock_type=%d\n",
4687 this, thd, xp, g, lock_type);
4688
4689 if (!g)
4690 DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
4691
4692 // Action will depend on lock_type
4693 switch (lock_type) {
4694 case F_WRLCK:
4695 newmode= MODE_WRITE;
4696 break;
4697 case F_RDLCK:
4698 newmode= MODE_READ;
4699 break;
4700 case F_UNLCK:
4701 default:
4702 newmode= MODE_ANY;
4703 break;
4704 } // endswitch mode
4705
4706 if (newmode == MODE_ANY) {
4707 int sqlcom= thd_sql_command(thd);
4708
4709 // This is unlocking, do it by closing the table
4710 if (xp->CheckQueryID() && sqlcom != SQLCOM_UNLOCK_TABLES
4711 && sqlcom != SQLCOM_LOCK_TABLES
4712 && sqlcom != SQLCOM_FLUSH
4713 && sqlcom != SQLCOM_BEGIN
4714 && sqlcom != SQLCOM_DROP_TABLE) {
4715 sprintf(g->Message, "external_lock: unexpected command %d", sqlcom);
4716 push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
4717 DBUG_RETURN(0);
4718 } else if (g->Xchk) {
4719 if (!tdbp) {
4720 if (!(tdbp = GetTDB(g))) {
4721// DBUG_RETURN(HA_ERR_INTERNAL_ERROR); causes assert error
4722 push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
4723 DBUG_RETURN(0);
4724 } else if (!tdbp->GetDef()->Indexable()) {
4725 sprintf(g->Message, "external_lock: Table %s is not indexable", tdbp->GetName());
4726// DBUG_RETURN(HA_ERR_INTERNAL_ERROR); causes assert error
4727 push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
4728 DBUG_RETURN(0);
4729 } else if (tdbp->GetDef()->Indexable() == 1) {
4730 bool oldsep= ((PCHK)g->Xchk)->oldsep;
4731 bool newsep= ((PCHK)g->Xchk)->newsep;
4732 PTDBDOS tdp= (PTDBDOS)tdbp;
4733
4734 PDOSDEF ddp= (PDOSDEF)tdp->GetDef();
4735 PIXDEF xp, xp1, xp2, drp=NULL, adp= NULL;
4736 PIXDEF oldpix= ((PCHK)g->Xchk)->oldpix;
4737 PIXDEF newpix= ((PCHK)g->Xchk)->newpix;
4738 PIXDEF *xlst, *xprc;
4739
4740 ddp->SetIndx(oldpix);
4741
4742 if (oldsep != newsep) {
4743 // All indexes have to be remade
4744 ddp->DeleteIndexFile(g, NULL);
4745 oldpix= NULL;
4746 ddp->SetIndx(NULL);
4747 SetBooleanOption("Sepindex", newsep);
4748 } else if (newsep) {
4749 // Make the list of dropped indexes
4750 xlst= &drp; xprc= &oldpix;
4751
4752 for (xp2= oldpix; xp2; xp2= xp) {
4753 for (xp1= newpix; xp1; xp1= xp1->Next)
4754 if (IsSameIndex(xp1, xp2))
4755 break; // Index not to drop
4756
4757 xp= xp2->GetNext();
4758
4759 if (!xp1) {
4760 *xlst= xp2;
4761 *xprc= xp;
4762 *(xlst= &xp2->Next)= NULL;
4763 } else
4764 xprc= &xp2->Next;
4765
4766 } // endfor xp2
4767
4768 if (drp) {
4769 // Here we erase the index files
4770 ddp->DeleteIndexFile(g, drp);
4771 } // endif xp1
4772
4773 } else if (oldpix) {
4774 // TODO: optimize the case of just adding new indexes
4775 if (!newpix)
4776 ddp->DeleteIndexFile(g, NULL);
4777
4778 oldpix= NULL; // To remake all indexes
4779 ddp->SetIndx(NULL);
4780 } // endif sepindex
4781
4782 // Make the list of new created indexes
4783 xlst= &adp; xprc= &newpix;
4784
4785 for (xp1= newpix; xp1; xp1= xp) {
4786 for (xp2= oldpix; xp2; xp2= xp2->Next)
4787 if (IsSameIndex(xp1, xp2))
4788 break; // Index already made
4789
4790 xp= xp1->Next;
4791
4792 if (!xp2) {
4793 *xlst= xp1;
4794 *xprc= xp;
4795 *(xlst= &xp1->Next)= NULL;
4796 } else
4797 xprc= &xp1->Next;
4798
4799 } // endfor xp1
4800
4801 if (adp)
4802 // Here we do make the new indexes
4803 if (tdp->MakeIndex(g, adp, true) == RC_FX) {
4804 // Make it a warning to avoid crash
4805 push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
4806 0, g->Message);
4807 rc= 0;
4808 //my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
4809 //rc= HA_ERR_INTERNAL_ERROR;
4810 } // endif MakeIndex
4811
4812 } else if (tdbp->GetDef()->Indexable() == 3) {
4813 if (CheckVirtualIndex(NULL)) {
4814 // Make it a warning to avoid crash
4815 push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
4816 0, g->Message);
4817 rc= 0;
4818 } // endif Check
4819
4820 } // endif indexable
4821
4822 } // endif Tdbp
4823
4824 } // endelse Xchk
4825
4826 if (CloseTable(g)) {
4827 // This is an error while builing index
4828 // Make it a warning to avoid crash
4829 push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
4830 rc= 0;
4831 //my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
4832 //rc = HA_ERR_INTERNAL_ERROR;
4833 } // endif Close
4834
4835 locked= 0;
4836// m_lock_type= lock_type;
4837 xmod= MODE_ANY; // For info commands
4838 DBUG_RETURN(rc);
4839 } // endif MODE_ANY
4840 else
4841 if (check_privileges(thd, options, table->s->db.str)) {
4842 strcpy(g->Message, "This operation requires the FILE privilege");
4843 htrc("%s\n", g->Message);
4844 DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
4845 } // endif check_privileges
4846
4847
4848 DBUG_ASSERT(table && table->s);
4849
4850 // Table mode depends on the query type
4851 newmode= CheckMode(g, thd, newmode, &xcheck, &cras);
4852
4853 if (newmode == MODE_ERROR)
4854 DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
4855
4856 // If this is the start of a new query, cleanup the previous one
4857 if (xp->CheckCleanup()) {
4858 tdbp= NULL;
4859 valid_info= false;
4860 } // endif CheckCleanup
4861
4862#if 0
4863 if (xcheck) {
4864 // This must occur after CheckCleanup
4865 if (!g->Xchk) {
4866 g->Xchk= new(g) XCHK;
4867 ((PCHK)g->Xchk)->oldsep= GetBooleanOption("Sepindex", false);
4868 ((PCHK)g->Xchk)->oldpix= GetIndexInfo();
4869 } // endif Xchk
4870
4871 } else
4872 g->Xchk= NULL;
4873#endif // 0
4874
4875 if (cras)
4876 g->Createas= 1; // To tell created table to ignore FLAG
4877
4878 if (trace(1)) {
4879#if 0
4880 htrc("xcheck=%d cras=%d\n", xcheck, cras);
4881
4882 if (xcheck)
4883 htrc("oldsep=%d oldpix=%p\n",
4884 ((PCHK)g->Xchk)->oldsep, ((PCHK)g->Xchk)->oldpix);
4885#endif // 0
4886 htrc("Calling CntCheckDB db=%s cras=%d\n", GetDBName(NULL), cras);
4887 } // endif trace
4888
4889 // Set or reset the good database environment
4890 if (CntCheckDB(g, this, GetDBName(NULL))) {
4891 htrc("%p external_lock: %s\n", this, g->Message);
4892 rc= HA_ERR_INTERNAL_ERROR;
4893 // This can NOT be called without open called first, but
4894 // the table can have been closed since then
4895 } else if (!tdbp || xp->CheckQuery(valid_query_id) || xmod != newmode) {
4896 if (tdbp) {
4897 // If this is called by a later query, the table may have
4898 // been already closed and the tdbp is not valid anymore.
4899 if (xp->