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 02110-1335 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(_WIN32)
143 #include <io.h>
144 #include "tabwmi.h"
145 #endif   // _WIN32
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     1024							// Default converted text size
168 #define SZWORK 67108864             // Default work area size 64M
169 #define SZWMIN  4194304             // Minimum work area size  4M
170 #define JSONMAX      50             // JSON Default max grp size
171 
172 extern "C" {
173        char version[]= "Version 1.07.0002 March 22, 2021";
174 #if defined(_WIN32)
175        char compver[]= "Version 1.07.0002 " __DATE__ " "  __TIME__;
176        static char slash= '\\';
177 #else   // !_WIN32
178        static char slash= '/';
179 #endif  // !_WIN32
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 
190 ulong  ha_connect::num= 0;
191 
192 #if defined(XMSG)
193 extern "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 
203 pthread_mutex_t parmut;
204 pthread_mutex_t usrmut;
205 pthread_mutex_t tblmut;
206 
207 #if defined(DEVELOPMENT)
208 char *GetUserVariable(PGLOBAL g, const uchar *varname);
209 
GetUserVariable(PGLOBAL g,const uchar * varname)210 char *GetUserVariable(PGLOBAL g, const uchar *varname)
211 {
212 	char buf[1024];
213 	bool b;
214 	THD *thd= current_thd;
215 	CHARSET_INFO *cs= system_charset_info;
216 	String *str= NULL, tmp(buf, sizeof(buf), cs);
217 	HASH uvars= thd->user_vars;
218 	user_var_entry *uvar= (user_var_entry*)my_hash_search(&uvars, varname, 0);
219 
220 	if (uvar)
221 		str= uvar->val_str(&b, &tmp, NOT_FIXED_DEC);
222 
223 	return str ? PlugDup(g, str->ptr()) : NULL;
224 }; // end of GetUserVariable
225 #endif   // DEVELOPMENT
226 
227 /***********************************************************************/
228 /*  Utility functions.                                                 */
229 /***********************************************************************/
230 PQRYRES OEMColumns(PGLOBAL g, PTOS topt, char *tab, char *db, bool info);
231 PQRYRES VirColumns(PGLOBAL g, bool info);
232 PQRYRES JSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info);
233 #ifdef    BSON_SUPPORT
234 PQRYRES BSONColumns(PGLOBAL g, PCSZ db, PCSZ dsn, PTOS topt, bool info);
235 #endif // BSON_SUPPORT
236 PQRYRES XMLColumns(PGLOBAL g, char *db, char *tab, PTOS topt, bool info);
237 #if defined(REST_SUPPORT)
238 PQRYRES RESTColumns(PGLOBAL g, PTOS topt, char *tab, char *db, bool info);
239 #endif // REST_SUPPORT
240 #if defined(JAVA_SUPPORT)
241 PQRYRES MGOColumns(PGLOBAL g, PCSZ db, PCSZ url, PTOS topt, bool info);
242 #endif   // JAVA_SUPPORT
243 int     TranslateJDBCType(int stp, char *tn, int prec, int& len, char& v);
244 void    PushWarning(PGLOBAL g, THD *thd, int level);
245 bool    CheckSelf(PGLOBAL g, TABLE_SHARE *s, PCSZ host, PCSZ db,
246 	                                           PCSZ tab, PCSZ src, int port);
247 #if defined(ZIP_SUPPORT)
248 bool    ZipLoadFile(PGLOBAL, PCSZ, PCSZ, PCSZ, bool, bool);
249 #endif   // ZIP_SUPPORT
250 bool    ExactInfo(void);
251 #if defined(CMGO_SUPPORT)
252 //void    mongo_init(bool);
253 #endif   // CMGO_SUPPORT
254 USETEMP UseTemp(void);
255 int     GetConvSize(void);
256 TYPCONV GetTypeConv(void);
257 int     GetDefaultDepth(void);
258 int     GetDefaultPrec(void);
259 bool    JsonAllPath(void);
260 char   *GetJsonNull(void);
261 uint    GetJsonGrpSize(void);
262 char   *GetJavaWrapper(void);
263 #if defined(BSON_SUPPORT)
264 bool    Force_Bson(void);
265 #endif   // BSON_SUPPORT
266 size_t  GetWorkSize(void);
267 void    SetWorkSize(size_t);
268 extern "C" const char *msglang(void);
269 
270 static void PopUser(PCONNECT xp);
271 static PCONNECT GetUser(THD *thd, PCONNECT xp);
272 static PGLOBAL  GetPlug(THD *thd, PCONNECT& lxp);
273 
274 static handler *connect_create_handler(handlerton *hton,
275                                        TABLE_SHARE *table,
276                                        MEM_ROOT *mem_root);
277 
278 static bool checkPrivileges(THD* thd, TABTYPE type, PTOS options,
279                             const char* db, TABLE* table = NULL,
280                             bool quick = false);
281 
282 static int connect_assisted_discovery(handlerton *hton, THD* thd,
283                                       TABLE_SHARE *table_s,
284                                       HA_CREATE_INFO *info);
285 
286 /****************************************************************************/
287 /*  Return str as a zero terminated string.                                 */
288 /****************************************************************************/
strz(PGLOBAL g,LEX_CSTRING & ls)289 static char *strz(PGLOBAL g, LEX_CSTRING &ls)
290 {
291   char* str= NULL;
292 
293   if (ls.str) {
294     str= (char*)PlugSubAlloc(g, NULL, ls.length + 1);
295     memcpy(str, ls.str, ls.length);
296     str[ls.length] = 0;
297   } // endif str
298 
299   return str;
300 } // end of strz
301 
302 /***********************************************************************/
303 /*  CONNECT session variables definitions.                             */
304 /***********************************************************************/
305 // Tracing: 0 no, 1 yes, 2 more, 4 index... 511 all
306 const char *xtrace_names[] =
307 {
308 	"YES", "MORE", "INDEX", "MEMORY", "SUBALLOC",
309 	"QUERY", "STMT", "HANDLER", "BLOCK", "MONGO", NullS
310 };
311 
312 TYPELIB xtrace_typelib =
313 {
314 	array_elements(xtrace_names) - 1, "xtrace_typelib",
315 	xtrace_names, NULL
316 };
317 
318 static MYSQL_THDVAR_SET(
319 	xtrace,                    // name
320 	PLUGIN_VAR_RQCMDARG,       // opt
321 	"Trace values.",           // comment
322 	NULL,                      // check
323 	NULL,                      // update function
324 	0,                         // def (NO)
325 	&xtrace_typelib);          // typelib
326 
327 // Getting exact info values
328 static MYSQL_THDVAR_BOOL(exact_info, PLUGIN_VAR_RQCMDARG,
329        "Getting exact info values",
330        NULL, NULL, 0);
331 
332 // Enabling cond_push
333 static MYSQL_THDVAR_BOOL(cond_push, PLUGIN_VAR_RQCMDARG,
334 	"Enabling cond_push",
335 	NULL, NULL, 1);							// YES by default
336 
337 /**
338   Temporary file usage:
339     no:    Not using temporary file
340     auto:  Using temporary file when needed
341     yes:   Allways using temporary file
342     force: Force using temporary file (no MAP)
343     test:  Reserved
344 */
345 const char *usetemp_names[]=
346 {
347   "NO", "AUTO", "YES", "FORCE", "TEST", NullS
348 };
349 
350 TYPELIB usetemp_typelib=
351 {
352   array_elements(usetemp_names) - 1, "usetemp_typelib",
353   usetemp_names, NULL
354 };
355 
356 static MYSQL_THDVAR_ENUM(
357   use_tempfile,                    // name
358   PLUGIN_VAR_RQCMDARG,             // opt
359   "Temporary file use.",           // comment
360   NULL,                            // check
361   NULL,                            // update function
362   1,                               // def (AUTO)
363   &usetemp_typelib);               // typelib
364 
365 #ifdef _WIN64
366 // Size used for g->Sarea_Size
367 static MYSQL_THDVAR_ULONGLONG(work_size,
368 	PLUGIN_VAR_RQCMDARG,
369 	"Size of the CONNECT work area.",
370 	NULL, NULL, SZWORK, SZWMIN, ULONGLONG_MAX, 1);
371 #else
372 // Size used for g->Sarea_Size
373 static MYSQL_THDVAR_ULONG(work_size,
374   PLUGIN_VAR_RQCMDARG,
375   "Size of the CONNECT work area.",
376   NULL, NULL, SZWORK, SZWMIN, ULONG_MAX, 1);
377 #endif
378 
379 // Size used when converting TEXT columns to VARCHAR
380 static MYSQL_THDVAR_INT(conv_size,
381        PLUGIN_VAR_RQCMDARG,             // opt
382        "Size used when converting TEXT columns.",
383        NULL, NULL, SZCONV, 0, 65500, 1);
384 
385 /**
386   Type conversion:
387     no:   Unsupported types -> TYPE_ERROR
388     yes:  TEXT -> VARCHAR
389 		force: Do it also for ODBC BINARY and BLOBs
390     skip: skip unsupported type columns in Discovery
391 */
392 const char *xconv_names[]=
393 {
394   "NO", "YES", "FORCE", "SKIP", NullS
395 };
396 
397 TYPELIB xconv_typelib=
398 {
399   array_elements(xconv_names) - 1, "xconv_typelib",
400   xconv_names, NULL
401 };
402 
403 static MYSQL_THDVAR_ENUM(
404   type_conv,                       // name
405   PLUGIN_VAR_RQCMDARG,             // opt
406   "Unsupported types conversion.", // comment
407   NULL,                            // check
408   NULL,                            // update function
409   1,                               // def (yes)
410   &xconv_typelib);                 // typelib
411 
412 // Adding JPATH to all Json table columns
413 static MYSQL_THDVAR_BOOL(json_all_path, PLUGIN_VAR_RQCMDARG,
414 	"Adding JPATH to all Json table columns",
415 	NULL, NULL, 1);							     // YES by default
416 
417 // Null representation for JSON values
418 static MYSQL_THDVAR_STR(json_null,
419 	PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
420 	"Representation of Json null values",
421 	//     check_json_null, update_json_null,
422 	NULL, NULL, "<null>");
423 
424 // Default Json, XML or Mongo depth
425 static MYSQL_THDVAR_INT(default_depth,
426 	PLUGIN_VAR_RQCMDARG,
427 	"Default depth used by Json, XML and Mongo discovery",
428 	NULL, NULL, 5, -1, 16, 1);			 // Defaults to 5
429 
430 // Default precision for doubles
431 static MYSQL_THDVAR_INT(default_prec,
432   PLUGIN_VAR_RQCMDARG,
433   "Default precision used for doubles",
434   NULL, NULL, 6, 0, 16, 1);			 // Defaults to 6
435 
436 // Estimate max number of rows for JSON aggregate functions
437 static MYSQL_THDVAR_UINT(json_grp_size,
438        PLUGIN_VAR_RQCMDARG,      // opt
439        "max number of rows for JSON aggregate functions.",
440        NULL, NULL, JSONMAX, 1, INT_MAX, 1);
441 
442 #if defined(JAVA_SUPPORT)
443 // Default java wrapper to use with JDBC tables
444 static MYSQL_THDVAR_STR(java_wrapper,
445 	PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
446 	"Java wrapper class name",
447 	//     check_java_wrapper, update_java_wrapper,
448 	NULL, NULL, "wrappers/JdbcInterface");
449 #endif   // JAVA_SUPPORT
450 
451 // This is apparently not acceptable for a plugin so it is undocumented
452 #if defined(JAVA_SUPPORT) || defined(CMGO_SUPPORT)
453 // Enabling MONGO table type
454 #if defined(MONGO_SUPPORT) || (MYSQL_VERSION_ID > 100200)
455 static MYSQL_THDVAR_BOOL(enable_mongo, PLUGIN_VAR_RQCMDARG,
456 	"Enabling the MongoDB access", NULL, NULL, 1);
457 #else   // !version 2,3
458 static MYSQL_THDVAR_BOOL(enable_mongo, PLUGIN_VAR_RQCMDARG,
459 	"Enabling the MongoDB access", NULL, NULL, 0);
460 #endif  // !version 2,3
461 #endif   // JAVA_SUPPORT || CMGO_SUPPORT
462 
463 #if defined(BSON_SUPPORT)
464 // Force using BSON for JSON tables
465 static MYSQL_THDVAR_BOOL(force_bson, PLUGIN_VAR_RQCMDARG,
466   "Force using BSON for JSON tables",
467   NULL, NULL, 0);							// NO by default
468 #endif   // BSON_SUPPORT
469 
470 #if defined(XMSG) || defined(NEWMSG)
471 const char *language_names[]=
472 {
473   "default", "english", "french", NullS
474 };
475 
476 TYPELIB language_typelib=
477 {
478   array_elements(language_names) - 1, "language_typelib",
479   language_names, NULL
480 };
481 
482 static MYSQL_THDVAR_ENUM(
483   msg_lang,                        // name
484   PLUGIN_VAR_RQCMDARG,             // opt
485   "Message language",              // comment
486   NULL,                            // check
487   NULL,                            // update
488   1,                               // def (ENGLISH)
489   &language_typelib);              // typelib
490 #endif   // XMSG || NEWMSG
491 
492 /***********************************************************************/
493 /*  The CONNECT handlerton object.                                     */
494 /***********************************************************************/
495 handlerton *connect_hton= NULL;
496 
497 /***********************************************************************/
498 /*  Function to export session variable values to other source files.  */
499 /***********************************************************************/
GetTraceValue(void)500 uint GetTraceValue(void)
501 	{return (uint)(connect_hton ? THDVAR(current_thd, xtrace) : 0);}
ExactInfo(void)502 bool ExactInfo(void) {return THDVAR(current_thd, exact_info);}
CondPushEnabled(void)503 static bool CondPushEnabled(void) {return THDVAR(current_thd, cond_push);}
JsonAllPath(void)504 bool JsonAllPath(void) {return THDVAR(current_thd, json_all_path);}
UseTemp(void)505 USETEMP UseTemp(void) {return (USETEMP)THDVAR(current_thd, use_tempfile);}
GetConvSize(void)506 int GetConvSize(void) {return THDVAR(current_thd, conv_size);}
GetTypeConv(void)507 TYPCONV GetTypeConv(void) {return (TYPCONV)THDVAR(current_thd, type_conv);}
GetJsonNull(void)508 char *GetJsonNull(void)
509 	{return connect_hton ? THDVAR(current_thd, json_null) : NULL;}
GetDefaultDepth(void)510 int GetDefaultDepth(void) {return THDVAR(current_thd, default_depth);}
GetDefaultPrec(void)511 int GetDefaultPrec(void) {return THDVAR(current_thd, default_prec);}
GetJsonGrpSize(void)512 uint GetJsonGrpSize(void)
513   {return connect_hton ? THDVAR(current_thd, json_grp_size) : 50;}
GetWorkSize(void)514 size_t GetWorkSize(void) {return (size_t)THDVAR(current_thd, work_size);}
SetWorkSize(size_t)515 void SetWorkSize(size_t)
516 {
517   // Changing the session variable value seems to be impossible here
518   // and should be done in a check function
519   push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0,
520     "Work size too big, try setting a smaller value");
521 } // end of SetWorkSize
522 
523 #if defined(JAVA_SUPPORT)
GetJavaWrapper(void)524 char *GetJavaWrapper(void)
525 {return connect_hton ? THDVAR(current_thd, java_wrapper)
526 	                   : (char*)"wrappers/JdbcInterface";}
527 #endif   // JAVA_SUPPORT
528 
529 #if defined(JAVA_SUPPORT) || defined(CMGO_SUPPORT)
MongoEnabled(void)530 bool MongoEnabled(void) {return THDVAR(current_thd, enable_mongo);}
531 #endif   // JAVA_SUPPORT || CMGO_SUPPORT
532 
533 #if defined(BSON_SUPPORT)
Force_Bson(void)534 bool Force_Bson(void) {return THDVAR(current_thd, force_bson);}
535 #endif   // BSON_SUPPORT)
536 
537 #if defined(XMSG) || defined(NEWMSG)
msglang(void)538 extern "C" const char *msglang(void)
539 	{return language_names[THDVAR(current_thd, msg_lang)];}
540 #else   // !XMSG && !NEWMSG
msglang(void)541 extern "C" const char *msglang(void)
542 {
543 #if defined(FRENCH)
544   return "french";
545 #else  // DEFAULT
546   return "english";
547 #endif // DEFAULT
548 } // end of msglang
549 #endif  // !XMSG && !NEWMSG
550 
551 #if 0
552 /***********************************************************************/
553 /*  Global variables update functions.                                 */
554 /***********************************************************************/
555 static void update_connect_zconv(MYSQL_THD thd,
556                                   struct st_mysql_sys_var *var,
557                                   void *var_ptr, const void *save)
558 {
559   zconv= *(int *)var_ptr= *(int *)save;
560 } // end of update_connect_zconv
561 
562 static void update_connect_xconv(MYSQL_THD thd,
563                                  struct st_mysql_sys_var *var,
564                                  void *var_ptr, const void *save)
565 {
566   xconv= (int)(*(ulong *)var_ptr= *(ulong *)save);
567 } // end of update_connect_xconv
568 
569 #if defined(XMAP)
570 static void update_connect_xmap(MYSQL_THD thd,
571                                 struct st_mysql_sys_var *var,
572                                 void *var_ptr, const void *save)
573 {
574   xmap= (my_bool)(*(my_bool *)var_ptr= *(my_bool *)save);
575 } // end of update_connect_xmap
576 #endif   // XMAP
577 #endif // 0
578 
579 #if 0 // (was XMSG) Unuseful because not called for default value
580 static void update_msg_path(MYSQL_THD thd,
581                             struct st_mysql_sys_var *var,
582                             void *var_ptr, const void *save)
583 {
584   char *value= *(char**)save;
585   char *old= *(char**)var_ptr;
586 
587   if (value)
588     *(char**)var_ptr= my_strdup(value, MYF(0));
589   else
590     *(char**)var_ptr= 0;
591 
592   my_free(old);
593 } // end of update_msg_path
594 
595 static int check_msg_path (MYSQL_THD thd, struct st_mysql_sys_var *var,
596 	                         void *save, struct st_mysql_value *value)
597 {
598 	const char *path;
599 	char	buff[512];
600 	int		len= sizeof(buff);
601 
602 	path= value->val_str(value, buff, &len);
603 
604 	if (path && *path != '*') {
605 		/* Save a pointer to the name in the
606 		'file_format_name_map' constant array. */
607 		*(char**)save= my_strdup(path, MYF(0));
608 		return(0);
609 	} else {
610 		push_warning_printf(thd,
611 		  Sql_condition::WARN_LEVEL_WARN,
612 		  ER_WRONG_ARGUMENTS,
613 		  "CONNECT: invalid message path");
614 	} // endif path
615 
616 	*(char**)save= NULL;
617 	return(1);
618 } // end of check_msg_path
619 #endif   // 0
620 
621 /**
622   CREATE TABLE option list (table options)
623 
624   These can be specified in the CREATE TABLE:
625   CREATE TABLE ( ... ) {...here...}
626 */
627 ha_create_table_option connect_table_option_list[]=
628 {
629   HA_TOPTION_STRING("TABLE_TYPE", type),
630   HA_TOPTION_STRING("FILE_NAME", filename),
631   HA_TOPTION_STRING("XFILE_NAME", optname),
632 //HA_TOPTION_STRING("CONNECT_STRING", connect),
633   HA_TOPTION_STRING("TABNAME", tabname),
634   HA_TOPTION_STRING("TABLE_LIST", tablist),
635   HA_TOPTION_STRING("DBNAME", dbname),
636   HA_TOPTION_STRING("SEP_CHAR", separator),
637   HA_TOPTION_STRING("QCHAR", qchar),
638   HA_TOPTION_STRING("MODULE", module),
639   HA_TOPTION_STRING("SUBTYPE", subtype),
640   HA_TOPTION_STRING("CATFUNC", catfunc),
641   HA_TOPTION_STRING("SRCDEF", srcdef),
642   HA_TOPTION_STRING("COLIST", colist),
643 	HA_TOPTION_STRING("FILTER", filter),
644 	HA_TOPTION_STRING("OPTION_LIST", oplist),
645   HA_TOPTION_STRING("DATA_CHARSET", data_charset),
646 	HA_TOPTION_STRING("HTTP", http),
647 	HA_TOPTION_STRING("URI", uri),
648 	HA_TOPTION_NUMBER("LRECL", lrecl, 0, 0, INT_MAX32, 1),
649   HA_TOPTION_NUMBER("BLOCK_SIZE", elements, 0, 0, INT_MAX32, 1),
650 //HA_TOPTION_NUMBER("ESTIMATE", estimate, 0, 0, INT_MAX32, 1),
651   HA_TOPTION_NUMBER("MULTIPLE", multiple, 0, 0, 3, 1),
652   HA_TOPTION_NUMBER("HEADER", header, 0, 0, 3, 1),
653   HA_TOPTION_NUMBER("QUOTED", quoted, (ulonglong) -1, 0, 3, 1),
654   HA_TOPTION_NUMBER("ENDING", ending, (ulonglong) -1, 0, INT_MAX32, 1),
655   HA_TOPTION_NUMBER("COMPRESS", compressed, 0, 0, 2, 1),
656   HA_TOPTION_BOOL("MAPPED", mapped, 0),
657   HA_TOPTION_BOOL("HUGE", huge, 0),
658   HA_TOPTION_BOOL("SPLIT", split, 0),
659   HA_TOPTION_BOOL("READONLY", readonly, 0),
660   HA_TOPTION_BOOL("SEPINDEX", sepindex, 0),
661 	HA_TOPTION_BOOL("ZIPPED", zipped, 0),
662 	HA_TOPTION_END
663 };
664 
665 
666 /**
667   CREATE TABLE option list (field options)
668 
669   These can be specified in the CREATE TABLE per field:
670   CREATE TABLE ( field ... {...here...}, ... )
671 */
672 ha_create_table_option connect_field_option_list[]=
673 {
674   HA_FOPTION_NUMBER("FLAG", offset, (ulonglong) -1, 0, INT_MAX32, 1),
675   HA_FOPTION_NUMBER("MAX_DIST", freq, 0, 0, INT_MAX32, 1), // BLK_INDX
676   HA_FOPTION_NUMBER("FIELD_LENGTH", fldlen, 0, 0, INT_MAX32, 1),
677   HA_FOPTION_STRING("DATE_FORMAT", dateformat),
678   HA_FOPTION_STRING("FIELD_FORMAT", fieldformat),
679   HA_FOPTION_STRING("JPATH", jsonpath),
680 	HA_FOPTION_STRING("XPATH", xmlpath),
681 	HA_FOPTION_STRING("SPECIAL", special),
682 	HA_FOPTION_ENUM("DISTRIB", opt, "scattered,clustered,sorted", 0),
683   HA_FOPTION_END
684 };
685 
686 /*
687   CREATE TABLE option list (index options)
688 
689   These can be specified in the CREATE TABLE per index:
690   CREATE TABLE ( field ..., .., INDEX .... *here*, ... )
691 */
692 ha_create_table_option connect_index_option_list[]=
693 {
694   HA_IOPTION_BOOL("DYNAM", dynamic, 0),
695   HA_IOPTION_BOOL("MAPPED", mapped, 0),
696   HA_IOPTION_END
697 };
698 
699 /***********************************************************************/
700 /*  Push G->Message as a MySQL warning.                                */
701 /***********************************************************************/
PushWarning(PGLOBAL g,PTDB tdbp,int level)702 bool PushWarning(PGLOBAL g, PTDB tdbp, int level)
703 {
704   PHC    phc;
705   THD   *thd;
706   MYCAT *cat= (MYCAT*)tdbp->GetDef()->GetCat();
707 
708   if (!cat || !(phc= cat->GetHandler()) || !phc->GetTable() ||
709       !(thd= (phc->GetTable())->in_use))
710     return true;
711 
712   PushWarning(g, thd, level);
713   return false;
714 } // end of PushWarning
715 
PushWarning(PGLOBAL g,THD * thd,int level)716 void PushWarning(PGLOBAL g, THD *thd, int level)
717   {
718   if (thd) {
719     Sql_condition::enum_warning_level wlvl;
720 
721     wlvl= (Sql_condition::enum_warning_level)level;
722     push_warning(thd, wlvl, 0, g->Message);
723   } else
724     htrc("%s\n", g->Message);
725 
726   } // end of PushWarning
727 
728 #ifdef HAVE_PSI_INTERFACE
729 static PSI_mutex_key con_key_mutex_CONNECT_SHARE_mutex;
730 
731 static PSI_mutex_info all_connect_mutexes[]=
732 {
733   { &con_key_mutex_CONNECT_SHARE_mutex, "CONNECT_SHARE::mutex", 0}
734 };
735 
init_connect_psi_keys()736 static void init_connect_psi_keys()
737 {
738   const char* category= "connect";
739   int count;
740 
741   if (PSI_server == NULL)
742     return;
743 
744   count= array_elements(all_connect_mutexes);
745   PSI_server->register_mutex(category, all_connect_mutexes, count);
746 }
747 #else
init_connect_psi_keys()748 static void init_connect_psi_keys() {}
749 #endif
750 
751 
PlugSetPath(LPSTR to,LPCSTR name,LPCSTR dir)752 DllExport LPCSTR PlugSetPath(LPSTR to, LPCSTR name, LPCSTR dir)
753 {
754   const char *res= PlugSetPath(to, mysql_data_home, name, dir);
755   return res;
756 }
757 
758 
759 /**
760   @brief
761   If frm_error() is called then we will use this to determine
762   the file extensions that exist for the storage engine. This is also
763   used by the default rename_table and delete_table method in
764   handler.cc.
765 
766   For engines that have two file name extensions (separate meta/index file
767   and data file), the order of elements is relevant. First element of engine
768   file name extensions array should be meta/index file extension. Second
769   element - data file extension. This order is assumed by
770   prepare_for_repair() when REPAIR TABLE ... USE_FRM is issued.
771 
772   @see
773   rename_table method in handler.cc and
774   delete_table method in handler.cc
775 */
776 static const char *ha_connect_exts[]= {
777   ".dos", ".fix", ".csv", ".bin", ".fmt", ".dbf", ".xml", ".json", ".ini",
778   ".vec", ".dnx", ".fnx", ".bnx", ".vnx", ".dbx", ".dop", ".fop", ".bop",
779   ".vop", NULL};
780 
781 /**
782   @brief
783   Plugin initialization
784 */
connect_init_func(void * p)785 static int connect_init_func(void *p)
786 {
787   DBUG_ENTER("connect_init_func");
788 
789 // added from Sergei mail
790 #if 0 // (defined(LINUX))
791   Dl_info dl_info;
792   if (dladdr(&connect_hton, &dl_info))
793   {
794     if (dlopen(dl_info.dli_fname, RTLD_NOLOAD | RTLD_NOW | RTLD_GLOBAL) == 0)
795     {
796       sql_print_information("CONNECT: dlopen() failed, OEM table type is not supported");
797       sql_print_information("CONNECT: %s", dlerror());
798     }
799   }
800   else
801   {
802     sql_print_information("CONNECT: dladdr() failed, OEM table type is not supported");
803     sql_print_information("CONNECT: %s", dlerror());
804   }
805 #endif   // 0 (LINUX)
806 
807 #if defined(_WIN32)
808   sql_print_information("CONNECT: %s", compver);
809 #else   // !_WIN32
810   sql_print_information("CONNECT: %s", version);
811 #endif  // !_WIN32
812 	pthread_mutex_init(&parmut, NULL);
813 	pthread_mutex_init(&usrmut, NULL);
814 	pthread_mutex_init(&tblmut, NULL);
815 
816 #if defined(LIBXML2_SUPPORT)
817   XmlInitParserLib();
818 #endif   // LIBXML2_SUPPORT
819 
820 #if 0  //defined(CMGO_SUPPORT)
821 	mongo_init(true);
822 #endif   // CMGO_SUPPORT
823 
824   init_connect_psi_keys();
825 
826   connect_hton= (handlerton *)p;
827   connect_hton->create= connect_create_handler;
828   connect_hton->flags= HTON_TEMPORARY_NOT_SUPPORTED;
829   connect_hton->table_options= connect_table_option_list;
830   connect_hton->field_options= connect_field_option_list;
831   connect_hton->index_options= connect_index_option_list;
832   connect_hton->tablefile_extensions= ha_connect_exts;
833   connect_hton->discover_table_structure= connect_assisted_discovery;
834 
835   if (trace(128))
836     sql_print_information("connect_init: hton=%p", p);
837 
838   DTVAL::SetTimeShift();      // Initialize time zone shift once for all
839   BINCOL::SetEndian();        // Initialize host endian setting
840 #if defined(JAVA_SUPPORT)
841 	JAVAConn::SetJVM();
842 #endif   // JAVA_SUPPORT
843   DBUG_RETURN(0);
844 } // end of connect_init_func
845 
846 
847 /**
848   @brief
849   Plugin clean up
850 */
connect_done_func(void *)851 int connect_done_func(void *)
852 {
853   int error= 0;
854   PCONNECT pc, pn;
855   DBUG_ENTER("connect_done_func");
856 
857 #ifdef LIBXML2_SUPPORT
858   XmlCleanupParserLib();
859 #endif // LIBXML2_SUPPORT
860 
861 #if defined(CMGO_SUPPORT)
862 	CMgoConn::mongo_init(false);
863 #endif   // CMGO_SUPPORT
864 
865 #ifdef JAVA_SUPPORT
866 	JAVAConn::ResetJVM();
867 #endif // JAVA_SUPPORT
868 
869 #if	!defined(_WIN32)
870 	PROFILE_End();
871 #endif  // !_WIN32
872 
873 	pthread_mutex_lock(&usrmut);
874 	for (pc= user_connect::to_users; pc; pc= pn) {
875     if (pc->g)
876       PlugCleanup(pc->g, true);
877 
878     pn= pc->next;
879     delete pc;
880     } // endfor pc
881 
882 	pthread_mutex_unlock(&usrmut);
883 
884 	pthread_mutex_destroy(&usrmut);
885 	pthread_mutex_destroy(&parmut);
886 	pthread_mutex_destroy(&tblmut);
887 	connect_hton= NULL;
888   DBUG_RETURN(error);
889 } // end of connect_done_func
890 
891 
892 /**
893   @brief
894   Example of simple lock controls. The "share" it creates is a
895   structure we will pass to each CONNECT handler. Do you have to have
896   one of these? Well, you have pieces that are used for locking, and
897   they are needed to function.
898 */
899 
get_share()900 CONNECT_SHARE *ha_connect::get_share()
901 {
902   CONNECT_SHARE *tmp_share;
903 
904   lock_shared_ha_data();
905 
906   if (!(tmp_share= static_cast<CONNECT_SHARE*>(get_ha_share_ptr()))) {
907     tmp_share= new CONNECT_SHARE;
908     if (!tmp_share)
909       goto err;
910     mysql_mutex_init(con_key_mutex_CONNECT_SHARE_mutex,
911                      &tmp_share->mutex, MY_MUTEX_INIT_FAST);
912     set_ha_share_ptr(static_cast<Handler_share*>(tmp_share));
913     } // endif tmp_share
914 
915  err:
916   unlock_shared_ha_data();
917   return tmp_share;
918 } // end of get_share
919 
920 
connect_create_handler(handlerton * hton,TABLE_SHARE * table,MEM_ROOT * mem_root)921 static handler* connect_create_handler(handlerton *hton,
922                                    TABLE_SHARE *table,
923                                    MEM_ROOT *mem_root)
924 {
925   handler *h= new (mem_root) ha_connect(hton, table);
926 
927   if (trace(128))
928     htrc("New CONNECT %p, table: %.*s\n", h,
929           table ? table->table_name.length : 6,
930           table ? table->table_name.str : "<null>");
931 
932   return h;
933 } // end of connect_create_handler
934 
935 /****************************************************************************/
936 /*  ha_connect constructor.                                                 */
937 /****************************************************************************/
ha_connect(handlerton * hton,TABLE_SHARE * table_arg)938 ha_connect::ha_connect(handlerton *hton, TABLE_SHARE *table_arg)
939        :handler(hton, table_arg)
940 {
941   hnum= ++num;
942   xp= (table) ? GetUser(ha_thd(), NULL) : NULL;
943   if (xp)
944     xp->SetHandler(this);
945 #if defined(_WIN32)
946   datapath= ".\\";
947 #else   // !_WIN32
948   datapath= "./";
949 #endif  // !_WIN32
950   tdbp= NULL;
951   sdvalin1= sdvalin2= sdvalin3= sdvalin4= NULL;
952   sdvalout= NULL;
953   xmod= MODE_ANY;
954   istable= false;
955   memset(partname, 0, sizeof(partname));
956   bzero((char*) &xinfo, sizeof(XINFO));
957   valid_info= false;
958   valid_query_id= 0;
959   creat_query_id= (table && table->in_use) ? table->in_use->query_id : 0;
960   stop= false;
961   alter= false;
962   mrr= false;
963   nox= true;
964   abort= false;
965   indexing= -1;
966   locked= 0;
967   part_id= NULL;
968   data_file_name= NULL;
969   index_file_name= NULL;
970   enable_activate_all_index= 0;
971   int_table_flags= (HA_NO_TRANSACTIONS | HA_NO_PREFIX_CHAR_KEYS);
972   ref_length= sizeof(int);
973   share= NULL;
974   tshp= NULL;
975 } // end of ha_connect constructor
976 
977 
978 /****************************************************************************/
979 /*  ha_connect destructor.                                                  */
980 /****************************************************************************/
~ha_connect(void)981 ha_connect::~ha_connect(void)
982 {
983   if (trace(128))
984     htrc("Delete CONNECT %p, table: %.*s, xp=%p count=%d\n", this,
985                          table ? table->s->table_name.length : 6,
986                          table ? table->s->table_name.str : "<null>",
987                          xp, xp ? xp->count : 0);
988 
989 	PopUser(xp);
990 } // end of ha_connect destructor
991 
992 
993 /****************************************************************************/
994 /*  Check whether this user can be removed.                                 */
995 /****************************************************************************/
PopUser(PCONNECT xp)996 static void PopUser(PCONNECT xp)
997 {
998 	if (xp) {
999 		pthread_mutex_lock(&usrmut);
1000 		xp->count--;
1001 
1002 		if (!xp->count) {
1003 			PCONNECT p;
1004 
1005 			for (p= user_connect::to_users; p; p= p->next)
1006 			  if (p == xp)
1007 				  break;
1008 
1009 		  if (p) {
1010 			  if (p->next)
1011 				  p->next->previous= p->previous;
1012 
1013 			  if (p->previous)
1014 				  p->previous->next= p->next;
1015 			  else
1016 				  user_connect::to_users= p->next;
1017 
1018 		  } // endif p
1019 
1020 			PlugCleanup(xp->g, true);
1021 			delete xp;
1022 		} // endif count
1023 
1024 		pthread_mutex_unlock(&usrmut);
1025 	} // endif xp
1026 
1027 } // end of PopUser
1028 
1029 
1030 /****************************************************************************/
1031 /*  Get a pointer to the user of this handler.                              */
1032 /****************************************************************************/
GetUser(THD * thd,PCONNECT xp)1033 static PCONNECT GetUser(THD *thd, PCONNECT xp)
1034 {
1035 	if (!thd)
1036     return NULL;
1037 
1038 	if (xp) {
1039 		if (thd == xp->thdp)
1040 			return xp;
1041 
1042 		PopUser(xp);		// Avoid memory leak
1043 	} // endif xp
1044 
1045 	pthread_mutex_lock(&usrmut);
1046 
1047 	for (xp= user_connect::to_users; xp; xp= xp->next)
1048     if (thd == xp->thdp)
1049       break;
1050 
1051 	if (xp)
1052 		xp->count++;
1053 
1054 	pthread_mutex_unlock(&usrmut);
1055 
1056 	if (!xp) {
1057 		xp= new user_connect(thd);
1058 
1059 		if (xp->user_init()) {
1060 			delete xp;
1061 			xp= NULL;
1062 		} // endif user_init
1063 
1064 	}	// endif xp
1065 
1066   //} else
1067   //  xp->count++;
1068 
1069   return xp;
1070 } // end of GetUser
1071 
1072 /****************************************************************************/
1073 /*  Get the global pointer of the user of this handler.                     */
1074 /****************************************************************************/
GetPlug(THD * thd,PCONNECT & lxp)1075 static PGLOBAL GetPlug(THD *thd, PCONNECT& lxp)
1076 {
1077   lxp= GetUser(thd, lxp);
1078   return (lxp) ? lxp->g : NULL;
1079 } // end of GetPlug
1080 
1081 /****************************************************************************/
1082 /*  Get the implied table type.                                             */
1083 /****************************************************************************/
GetRealType(PTOS pos)1084 TABTYPE ha_connect::GetRealType(PTOS pos)
1085 {
1086   TABTYPE type= TAB_UNDEF;
1087 
1088   if (pos || (pos= GetTableOptionStruct())) {
1089     type= GetTypeID(pos->type);
1090 
1091     if (type == TAB_UNDEF && !pos->http)
1092       type= pos->srcdef ? TAB_MYSQL : pos->tabname ? TAB_PRX : TAB_DOS;
1093 #if defined(REST_SUPPORT)
1094 		else if (pos->http)
1095 			switch (type) {
1096 				case TAB_JSON:
1097 				case TAB_XML:
1098 				case TAB_CSV:
1099         case TAB_UNDEF:
1100           type = TAB_REST;
1101 					break;
1102 				case TAB_REST:
1103 					type = TAB_NIY;
1104 					break;
1105 				default:
1106 					break;
1107 			}	// endswitch type
1108 #endif   // REST_SUPPORT
1109 
1110   } // endif pos
1111 
1112   return type;
1113 } // end of GetRealType
1114 
1115 /** @brief
1116   The name of the index type that will be used for display.
1117   Don't implement this method unless you really have indexes.
1118  */
index_type(uint inx)1119 const char *ha_connect::index_type(uint inx)
1120 {
1121   switch (GetIndexType(GetRealType())) {
1122     case 1:
1123       if (table_share)
1124         return (GetIndexOption(&table_share->key_info[inx], "Dynamic"))
1125              ? "KINDEX" : "XINDEX";
1126       else
1127         return "XINDEX";
1128 
1129     case 2: return "REMOTE";
1130     case 3: return "VIRTUAL";
1131     } // endswitch
1132 
1133   return "Unknown";
1134 } // end of index_type
1135 
1136 /** @brief
1137   This is a bitmap of flags that indicates how the storage engine
1138   implements indexes. The current index flags are documented in
1139   handler.h. If you do not implement indexes, just return zero here.
1140 
1141     @details
1142   part is the key part to check. First key part is 0.
1143   If all_parts is set, MySQL wants to know the flags for the combined
1144   index, up to and including 'part'.
1145 */
1146 //ong ha_connect::index_flags(uint inx, uint part, bool all_parts) const
index_flags(uint,uint,bool) const1147 ulong ha_connect::index_flags(uint, uint, bool) const
1148 {
1149   ulong       flags= HA_READ_NEXT | HA_READ_RANGE |
1150                      HA_KEYREAD_ONLY | HA_KEY_SCAN_NOT_ROR;
1151   ha_connect *hp= (ha_connect*)this;
1152   PTOS        pos= hp->GetTableOptionStruct();
1153 
1154   if (pos) {
1155     TABTYPE type= hp->GetRealType(pos);
1156 
1157     switch (GetIndexType(type)) {
1158       case 1: flags|= (HA_READ_ORDER | HA_READ_PREV); break;
1159       case 2: flags|= HA_READ_AFTER_KEY;              break;
1160       } // endswitch
1161 
1162     } // endif pos
1163 
1164   return flags;
1165 } // end of index_flags
1166 
1167 /** @brief
1168   This is a list of flags that indicate what functionality the storage
1169   engine implements. The current table flags are documented in handler.h
1170 */
table_flags() const1171 ulonglong ha_connect::table_flags() const
1172 {
1173   ulonglong   flags= HA_CAN_VIRTUAL_COLUMNS | HA_REC_NOT_IN_SEQ |
1174                      HA_NO_AUTO_INCREMENT | HA_NO_PREFIX_CHAR_KEYS |
1175                      HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE |
1176                      HA_PARTIAL_COLUMN_READ | HA_FILE_BASED |
1177 //                   HA_NULL_IN_KEY |    not implemented yet
1178 //                   HA_FAST_KEY_READ |  causes error when sorting (???)
1179                      HA_NO_TRANSACTIONS | HA_DUPLICATE_KEY_NOT_IN_ORDER |
1180                      HA_NO_BLOBS | HA_MUST_USE_TABLE_CONDITION_PUSHDOWN |
1181                      HA_REUSES_FILE_NAMES;
1182   ha_connect *hp= (ha_connect*)this;
1183   PTOS        pos= hp->GetTableOptionStruct();
1184 
1185   if (pos) {
1186     TABTYPE type= hp->GetRealType(pos);
1187 
1188     if (IsFileType(type))
1189       flags|= HA_FILE_BASED;
1190 
1191     if (IsExactType(type))
1192       flags|= (HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT);
1193 
1194     // No data change on ALTER for outward tables
1195     if (!IsFileType(type) || hp->FileExists(pos->filename, true))
1196       flags|= HA_NO_COPY_ON_ALTER;
1197 
1198     } // endif pos
1199 
1200   return flags;
1201 } // end of table_flags
1202 
1203 /****************************************************************************/
1204 /*  Return the value of an option specified in an option list.              */
1205 /****************************************************************************/
GetListOption(PGLOBAL g,PCSZ opname,PCSZ oplist,PCSZ def)1206 PCSZ GetListOption(PGLOBAL g, PCSZ opname, PCSZ oplist, PCSZ def)
1207 {
1208   if (!oplist)
1209     return (char*)def;
1210 
1211 	char  key[16], val[256];
1212 	char *pv, *pn, *pk= (char*)oplist;
1213 	PCSZ  opval= def;
1214 	int   n;
1215 
1216 	while (*pk == ' ')
1217 		pk++;
1218 
1219 	for (; pk; pk= pn) {
1220 		pn= strchr(pk, ',');
1221 		pv= strchr(pk, '=');
1222 
1223 		if (pv && (!pn || pv < pn)) {
1224 			n= MY_MIN(static_cast<size_t>(pv - pk), sizeof(key) - 1);
1225 			memcpy(key, pk, n);
1226 
1227 			while (n && key[n - 1] == ' ')
1228 				n--;
1229 
1230 			key[n]= 0;
1231 
1232 			while (*(++pv) == ' ');
1233 
1234 			n= MY_MIN((pn ? pn - pv : strlen(pv)), sizeof(val) - 1);
1235 			memcpy(val, pv, n);
1236 
1237 			while (n && val[n - 1] == ' ')
1238 				n--;
1239 
1240 			val[n]= 0;
1241 		} else {
1242 			n= MY_MIN((pn ? pn - pk : strlen(pk)), sizeof(key) - 1);
1243 			memcpy(key, pk, n);
1244 
1245 			while (n && key[n - 1] == ' ')
1246 				n--;
1247 
1248 			key[n]= 0;
1249 			val[0]= 0;
1250 		} // endif pv
1251 
1252 		if (!stricmp(opname, key)) {
1253 			opval= PlugDup(g, val);
1254 			break;
1255 		} else if (!pn)
1256 			break;
1257 
1258 		while (*(++pn) == ' ');
1259 	} // endfor pk
1260 
1261   return opval;
1262 } // end of GetListOption
1263 
1264 /****************************************************************************/
1265 /*  Return the value of a string option or NULL if not specified.           */
1266 /****************************************************************************/
GetStringTableOption(PGLOBAL g,PTOS options,PCSZ opname,PCSZ sdef)1267 PCSZ GetStringTableOption(PGLOBAL g, PTOS options, PCSZ opname, PCSZ sdef)
1268 {
1269 	PCSZ opval= NULL;
1270 
1271   if (!options)
1272     return sdef;
1273   else if (!stricmp(opname, "Type"))
1274     opval= options->type;
1275   else if (!stricmp(opname, "Filename"))
1276     opval= options->filename;
1277   else if (!stricmp(opname, "Optname"))
1278     opval= options->optname;
1279   else if (!stricmp(opname, "Tabname"))
1280     opval= options->tabname;
1281   else if (!stricmp(opname, "Tablist"))
1282     opval= options->tablist;
1283   else if (!stricmp(opname, "Database") ||
1284            !stricmp(opname, "DBname"))
1285     opval= options->dbname;
1286   else if (!stricmp(opname, "Separator"))
1287     opval= options->separator;
1288   else if (!stricmp(opname, "Qchar"))
1289     opval= options->qchar;
1290   else if (!stricmp(opname, "Module"))
1291     opval= options->module;
1292   else if (!stricmp(opname, "Subtype"))
1293     opval= options->subtype;
1294   else if (!stricmp(opname, "Catfunc"))
1295     opval= options->catfunc;
1296   else if (!stricmp(opname, "Srcdef"))
1297     opval= options->srcdef;
1298   else if (!stricmp(opname, "Colist"))
1299     opval= options->colist;
1300 	else if (!stricmp(opname, "Filter"))
1301 		opval= options->filter;
1302 	else if (!stricmp(opname, "Data_charset"))
1303     opval= options->data_charset;
1304 	else if (!stricmp(opname, "Http") || !stricmp(opname, "URL"))
1305 		opval= options->http;
1306 	else if (!stricmp(opname, "Uri"))
1307 		opval= options->uri;
1308 
1309   if (!opval && options->oplist)
1310     opval= GetListOption(g, opname, options->oplist);
1311 
1312   return opval ? (char*)opval : sdef;
1313 } // end of GetStringTableOption
1314 
1315 /****************************************************************************/
1316 /*  Return the value of a Boolean option or bdef if not specified.          */
1317 /****************************************************************************/
GetBooleanTableOption(PGLOBAL g,PTOS options,PCSZ opname,bool bdef)1318 bool GetBooleanTableOption(PGLOBAL g, PTOS options, PCSZ opname, bool bdef)
1319 {
1320   bool opval= bdef;
1321 	PCSZ pv;
1322 
1323   if (!options)
1324     return bdef;
1325   else if (!stricmp(opname, "Mapped"))
1326     opval= options->mapped;
1327   else if (!stricmp(opname, "Huge"))
1328     opval= options->huge;
1329   else if (!stricmp(opname, "Split"))
1330     opval= options->split;
1331   else if (!stricmp(opname, "Readonly"))
1332     opval= options->readonly;
1333   else if (!stricmp(opname, "SepIndex"))
1334     opval= options->sepindex;
1335   else if (!stricmp(opname, "Header"))
1336     opval= (options->header != 0);   // Is Boolean for some table types
1337 	else if (!stricmp(opname, "Zipped"))
1338 		opval= options->zipped;
1339 	else if (options->oplist)
1340     if ((pv= GetListOption(g, opname, options->oplist)))
1341       opval= (!*pv || *pv == 'y' || *pv == 'Y' || atoi(pv) != 0);
1342 
1343   return opval;
1344 } // end of GetBooleanTableOption
1345 
1346 /****************************************************************************/
1347 /*  Return the value of an integer option or NO_IVAL if not specified.      */
1348 /****************************************************************************/
GetIntegerTableOption(PGLOBAL g,PTOS options,PCSZ opname,int idef)1349 int GetIntegerTableOption(PGLOBAL g, PTOS options, PCSZ opname, int idef)
1350 {
1351   ulonglong opval= (ulonglong) NO_IVAL;
1352 
1353   if (!options)
1354     return idef;
1355   else if (!stricmp(opname, "Lrecl"))
1356     opval= options->lrecl;
1357   else if (!stricmp(opname, "Elements"))
1358     opval= options->elements;
1359   else if (!stricmp(opname, "Multiple"))
1360     opval= options->multiple;
1361   else if (!stricmp(opname, "Header"))
1362     opval= options->header;
1363   else if (!stricmp(opname, "Quoted"))
1364     opval= options->quoted;
1365   else if (!stricmp(opname, "Ending"))
1366     opval= options->ending;
1367   else if (!stricmp(opname, "Compressed"))
1368     opval= (options->compressed);
1369 
1370   if ((ulonglong) opval == (ulonglong)NO_IVAL) {
1371 		PCSZ pv;
1372 
1373 		if ((pv = GetListOption(g, opname, options->oplist))) {
1374 			// opval = CharToNumber((char*)pv, strlen(pv), ULONGLONG_MAX, false);
1375 			return atoi(pv);
1376 		} else
1377       return idef;
1378 
1379     } // endif opval
1380 
1381   return (int)opval;
1382 } // end of GetIntegerTableOption
1383 
1384 /****************************************************************************/
1385 /*  Return the table option structure.                                      */
1386 /****************************************************************************/
GetTableOptionStruct(TABLE_SHARE * s)1387 PTOS ha_connect::GetTableOptionStruct(TABLE_SHARE *s)
1388 {
1389   TABLE_SHARE *tsp= (tshp) ? tshp : (s) ? s : table_share;
1390 
1391 	return (tsp && (!tsp->db_plugin ||
1392 		              !stricmp(plugin_name(tsp->db_plugin)->str, "connect") ||
1393 									!stricmp(plugin_name(tsp->db_plugin)->str, "partition")))
1394 									? tsp->option_struct : NULL;
1395 } // end of GetTableOptionStruct
1396 
1397 /****************************************************************************/
1398 /*  Return the string eventually formatted with partition name.             */
1399 /****************************************************************************/
GetRealString(PCSZ s)1400 char *ha_connect::GetRealString(PCSZ s)
1401 {
1402   char *sv;
1403 
1404   if (IsPartitioned() && s && *partname) {
1405     sv= (char*)PlugSubAlloc(xp->g, NULL, 0);
1406     sprintf(sv, s, partname);
1407     PlugSubAlloc(xp->g, NULL, strlen(sv) + 1);
1408   } else
1409     sv= (char*)s;
1410 
1411   return sv;
1412 } // end of GetRealString
1413 
1414 /****************************************************************************/
1415 /*  Return the value of a string option or sdef if not specified.           */
1416 /****************************************************************************/
GetStringOption(PCSZ opname,PCSZ sdef)1417 PCSZ ha_connect::GetStringOption(PCSZ opname, PCSZ sdef)
1418 {
1419 	PCSZ opval= NULL;
1420   PTOS options= GetTableOptionStruct();
1421 
1422   if (!stricmp(opname, "Connect")) {
1423     LEX_CSTRING cnc= (tshp) ? tshp->connect_string
1424                            : table->s->connect_string;
1425 
1426     if (cnc.length)
1427       opval= strz(xp->g, cnc);
1428 		else
1429 			opval= GetListOption(xp->g, opname, options->oplist);
1430 
1431 	} else if (!stricmp(opname, "Query_String")) {
1432 //  This escapes everything and returns a wrong query
1433 //	opval= thd_query_string(table->in_use)->str;
1434 		opval= (PCSZ)PlugSubAlloc(xp->g, NULL,
1435 			thd_query_string(table->in_use)->length + 1);
1436 		strcpy((char*)opval, thd_query_string(table->in_use)->str);
1437 //	sprintf((char*)opval, "%s", thd_query_string(table->in_use)->str);
1438 	} else if (!stricmp(opname, "Partname"))
1439     opval= partname;
1440   else if (!stricmp(opname, "Table_charset")) {
1441     const CHARSET_INFO *chif= (tshp) ? tshp->table_charset
1442                                      : table->s->table_charset;
1443 
1444     if (chif)
1445       opval= (char*)chif->csname;
1446 
1447   } else
1448     opval= GetStringTableOption(xp->g, options, opname, NULL);
1449 
1450   if (opval && (!stricmp(opname, "connect")
1451              || !stricmp(opname, "tabname")
1452              || !stricmp(opname, "filename")
1453 						 || !stricmp(opname, "optname")
1454 						 || !stricmp(opname, "entry")))
1455 						 opval= GetRealString(opval);
1456 
1457   if (!opval) {
1458     if (sdef && !strcmp(sdef, "*")) {
1459       // Return the handler default value
1460       if (!stricmp(opname, "Dbname") || !stricmp(opname, "Database"))
1461         opval= (char*)GetDBName(NULL);    // Current database
1462       else if (!stricmp(opname, "Type"))  // Default type
1463         opval= (!options) ? NULL :
1464                (options->srcdef)  ? (char*)"MYSQL" :
1465                (options->tabname) ? (char*)"PROXY" : (char*)"DOS";
1466       else if (!stricmp(opname, "User"))  // Connected user
1467         opval= (char *) "root";
1468       else if (!stricmp(opname, "Host"))  // Connected user host
1469         opval= (char *) "localhost";
1470       else
1471         opval= sdef;                      // Caller default
1472 
1473     } else
1474       opval= sdef;                        // Caller default
1475 
1476     } // endif !opval
1477 
1478   return opval;
1479 } // end of GetStringOption
1480 
1481 /****************************************************************************/
1482 /*  Return the value of a Boolean option or bdef if not specified.          */
1483 /****************************************************************************/
GetBooleanOption(PCSZ opname,bool bdef)1484 bool ha_connect::GetBooleanOption(PCSZ opname, bool bdef)
1485 {
1486   bool  opval;
1487   PTOS  options= GetTableOptionStruct();
1488 
1489   if (!stricmp(opname, "View"))
1490     opval= (tshp) ? tshp->is_view : table_share->is_view;
1491   else
1492     opval= GetBooleanTableOption(xp->g, options, opname, bdef);
1493 
1494   return opval;
1495 } // end of GetBooleanOption
1496 
1497 /****************************************************************************/
1498 /*  Set the value of the opname option (does not work for oplist options)   */
1499 /*  Currently used only to set the Sepindex value.                          */
1500 /****************************************************************************/
SetBooleanOption(PCSZ opname,bool b)1501 bool ha_connect::SetBooleanOption(PCSZ opname, bool b)
1502 {
1503   PTOS options= GetTableOptionStruct();
1504 
1505   if (!options)
1506     return true;
1507 
1508   if (!stricmp(opname, "SepIndex"))
1509     options->sepindex= b;
1510   else
1511     return true;
1512 
1513   return false;
1514 } // end of SetBooleanOption
1515 
1516 /****************************************************************************/
1517 /*  Return the value of an integer option or NO_IVAL if not specified.      */
1518 /****************************************************************************/
GetIntegerOption(PCSZ opname)1519 int ha_connect::GetIntegerOption(PCSZ opname)
1520 {
1521   int          opval;
1522   PTOS         options= GetTableOptionStruct();
1523   TABLE_SHARE *tsp= (tshp) ? tshp : table_share;
1524 
1525   if (!stricmp(opname, "Avglen"))
1526     opval= (int)tsp->avg_row_length;
1527   else if (!stricmp(opname, "Estimate"))
1528     opval= (int)tsp->max_rows;
1529   else
1530     opval= GetIntegerTableOption(xp->g, options, opname, NO_IVAL);
1531 
1532   return opval;
1533 } // end of GetIntegerOption
1534 
1535 /****************************************************************************/
1536 /*  Set the value of the opname option (does not work for oplist options)   */
1537 /*  Currently used only to set the Lrecl value.                             */
1538 /****************************************************************************/
SetIntegerOption(PCSZ opname,int n)1539 bool ha_connect::SetIntegerOption(PCSZ opname, int n)
1540 {
1541   PTOS options= GetTableOptionStruct();
1542 
1543   if (!options)
1544     return true;
1545 
1546   if (!stricmp(opname, "Lrecl"))
1547     options->lrecl= n;
1548   else if (!stricmp(opname, "Elements"))
1549     options->elements= n;
1550 //else if (!stricmp(opname, "Estimate"))
1551 //  options->estimate= n;
1552   else if (!stricmp(opname, "Multiple"))
1553     options->multiple= n;
1554   else if (!stricmp(opname, "Header"))
1555     options->header= n;
1556   else if (!stricmp(opname, "Quoted"))
1557     options->quoted= n;
1558   else if (!stricmp(opname, "Ending"))
1559     options->ending= n;
1560   else if (!stricmp(opname, "Compressed"))
1561     options->compressed= n;
1562   else
1563     return true;
1564 //else if (options->oplist)
1565 //  SetListOption(opname, options->oplist, n);
1566 
1567   return false;
1568 } // end of SetIntegerOption
1569 
1570 /****************************************************************************/
1571 /*  Return a field option structure.                                        */
1572 /****************************************************************************/
GetFieldOptionStruct(Field * fdp)1573 PFOS ha_connect::GetFieldOptionStruct(Field *fdp)
1574 {
1575   return fdp->option_struct;
1576 } // end of GetFildOptionStruct
1577 
1578 /****************************************************************************/
1579 /*  Returns the column description structure used to make the column.       */
1580 /****************************************************************************/
GetColumnOption(PGLOBAL g,void * field,PCOLINFO pcf)1581 void *ha_connect::GetColumnOption(PGLOBAL g, void *field, PCOLINFO pcf)
1582 {
1583   const char *cp;
1584   char   *chset, v= 0;
1585   ha_field_option_struct *fop;
1586   Field*  fp;
1587   Field* *fldp;
1588 
1589   // Double test to be on the safe side
1590   if (!table)
1591     return NULL;
1592 
1593   // Find the column to describe
1594   if (field) {
1595     fldp= (Field**)field;
1596     fldp++;
1597   } else
1598     fldp= (tshp) ? tshp->field : table->field;
1599 
1600   if (!fldp || !(fp= *fldp))
1601     return NULL;
1602 
1603   // Get the CONNECT field options structure
1604   fop= GetFieldOptionStruct(fp);
1605   pcf->Flags= 0;
1606 
1607   // Now get column information
1608   pcf->Name= (char*)fp->field_name.str;
1609 	chset = (char*)fp->charset()->name;
1610 
1611   if (fop && fop->special) {
1612     pcf->Fieldfmt= (char*)fop->special;
1613     pcf->Flags= U_SPECIAL;
1614     return fldp;
1615     } // endif special
1616 
1617   pcf->Scale= 0;
1618   pcf->Opt= (fop) ? (int)fop->opt : 0;
1619 
1620 	if (fp->field_length >= 0) {
1621 		pcf->Length= fp->field_length;
1622 
1623 		// length is bytes for Connect, not characters
1624 		if (!strnicmp(chset, "utf8", 4))
1625 			pcf->Length /= 3;
1626 
1627 	} else
1628 		pcf->Length= 256;            // BLOB?
1629 
1630   pcf->Precision= pcf->Length;
1631 
1632   if (fop) {
1633     pcf->Offset= (int)fop->offset;
1634     pcf->Freq= (int)fop->freq;
1635     pcf->Datefmt= (char*)fop->dateformat;
1636 		pcf->Fieldfmt= fop->fieldformat ? (char*)fop->fieldformat
1637 			: fop->jsonpath ? (char*)fop->jsonpath : (char*)fop->xmlpath;
1638 	} else {
1639     pcf->Offset= -1;
1640     pcf->Freq= 0;
1641     pcf->Datefmt= NULL;
1642     pcf->Fieldfmt= NULL;
1643   } // endif fop
1644 
1645 	if (!strcmp(chset, "binary"))
1646 		v = 'B';		// Binary string
1647 
1648   switch (fp->type()) {
1649     case MYSQL_TYPE_BLOB:
1650     case MYSQL_TYPE_VARCHAR:
1651     case MYSQL_TYPE_VAR_STRING:
1652       pcf->Flags |= U_VAR;
1653 			// fall through
1654     default:
1655       pcf->Type= MYSQLtoPLG(fp->type(), &v);
1656       break;
1657   } // endswitch SQL type
1658 
1659   switch (pcf->Type) {
1660     case TYPE_STRING:
1661 		case TYPE_BIN:
1662 			// Do something for case
1663       cp= chset;
1664 
1665       // Find if collation name ends by _ci
1666       if (!strcmp(cp + strlen(cp) - 3, "_ci")) {
1667         pcf->Scale= 1;     // Case insensitive
1668         pcf->Opt= 0;       // Prevent index opt until it is safe
1669         } // endif ci
1670 
1671       break;
1672     case TYPE_DOUBLE:
1673       pcf->Scale= MY_MAX(MY_MIN(fp->decimals(), ((unsigned)pcf->Length - 2)), 0);
1674       break;
1675     case TYPE_DECIM:
1676       pcf->Precision= ((Field_new_decimal*)fp)->precision;
1677       pcf->Length= pcf->Precision;
1678       pcf->Scale= fp->decimals();
1679       break;
1680     case TYPE_DATE:
1681       // Field_length is only used for DATE columns
1682       if (fop && fop->fldlen)
1683         pcf->Length= (int)fop->fldlen;
1684       else {
1685         int len;
1686 
1687         if (pcf->Datefmt) {
1688           // Find the (max) length produced by the date format
1689           char    buf[256];
1690           PGLOBAL g= GetPlug(table->in_use, xp);
1691           PDTP    pdtp= MakeDateFormat(g, pcf->Datefmt, false, true, 0);
1692           struct tm datm;
1693           bzero(&datm, sizeof(datm));
1694           datm.tm_mday= 12;
1695           datm.tm_mon= 11;
1696           datm.tm_year= 112;
1697           mktime(&datm); // set other fields get proper day name
1698           len= strftime(buf, 256, pdtp->OutFmt, &datm);
1699         } else
1700           len= 0;
1701 
1702         // 11 is for signed numeric representation of the date
1703         pcf->Length= (len) ? len : 11;
1704         } // endelse
1705 
1706       // For Value setting
1707       pcf->Precision= MY_MAX(pcf->Precision, pcf->Length);
1708       break;
1709     default:
1710       break;
1711   } // endswitch type
1712 
1713   if (fp->flags & UNSIGNED_FLAG)
1714     pcf->Flags |= U_UNSIGNED;
1715 
1716   if (fp->flags & ZEROFILL_FLAG)
1717     pcf->Flags |= U_ZEROFILL;
1718 
1719   // This is used to skip null bit
1720   if (fp->real_maybe_null())
1721     pcf->Flags |= U_NULLS;
1722 
1723   // Mark virtual columns as such
1724   if (fp->vcol_info && !fp->stored_in_db)
1725     pcf->Flags |= U_VIRTUAL;
1726 
1727   pcf->Key= 0;   // Not used when called from MySQL
1728 
1729   // Get the comment if any
1730   if (fp->comment.str && fp->comment.length)
1731     pcf->Remark= strz(g, fp->comment);
1732   else
1733     pcf->Remark= NULL;
1734 
1735   return fldp;
1736 } // end of GetColumnOption
1737 
1738 /****************************************************************************/
1739 /*  Return an index option structure.                                       */
1740 /****************************************************************************/
GetIndexOptionStruct(KEY * kp)1741 PXOS ha_connect::GetIndexOptionStruct(KEY *kp)
1742 {
1743   return kp->option_struct;
1744 } // end of GetIndexOptionStruct
1745 
1746 /****************************************************************************/
1747 /*  Return a Boolean index option or false if not specified.                */
1748 /****************************************************************************/
GetIndexOption(KEY * kp,PCSZ opname)1749 bool ha_connect::GetIndexOption(KEY *kp, PCSZ opname)
1750 {
1751   bool opval= false;
1752   PXOS options= GetIndexOptionStruct(kp);
1753 
1754   if (options) {
1755     if (!stricmp(opname, "Dynamic"))
1756       opval= options->dynamic;
1757     else if (!stricmp(opname, "Mapped"))
1758       opval= options->mapped;
1759 
1760   } else if (kp->comment.str && kp->comment.length) {
1761 		PCSZ pv, oplist= strz(xp->g, kp->comment);
1762 
1763     if ((pv= GetListOption(xp->g, opname, oplist)))
1764       opval= (!*pv || *pv == 'y' || *pv == 'Y' || atoi(pv) != 0);
1765 
1766   } // endif comment
1767 
1768   return opval;
1769 } // end of GetIndexOption
1770 
1771 /****************************************************************************/
1772 /*  Returns the index description structure used to make the index.         */
1773 /****************************************************************************/
IsUnique(uint n)1774 bool ha_connect::IsUnique(uint n)
1775 {
1776   return (table->key_info[n].flags & HA_NOSAME) != 0;
1777 } // end of IsUnique
1778 
1779 /****************************************************************************/
1780 /*  Returns the index description structure used to make the index.         */
1781 /****************************************************************************/
GetIndexInfo(TABLE_SHARE * s)1782 PIXDEF ha_connect::GetIndexInfo(TABLE_SHARE *s)
1783 {
1784   char    *name, *pn;
1785   bool     unique;
1786   PIXDEF   xdp, pxd=NULL, toidx= NULL;
1787   PKPDEF   kpp, pkp;
1788   KEY      kp;
1789   PGLOBAL& g= xp->g;
1790 
1791   if (!s)
1792     s= table->s;
1793 
1794   for (int n= 0; (unsigned)n < s->keynames.count; n++) {
1795     if (trace(1))
1796       htrc("Getting created index %d info\n", n + 1);
1797 
1798     // Find the index to describe
1799     kp= s->key_info[n];
1800 
1801     // Now get index information
1802     pn= (char*)s->keynames.type_names[n];
1803     name= PlugDup(g, pn);
1804     unique= (kp.flags & 1) != 0;
1805     pkp= NULL;
1806 
1807     // Allocate the index description block
1808     xdp= new(g) INDEXDEF(name, unique, n);
1809 
1810     // Get the the key parts info
1811     for (int k= 0; (unsigned)k < kp.user_defined_key_parts; k++) {
1812       pn= (char*)kp.key_part[k].field->field_name.str;
1813       name= PlugDup(g, pn);
1814 
1815       // Allocate the key part description block
1816       kpp= new(g) KPARTDEF(name, k + 1);
1817       kpp->SetKlen(kp.key_part[k].length);
1818 
1819 #if 0             // NIY
1820     // Index on auto increment column can be an XXROW index
1821     if (kp.key_part[k].field->flags & AUTO_INCREMENT_FLAG &&
1822         kp.uder_defined_key_parts == 1) {
1823       char   *type= GetStringOption("Type", "DOS");
1824       TABTYPE typ= GetTypeID(type);
1825 
1826       xdp->SetAuto(IsTypeFixed(typ));
1827       } // endif AUTO_INCREMENT
1828 #endif // 0
1829 
1830       if (pkp)
1831         pkp->SetNext(kpp);
1832       else
1833         xdp->SetToKeyParts(kpp);
1834 
1835       pkp= kpp;
1836       } // endfor k
1837 
1838     xdp->SetNParts(kp.user_defined_key_parts);
1839     xdp->Dynamic= GetIndexOption(&kp, "Dynamic");
1840     xdp->Mapped= GetIndexOption(&kp, "Mapped");
1841 
1842     if (pxd)
1843       pxd->SetNext(xdp);
1844     else
1845       toidx= xdp;
1846 
1847     pxd= xdp;
1848     } // endfor n
1849 
1850   return toidx;
1851 } // end of GetIndexInfo
1852 
1853 /****************************************************************************/
1854 /*  Returns the index description structure used to make the index.         */
1855 /****************************************************************************/
CheckVirtualIndex(TABLE_SHARE * s)1856 bool ha_connect::CheckVirtualIndex(TABLE_SHARE *s)
1857 {
1858 
1859   char    *rid;
1860   KEY      kp;
1861   Field   *fp;
1862   PGLOBAL& g= xp->g;
1863 
1864   if (!s)
1865     s= table->s;
1866 
1867   for (int n= 0; (unsigned)n < s->keynames.count; n++) {
1868     kp= s->key_info[n];
1869 
1870     // Now get index information
1871 
1872     // Get the the key parts info
1873     for (int k= 0; (unsigned)k < kp.user_defined_key_parts; k++) {
1874       fp= kp.key_part[k].field;
1875       rid= (fp->option_struct) ? fp->option_struct->special : NULL;
1876 
1877       if (!rid || (stricmp(rid, "ROWID") && stricmp(rid, "ROWNUM"))) {
1878         strcpy(g->Message, "Invalid virtual index");
1879         return true;
1880         } // endif rowid
1881 
1882       } // endfor k
1883 
1884     } // endfor n
1885 
1886   return false;
1887 } // end of CheckVirtualIndex
1888 
IsPartitioned(void)1889 bool ha_connect::IsPartitioned(void)
1890 {
1891 #ifdef WITH_PARTITION_STORAGE_ENGINE
1892 	if (tshp)
1893     return tshp->partition_info_str_len > 0;
1894   else if (table && table->part_info)
1895     return true;
1896   else
1897 #endif
1898 		return false;
1899 
1900 } // end of IsPartitioned
1901 
GetDBName(PCSZ name)1902 PCSZ ha_connect::GetDBName(PCSZ name)
1903 {
1904   return (name) ? name : table->s->db.str;
1905 } // end of GetDBName
1906 
GetTableName(void)1907 const char *ha_connect::GetTableName(void)
1908 {
1909   const char *path= tshp ? tshp->path.str : table_share->path.str;
1910   const char *name= strrchr(path, slash);
1911   return name ? name + 1 : path;
1912 } // end of GetTableName
1913 
GetPartName(void)1914 char *ha_connect::GetPartName(void)
1915 {
1916   return (IsPartitioned()) ? partname : (char*)GetTableName();
1917 } // end of GetTableName
1918 
1919 #if 0
1920 /****************************************************************************/
1921 /*  Returns the column real or special name length of a field.              */
1922 /****************************************************************************/
1923 int ha_connect::GetColNameLen(Field *fp)
1924 {
1925   int n;
1926   PFOS fop= GetFieldOptionStruct(fp);
1927 
1928   // Now get the column name length
1929   if (fop && fop->special)
1930     n= strlen(fop->special) + 1;
1931   else
1932     n= fp->field_name.length;
1933 
1934   return n;
1935 } // end of GetColNameLen
1936 
1937 /****************************************************************************/
1938 /*  Returns the column real or special name of a field.                     */
1939 /****************************************************************************/
1940 char *ha_connect::GetColName(Field *fp)
1941 {
1942   PFOS fop= GetFieldOptionStruct(fp);
1943 
1944   return (fop && fop->special) ? fop->special : (char*)fp->field_name.str;
1945 } // end of GetColName
1946 
1947 /****************************************************************************/
1948 /*  Adds the column real or special name of a field to a string.            */
1949 /****************************************************************************/
1950 void ha_connect::AddColName(char *cp, Field *fp)
1951 {
1952   PFOS fop= GetFieldOptionStruct(fp);
1953 
1954   // Now add the column name
1955   if (fop && fop->special)
1956     // The prefix * mark the column as "special"
1957     strcat(strcpy(cp, "*"), strupr(fop->special));
1958   else
1959     strcpy(cp, fp->field_name.str);
1960 
1961 } // end of AddColName
1962 #endif // 0
1963 
1964 /***********************************************************************/
1965 /*  This function sets the current database path.                      */
1966 /***********************************************************************/
SetDataPath(PGLOBAL g,PCSZ path)1967 bool ha_connect::SetDataPath(PGLOBAL g, PCSZ path)
1968 {
1969   return (!(datapath= SetPath(g, path)));
1970 } // end of SetDataPath
1971 
1972 /****************************************************************************/
1973 /*  Get the table description block of a CONNECT table.                     */
1974 /****************************************************************************/
GetTDB(PGLOBAL g)1975 PTDB ha_connect::GetTDB(PGLOBAL g)
1976 {
1977   const char *table_name;
1978   PTDB        tp;
1979 
1980   // Double test to be on the safe side
1981   if (!g || !table)
1982     return NULL;
1983 
1984   table_name= GetTableName();
1985 
1986   if (!xp->CheckQuery(valid_query_id) && tdbp
1987                       && !stricmp(tdbp->GetName(), table_name)
1988                       && (tdbp->GetMode() == xmod
1989                        || (tdbp->GetMode() == MODE_READ && xmod == MODE_READX)
1990                        || tdbp->GetAmType() == TYPE_AM_XML)) {
1991     tp= tdbp;
1992     tp->SetMode(xmod);
1993   } else if ((tp= CntGetTDB(g, table_name, xmod, this))) {
1994     valid_query_id= xp->last_query_id;
1995 //  tp->SetMode(xmod);
1996   } else
1997     htrc("GetTDB: %s\n", g->Message);
1998 
1999   return tp;
2000 } // end of GetTDB
2001 
2002 /****************************************************************************/
2003 /*  Open a CONNECT table, restricting column list if cols is true.          */
2004 /****************************************************************************/
OpenTable(PGLOBAL g,bool del)2005 int ha_connect::OpenTable(PGLOBAL g, bool del)
2006 {
2007   bool  rc= false;
2008   char *c1= NULL, *c2=NULL;
2009 
2010   // Double test to be on the safe side
2011   if (!g || !table) {
2012     htrc("OpenTable logical error; g=%p table=%p\n", g, table);
2013     return HA_ERR_INITIALIZATION;
2014     } // endif g
2015 
2016   if (!(tdbp= GetTDB(g)))
2017     return RC_FX;
2018   else if (tdbp->IsReadOnly())
2019     switch (xmod) {
2020       case MODE_WRITE:
2021       case MODE_INSERT:
2022       case MODE_UPDATE:
2023       case MODE_DELETE:
2024         strcpy(g->Message, MSG(READ_ONLY));
2025         return HA_ERR_TABLE_READONLY;
2026       default:
2027         break;
2028       } // endswitch xmode
2029 
2030 	// g->More is 1 when executing commands from triggers
2031   if (!g->More && (xmod != MODE_INSERT
2032 		           || tdbp->GetAmType() == TYPE_AM_MYSQL
2033                || tdbp->GetAmType() == TYPE_AM_ODBC
2034 							 || tdbp->GetAmType() == TYPE_AM_JDBC)) {
2035 		// Get the list of used fields (columns)
2036     char        *p;
2037     unsigned int k1, k2, n1, n2;
2038     Field*      *field;
2039     Field*       fp;
2040     MY_BITMAP   *map= (xmod == MODE_INSERT) ? table->write_set : table->read_set;
2041     MY_BITMAP   *ump= (xmod == MODE_UPDATE) ? table->write_set : NULL;
2042 
2043     k1= k2= 0;
2044     n1= n2= 1;         // 1 is space for final null character
2045 
2046     for (field= table->field; (fp= *field); field++) {
2047       if (bitmap_is_set(map, fp->field_index)) {
2048         n1+= (fp->field_name.length + 1);
2049         k1++;
2050         } // endif
2051 
2052       if (ump && bitmap_is_set(ump, fp->field_index)) {
2053         n2+= (fp->field_name.length + 1);
2054         k2++;
2055         } // endif
2056 
2057       } // endfor field
2058 
2059     if (k1) {
2060       p= c1= (char*)PlugSubAlloc(g, NULL, n1);
2061 
2062       for (field= table->field; (fp= *field); field++)
2063         if (bitmap_is_set(map, fp->field_index)) {
2064           strcpy(p, fp->field_name.str);
2065           p+= (fp->field_name.length + 1);
2066           } // endif used field
2067 
2068       *p= '\0';          // mark end of list
2069       } // endif k1
2070 
2071     if (k2) {
2072       p= c2= (char*)PlugSubAlloc(g, NULL, n2);
2073 
2074       for (field= table->field; (fp= *field); field++)
2075         if (bitmap_is_set(ump, fp->field_index)) {
2076           strcpy(p, fp->field_name.str);
2077 
2078           if (part_id && bitmap_is_set(part_id, fp->field_index)) {
2079             // Trying to update a column used for partitioning
2080             // This cannot be currently done because it may require
2081             // a row to be moved in another partition.
2082             sprintf(g->Message,
2083               "Cannot update column %s because it is used for partitioning",
2084               p);
2085             return HA_ERR_INTERNAL_ERROR;
2086             } // endif part_id
2087 
2088           p+= (strlen(p) + 1);
2089           } // endif used field
2090 
2091       *p= '\0';          // mark end of list
2092       } // endif k2
2093 
2094     } // endif xmod
2095 
2096   // Open the table
2097   if (!(rc= CntOpenTable(g, tdbp, xmod, c1, c2, del, this))) {
2098     istable= true;
2099 //  strmake(tname, table_name, sizeof(tname)-1);
2100 
2101 #ifdef NOT_USED_VARIABLE
2102     // We may be in a create index query
2103     if (xmod == MODE_ANY && *tdbp->GetName() != '#') {
2104       // The current indexes
2105       PIXDEF oldpix= GetIndexInfo();
2106       } // endif xmod
2107 #endif
2108 
2109   } else
2110     htrc("OpenTable: %s\n", g->Message);
2111 
2112   if (rc) {
2113     tdbp= NULL;
2114     valid_info= false;
2115     } // endif rc
2116 
2117   return (rc) ? HA_ERR_INITIALIZATION : 0;
2118 } // end of OpenTable
2119 
2120 
2121 /****************************************************************************/
2122 /*  CheckColumnList: check that all bitmap columns do exist.                */
2123 /****************************************************************************/
CheckColumnList(PGLOBAL g)2124 bool ha_connect::CheckColumnList(PGLOBAL g)
2125 {
2126   // Check the list of used fields (columns)
2127   bool       brc= false;
2128   PCOL       colp;
2129   Field*    *field;
2130   Field*     fp;
2131   MY_BITMAP *map= table->read_set;
2132 
2133 	try {
2134           for (field= table->field; (fp= *field); field++)
2135       if (bitmap_is_set(map, fp->field_index)) {
2136         if (!(colp= tdbp->ColDB(g, (PSZ)fp->field_name.str, 0))) {
2137           sprintf(g->Message, "Column %s not found in %s",
2138                   fp->field_name.str, tdbp->GetName());
2139 					throw 1;
2140 				} // endif colp
2141 
2142         if ((brc= colp->InitValue(g)))
2143 					throw 2;
2144 
2145         colp->AddColUse(U_P);           // For PLG tables
2146         } // endif
2147 
2148 	} catch (int n) {
2149 		if (trace(1))
2150 			htrc("Exception %d: %s\n", n, g->Message);
2151 		brc= true;
2152 	} catch (const char *msg) {
2153 		strcpy(g->Message, msg);
2154 		brc= true;
2155 	} // end catch
2156 
2157   return brc;
2158 } // end of CheckColumnList
2159 
2160 
2161 /****************************************************************************/
2162 /*  IsOpened: returns true if the table is already opened.                  */
2163 /****************************************************************************/
IsOpened(void)2164 bool ha_connect::IsOpened(void)
2165 {
2166   return (!xp->CheckQuery(valid_query_id) && tdbp
2167                                           && tdbp->GetUse() == USE_OPEN);
2168 } // end of IsOpened
2169 
2170 
2171 /****************************************************************************/
2172 /*  Close a CONNECT table.                                                  */
2173 /****************************************************************************/
CloseTable(PGLOBAL g)2174 int ha_connect::CloseTable(PGLOBAL g)
2175 {
2176   int rc= CntCloseTable(g, tdbp, nox, abort);
2177   tdbp= NULL;
2178   sdvalin1= sdvalin2= sdvalin3= sdvalin4= NULL;
2179   sdvalout=NULL;
2180   valid_info= false;
2181   indexing= -1;
2182   nox= true;
2183   abort= false;
2184   return rc;
2185 } // end of CloseTable
2186 
2187 
2188 /***********************************************************************/
2189 /*  Make a pseudo record from current row values. Specific to MySQL.   */
2190 /***********************************************************************/
MakeRecord(char * buf)2191 int ha_connect::MakeRecord(char *buf)
2192 {
2193 	PCSZ           fmt;
2194   char          *p, val[32];
2195   int            rc= 0;
2196   Field*        *field;
2197   Field         *fp;
2198   CHARSET_INFO  *charset= tdbp->data_charset();
2199 //MY_BITMAP      readmap;
2200   MY_BITMAP     *map;
2201   PVAL           value;
2202   PCOL           colp= NULL;
2203   DBUG_ENTER("ha_connect::MakeRecord");
2204 
2205   if (trace(2))
2206     htrc("Maps: read=%08X write=%08X defr=%08X defw=%08X\n",
2207             *table->read_set->bitmap, *table->write_set->bitmap,
2208             *table->def_read_set.bitmap, *table->def_write_set.bitmap);
2209 
2210   // Avoid asserts in field::store() for columns that are not updated
2211   MY_BITMAP *org_bitmap= dbug_tmp_use_all_columns(table, &table->write_set);
2212 
2213   // This is for variable_length rows
2214   memset(buf, 0, table->s->null_bytes);
2215 
2216   // When sorting read_set selects all columns, so we use def_read_set
2217   map= (MY_BITMAP *)&table->def_read_set;
2218 
2219   // Make the pseudo record from field values
2220   for (field= table->field; *field && !rc; field++) {
2221     fp= *field;
2222 
2223     if (fp->vcol_info && !fp->stored_in_db)
2224       continue;            // This is a virtual column
2225 
2226     if (bitmap_is_set(map, fp->field_index) || alter) {
2227       // This is a used field, fill the buffer with value
2228       for (colp= tdbp->GetColumns(); colp; colp= colp->GetNext())
2229         if ((!mrr || colp->GetKcol()) &&
2230             !stricmp(colp->GetName(), fp->field_name.str))
2231           break;
2232 
2233       if (!colp) {
2234         if (mrr)
2235           continue;
2236 
2237         htrc("Column %s not found\n", fp->field_name.str);
2238         dbug_tmp_restore_column_map(&table->write_set, org_bitmap);
2239         DBUG_RETURN(HA_ERR_WRONG_IN_RECORD);
2240         } // endif colp
2241 
2242       value= colp->GetValue();
2243       p= NULL;
2244 
2245       // All this was better optimized
2246       if (!value->IsNull()) {
2247         switch (value->GetType()) {
2248           case TYPE_DATE:
2249             if (!sdvalout)
2250               sdvalout= AllocateValue(xp->g, TYPE_STRING, 20);
2251 
2252             switch (fp->type()) {
2253               case MYSQL_TYPE_DATE:
2254                 fmt= "%Y-%m-%d";
2255                 break;
2256               case MYSQL_TYPE_TIME:
2257                 fmt= "%H:%M:%S";
2258                 break;
2259               case MYSQL_TYPE_YEAR:
2260                 fmt= "%Y";
2261                 break;
2262               default:
2263                 fmt= "%Y-%m-%d %H:%M:%S";
2264                 break;
2265               } // endswitch type
2266 
2267             // Get date in the format required by MySQL fields
2268             value->FormatValue(sdvalout, fmt);
2269             p= sdvalout->GetCharValue();
2270             rc= fp->store(p, strlen(p), charset, CHECK_FIELD_WARN);
2271             break;
2272           case TYPE_STRING:
2273           case TYPE_DECIM:
2274             p= value->GetCharString(val);
2275             charset= tdbp->data_charset();
2276             rc= fp->store_text(p, strlen(p), charset, CHECK_FIELD_WARN);
2277             break;
2278 					case TYPE_BIN:
2279 						p= value->GetCharValue();
2280 						charset= &my_charset_bin;
2281 						rc= fp->store(p, value->GetSize(), charset, CHECK_FIELD_WARN);
2282 						break;
2283           case TYPE_DOUBLE:
2284             rc= fp->store(value->GetFloatValue());
2285             break;
2286           default:
2287             rc= fp->store(value->GetBigintValue(), value->IsUnsigned());
2288             break;
2289           } // endswitch Type
2290 
2291         // Store functions returns 1 on overflow and -1 on fatal error
2292         if (rc > 0) {
2293           char buf[256];
2294           THD *thd= ha_thd();
2295 
2296           sprintf(buf, "Out of range value %.140s for column '%s' at row %ld",
2297             value->GetCharString(val),
2298             fp->field_name.str,
2299             thd->get_stmt_da()->current_row_for_warning());
2300 
2301           push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, buf);
2302           DBUG_PRINT("MakeRecord", ("%s", buf));
2303           rc= 0;
2304         } else if (rc < 0)
2305           rc= HA_ERR_WRONG_IN_RECORD;
2306 
2307         fp->set_notnull();
2308       } else
2309         fp->set_null();
2310 
2311       } // endif bitmap
2312 
2313     } // endfor field
2314 
2315   // This is sometimes required for partition tables because the buf
2316   // can be different from the table->record[0] buffer
2317   if (buf != (char*)table->record[0])
2318     memcpy(buf, table->record[0], table->s->stored_rec_length);
2319 
2320   // This is copied from ha_tina and is necessary to avoid asserts
2321   dbug_tmp_restore_column_map(&table->write_set, org_bitmap);
2322   DBUG_RETURN(rc);
2323 } // end of MakeRecord
2324 
2325 
2326 /***********************************************************************/
2327 /*  Set row values from a MySQL pseudo record. Specific to MySQL.      */
2328 /***********************************************************************/
ScanRecord(PGLOBAL g,const uchar *)2329 int ha_connect::ScanRecord(PGLOBAL g, const uchar *)
2330 {
2331   char    attr_buffer[1024];
2332   char    data_buffer[1024];
2333   PCSZ    fmt;
2334   int     rc= 0;
2335   PCOL    colp;
2336   PVAL    value, sdvalin;
2337   Field  *fp;
2338 //PTDBASE tp= (PTDBASE)tdbp;
2339   String  attribute(attr_buffer, sizeof(attr_buffer),
2340                     table->s->table_charset);
2341   MY_BITMAP *bmap= dbug_tmp_use_all_columns(table, &table->read_set);
2342   const CHARSET_INFO *charset= tdbp->data_charset();
2343   String  data_charset_value(data_buffer, sizeof(data_buffer),  charset);
2344 
2345   // Scan the pseudo record for field values and set column values
2346   for (Field **field=table->field ; *field ; field++) {
2347     fp= *field;
2348 
2349     if ((fp->vcol_info && !fp->stored_in_db) ||
2350          fp->option_struct->special)
2351       continue;            // Is a virtual column possible here ???
2352 
2353     if ((xmod == MODE_INSERT && tdbp->GetAmType() != TYPE_AM_MYSQL
2354                              && tdbp->GetAmType() != TYPE_AM_ODBC
2355 														 && tdbp->GetAmType() != TYPE_AM_JDBC) ||
2356 														 bitmap_is_set(table->write_set, fp->field_index)) {
2357       for (colp= tdbp->GetSetCols(); colp; colp= colp->GetNext())
2358         if (!stricmp(colp->GetName(), fp->field_name.str))
2359           break;
2360 
2361       if (!colp) {
2362         htrc("Column %s not found\n", fp->field_name.str);
2363         rc= HA_ERR_WRONG_IN_RECORD;
2364         goto err;
2365       } else
2366         value= colp->GetValue();
2367 
2368       // This is a used field, fill the value from the row buffer
2369       // All this could be better optimized
2370       if (fp->is_null()) {
2371         if (colp->IsNullable())
2372           value->SetNull(true);
2373 
2374         value->Reset();
2375       } else switch (value->GetType()) {
2376         case TYPE_DOUBLE:
2377           value->SetValue(fp->val_real());
2378           break;
2379         case TYPE_DATE:
2380           // Get date in the format produced by MySQL fields
2381           switch (fp->type()) {
2382             case MYSQL_TYPE_DATE:
2383               if (!sdvalin2) {
2384                 sdvalin2= (DTVAL*)AllocateValue(xp->g, TYPE_DATE, 19);
2385                 fmt= "YYYY-MM-DD";
2386                 ((DTVAL*)sdvalin2)->SetFormat(g, fmt, strlen(fmt));
2387                 } // endif sdvalin1
2388 
2389               sdvalin= sdvalin2;
2390               break;
2391             case MYSQL_TYPE_TIME:
2392               if (!sdvalin3) {
2393                 sdvalin3= (DTVAL*)AllocateValue(xp->g, TYPE_DATE, 19);
2394                 fmt= "hh:mm:ss";
2395                 ((DTVAL*)sdvalin3)->SetFormat(g, fmt, strlen(fmt));
2396                 } // endif sdvalin1
2397 
2398               sdvalin= sdvalin3;
2399               break;
2400             case MYSQL_TYPE_YEAR:
2401               if (!sdvalin4) {
2402                 sdvalin4= (DTVAL*)AllocateValue(xp->g, TYPE_DATE, 19);
2403                 fmt= "YYYY";
2404                 ((DTVAL*)sdvalin4)->SetFormat(g, fmt, strlen(fmt));
2405                 } // endif sdvalin1
2406 
2407               sdvalin= sdvalin4;
2408               break;
2409             default:
2410               if (!sdvalin1) {
2411                 sdvalin1= (DTVAL*)AllocateValue(xp->g, TYPE_DATE, 19);
2412                 fmt= "YYYY-MM-DD hh:mm:ss";
2413                 ((DTVAL*)sdvalin1)->SetFormat(g, fmt, strlen(fmt));
2414                 } // endif sdvalin1
2415 
2416               sdvalin= sdvalin1;
2417             } // endswitch type
2418 
2419           sdvalin->SetNullable(colp->IsNullable());
2420           fp->val_str(&attribute);
2421           sdvalin->SetValue_psz(attribute.c_ptr_safe());
2422           value->SetValue_pval(sdvalin);
2423           break;
2424         default:
2425           fp->val_str(&attribute);
2426 
2427           if (charset != &my_charset_bin) {
2428             // Convert from SQL field charset to DATA_CHARSET
2429             uint cnv_errors;
2430 
2431             data_charset_value.copy(attribute.ptr(), attribute.length(),
2432                                     attribute.charset(), charset, &cnv_errors);
2433             value->SetValue_psz(data_charset_value.c_ptr_safe());
2434           } else
2435             value->SetValue_psz(attribute.c_ptr_safe());
2436 
2437           break;
2438         } // endswitch Type
2439 
2440 #ifdef NEWCHANGE
2441     } else if (xmod == MODE_UPDATE) {
2442       PCOL cp;
2443 
2444       for (cp= tdbp->GetColumns(); cp; cp= cp->GetNext())
2445         if (!stricmp(colp->GetName(), cp->GetName()))
2446           break;
2447 
2448       if (!cp) {
2449         rc= HA_ERR_WRONG_IN_RECORD;
2450         goto err;
2451         } // endif cp
2452 
2453       value->SetValue_pval(cp->GetValue());
2454     } else // mode Insert
2455       value->Reset();
2456 #else
2457     } // endif bitmap_is_set
2458 #endif
2459 
2460     } // endfor field
2461 
2462  err:
2463   dbug_tmp_restore_column_map(&table->read_set, bmap);
2464   return rc;
2465 } // end of ScanRecord
2466 
2467 
2468 /***********************************************************************/
2469 /*  Check change in index column. Specific to MySQL.                   */
2470 /*  Should be elaborated to check for real changes.                    */
2471 /***********************************************************************/
CheckRecord(PGLOBAL g,const uchar *,const uchar * newbuf)2472 int ha_connect::CheckRecord(PGLOBAL g, const uchar *, const uchar *newbuf)
2473 {
2474 	return ScanRecord(g, newbuf);
2475 } // end of dummy CheckRecord
2476 
2477 
2478 /***********************************************************************/
2479 /*  Return true if this field is used in current indexing.             */
2480 /***********************************************************************/
IsIndexed(Field * fp)2481 bool ha_connect::IsIndexed(Field *fp)
2482 {
2483 	if (active_index < MAX_KEY) {
2484 		KEY_PART_INFO *kpart;
2485 		KEY           *kfp= &table->key_info[active_index];
2486 		uint           rem= kfp->user_defined_key_parts;
2487 
2488 		for (kpart= kfp->key_part; rem; rem--, kpart++)
2489 			if (kpart->field == fp)
2490 				return true;
2491 
2492 	} // endif active_index
2493 
2494 	return false;
2495 } // end of IsIndexed
2496 
2497 
2498 /***********************************************************************/
2499 /*  Return the where clause for remote indexed read.                   */
2500 /***********************************************************************/
MakeKeyWhere(PGLOBAL g,PSTRG qry,OPVAL vop,char q,const key_range * kr)2501 bool ha_connect::MakeKeyWhere(PGLOBAL g, PSTRG qry, OPVAL vop, char q,
2502 	                            const key_range *kr)
2503 {
2504 	const uchar     *ptr;
2505 //uint             i, rem, len, klen, stlen;
2506 	uint             i, rem, len, stlen;
2507 	bool             nq, both, oom;
2508 	OPVAL            op;
2509 	Field           *fp;
2510 	const key_range *ranges[2];
2511 	MY_BITMAP    *old_map;
2512 	KEY             *kfp;
2513   KEY_PART_INFO   *kpart;
2514 
2515   if (active_index == MAX_KEY)
2516     return false;
2517 
2518 	ranges[0]= kr;
2519 	ranges[1]= (end_range && !eq_range) ? &save_end_range : NULL;
2520 
2521 	if (!ranges[0] && !ranges[1]) {
2522 		strcpy(g->Message, "MakeKeyWhere: No key");
2523 	  return true;
2524 	}	else
2525 		both= ranges[0] && ranges[1];
2526 
2527 	kfp= &table->key_info[active_index];
2528 	old_map= dbug_tmp_use_all_columns(table, &table->write_set);
2529 
2530 	for (i= 0; i <= 1; i++) {
2531 		if (ranges[i] == NULL)
2532 			continue;
2533 
2534 		if (both && i > 0)
2535 			qry->Append(") AND (");
2536 		else
2537 			qry->Append(" WHERE (");
2538 
2539 //	klen= len= ranges[i]->length;
2540 		len= ranges[i]->length;
2541 		rem= kfp->user_defined_key_parts;
2542 		ptr= ranges[i]->key;
2543 
2544 		for (kpart= kfp->key_part; rem; rem--, kpart++) {
2545 			fp= kpart->field;
2546 			stlen= kpart->store_length;
2547 			nq= fp->str_needs_quotes();
2548 
2549 			if (kpart != kfp->key_part)
2550 				qry->Append(" AND ");
2551 
2552 			if (q) {
2553 				qry->Append(q);
2554 				qry->Append((PSZ)fp->field_name.str);
2555 				qry->Append(q);
2556 			}	else
2557 				qry->Append((PSZ)fp->field_name.str);
2558 
2559 			switch (ranges[i]->flag) {
2560 			case HA_READ_KEY_EXACT:
2561 //			op= (stlen >= len || !nq || fp->result_type() != STRING_RESULT)
2562 //				? OP_EQ : OP_LIKE;
2563 				op= OP_EQ;
2564 				break;
2565 			case HA_READ_AFTER_KEY:
2566 				op= (stlen >= len || i > 0) ? (i > 0 ? OP_LE : OP_GT) : OP_GE;
2567 				break;
2568 			case HA_READ_KEY_OR_NEXT:
2569 				op= OP_GE;
2570 				break;
2571 			case HA_READ_BEFORE_KEY:
2572 				op= (stlen >= len) ? OP_LT : OP_LE;
2573 				break;
2574 			case HA_READ_KEY_OR_PREV:
2575 				op= OP_LE;
2576 				break;
2577 			default:
2578 				sprintf(g->Message, "cannot handle flag %d", ranges[i]->flag);
2579 				goto err;
2580 			}	// endswitch flag
2581 
2582 			qry->Append((PSZ)GetValStr(op, false));
2583 
2584 			if (nq)
2585 				qry->Append('\'');
2586 
2587 			if (kpart->key_part_flag & HA_VAR_LENGTH_PART) {
2588 				String varchar;
2589 				uint   var_length= uint2korr(ptr);
2590 
2591 				varchar.set_quick((char*)ptr + HA_KEY_BLOB_LENGTH,
2592 					var_length, &my_charset_bin);
2593 				qry->Append(varchar.ptr(), varchar.length(), nq);
2594 			}	else {
2595 				char   strbuff[MAX_FIELD_WIDTH];
2596 				String str(strbuff, sizeof(strbuff), kpart->field->charset()), *res;
2597 
2598 				res= fp->val_str(&str, ptr);
2599 				qry->Append(res->ptr(), res->length(), nq);
2600 			} // endif flag
2601 
2602 			if (nq)
2603 				qry->Append('\'');
2604 
2605 			if (stlen >= len)
2606 				break;
2607 
2608 			len-= stlen;
2609 
2610 			/* For nullable columns, null-byte is already skipped before, that is
2611 			ptr was incremented by 1. Since store_length still counts null-byte,
2612 			we need to subtract 1 from store_length. */
2613 			ptr+= stlen - MY_TEST(kpart->null_bit);
2614 		} // endfor kpart
2615 
2616 		} // endfor i
2617 
2618 	qry->Append(')');
2619 
2620   if ((oom= qry->IsTruncated()))
2621     strcpy(g->Message, "Out of memory");
2622 
2623 	dbug_tmp_restore_column_map(&table->write_set, old_map);
2624 	return oom;
2625 
2626 err:
2627 	dbug_tmp_restore_column_map(&table->write_set, old_map);
2628 	return true;
2629 } // end of MakeKeyWhere
2630 
2631 
2632 /***********************************************************************/
2633 /*  Return the string representing an operator.                        */
2634 /***********************************************************************/
GetValStr(OPVAL vop,bool neg)2635 const char *ha_connect::GetValStr(OPVAL vop, bool neg)
2636 {
2637   const char *val;
2638 
2639   switch (vop) {
2640     case OP_EQ:
2641       val= "= ";
2642       break;
2643     case OP_NE:
2644       val= " <> ";
2645       break;
2646     case OP_GT:
2647       val= " > ";
2648       break;
2649     case OP_GE:
2650       val= " >= ";
2651       break;
2652     case OP_LT:
2653       val= " < ";
2654       break;
2655     case OP_LE:
2656       val= " <= ";
2657       break;
2658     case OP_IN:
2659       val= (neg) ? " NOT IN (" : " IN (";
2660       break;
2661     case OP_NULL:
2662       val= (neg) ? " IS NOT NULL" : " IS NULL";
2663       break;
2664     case OP_LIKE:
2665       val= (neg) ? " NOT LIKE " : " LIKE ";
2666       break;
2667     case OP_XX:
2668       val= (neg) ? " NOT BETWEEN " : " BETWEEN ";
2669       break;
2670     case OP_EXIST:
2671       val= (neg) ? " NOT EXISTS " : " EXISTS ";
2672       break;
2673     case OP_AND:
2674       val= " AND ";
2675       break;
2676     case OP_OR:
2677       val= " OR ";
2678       break;
2679     case OP_NOT:
2680       val= " NOT ";
2681       break;
2682     case OP_CNC:
2683       val= " || ";
2684       break;
2685     case OP_ADD:
2686       val= " + ";
2687       break;
2688     case OP_SUB:
2689       val= " - ";
2690       break;
2691     case OP_MULT:
2692       val= " * ";
2693       break;
2694     case OP_DIV:
2695       val= " / ";
2696       break;
2697     default:
2698       val= " ? ";
2699       break;
2700     } /* endswitch */
2701 
2702   return val;
2703 } // end of GetValStr
2704 
2705 #if 0
2706 /***********************************************************************/
2707 /*  Check the WHERE condition and return a CONNECT filter.             */
2708 /***********************************************************************/
2709 PFIL ha_connect::CheckFilter(PGLOBAL g)
2710 {
2711   return CondFilter(g, (Item *)pushed_cond);
2712 } // end of CheckFilter
2713 #endif // 0
2714 
2715 /***********************************************************************/
2716 /*  Check the WHERE condition and return a CONNECT filter.             */
2717 /***********************************************************************/
CondFilter(PGLOBAL g,Item * cond)2718 PFIL ha_connect::CondFilter(PGLOBAL g, Item *cond)
2719 {
2720   unsigned int i;
2721   bool  ismul= false;
2722   OPVAL vop= OP_XX;
2723   PFIL  filp= NULL;
2724 
2725   if (!cond)
2726     return NULL;
2727 
2728   if (trace(1))
2729     htrc("Cond type=%d\n", cond->type());
2730 
2731   if (cond->type() == COND::COND_ITEM) {
2732     PFIL       fp;
2733     Item_cond *cond_item= (Item_cond *)cond;
2734 
2735     if (trace(1))
2736       htrc("Cond: Ftype=%d name=%s\n", cond_item->functype(),
2737                                        cond_item->func_name());
2738 
2739     switch (cond_item->functype()) {
2740       case Item_func::COND_AND_FUNC: vop= OP_AND; break;
2741       case Item_func::COND_OR_FUNC:  vop= OP_OR;  break;
2742       default: return NULL;
2743       } // endswitch functype
2744 
2745     List<Item>* arglist= cond_item->argument_list();
2746     List_iterator<Item> li(*arglist);
2747     Item *subitem;
2748 
2749     for (i= 0; i < arglist->elements; i++)
2750       if ((subitem= li++)) {
2751         if (!(fp= CondFilter(g, subitem))) {
2752           if (vop == OP_OR)
2753             return NULL;
2754         } else
2755           filp= (filp) ? MakeFilter(g, filp, vop, fp) : fp;
2756 
2757       } else
2758         return NULL;
2759 
2760   } else if (cond->type() == COND::FUNC_ITEM) {
2761     unsigned int i;
2762     bool       iscol, neg= FALSE;
2763     PCOL       colp[2]= {NULL,NULL};
2764     PPARM      pfirst= NULL, pprec= NULL;
2765     POPER      pop;
2766     Item_func *condf= (Item_func *)cond;
2767     Item*     *args= condf->arguments();
2768 
2769     if (trace(1))
2770       htrc("Func type=%d argnum=%d\n", condf->functype(),
2771                                        condf->argument_count());
2772 
2773     switch (condf->functype()) {
2774       case Item_func::EQUAL_FUNC:
2775       case Item_func::EQ_FUNC: vop= OP_EQ;  break;
2776       case Item_func::NE_FUNC: vop= OP_NE;  break;
2777       case Item_func::LT_FUNC: vop= OP_LT;  break;
2778       case Item_func::LE_FUNC: vop= OP_LE;  break;
2779       case Item_func::GE_FUNC: vop= OP_GE;  break;
2780       case Item_func::GT_FUNC: vop= OP_GT;  break;
2781       case Item_func::IN_FUNC: vop= OP_IN;	/* fall through */
2782       case Item_func::BETWEEN:
2783         ismul= true;
2784         neg= ((Item_func_opt_neg *)condf)->negated;
2785         break;
2786       default: return NULL;
2787       } // endswitch functype
2788 
2789     pop= (POPER)PlugSubAlloc(g, NULL, sizeof(OPER));
2790     pop->Name= NULL;
2791     pop->Val=vop;
2792     pop->Mod= 0;
2793 
2794     if (condf->argument_count() < 2)
2795       return NULL;
2796 
2797     for (i= 0; i < condf->argument_count(); i++) {
2798       if (trace(1))
2799         htrc("Argtype(%d)=%d\n", i, args[i]->type());
2800 
2801       if (i >= 2 && !ismul) {
2802         if (trace(1))
2803           htrc("Unexpected arg for vop=%d\n", vop);
2804 
2805         continue;
2806         } // endif i
2807 
2808       if ((iscol= args[i]->type() == COND::FIELD_ITEM)) {
2809         Item_field *pField= (Item_field *)args[i];
2810 
2811         // IN and BETWEEN clauses should be col VOP list
2812         if (i && ismul)
2813           return NULL;
2814 
2815         if (pField->field->table != table ||
2816             !(colp[i]= tdbp->ColDB(g, (PSZ)pField->field->field_name.str, 0)))
2817           return NULL;  // Column does not belong to this table
2818 
2819 				// These types are not yet implemented (buggy)
2820 				switch (pField->field->type()) {
2821 				case MYSQL_TYPE_TIMESTAMP:
2822 				case MYSQL_TYPE_DATE:
2823 				case MYSQL_TYPE_TIME:
2824 				case MYSQL_TYPE_DATETIME:
2825 				case MYSQL_TYPE_YEAR:
2826 				case MYSQL_TYPE_NEWDATE:
2827 					return NULL;
2828 				default:
2829 					break;
2830 				} // endswitch type
2831 
2832         if (trace(1)) {
2833           htrc("Field index=%d\n", pField->field->field_index);
2834           htrc("Field name=%s\n", pField->field->field_name.str);
2835           } // endif trace
2836 
2837       } else {
2838         char    buff[256];
2839         String *res, tmp(buff, sizeof(buff), &my_charset_bin);
2840         PPARM pp= (PPARM)PlugSubAlloc(g, NULL, sizeof(PARM));
2841 
2842         // IN and BETWEEN clauses should be col VOP list
2843         if (!i && (ismul))
2844           return NULL;
2845 
2846         switch (args[i]->real_type()) {
2847           case COND::CONST_ITEM:
2848           {
2849             Item *pval= (Item *)args[i];
2850           switch (args[i]->cmp_type()) {
2851             case STRING_RESULT:
2852               res= pval->val_str(&tmp);
2853               pp->Value= PlugSubAllocStr(g, NULL, res->ptr(), res->length());
2854               pp->Type= (pp->Value) ? TYPE_STRING : TYPE_ERROR;
2855               break;
2856             case INT_RESULT:
2857               pp->Type= TYPE_INT;
2858               pp->Value= PlugSubAlloc(g, NULL, sizeof(int));
2859               *((int*)pp->Value)= (int)pval->val_int();
2860               break;
2861             case TIME_RESULT:
2862               pp->Type= TYPE_DATE;
2863               pp->Value= PlugSubAlloc(g, NULL, sizeof(int));
2864               *((int*)pp->Value)= (int) Temporal_hybrid(pval).to_longlong();
2865               break;
2866             case REAL_RESULT:
2867             case DECIMAL_RESULT:
2868               pp->Type= TYPE_DOUBLE;
2869               pp->Value= PlugSubAlloc(g, NULL, sizeof(double));
2870               *((double*)pp->Value)= pval->val_real();
2871               break;
2872             case ROW_RESULT:
2873               DBUG_ASSERT(0);
2874               return NULL;
2875           }
2876           }
2877           break;
2878           case COND::CACHE_ITEM:    // Possible ???
2879           case COND::NULL_ITEM:     // TODO: handle this
2880           default:
2881             return NULL;
2882         } // endswitch type
2883 
2884 				if (trace(1))
2885           htrc("Value type=%hd\n", pp->Type);
2886 
2887         // Append the value to the argument list
2888         if (pprec)
2889           pprec->Next= pp;
2890         else
2891           pfirst= pp;
2892 
2893         pp->Domain= i;
2894         pp->Next= NULL;
2895         pprec= pp;
2896       } // endif type
2897 
2898       } // endfor i
2899 
2900     filp= MakeFilter(g, colp, pop, pfirst, neg);
2901   } else {
2902     if (trace(1))
2903       htrc("Unsupported condition\n");
2904 
2905     return NULL;
2906   } // endif's type
2907 
2908   return filp;
2909 } // end of CondFilter
2910 
2911 /***********************************************************************/
2912 /*  Check the WHERE condition and return a MYSQL/ODBC/JDBC/WQL filter. */
2913 /***********************************************************************/
CheckCond(PGLOBAL g,PCFIL filp,const Item * cond)2914 PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond)
2915 {
2916 	AMT   tty= filp->Type;
2917   char *body= filp->Body;
2918 	char *havg= filp->Having;
2919 	unsigned int i;
2920   bool  ismul= false, x= (tty == TYPE_AM_MYX || tty == TYPE_AM_XDBC);
2921   bool  nonul= ((tty == TYPE_AM_ODBC || tty == TYPE_AM_JDBC) &&
2922 		 (tdbp->GetMode() == MODE_INSERT || tdbp->GetMode() == MODE_DELETE));
2923   OPVAL vop= OP_XX;
2924 
2925   if (!cond)
2926     return NULL;
2927 
2928   if (trace(1))
2929     htrc("Cond type=%d\n", cond->type());
2930 
2931   if (cond->type() == COND::COND_ITEM) {
2932     char      *pb0, *pb1, *pb2, *ph0= 0, *ph1= 0, *ph2= 0;
2933 		bool       bb= false, bh= false;
2934     Item_cond *cond_item= (Item_cond *)cond;
2935 
2936     if (x)
2937       return NULL;
2938 		else
2939 			pb0= pb1= pb2= ph0= ph1= ph2= NULL;
2940 
2941     if (trace(1))
2942       htrc("Cond: Ftype=%d name=%s\n", cond_item->functype(),
2943                                        cond_item->func_name());
2944 
2945     switch (cond_item->functype()) {
2946       case Item_func::COND_AND_FUNC: vop= OP_AND; break;
2947       case Item_func::COND_OR_FUNC:  vop= OP_OR;  break;
2948       default: return NULL;
2949       } // endswitch functype
2950 
2951     List<Item>* arglist= cond_item->argument_list();
2952     List_iterator<Item> li(*arglist);
2953     const Item *subitem;
2954 
2955     pb0= pb1= body + strlen(body);
2956     strcpy(pb0, "(");
2957     pb2= pb1 + 1;
2958 
2959 		if (havg) {
2960 			ph0= ph1= havg + strlen(havg);
2961 			strcpy(ph0, "(");
2962 			ph2= ph1 + 1;
2963 		} // endif havg
2964 
2965     for (i= 0; i < arglist->elements; i++)
2966       if ((subitem= li++)) {
2967         if (!CheckCond(g, filp, subitem)) {
2968           if (vop == OP_OR || nonul)
2969             return NULL;
2970 					else {
2971 						*pb2= 0;
2972 						if (havg) *ph2= 0;
2973 					}	// endelse
2974 
2975         } else {
2976 					if (filp->Bd) {
2977 						pb1= pb2 + strlen(pb2);
2978 						strcpy(pb1, GetValStr(vop, false));
2979 						pb2= pb1 + strlen(pb1);
2980 					} // endif Bd
2981 
2982 					if (filp->Hv) {
2983 						ph1= ph2 + strlen(ph2);
2984 						strcpy(ph1, GetValStr(vop, false));
2985 						ph2= ph1 + strlen(ph1);
2986 					} // endif Hv
2987 
2988         } // endif CheckCond
2989 
2990 				bb |= filp->Bd;
2991 				bh |= filp->Hv;
2992 				filp->Bd= filp->Hv= false;
2993       } else
2994         return NULL;
2995 
2996     if (bb)	{
2997       strcpy(pb1, ")");
2998 			filp->Bd= bb;
2999 		} else
3000 			*pb0= 0;
3001 
3002 		if (havg) {
3003 			if (bb && bh && vop == OP_OR) {
3004 				// Cannot or'ed a where clause with a having clause
3005 				bb= bh= 0;
3006 				*pb0= 0;
3007 				*ph0= 0;
3008 			} else if (bh)	{
3009 				strcpy(ph1, ")");
3010 				filp->Hv= bh;
3011 			} else
3012 				*ph0= 0;
3013 
3014 		} // endif havg
3015 
3016 		if (!bb && !bh)
3017 			return NULL;
3018 
3019   } else if (cond->type() == COND::FUNC_ITEM) {
3020     unsigned int i;
3021     bool       iscol, ishav= false, neg= false;
3022     Item_func *condf= (Item_func *)cond;
3023     Item*     *args= condf->arguments();
3024 
3025 		filp->Bd= filp->Hv= false;
3026 
3027     if (trace(1))
3028       htrc("Func type=%d argnum=%d\n", condf->functype(),
3029                                        condf->argument_count());
3030 
3031     switch (condf->functype()) {
3032       case Item_func::EQUAL_FUNC:
3033 			case Item_func::EQ_FUNC:     vop= OP_EQ;   break;
3034 			case Item_func::NE_FUNC:     vop= OP_NE;   break;
3035 			case Item_func::LT_FUNC:     vop= OP_LT;   break;
3036 			case Item_func::LE_FUNC:     vop= OP_LE;   break;
3037 			case Item_func::GE_FUNC:     vop= OP_GE;   break;
3038 			case Item_func::GT_FUNC:     vop= OP_GT;   break;
3039 #if MYSQL_VERSION_ID > 100200
3040 			case Item_func::LIKE_FUNC:
3041 				vop = OP_LIKE;
3042 			  neg= ((Item_func_like*)condf)->negated;
3043 			  break;
3044 #endif // VERSION_ID > 100200
3045 			case Item_func::ISNOTNULL_FUNC:
3046 				neg= true;
3047 				// fall through
3048 			case Item_func::ISNULL_FUNC: vop= OP_NULL; break;
3049 			case Item_func::IN_FUNC:     vop= OP_IN; /* fall through */
3050       case Item_func::BETWEEN:
3051         ismul= true;
3052         neg= ((Item_func_opt_neg *)condf)->negated;
3053         break;
3054       default: return NULL;
3055       } // endswitch functype
3056 
3057     if (condf->argument_count() < 2)
3058       return NULL;
3059     else if (ismul && tty == TYPE_AM_WMI)
3060       return NULL;        // Not supported by WQL
3061 
3062     if (x && (neg || !(vop == OP_EQ || vop == OP_IN || vop == OP_NULL)))
3063       return NULL;
3064 
3065     for (i= 0; i < condf->argument_count(); i++) {
3066       if (trace(1))
3067         htrc("Argtype(%d)=%d\n", i, args[i]->type());
3068 
3069       if (i >= 2 && !ismul) {
3070         if (trace(1))
3071           htrc("Unexpected arg for vop=%d\n", vop);
3072 
3073         continue;
3074         } // endif i
3075 
3076       if ((iscol= args[i]->type() == COND::FIELD_ITEM)) {
3077         const char *fnm;
3078         ha_field_option_struct *fop;
3079         Item_field *pField= (Item_field *)args[i];
3080 
3081 				// IN and BETWEEN clauses should be col VOP list
3082 				if (i && (x || ismul))
3083           return NULL;	// IN and BETWEEN clauses should be col VOP list
3084 				else if (pField->field->table != table)
3085 					return NULL;  // Field does not belong to this table
3086 				else if (tty != TYPE_AM_WMI && IsIndexed(pField->field))
3087 					return NULL;  // Will be handled by ReadKey
3088         else
3089           fop= GetFieldOptionStruct(pField->field);
3090 
3091         if (fop && fop->special) {
3092           if (tty == TYPE_AM_TBL && !stricmp(fop->special, "TABID"))
3093             fnm= "TABID";
3094           else if (tty == TYPE_AM_PLG)
3095             fnm= fop->special;
3096           else
3097             return NULL;
3098 
3099 				} else if (tty == TYPE_AM_TBL) {
3100 					return NULL;
3101 				} else {
3102 					bool h;
3103 
3104 					fnm= filp->Chk(pField->field->field_name.str, &h);
3105 
3106 					if (h && i && !ishav)
3107 						return NULL;			// Having should be	col VOP arg
3108 					else
3109 						ishav= h;
3110 
3111 				}	// endif's
3112 
3113         if (trace(1)) {
3114           htrc("Field index=%d\n", pField->field->field_index);
3115           htrc("Field name=%s\n", pField->field->field_name.str);
3116           htrc("Field type=%d\n", pField->field->type());
3117           htrc("Field_type=%d\n", args[i]->field_type());
3118           } // endif trace
3119 
3120         strcat((ishav ? havg : body), fnm);
3121       } else if (args[i]->type() == COND::FUNC_ITEM) {
3122         if (tty == TYPE_AM_MYSQL) {
3123           if (!CheckCond(g, filp, args[i]))
3124             return NULL;
3125 
3126         } else
3127           return NULL;
3128 
3129       } else {
3130         char    buff[256];
3131         String *res, tmp(buff, sizeof(buff), &my_charset_bin);
3132         Item *pval= (Item *)args[i];
3133         Item::Type type= args[i]->real_type();
3134 
3135         switch (type) {
3136           case COND::CONST_ITEM:
3137           case COND::NULL_ITEM:
3138           case COND::CACHE_ITEM:
3139             break;
3140           default:
3141             return NULL;
3142           } // endswitch type
3143 
3144         if ((res= pval->val_str(&tmp)) == NULL)
3145           return NULL;                      // To be clarified
3146 
3147         if (trace(1))
3148           htrc("Value=%.*s\n", res->length(), res->ptr());
3149 
3150         // IN and BETWEEN clauses should be col VOP list
3151         if (!i && (x || ismul))
3152           return NULL;
3153 
3154         if (!x) {
3155 					const char *p;
3156 					char *s= (ishav) ? havg : body;
3157 					uint	j, k, n;
3158 
3159           // Append the value to the filter
3160           switch (args[i]->field_type()) {
3161             case MYSQL_TYPE_TIMESTAMP:
3162             case MYSQL_TYPE_DATETIME:
3163               if (tty == TYPE_AM_ODBC) {
3164                 strcat(s, "{ts '");
3165                 strncat(s, res->ptr(), res->length());
3166 
3167                 if (res->length() < 19)
3168                   strcat(s, &"1970-01-01 00:00:00"[res->length()]);
3169 
3170                 strcat(s, "'}");
3171                 break;
3172                 } // endif ODBC
3173 		// fall through
3174             case MYSQL_TYPE_DATE:
3175               if (tty == TYPE_AM_ODBC) {
3176                 strcat(s, "{d '");
3177                 strcat(strncat(s, res->ptr(), res->length()), "'}");
3178                 break;
3179                 } // endif ODBC
3180 		// fall through
3181 
3182             case MYSQL_TYPE_TIME:
3183               if (tty == TYPE_AM_ODBC) {
3184                 strcat(s, "{t '");
3185                 strcat(strncat(s, res->ptr(), res->length()), "'}");
3186                 break;
3187                 } // endif ODBC
3188 		// fall through
3189 
3190             case MYSQL_TYPE_VARCHAR:
3191               if (tty == TYPE_AM_ODBC && i) {
3192                 switch (args[0]->field_type()) {
3193                   case MYSQL_TYPE_TIMESTAMP:
3194                   case MYSQL_TYPE_DATETIME:
3195                     strcat(s, "{ts '");
3196                     strncat(s, res->ptr(), res->length());
3197 
3198                     if (res->length() < 19)
3199                       strcat(s, &"1970-01-01 00:00:00"[res->length()]);
3200 
3201                     strcat(s, "'}");
3202                     break;
3203                   case MYSQL_TYPE_DATE:
3204                     strcat(s, "{d '");
3205                     strncat(s, res->ptr(), res->length());
3206                     strcat(s, "'}");
3207                     break;
3208                   case MYSQL_TYPE_TIME:
3209                     strcat(s, "{t '");
3210                     strncat(s, res->ptr(), res->length());
3211                     strcat(s, "'}");
3212                     break;
3213                   default:
3214 										j= strlen(s);
3215 										s[j++]= '\'';
3216 										p= res->ptr();
3217 										n= res->length();
3218 
3219 										for (k= 0; k < n; k++) {
3220 											if (p[k] == '\'')
3221 												s[j++]= '\'';
3222 
3223 											s[j++]= p[k];
3224 										} // endfor k
3225 
3226 										s[j++]= '\'';
3227 										s[j]= 0;
3228 								} // endswitch field type
3229 
3230               } else {
3231 								j= strlen(s);
3232 								s[j++]= '\'';
3233 								p= res->ptr();
3234 								n= res->length();
3235 
3236 								for (k= 0; k < n; k++) {
3237 									if (p[k] == '\'')
3238 										s[j++]= '\'';
3239 
3240 									s[j++]= p[k];
3241 								} // endfor k
3242 
3243 								s[j++]= '\'';
3244 								s[j]= 0;
3245 							} // endif tty
3246 
3247               break;
3248             default:
3249               strncat(s, res->ptr(), res->length());
3250             } // endswitch field type
3251 
3252         } else {
3253           if (args[i]->field_type() == MYSQL_TYPE_VARCHAR) {
3254             // Add the command to the list
3255             PCMD *ncp, cmdp= new(g) CMD(g, (char*)res->c_ptr());
3256 
3257             for (ncp= &filp->Cmds; *ncp; ncp= &(*ncp)->Next) ;
3258 
3259             *ncp= cmdp;
3260           } else
3261             return NULL;
3262 
3263         } // endif x
3264 
3265       } // endif's Type
3266 
3267       if (!x) {
3268 				char *s= (ishav) ? havg : body;
3269 
3270 				if (!i)
3271           strcat(s, GetValStr(vop, neg));
3272         else if (vop == OP_XX && i == 1)
3273           strcat(s, " AND ");
3274         else if (vop == OP_IN)
3275           strcat(s, (i == condf->argument_count() - 1) ? ")" : ",");
3276 
3277         } // endif x
3278 
3279       } // endfor i
3280 
3281 			if (x)
3282 				filp->Op= vop;
3283 			else if (ishav)
3284 				filp->Hv= true;
3285 			else
3286 				filp->Bd= true;
3287 
3288   } else {
3289     if (trace(1))
3290       htrc("Unsupported condition\n");
3291 
3292     return NULL;
3293   } // endif's type
3294 
3295   return filp;
3296 } // end of CheckCond
3297 
3298 
3299  /**
3300    Push condition down to the table handler.
3301 
3302    @param  cond   Condition to be pushed. The condition tree must not be
3303                   modified by the caller.
3304 
3305    @return
3306      The 'remainder' condition that caller must use to filter out records.
3307      NULL means the handler will not return rows that do not match the
3308      passed condition.
3309 
3310    @note
3311      CONNECT handles the filtering only for table types that construct
3312      an SQL or WQL query, but still leaves it to MySQL because only some
3313      parts of the filter may be relevant.
3314      The first suballocate finds the position where the string will be
3315      constructed in the sarea. The second one does make the suballocation
3316      with the proper length.
3317  */
cond_push(const COND * cond)3318 const COND *ha_connect::cond_push(const COND *cond)
3319 {
3320   DBUG_ENTER("ha_connect::cond_push");
3321 
3322   if (tdbp && CondPushEnabled()) {
3323     PGLOBAL& g= xp->g;
3324     AMT      tty= tdbp->GetAmType();
3325     bool     x= (tty == TYPE_AM_MYX || tty == TYPE_AM_XDBC);
3326     bool     b= (tty == TYPE_AM_WMI || tty == TYPE_AM_ODBC  ||
3327                  tty == TYPE_AM_TBL || tty == TYPE_AM_MYSQL ||
3328 								 tty == TYPE_AM_PLG || tty == TYPE_AM_JDBC  || x);
3329 
3330 		// This should never happen but is done to avoid crashing
3331 		try {
3332 			if (b) {
3333 				PCFIL filp;
3334 				int   rc;
3335 
3336 				if ((filp= tdbp->GetCondFil()) && tdbp->GetCond() == cond &&
3337 					filp->Idx == active_index && filp->Type == tty)
3338 					goto fin;
3339 
3340 				filp= new(g) CONDFIL(active_index, tty);
3341 				rc= filp->Init(g, this);
3342 
3343 				if (rc == RC_INFO) {
3344 					filp->Having= (char*)PlugSubAlloc(g, NULL, 256);
3345 					*filp->Having= 0;
3346 				} else if (rc == RC_FX)
3347 					goto fin;
3348 
3349 				filp->Body= (char*)PlugSubAlloc(g, NULL, (x) ? 128 : 0);
3350 				*filp->Body= 0;
3351 
3352 				if (CheckCond(g, filp, cond)) {
3353 					if (filp->Having && strlen(filp->Having) > 255)
3354 						goto fin;								// Memory collapse
3355 
3356 					if (trace(1))
3357 						htrc("cond_push: %s\n", filp->Body);
3358 
3359 					tdbp->SetCond(cond);
3360 
3361 					if (!x)
3362 						PlugSubAlloc(g, NULL, strlen(filp->Body) + 1);
3363 					else
3364 						cond= NULL;             // Does this work?
3365 
3366 					tdbp->SetCondFil(filp);
3367 				} else if (x && cond)
3368 					tdbp->SetCondFil(filp);   // Wrong filter
3369 
3370 			} else if (tdbp->CanBeFiltered()) {
3371 				if (!tdbp->GetCond() || tdbp->GetCond() != cond) {
3372 					tdbp->SetFilter(CondFilter(g, (Item *)cond));
3373 
3374 					if (tdbp->GetFilter())
3375 					  tdbp->SetCond(cond);
3376 
3377 			  } // endif cond
3378 
3379 			}	// endif tty
3380 
3381 		} catch (int n) {
3382 			if (trace(1))
3383 				htrc("Exception %d: %s\n", n, g->Message);
3384 		} catch (const char *msg) {
3385 			strcpy(g->Message, msg);
3386 		} // end catch
3387 
3388 	fin:;
3389   } // endif tdbp
3390 
3391   // Let MySQL do the filtering
3392   DBUG_RETURN(cond);
3393 } // end of cond_push
3394 
3395 /**
3396   Number of rows in table. It will only be called if
3397   (table_flags() & (HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT)) != 0
3398 */
records()3399 ha_rows ha_connect::records()
3400 {
3401   if (!valid_info)
3402     info(HA_STATUS_VARIABLE);
3403 
3404   if (tdbp)
3405     return stats.records;
3406   else
3407     return HA_POS_ERROR;
3408 
3409 } // end of records
3410 
3411 
check(THD * thd,HA_CHECK_OPT * check_opt)3412 int ha_connect::check(THD* thd, HA_CHECK_OPT* check_opt)
3413 {
3414 	int     rc= HA_ADMIN_OK;
3415 	PGLOBAL g= ((table && table->in_use) ? GetPlug(table->in_use, xp) :
3416 		(xp) ? xp->g : NULL);
3417 	DBUG_ENTER("ha_connect::check");
3418 
3419 	if (!g || !table || xmod != MODE_READ)
3420 		DBUG_RETURN(HA_ADMIN_INTERNAL_ERROR);
3421 
3422 	// Do not close the table if it was opened yet (possible?)
3423 	if (IsOpened()) {
3424 		if (IsPartitioned() && CheckColumnList(g)) // map can have been changed
3425 			rc= HA_ADMIN_CORRUPT;
3426 		else if (tdbp->OpenDB(g))      // Rewind table
3427 			rc= HA_ADMIN_CORRUPT;
3428 
3429 	} else if (xp->CheckQuery(valid_query_id)) {
3430 		tdbp= NULL;       // Not valid anymore
3431 
3432 		if (OpenTable(g, false))
3433 			rc= HA_ADMIN_CORRUPT;
3434 
3435 	} else // possible?
3436 		DBUG_RETURN(HA_ADMIN_INTERNAL_ERROR);
3437 
3438 	if (rc == HA_ADMIN_OK) {
3439 		TABTYPE type= GetTypeID(GetStringOption("Type", "*"));
3440 
3441 		if (IsFileType(type)) {
3442 			if (check_opt->flags & T_MEDIUM) {
3443 				// TO DO
3444 				do {
3445 					if ((rc= CntReadNext(g, tdbp)) == RC_FX)
3446 						break;
3447 
3448 				} while (rc != RC_EF);
3449 
3450 				rc= (rc == RC_EF) ? HA_ADMIN_OK : HA_ADMIN_CORRUPT;
3451 			} else if (check_opt->flags & T_EXTEND) {
3452 				// TO DO
3453 			} // endif's flags
3454 
3455 		} // endif file type
3456 
3457 	} else
3458 		PushWarning(g, thd, 1);
3459 
3460 	DBUG_RETURN(rc);
3461 }	// end of check
3462 
3463 
3464 /**
3465   Return an error message specific to this handler.
3466 
3467   @param error  error code previously returned by handler
3468   @param buf    pointer to String where to add error message
3469 
3470   @return
3471     Returns true if this is a temporary error
3472 */
get_error_message(int error,String * buf)3473 bool ha_connect::get_error_message(int error, String* buf)
3474 {
3475   DBUG_ENTER("ha_connect::get_error_message");
3476 
3477 	if (xp && xp->g) {
3478 		PGLOBAL g= xp->g;
3479 
3480 		if (trace(1))
3481 			htrc("GEM(%d): %s\n", error, g->Message);
3482 
3483 		buf->append(ErrConvString(g->Message, strlen(g->Message),
3484 			&my_charset_latin1).ptr());
3485 	} else
3486     buf->append("Cannot retrieve error message");
3487 
3488   DBUG_RETURN(false);
3489 } // end of get_error_message
3490 
3491 /**
3492   Convert a filename partition name to system
3493 */
decode(PGLOBAL g,const char * pn)3494 static char *decode(PGLOBAL g, const char *pn)
3495   {
3496   char  *buf= (char*)PlugSubAlloc(g, NULL, strlen(pn) + 1);
3497   uint   dummy_errors;
3498   uint32 len= copy_and_convert(buf, strlen(pn) + 1,
3499                                system_charset_info,
3500                                pn, strlen(pn),
3501                                &my_charset_filename,
3502                                &dummy_errors);
3503   buf[len]= '\0';
3504   return buf;
3505   } // end of decode
3506 
3507 /**
3508   @brief
3509   Used for opening tables. The name will be the name of the file.
3510 
3511   @details
3512   A table is opened when it needs to be opened; e.g. when a request comes in
3513   for a SELECT on the table (tables are not open and closed for each request,
3514   they are cached).
3515 
3516   Called from handler.cc by handler::ha_open(). The server opens all tables by
3517   calling ha_open() which then calls the handler specific open().
3518 
3519   @note
3520   For CONNECT no open can be done here because field information is not yet
3521   updated. >>>>> TO BE CHECKED <<<<<
3522   (Thread information could be get by using 'ha_thd')
3523 
3524   @see
3525   handler::ha_open() in handler.cc
3526 */
open(const char * name,int mode,uint test_if_locked)3527 int ha_connect::open(const char *name, int mode, uint test_if_locked)
3528 {
3529   int rc= 0;
3530   DBUG_ENTER("ha_connect::open");
3531 
3532   if (trace(1))
3533      htrc("open: name=%s mode=%d test=%u\n", name, mode, test_if_locked);
3534 
3535   if (!(share= get_share()))
3536     DBUG_RETURN(1);
3537 
3538   thr_lock_data_init(&share->lock,&lock,NULL);
3539 
3540   // Try to get the user if possible
3541   xp= GetUser(ha_thd(), xp);
3542   PGLOBAL g= (xp) ? xp->g : NULL;
3543 
3544   // Try to set the database environment
3545   if (g) {
3546     rc= (CntCheckDB(g, this, name)) ? (-2) : 0;
3547 
3548     if (g->Mrr) {
3549       // This should only happen for the mrr secondary handler
3550       mrr= true;
3551       g->Mrr= false;
3552     } else
3553       mrr= false;
3554 
3555 #if defined(WITH_PARTITION_STORAGE_ENGINE)
3556     if (table->part_info) {
3557       if (GetStringOption("Filename") || GetStringOption("Tabname")
3558                                       || GetStringOption("Connect")) {
3559         strncpy(partname, decode(g, strrchr(name, '#') + 1), sizeof(partname) - 1);
3560 //      strcpy(partname, table->part_info->curr_part_elem->partition_name);
3561 //      part_id= &table->part_info->full_part_field_set;
3562       } else       // Inward table
3563         strncpy(partname, strrchr(name, slash) + 1, sizeof(partname) - 1);
3564 
3565       part_id= &table->part_info->full_part_field_set; // Temporary
3566       } // endif part_info
3567 #endif   // WITH_PARTITION_STORAGE_ENGINE
3568   } else
3569     rc= HA_ERR_INTERNAL_ERROR;
3570 
3571   DBUG_RETURN(rc);
3572 } // end of open
3573 
3574 /**
3575   @brief
3576   Make the indexes for this table
3577 */
optimize(THD * thd,HA_CHECK_OPT *)3578 int ha_connect::optimize(THD* thd, HA_CHECK_OPT*)
3579 {
3580   int      rc= 0;
3581   PGLOBAL& g= xp->g;
3582   PDBUSER  dup= PlgGetUser(g);
3583 
3584 	try {
3585 		// Ignore error on the opt file
3586 		dup->Check &= ~CHK_OPT;
3587 		tdbp= GetTDB(g);
3588 		dup->Check |= CHK_OPT;
3589 
3590 		if (tdbp && !tdbp->IsRemote()) {
3591 			bool dop= IsTypeIndexable(GetRealType(NULL));
3592 			bool dox= (tdbp->GetDef()->Indexable() == 1);
3593 
3594 			if ((rc= ((PTDBASE)tdbp)->ResetTableOpt(g, dop, dox))) {
3595 				if (rc == RC_INFO) {
3596 					push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
3597 					rc= 0;
3598 				} else
3599 					rc= HA_ERR_CRASHED_ON_USAGE;		// Table must be repaired
3600 
3601 			} // endif rc
3602 
3603 		} else if (!tdbp)
3604 			rc= HA_ERR_INTERNAL_ERROR;
3605 
3606 	} catch (int n) {
3607 		if (trace(1))
3608 			htrc("Exception %d: %s\n", n, g->Message);
3609 		rc= HA_ERR_INTERNAL_ERROR;
3610 	} catch (const char *msg) {
3611 		strcpy(g->Message, msg);
3612 		rc= HA_ERR_INTERNAL_ERROR;
3613 	} // end catch
3614 
3615 	if (rc)
3616 		my_message(ER_WARN_DATA_OUT_OF_RANGE, g->Message, MYF(0));
3617 
3618   return rc;
3619 } // end of optimize
3620 
3621 /**
3622   @brief
3623   Closes a table.
3624 
3625   @details
3626   Called from sql_base.cc, sql_select.cc, and table.cc. In sql_select.cc it is
3627   only used to close up temporary tables or during the process where a
3628   temporary table is converted over to being a myisam table.
3629 
3630   For sql_base.cc look at close_data_tables().
3631 
3632   @see
3633   sql_base.cc, sql_select.cc and table.cc
3634 */
close(void)3635 int ha_connect::close(void)
3636 {
3637   int rc= 0;
3638   DBUG_ENTER("ha_connect::close");
3639 
3640   // If this is called by a later query, the table may have
3641   // been already closed and the tdbp is not valid anymore.
3642   if (tdbp && xp->last_query_id == valid_query_id)
3643     rc= CloseTable(xp->g);
3644 
3645   DBUG_RETURN(rc);
3646 } // end of close
3647 
3648 
3649 /**
3650   @brief
3651   write_row() inserts a row. No extra() hint is given currently if a bulk load
3652   is happening. buf() is a byte array of data. You can use the field
3653   information to extract the data from the native byte array type.
3654 
3655     @details
3656   Example of this would be:
3657     @code
3658   for (Field **field=table->field ; *field ; field++)
3659   {
3660     ...
3661   }
3662     @endcode
3663 
3664   See ha_tina.cc for an example of extracting all of the data as strings.
3665   ha_berekly.cc has an example of how to store it intact by "packing" it
3666   for ha_berkeley's own native storage type.
3667 
3668   See the note for update_row() on auto_increments and timestamps. This
3669   case also applies to write_row().
3670 
3671   Called from item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc,
3672   sql_insert.cc, sql_select.cc, sql_table.cc, sql_udf.cc, and sql_update.cc.
3673 
3674     @see
3675   item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc,
3676   sql_insert.cc, sql_select.cc, sql_table.cc, sql_udf.cc and sql_update.cc
3677 */
write_row(const uchar * buf)3678 int ha_connect::write_row(const uchar *buf)
3679 {
3680   int      rc= 0;
3681   PGLOBAL& g= xp->g;
3682   DBUG_ENTER("ha_connect::write_row");
3683 
3684   // This is not tested yet
3685   if (xmod == MODE_ALTER) {
3686     if (IsPartitioned() && GetStringOption("Filename", NULL))
3687       // Why does this happen now that check_if_supported_inplace_alter is called?
3688       DBUG_RETURN(0);     // Alter table on an outward partition table
3689 
3690     xmod= MODE_INSERT;
3691   } else if (xmod == MODE_ANY)
3692     DBUG_RETURN(0);       // Probably never met
3693 
3694   // Open the table if it was not opened yet (locked)
3695   if (!IsOpened() || xmod != tdbp->GetMode()) {
3696     if (IsOpened())
3697       CloseTable(g);
3698 
3699     if ((rc= OpenTable(g)))
3700       DBUG_RETURN(rc);
3701 
3702     } // endif isopened
3703 
3704 #if 0                // AUTO_INCREMENT NIY
3705   if (table->next_number_field && buf == table->record[0]) {
3706     int error;
3707 
3708     if ((error= update_auto_increment()))
3709       return error;
3710 
3711     } // endif nex_number_field
3712 #endif // 0
3713 
3714   // Set column values from the passed pseudo record
3715   if ((rc= ScanRecord(g, buf)))
3716     DBUG_RETURN(rc);
3717 
3718   // Return result code from write operation
3719   if (CntWriteRow(g, tdbp)) {
3720     DBUG_PRINT("write_row", ("%s", g->Message));
3721     htrc("write_row: %s\n", g->Message);
3722     rc= HA_ERR_INTERNAL_ERROR;
3723   } else                // Table is modified
3724     nox= false;         // Indexes to be remade
3725 
3726   DBUG_RETURN(rc);
3727 } // end of write_row
3728 
3729 
3730 /**
3731   @brief
3732   Yes, update_row() does what you expect, it updates a row. old_data will have
3733   the previous row record in it, while new_data will have the newest data in it.
3734   Keep in mind that the server can do updates based on ordering if an ORDER BY
3735   clause was used. Consecutive ordering is not guaranteed.
3736 
3737     @details
3738   Currently new_data will not have an updated auto_increament record, or
3739   and updated timestamp field. You can do these for example by doing:
3740     @code
3741   if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
3742     table->timestamp_field->set_time();
3743   if (table->next_number_field && record == table->record[0])
3744     update_auto_increment();
3745     @endcode
3746 
3747   Called from sql_select.cc, sql_acl.cc, sql_update.cc, and sql_insert.cc.
3748 
3749     @see
3750   sql_select.cc, sql_acl.cc, sql_update.cc and sql_insert.cc
3751 */
update_row(const uchar * old_data,const uchar * new_data)3752 int ha_connect::update_row(const uchar *old_data, const uchar *new_data)
3753 {
3754   int      rc= 0;
3755   PGLOBAL& g= xp->g;
3756   DBUG_ENTER("ha_connect::update_row");
3757 
3758   if (trace(2))
3759     htrc("update_row: old=%s new=%s\n", old_data, new_data);
3760 
3761   // Check values for possible change in indexed column
3762   if ((rc= CheckRecord(g, old_data, new_data)))
3763     DBUG_RETURN(rc);
3764 
3765   if (CntUpdateRow(g, tdbp)) {
3766     DBUG_PRINT("update_row", ("%s", g->Message));
3767     htrc("update_row CONNECT: %s\n", g->Message);
3768     rc= HA_ERR_INTERNAL_ERROR;
3769   } else
3770     nox= false;               // Table is modified
3771 
3772   DBUG_RETURN(rc);
3773 } // end of update_row
3774 
3775 
3776 /**
3777   @brief
3778   This will delete a row. buf will contain a copy of the row to be deleted.
3779   The server will call this right after the current row has been called (from
3780   either a previous rnd_nexT() or index call).
3781 
3782   @details
3783   If you keep a pointer to the last row or can access a primary key it will
3784   make doing the deletion quite a bit easier. Keep in mind that the server does
3785   not guarantee consecutive deletions. ORDER BY clauses can be used.
3786 
3787   Called in sql_acl.cc and sql_udf.cc to manage internal table
3788   information.  Called in sql_delete.cc, sql_insert.cc, and
3789   sql_select.cc. In sql_select it is used for removing duplicates
3790   while in insert it is used for REPLACE calls.
3791 
3792   @see
3793   sql_acl.cc, sql_udf.cc, sql_delete.cc, sql_insert.cc and sql_select.cc
3794 */
delete_row(const uchar *)3795 int ha_connect::delete_row(const uchar *)
3796 {
3797   int rc= 0;
3798   DBUG_ENTER("ha_connect::delete_row");
3799 
3800   if (CntDeleteRow(xp->g, tdbp, false)) {
3801     rc= HA_ERR_INTERNAL_ERROR;
3802     htrc("delete_row CONNECT: %s\n", xp->g->Message);
3803   } else
3804     nox= false;             // To remake indexes
3805 
3806   DBUG_RETURN(rc);
3807 } // end of delete_row
3808 
3809 
3810 /****************************************************************************/
3811 /*  We seem to come here at the begining of an index use.                   */
3812 /****************************************************************************/
index_init(uint idx,bool sorted)3813 int ha_connect::index_init(uint idx, bool sorted)
3814 {
3815   int rc;
3816   PGLOBAL& g= xp->g;
3817   DBUG_ENTER("index_init");
3818 
3819   if (trace(1))
3820     htrc("index_init: this=%p idx=%u sorted=%d\n", this, idx, sorted);
3821 
3822   if (GetIndexType(GetRealType()) == 2) {
3823     if (xmod == MODE_READ)
3824       // This is a remote index
3825       xmod= MODE_READX;
3826 
3827     if (!(rc= rnd_init(0))) {
3828 //    if (xmod == MODE_READX) {
3829         active_index= idx;
3830         indexing= IsUnique(idx) ? 1 : 2;
3831 //    } else {
3832 //      active_index= MAX_KEY;
3833 //      indexing= 0;
3834 //    } // endif xmod
3835 
3836       } //endif rc
3837 
3838     DBUG_RETURN(rc);
3839     } // endif index type
3840 
3841   if ((rc= rnd_init(0)))
3842     DBUG_RETURN(rc);
3843 
3844   if (locked == 2) {
3845     // Indexes are not updated in lock write mode
3846     active_index= MAX_KEY;
3847     indexing= 0;
3848     DBUG_RETURN(0);
3849     } // endif locked
3850 
3851   indexing= CntIndexInit(g, tdbp, (signed)idx, sorted);
3852 
3853   if (indexing <= 0) {
3854     DBUG_PRINT("index_init", ("%s", g->Message));
3855     htrc("index_init CONNECT: %s\n", g->Message);
3856     active_index= MAX_KEY;
3857     rc= HA_ERR_INTERNAL_ERROR;
3858   } else if (tdbp->GetKindex()) {
3859     if (((PTDBDOS)tdbp)->GetKindex()->GetNum_K()) {
3860       if (tdbp->GetFtype() != RECFM_NAF)
3861         ((PTDBDOS)tdbp)->GetTxfp()->ResetBuffer(g);
3862 
3863       active_index= idx;
3864 //  } else {        // Void table
3865 //    active_index= MAX_KEY;
3866 //    indexing= 0;
3867     } // endif Num
3868 
3869     rc= 0;
3870   } // endif indexing
3871 
3872   if (trace(1))
3873     htrc("index_init: rc=%d indexing=%d active_index=%d\n",
3874             rc, indexing, active_index);
3875 
3876   DBUG_RETURN(rc);
3877 } // end of index_init
3878 
3879 /****************************************************************************/
3880 /*  We seem to come here at the end of an index use.                        */
3881 /****************************************************************************/
index_end()3882 int ha_connect::index_end()
3883 {
3884   DBUG_ENTER("index_end");
3885   active_index= MAX_KEY;
3886   ds_mrr.dsmrr_close();
3887   DBUG_RETURN(rnd_end());
3888 } // end of index_end
3889 
3890 
3891 /****************************************************************************/
3892 /*  This is internally called by all indexed reading functions.             */
3893 /****************************************************************************/
ReadIndexed(uchar * buf,OPVAL op,const key_range * kr)3894 int ha_connect::ReadIndexed(uchar *buf, OPVAL op, const key_range *kr)
3895 {
3896   int rc;
3897 
3898 //statistic_increment(ha_read_key_count, &LOCK_status);
3899 
3900   switch (CntIndexRead(xp->g, tdbp, op, kr, mrr)) {
3901     case RC_OK:
3902       xp->fnd++;
3903       rc= MakeRecord((char*)buf);
3904       break;
3905     case RC_EF:         // End of file
3906       rc= HA_ERR_END_OF_FILE;
3907       break;
3908     case RC_NF:         // Not found
3909       xp->nfd++;
3910       rc= (op == OP_SAME) ? HA_ERR_END_OF_FILE : HA_ERR_KEY_NOT_FOUND;
3911       break;
3912     default:          // Read error
3913       DBUG_PRINT("ReadIndexed", ("%s", xp->g->Message));
3914       htrc("ReadIndexed: %s\n", xp->g->Message);
3915       rc= HA_ERR_INTERNAL_ERROR;
3916       break;
3917     } // endswitch RC
3918 
3919   if (trace(2))
3920     htrc("ReadIndexed: op=%d rc=%d\n", op, rc);
3921 
3922   table->status= (rc == RC_OK) ? 0 : STATUS_NOT_FOUND;
3923   return rc;
3924 } // end of ReadIndexed
3925 
3926 
3927 #ifdef NOT_USED
3928 /**
3929   @brief
3930   Positions an index cursor to the index specified in the handle. Fetches the
3931   row if available. If the key value is null, begin at the first key of the
3932   index.
3933 */
index_read_map(uchar * buf,const uchar * key,key_part_map keypart_map,enum ha_rkey_function find_flag)3934 int ha_connect::index_read_map(uchar *buf, const uchar *key,
3935                                key_part_map keypart_map __attribute__((unused)),
3936                                enum ha_rkey_function find_flag
3937                                __attribute__((unused)))
3938 {
3939   DBUG_ENTER("ha_connect::index_read");
3940   DBUG_RETURN(HA_ERR_WRONG_COMMAND);
3941 }
3942 #endif // NOT_USED
3943 
3944 
3945 /****************************************************************************/
3946 /*  This is called by handler::index_read_map.                              */
3947 /****************************************************************************/
index_read(uchar * buf,const uchar * key,uint key_len,enum ha_rkey_function find_flag)3948 int ha_connect::index_read(uchar * buf, const uchar * key, uint key_len,
3949                            enum ha_rkey_function find_flag)
3950 {
3951   int rc;
3952   OPVAL op= OP_XX;
3953   DBUG_ENTER("ha_connect::index_read");
3954 
3955   switch(find_flag) {
3956     case HA_READ_KEY_EXACT:   op= OP_EQ; break;
3957     case HA_READ_AFTER_KEY:   op= OP_GT; break;
3958     case HA_READ_KEY_OR_NEXT: op= OP_GE; break;
3959     default: DBUG_RETURN(-1);      break;
3960     } // endswitch find_flag
3961 
3962   if (trace(2))
3963     htrc("%p index_read: op=%d\n", this, op);
3964 
3965   if (indexing > 0) {
3966 		start_key.key= key;
3967 		start_key.length= key_len;
3968 		start_key.flag= find_flag;
3969 		start_key.keypart_map= 0;
3970 
3971     rc= ReadIndexed(buf, op, &start_key);
3972 
3973     if (rc == HA_ERR_INTERNAL_ERROR) {
3974       nox= true;                  // To block making indexes
3975       abort= true;                // Don't rename temp file
3976       } // endif rc
3977 
3978   } else
3979     rc= HA_ERR_INTERNAL_ERROR;  // HA_ERR_KEY_NOT_FOUND ?
3980 
3981   DBUG_RETURN(rc);
3982 } // end of index_read
3983 
3984 
3985 /**
3986   @brief
3987   Used to read forward through the index.
3988 */
index_next(uchar * buf)3989 int ha_connect::index_next(uchar *buf)
3990 {
3991   int rc;
3992   DBUG_ENTER("ha_connect::index_next");
3993   //statistic_increment(ha_read_next_count, &LOCK_status);
3994 
3995   if (indexing > 0)
3996     rc= ReadIndexed(buf, OP_NEXT);
3997   else if (!indexing)
3998     rc= rnd_next(buf);
3999   else
4000     rc= HA_ERR_INTERNAL_ERROR;
4001 
4002   DBUG_RETURN(rc);
4003 } // end of index_next
4004 
4005 
4006 /**
4007   @brief
4008   Used to read backwards through the index.
4009 */
index_prev(uchar * buf)4010 int ha_connect::index_prev(uchar *buf)
4011 {
4012   DBUG_ENTER("ha_connect::index_prev");
4013   int rc;
4014 
4015   if (indexing > 0) {
4016     rc= ReadIndexed(buf, OP_PREV);
4017   } else
4018     rc= HA_ERR_WRONG_COMMAND;
4019 
4020   DBUG_RETURN(rc);
4021 } // end of index_prev
4022 
4023 
4024 /**
4025   @brief
4026   index_first() asks for the first key in the index.
4027 
4028     @details
4029   Called from opt_range.cc, opt_sum.cc, sql_handler.cc, and sql_select.cc.
4030 
4031     @see
4032   opt_range.cc, opt_sum.cc, sql_handler.cc and sql_select.cc
4033 */
index_first(uchar * buf)4034 int ha_connect::index_first(uchar *buf)
4035 {
4036   int rc;
4037   DBUG_ENTER("ha_connect::index_first");
4038 
4039   if (indexing > 0)
4040     rc= ReadIndexed(buf, OP_FIRST);
4041   else if (indexing < 0)
4042     rc= HA_ERR_INTERNAL_ERROR;
4043   else if (CntRewindTable(xp->g, tdbp)) {
4044     table->status= STATUS_NOT_FOUND;
4045     rc= HA_ERR_INTERNAL_ERROR;
4046   } else
4047     rc= rnd_next(buf);
4048 
4049   DBUG_RETURN(rc);
4050 } // end of index_first
4051 
4052 
4053 /**
4054   @brief
4055   index_last() asks for the last key in the index.
4056 
4057     @details
4058   Called from opt_range.cc, opt_sum.cc, sql_handler.cc, and sql_select.cc.
4059 
4060     @see
4061   opt_range.cc, opt_sum.cc, sql_handler.cc and sql_select.cc
4062 */
index_last(uchar * buf)4063 int ha_connect::index_last(uchar *buf)
4064 {
4065   DBUG_ENTER("ha_connect::index_last");
4066   int rc;
4067 
4068   if (indexing <= 0) {
4069     rc= HA_ERR_INTERNAL_ERROR;
4070   } else
4071     rc= ReadIndexed(buf, OP_LAST);
4072 
4073   DBUG_RETURN(rc);
4074 }
4075 
4076 
4077 /****************************************************************************/
4078 /*  This is called to get more rows having the same index value.            */
4079 /****************************************************************************/
4080 //t ha_connect::index_next_same(uchar *buf, const uchar *key, uint keylen)
index_next_same(uchar * buf,const uchar *,uint)4081 int ha_connect::index_next_same(uchar *buf, const uchar *, uint)
4082 {
4083   int rc;
4084   DBUG_ENTER("ha_connect::index_next_same");
4085 //statistic_increment(ha_read_next_count, &LOCK_status);
4086 
4087   if (!indexing)
4088     rc= rnd_next(buf);
4089   else if (indexing > 0)
4090     rc= ReadIndexed(buf, OP_SAME);
4091   else
4092     rc= HA_ERR_INTERNAL_ERROR;
4093 
4094   DBUG_RETURN(rc);
4095 } // end of index_next_same
4096 
4097 
4098 /**
4099   @brief
4100   rnd_init() is called when the system wants the storage engine to do a table
4101   scan. See the example in the introduction at the top of this file to see when
4102   rnd_init() is called.
4103 
4104     @details
4105   Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc,
4106   and sql_update.cc.
4107 
4108     @note
4109   We always call open and extern_lock/start_stmt before comming here.
4110 
4111     @see
4112   filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc and sql_update.cc
4113 */
rnd_init(bool scan)4114 int ha_connect::rnd_init(bool scan)
4115 {
4116   PGLOBAL g= ((table && table->in_use) ? GetPlug(table->in_use, xp) :
4117               (xp) ? xp->g : NULL);
4118   DBUG_ENTER("ha_connect::rnd_init");
4119 
4120   // This is not tested yet
4121   if (xmod == MODE_ALTER) {
4122     xmod= MODE_READ;
4123     alter= 1;
4124     } // endif xmod
4125 
4126   if (trace(1))
4127     htrc("rnd_init: this=%p scan=%d xmod=%d alter=%d\n",
4128             this, scan, xmod, alter);
4129 
4130   if (!g || !table || xmod == MODE_INSERT)
4131     DBUG_RETURN(HA_ERR_INITIALIZATION);
4132 
4133   // Do not close the table if it was opened yet (locked?)
4134   if (IsOpened()) {
4135     if (IsPartitioned() && xmod != MODE_INSERT)
4136       if (CheckColumnList(g)) // map can have been changed
4137         DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
4138 
4139     if (tdbp->OpenDB(g))      // Rewind table
4140       DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
4141     else
4142       DBUG_RETURN(0);
4143 
4144   } else if (xp->CheckQuery(valid_query_id))
4145     tdbp= NULL;       // Not valid anymore
4146 
4147   // When updating, to avoid skipped update, force the table
4148   // handler to retrieve write-only fields to be able to compare
4149   // records and detect data change.
4150   if (xmod == MODE_UPDATE)
4151     bitmap_union(table->read_set, table->write_set);
4152 
4153   if (OpenTable(g, xmod == MODE_DELETE))
4154     DBUG_RETURN(HA_ERR_INITIALIZATION);
4155 
4156   xp->nrd= xp->fnd= xp->nfd= 0;
4157   xp->tb1= my_interval_timer();
4158   DBUG_RETURN(0);
4159 } // end of rnd_init
4160 
4161 /**
4162   @brief
4163   Not described.
4164 
4165   @note
4166   The previous version said:
4167   Stop scanning of table. Note that this may be called several times during
4168   execution of a sub select.
4169   =====> This has been moved to external lock to avoid closing subselect tables.
4170 */
rnd_end()4171 int ha_connect::rnd_end()
4172 {
4173   int rc= 0;
4174   DBUG_ENTER("ha_connect::rnd_end");
4175 
4176   // If this is called by a later query, the table may have
4177   // been already closed and the tdbp is not valid anymore.
4178 //  if (tdbp && xp->last_query_id == valid_query_id)
4179 //    rc= CloseTable(xp->g);
4180 
4181   ds_mrr.dsmrr_close();
4182   DBUG_RETURN(rc);
4183 } // end of rnd_end
4184 
4185 
4186 /**
4187   @brief
4188   This is called for each row of the table scan. When you run out of records
4189   you should return HA_ERR_END_OF_FILE. Fill buff up with the row information.
4190   The Field structure for the table is the key to getting data into buf
4191   in a manner that will allow the server to understand it.
4192 
4193     @details
4194   Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc,
4195   and sql_update.cc.
4196 
4197     @see
4198   filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc and sql_update.cc
4199 */
rnd_next(uchar * buf)4200 int ha_connect::rnd_next(uchar *buf)
4201 {
4202   int rc;
4203   DBUG_ENTER("ha_connect::rnd_next");
4204 //statistic_increment(ha_read_rnd_next_count, &LOCK_status);
4205 
4206   if (tdbp->GetMode() == MODE_ANY) {
4207     // We will stop on next read
4208     if (!stop) {
4209       stop= true;
4210       DBUG_RETURN(RC_OK);
4211     } else
4212       DBUG_RETURN(HA_ERR_END_OF_FILE);
4213 
4214     } // endif Mode
4215 
4216   switch (CntReadNext(xp->g, tdbp)) {
4217     case RC_OK:
4218       rc= MakeRecord((char*)buf);
4219       break;
4220     case RC_EF:         // End of file
4221       rc= HA_ERR_END_OF_FILE;
4222       break;
4223     case RC_NF:         // Not found
4224       rc= HA_ERR_RECORD_DELETED;
4225       break;
4226     default:            // Read error
4227       htrc("rnd_next CONNECT: %s\n", xp->g->Message);
4228       rc= (records()) ? HA_ERR_INTERNAL_ERROR : HA_ERR_END_OF_FILE;
4229       break;
4230     } // endswitch RC
4231 
4232   if (trace(2) && (rc || !(xp->nrd++ % 16384))) {
4233     ulonglong tb2= my_interval_timer();
4234     double elapsed= (double) (tb2 - xp->tb1) / 1000000000ULL;
4235     DBUG_PRINT("rnd_next", ("rc=%d nrd=%u fnd=%u nfd=%u sec=%.3lf\n",
4236                              rc, (uint)xp->nrd, (uint)xp->fnd,
4237                              (uint)xp->nfd, elapsed));
4238     htrc("rnd_next: rc=%d nrd=%u fnd=%u nfd=%u sec=%.3lf\n",
4239                              rc, (uint)xp->nrd, (uint)xp->fnd,
4240                              (uint)xp->nfd, elapsed);
4241     xp->tb1= tb2;
4242     xp->fnd= xp->nfd= 0;
4243     } // endif nrd
4244 
4245   table->status= (!rc) ? 0 : STATUS_NOT_FOUND;
4246   DBUG_RETURN(rc);
4247 } // end of rnd_next
4248 
4249 
4250 /**
4251   @brief
4252   position() is called after each call to rnd_next() if the data needs
4253   to be ordered. You can do something like the following to store
4254   the position:
4255     @code
4256   my_store_ptr(ref, ref_length, current_position);
4257     @endcode
4258 
4259     @details
4260   The server uses ref to store data. ref_length in the above case is
4261   the size needed to store current_position. ref is just a byte array
4262   that the server will maintain. If you are using offsets to mark rows, then
4263   current_position should be the offset. If it is a primary key like in
4264   BDB, then it needs to be a primary key.
4265 
4266   Called from filesort.cc, sql_select.cc, sql_delete.cc, and sql_update.cc.
4267 
4268     @see
4269   filesort.cc, sql_select.cc, sql_delete.cc and sql_update.cc
4270 */
position(const uchar *)4271 void ha_connect::position(const uchar *)
4272 {
4273   DBUG_ENTER("ha_connect::position");
4274   my_store_ptr(ref, ref_length, (my_off_t)tdbp->GetRecpos());
4275 
4276   if (trace(2))
4277     htrc("position: pos=%d\n", tdbp->GetRecpos());
4278 
4279   DBUG_VOID_RETURN;
4280 } // end of position
4281 
4282 
4283 /**
4284   @brief
4285   This is like rnd_next, but you are given a position to use
4286   to determine the row. The position will be of the type that you stored in
4287   ref. You can use my_get_ptr(pos,ref_length) to retrieve whatever key
4288   or position you saved when position() was called.
4289 
4290     @details
4291   Called from filesort.cc, records.cc, sql_insert.cc, sql_select.cc, and sql_update.cc.
4292 
4293     @note
4294   Is this really useful? It was never called even when sorting.
4295 
4296     @see
4297   filesort.cc, records.cc, sql_insert.cc, sql_select.cc and sql_update.cc
4298 */
rnd_pos(uchar * buf,uchar * pos)4299 int ha_connect::rnd_pos(uchar *buf, uchar *pos)
4300 {
4301   int     rc;
4302 //PTDBASE tp= (PTDBASE)tdbp;
4303   DBUG_ENTER("ha_connect::rnd_pos");
4304 
4305   if (!tdbp->SetRecpos(xp->g, (int)my_get_ptr(pos, ref_length))) {
4306     if (trace(1))
4307       htrc("rnd_pos: %d\n", tdbp->GetRecpos());
4308 
4309     tdbp->SetFilter(NULL);
4310     rc= rnd_next(buf);
4311 	} else {
4312 		PGLOBAL g= GetPlug((table) ? table->in_use : NULL, xp);
4313 //	strcpy(g->Message, "Not supported by this table type");
4314 		my_message(ER_ILLEGAL_HA, g->Message, MYF(0));
4315 		rc= HA_ERR_INTERNAL_ERROR;
4316 	}	// endif SetRecpos
4317 
4318   DBUG_RETURN(rc);
4319 } // end of rnd_pos
4320 
4321 
4322 /**
4323   @brief
4324   ::info() is used to return information to the optimizer. See my_base.h for
4325   the complete description.
4326 
4327     @details
4328   Currently this table handler doesn't implement most of the fields really needed.
4329   SHOW also makes use of this data.
4330 
4331   You will probably want to have the following in your code:
4332     @code
4333   if (records < 2)
4334     records= 2;
4335     @endcode
4336   The reason is that the server will optimize for cases of only a single
4337   record. If, in a table scan, you don't know the number of records, it
4338   will probably be better to set records to two so you can return as many
4339   records as you need. Along with records, a few more variables you may wish
4340   to set are:
4341     records
4342     deleted
4343     data_file_length
4344     index_file_length
4345     delete_length
4346     check_time
4347   Take a look at the public variables in handler.h for more information.
4348 
4349   Called in filesort.cc, ha_heap.cc, item_sum.cc, opt_sum.cc, sql_delete.cc,
4350   sql_delete.cc, sql_derived.cc, sql_select.cc, sql_select.cc, sql_select.cc,
4351   sql_select.cc, sql_select.cc, sql_show.cc, sql_show.cc, sql_show.cc, sql_show.cc,
4352   sql_table.cc, sql_union.cc, and sql_update.cc.
4353 
4354     @see
4355   filesort.cc, ha_heap.cc, item_sum.cc, opt_sum.cc, sql_delete.cc, sql_delete.cc,
4356   sql_derived.cc, sql_select.cc, sql_select.cc, sql_select.cc, sql_select.cc,
4357   sql_select.cc, sql_show.cc, sql_show.cc, sql_show.cc, sql_show.cc, sql_table.cc,
4358   sql_union.cc and sql_update.cc
4359 */
info(uint flag)4360 int ha_connect::info(uint flag)
4361 {
4362   bool    pure= false;
4363   PGLOBAL g= GetPlug((table) ? table->in_use : NULL, xp);
4364 
4365   DBUG_ENTER("ha_connect::info");
4366 
4367 	if (!g) {
4368 		my_message(ER_UNKNOWN_ERROR, "Cannot get g pointer", MYF(0));
4369 		DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
4370 	}	// endif g
4371 
4372 	if (trace(1))
4373     htrc("%p In info: flag=%u valid_info=%d\n", this, flag, valid_info);
4374 
4375   // tdbp must be available to get updated info
4376   if (xp->CheckQuery(valid_query_id) || !tdbp) {
4377 
4378     if (xmod == MODE_ANY || xmod == MODE_ALTER) {
4379       // Pure info, not a query
4380       pure= true;
4381       xp->CheckCleanup(xmod == MODE_ANY && valid_query_id == 0);
4382       } // endif xmod
4383 
4384     // This is necessary for getting file length
4385 		if (table) {
4386 			if (SetDataPath(g, table->s->db.str)) {
4387 				my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
4388 				DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
4389 			}	// endif SetDataPath
4390 
4391 		} else
4392       DBUG_RETURN(HA_ERR_INTERNAL_ERROR);       // Should never happen
4393 
4394 		if (!(tdbp= GetTDB(g))) {
4395 			my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
4396 			DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
4397 		} // endif tdbp
4398 
4399     valid_info= false;
4400     } // endif tdbp
4401 
4402   if (!valid_info) {
4403     valid_info= CntInfo(g, tdbp, &xinfo);
4404 
4405     if (((signed)xinfo.records) < 0)
4406       DBUG_RETURN(HA_ERR_INITIALIZATION);  // Error in Cardinality
4407 
4408     } // endif valid_info
4409 
4410   if (flag & HA_STATUS_VARIABLE) {
4411     stats.records= xinfo.records;
4412     stats.deleted= 0;
4413     stats.data_file_length= xinfo.data_file_length;
4414     stats.index_file_length= 0;
4415     stats.delete_length= 0;
4416     stats.check_time= 0;
4417     stats.mean_rec_length= xinfo.mean_rec_length;
4418     } // endif HA_STATUS_VARIABLE
4419 
4420   if (flag & HA_STATUS_CONST) {
4421     // This is imported from the previous handler and must be reconsidered
4422     stats.max_data_file_length= 4294967295LL;
4423     stats.max_index_file_length= 4398046510080LL;
4424     stats.create_time= 0;
4425     data_file_name= xinfo.data_file_name;
4426     index_file_name= NULL;
4427 //  sortkey= (uint) - 1;           // Table is not sorted
4428     ref_length= sizeof(int);      // Pointer size to row
4429     table->s->db_options_in_use= 03;
4430     stats.block_size= 1024;
4431     table->s->keys_in_use.set_prefix(table->s->keys);
4432     table->s->keys_for_keyread= table->s->keys_in_use;
4433 //  table->s->keys_for_keyread.subtract(table->s->read_only_keys);
4434     table->s->db_record_offset= 0;
4435     } // endif HA_STATUS_CONST
4436 
4437   if (flag & HA_STATUS_ERRKEY) {
4438     errkey= 0;
4439     } // endif HA_STATUS_ERRKEY
4440 
4441   if (flag & HA_STATUS_TIME)
4442     stats.update_time= 0;
4443 
4444   if (flag & HA_STATUS_AUTO)
4445     stats.auto_increment_value= 1;
4446 
4447   if (tdbp && pure)
4448     CloseTable(g);        // Not used anymore
4449 
4450   DBUG_RETURN(0);
4451 } // end of info
4452 
4453 
4454 /**
4455   @brief
4456   extra() is called whenever the server wishes to send a hint to
4457   the storage engine. The myisam engine implements the most hints.
4458   ha_innodb.cc has the most exhaustive list of these hints.
4459 
4460   @note
4461   This is not yet implemented for CONNECT.
4462 
4463   @see
4464   ha_innodb.cc
4465 */
extra(enum ha_extra_function)4466 int ha_connect::extra(enum ha_extra_function /*operation*/)
4467 {
4468   DBUG_ENTER("ha_connect::extra");
4469   DBUG_RETURN(0);
4470 } // end of extra
4471 
4472 
4473 /**
4474   @brief
4475   Used to delete all rows in a table, including cases of truncate and cases where
4476   the optimizer realizes that all rows will be removed as a result of an SQL statement.
4477 
4478     @details
4479   Called from item_sum.cc by Item_func_group_concat::clear(),
4480   Item_sum_count_distinct::clear(), and Item_func_group_concat::clear().
4481   Called from sql_delete.cc by mysql_delete().
4482   Called from sql_select.cc by JOIN::reinit().
4483   Called from sql_union.cc by st_select_lex_unit::exec().
4484 
4485     @see
4486   Item_func_group_concat::clear(), Item_sum_count_distinct::clear() and
4487   Item_func_group_concat::clear() in item_sum.cc;
4488   mysql_delete() in sql_delete.cc;
4489   JOIN::reinit() in sql_select.cc and
4490   st_select_lex_unit::exec() in sql_union.cc.
4491 */
delete_all_rows()4492 int ha_connect::delete_all_rows()
4493 {
4494   int     rc= 0;
4495   PGLOBAL g= xp->g;
4496   DBUG_ENTER("ha_connect::delete_all_rows");
4497 
4498   if (tdbp && tdbp->GetUse() == USE_OPEN &&
4499       tdbp->GetAmType() != TYPE_AM_XML &&
4500       tdbp->GetFtype() != RECFM_NAF)
4501     // Close and reopen the table so it will be deleted
4502     rc= CloseTable(g);
4503 
4504   if (!(rc= OpenTable(g))) {
4505     if (CntDeleteRow(g, tdbp, true)) {
4506       htrc("%s\n", g->Message);
4507       rc= HA_ERR_INTERNAL_ERROR;
4508     } else
4509       nox= false;
4510 
4511     } // endif rc
4512 
4513   DBUG_RETURN(rc);
4514 } // end of delete_all_rows
4515 
4516 
checkPrivileges(THD * thd,TABTYPE type,PTOS options,const char * db,TABLE * table,bool quick)4517 static bool checkPrivileges(THD *thd, TABTYPE type, PTOS options,
4518                             const char *db, TABLE *table, bool quick)
4519 {
4520   switch (type) {
4521     case TAB_UNDEF:
4522 //  case TAB_CATLG:
4523     case TAB_PLG:
4524     case TAB_JCT:
4525     case TAB_DMY:
4526     case TAB_NIY:
4527       my_printf_error(ER_UNKNOWN_ERROR,
4528                       "Unsupported table type %s", MYF(0), options->type);
4529       return true;
4530 
4531     case TAB_DOS:
4532     case TAB_FIX:
4533     case TAB_BIN:
4534     case TAB_CSV:
4535     case TAB_FMT:
4536     case TAB_DBF:
4537     case TAB_XML:
4538     case TAB_INI:
4539     case TAB_VEC:
4540 		case TAB_REST:
4541     case TAB_JSON:
4542 #if defined(BSON_SUPPORT)
4543     case TAB_BSON:
4544 #endif   // BSON_SUPPORT
4545       if (options->filename && *options->filename) {
4546 				if (!quick) {
4547 					char path[FN_REFLEN], dbpath[FN_REFLEN];
4548 
4549  					strcpy(dbpath, mysql_real_data_home);
4550 
4551 					if (db)
4552 #if defined(_WIN32)
4553 						strcat(strcat(dbpath, db), "\\");
4554 #else   // !_WIN32
4555 						strcat(strcat(dbpath, db), "/");
4556 #endif  // !_WIN32
4557 
4558 					(void)fn_format(path, options->filename, dbpath, "",
4559 						MY_RELATIVE_PATH | MY_UNPACK_FILENAME);
4560 
4561 					if (!is_secure_file_path(path)) {
4562 						my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--secure-file-priv");
4563 						return true;
4564 					} // endif path
4565 
4566 				}	// endif !quick
4567 
4568 			} else
4569         return false;
4570 
4571 			// Fall through
4572 		case TAB_MYSQL:
4573 		case TAB_DIR:
4574 		case TAB_ZIP:
4575 		case TAB_OEM:
4576       if (table && table->pos_in_table_list) { // if SELECT
4577 #if MYSQL_VERSION_ID > 100200
4578 				Switch_to_definer_security_ctx backup_ctx(thd, table->pos_in_table_list);
4579 #endif // VERSION_ID > 100200
4580         return check_global_access(thd, FILE_ACL);
4581       }	else
4582         return check_global_access(thd, FILE_ACL);
4583     case TAB_ODBC:
4584 		case TAB_JDBC:
4585 		case TAB_MONGO:
4586     case TAB_MAC:
4587     case TAB_WMI:
4588 			return false;
4589     case TAB_TBL:
4590     case TAB_XCL:
4591     case TAB_PRX:
4592     case TAB_OCCUR:
4593     case TAB_PIVOT:
4594     case TAB_VIR:
4595     default:
4596 			// This is temporary until a solution is found
4597 			return false;
4598     } // endswitch type
4599 
4600   my_printf_error(ER_UNKNOWN_ERROR, "check_privileges failed", MYF(0));
4601   return true;
4602 } // end of checkPrivileges
4603 
4604 // Check whether the user has required (file) privileges
check_privileges(THD * thd,PTOS options,const char * dbn,bool quick)4605 bool ha_connect::check_privileges(THD *thd, PTOS options, const char *dbn,
4606 				  bool quick)
4607 {
4608   const char *db= (dbn && *dbn) ? dbn : NULL;
4609   TABTYPE     type=GetRealType(options);
4610 
4611   return checkPrivileges(thd, type, options, db, table, quick);
4612 } // end of check_privileges
4613 
4614 // Check that two indexes are equivalent
IsSameIndex(PIXDEF xp1,PIXDEF xp2)4615 bool ha_connect::IsSameIndex(PIXDEF xp1, PIXDEF xp2)
4616 {
4617   bool   b= true;
4618   PKPDEF kp1, kp2;
4619 
4620   if (stricmp(xp1->Name, xp2->Name))
4621     b= false;
4622   else if (xp1->Nparts  != xp2->Nparts  ||
4623            xp1->MaxSame != xp2->MaxSame ||
4624            xp1->Unique  != xp2->Unique)
4625     b= false;
4626   else for (kp1= xp1->ToKeyParts, kp2= xp2->ToKeyParts;
4627             b && (kp1 || kp2);
4628             kp1= kp1->Next, kp2= kp2->Next)
4629     if (!kp1 || !kp2)
4630       b= false;
4631     else if (stricmp(kp1->Name, kp2->Name))
4632       b= false;
4633     else if (kp1->Klen != kp2->Klen)
4634       b= false;
4635 
4636   return b;
4637 } // end of IsSameIndex
4638 
CheckMode(PGLOBAL g,THD * thd,MODE newmode,bool * chk,bool * cras)4639 MODE ha_connect::CheckMode(PGLOBAL g, THD *thd,
4640 	                         MODE newmode, bool *chk, bool *cras)
4641 {
4642 #if defined(DEVELOPMENT)
4643 	if (true) {
4644 #else
4645   if (trace(65)) {
4646 #endif
4647     LEX_STRING *query_string= thd_query_string(thd);
4648     htrc("%p check_mode: cmdtype=%d\n", this, thd_sql_command(thd));
4649     htrc("Cmd=%.*s\n", (int) query_string->length, query_string->str);
4650     } // endif trace
4651 
4652   // Next code is temporarily replaced until sql_command is set
4653   stop= false;
4654 
4655   if (newmode == MODE_WRITE) {
4656     switch (thd_sql_command(thd)) {
4657       case SQLCOM_LOCK_TABLES:
4658         locked= 2; // fall through
4659       case SQLCOM_CREATE_TABLE:
4660       case SQLCOM_INSERT:
4661       case SQLCOM_LOAD:
4662       case SQLCOM_INSERT_SELECT:
4663         newmode= MODE_INSERT;
4664         break;
4665 //    case SQLCOM_REPLACE:
4666 //    case SQLCOM_REPLACE_SELECT:
4667 //      newmode= MODE_UPDATE;               // To be checked
4668 //      break;
4669 			case SQLCOM_DELETE_MULTI:
4670 				*cras= true;
4671                                 // fall through
4672 			case SQLCOM_DELETE:
4673       case SQLCOM_TRUNCATE:
4674         newmode= MODE_DELETE;
4675         break;
4676       case SQLCOM_UPDATE_MULTI:
4677 				*cras= true;
4678                                 // fall through
4679 			case SQLCOM_UPDATE:
4680 				newmode= MODE_UPDATE;
4681         break;
4682       case SQLCOM_SELECT:
4683       case SQLCOM_OPTIMIZE:
4684         newmode= MODE_READ;
4685         break;
4686       case SQLCOM_FLUSH:
4687         locked= 0;
4688         // fall through
4689       case SQLCOM_DROP_TABLE:
4690       case SQLCOM_RENAME_TABLE:
4691         newmode= MODE_ANY;
4692         break;
4693       case SQLCOM_CREATE_VIEW:
4694       case SQLCOM_DROP_VIEW:
4695         newmode= MODE_ANY;
4696         break;
4697       case SQLCOM_ALTER_TABLE:
4698         newmode= MODE_ALTER;
4699         break;
4700       case SQLCOM_DROP_INDEX:
4701       case SQLCOM_CREATE_INDEX:
4702 //      if (!IsPartitioned()) {
4703           newmode= MODE_ANY;
4704           break;
4705 //        } // endif partitioned
4706 			case SQLCOM_REPAIR: // TODO implement it
4707 				newmode= MODE_UPDATE;
4708 				break;
4709 			default:
4710         htrc("Unsupported sql_command=%d\n", thd_sql_command(thd));
4711         strcpy(g->Message, "CONNECT Unsupported command");
4712         my_message(ER_NOT_ALLOWED_COMMAND, g->Message, MYF(0));
4713         newmode= MODE_ERROR;
4714         break;
4715       } // endswitch newmode
4716 
4717   } else if (newmode == MODE_READ) {
4718     switch (thd_sql_command(thd)) {
4719       case SQLCOM_CREATE_TABLE:
4720         *chk= true;
4721 				break;
4722 			case SQLCOM_UPDATE_MULTI:
4723 			case SQLCOM_DELETE_MULTI:
4724 				*cras= true;
4725       case SQLCOM_INSERT:
4726       case SQLCOM_LOAD:
4727       case SQLCOM_INSERT_SELECT:
4728 //    case SQLCOM_REPLACE:
4729 //    case SQLCOM_REPLACE_SELECT:
4730       case SQLCOM_DELETE:
4731       case SQLCOM_TRUNCATE:
4732       case SQLCOM_UPDATE:
4733       case SQLCOM_SELECT:
4734       case SQLCOM_OPTIMIZE:
4735       case SQLCOM_SET_OPTION:
4736         break;
4737       case SQLCOM_LOCK_TABLES:
4738         locked= 1;
4739         break;
4740       case SQLCOM_DROP_TABLE:
4741       case SQLCOM_RENAME_TABLE:
4742         newmode= MODE_ANY;
4743         break;
4744       case SQLCOM_CREATE_VIEW:
4745       case SQLCOM_DROP_VIEW:
4746 			case SQLCOM_CREATE_TRIGGER:
4747 			case SQLCOM_DROP_TRIGGER:
4748 				newmode= MODE_ANY;
4749         break;
4750       case SQLCOM_ALTER_TABLE:
4751         *chk= true;
4752         newmode= MODE_ALTER;
4753         break;
4754       case SQLCOM_DROP_INDEX:
4755       case SQLCOM_CREATE_INDEX:
4756 //      if (!IsPartitioned()) {
4757           *chk= true;
4758           newmode= MODE_ANY;
4759           break;
4760 //        } // endif partitioned
4761 
4762 			case SQLCOM_CHECK:   // TODO implement it
4763 			case SQLCOM_ANALYZE: // TODO implement it
4764 			case SQLCOM_END:	   // Met in procedures: IF(EXISTS(SELECT...
4765 				newmode= MODE_READ;
4766         break;
4767       default:
4768         htrc("Unsupported sql_command=%d\n", thd_sql_command(thd));
4769         strcpy(g->Message, "CONNECT Unsupported command");
4770         my_message(ER_NOT_ALLOWED_COMMAND, g->Message, MYF(0));
4771         newmode= MODE_ERROR;
4772         break;
4773       } // endswitch newmode
4774 
4775   } // endif's newmode
4776 
4777   if (trace(1))
4778     htrc("New mode=%d\n", newmode);
4779 
4780   return newmode;
4781 } // end of check_mode
4782 
4783 int ha_connect::start_stmt(THD *thd, thr_lock_type lock_type)
4784 {
4785   int     rc= 0;
4786   bool    chk=false, cras= false;
4787   MODE    newmode;
4788   PGLOBAL g= GetPlug(thd, xp);
4789   DBUG_ENTER("ha_connect::start_stmt");
4790 
4791   if (check_privileges(thd, GetTableOptionStruct(), table->s->db.str, true))
4792     DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
4793 
4794   // Action will depend on lock_type
4795   switch (lock_type) {
4796     case TL_WRITE_ALLOW_WRITE:
4797     case TL_WRITE_CONCURRENT_INSERT:
4798     case TL_WRITE_DELAYED:
4799     case TL_WRITE_DEFAULT:
4800     case TL_WRITE_LOW_PRIORITY:
4801     case TL_WRITE:
4802     case TL_WRITE_ONLY:
4803       newmode= MODE_WRITE;
4804       break;
4805     case TL_READ:
4806     case TL_READ_WITH_SHARED_LOCKS:
4807     case TL_READ_HIGH_PRIORITY:
4808     case TL_READ_NO_INSERT:
4809     case TL_READ_DEFAULT:
4810       newmode= MODE_READ;
4811       break;
4812     case TL_UNLOCK:
4813     default:
4814       newmode= MODE_ANY;
4815       break;
4816     } // endswitch mode
4817 
4818 	if (newmode == MODE_ANY) {
4819 		if (CloseTable(g)) {
4820 			// Make error a warning to avoid crash
4821 			push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
4822 			rc= 0;
4823 		} // endif Close
4824 
4825 		locked= 0;
4826 		xmod= MODE_ANY;              // For info commands
4827 		DBUG_RETURN(rc);
4828 	} // endif MODE_ANY
4829 
4830 	newmode= CheckMode(g, thd, newmode, &chk, &cras);
4831 
4832 	if (newmode == MODE_ERROR)
4833 		DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
4834 
4835 	DBUG_RETURN(check_stmt(g, newmode, cras));
4836 } // end of start_stmt
4837 
4838 /**
4839   @brief
4840   This create a lock on the table. If you are implementing a storage engine
4841   that can handle transacations look at ha_berkely.cc to see how you will
4842   want to go about doing this. Otherwise you should consider calling flock()
4843   here. Hint: Read the section "locking functions for mysql" in lock.cc to understand
4844   this.
4845 
4846     @details
4847   Called from lock.cc by lock_external() and unlock_external(). Also called
4848   from sql_table.cc by copy_data_between_tables().
4849 
4850     @note
4851   Following what we did in the MySQL XDB handler, we use this call to actually
4852   physically open the table. This could be reconsider when finalizing this handler
4853   design, which means we have a better understanding of what MariaDB does.
4854 
4855     @see
4856   lock.cc by lock_external() and unlock_external() in lock.cc;
4857   the section "locking functions for mysql" in lock.cc;
4858   copy_data_between_tables() in sql_table.cc.
4859 
4860 */
4861 int ha_connect::external_lock(THD *thd, int lock_type)
4862 {
4863   int     rc= 0;
4864   bool    xcheck=false, cras= false;
4865   MODE    newmode;
4866   PTOS    options= GetTableOptionStruct();
4867   PGLOBAL g= GetPlug(thd, xp);
4868   DBUG_ENTER("ha_connect::external_lock");
4869 
4870   DBUG_ASSERT(thd == current_thd);
4871 
4872   if (trace(1))
4873     htrc("external_lock: this=%p thd=%p xp=%p g=%p lock_type=%d\n",
4874             this, thd, xp, g, lock_type);
4875 
4876   if (!g)
4877     DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
4878 
4879   // Action will depend on lock_type
4880   switch (lock_type) {
4881     case F_WRLCK:
4882       newmode= MODE_WRITE;
4883       break;
4884     case F_RDLCK:
4885       newmode= MODE_READ;
4886       break;
4887     case F_UNLCK:
4888     default:
4889       newmode= MODE_ANY;
4890       break;
4891     } // endswitch mode
4892 
4893   if (newmode == MODE_ANY) {
4894     int sqlcom= thd_sql_command(thd);
4895 
4896     // This is unlocking, do it by closing the table
4897     if (xp->CheckQueryID() && sqlcom != SQLCOM_UNLOCK_TABLES
4898                            && sqlcom != SQLCOM_LOCK_TABLES
4899                            && sqlcom != SQLCOM_FLUSH
4900                            && sqlcom != SQLCOM_BEGIN
4901                            && sqlcom != SQLCOM_DROP_TABLE) {
4902       sprintf(g->Message, "external_lock: unexpected command %d", sqlcom);
4903       push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
4904       DBUG_RETURN(0);
4905     } else if (g->Xchk) {
4906       if (!tdbp) {
4907 				if (!(tdbp= GetTDB(g))) {
4908 //        DBUG_RETURN(HA_ERR_INTERNAL_ERROR);  causes assert error
4909 					push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
4910 					DBUG_RETURN(0);
4911 				} else if (!tdbp->GetDef()->Indexable()) {
4912           sprintf(g->Message, "external_lock: Table %s is not indexable", tdbp->GetName());
4913 //        DBUG_RETURN(HA_ERR_INTERNAL_ERROR);  causes assert error
4914           push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
4915           DBUG_RETURN(0);
4916         } else if (tdbp->GetDef()->Indexable() == 1) {
4917           bool    oldsep= ((PCHK)g->Xchk)->oldsep;
4918           bool    newsep= ((PCHK)g->Xchk)->newsep;
4919           PTDBDOS tdp= (PTDBDOS)tdbp;
4920 
4921           PDOSDEF ddp= (PDOSDEF)tdp->GetDef();
4922           PIXDEF  xp, xp1, xp2, drp=NULL, adp= NULL;
4923           PIXDEF  oldpix= ((PCHK)g->Xchk)->oldpix;
4924           PIXDEF  newpix= ((PCHK)g->Xchk)->newpix;
4925           PIXDEF *xlst, *xprc;
4926 
4927           ddp->SetIndx(oldpix);
4928 
4929           if (oldsep != newsep) {
4930             // All indexes have to be remade
4931             ddp->DeleteIndexFile(g, NULL);
4932             oldpix= NULL;
4933             ddp->SetIndx(NULL);
4934             SetBooleanOption("Sepindex", newsep);
4935           } else if (newsep) {
4936             // Make the list of dropped indexes
4937             xlst= &drp; xprc= &oldpix;
4938 
4939             for (xp2= oldpix; xp2; xp2= xp) {
4940               for (xp1= newpix; xp1; xp1= xp1->Next)
4941                 if (IsSameIndex(xp1, xp2))
4942                   break;        // Index not to drop
4943 
4944               xp= xp2->GetNext();
4945 
4946               if (!xp1) {
4947                 *xlst= xp2;
4948                 *xprc= xp;
4949                 *(xlst= &xp2->Next)= NULL;
4950               } else
4951                 xprc= &xp2->Next;
4952 
4953               } // endfor xp2
4954 
4955             if (drp) {
4956               // Here we erase the index files
4957               ddp->DeleteIndexFile(g, drp);
4958               } // endif xp1
4959 
4960           } else if (oldpix) {
4961             // TODO: optimize the case of just adding new indexes
4962             if (!newpix)
4963               ddp->DeleteIndexFile(g, NULL);
4964 
4965             oldpix= NULL;     // To remake all indexes
4966             ddp->SetIndx(NULL);
4967           } // endif sepindex
4968 
4969           // Make the list of new created indexes
4970           xlst= &adp; xprc= &newpix;
4971 
4972           for (xp1= newpix; xp1; xp1= xp) {
4973             for (xp2= oldpix; xp2; xp2= xp2->Next)
4974               if (IsSameIndex(xp1, xp2))
4975                 break;        // Index already made
4976 
4977             xp= xp1->Next;
4978 
4979             if (!xp2) {
4980               *xlst= xp1;
4981               *xprc= xp;
4982               *(xlst= &xp1->Next)= NULL;
4983             } else
4984               xprc= &xp1->Next;
4985 
4986             } // endfor xp1
4987 
4988           if (adp)
4989             // Here we do make the new indexes
4990             if (tdp->MakeIndex(g, adp, true) == RC_FX) {
4991               // Make it a warning to avoid crash
4992               //push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
4993               //                  0, g->Message);
4994               //rc= 0;
4995 							my_message(ER_TOO_MANY_KEYS, g->Message, MYF(0));
4996 							rc= HA_ERR_INDEX_CORRUPT;
4997 						} // endif MakeIndex
4998 
4999         } else if (tdbp->GetDef()->Indexable() == 3) {
5000           if (CheckVirtualIndex(NULL)) {
5001             // Make it a warning to avoid crash
5002             push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
5003                               0, g->Message);
5004             rc= 0;
5005             } // endif Check
5006 
5007         } // endif indexable
5008 
5009         } // endif Tdbp
5010 
5011       } // endelse Xchk
5012 
5013     if (CloseTable(g)) {
5014       // This is an error while builing index
5015       // Make it a warning to avoid crash
5016       push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
5017       rc= 0;
5018 		} // endif Close
5019 
5020     locked= 0;
5021     xmod= MODE_ANY;              // For info commands
5022     DBUG_RETURN(rc);
5023 	} else if (check_privileges(thd, options, table->s->db.str)) {
5024 		strcpy(g->Message, "This operation requires the FILE privilege");
5025 		htrc("%s\n", g->Message);
5026 		DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
5027 	} // endif check_privileges
5028 
5029 
5030   DBUG_ASSERT(table && table->s);
5031 
5032   // Table mode depends on the query type
5033   newmode= CheckMode(g, thd, newmode, &xcheck, &cras);
5034 
5035   if (newmode == MODE_ERROR)
5036     DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
5037 
5038 	DBUG_RETURN(check_stmt(g, newmode, cras));
5039 } // end of external_lock
5040 
5041 
5042 int ha_connect::check_stmt(PGLOBAL g, MODE newmode, bool cras)
5043 {
5044 	int rc= 0;
5045 	DBUG_ENTER("ha_connect::check_stmt");
5046 
5047 	// If this is the start of a new query, cleanup the previous one
5048   if (xp->CheckCleanup()) {
5049     tdbp= NULL;
5050     valid_info= false;
5051 	} // endif CheckCleanup
5052 
5053   if (cras)
5054     g->Createas= true;  // To tell external tables of a multi-table command
5055 
5056 	if (trace(1))
5057 		htrc("Calling CntCheckDB db=%s cras=%d\n", GetDBName(NULL), cras);
5058 
5059   // Set or reset the good database environment
5060   if (CntCheckDB(g, this, GetDBName(NULL))) {
5061 		htrc("%p check_stmt: %s\n", this, g->Message);
5062 		rc= HA_ERR_INTERNAL_ERROR;
5063   // This can NOT be called without open called first, but
5064   // the table can have been closed since then
5065   } else if (!tdbp || xp->CheckQuery(valid_query_id) || xmod != newmode) {
5066     if (tdbp) {
5067       // If this is called by a later query, the table may have
5068       // been already closed and the tdbp is not valid anymore.
5069       if (xp->last_query_id == valid_query_id)
5070         rc= CloseTable(g);
5071       else
5072         tdbp= NULL;
5073 
5074       } // endif tdbp
5075 
5076     xmod= newmode;
5077 
5078     // Delay open until used fields are known
5079   } // endif tdbp
5080 
5081   if (trace(1))
5082 		htrc("check_stmt: rc=%d\n", rc);
5083 
5084   DBUG_RETURN(rc);
5085 } // end of check_stmt
5086 
5087 
5088 /**
5089   @brief
5090   The idea with handler::store_lock() is: The statement decides which locks
5091   should be needed for the table. For updates/deletes/inserts we get WRITE
5092   locks, for SELECT... we get read locks.
5093 
5094     @details
5095   Before adding the lock into the table lock handler (see thr_lock.c),
5096   mysqld calls store lock with the requested locks. Store lock can now
5097   modify a write lock to a read lock (or some other lock), ignore the
5098   lock (if we don't want to use MySQL table locks at all), or add locks
5099   for many tables (like we do when we are using a MERGE handler).
5100 
5101   Berkeley DB, for example, changes all WRITE locks to TL_WRITE_ALLOW_WRITE
5102   (which signals that we are doing WRITES, but are still allowing other
5103   readers and writers).
5104 
5105   When releasing locks, store_lock() is also called. In this case one
5106   usually doesn't have to do anything.
5107 
5108   In some exceptional cases MySQL may send a request for a TL_IGNORE;
5109   This means that we are requesting the same lock as last time and this
5110   should also be ignored. (This may happen when someone does a flush
5111   table when we have opened a part of the tables, in which case mysqld
5112   closes and reopens the tables and tries to get the same locks at last
5113   time). In the future we will probably try to remove this.
5114 
5115   Called from lock.cc by get_lock_data().
5116 
5117     @note
5118   In this method one should NEVER rely on table->in_use, it may, in fact,
5119   refer to a different thread! (this happens if get_lock_data() is called
5120   from mysql_lock_abort_for_thread() function)
5121 
5122     @see
5123   get_lock_data() in lock.cc
5124 */
5125 THR_LOCK_DATA **ha_connect::store_lock(THD *,
5126                                        THR_LOCK_DATA **to,
5127                                        enum thr_lock_type lock_type)
5128 {
5129   if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
5130     lock.type=lock_type;
5131   *to++= &lock;
5132   return to;
5133 }
5134 
5135 
5136 /**
5137   Searches for a pointer to the last occurrence of  the
5138   character c in the string src.
5139   Returns true on failure, false on success.
5140 */
5141 static bool
5142 strnrchr(LEX_CSTRING *ls, const char *src, size_t length, int c)
5143 {
5144   const char *srcend, *s;
5145   for (s= srcend= src + length; s > src; s--)
5146   {
5147     if (s[-1] == c)
5148     {
5149       ls->str= s;
5150       ls->length= srcend - s;
5151       return false;
5152     }
5153   }
5154   return true;
5155 }
5156 
5157 
5158 /**
5159   Split filename into database and table name.
5160 */
5161 static bool
5162 filename_to_dbname_and_tablename(const char *filename,
5163                                  char *database, size_t database_size,
5164                                  char *table, size_t table_size)
5165 {
5166   LEX_CSTRING d, t;
5167   size_t length= strlen(filename);
5168 
5169   /* Find filename - the rightmost directory part */
5170   if (strnrchr(&t, filename, length, slash) || t.length + 1 > table_size)
5171     return true;
5172   memcpy(table, t.str, t.length);
5173   table[t.length]= '\0';
5174   if (!(length-= t.length))
5175     return true;
5176 
5177   length--; /* Skip slash */
5178 
5179   /* Find database name - the second rightmost directory part */
5180   if (strnrchr(&d, filename, length, slash) || d.length + 1 > database_size)
5181     return true;
5182   memcpy(database, d.str, d.length);
5183   database[d.length]= '\0';
5184   return false;
5185 } // end of filename_to_dbname_and_tablename
5186 
5187 /**
5188   @brief
5189   Used to delete or rename a table. By the time delete_table() has been
5190   called all opened references to this table will have been closed
5191   (and your globally shared references released) ===> too bad!!!
5192   The variable name will just be the name of the table.
5193   You will need to remove or rename any files you have created at
5194   this point.
5195 
5196     @details
5197   If you do not implement this, the default delete_table() is called from
5198   handler.cc and it will delete all files with the file extensions returned
5199   by bas_ext().
5200 
5201   Called from handler.cc by delete_table and ha_create_table(). Only used
5202   during create if the table_flag HA_DROP_BEFORE_CREATE was specified for
5203   the storage engine.
5204 
5205     @see
5206   delete_table and ha_create_table() in handler.cc
5207 */
5208 int ha_connect::delete_or_rename_table(const char *name, const char *to)
5209 {
5210   DBUG_ENTER("ha_connect::delete_or_rename_table");
5211   char db[128], tabname[128];
5212   int  rc= 0;
5213   bool ok= false;
5214   THD *thd= current_thd;
5215   int  sqlcom= thd_sql_command(thd);
5216 
5217   if (trace(1)) {
5218     if (to)
5219       htrc("rename_table: this=%p thd=%p sqlcom=%d from=%s to=%s\n",
5220               this, thd, sqlcom, name, to);
5221     else
5222       htrc("delete_table: this=%p thd=%p sqlcom=%d name=%s\n",
5223               this, thd, sqlcom, name);
5224 
5225     } // endif trace
5226 
5227   if (to && (filename_to_dbname_and_tablename(to, db, sizeof(db),
5228                                              tabname, sizeof(tabname))
5229       || (*tabname == '#' && sqlcom == SQLCOM_CREATE_INDEX)))
5230     DBUG_RETURN(0);
5231 
5232   if (filename_to_dbname_and_tablename(name, db, sizeof(db),
5233                                        tabname, sizeof(tabname))
5234       || (*tabname == '#' && sqlcom == SQLCOM_CREATE_INDEX))
5235     DBUG_RETURN(0);
5236 
5237   // If a temporary file exists, all the tests below were passed
5238   // successfully when making it, so they are not needed anymore
5239   // in particular because they sometimes cause DBUG_ASSERT crash.
5240   // Also, for partitioned tables, no test can be done because when
5241   // this function is called, the .par file is already deleted and
5242   // this causes the open_table_def function to fail.
5243   // Not having any other clues (table and table_share are NULL)
5244   // the only mean we have to test for partitioning is this:
5245   if (*tabname != '#' && !strstr(tabname, "#P#")) {
5246     // We have to retrieve the information about this table options.
5247     ha_table_option_struct *pos;
5248     char         key[MAX_DBKEY_LENGTH];
5249     uint         key_length;
5250     TABLE_SHARE *share;
5251 
5252 //  if ((p= strstr(tabname, "#P#")))   won't work, see above
5253 //    *p= 0;             // Get the main the table name
5254 
5255     key_length= tdc_create_key(key, db, tabname);
5256 
5257     // share contains the option struct that we need
5258     if (!(share= alloc_table_share(db, tabname, key, key_length)))
5259       DBUG_RETURN(rc);
5260 
5261     // Get the share info from the .frm file
5262     Dummy_error_handler error_handler;
5263     thd->push_internal_handler(&error_handler);
5264     bool got_error= open_table_def(thd, share);
5265     thd->pop_internal_handler();
5266     if (!got_error && share->db_type() != connect_hton)
5267     {
5268       /* The .frm file is not for the connect engine. Something is wrong! */
5269       got_error= 1;
5270       rc= HA_ERR_INTERNAL_ERROR;
5271       my_error(HA_ERR_INTERNAL_ERROR, MYF(0),
5272                "TABLE_SHARE is not for the CONNECT engine");
5273     }
5274     if (!got_error) {
5275       // Now we can work
5276       if ((pos= share->option_struct)) {
5277         if (check_privileges(thd, pos, db))
5278           rc= HA_ERR_INTERNAL_ERROR;         // ???
5279         else
5280           if (IsFileType(GetRealType(pos)) && !pos->filename)
5281             ok= true;
5282 
5283         } // endif pos
5284 
5285       } // endif open_table_def
5286       else
5287         rc= ENOENT;
5288     free_table_share(share);
5289   } else              // Temporary file
5290     ok= true;
5291 
5292   if (ok) {
5293     // Let the base handler do the job
5294     if (to)
5295       rc= handler::rename_table(name, to);
5296     else if ((rc= handler::delete_table(name)) == ENOENT)
5297       rc= 0;        // No files is not an error for CONNECT
5298 
5299     } // endif ok
5300 
5301   DBUG_RETURN(rc);
5302 } // end of delete_or_rename_table
5303 
5304 int ha_connect::delete_table(const char *name)
5305 {
5306   return delete_or_rename_table(name, NULL);
5307 } // end of delete_table
5308 
5309 int ha_connect::rename_table(const char *from, const char *to)
5310 {
5311   return delete_or_rename_table(from, to);
5312 } // end of rename_table
5313 
5314 /**
5315   @brief
5316   Given a starting key and an ending key, estimate the number of rows that
5317   will exist between the two keys.
5318 
5319   @details
5320   end_key may be empty, in which case determine if start_key matches any rows.
5321 
5322   Called from opt_range.cc by check_quick_keys().
5323 
5324   @see
5325   check_quick_keys() in opt_range.cc
5326 */
5327 ha_rows ha_connect::records_in_range(uint inx,
5328                                      const key_range *min_key,
5329                                      const key_range *max_key,
5330                                      page_range *pages)
5331 
5332 {
5333   ha_rows rows;
5334   DBUG_ENTER("ha_connect::records_in_range");
5335 
5336   if (indexing < 0 || inx != active_index)
5337     if (index_init(inx, false))
5338       DBUG_RETURN(HA_POS_ERROR);
5339 
5340   if (trace(1))
5341     htrc("records_in_range: inx=%d indexing=%d\n", inx, indexing);
5342 
5343   if (indexing > 0) {
5344     int          nval;
5345     uint         len[2];
5346     const uchar *key[2];
5347     bool         incl[2];
5348     key_part_map kmap[2];
5349 
5350     key[0]= (min_key) ? min_key->key : NULL;
5351     key[1]= (max_key) ? max_key->key : NULL;
5352     len[0]= (min_key) ? min_key->length : 0;
5353     len[1]= (max_key) ? max_key->length : 0;
5354     incl[0]= (min_key) ? (min_key->flag == HA_READ_KEY_EXACT) : false;
5355     incl[1]= (max_key) ? (max_key->flag == HA_READ_AFTER_KEY) : false;
5356     kmap[0]= (min_key) ? min_key->keypart_map : 0;
5357     kmap[1]= (max_key) ? max_key->keypart_map : 0;
5358 
5359     if ((nval= CntIndexRange(xp->g, tdbp, key, len, incl, kmap)) < 0)
5360       rows= HA_POS_ERROR;
5361     else
5362       rows= (ha_rows)nval;
5363 
5364   } else if (indexing == 0)
5365     rows= 100000000;        // Don't use missing index
5366   else
5367     rows= HA_POS_ERROR;
5368 
5369   if (trace(1))
5370     htrc("records_in_range: rows=%llu\n", rows);
5371 
5372   DBUG_RETURN(rows);
5373 } // end of records_in_range
5374 
5375 // Used to check whether a MYSQL table is created on itself
5376 bool CheckSelf(PGLOBAL g, TABLE_SHARE *s, PCSZ host,
5377 	             PCSZ db, PCSZ tab, PCSZ src, int port)
5378 {
5379   if (src)
5380     return false;
5381   else if (host && stricmp(host, "localhost") && strcmp(host, "127.0.0.1"))
5382     return false;
5383   else if (db && stricmp(db, s->db.str))
5384     return false;
5385   else if (tab && stricmp(tab, s->table_name.str))
5386     return false;
5387   else if (port && port != (signed)GetDefaultPort())
5388     return false;
5389 
5390   strcpy(g->Message, "This MySQL table is defined on itself");
5391   return true;
5392 } // end of CheckSelf
5393 
5394 /**
5395   Convert an ISO-8859-1 column name to UTF-8
5396 */
5397 static char *encode(PGLOBAL g, const char *cnm)
5398   {
5399   char  *buf= (char*)PlugSubAlloc(g, NULL, strlen(cnm) * 3);
5400   uint   dummy_errors;
5401   uint32 len= copy_and_convert(buf, strlen(cnm) * 3,
5402                                &my_charset_utf8mb3_general_ci,
5403                                cnm, strlen(cnm),
5404                                &my_charset_latin1,
5405                                &dummy_errors);
5406   buf[len]= '\0';
5407   return buf;
5408   } // end of encode
5409 
5410 /**
5411   Store field definition for create.
5412 
5413   @return
5414     Return 0 if ok
5415 */
5416 static bool add_field(String* sql, TABTYPE ttp, const char* field_name, int typ,
5417 	                    int len, int dec, char* key, uint tm, const char* rem,
5418 	                    char* dft, char* xtra, char* fmt, int flag, bool dbf, char v)
5419 {
5420 	char var = (len > 255) ? 'V' : v;
5421 	bool q, error = false;
5422 	const char* type = PLGtoMYSQLtype(typ, dbf, var);
5423 
5424 	error |= sql->append('`');
5425 	error |= sql->append(field_name);
5426 	error |= sql->append("` ");
5427 	error |= sql->append(type);
5428 
5429 	if (typ == TYPE_STRING ||
5430 		(len && typ != TYPE_DATE && (typ != TYPE_DOUBLE || dec >= 0))) {
5431 		error |= sql->append('(');
5432 		error |= sql->append_ulonglong(len);
5433 
5434 		if (typ == TYPE_DOUBLE) {
5435 			error |= sql->append(',');
5436 			// dec must be < len and < 31
5437 			error |= sql->append_ulonglong(MY_MIN(dec, (MY_MIN(len, 31) - 1)));
5438 		} else if (dec > 0 && !strcmp(type, "DECIMAL")) {
5439 			error |= sql->append(',');
5440 			// dec must be < len
5441 			error |= sql->append_ulonglong(MY_MIN(dec, len - 1));
5442 		} // endif dec
5443 
5444 		error |= sql->append(')');
5445 	} // endif len
5446 
5447 	if (v == 'U')
5448 		error |= sql->append(" UNSIGNED");
5449 	else if (v == 'Z')
5450 		error |= sql->append(" ZEROFILL");
5451 
5452 	if (key && *key) {
5453 		error |= sql->append(" ");
5454 		error |= sql->append(key);
5455 	} // endif key
5456 
5457 	if (tm)
5458 		error |= sql->append(STRING_WITH_LEN(" NOT NULL"), system_charset_info);
5459 
5460 	if (dft && *dft) {
5461 		error |= sql->append(" DEFAULT ");
5462 
5463 		if (typ == TYPE_DATE)
5464 			q = (strspn(dft, "0123456789 -:/") == strlen(dft));
5465 		else
5466 			q = !IsTypeNum(typ);
5467 
5468 		if (q) {
5469 			error |= sql->append("'");
5470 			error |= sql->append_for_single_quote(dft, strlen(dft));
5471 			error |= sql->append("'");
5472 		} else
5473 			error |= sql->append(dft);
5474 
5475 	} // endif dft
5476 
5477 	if (xtra && *xtra) {
5478 		error |= sql->append(" ");
5479 		error |= sql->append(xtra);
5480 	} // endif rem
5481 
5482 	if (rem && *rem) {
5483 		error |= sql->append(" COMMENT '");
5484 		error |= sql->append_for_single_quote(rem, strlen(rem));
5485 		error |= sql->append("'");
5486 	} // endif rem
5487 
5488 	if (fmt && *fmt) {
5489     switch (ttp) {
5490       case TAB_MONGO:
5491       case TAB_BSON:
5492       case TAB_JSON: error |= sql->append(" JPATH='"); break;
5493       case TAB_XML:  error |= sql->append(" XPATH='"); break;
5494       default:	     error |= sql->append(" FIELD_FORMAT='");
5495     } // endswitch ttp
5496 
5497 		error |= sql->append_for_single_quote(fmt, strlen(fmt));
5498 		error |= sql->append("'");
5499 	} // endif flag
5500 
5501 	if (flag) {
5502 		error |= sql->append(" FLAG=");
5503 		error |= sql->append_ulonglong(flag);
5504 	} // endif flag
5505 
5506 	error |= sql->append(',');
5507 	return error;
5508 } // end of add_field
5509 
5510 /**
5511   Initialise the table share with the new columns.
5512 
5513   @return
5514     Return 0 if ok
5515 */
5516 static int init_table_share(THD* thd,
5517                             TABLE_SHARE *table_s,
5518                             HA_CREATE_INFO *create_info,
5519                             String *sql)
5520 {
5521   bool oom= false;
5522   PTOS topt= table_s->option_struct;
5523 
5524   sql->length(sql->length()-1); // remove the trailing comma
5525   sql->append(')');
5526 
5527   for (ha_create_table_option *opt= connect_table_option_list;
5528        opt->name; opt++) {
5529     ulonglong   vull;
5530     const char *vstr;
5531 
5532     switch (opt->type) {
5533       case HA_OPTION_TYPE_ULL:
5534         vull= *(ulonglong*)(((char*)topt) + opt->offset);
5535 
5536         if (vull != opt->def_value) {
5537           oom|= sql->append(' ');
5538           oom|= sql->append(opt->name);
5539           oom|= sql->append('=');
5540           oom|= sql->append_ulonglong(vull);
5541           } // endif vull
5542 
5543         break;
5544       case HA_OPTION_TYPE_STRING:
5545         vstr= *(char**)(((char*)topt) + opt->offset);
5546 
5547         if (vstr) {
5548           oom|= sql->append(' ');
5549           oom|= sql->append(opt->name);
5550           oom|= sql->append("='");
5551           oom|= sql->append_for_single_quote(vstr, strlen(vstr));
5552           oom|= sql->append('\'');
5553           } // endif vstr
5554 
5555         break;
5556       case HA_OPTION_TYPE_BOOL:
5557         vull= *(bool*)(((char*)topt) + opt->offset);
5558 
5559         if (vull != opt->def_value) {
5560           oom|= sql->append(' ');
5561           oom|= sql->append(opt->name);
5562           oom|= sql->append('=');
5563           oom|= sql->append(vull ? "YES" : "NO");
5564           } // endif vull
5565 
5566         break;
5567       default: // no enums here, good :)
5568         break;
5569       } // endswitch type
5570 
5571     if (oom)
5572       return HA_ERR_OUT_OF_MEM;
5573 
5574     } // endfor opt
5575 
5576   if (create_info->connect_string.length) {
5577     oom|= sql->append(' ');
5578     oom|= sql->append("CONNECTION='");
5579     oom|= sql->append_for_single_quote(create_info->connect_string.str,
5580                                        create_info->connect_string.length);
5581     oom|= sql->append('\'');
5582 
5583     if (oom)
5584       return HA_ERR_OUT_OF_MEM;
5585 
5586     } // endif string
5587 
5588   if (create_info->default_table_charset) {
5589     oom|= sql->append(' ');
5590     oom|= sql->append("CHARSET=");
5591     oom|= sql->append(create_info->default_table_charset->csname);
5592 
5593     if (oom)
5594       return HA_ERR_OUT_OF_MEM;
5595 
5596     } // endif charset
5597 
5598   if (trace(1))
5599     htrc("s_init: %.*s\n", sql->length(), sql->ptr());
5600 
5601   return table_s->init_from_sql_statement_string(thd, true,
5602                                                  sql->ptr(), sql->length());
5603 } // end of init_table_share
5604 
5605 /**
5606   @brief
5607   connect_assisted_discovery() is called when creating a table with no columns.
5608 
5609   @details
5610   When assisted discovery is used the .frm file have not already been
5611   created. You can overwrite some definitions at this point but the
5612   main purpose of it is to define the columns for some table types.
5613 
5614   @note
5615   this function is no more called in case of CREATE .. SELECT
5616 */
5617 static int connect_assisted_discovery(handlerton *, THD* thd,
5618                                       TABLE_SHARE *table_s,
5619                                       HA_CREATE_INFO *create_info)
5620 {
5621   char     v=0;
5622 	PCSZ     fncn= "?";
5623 	PCSZ     user, fn, db, host, pwd, sep, tbl, src;
5624 	PCSZ     col, ocl, rnk, pic, fcl, skc, zfn;
5625 	char    *tab, *dsn, *shm, *dpath, *url;
5626 #if defined(_WIN32)
5627 	PCSZ     nsp= NULL, cls= NULL;
5628 #endif   // _WIN32
5629 //int      hdr, mxe;
5630 	int      port= 0, mxr __attribute__((unused)) = 0, rc= 0, mul= 0;
5631 //PCSZ     tabtyp= NULL;
5632 #if defined(ODBC_SUPPORT)
5633   POPARM   sop= NULL;
5634 	PCSZ     ucnc= NULL;
5635 	bool     cnc= false;
5636   int      cto= -1, qto= -1;
5637 #endif   // ODBC_SUPPORT
5638 #if defined(JAVA_SUPPORT)
5639 	PJPARM   sjp= NULL;
5640 	PCSZ     driver= NULL;
5641 #endif   // JAVA_SUPPORT
5642   uint     tm, fnc= FNC_NO, supfnc= (FNC_NO | FNC_COL);
5643   bool     bif, ok= false, dbf= false;
5644   TABTYPE  ttp= TAB_UNDEF, ttr=TAB_UNDEF;
5645   PQRYRES  qrp= NULL;
5646   PCOLRES  crp;
5647   PCONNECT xp= NULL;
5648   PGLOBAL  g= GetPlug(thd, xp);
5649 
5650   if (!g)
5651     return HA_ERR_INTERNAL_ERROR;
5652 
5653   PTOS     topt= table_s->option_struct;
5654   char     buf[1024];
5655   String   sql(buf, sizeof(buf), system_charset_info);
5656 
5657   sql.copy(STRING_WITH_LEN("CREATE TABLE whatever ("), system_charset_info);
5658 	user= host= pwd= tbl= src= col= ocl= pic= fcl= skc= rnk= zfn= NULL;
5659 	dsn= url= NULL;
5660 
5661   // Get the useful create options
5662   ttp= GetTypeID(topt->type);
5663   fn=  topt->filename;
5664   tab= (char*)topt->tabname;
5665   src= topt->srcdef;
5666   db=  topt->dbname;
5667   fncn= topt->catfunc;
5668   fnc= GetFuncID(fncn);
5669   sep= topt->separator;
5670 	mul= (int)topt->multiple;
5671 	tbl= topt->tablist;
5672   col= topt->colist;
5673 
5674   if (topt->oplist) {
5675     host= GetListOption(g, "host", topt->oplist, "localhost");
5676     user= GetListOption(g, "user", topt->oplist,
5677           ((ttp == TAB_ODBC || ttp == TAB_JDBC) ? NULL : "root"));
5678     // Default value db can come from the DBNAME=xxx option.
5679     db= GetListOption(g, "database", topt->oplist, db);
5680     col= GetListOption(g, "colist", topt->oplist, col);
5681     ocl= GetListOption(g, "occurcol", topt->oplist, NULL);
5682     pic= GetListOption(g, "pivotcol", topt->oplist, NULL);
5683     fcl= GetListOption(g, "fnccol", topt->oplist, NULL);
5684     skc= GetListOption(g, "skipcol", topt->oplist, NULL);
5685     rnk= GetListOption(g, "rankcol", topt->oplist, NULL);
5686     pwd= GetListOption(g, "password", topt->oplist);
5687 #if defined(_WIN32)
5688     nsp= GetListOption(g, "namespace", topt->oplist);
5689     cls= GetListOption(g, "class", topt->oplist);
5690 #endif   // _WIN32
5691     port= atoi(GetListOption(g, "port", topt->oplist, "0"));
5692 #if defined(ODBC_SUPPORT)
5693 //	tabtyp= GetListOption(g, "Tabtype", topt->oplist, NULL);
5694 		mxr= atoi(GetListOption(g,"maxres", topt->oplist, "0"));
5695     cto= atoi(GetListOption(g,"ConnectTimeout", topt->oplist, "-1"));
5696     qto= atoi(GetListOption(g,"QueryTimeout", topt->oplist, "-1"));
5697 
5698     if ((ucnc= GetListOption(g, "UseDSN", topt->oplist)))
5699       cnc= (!*ucnc || *ucnc == 'y' || *ucnc == 'Y' || atoi(ucnc) != 0);
5700 #endif
5701 #if defined(JAVA_SUPPORT)
5702 		driver= GetListOption(g, "Driver", topt->oplist, NULL);
5703 #endif   // JAVA_SUPPORT
5704 #if defined(PROMPT_OK)
5705     cop= atoi(GetListOption(g, "checkdsn", topt->oplist, "0"));
5706 #endif   // PROMPT_OK
5707 #if defined(ZIP_SUPPORT)
5708 		zfn= GetListOption(g, "Zipfile", topt->oplist, NULL);
5709 #endif   // ZIP_SUPPORT
5710 	} else {
5711     host= "localhost";
5712     user= ((ttp == TAB_ODBC || ttp == TAB_JDBC) ? NULL : "root");
5713   } // endif option_list
5714 
5715   if (!(shm= (char*)db))
5716     db= table_s->db.str;                   // Default value
5717 
5718 	try {
5719 		// Check table type
5720 		if (ttp == TAB_UNDEF && !topt->http) {
5721 			topt->type= (src) ? "MYSQL" : (tab) ? "PROXY" : "DOS";
5722 			ttp= GetTypeID(topt->type);
5723 			snprintf(g->Message, sizeof(g->Message), "No table_type. Was set to %s", topt->type);
5724 			push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, 0, g->Message);
5725 		} else if (ttp == TAB_NIY) {
5726 			snprintf(g->Message, sizeof(g->Message), "Unsupported table type %s", topt->type);
5727 			rc= HA_ERR_INTERNAL_ERROR;
5728 			goto err;
5729 #if defined(REST_SUPPORT)
5730 		} else if (topt->http) {
5731       if (ttp == TAB_UNDEF) {
5732         ttr= TAB_JSON;
5733         strcpy(g->Message, "No table_type. Was set to JSON");
5734         push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, 0, g->Message);
5735       } else
5736         ttr= ttp;
5737 
5738       switch (ttr) {
5739 				case TAB_JSON:
5740 #if defined(BSON_SUPPORT)
5741         case TAB_BSON:
5742 #endif   // BSON_SUPPORT
5743         case TAB_XML:
5744 				case TAB_CSV:
5745           ttp = TAB_REST;
5746 					break;
5747 				default:
5748 					break;
5749 			}	// endswitch type
5750 #endif   // REST_SUPPORT
5751 		} // endif ttp
5752 
5753     if (fn && *fn)
5754       switch (ttp) {
5755         case TAB_FMT:
5756         case TAB_DBF:
5757         case TAB_XML:
5758         case TAB_INI:
5759         case TAB_VEC:
5760         case TAB_REST:
5761         case TAB_JSON:
5762 #if defined(BSON_SUPPORT)
5763         case TAB_BSON:
5764 #endif   // BSON_SUPPORT
5765           if (checkPrivileges(thd, ttp, topt, db)) {
5766             strcpy(g->Message, "This operation requires the FILE privilege");
5767             rc= HA_ERR_INTERNAL_ERROR;
5768             goto err;
5769           } // endif check_privileges
5770 
5771           break;
5772         default:
5773           break;
5774       } // endswitch ttp
5775 
5776 		if (!tab) {
5777 			if (ttp == TAB_TBL) {
5778 				// Make tab the first table of the list
5779 				char *p;
5780 
5781 				if (!tbl) {
5782 					strcpy(g->Message, "Missing table list");
5783 					rc= HA_ERR_INTERNAL_ERROR;
5784 					goto err;
5785 				} // endif tbl
5786 
5787 				tab= PlugDup(g, tbl);
5788 
5789 				if ((p= strchr(tab, ',')))
5790 					*p= 0;
5791 
5792 				if ((p= strchr(tab, '.'))) {
5793 					*p= 0;
5794 					db= tab;
5795 					tab= p + 1;
5796 				} // endif p
5797 
5798 			} else if (ttp != TAB_ODBC || !(fnc & (FNC_TABLE | FNC_COL)))
5799 			  tab= (char*)table_s->table_name.str;   // Default value
5800 
5801 		} // endif tab
5802 
5803 		switch (ttp) {
5804 #if defined(ODBC_SUPPORT)
5805 			case TAB_ODBC:
5806 				dsn= strz(g, create_info->connect_string);
5807 
5808 				if (fnc & (FNC_DSN | FNC_DRIVER)) {
5809 					ok= true;
5810 #if defined(PROMPT_OK)
5811 				} else if (!stricmp(thd->main_security_ctx.host, "localhost")
5812 					&& cop == 1) {
5813 					if ((dsn= ODBCCheckConnection(g, dsn, cop)) != NULL) {
5814 						thd->make_lex_string(&create_info->connect_string, dsn, strlen(dsn));
5815 						ok= true;
5816 					} // endif dsn
5817 #endif   // PROMPT_OK
5818 
5819 				} else if (!dsn) {
5820 					sprintf(g->Message, "Missing %s connection string", topt->type);
5821 				} else {
5822 					// Store ODBC additional parameters
5823 					sop= (POPARM)PlugSubAlloc(g, NULL, sizeof(ODBCPARM));
5824 					sop->User= (char*)user;
5825 					sop->Pwd= (char*)pwd;
5826 					sop->Cto= cto;
5827 					sop->Qto= qto;
5828 					sop->UseCnc= cnc;
5829 					ok= true;
5830 				} // endif's
5831 
5832 				supfnc |= (FNC_TABLE | FNC_DSN | FNC_DRIVER);
5833 				break;
5834 #endif   // ODBC_SUPPORT
5835 #if defined(JAVA_SUPPORT)
5836 			case TAB_JDBC:
5837 				if (fnc & FNC_DRIVER) {
5838 					ok= true;
5839 				} else if (!(url= strz(g, create_info->connect_string))) {
5840 					strcpy(g->Message, "Missing URL");
5841 				} else {
5842 					// Store JDBC additional parameters
5843 					int      rc;
5844 					PJDBCDEF jdef= new(g) JDBCDEF();
5845 
5846 					jdef->SetName(create_info->alias.str);
5847 					sjp = (PJPARM)PlugSubAlloc(g, NULL, sizeof(JDBCPARM));
5848 					sjp->Driver = driver;
5849 					//		sjp->Properties = prop;
5850 					sjp->Fsize = 0;
5851 					sjp->Scrollable = false;
5852 
5853 					if ((rc = jdef->ParseURL(g, url, false)) == RC_OK) {
5854 						sjp->Url = url;
5855 						sjp->User = (char*)user;
5856 						sjp->Pwd = (char*)pwd;
5857 						ok = true;
5858 					} else if (rc == RC_NF) {
5859 						if (jdef->GetTabname())
5860 							tab= (char*)jdef->GetTabname();
5861 
5862 						ok= jdef->SetParms(sjp);
5863 					} // endif rc
5864 
5865 				} // endif's
5866 
5867 				supfnc |= (FNC_DRIVER | FNC_TABLE);
5868 				break;
5869 #endif   // JAVA_SUPPORT
5870 			case TAB_DBF:
5871 				dbf= true;
5872 				// fall through
5873 			case TAB_CSV:
5874 				if (!fn && fnc != FNC_NO)
5875 					sprintf(g->Message, "Missing %s file name", topt->type);
5876 				else if (sep && strlen(sep) > 1)
5877 					sprintf(g->Message, "Invalid separator %s", sep);
5878 				else
5879 					ok= true;
5880 
5881 				break;
5882 			case TAB_MYSQL:
5883 				ok= true;
5884 
5885 				if (create_info->connect_string.str &&
5886 					create_info->connect_string.length) {
5887 					PMYDEF  mydef= new(g) MYSQLDEF();
5888 
5889 					dsn= strz(g, create_info->connect_string);
5890 					mydef->SetName(create_info->alias.str);
5891 
5892 					if (!mydef->ParseURL(g, dsn, false)) {
5893 						if (mydef->GetHostname())
5894 							host= mydef->GetHostname();
5895 
5896 						if (mydef->GetUsername())
5897 							user= mydef->GetUsername();
5898 
5899 						if (mydef->GetPassword())
5900 							pwd= mydef->GetPassword();
5901 
5902 						if (mydef->GetTabschema())
5903 							db= mydef->GetTabschema();
5904 
5905 						if (mydef->GetTabname())
5906 							tab= (char*)mydef->GetTabname();
5907 
5908 						if (mydef->GetPortnumber())
5909 							port= mydef->GetPortnumber();
5910 
5911 					} else
5912 						ok= false;
5913 
5914 				} else if (!user)
5915 					user= "root";
5916 
5917 				if (ok && CheckSelf(g, table_s, host, db, tab, src, port))
5918 					ok= false;
5919 
5920 				break;
5921 #if defined(_WIN32)
5922 			case TAB_WMI:
5923 				ok= true;
5924 				break;
5925 #endif   // _WIN32
5926 			case TAB_PIVOT:
5927 				supfnc= FNC_NO;
5928                                 // fall through
5929 			case TAB_PRX:
5930 			case TAB_TBL:
5931 			case TAB_XCL:
5932 			case TAB_OCCUR:
5933 				if (!src && !stricmp(tab, create_info->alias.str) &&
5934 					(!db || !stricmp(db, table_s->db.str)))
5935 					sprintf(g->Message, "A %s table cannot refer to itself", topt->type);
5936 				else
5937 					ok= true;
5938 
5939 				break;
5940 			case TAB_OEM:
5941 				if (topt->module && topt->subtype)
5942 					ok= true;
5943 				else
5944 					strcpy(g->Message, "Missing OEM module or subtype");
5945 
5946 				break;
5947 #if defined(LIBXML2_SUPPORT) || defined(DOMDOC_SUPPORT)
5948 			case TAB_XML:
5949 #endif   // LIBXML2_SUPPORT  ||         DOMDOC_SUPPORT
5950 			case TAB_JSON:
5951 #if defined(BSON_SUPPORT)
5952       case TAB_BSON:
5953 #endif   // BSON_SUPPORT
5954         dsn= strz(g, create_info->connect_string);
5955 
5956 				if (!fn && !zfn && !mul && !dsn)
5957 					sprintf(g->Message, "Missing %s file name", topt->type);
5958 				else if (dsn && !topt->tabname)
5959           topt->tabname= tab;
5960 
5961 				ok= true;
5962 				break;
5963 #if defined(JAVA_SUPPORT)
5964 			case TAB_MONGO:
5965 				if (!topt->tabname)
5966 					topt->tabname= tab;
5967 
5968 				ok= true;
5969 				break;
5970 #endif   // JAVA_SUPPORT
5971 #if defined(REST_SUPPORT)
5972 			case TAB_REST:
5973 				if (!topt->http)
5974 					strcpy(g->Message, "Missing REST HTTP option");
5975 				else
5976 					ok = true;
5977 
5978 				break;
5979 #endif   // REST_SUPPORT
5980 			case TAB_VIR:
5981 				ok= true;
5982 				break;
5983 			default:
5984 				sprintf(g->Message, "Cannot get column info for table type %s", topt->type);
5985 				break;
5986 		} // endif ttp
5987 
5988 	// Check for supported catalog function
5989 		if (ok && !(supfnc & fnc)) {
5990 			sprintf(g->Message, "Unsupported catalog function %s for table type %s",
5991 				fncn, topt->type);
5992 			ok= false;
5993 		} // endif supfnc
5994 
5995 		if (src && fnc != FNC_NO) {
5996 			strcpy(g->Message, "Cannot make catalog table from srcdef");
5997 			ok= false;
5998 		} // endif src
5999 
6000 		if (ok) {
6001 			const char *cnm, *rem;
6002 			char *dft, *xtra, *key, *fmt;
6003 			int   i, len, prec, dec, typ, flg;
6004 
6005 			if (!(dpath= SetPath(g, table_s->db.str))) {
6006 				rc= HA_ERR_INTERNAL_ERROR;
6007 				goto err;
6008 			}	// endif dpath
6009 
6010 			if (src && ttp != TAB_PIVOT && ttp != TAB_ODBC && ttp != TAB_JDBC) {
6011 				qrp= SrcColumns(g, host, db, user, pwd, src, port);
6012 
6013 				if (qrp && ttp == TAB_OCCUR)
6014 					if (OcrSrcCols(g, qrp, col, ocl, rnk)) {
6015 						rc= HA_ERR_INTERNAL_ERROR;
6016 						goto err;
6017 					} // endif OcrSrcCols
6018 
6019 			} else switch (ttp) {
6020 				case TAB_DBF:
6021 					qrp= DBFColumns(g, dpath, fn, topt, fnc == FNC_COL);
6022 					break;
6023 #if defined(ODBC_SUPPORT)
6024 				case TAB_ODBC:
6025 					switch (fnc) {
6026 						case FNC_NO:
6027 						case FNC_COL:
6028 							if (src) {
6029 								qrp= ODBCSrcCols(g, dsn, (char*)src, sop);
6030 								src= NULL;     // for next tests
6031 							} else
6032 								qrp= ODBCColumns(g, dsn, shm, tab, NULL,
6033 									mxr, fnc == FNC_COL, sop);
6034 
6035 							break;
6036 						case FNC_TABLE:
6037 							qrp= ODBCTables(g, dsn, shm, tab, NULL, mxr, true, sop);
6038 							break;
6039 						case FNC_DSN:
6040 							qrp= ODBCDataSources(g, mxr, true);
6041 							break;
6042 						case FNC_DRIVER:
6043 							qrp= ODBCDrivers(g, mxr, true);
6044 							break;
6045 						default:
6046 							sprintf(g->Message, "invalid catfunc %s", fncn);
6047 							break;
6048 					} // endswitch info
6049 
6050 					break;
6051 #endif   // ODBC_SUPPORT
6052 #if defined(JAVA_SUPPORT)
6053 				case TAB_JDBC:
6054 					switch (fnc) {
6055 						case FNC_NO:
6056 						case FNC_COL:
6057 							if (src) {
6058 								qrp= JDBCSrcCols(g, (char*)src, sjp);
6059 								src= NULL;     // for next tests
6060 							} else
6061 								qrp= JDBCColumns(g, shm, tab, NULL, mxr, fnc == FNC_COL, sjp);
6062 
6063 							break;
6064 						case FNC_TABLE:
6065 //						qrp= JDBCTables(g, shm, tab, tabtyp, mxr, true, sjp);
6066 							qrp= JDBCTables(g, shm, tab, NULL, mxr, true, sjp);
6067 							break;
6068 #if 0
6069 						case FNC_DSN:
6070 							qrp= JDBCDataSources(g, mxr, true);
6071 							break;
6072 #endif // 0
6073 						case FNC_DRIVER:
6074 							qrp= JDBCDrivers(g, mxr, true);
6075 							break;
6076 						default:
6077 							sprintf(g->Message, "invalid catfunc %s", fncn);
6078 							break;
6079 					} // endswitch info
6080 
6081 					break;
6082 #endif   // JAVA_SUPPORT
6083 				case TAB_MYSQL:
6084 					qrp= MyColumns(g, thd, host, db, user, pwd, tab,
6085 						NULL, port, fnc == FNC_COL);
6086 					break;
6087 				case TAB_CSV:
6088 					qrp= CSVColumns(g, dpath, topt, fnc == FNC_COL);
6089 					break;
6090 #if defined(_WIN32)
6091 				case TAB_WMI:
6092 					qrp= WMIColumns(g, nsp, cls, fnc == FNC_COL);
6093 					break;
6094 #endif   // _WIN32
6095 				case TAB_PRX:
6096 				case TAB_TBL:
6097 				case TAB_XCL:
6098 				case TAB_OCCUR:
6099 					bif= fnc == FNC_COL;
6100 					qrp= TabColumns(g, thd, db, tab, bif);
6101 
6102 					if (!qrp && bif && fnc != FNC_COL)         // tab is a view
6103 						qrp= MyColumns(g, thd, host, db, user, pwd, tab, NULL, port, false);
6104 
6105 					if (qrp && ttp == TAB_OCCUR && fnc != FNC_COL)
6106 						if (OcrColumns(g, qrp, col, ocl, rnk)) {
6107 							rc= HA_ERR_INTERNAL_ERROR;
6108 							goto err;
6109 						} // endif OcrColumns
6110 
6111 					break;
6112 				case TAB_PIVOT:
6113 					qrp= PivotColumns(g, tab, src, pic, fcl, skc, host, db, user, pwd, port);
6114 					break;
6115 				case TAB_VIR:
6116 					qrp= VirColumns(g, fnc == FNC_COL);
6117 					break;
6118 				case TAB_JSON:
6119 #if !defined(FORCE_BSON)
6120 					qrp= JSONColumns(g, db, dsn, topt, fnc == FNC_COL);
6121 					break;
6122 #endif   // !FORCE_BSON
6123 #if defined(BSON_SUPPORT)
6124         case TAB_BSON:
6125           qrp= BSONColumns(g, db, dsn, topt, fnc == FNC_COL);
6126           break;
6127 #endif   // BSON_SUPPORT
6128 #if defined(JAVA_SUPPORT)
6129 				case TAB_MONGO:
6130 					url= strz(g, create_info->connect_string);
6131 					qrp= MGOColumns(g, db, url, topt, fnc == FNC_COL);
6132 					break;
6133 #endif   // JAVA_SUPPORT
6134 #if defined(LIBXML2_SUPPORT) || defined(DOMDOC_SUPPORT)
6135 				case TAB_XML:
6136 					qrp= XMLColumns(g, (char*)db, tab, topt, fnc == FNC_COL);
6137 					break;
6138 #endif   // LIBXML2_SUPPORT  ||         DOMDOC_SUPPORT
6139 #if defined(REST_SUPPORT)
6140 				case TAB_REST:
6141 					qrp = RESTColumns(g, topt, tab, (char *)db, fnc == FNC_COL);
6142 					break;
6143 #endif   // REST_SUPPORT
6144 				case TAB_OEM:
6145 					qrp= OEMColumns(g, topt, tab, (char*)db, fnc == FNC_COL);
6146 					break;
6147 				default:
6148 					strcpy(g->Message, "System error during assisted discovery");
6149 					break;
6150 			} // endswitch ttp
6151 
6152 			if (!qrp) {
6153 				rc= HA_ERR_INTERNAL_ERROR;
6154 				goto err;
6155 			} // endif !qrp
6156 
6157 			if (fnc != FNC_NO || src || ttp == TAB_PIVOT) {
6158 				// Catalog like table
6159 				for (crp= qrp->Colresp; !rc && crp; crp= crp->Next) {
6160 					cnm= (ttp == TAB_PIVOT) ? crp->Name : encode(g, crp->Name);
6161 					typ= crp->Type;
6162 					len= crp->Length;
6163 					dec= crp->Prec;
6164 					flg= crp->Flag;
6165 					v= (crp->Kdata->IsUnsigned()) ? 'U' : crp->Var;
6166 					tm= (crp->Kdata->IsNullable()) ? 0 : NOT_NULL_FLAG;
6167 
6168 					if (!len && typ == TYPE_STRING)
6169 						len= 256;      // STRBLK's have 0 length
6170 
6171 					// Now add the field
6172 					if (add_field(&sql, ttp, cnm, typ, len, dec, NULL, tm,
6173 						NULL, NULL, NULL, NULL, flg, dbf, v))
6174 						rc= HA_ERR_OUT_OF_MEM;
6175 				} // endfor crp
6176 
6177 			} else {
6178                                 char *schem __attribute__((unused)) = NULL;
6179 				char *tn= NULL;
6180 
6181 				// Not a catalog table
6182 				if (!qrp->Nblin) {
6183 					if (tab)
6184 						sprintf(g->Message, "Cannot get columns from %s", tab);
6185 					else
6186 						strcpy(g->Message, "Fail to retrieve columns");
6187 
6188 					rc= HA_ERR_INTERNAL_ERROR;
6189 					goto err;
6190 				} // endif !nblin
6191 
6192 				// Restore language type
6193 				if (ttp == TAB_REST)
6194           ttp = ttr;
6195 
6196 				for (i= 0; !rc && i < qrp->Nblin; i++) {
6197 					typ= len= prec= dec= flg= 0;
6198 					tm= NOT_NULL_FLAG;
6199 					cnm= (char*)"noname";
6200 					dft= xtra= key= fmt= tn= NULL;
6201 					v= ' ';
6202 					rem= NULL;
6203 
6204 					for (crp= qrp->Colresp; crp; crp= crp->Next)
6205 						switch (crp->Fld) {
6206 							case FLD_NAME:
6207 								if (ttp == TAB_PRX ||
6208 									(ttp == TAB_CSV && topt->data_charset &&
6209 									(!stricmp(topt->data_charset, "UTF8") ||
6210 										!stricmp(topt->data_charset, "UTF-8"))))
6211 									cnm= crp->Kdata->GetCharValue(i);
6212 								else
6213 									cnm= encode(g, crp->Kdata->GetCharValue(i));
6214 
6215 								break;
6216 							case FLD_TYPE:
6217 								typ= crp->Kdata->GetIntValue(i);
6218 								v= (crp->Nulls) ? crp->Nulls[i] : 0;
6219 								break;
6220 							case FLD_TYPENAME:
6221 								tn= crp->Kdata->GetCharValue(i);
6222 								break;
6223 							case FLD_PREC:
6224 								// PREC must be always before LENGTH
6225 								len= prec= crp->Kdata->GetIntValue(i);
6226 								break;
6227 							case FLD_LENGTH:
6228 								len= crp->Kdata->GetIntValue(i);
6229 								break;
6230 							case FLD_SCALE:
6231 								dec= (!crp->Kdata->IsNull(i)) ? crp->Kdata->GetIntValue(i) : -1;
6232 								break;
6233 							case FLD_NULL:
6234 								if (crp->Kdata->GetIntValue(i))
6235 									tm= 0;               // Nullable
6236 
6237 								break;
6238 							case FLD_FLAG:
6239 								flg = crp->Kdata->GetIntValue(i);
6240 								break;
6241 							case FLD_FORMAT:
6242 								fmt= (crp->Kdata) ? crp->Kdata->GetCharValue(i) : NULL;
6243 								break;
6244 							case FLD_REM:
6245 								rem= crp->Kdata->GetCharValue(i);
6246 								break;
6247 								//          case FLD_CHARSET:
6248 															// No good because remote table is already translated
6249 								//            if (*(csn= crp->Kdata->GetCharValue(i)))
6250 								//              cs= get_charset_by_name(csn, 0);
6251 
6252 								//            break;
6253 							case FLD_DEFAULT:
6254 								dft= crp->Kdata->GetCharValue(i);
6255 								break;
6256 							case FLD_EXTRA:
6257 								xtra= crp->Kdata->GetCharValue(i);
6258 
6259 								// Auto_increment is not supported yet
6260 								if (!stricmp(xtra, "AUTO_INCREMENT"))
6261 									xtra= NULL;
6262 
6263 								break;
6264 							case FLD_KEY:
6265 								if (ttp == TAB_VIR)
6266 									key= crp->Kdata->GetCharValue(i);
6267 
6268 								break;
6269 							case FLD_SCHEM:
6270 #if defined(ODBC_SUPPORT) || defined(JAVA_SUPPORT)
6271 								if ((ttp == TAB_ODBC || ttp == TAB_JDBC) && crp->Kdata) {
6272 									if (schem && stricmp(schem, crp->Kdata->GetCharValue(i))) {
6273 										sprintf(g->Message,
6274 											"Several %s tables found, specify DBNAME", tab);
6275 										rc= HA_ERR_INTERNAL_ERROR;
6276 										goto err;
6277 									} else if (!schem)
6278 										schem= crp->Kdata->GetCharValue(i);
6279 
6280 								} // endif ttp
6281 #endif   // ODBC_SUPPORT	||				 JAVA_SUPPORT
6282 							default:
6283 								break;                 // Ignore
6284 						} // endswitch Fld
6285 
6286 #if defined(ODBC_SUPPORT)
6287 					if (ttp == TAB_ODBC) {
6288 						int  plgtyp;
6289 						bool w= false;            // Wide character type
6290 
6291 						// typ must be PLG type, not SQL type
6292 						if (!(plgtyp= TranslateSQLType(typ, dec, prec, v, w))) {
6293 							if (GetTypeConv() == TPC_SKIP) {
6294 								// Skip this column
6295 								sprintf(g->Message, "Column %s skipped (unsupported type %d)",
6296 									cnm, typ);
6297 								push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
6298 								continue;
6299 							} else {
6300 								sprintf(g->Message, "Unsupported SQL type %d", typ);
6301 								rc= HA_ERR_INTERNAL_ERROR;
6302 								goto err;
6303 							} // endif type_conv
6304 
6305 						} else
6306 							typ= plgtyp;
6307 
6308 						switch (typ) {
6309 							case TYPE_STRING:
6310 								if (w) {
6311 									sprintf(g->Message, "Column %s is wide characters", cnm);
6312 									push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, 0, g->Message);
6313 								} // endif w
6314 
6315 								break;
6316 							case TYPE_DOUBLE:
6317 								// Some data sources do not count dec in length (prec)
6318 								prec += (dec + 2);        // To be safe
6319 								break;
6320 							case TYPE_DECIM:
6321 								prec= len;
6322 								break;
6323 							default:
6324 								dec= 0;
6325 						} // endswitch typ
6326 
6327 					} else
6328 #endif   // ODBC_SUPPORT
6329 #if defined(JAVA_SUPPORT)
6330 						if (ttp == TAB_JDBC) {
6331 							int  plgtyp;
6332 
6333 							// typ must be PLG type, not SQL type
6334 							if (!(plgtyp= TranslateJDBCType(typ, tn, dec, prec, v))) {
6335 								if (GetTypeConv() == TPC_SKIP) {
6336 									// Skip this column
6337 									sprintf(g->Message, "Column %s skipped (unsupported type %d)",
6338 										cnm, typ);
6339 									push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
6340 									continue;
6341 								} else {
6342 									sprintf(g->Message, "Unsupported SQL type %d", typ);
6343 									rc= HA_ERR_INTERNAL_ERROR;
6344 									goto err;
6345 								} // endif type_conv
6346 
6347 							} else
6348 								typ= plgtyp;
6349 
6350 							switch (typ) {
6351 								case TYPE_DOUBLE:
6352 								case TYPE_DECIM:
6353 									// Some data sources do not count dec in length (prec)
6354 									prec += (dec + 2);        // To be safe
6355 									break;
6356 								default:
6357 									dec= 0;
6358 							} // endswitch typ
6359 
6360 						} else
6361 #endif   // ODBC_SUPPORT
6362 							// Make the arguments as required by add_fields
6363 							if (typ == TYPE_DOUBLE)
6364 								prec= len;
6365 
6366 						if (typ == TYPE_DATE)
6367 							prec= 0;
6368 
6369 						// Now add the field
6370 						if (add_field(&sql, ttp, cnm, typ, prec, dec, key, tm, rem, dft, xtra,
6371 								fmt, flg, dbf, v))
6372 							rc= HA_ERR_OUT_OF_MEM;
6373 				} // endfor i
6374 
6375 			} // endif fnc
6376 
6377 			if (!rc)
6378 				rc= init_table_share(thd, table_s, create_info, &sql);
6379 
6380 			//g->jump_level--;
6381 			//PopUser(xp);
6382 			//return rc;
6383 		} else {
6384 			rc= HA_ERR_UNSUPPORTED;
6385 		} // endif ok
6386 
6387 	} catch (int n) {
6388 		if (trace(1))
6389 			htrc("Exception %d: %s\n", n, g->Message);
6390 		rc= HA_ERR_INTERNAL_ERROR;
6391 	} catch (const char *msg) {
6392 		strcpy(g->Message, msg);
6393 		rc= HA_ERR_INTERNAL_ERROR;
6394 	} // end catch
6395 
6396  err:
6397   if (rc)
6398     my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6399 
6400 	PopUser(xp);
6401 	return rc;
6402 } // end of connect_assisted_discovery
6403 
6404 /**
6405   Get the database name from a qualified table name.
6406 */
6407 char *ha_connect::GetDBfromName(const char *name)
6408 {
6409   char *db, dbname[128], tbname[128];
6410 
6411   if (filename_to_dbname_and_tablename(name, dbname, sizeof(dbname),
6412                                              tbname, sizeof(tbname)))
6413     *dbname= 0;
6414 
6415   if (*dbname) {
6416     assert(xp && xp->g);
6417     db= (char*)PlugSubAlloc(xp->g, NULL, strlen(dbname + 1));
6418     strcpy(db, dbname);
6419   } else
6420     db= NULL;
6421 
6422   return db;
6423 } // end of GetDBfromName
6424 
6425 
6426 /**
6427   @brief
6428   create() is called to create a database. The variable name will have the name
6429   of the table.
6430 
6431   @details
6432   When create() is called you do not need to worry about
6433   opening the table. Also, the .frm file will have already been
6434   created so adjusting create_info is not necessary. You can overwrite
6435   the .frm file at this point if you wish to change the table
6436   definition, but there are no methods currently provided for doing
6437   so.
6438 
6439   Called from handle.cc by ha_create_table().
6440 
6441   @note
6442   Currently we do some checking on the create definitions and stop
6443   creating if an error is found. We wish we could change the table
6444   definition such as providing a default table type. However, as said
6445   above, there are no method to do so.
6446 
6447   @see
6448   ha_create_table() in handle.cc
6449 */
6450 
6451 int ha_connect::create(const char *name, TABLE *table_arg,
6452                        HA_CREATE_INFO *create_info)
6453 {
6454   int     rc= RC_OK;
6455   bool    dbf, inward;
6456   Field* *field;
6457   Field  *fp;
6458   TABTYPE type;
6459   TABLE  *st= table;                       // Probably unuseful
6460   THD    *thd= ha_thd();
6461   LEX_CSTRING cnc = table_arg->s->connect_string;
6462 #if defined(WITH_PARTITION_STORAGE_ENGINE)
6463   partition_info *part_info= table_arg->part_info;
6464 #else		// !WITH_PARTITION_STORAGE_ENGINE
6465 #define part_info 0
6466 #endif  // !WITH_PARTITION_STORAGE_ENGINE
6467   xp= GetUser(thd, xp);
6468   PGLOBAL g= xp->g;
6469 
6470   DBUG_ENTER("ha_connect::create");
6471   /*
6472     This assignment fixes test failures if some
6473     "ALTER TABLE t1 ADD KEY(a)" query exits on ER_ACCESS_DENIED_ERROR
6474     (e.g. on missing FILE_ACL). All following "CREATE TABLE" failed with
6475     "ERROR 1105: CONNECT index modification should be in-place"
6476     TODO: check with Olivier.
6477   */
6478   g->Xchk= NULL;
6479   int  sqlcom= thd_sql_command(table_arg->in_use);
6480   PTOS options= GetTableOptionStruct(table_arg->s);
6481 
6482   table= table_arg;         // Used by called functions
6483 
6484   if (trace(1))
6485     htrc("create: this=%p thd=%p xp=%p g=%p sqlcom=%d name=%s\n",
6486            this, thd, xp, g, sqlcom, GetTableName());
6487 
6488   // CONNECT engine specific table options:
6489   DBUG_ASSERT(options);
6490   type= GetTypeID(options->type);
6491 
6492   // Check table type
6493   if (type == TAB_UNDEF) {
6494     options->type= (options->srcdef)  ? "MYSQL" :
6495 #if defined(REST_SUPPORT)
6496                    (options->http) ? "JSON" :
6497 #endif   // REST_SUPPORT
6498                    (options->tabname) ? "PROXY" : "DOS";
6499     type= GetTypeID(options->type);
6500     sprintf(g->Message, "No table_type. Will be set to %s", options->type);
6501 
6502     if (sqlcom == SQLCOM_CREATE_TABLE)
6503       push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
6504 
6505   } else if (type == TAB_NIY) {
6506     sprintf(g->Message, "Unsupported table type %s", options->type);
6507     my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6508     DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
6509   } // endif ttp
6510 
6511   if (check_privileges(thd, options, GetDBfromName(name)))
6512     DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
6513 
6514   inward= IsFileType(type) && !options->filename &&
6515 		     ((type != TAB_JSON && type != TAB_BSON) || !cnc.length);
6516 
6517   if (options->data_charset) {
6518     const CHARSET_INFO *data_charset;
6519 
6520     if (!(data_charset= get_charset_by_csname(options->data_charset,
6521                                               MY_CS_PRIMARY, MYF(0)))) {
6522       my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), options->data_charset);
6523       DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
6524       } // endif charset
6525 
6526     if (type == TAB_XML && data_charset != &my_charset_utf8mb3_general_ci) {
6527       my_printf_error(ER_UNKNOWN_ERROR,
6528                       "DATA_CHARSET='%s' is not supported for TABLE_TYPE=XML",
6529                         MYF(0), options->data_charset);
6530       DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
6531       } // endif utf8
6532 
6533     } // endif charset
6534 
6535   if (!g) {
6536     rc= HA_ERR_INTERNAL_ERROR;
6537     DBUG_RETURN(rc);
6538   } else
6539     dbf= (GetTypeID(options->type) == TAB_DBF && !options->catfunc);
6540 
6541   // Can be null in ALTER TABLE
6542   if (create_info->alias.str)
6543     // Check whether a table is defined on itself
6544     switch (type) {
6545       case TAB_PRX:
6546       case TAB_XCL:
6547       case TAB_PIVOT:
6548       case TAB_OCCUR:
6549         if (options->srcdef) {
6550           strcpy(g->Message, "Cannot check looping reference");
6551           push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
6552         } else if (options->tabname) {
6553           if (!stricmp(options->tabname, create_info->alias.str) &&
6554              (!options->dbname ||
6555               !stricmp(options->dbname, table_arg->s->db.str))) {
6556             sprintf(g->Message, "A %s table cannot refer to itself",
6557                                 options->type);
6558             my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6559             DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
6560             } // endif tab
6561 
6562         } else {
6563           strcpy(g->Message, "Missing object table name or definition");
6564           my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6565           DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
6566         } // endif tabname
6567 
6568 				// fall through
6569       case TAB_MYSQL:
6570         if (!part_info)
6571        {const char *src= options->srcdef;
6572 				PCSZ host, db, tab= options->tabname;
6573         int  port;
6574 
6575         host= GetListOption(g, "host", options->oplist, NULL);
6576         db= GetStringOption("database", NULL);
6577         port= atoi(GetListOption(g, "port", options->oplist, "0"));
6578 
6579         if (create_info->connect_string.str &&
6580             create_info->connect_string.length) {
6581           char   *dsn= strz(g, create_info->connect_string);
6582           PMYDEF  mydef= new(g) MYSQLDEF();
6583 
6584           mydef->SetName(create_info->alias.str);
6585 
6586           if (!mydef->ParseURL(g, dsn, false)) {
6587             if (mydef->GetHostname())
6588               host= mydef->GetHostname();
6589 
6590 						if (mydef->GetTabschema())
6591 							db= mydef->GetTabschema();
6592 
6593             if (mydef->GetTabname())
6594               tab= mydef->GetTabname();
6595 
6596             if (mydef->GetPortnumber())
6597               port= mydef->GetPortnumber();
6598 
6599           } else {
6600             my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6601             DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
6602           } // endif ParseURL
6603 
6604           } // endif connect_string
6605 
6606         if (CheckSelf(g, table_arg->s, host, db, tab, src, port)) {
6607           my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6608           DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
6609           } // endif CheckSelf
6610 
6611        } break;
6612       default: /* do nothing */;
6613         break;
6614      } // endswitch ttp
6615 
6616   if (type == TAB_XML) {
6617     bool dom;                  // True: MS-DOM, False libxml2
6618 		PCSZ xsup= GetListOption(g, "Xmlsup", options->oplist, "*");
6619 
6620     // Note that if no support is specified, the default is MS-DOM
6621     // on Windows and libxml2 otherwise
6622     switch (toupper(*xsup)) {
6623       case '*':
6624 #if defined(_WIN32)
6625         dom= true;
6626 #else   // !_WIN32
6627         dom= false;
6628 #endif  // !_WIN32
6629         break;
6630       case 'M':
6631       case 'D':
6632         dom= true;
6633         break;
6634       default:
6635         dom= false;
6636         break;
6637       } // endswitch xsup
6638 
6639 #if !defined(DOMDOC_SUPPORT)
6640     if (dom) {
6641       strcpy(g->Message, "MS-DOM not supported by this version");
6642       xsup= NULL;
6643       } // endif DomDoc
6644 #endif   // !DOMDOC_SUPPORT
6645 
6646 #if !defined(LIBXML2_SUPPORT)
6647     if (!dom) {
6648       strcpy(g->Message, "libxml2 not supported by this version");
6649       xsup= NULL;
6650       } // endif Libxml2
6651 #endif   // !LIBXML2_SUPPORT
6652 
6653     if (!xsup) {
6654       my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6655       rc= HA_ERR_INTERNAL_ERROR;
6656       DBUG_RETURN(rc);
6657       } // endif xsup
6658 
6659     } // endif type
6660 
6661   if (type == TAB_JSON) {
6662     int pretty= atoi(GetListOption(g, "Pretty", options->oplist, "2"));
6663 
6664     if (!options->lrecl && pretty != 2) {
6665       sprintf(g->Message, "LRECL must be specified for pretty=%d", pretty);
6666       my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6667       rc= HA_ERR_INTERNAL_ERROR;
6668       DBUG_RETURN(rc);
6669       } // endif lrecl
6670 
6671     } // endif type	JSON
6672 
6673 	if (type == TAB_CSV) {
6674 		const char *sep= options->separator;
6675 
6676 		if (sep && strlen(sep) > 1) {
6677 			sprintf(g->Message, "Invalid separator %s", sep);
6678 			my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6679 			rc= HA_ERR_INTERNAL_ERROR;
6680 			DBUG_RETURN(rc);
6681 		} // endif sep
6682 
6683 	} // endif type	CSV
6684 
6685   // Check column types
6686   for (field= table_arg->field; *field; field++) {
6687     fp= *field;
6688 
6689     if (fp->vcol_info && !fp->stored_in_db)
6690       continue;            // This is a virtual column
6691 
6692     if (fp->flags & AUTO_INCREMENT_FLAG) {
6693       strcpy(g->Message, "Auto_increment is not supported yet");
6694       my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6695       rc= HA_ERR_INTERNAL_ERROR;
6696       DBUG_RETURN(rc);
6697       } // endif flags
6698 
6699     if (fp->flags & (BLOB_FLAG | ENUM_FLAG | SET_FLAG)) {
6700       sprintf(g->Message, "Unsupported type for column %s",
6701                           fp->field_name.str);
6702       my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6703       rc= HA_ERR_INTERNAL_ERROR;
6704       DBUG_RETURN(rc);
6705       } // endif flags
6706 
6707     if (type == TAB_VIR)
6708       if (!fp->option_struct || !fp->option_struct->special) {
6709         strcpy(g->Message, "Virtual tables accept only special or virtual columns");
6710         my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6711         rc= HA_ERR_INTERNAL_ERROR;
6712         DBUG_RETURN(rc);
6713         } // endif special
6714 
6715     switch (fp->type()) {
6716       case MYSQL_TYPE_SHORT:
6717       case MYSQL_TYPE_LONG:
6718       case MYSQL_TYPE_FLOAT:
6719       case MYSQL_TYPE_DOUBLE:
6720       case MYSQL_TYPE_TIMESTAMP:
6721       case MYSQL_TYPE_DATE:
6722       case MYSQL_TYPE_TIME:
6723       case MYSQL_TYPE_DATETIME:
6724       case MYSQL_TYPE_YEAR:
6725       case MYSQL_TYPE_NEWDATE:
6726       case MYSQL_TYPE_LONGLONG:
6727       case MYSQL_TYPE_TINY:
6728       case MYSQL_TYPE_DECIMAL:
6729       case MYSQL_TYPE_NEWDECIMAL:
6730       case MYSQL_TYPE_INT24:
6731         break;                     // Ok
6732       case MYSQL_TYPE_VARCHAR:
6733       case MYSQL_TYPE_VAR_STRING:
6734       case MYSQL_TYPE_STRING:
6735 #if 0
6736         if (!fp->field_length) {
6737           sprintf(g->Message, "Unsupported 0 length for column %s",
6738                               fp->field_name.str);
6739           rc= HA_ERR_INTERNAL_ERROR;
6740           my_printf_error(ER_UNKNOWN_ERROR,
6741                           "Unsupported 0 length for column %s",
6742                           MYF(0), fp->field_name.str);
6743           DBUG_RETURN(rc);
6744           } // endif fp
6745 #endif // 0
6746         break;                     // To be checked
6747       case MYSQL_TYPE_BIT:
6748       case MYSQL_TYPE_NULL:
6749       case MYSQL_TYPE_ENUM:
6750       case MYSQL_TYPE_SET:
6751       case MYSQL_TYPE_TINY_BLOB:
6752       case MYSQL_TYPE_MEDIUM_BLOB:
6753       case MYSQL_TYPE_LONG_BLOB:
6754       case MYSQL_TYPE_BLOB:
6755       case MYSQL_TYPE_GEOMETRY:
6756       default:
6757 //      fprintf(stderr, "Unsupported type column %s\n", fp->field_name.str);
6758         sprintf(g->Message, "Unsupported type for column %s",
6759                             fp->field_name.str);
6760         rc= HA_ERR_INTERNAL_ERROR;
6761         my_printf_error(ER_UNKNOWN_ERROR, "Unsupported type for column %s",
6762                         MYF(0), fp->field_name.str);
6763         DBUG_RETURN(rc);
6764         break;
6765       } // endswitch type
6766 
6767     if ((fp)->real_maybe_null() && !IsTypeNullable(type)) {
6768       my_printf_error(ER_UNKNOWN_ERROR,
6769                       "Table type %s does not support nullable columns",
6770                       MYF(0), options->type);
6771       DBUG_RETURN(HA_ERR_UNSUPPORTED);
6772       } // endif !nullable
6773 
6774     if (dbf) {
6775       bool b= false;
6776 
6777       if ((b= fp->field_name.length > 10))
6778         sprintf(g->Message, "DBF: Column name '%s' is too long (max=10)",
6779                             fp->field_name.str);
6780       else if ((b= fp->field_length > 255))
6781         sprintf(g->Message, "DBF: Column length too big for '%s' (max=255)",
6782                             fp->field_name.str);
6783 
6784       if (b) {
6785         my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6786         rc= HA_ERR_INTERNAL_ERROR;
6787         DBUG_RETURN(rc);
6788         } // endif b
6789 
6790       } // endif dbf
6791 
6792     } // endfor field
6793 
6794   if ((sqlcom == SQLCOM_CREATE_TABLE || *GetTableName() == '#') && inward) {
6795     // The file name is not specified, create a default file in
6796     // the database directory named table_name.table_type.
6797     // (temporarily not done for XML because a void file causes
6798     // the XML parsers to report an error on the first Insert)
6799     char buf[_MAX_PATH], fn[_MAX_PATH], dbpath[_MAX_PATH], lwt[12];
6800     int  h;
6801 
6802     // Check for incompatible options
6803     if (options->sepindex) {
6804       my_message(ER_UNKNOWN_ERROR,
6805             "SEPINDEX is incompatible with unspecified file name", MYF(0));
6806       DBUG_RETURN(HA_ERR_UNSUPPORTED);
6807 		} else if (GetTypeID(options->type) == TAB_VEC) {
6808 			if (!table->s->max_rows || options->split) {
6809 				my_printf_error(ER_UNKNOWN_ERROR,
6810 					"%s tables whose file name is unspecified cannot be split",
6811 					MYF(0), options->type);
6812 				DBUG_RETURN(HA_ERR_UNSUPPORTED);
6813 			} else if (options->header == 2) {
6814 				my_printf_error(ER_UNKNOWN_ERROR,
6815 					"header=2 is not allowed for %s tables whose file name is unspecified",
6816 					MYF(0), options->type);
6817 				DBUG_RETURN(HA_ERR_UNSUPPORTED);
6818 			} // endif's
6819 
6820 		} else if (options->zipped) {
6821 			my_message(ER_UNKNOWN_ERROR,
6822 				"ZIPPED is incompatible with unspecified file name", MYF(0));
6823 			DBUG_RETURN(HA_ERR_UNSUPPORTED);
6824 		}	// endif's options
6825 
6826     // Fold type to lower case
6827     for (int i= 0; i < 12; i++)
6828       if (!options->type[i]) {
6829         lwt[i]= 0;
6830         break;
6831       } else
6832         lwt[i]= tolower(options->type[i]);
6833 
6834     if (part_info) {
6835       char *p;
6836 
6837       strcpy(dbpath, name);
6838       p= strrchr(dbpath, slash);
6839       strncpy(partname, ++p, sizeof(partname) - 1);
6840       strcat(strcat(strcpy(buf, p), "."), lwt);
6841       *p= 0;
6842     } else {
6843       strcat(strcat(strcpy(buf, GetTableName()), "."), lwt);
6844       sprintf(g->Message, "No file name. Table will use %s", buf);
6845 
6846       if (sqlcom == SQLCOM_CREATE_TABLE)
6847         push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
6848 
6849       strcat(strcat(strcpy(dbpath, "./"), table->s->db.str), "/");
6850     } // endif part_info
6851 
6852     PlugSetPath(fn, buf, dbpath);
6853 
6854     if ((h= ::open(fn, O_CREAT | O_EXCL, 0666)) == -1) {
6855       if (errno == EEXIST)
6856         sprintf(g->Message, "Default file %s already exists", fn);
6857       else
6858         sprintf(g->Message, "Error %d creating file %s", errno, fn);
6859 
6860       push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
6861     } else
6862       ::close(h);
6863 
6864     if ((type == TAB_FMT || options->readonly) && sqlcom == SQLCOM_CREATE_TABLE)
6865       push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0,
6866         "Congratulation, you just created a read-only void table!");
6867 
6868     } // endif sqlcom
6869 
6870   if (trace(1))
6871     htrc("xchk=%p createas=%d\n", g->Xchk, g->Createas);
6872 
6873 	if (options->zipped) {
6874 #if defined(ZIP_SUPPORT)
6875 		// Check whether the zip entry must be made from a file
6876 		PCSZ fn= GetListOption(g, "Load", options->oplist, NULL);
6877 
6878 		if (fn) {
6879 			char zbuf[_MAX_PATH], buf[_MAX_PATH], dbpath[_MAX_PATH];
6880 			PCSZ entry= GetListOption(g, "Entry", options->oplist, NULL);
6881 			PCSZ a= GetListOption(g, "Append", options->oplist, "NO");
6882 			bool append= *a == '1' || *a == 'Y' || *a == 'y' || !stricmp(a, "ON");
6883 			PCSZ m= GetListOption(g, "Mulentries", options->oplist, "NO");
6884 			bool mul= *m == '1' || *m == 'Y' || *m == 'y' || !stricmp(m, "ON");
6885 
6886 			strcat(strcat(strcpy(dbpath, "./"), table->s->db.str), "/");
6887 			PlugSetPath(zbuf, options->filename, dbpath);
6888 			PlugSetPath(buf, fn, dbpath);
6889 
6890 			if (ZipLoadFile(g, zbuf, buf, entry, append, mul)) {
6891 				my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6892 				DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
6893 			}	// endif LoadFile
6894 
6895 		}	// endif fn
6896 #else   // !ZIP_SUPPORT
6897 		my_message(ER_UNKNOWN_ERROR, "Option ZIP not supported", MYF(0));
6898 		DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
6899 #endif  // !ZIP_SUPPORT
6900 	}	// endif zipped
6901 
6902   // To check whether indexes have to be made or remade
6903   if (!g->Xchk) {
6904     PIXDEF xdp;
6905 
6906     // We should be in CREATE TABLE, ALTER_TABLE or CREATE INDEX
6907     if (!(sqlcom == SQLCOM_CREATE_TABLE || sqlcom == SQLCOM_ALTER_TABLE ||
6908           sqlcom == SQLCOM_CREATE_INDEX || sqlcom == SQLCOM_DROP_INDEX))
6909 //         (sqlcom == SQLCOM_CREATE_INDEX && part_info) ||
6910 //         (sqlcom == SQLCOM_DROP_INDEX && part_info)))
6911       push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0,
6912         "Unexpected command in create, please contact CONNECT team");
6913 
6914     if (part_info && !inward)
6915       strncpy(partname, decode(g, strrchr(name, '#') + 1), sizeof(partname) - 1);
6916 //    strcpy(partname, part_info->curr_part_elem->partition_name);
6917 
6918     if (g->Alchecked == 0 &&
6919         (!IsFileType(type) || FileExists(options->filename, false))) {
6920       if (part_info) {
6921         sprintf(g->Message, "Data repartition in %s is unchecked", partname);
6922         push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
6923       } else if (sqlcom == SQLCOM_ALTER_TABLE) {
6924         // This is an ALTER to CONNECT from another engine.
6925         // It cannot be accepted because the table data would be modified
6926         // except when the target file does not exist.
6927         strcpy(g->Message, "Operation denied. Table data would be modified.");
6928         my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6929         DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
6930       } // endif part_info
6931 
6932       } // endif outward
6933 
6934     // Get the index definitions
6935     if ((xdp= GetIndexInfo()) || sqlcom == SQLCOM_DROP_INDEX) {
6936       if (options->multiple) {
6937         strcpy(g->Message, "Multiple tables are not indexable");
6938         my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6939         rc= HA_ERR_UNSUPPORTED;
6940       } else if (options->compressed) {
6941         strcpy(g->Message, "Compressed tables are not indexable");
6942         my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6943         rc= HA_ERR_UNSUPPORTED;
6944       } else if (GetIndexType(type) == 1) {
6945         PDBUSER dup= PlgGetUser(g);
6946         PCATLG  cat= (dup) ? dup->Catalog : NULL;
6947 
6948 				if (SetDataPath(g, table_arg->s->db.str)) {
6949 					my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6950 					rc= HA_ERR_INTERNAL_ERROR;
6951 				} else if (cat) {
6952           if (part_info)
6953             strncpy(partname,
6954                     decode(g, strrchr(name, (inward ? slash : '#')) + 1),
6955 										sizeof(partname) - 1);
6956 
6957           if ((rc= optimize(table->in_use, NULL))) {
6958             htrc("Create rc=%d %s\n", rc, g->Message);
6959             my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6960             rc= HA_ERR_INTERNAL_ERROR;
6961           } else
6962             CloseTable(g);
6963 
6964           } // endif cat
6965 
6966       } else if (GetIndexType(type) == 3) {
6967         if (CheckVirtualIndex(table_arg->s)) {
6968           my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6969           rc= HA_ERR_UNSUPPORTED;
6970           } // endif Check
6971 
6972       } else if (!GetIndexType(type)) {
6973         sprintf(g->Message, "Table type %s is not indexable", options->type);
6974         my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6975         rc= HA_ERR_UNSUPPORTED;
6976       } // endif index type
6977 
6978       } // endif xdp
6979 
6980   } else {
6981     // This should not happen anymore with indexing new way
6982     my_message(ER_UNKNOWN_ERROR,
6983                "CONNECT index modification should be in-place", MYF(0));
6984     DBUG_RETURN(HA_ERR_UNSUPPORTED);
6985   } // endif Xchk
6986 
6987   table= st;
6988   DBUG_RETURN(rc);
6989 } // end of create
6990 
6991 /**
6992   Used to check whether a file based outward table can be populated by
6993   an ALTER TABLE command. The conditions are:
6994   - file does not exist or is void
6995   - user has file privilege
6996 */
6997 bool ha_connect::FileExists(const char *fn, bool bf)
6998 {
6999   if (!fn || !*fn)
7000     return false;
7001   else if (IsPartitioned() && bf)
7002     return true;
7003 
7004   if (table) {
7005     const char *s;
7006 		char  tfn[_MAX_PATH], filename[_MAX_PATH], path[_MAX_PATH];
7007 		bool  b= false;
7008     int   n;
7009     struct stat info;
7010 
7011 #if defined(_WIN32)
7012     s= "\\";
7013 #else   // !_WIN32
7014     s= "/";
7015 #endif  // !_WIN32
7016     if (IsPartitioned()) {
7017       sprintf(tfn, fn, GetPartName());
7018 
7019       // This is to avoid an initialization error raised by the
7020       // test on check_table_flags made in ha_partition::open
7021       // that can fail if some partition files are empty.
7022       b= true;
7023     } else
7024       strcpy(tfn, fn);
7025 
7026     strcat(strcat(strcat(strcpy(path, "."), s), table->s->db.str), s);
7027     PlugSetPath(filename, tfn, path);
7028     n= stat(filename, &info);
7029 
7030     if (n < 0) {
7031       if (errno != ENOENT) {
7032         char buf[_MAX_PATH + 20];
7033 
7034         sprintf(buf, "Error %d for file %s", errno, filename);
7035         push_warning(table->in_use, Sql_condition::WARN_LEVEL_WARN, 0, buf);
7036         return true;
7037       } else
7038         return false;
7039 
7040     } else
7041       return (info.st_size || b) ? true : false;
7042 
7043     } // endif table
7044 
7045   return true;
7046 } // end of FileExists
7047 
7048 // Called by SameString and NoFieldOptionChange
7049 bool ha_connect::CheckString(PCSZ str1, PCSZ str2)
7050 {
7051   bool  b1= (!str1 || !*str1), b2= (!str2 || !*str2);
7052 
7053   if (b1 && b2)
7054     return true;
7055   else if ((b1 && !b2) || (!b1 && b2) || stricmp(str1, str2))
7056     return false;
7057 
7058   return true;
7059 } // end of CheckString
7060 
7061 /**
7062   check whether a string option have changed
7063   */
7064 bool ha_connect::SameString(TABLE *tab, PCSZ opn)
7065 {
7066   PCSZ str1, str2;
7067 
7068   tshp= tab->s;                 // The altered table
7069   str1= GetStringOption(opn);
7070   tshp= NULL;
7071   str2= GetStringOption(opn);
7072   return CheckString(str1, str2);
7073 } // end of SameString
7074 
7075 /**
7076   check whether a Boolean option have changed
7077   */
7078 bool ha_connect::SameBool(TABLE *tab, PCSZ opn)
7079 {
7080   bool b1, b2;
7081 
7082   tshp= tab->s;                 // The altered table
7083   b1= GetBooleanOption(opn, false);
7084   tshp= NULL;
7085   b2= GetBooleanOption(opn, false);
7086   return (b1 == b2);
7087 } // end of SameBool
7088 
7089 /**
7090   check whether an integer option have changed
7091   */
7092 bool ha_connect::SameInt(TABLE *tab, PCSZ opn)
7093 {
7094   int i1, i2;
7095 
7096   tshp= tab->s;                 // The altered table
7097   i1= GetIntegerOption(opn);
7098   tshp= NULL;
7099   i2= GetIntegerOption(opn);
7100 
7101   if (!stricmp(opn, "lrecl"))
7102     return (i1 == i2 || !i1 || !i2);
7103   else if (!stricmp(opn, "ending"))
7104     return (i1 == i2 || i1 <= 0 || i2 <= 0);
7105   else
7106     return (i1 == i2);
7107 
7108 } // end of SameInt
7109 
7110 /**
7111   check whether a field option have changed
7112   */
7113 bool ha_connect::NoFieldOptionChange(TABLE *tab)
7114 {
7115   bool rc= true;
7116   ha_field_option_struct *fop1, *fop2;
7117   Field* *fld1= table->s->field;
7118   Field* *fld2= tab->s->field;
7119 
7120   for (; rc && *fld1 && *fld2; fld1++, fld2++) {
7121     fop1= (*fld1)->option_struct;
7122     fop2= (*fld2)->option_struct;
7123 
7124     rc= (fop1->offset == fop2->offset &&
7125          fop1->fldlen == fop2->fldlen &&
7126          CheckString(fop1->dateformat, fop2->dateformat) &&
7127          CheckString(fop1->fieldformat, fop2->fieldformat) &&
7128 			   CheckString(fop1->special, fop2->special));
7129     } // endfor fld
7130 
7131   return rc;
7132 } // end of NoFieldOptionChange
7133 
7134  /**
7135     Check if a storage engine supports a particular alter table in-place
7136 
7137     @param    altered_table     TABLE object for new version of table.
7138     @param    ha_alter_info     Structure describing changes to be done
7139                                 by ALTER TABLE and holding data used
7140                                 during in-place alter.
7141 
7142     @retval   HA_ALTER_ERROR                  Unexpected error.
7143     @retval   HA_ALTER_INPLACE_NOT_SUPPORTED  Not supported, must use copy.
7144     @retval   HA_ALTER_INPLACE_EXCLUSIVE_LOCK Supported, but requires X lock.
7145     @retval   HA_ALTER_INPLACE_COPY_LOCK
7146                                               Supported, but requires SNW lock
7147                                               during main phase. Prepare phase
7148                                               requires X lock.
7149     @retval   HA_ALTER_INPLACE_SHARED_LOCK    Supported, but requires SNW lock.
7150     @retval   HA_ALTER_INPLACE_COPY_NO_LOCK
7151                                               Supported, concurrent reads/writes
7152                                               allowed. However, prepare phase
7153                                               requires X lock.
7154     @retval   HA_ALTER_INPLACE_NO_LOCK        Supported, concurrent
7155                                               reads/writes allowed.
7156 
7157     @note The default implementation uses the old in-place ALTER API
7158     to determine if the storage engine supports in-place ALTER or not.
7159 
7160     @note Called without holding thr_lock.c lock.
7161  */
7162 enum_alter_inplace_result
7163 ha_connect::check_if_supported_inplace_alter(TABLE *altered_table,
7164                                 Alter_inplace_info *ha_alter_info)
7165 {
7166   DBUG_ENTER("check_if_supported_alter");
7167 
7168   bool            idx= false, outward= false;
7169   THD            *thd= ha_thd();
7170   int             sqlcom= thd_sql_command(thd);
7171   TABTYPE         newtyp, type= TAB_UNDEF;
7172   HA_CREATE_INFO *create_info= ha_alter_info->create_info;
7173   PTOS            newopt, oldopt;
7174   xp= GetUser(thd, xp);
7175   PGLOBAL         g= xp->g;
7176 
7177   if (!g || !table) {
7178     my_message(ER_UNKNOWN_ERROR, "Cannot check ALTER operations", MYF(0));
7179     DBUG_RETURN(HA_ALTER_ERROR);
7180     } // endif Xchk
7181 
7182   newopt= altered_table->s->option_struct;
7183   oldopt= table->s->option_struct;
7184 
7185   // If this is the start of a new query, cleanup the previous one
7186   if (xp->CheckCleanup()) {
7187     tdbp= NULL;
7188     valid_info= false;
7189     } // endif CheckCleanup
7190 
7191   g->Alchecked= 1;       // Tested in create
7192   g->Xchk= NULL;
7193   type= GetRealType(oldopt);
7194   newtyp= GetRealType(newopt);
7195 
7196   // No copy algorithm for outward tables
7197   outward= (!IsFileType(type) || (oldopt->filename && *oldopt->filename));
7198 
7199   // Index operations
7200   alter_table_operations index_operations=
7201     ALTER_ADD_INDEX |
7202     ALTER_DROP_INDEX |
7203     ALTER_ADD_NON_UNIQUE_NON_PRIM_INDEX |
7204     ALTER_DROP_NON_UNIQUE_NON_PRIM_INDEX |
7205     ALTER_ADD_UNIQUE_INDEX |
7206     ALTER_DROP_UNIQUE_INDEX |
7207     ALTER_ADD_PK_INDEX |
7208     ALTER_DROP_PK_INDEX |
7209     ALTER_INDEX_ORDER;
7210 
7211   alter_table_operations inplace_offline_operations=
7212     ALTER_COLUMN_TYPE_CHANGE_BY_ENGINE |
7213     ALTER_COLUMN_NAME |
7214     ALTER_COLUMN_DEFAULT |
7215     ALTER_CHANGE_CREATE_OPTION |
7216     ALTER_RENAME |
7217     ALTER_PARTITIONED | index_operations;
7218 
7219   if (ha_alter_info->handler_flags & index_operations ||
7220       !SameString(altered_table, "optname") ||
7221       !SameBool(altered_table, "sepindex")) {
7222     if (newopt->multiple) {
7223       strcpy(g->Message, "Multiple tables are not indexable");
7224       my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
7225       DBUG_RETURN(HA_ALTER_ERROR);
7226     } else if (newopt->compressed) {
7227       strcpy(g->Message, "Compressed tables are not indexable");
7228       my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
7229       DBUG_RETURN(HA_ALTER_ERROR);
7230     } else if (GetIndexType(type) == 1) {
7231       g->Xchk= new(g) XCHK;
7232       PCHK xcp= (PCHK)g->Xchk;
7233 
7234       xcp->oldpix= GetIndexInfo(table->s);
7235       xcp->newpix= GetIndexInfo(altered_table->s);
7236       xcp->oldsep= GetBooleanOption("sepindex", false);
7237       xcp->oldsep= xcp->SetName(g, GetStringOption("optname"));
7238       tshp= altered_table->s;
7239       xcp->newsep= GetBooleanOption("sepindex", false);
7240       xcp->newsep= xcp->SetName(g, GetStringOption("optname"));
7241       tshp= NULL;
7242 
7243       if (trace(1) && g->Xchk)
7244         htrc(
7245           "oldsep=%d newsep=%d oldopn=%s newopn=%s oldpix=%p newpix=%p\n",
7246                 xcp->oldsep, xcp->newsep,
7247                 SVP(xcp->oldopn), SVP(xcp->newopn),
7248                 xcp->oldpix, xcp->newpix);
7249 
7250       if (sqlcom == SQLCOM_ALTER_TABLE)
7251         idx= true;
7252       else
7253         DBUG_RETURN(HA_ALTER_INPLACE_EXCLUSIVE_LOCK);
7254 
7255     } else if (GetIndexType(type) == 3) {
7256       if (CheckVirtualIndex(altered_table->s)) {
7257         my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
7258         DBUG_RETURN(HA_ALTER_ERROR);
7259         } // endif Check
7260 
7261     } else if (!GetIndexType(type)) {
7262       sprintf(g->Message, "Table type %s is not indexable", oldopt->type);
7263       my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
7264       DBUG_RETURN(HA_ALTER_ERROR);
7265     } // endif index type
7266 
7267     } // endif index operation
7268 
7269   if (!SameString(altered_table, "filename")) {
7270     if (!outward) {
7271       // Conversion to outward table is only allowed for file based
7272       // tables whose file does not exist.
7273       tshp= altered_table->s;
7274 			PCSZ fn= GetStringOption("filename");
7275       tshp= NULL;
7276 
7277       if (FileExists(fn, false)) {
7278         strcpy(g->Message, "Operation denied. Table data would be lost.");
7279         my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
7280         DBUG_RETURN(HA_ALTER_ERROR);
7281       } else
7282         goto fin;
7283 
7284     } else
7285       goto fin;
7286 
7287     } // endif filename
7288 
7289   /* Is there at least one operation that requires copy algorithm? */
7290   if (ha_alter_info->handler_flags & ~inplace_offline_operations)
7291     goto fin;
7292 
7293   /*
7294     ALTER TABLE tbl_name CONVERT TO CHARACTER SET .. and
7295     ALTER TABLE table_name DEFAULT CHARSET= .. most likely
7296     change column charsets and so not supported in-place through
7297     old API.
7298 
7299     Changing of PACK_KEYS, MAX_ROWS and ROW_FORMAT options were
7300     not supported as in-place operations in old API either.
7301   */
7302   if (create_info->used_fields & (HA_CREATE_USED_CHARSET |
7303                                   HA_CREATE_USED_DEFAULT_CHARSET |
7304                                   HA_CREATE_USED_PACK_KEYS |
7305                                   HA_CREATE_USED_MAX_ROWS) ||
7306       (table->s->row_type != create_info->row_type))
7307     goto fin;
7308 
7309 #if 0
7310   uint table_changes= (ha_alter_info->handler_flags &
7311                        ALTER_COLUMN_TYPE_CHANGE_BY_ENGINE) ?
7312     IS_EQUAL_PACK_LENGTH : IS_EQUAL_YES;
7313 
7314   if (table->file->check_if_incompatible_data(create_info, table_changes)
7315       == COMPATIBLE_DATA_YES)
7316     DBUG_RETURN(HA_ALTER_INPLACE_EXCLUSIVE_LOCK);
7317 #endif // 0
7318 
7319   // This was in check_if_incompatible_data
7320   if (NoFieldOptionChange(altered_table) &&
7321       type == newtyp &&
7322       SameInt(altered_table, "lrecl") &&
7323       SameInt(altered_table, "elements") &&
7324       SameInt(altered_table, "header") &&
7325       SameInt(altered_table, "quoted") &&
7326       SameInt(altered_table, "ending") &&
7327       SameInt(altered_table, "compressed"))
7328     DBUG_RETURN(HA_ALTER_INPLACE_EXCLUSIVE_LOCK);
7329 
7330 fin:
7331   if (idx) {
7332     // Indexing is only supported inplace
7333     my_message(ER_ALTER_OPERATION_NOT_SUPPORTED,
7334       "Alter operations not supported together by CONNECT", MYF(0));
7335     DBUG_RETURN(HA_ALTER_ERROR);
7336   } else if (outward) {
7337     if (IsFileType(type))
7338       push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0,
7339         "This is an outward table, table data were not modified.");
7340 
7341     DBUG_RETURN(HA_ALTER_INPLACE_EXCLUSIVE_LOCK);
7342   } else
7343     DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
7344 
7345 } // end of check_if_supported_inplace_alter
7346 
7347 
7348 /**
7349   check_if_incompatible_data() called if ALTER TABLE can't detect otherwise
7350   if new and old definition are compatible
7351 
7352   @details If there are no other explicit signs like changed number of
7353   fields this function will be called by compare_tables()
7354   (sql/sql_tables.cc) to decide should we rewrite whole table or only .frm
7355   file.
7356 
7357   @note: This function is no more called by check_if_supported_inplace_alter
7358 */
7359 
7360 bool ha_connect::check_if_incompatible_data(HA_CREATE_INFO *, uint)
7361 {
7362   DBUG_ENTER("ha_connect::check_if_incompatible_data");
7363   // TO DO: really implement and check it.
7364   push_warning(ha_thd(), Sql_condition::WARN_LEVEL_WARN, 0,
7365       "Unexpected call to check_if_incompatible_data.");
7366   DBUG_RETURN(COMPATIBLE_DATA_NO);
7367 } // end of check_if_incompatible_data
7368 
7369 /****************************************************************************
7370  * CONNECT MRR implementation: use DS-MRR
7371    This is just copied from myisam
7372  ***************************************************************************/
7373 
7374 int ha_connect::multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
7375                                      uint n_ranges, uint mode,
7376                                      HANDLER_BUFFER *buf)
7377 {
7378   return ds_mrr.dsmrr_init(this, seq, seq_init_param, n_ranges, mode, buf);
7379 } // end of multi_range_read_init
7380 
7381 int ha_connect::multi_range_read_next(range_id_t *range_info)
7382 {
7383   return ds_mrr.dsmrr_next(range_info);
7384 } // end of multi_range_read_next
7385 
7386 ha_rows ha_connect::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
7387                                                void *seq_init_param,
7388                                                uint n_ranges, uint *bufsz,
7389                                                uint *flags, Cost_estimate *cost)
7390 {
7391   /*
7392     This call is here because there is no location where this->table would
7393     already be known.
7394     TODO: consider moving it into some per-query initialization call.
7395   */
7396   ds_mrr.init(this, table);
7397 
7398   // MMR is implemented for "local" file based tables only
7399   if (!IsFileType(GetRealType(GetTableOptionStruct())))
7400     *flags|= HA_MRR_USE_DEFAULT_IMPL;
7401 
7402   ha_rows rows= ds_mrr.dsmrr_info_const(keyno, seq, seq_init_param, n_ranges,
7403                                         bufsz, flags, cost);
7404   xp->g->Mrr= !(*flags & HA_MRR_USE_DEFAULT_IMPL);
7405   return rows;
7406 } // end of multi_range_read_info_const
7407 
7408 ha_rows ha_connect::multi_range_read_info(uint keyno, uint n_ranges, uint keys,
7409                                          uint key_parts, uint *bufsz,
7410                                          uint *flags, Cost_estimate *cost)
7411 {
7412   ds_mrr.init(this, table);
7413 
7414   // MMR is implemented for "local" file based tables only
7415   if (!IsFileType(GetRealType(GetTableOptionStruct())))
7416     *flags|= HA_MRR_USE_DEFAULT_IMPL;
7417 
7418   ha_rows rows= ds_mrr.dsmrr_info(keyno, n_ranges, keys, key_parts, bufsz,
7419                                   flags, cost);
7420   xp->g->Mrr= !(*flags & HA_MRR_USE_DEFAULT_IMPL);
7421   return rows;
7422 } // end of multi_range_read_info
7423 
7424 
7425 int ha_connect::multi_range_read_explain_info(uint mrr_mode, char *str,
7426                                              size_t size)
7427 {
7428   return ds_mrr.dsmrr_explain_info(mrr_mode, str, size);
7429 } // end of multi_range_read_explain_info
7430 
7431 /* CONNECT MRR implementation ends */
7432 
7433 #if 0
7434 // Does this make sens for CONNECT?
7435 Item *ha_connect::idx_cond_push(uint keyno_arg, Item* idx_cond_arg)
7436 {
7437   pushed_idx_cond_keyno= keyno_arg;
7438   pushed_idx_cond= idx_cond_arg;
7439   in_range_check_pushed_down= TRUE;
7440   if (active_index == pushed_idx_cond_keyno)
7441     mi_set_index_cond_func(file, handler_index_cond_check, this);
7442   return NULL;
7443 }
7444 #endif // 0
7445 
7446 
7447 struct st_mysql_storage_engine connect_storage_engine=
7448 { MYSQL_HANDLERTON_INTERFACE_VERSION };
7449 
7450 /***********************************************************************/
7451 /*  CONNECT global variables definitions.                              */
7452 /***********************************************************************/
7453 #if defined(XMAP)
7454 // Using file mapping for indexes if true
7455 static MYSQL_SYSVAR_BOOL(indx_map, xmap, PLUGIN_VAR_RQCMDARG,
7456        "Using file mapping for indexes", NULL, NULL, 0);
7457 #endif   // XMAP
7458 
7459 #if defined(XMSG)
7460 static MYSQL_SYSVAR_STR(errmsg_dir_path, msg_path,
7461 //     PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
7462        PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
7463        "Path to the directory where are the message files",
7464 //     check_msg_path, update_msg_path,
7465        NULL, NULL,
7466        "../../../../storage/connect/");     // for testing
7467 #endif   // XMSG
7468 
7469 #if defined(JAVA_SUPPORT)
7470 static MYSQL_SYSVAR_STR(jvm_path, JvmPath,
7471 	PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
7472 	"Path to the directory where is the JVM lib",
7473 	//     check_jvm_path, update_jvm_path,
7474 	NULL, NULL,	NULL);
7475 
7476 static MYSQL_SYSVAR_STR(class_path, ClassPath,
7477 	PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
7478 	"Java class path",
7479 	//     check_class_path, update_class_path,
7480 	NULL, NULL, NULL);
7481 #endif   // JAVA_SUPPORT
7482 
7483 
7484 static struct st_mysql_sys_var* connect_system_variables[]= {
7485   MYSQL_SYSVAR(xtrace),
7486   MYSQL_SYSVAR(conv_size),
7487   MYSQL_SYSVAR(type_conv),
7488 #if defined(XMAP)
7489   MYSQL_SYSVAR(indx_map),
7490 #endif   // XMAP
7491   MYSQL_SYSVAR(work_size),
7492   MYSQL_SYSVAR(use_tempfile),
7493   MYSQL_SYSVAR(exact_info),
7494 #if defined(XMSG) || defined(NEWMSG)
7495   MYSQL_SYSVAR(msg_lang),
7496 #endif   // XMSG || NEWMSG
7497 #if defined(XMSG)
7498   MYSQL_SYSVAR(errmsg_dir_path),
7499 #endif   // XMSG
7500 	MYSQL_SYSVAR(json_null),
7501 	MYSQL_SYSVAR(json_all_path),
7502 	MYSQL_SYSVAR(default_depth),
7503   MYSQL_SYSVAR(default_prec),
7504   MYSQL_SYSVAR(json_grp_size),
7505 #if defined(JAVA_SUPPORT)
7506 	MYSQL_SYSVAR(jvm_path),
7507 	MYSQL_SYSVAR(class_path),
7508 	MYSQL_SYSVAR(java_wrapper),
7509 #endif   // JAVA_SUPPORT
7510 #if defined(JAVA_SUPPORT) || defined(CMGO_SUPPORT)
7511 	MYSQL_SYSVAR(enable_mongo),
7512 #endif   // JAVA_SUPPORT || CMGO_SUPPORT
7513 	MYSQL_SYSVAR(cond_push),
7514 #if defined(BSON_SUPPORT)
7515   MYSQL_SYSVAR(force_bson),
7516 #endif   // BSON_SUPPORT
7517   NULL
7518 };
7519 
7520 maria_declare_plugin(connect)
7521 {
7522   MYSQL_STORAGE_ENGINE_PLUGIN,
7523   &connect_storage_engine,
7524   "CONNECT",
7525   "Olivier Bertrand",
7526   "Management of External Data (SQL/NOSQL/MED), including Rest query results",
7527   PLUGIN_LICENSE_GPL,
7528   connect_init_func,                            /* Plugin Init */
7529   connect_done_func,                            /* Plugin Deinit */
7530   0x0107,                                       /* version number (1.07) */
7531   NULL,                                         /* status variables */
7532   connect_system_variables,                     /* system variables */
7533   "1.07.0003",                                  /* string version */
7534 	MariaDB_PLUGIN_MATURITY_STABLE                /* maturity */
7535 }
7536 maria_declare_plugin_end;
7537