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.0003 June 06, 2021";
174 #if defined(_WIN32)
175        char compver[]= "Version 1.07.0003 " __DATE__ " "  __TIME__;
176        char slash= '\\';
177 #else   // !_WIN32
178        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->state= SHOW_OPTION_YES;
828   connect_hton->create= connect_create_handler;
829   connect_hton->flags= HTON_TEMPORARY_NOT_SUPPORTED;
830   connect_hton->table_options= connect_table_option_list;
831   connect_hton->field_options= connect_field_option_list;
832   connect_hton->index_options= connect_index_option_list;
833   connect_hton->tablefile_extensions= ha_connect_exts;
834   connect_hton->discover_table_structure= connect_assisted_discovery;
835 
836   if (trace(128))
837     sql_print_information("connect_init: hton=%p", p);
838 
839   DTVAL::SetTimeShift();      // Initialize time zone shift once for all
840   BINCOL::SetEndian();        // Initialize host endian setting
841 #if defined(JAVA_SUPPORT)
842 	JAVAConn::SetJVM();
843 #endif   // JAVA_SUPPORT
844   DBUG_RETURN(0);
845 } // end of connect_init_func
846 
847 
848 /**
849   @brief
850   Plugin clean up
851 */
connect_done_func(void *)852 static int connect_done_func(void *)
853 {
854   int error= 0;
855   PCONNECT pc, pn;
856   DBUG_ENTER("connect_done_func");
857 
858 #ifdef LIBXML2_SUPPORT
859   XmlCleanupParserLib();
860 #endif // LIBXML2_SUPPORT
861 
862 #if defined(CMGO_SUPPORT)
863 	CMgoConn::mongo_init(false);
864 #endif   // CMGO_SUPPORT
865 
866 #ifdef JAVA_SUPPORT
867 	JAVAConn::ResetJVM();
868 #endif // JAVA_SUPPORT
869 
870 #if	!defined(_WIN32)
871 	PROFILE_End();
872 #endif  // !_WIN32
873 
874 	pthread_mutex_lock(&usrmut);
875 	for (pc= user_connect::to_users; pc; pc= pn) {
876     if (pc->g)
877       PlugCleanup(pc->g, true);
878 
879     pn= pc->next;
880     delete pc;
881     } // endfor pc
882 
883 	pthread_mutex_unlock(&usrmut);
884 
885 	pthread_mutex_destroy(&usrmut);
886 	pthread_mutex_destroy(&parmut);
887 	pthread_mutex_destroy(&tblmut);
888 	connect_hton= NULL;
889   DBUG_RETURN(error);
890 } // end of connect_done_func
891 
892 
893 /**
894   @brief
895   Example of simple lock controls. The "share" it creates is a
896   structure we will pass to each CONNECT handler. Do you have to have
897   one of these? Well, you have pieces that are used for locking, and
898   they are needed to function.
899 */
900 
get_share()901 CONNECT_SHARE *ha_connect::get_share()
902 {
903   CONNECT_SHARE *tmp_share;
904 
905   lock_shared_ha_data();
906 
907   if (!(tmp_share= static_cast<CONNECT_SHARE*>(get_ha_share_ptr()))) {
908     tmp_share= new CONNECT_SHARE;
909     if (!tmp_share)
910       goto err;
911     mysql_mutex_init(con_key_mutex_CONNECT_SHARE_mutex,
912                      &tmp_share->mutex, MY_MUTEX_INIT_FAST);
913     set_ha_share_ptr(static_cast<Handler_share*>(tmp_share));
914     } // endif tmp_share
915 
916  err:
917   unlock_shared_ha_data();
918   return tmp_share;
919 } // end of get_share
920 
921 
connect_create_handler(handlerton * hton,TABLE_SHARE * table,MEM_ROOT * mem_root)922 static handler* connect_create_handler(handlerton *hton,
923                                    TABLE_SHARE *table,
924                                    MEM_ROOT *mem_root)
925 {
926   handler *h= new (mem_root) ha_connect(hton, table);
927 
928   if (trace(128))
929     htrc("New CONNECT %p, table: %.*s\n", h,
930           table ? table->table_name.length : 6,
931           table ? table->table_name.str : "<null>");
932 
933   return h;
934 } // end of connect_create_handler
935 
936 /****************************************************************************/
937 /*  ha_connect constructor.                                                 */
938 /****************************************************************************/
ha_connect(handlerton * hton,TABLE_SHARE * table_arg)939 ha_connect::ha_connect(handlerton *hton, TABLE_SHARE *table_arg)
940        :handler(hton, table_arg)
941 {
942   hnum= ++num;
943   xp= (table) ? GetUser(ha_thd(), NULL) : NULL;
944   if (xp)
945     xp->SetHandler(this);
946 #if defined(_WIN32)
947   datapath= ".\\";
948 #else   // !_WIN32
949   datapath= "./";
950 #endif  // !_WIN32
951   tdbp= NULL;
952   sdvalin1= sdvalin2= sdvalin3= sdvalin4= NULL;
953   sdvalout= NULL;
954   xmod= MODE_ANY;
955   istable= false;
956   memset(partname, 0, sizeof(partname));
957   bzero((char*) &xinfo, sizeof(XINFO));
958   valid_info= false;
959   valid_query_id= 0;
960   creat_query_id= (table && table->in_use) ? table->in_use->query_id : 0;
961   stop= false;
962   alter= false;
963   mrr= false;
964   nox= true;
965   abort= false;
966   indexing= -1;
967   locked= 0;
968   part_id= NULL;
969   data_file_name= NULL;
970   index_file_name= NULL;
971   enable_activate_all_index= 0;
972   int_table_flags= (HA_NO_TRANSACTIONS | HA_NO_PREFIX_CHAR_KEYS);
973   ref_length= sizeof(int);
974   share= NULL;
975   tshp= NULL;
976 } // end of ha_connect constructor
977 
978 
979 /****************************************************************************/
980 /*  ha_connect destructor.                                                  */
981 /****************************************************************************/
~ha_connect(void)982 ha_connect::~ha_connect(void)
983 {
984   if (trace(128))
985     htrc("Delete CONNECT %p, table: %.*s, xp=%p count=%d\n", this,
986                          table ? table->s->table_name.length : 6,
987                          table ? table->s->table_name.str : "<null>",
988                          xp, xp ? xp->count : 0);
989 
990 	PopUser(xp);
991 } // end of ha_connect destructor
992 
993 
994 /****************************************************************************/
995 /*  Check whether this user can be removed.                                 */
996 /****************************************************************************/
PopUser(PCONNECT xp)997 static void PopUser(PCONNECT xp)
998 {
999 	if (xp) {
1000 		pthread_mutex_lock(&usrmut);
1001 		xp->count--;
1002 
1003 		if (!xp->count) {
1004 			PCONNECT p;
1005 
1006 			for (p= user_connect::to_users; p; p= p->next)
1007 			  if (p == xp)
1008 				  break;
1009 
1010 		  if (p) {
1011 			  if (p->next)
1012 				  p->next->previous= p->previous;
1013 
1014 			  if (p->previous)
1015 				  p->previous->next= p->next;
1016 			  else
1017 				  user_connect::to_users= p->next;
1018 
1019 		  } // endif p
1020 
1021 			PlugCleanup(xp->g, true);
1022 			delete xp;
1023 		} // endif count
1024 
1025 		pthread_mutex_unlock(&usrmut);
1026 	} // endif xp
1027 
1028 } // end of PopUser
1029 
1030 
1031 /****************************************************************************/
1032 /*  Get a pointer to the user of this handler.                              */
1033 /****************************************************************************/
GetUser(THD * thd,PCONNECT xp)1034 static PCONNECT GetUser(THD *thd, PCONNECT xp)
1035 {
1036 	if (!thd)
1037     return NULL;
1038 
1039 	if (xp) {
1040 		if (thd == xp->thdp)
1041 			return xp;
1042 
1043 		PopUser(xp);		// Avoid memory leak
1044 	} // endif xp
1045 
1046 	pthread_mutex_lock(&usrmut);
1047 
1048 	for (xp= user_connect::to_users; xp; xp= xp->next)
1049     if (thd == xp->thdp)
1050       break;
1051 
1052 	if (xp)
1053 		xp->count++;
1054 
1055 	pthread_mutex_unlock(&usrmut);
1056 
1057 	if (!xp) {
1058 		xp= new user_connect(thd);
1059 
1060 		if (xp->user_init()) {
1061 			delete xp;
1062 			xp= NULL;
1063 		} // endif user_init
1064 
1065 	}	// endif xp
1066 
1067   //} else
1068   //  xp->count++;
1069 
1070   return xp;
1071 } // end of GetUser
1072 
1073 /****************************************************************************/
1074 /*  Get the global pointer of the user of this handler.                     */
1075 /****************************************************************************/
GetPlug(THD * thd,PCONNECT & lxp)1076 static PGLOBAL GetPlug(THD *thd, PCONNECT& lxp)
1077 {
1078   lxp= GetUser(thd, lxp);
1079   return (lxp) ? lxp->g : NULL;
1080 } // end of GetPlug
1081 
1082 /****************************************************************************/
1083 /*  Get the implied table type.                                             */
1084 /****************************************************************************/
GetRealType(PTOS pos)1085 TABTYPE ha_connect::GetRealType(PTOS pos)
1086 {
1087   TABTYPE type= TAB_UNDEF;
1088 
1089   if (pos || (pos= GetTableOptionStruct())) {
1090     type= GetTypeID(pos->type);
1091 
1092     if (type == TAB_UNDEF && !pos->http)
1093       type= pos->srcdef ? TAB_MYSQL : pos->tabname ? TAB_PRX : TAB_DOS;
1094 #if defined(REST_SUPPORT)
1095 		else if (pos->http)
1096 			switch (type) {
1097 				case TAB_JSON:
1098 				case TAB_XML:
1099 				case TAB_CSV:
1100         case TAB_UNDEF:
1101           type = TAB_REST;
1102 					break;
1103 				case TAB_REST:
1104 					type = TAB_NIY;
1105 					break;
1106 				default:
1107 					break;
1108 			}	// endswitch type
1109 #endif   // REST_SUPPORT
1110 
1111   } // endif pos
1112 
1113   return type;
1114 } // end of GetRealType
1115 
1116 /** @brief
1117   The name of the index type that will be used for display.
1118   Don't implement this method unless you really have indexes.
1119  */
index_type(uint inx)1120 const char *ha_connect::index_type(uint inx)
1121 {
1122   switch (GetIndexType(GetRealType())) {
1123     case 1:
1124       if (table_share)
1125         return (GetIndexOption(&table_share->key_info[inx], "Dynamic"))
1126              ? "KINDEX" : "XINDEX";
1127       else
1128         return "XINDEX";
1129 
1130     case 2: return "REMOTE";
1131     case 3: return "VIRTUAL";
1132     } // endswitch
1133 
1134   return "Unknown";
1135 } // end of index_type
1136 
1137 /** @brief
1138   This is a bitmap of flags that indicates how the storage engine
1139   implements indexes. The current index flags are documented in
1140   handler.h. If you do not implement indexes, just return zero here.
1141 
1142     @details
1143   part is the key part to check. First key part is 0.
1144   If all_parts is set, MySQL wants to know the flags for the combined
1145   index, up to and including 'part'.
1146 */
1147 //ong ha_connect::index_flags(uint inx, uint part, bool all_parts) const
index_flags(uint,uint,bool) const1148 ulong ha_connect::index_flags(uint, uint, bool) const
1149 {
1150   ulong       flags= HA_READ_NEXT | HA_READ_RANGE |
1151                      HA_KEYREAD_ONLY | HA_KEY_SCAN_NOT_ROR;
1152   ha_connect *hp= (ha_connect*)this;
1153   PTOS        pos= hp->GetTableOptionStruct();
1154 
1155   if (pos) {
1156     TABTYPE type= hp->GetRealType(pos);
1157 
1158     switch (GetIndexType(type)) {
1159       case 1: flags|= (HA_READ_ORDER | HA_READ_PREV); break;
1160       case 2: flags|= HA_READ_AFTER_KEY;              break;
1161       } // endswitch
1162 
1163     } // endif pos
1164 
1165   return flags;
1166 } // end of index_flags
1167 
1168 /** @brief
1169   This is a list of flags that indicate what functionality the storage
1170   engine implements. The current table flags are documented in handler.h
1171 */
table_flags() const1172 ulonglong ha_connect::table_flags() const
1173 {
1174   ulonglong   flags= HA_CAN_VIRTUAL_COLUMNS | HA_REC_NOT_IN_SEQ |
1175                      HA_NO_AUTO_INCREMENT | HA_NO_PREFIX_CHAR_KEYS |
1176                      HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE |
1177                      HA_PARTIAL_COLUMN_READ | HA_FILE_BASED |
1178 //                   HA_NULL_IN_KEY |    not implemented yet
1179 //                   HA_FAST_KEY_READ |  causes error when sorting (???)
1180                      HA_NO_TRANSACTIONS | HA_DUPLICATE_KEY_NOT_IN_ORDER |
1181                      HA_NO_BLOBS | HA_MUST_USE_TABLE_CONDITION_PUSHDOWN;
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   TABLE_SHARE *s= (table) ? table->s : NULL;
1777   KEY          kp= s->key_info[n];
1778 
1779   return (kp.flags & 1) != 0;
1780 } // end of IsUnique
1781 
1782 /****************************************************************************/
1783 /*  Returns the index description structure used to make the index.         */
1784 /****************************************************************************/
GetIndexInfo(TABLE_SHARE * s)1785 PIXDEF ha_connect::GetIndexInfo(TABLE_SHARE *s)
1786 {
1787   char    *name, *pn;
1788   bool     unique;
1789   PIXDEF   xdp, pxd=NULL, toidx= NULL;
1790   PKPDEF   kpp, pkp;
1791   KEY      kp;
1792   PGLOBAL& g= xp->g;
1793 
1794   if (!s)
1795     s= table->s;
1796 
1797   for (int n= 0; (unsigned)n < s->keynames.count; n++) {
1798     if (trace(1))
1799       htrc("Getting created index %d info\n", n + 1);
1800 
1801     // Find the index to describe
1802     kp= s->key_info[n];
1803 
1804     // Now get index information
1805     pn= (char*)s->keynames.type_names[n];
1806     name= PlugDup(g, pn);
1807     unique= (kp.flags & 1) != 0;
1808     pkp= NULL;
1809 
1810     // Allocate the index description block
1811     xdp= new(g) INDEXDEF(name, unique, n);
1812 
1813     // Get the the key parts info
1814     for (int k= 0; (unsigned)k < kp.user_defined_key_parts; k++) {
1815       pn= (char*)kp.key_part[k].field->field_name.str;
1816       name= PlugDup(g, pn);
1817 
1818       // Allocate the key part description block
1819       kpp= new(g) KPARTDEF(name, k + 1);
1820       kpp->SetKlen(kp.key_part[k].length);
1821 
1822 #if 0             // NIY
1823     // Index on auto increment column can be an XXROW index
1824     if (kp.key_part[k].field->flags & AUTO_INCREMENT_FLAG &&
1825         kp.uder_defined_key_parts == 1) {
1826       char   *type= GetStringOption("Type", "DOS");
1827       TABTYPE typ= GetTypeID(type);
1828 
1829       xdp->SetAuto(IsTypeFixed(typ));
1830       } // endif AUTO_INCREMENT
1831 #endif // 0
1832 
1833       if (pkp)
1834         pkp->SetNext(kpp);
1835       else
1836         xdp->SetToKeyParts(kpp);
1837 
1838       pkp= kpp;
1839       } // endfor k
1840 
1841     xdp->SetNParts(kp.user_defined_key_parts);
1842     xdp->Dynamic= GetIndexOption(&kp, "Dynamic");
1843     xdp->Mapped= GetIndexOption(&kp, "Mapped");
1844 
1845     if (pxd)
1846       pxd->SetNext(xdp);
1847     else
1848       toidx= xdp;
1849 
1850     pxd= xdp;
1851     } // endfor n
1852 
1853   return toidx;
1854 } // end of GetIndexInfo
1855 
1856 /****************************************************************************/
1857 /*  Returns the index description structure used to make the index.         */
1858 /****************************************************************************/
CheckVirtualIndex(TABLE_SHARE * s)1859 bool ha_connect::CheckVirtualIndex(TABLE_SHARE *s)
1860 {
1861 
1862   char    *rid;
1863   KEY      kp;
1864   Field   *fp;
1865   PGLOBAL& g= xp->g;
1866 
1867   if (!s)
1868     s= table->s;
1869 
1870   for (int n= 0; (unsigned)n < s->keynames.count; n++) {
1871     kp= s->key_info[n];
1872 
1873     // Now get index information
1874 
1875     // Get the the key parts info
1876     for (int k= 0; (unsigned)k < kp.user_defined_key_parts; k++) {
1877       fp= kp.key_part[k].field;
1878       rid= (fp->option_struct) ? fp->option_struct->special : NULL;
1879 
1880       if (!rid || (stricmp(rid, "ROWID") && stricmp(rid, "ROWNUM"))) {
1881         strcpy(g->Message, "Invalid virtual index");
1882         return true;
1883         } // endif rowid
1884 
1885       } // endfor k
1886 
1887     } // endfor n
1888 
1889   return false;
1890 } // end of CheckVirtualIndex
1891 
IsPartitioned(void)1892 bool ha_connect::IsPartitioned(void)
1893 {
1894 #ifdef WITH_PARTITION_STORAGE_ENGINE
1895 	if (tshp)
1896     return tshp->partition_info_str_len > 0;
1897   else if (table && table->part_info)
1898     return true;
1899   else
1900 #endif
1901 		return false;
1902 
1903 } // end of IsPartitioned
1904 
GetDBName(PCSZ name)1905 PCSZ ha_connect::GetDBName(PCSZ name)
1906 {
1907   return (name) ? name : table->s->db.str;
1908 } // end of GetDBName
1909 
GetTableName(void)1910 const char *ha_connect::GetTableName(void)
1911 {
1912   const char *path= tshp ? tshp->path.str : table_share->path.str;
1913   const char *name= strrchr(path, slash);
1914   return name ? name + 1 : path;
1915 } // end of GetTableName
1916 
GetPartName(void)1917 char *ha_connect::GetPartName(void)
1918 {
1919   return (IsPartitioned()) ? partname : (char*)GetTableName();
1920 } // end of GetTableName
1921 
1922 #if 0
1923 /****************************************************************************/
1924 /*  Returns the column real or special name length of a field.              */
1925 /****************************************************************************/
1926 int ha_connect::GetColNameLen(Field *fp)
1927 {
1928   int n;
1929   PFOS fop= GetFieldOptionStruct(fp);
1930 
1931   // Now get the column name length
1932   if (fop && fop->special)
1933     n= strlen(fop->special) + 1;
1934   else
1935     n= fp->field_name.length;
1936 
1937   return n;
1938 } // end of GetColNameLen
1939 
1940 /****************************************************************************/
1941 /*  Returns the column real or special name of a field.                     */
1942 /****************************************************************************/
1943 char *ha_connect::GetColName(Field *fp)
1944 {
1945   PFOS fop= GetFieldOptionStruct(fp);
1946 
1947   return (fop && fop->special) ? fop->special : (char*)fp->field_name.str;
1948 } // end of GetColName
1949 
1950 /****************************************************************************/
1951 /*  Adds the column real or special name of a field to a string.            */
1952 /****************************************************************************/
1953 void ha_connect::AddColName(char *cp, Field *fp)
1954 {
1955   PFOS fop= GetFieldOptionStruct(fp);
1956 
1957   // Now add the column name
1958   if (fop && fop->special)
1959     // The prefix * mark the column as "special"
1960     strcat(strcpy(cp, "*"), strupr(fop->special));
1961   else
1962     strcpy(cp, fp->field_name.str);
1963 
1964 } // end of AddColName
1965 #endif // 0
1966 
1967 /***********************************************************************/
1968 /*  This function sets the current database path.                      */
1969 /***********************************************************************/
SetDataPath(PGLOBAL g,PCSZ path)1970 bool ha_connect::SetDataPath(PGLOBAL g, PCSZ path)
1971 {
1972   return (!(datapath= SetPath(g, path)));
1973 } // end of SetDataPath
1974 
1975 /****************************************************************************/
1976 /*  Get the table description block of a CONNECT table.                     */
1977 /****************************************************************************/
GetTDB(PGLOBAL g)1978 PTDB ha_connect::GetTDB(PGLOBAL g)
1979 {
1980   const char *table_name;
1981   PTDB        tp;
1982 
1983   // Double test to be on the safe side
1984   if (!g || !table)
1985     return NULL;
1986 
1987   table_name= GetTableName();
1988 
1989   if (!xp->CheckQuery(valid_query_id) && tdbp
1990                       && !stricmp(tdbp->GetName(), table_name)
1991                       && (tdbp->GetMode() == xmod
1992                        || (tdbp->GetMode() == MODE_READ && xmod == MODE_READX)
1993                        || tdbp->GetAmType() == TYPE_AM_XML)) {
1994     tp= tdbp;
1995     tp->SetMode(xmod);
1996   } else if ((tp= CntGetTDB(g, table_name, xmod, this))) {
1997     valid_query_id= xp->last_query_id;
1998 //  tp->SetMode(xmod);
1999   } else
2000     htrc("GetTDB: %s\n", g->Message);
2001 
2002   return tp;
2003 } // end of GetTDB
2004 
2005 /****************************************************************************/
2006 /*  Open a CONNECT table, restricting column list if cols is true.          */
2007 /****************************************************************************/
OpenTable(PGLOBAL g,bool del)2008 int ha_connect::OpenTable(PGLOBAL g, bool del)
2009 {
2010   bool  rc= false;
2011   char *c1= NULL, *c2=NULL;
2012 
2013   // Double test to be on the safe side
2014   if (!g || !table) {
2015     htrc("OpenTable logical error; g=%p table=%p\n", g, table);
2016     return HA_ERR_INITIALIZATION;
2017     } // endif g
2018 
2019   if (!(tdbp= GetTDB(g)))
2020     return RC_FX;
2021   else if (tdbp->IsReadOnly())
2022     switch (xmod) {
2023       case MODE_WRITE:
2024       case MODE_INSERT:
2025       case MODE_UPDATE:
2026       case MODE_DELETE:
2027         strcpy(g->Message, MSG(READ_ONLY));
2028         return HA_ERR_TABLE_READONLY;
2029       default:
2030         break;
2031       } // endswitch xmode
2032 
2033 	// g->More is 1 when executing commands from triggers
2034   if (!g->More && (xmod != MODE_INSERT
2035 		           || tdbp->GetAmType() == TYPE_AM_MYSQL
2036                || tdbp->GetAmType() == TYPE_AM_ODBC
2037 							 || tdbp->GetAmType() == TYPE_AM_JDBC)) {
2038 		// Get the list of used fields (columns)
2039     char        *p;
2040     unsigned int k1, k2, n1, n2;
2041     Field*      *field;
2042     Field*       fp;
2043     MY_BITMAP   *map= (xmod == MODE_INSERT) ? table->write_set : table->read_set;
2044     MY_BITMAP   *ump= (xmod == MODE_UPDATE) ? table->write_set : NULL;
2045 
2046     k1= k2= 0;
2047     n1= n2= 1;         // 1 is space for final null character
2048 
2049     for (field= table->field; fp= *field; field++) {
2050       if (bitmap_is_set(map, fp->field_index)) {
2051         n1+= (fp->field_name.length + 1);
2052         k1++;
2053         } // endif
2054 
2055       if (ump && bitmap_is_set(ump, fp->field_index)) {
2056         n2+= (fp->field_name.length + 1);
2057         k2++;
2058         } // endif
2059 
2060       } // endfor field
2061 
2062     if (k1) {
2063       p= c1= (char*)PlugSubAlloc(g, NULL, n1);
2064 
2065       for (field= table->field; fp= *field; field++)
2066         if (bitmap_is_set(map, fp->field_index)) {
2067           strcpy(p, fp->field_name.str);
2068           p+= (fp->field_name.length + 1);
2069           } // endif used field
2070 
2071       *p= '\0';          // mark end of list
2072       } // endif k1
2073 
2074     if (k2) {
2075       p= c2= (char*)PlugSubAlloc(g, NULL, n2);
2076 
2077       for (field= table->field; fp= *field; field++)
2078         if (bitmap_is_set(ump, fp->field_index)) {
2079           strcpy(p, fp->field_name.str);
2080 
2081           if (part_id && bitmap_is_set(part_id, fp->field_index)) {
2082             // Trying to update a column used for partitioning
2083             // This cannot be currently done because it may require
2084             // a row to be moved in another partition.
2085             sprintf(g->Message,
2086               "Cannot update column %s because it is used for partitioning",
2087               p);
2088             return HA_ERR_INTERNAL_ERROR;
2089             } // endif part_id
2090 
2091           p+= (strlen(p) + 1);
2092           } // endif used field
2093 
2094       *p= '\0';          // mark end of list
2095       } // endif k2
2096 
2097     } // endif xmod
2098 
2099   // Open the table
2100   if (!(rc= CntOpenTable(g, tdbp, xmod, c1, c2, del, this))) {
2101     istable= true;
2102 //  strmake(tname, table_name, sizeof(tname)-1);
2103 
2104     // We may be in a create index query
2105     if (xmod == MODE_ANY && *tdbp->GetName() != '#') {
2106       // The current indexes
2107       PIXDEF oldpix= GetIndexInfo();
2108       } // endif xmod
2109 
2110   } else
2111     htrc("OpenTable: %s\n", g->Message);
2112 
2113   if (rc) {
2114     tdbp= NULL;
2115     valid_info= false;
2116     } // endif rc
2117 
2118   return (rc) ? HA_ERR_INITIALIZATION : 0;
2119 } // end of OpenTable
2120 
2121 
2122 /****************************************************************************/
2123 /*  CheckColumnList: check that all bitmap columns do exist.                */
2124 /****************************************************************************/
CheckColumnList(PGLOBAL g)2125 bool ha_connect::CheckColumnList(PGLOBAL g)
2126 {
2127   // Check the list of used fields (columns)
2128   bool       brc= false;
2129   PCOL       colp;
2130   Field*    *field;
2131   Field*     fp;
2132   MY_BITMAP *map= table->read_set;
2133 
2134 	try {
2135     for (field= table->field; fp= *field; field++)
2136       if (bitmap_is_set(map, fp->field_index)) {
2137         if (!(colp= tdbp->ColDB(g, (PSZ)fp->field_name.str, 0))) {
2138           sprintf(g->Message, "Column %s not found in %s",
2139                   fp->field_name.str, tdbp->GetName());
2140 					throw 1;
2141 				} // endif colp
2142 
2143         if ((brc= colp->InitValue(g)))
2144 					throw 2;
2145 
2146         colp->AddColUse(U_P);           // For PLG tables
2147         } // endif
2148 
2149 	} catch (int n) {
2150 		if (trace(1))
2151 			htrc("Exception %d: %s\n", n, g->Message);
2152 		brc= true;
2153 	} catch (const char *msg) {
2154 		strcpy(g->Message, msg);
2155 		brc= true;
2156 	} // end catch
2157 
2158   return brc;
2159 } // end of CheckColumnList
2160 
2161 
2162 /****************************************************************************/
2163 /*  IsOpened: returns true if the table is already opened.                  */
2164 /****************************************************************************/
IsOpened(void)2165 bool ha_connect::IsOpened(void)
2166 {
2167   return (!xp->CheckQuery(valid_query_id) && tdbp
2168                                           && tdbp->GetUse() == USE_OPEN);
2169 } // end of IsOpened
2170 
2171 
2172 /****************************************************************************/
2173 /*  Close a CONNECT table.                                                  */
2174 /****************************************************************************/
CloseTable(PGLOBAL g)2175 int ha_connect::CloseTable(PGLOBAL g)
2176 {
2177   int rc= CntCloseTable(g, tdbp, nox, abort);
2178   tdbp= NULL;
2179   sdvalin1= sdvalin2= sdvalin3= sdvalin4= NULL;
2180   sdvalout=NULL;
2181   valid_info= false;
2182   indexing= -1;
2183   nox= true;
2184   abort= false;
2185   return rc;
2186 } // end of CloseTable
2187 
2188 
2189 /***********************************************************************/
2190 /*  Make a pseudo record from current row values. Specific to MySQL.   */
2191 /***********************************************************************/
MakeRecord(char * buf)2192 int ha_connect::MakeRecord(char *buf)
2193 {
2194 	PCSZ           fmt;
2195   char          *p, val[32];
2196   int            rc= 0;
2197   Field*        *field;
2198   Field         *fp;
2199   CHARSET_INFO  *charset= tdbp->data_charset();
2200 //MY_BITMAP      readmap;
2201   MY_BITMAP     *map;
2202   PVAL           value;
2203   PCOL           colp= NULL;
2204   DBUG_ENTER("ha_connect::MakeRecord");
2205 
2206   if (trace(2))
2207     htrc("Maps: read=%08X write=%08X vcol=%08X defr=%08X defw=%08X\n",
2208             *table->read_set->bitmap, *table->write_set->bitmap,
2209             (table->vcol_set) ? *table->vcol_set->bitmap : 0,
2210             *table->def_read_set.bitmap, *table->def_write_set.bitmap);
2211 
2212   // Avoid asserts in field::store() for columns that are not updated
2213   MY_BITMAP *org_bitmap= dbug_tmp_use_all_columns(table, &table->write_set);
2214 
2215   // This is for variable_length rows
2216   memset(buf, 0, table->s->null_bytes);
2217 
2218   // When sorting read_set selects all columns, so we use def_read_set
2219   map= (MY_BITMAP *)&table->def_read_set;
2220 
2221   // Make the pseudo record from field values
2222   for (field= table->field; *field && !rc; field++) {
2223     fp= *field;
2224 
2225     if (fp->vcol_info && !fp->stored_in_db)
2226       continue;            // This is a virtual column
2227 
2228     if (bitmap_is_set(map, fp->field_index) || alter) {
2229       // This is a used field, fill the buffer with value
2230       for (colp= tdbp->GetColumns(); colp; colp= colp->GetNext())
2231         if ((!mrr || colp->GetKcol()) &&
2232             !stricmp(colp->GetName(), fp->field_name.str))
2233           break;
2234 
2235       if (!colp) {
2236         if (mrr)
2237           continue;
2238 
2239         htrc("Column %s not found\n", fp->field_name.str);
2240         dbug_tmp_restore_column_map(&table->write_set, org_bitmap);
2241         DBUG_RETURN(HA_ERR_WRONG_IN_RECORD);
2242         } // endif colp
2243 
2244       value= colp->GetValue();
2245       p= NULL;
2246 
2247       // All this was better optimized
2248       if (!value->IsNull()) {
2249         switch (value->GetType()) {
2250           case TYPE_DATE:
2251             if (!sdvalout)
2252               sdvalout= AllocateValue(xp->g, TYPE_STRING, 20);
2253 
2254             switch (fp->type()) {
2255               case MYSQL_TYPE_DATE:
2256                 fmt= "%Y-%m-%d";
2257                 break;
2258               case MYSQL_TYPE_TIME:
2259                 fmt= "%H:%M:%S";
2260                 break;
2261               case MYSQL_TYPE_YEAR:
2262                 fmt= "%Y";
2263                 break;
2264               default:
2265                 fmt= "%Y-%m-%d %H:%M:%S";
2266                 break;
2267               } // endswitch type
2268 
2269             // Get date in the format required by MySQL fields
2270             value->FormatValue(sdvalout, fmt);
2271             p= sdvalout->GetCharValue();
2272             rc= fp->store(p, strlen(p), charset, CHECK_FIELD_WARN);
2273             break;
2274           case TYPE_STRING:
2275           case TYPE_DECIM:
2276             p= value->GetCharString(val);
2277             charset= tdbp->data_charset();
2278             rc= fp->store(p, strlen(p), charset, CHECK_FIELD_WARN);
2279             break;
2280 					case TYPE_BIN:
2281 						p= value->GetCharValue();
2282 						charset= &my_charset_bin;
2283 						rc= fp->store(p, value->GetSize(), charset, CHECK_FIELD_WARN);
2284 						break;
2285           case TYPE_DOUBLE:
2286             rc= fp->store(value->GetFloatValue());
2287             break;
2288           default:
2289             rc= fp->store(value->GetBigintValue(), value->IsUnsigned());
2290             break;
2291           } // endswitch Type
2292 
2293         // Store functions returns 1 on overflow and -1 on fatal error
2294         if (rc > 0) {
2295           char buf[256];
2296           THD *thd= ha_thd();
2297 
2298           sprintf(buf, "Out of range value %.140s for column '%s' at row %ld",
2299             value->GetCharString(val),
2300             fp->field_name.str,
2301             thd->get_stmt_da()->current_row_for_warning());
2302 
2303           push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, buf);
2304           DBUG_PRINT("MakeRecord", ("%s", buf));
2305           rc= 0;
2306         } else if (rc < 0)
2307           rc= HA_ERR_WRONG_IN_RECORD;
2308 
2309         fp->set_notnull();
2310       } else
2311         fp->set_null();
2312 
2313       } // endif bitmap
2314 
2315     } // endfor field
2316 
2317   // This is sometimes required for partition tables because the buf
2318   // can be different from the table->record[0] buffer
2319   if (buf != (char*)table->record[0])
2320     memcpy(buf, table->record[0], table->s->stored_rec_length);
2321 
2322   // This is copied from ha_tina and is necessary to avoid asserts
2323   dbug_tmp_restore_column_map(&table->write_set, org_bitmap);
2324   DBUG_RETURN(rc);
2325 } // end of MakeRecord
2326 
2327 
2328 /***********************************************************************/
2329 /*  Set row values from a MySQL pseudo record. Specific to MySQL.      */
2330 /***********************************************************************/
ScanRecord(PGLOBAL g,const uchar *)2331 int ha_connect::ScanRecord(PGLOBAL g, const uchar *)
2332 {
2333   char    attr_buffer[1024];
2334   char    data_buffer[1024];
2335   PCSZ    fmt;
2336   int     rc= 0;
2337   PCOL    colp;
2338   PVAL    value, sdvalin;
2339   Field  *fp;
2340 //PTDBASE tp= (PTDBASE)tdbp;
2341   String  attribute(attr_buffer, sizeof(attr_buffer),
2342                     table->s->table_charset);
2343   MY_BITMAP *bmap= dbug_tmp_use_all_columns(table, &table->read_set);
2344   const CHARSET_INFO *charset= tdbp->data_charset();
2345   String  data_charset_value(data_buffer, sizeof(data_buffer),  charset);
2346 
2347   // Scan the pseudo record for field values and set column values
2348   for (Field **field=table->field ; *field ; field++) {
2349     fp= *field;
2350 
2351     if ((fp->vcol_info && !fp->stored_in_db) ||
2352          fp->option_struct->special)
2353       continue;            // Is a virtual column possible here ???
2354 
2355     if ((xmod == MODE_INSERT && tdbp->GetAmType() != TYPE_AM_MYSQL
2356                              && tdbp->GetAmType() != TYPE_AM_ODBC
2357 														 && tdbp->GetAmType() != TYPE_AM_JDBC) ||
2358 														 bitmap_is_set(table->write_set, fp->field_index)) {
2359       for (colp= tdbp->GetSetCols(); colp; colp= colp->GetNext())
2360         if (!stricmp(colp->GetName(), fp->field_name.str))
2361           break;
2362 
2363       if (!colp) {
2364         htrc("Column %s not found\n", fp->field_name.str);
2365         rc= HA_ERR_WRONG_IN_RECORD;
2366         goto err;
2367       } else
2368         value= colp->GetValue();
2369 
2370       // This is a used field, fill the value from the row buffer
2371       // All this could be better optimized
2372       if (fp->is_null()) {
2373         if (colp->IsNullable())
2374           value->SetNull(true);
2375 
2376         value->Reset();
2377       } else switch (value->GetType()) {
2378         case TYPE_DOUBLE:
2379           value->SetValue(fp->val_real());
2380           break;
2381         case TYPE_DATE:
2382           // Get date in the format produced by MySQL fields
2383           switch (fp->type()) {
2384             case MYSQL_TYPE_DATE:
2385               if (!sdvalin2) {
2386                 sdvalin2= (DTVAL*)AllocateValue(xp->g, TYPE_DATE, 19);
2387                 fmt= "YYYY-MM-DD";
2388                 ((DTVAL*)sdvalin2)->SetFormat(g, fmt, strlen(fmt));
2389                 } // endif sdvalin1
2390 
2391               sdvalin= sdvalin2;
2392               break;
2393             case MYSQL_TYPE_TIME:
2394               if (!sdvalin3) {
2395                 sdvalin3= (DTVAL*)AllocateValue(xp->g, TYPE_DATE, 19);
2396                 fmt= "hh:mm:ss";
2397                 ((DTVAL*)sdvalin3)->SetFormat(g, fmt, strlen(fmt));
2398                 } // endif sdvalin1
2399 
2400               sdvalin= sdvalin3;
2401               break;
2402             case MYSQL_TYPE_YEAR:
2403               if (!sdvalin4) {
2404                 sdvalin4= (DTVAL*)AllocateValue(xp->g, TYPE_DATE, 19);
2405                 fmt= "YYYY";
2406                 ((DTVAL*)sdvalin4)->SetFormat(g, fmt, strlen(fmt));
2407                 } // endif sdvalin1
2408 
2409               sdvalin= sdvalin4;
2410               break;
2411             default:
2412               if (!sdvalin1) {
2413                 sdvalin1= (DTVAL*)AllocateValue(xp->g, TYPE_DATE, 19);
2414                 fmt= "YYYY-MM-DD hh:mm:ss";
2415                 ((DTVAL*)sdvalin1)->SetFormat(g, fmt, strlen(fmt));
2416                 } // endif sdvalin1
2417 
2418               sdvalin= sdvalin1;
2419             } // endswitch type
2420 
2421           sdvalin->SetNullable(colp->IsNullable());
2422           fp->val_str(&attribute);
2423           sdvalin->SetValue_psz(attribute.c_ptr_safe());
2424           value->SetValue_pval(sdvalin);
2425           break;
2426         default:
2427           fp->val_str(&attribute);
2428 
2429           if (charset != &my_charset_bin) {
2430             // Convert from SQL field charset to DATA_CHARSET
2431             uint cnv_errors;
2432 
2433             data_charset_value.copy(attribute.ptr(), attribute.length(),
2434                                     attribute.charset(), charset, &cnv_errors);
2435             value->SetValue_psz(data_charset_value.c_ptr_safe());
2436           } else
2437             value->SetValue_psz(attribute.c_ptr_safe());
2438 
2439           break;
2440         } // endswitch Type
2441 
2442 #ifdef NEWCHANGE
2443     } else if (xmod == MODE_UPDATE) {
2444       PCOL cp;
2445 
2446       for (cp= tdbp->GetColumns(); cp; cp= cp->GetNext())
2447         if (!stricmp(colp->GetName(), cp->GetName()))
2448           break;
2449 
2450       if (!cp) {
2451         rc= HA_ERR_WRONG_IN_RECORD;
2452         goto err;
2453         } // endif cp
2454 
2455       value->SetValue_pval(cp->GetValue());
2456     } else // mode Insert
2457       value->Reset();
2458 #else
2459     } // endif bitmap_is_set
2460 #endif
2461 
2462     } // endfor field
2463 
2464  err:
2465   dbug_tmp_restore_column_map(&table->read_set, bmap);
2466   return rc;
2467 } // end of ScanRecord
2468 
2469 
2470 /***********************************************************************/
2471 /*  Check change in index column. Specific to MySQL.                   */
2472 /*  Should be elaborated to check for real changes.                    */
2473 /***********************************************************************/
CheckRecord(PGLOBAL g,const uchar *,const uchar * newbuf)2474 int ha_connect::CheckRecord(PGLOBAL g, const uchar *, const uchar *newbuf)
2475 {
2476 	return ScanRecord(g, newbuf);
2477 } // end of dummy CheckRecord
2478 
2479 
2480 /***********************************************************************/
2481 /*  Return true if this field is used in current indexing.             */
2482 /***********************************************************************/
IsIndexed(Field * fp)2483 bool ha_connect::IsIndexed(Field *fp)
2484 {
2485 	if (active_index < MAX_KEY) {
2486 		KEY_PART_INFO *kpart;
2487 		KEY           *kfp= &table->key_info[active_index];
2488 		uint           rem= kfp->user_defined_key_parts;
2489 
2490 		for (kpart= kfp->key_part; rem; rem--, kpart++)
2491 			if (kpart->field == fp)
2492 				return true;
2493 
2494 	} // endif active_index
2495 
2496 	return false;
2497 } // end of IsIndexed
2498 
2499 
2500 /***********************************************************************/
2501 /*  Return the where clause for remote indexed read.                   */
2502 /***********************************************************************/
MakeKeyWhere(PGLOBAL g,PSTRG qry,OPVAL vop,char q,const key_range * kr)2503 bool ha_connect::MakeKeyWhere(PGLOBAL g, PSTRG qry, OPVAL vop, char q,
2504 	                            const key_range *kr)
2505 {
2506 	const uchar     *ptr;
2507 //uint             i, rem, len, klen, stlen;
2508 	uint             i, rem, len, stlen;
2509 	bool             nq, both, oom;
2510 	OPVAL            op;
2511 	Field           *fp;
2512 	const key_range *ranges[2];
2513 	MY_BITMAP    *old_map;
2514 	KEY             *kfp;
2515   KEY_PART_INFO   *kpart;
2516 
2517   if (active_index == MAX_KEY)
2518     return false;
2519 
2520 	ranges[0]= kr;
2521 	ranges[1]= (end_range && !eq_range) ? &save_end_range : NULL;
2522 
2523 	if (!ranges[0] && !ranges[1]) {
2524 		strcpy(g->Message, "MakeKeyWhere: No key");
2525 	  return true;
2526 	}	else
2527 		both= ranges[0] && ranges[1];
2528 
2529 	kfp= &table->key_info[active_index];
2530 	old_map= dbug_tmp_use_all_columns(table, &table->write_set);
2531 
2532 	for (i= 0; i <= 1; i++) {
2533 		if (ranges[i] == NULL)
2534 			continue;
2535 
2536 		if (both && i > 0)
2537 			qry->Append(") AND (");
2538 		else
2539 			qry->Append(" WHERE (");
2540 
2541 //	klen= len= ranges[i]->length;
2542 		len= ranges[i]->length;
2543 		rem= kfp->user_defined_key_parts;
2544 		ptr= ranges[i]->key;
2545 
2546 		for (kpart= kfp->key_part; rem; rem--, kpart++) {
2547 			fp= kpart->field;
2548 			stlen= kpart->store_length;
2549 			nq= fp->str_needs_quotes();
2550 
2551 			if (kpart != kfp->key_part)
2552 				qry->Append(" AND ");
2553 
2554 			if (q) {
2555 				qry->Append(q);
2556 				qry->Append((PSZ)fp->field_name.str);
2557 				qry->Append(q);
2558 			}	else
2559 				qry->Append((PSZ)fp->field_name.str);
2560 
2561 			switch (ranges[i]->flag) {
2562 			case HA_READ_KEY_EXACT:
2563 //			op= (stlen >= len || !nq || fp->result_type() != STRING_RESULT)
2564 //				? OP_EQ : OP_LIKE;
2565 				op= OP_EQ;
2566 				break;
2567 			case HA_READ_AFTER_KEY:
2568 				op= (stlen >= len || i > 0) ? (i > 0 ? OP_LE : OP_GT) : OP_GE;
2569 				break;
2570 			case HA_READ_KEY_OR_NEXT:
2571 				op= OP_GE;
2572 				break;
2573 			case HA_READ_BEFORE_KEY:
2574 				op= (stlen >= len) ? OP_LT : OP_LE;
2575 				break;
2576 			case HA_READ_KEY_OR_PREV:
2577 				op= OP_LE;
2578 				break;
2579 			default:
2580 				sprintf(g->Message, "cannot handle flag %d", ranges[i]->flag);
2581 				goto err;
2582 			}	// endswitch flag
2583 
2584 			qry->Append((PSZ)GetValStr(op, false));
2585 
2586 			if (nq)
2587 				qry->Append('\'');
2588 
2589 			if (kpart->key_part_flag & HA_VAR_LENGTH_PART) {
2590 				String varchar;
2591 				uint   var_length= uint2korr(ptr);
2592 
2593 				varchar.set_quick((char*)ptr + HA_KEY_BLOB_LENGTH,
2594 					var_length, &my_charset_bin);
2595 				qry->Append(varchar.ptr(), varchar.length(), nq);
2596 			}	else {
2597 				char   strbuff[MAX_FIELD_WIDTH];
2598 				String str(strbuff, sizeof(strbuff), kpart->field->charset()), *res;
2599 
2600 				res= fp->val_str(&str, ptr);
2601 				qry->Append(res->ptr(), res->length(), nq);
2602 			} // endif flag
2603 
2604 			if (nq)
2605 				qry->Append('\'');
2606 
2607 			if (stlen >= len)
2608 				break;
2609 
2610 			len-= stlen;
2611 
2612 			/* For nullable columns, null-byte is already skipped before, that is
2613 			ptr was incremented by 1. Since store_length still counts null-byte,
2614 			we need to subtract 1 from store_length. */
2615 			ptr+= stlen - MY_TEST(kpart->null_bit);
2616 		} // endfor kpart
2617 
2618 		} // endfor i
2619 
2620 	qry->Append(')');
2621 
2622   if ((oom= qry->IsTruncated()))
2623     strcpy(g->Message, "Out of memory");
2624 
2625 	dbug_tmp_restore_column_map(&table->write_set, old_map);
2626 	return oom;
2627 
2628 err:
2629 	dbug_tmp_restore_column_map(&table->write_set, old_map);
2630 	return true;
2631 } // end of MakeKeyWhere
2632 
2633 
2634 /***********************************************************************/
2635 /*  Return the string representing an operator.                        */
2636 /***********************************************************************/
GetValStr(OPVAL vop,bool neg)2637 const char *ha_connect::GetValStr(OPVAL vop, bool neg)
2638 {
2639   const char *val;
2640 
2641   switch (vop) {
2642     case OP_EQ:
2643       val= "= ";
2644       break;
2645     case OP_NE:
2646       val= " <> ";
2647       break;
2648     case OP_GT:
2649       val= " > ";
2650       break;
2651     case OP_GE:
2652       val= " >= ";
2653       break;
2654     case OP_LT:
2655       val= " < ";
2656       break;
2657     case OP_LE:
2658       val= " <= ";
2659       break;
2660     case OP_IN:
2661       val= (neg) ? " NOT IN (" : " IN (";
2662       break;
2663     case OP_NULL:
2664       val= (neg) ? " IS NOT NULL" : " IS NULL";
2665       break;
2666     case OP_LIKE:
2667       val= (neg) ? " NOT LIKE " : " LIKE ";
2668       break;
2669     case OP_XX:
2670       val= (neg) ? " NOT BETWEEN " : " BETWEEN ";
2671       break;
2672     case OP_EXIST:
2673       val= (neg) ? " NOT EXISTS " : " EXISTS ";
2674       break;
2675     case OP_AND:
2676       val= " AND ";
2677       break;
2678     case OP_OR:
2679       val= " OR ";
2680       break;
2681     case OP_NOT:
2682       val= " NOT ";
2683       break;
2684     case OP_CNC:
2685       val= " || ";
2686       break;
2687     case OP_ADD:
2688       val= " + ";
2689       break;
2690     case OP_SUB:
2691       val= " - ";
2692       break;
2693     case OP_MULT:
2694       val= " * ";
2695       break;
2696     case OP_DIV:
2697       val= " / ";
2698       break;
2699     default:
2700       val= " ? ";
2701       break;
2702     } /* endswitch */
2703 
2704   return val;
2705 } // end of GetValStr
2706 
2707 #if 0
2708 /***********************************************************************/
2709 /*  Check the WHERE condition and return a CONNECT filter.             */
2710 /***********************************************************************/
2711 PFIL ha_connect::CheckFilter(PGLOBAL g)
2712 {
2713   return CondFilter(g, (Item *)pushed_cond);
2714 } // end of CheckFilter
2715 #endif // 0
2716 
2717 /***********************************************************************/
2718 /*  Check the WHERE condition and return a CONNECT filter.             */
2719 /***********************************************************************/
CondFilter(PGLOBAL g,Item * cond)2720 PFIL ha_connect::CondFilter(PGLOBAL g, Item *cond)
2721 {
2722   unsigned int i;
2723   bool  ismul= false;
2724   OPVAL vop= OP_XX;
2725   PFIL  filp= NULL;
2726 
2727   if (!cond)
2728     return NULL;
2729 
2730   if (trace(1))
2731     htrc("Cond type=%d\n", cond->type());
2732 
2733   if (cond->type() == COND::COND_ITEM) {
2734     PFIL       fp;
2735     Item_cond *cond_item= (Item_cond *)cond;
2736 
2737     if (trace(1))
2738       htrc("Cond: Ftype=%d name=%s\n", cond_item->functype(),
2739                                        cond_item->func_name());
2740 
2741     switch (cond_item->functype()) {
2742       case Item_func::COND_AND_FUNC: vop= OP_AND; break;
2743       case Item_func::COND_OR_FUNC:  vop= OP_OR;  break;
2744       default: return NULL;
2745       } // endswitch functype
2746 
2747     List<Item>* arglist= cond_item->argument_list();
2748     List_iterator<Item> li(*arglist);
2749     Item *subitem;
2750 
2751     for (i= 0; i < arglist->elements; i++)
2752       if ((subitem= li++)) {
2753         if (!(fp= CondFilter(g, subitem))) {
2754           if (vop == OP_OR)
2755             return NULL;
2756         } else
2757           filp= (filp) ? MakeFilter(g, filp, vop, fp) : fp;
2758 
2759       } else
2760         return NULL;
2761 
2762   } else if (cond->type() == COND::FUNC_ITEM) {
2763     unsigned int i;
2764     bool       iscol, neg= FALSE;
2765     PCOL       colp[2]= {NULL,NULL};
2766     PPARM      pfirst= NULL, pprec= NULL;
2767     POPER      pop;
2768     Item_func *condf= (Item_func *)cond;
2769     Item*     *args= condf->arguments();
2770 
2771     if (trace(1))
2772       htrc("Func type=%d argnum=%d\n", condf->functype(),
2773                                        condf->argument_count());
2774 
2775     switch (condf->functype()) {
2776       case Item_func::EQUAL_FUNC:
2777       case Item_func::EQ_FUNC: vop= OP_EQ;  break;
2778       case Item_func::NE_FUNC: vop= OP_NE;  break;
2779       case Item_func::LT_FUNC: vop= OP_LT;  break;
2780       case Item_func::LE_FUNC: vop= OP_LE;  break;
2781       case Item_func::GE_FUNC: vop= OP_GE;  break;
2782       case Item_func::GT_FUNC: vop= OP_GT;  break;
2783       case Item_func::IN_FUNC: vop= OP_IN;	/* fall through */
2784       case Item_func::BETWEEN:
2785         ismul= true;
2786         neg= ((Item_func_opt_neg *)condf)->negated;
2787         break;
2788       default: return NULL;
2789       } // endswitch functype
2790 
2791     pop= (POPER)PlugSubAlloc(g, NULL, sizeof(OPER));
2792     pop->Name= NULL;
2793     pop->Val=vop;
2794     pop->Mod= 0;
2795 
2796     if (condf->argument_count() < 2)
2797       return NULL;
2798 
2799     for (i= 0; i < condf->argument_count(); i++) {
2800       if (trace(1))
2801         htrc("Argtype(%d)=%d\n", i, args[i]->type());
2802 
2803       if (i >= 2 && !ismul) {
2804         if (trace(1))
2805           htrc("Unexpected arg for vop=%d\n", vop);
2806 
2807         continue;
2808         } // endif i
2809 
2810       if ((iscol= args[i]->type() == COND::FIELD_ITEM)) {
2811         Item_field *pField= (Item_field *)args[i];
2812 
2813         // IN and BETWEEN clauses should be col VOP list
2814         if (i && ismul)
2815           return NULL;
2816 
2817         if (pField->field->table != table ||
2818             !(colp[i]= tdbp->ColDB(g, (PSZ)pField->field->field_name.str, 0)))
2819           return NULL;  // Column does not belong to this table
2820 
2821 				// These types are not yet implemented (buggy)
2822 				switch (pField->field->type()) {
2823 				case MYSQL_TYPE_TIMESTAMP:
2824 				case MYSQL_TYPE_DATE:
2825 				case MYSQL_TYPE_TIME:
2826 				case MYSQL_TYPE_DATETIME:
2827 				case MYSQL_TYPE_YEAR:
2828 				case MYSQL_TYPE_NEWDATE:
2829 					return NULL;
2830 				default:
2831 					break;
2832 				} // endswitch type
2833 
2834         if (trace(1)) {
2835           htrc("Field index=%d\n", pField->field->field_index);
2836           htrc("Field name=%s\n", pField->field->field_name.str);
2837           } // endif trace
2838 
2839       } else {
2840         char    buff[256];
2841         String *res, tmp(buff, sizeof(buff), &my_charset_bin);
2842         Item_basic_constant *pval= (Item_basic_constant *)args[i];
2843         PPARM pp= (PPARM)PlugSubAlloc(g, NULL, sizeof(PARM));
2844 
2845         // IN and BETWEEN clauses should be col VOP list
2846         if (!i && (ismul))
2847           return NULL;
2848 
2849 				switch (args[i]->real_type()) {
2850           case COND::STRING_ITEM:
2851 						res= pval->val_str(&tmp);
2852 						pp->Value= PlugSubAllocStr(g, NULL, res->ptr(), res->length());
2853             pp->Type= (pp->Value) ? TYPE_STRING : TYPE_ERROR;
2854             break;
2855           case COND::INT_ITEM:
2856             pp->Type= TYPE_INT;
2857             pp->Value= PlugSubAlloc(g, NULL, sizeof(int));
2858             *((int*)pp->Value)= (int)pval->val_int();
2859             break;
2860           case COND::DATE_ITEM:
2861             pp->Type= TYPE_DATE;
2862             pp->Value= PlugSubAlloc(g, NULL, sizeof(int));
2863             *((int*)pp->Value)= (int)pval->val_int_from_date();
2864             break;
2865           case COND::REAL_ITEM:
2866             pp->Type= TYPE_DOUBLE;
2867             pp->Value= PlugSubAlloc(g, NULL, sizeof(double));
2868             *((double*)pp->Value)= pval->val_real();
2869             break;
2870           case COND::DECIMAL_ITEM:
2871             pp->Type= TYPE_DOUBLE;
2872             pp->Value= PlugSubAlloc(g, NULL, sizeof(double));
2873             *((double*)pp->Value)= pval->val_real_from_decimal();
2874             break;
2875           case COND::CACHE_ITEM:    // Possible ???
2876           case COND::NULL_ITEM:     // TODO: handle this
2877           default:
2878             return NULL;
2879           } // endswitch type
2880 
2881 				if (trace(1))
2882           htrc("Value type=%hd\n", pp->Type);
2883 
2884         // Append the value to the argument list
2885         if (pprec)
2886           pprec->Next= pp;
2887         else
2888           pfirst= pp;
2889 
2890         pp->Domain= i;
2891         pp->Next= NULL;
2892         pprec= pp;
2893       } // endif type
2894 
2895       } // endfor i
2896 
2897     filp= MakeFilter(g, colp, pop, pfirst, neg);
2898   } else {
2899     if (trace(1))
2900       htrc("Unsupported condition\n");
2901 
2902     return NULL;
2903   } // endif's type
2904 
2905   return filp;
2906 } // end of CondFilter
2907 
2908 /***********************************************************************/
2909 /*  Check the WHERE condition and return a MYSQL/ODBC/JDBC/WQL filter. */
2910 /***********************************************************************/
CheckCond(PGLOBAL g,PCFIL filp,const Item * cond)2911 PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond)
2912 {
2913 	AMT   tty= filp->Type;
2914   char *body= filp->Body;
2915 	char *havg= filp->Having;
2916 	unsigned int i;
2917   bool  ismul= false, x= (tty == TYPE_AM_MYX || tty == TYPE_AM_XDBC);
2918   bool  nonul= ((tty == TYPE_AM_ODBC || tty == TYPE_AM_JDBC) &&
2919 		 (tdbp->GetMode() == MODE_INSERT || tdbp->GetMode() == MODE_DELETE));
2920   OPVAL vop= OP_XX;
2921 
2922   if (!cond)
2923     return NULL;
2924 
2925   if (trace(1))
2926     htrc("Cond type=%d\n", cond->type());
2927 
2928   if (cond->type() == COND::COND_ITEM) {
2929     char      *pb0, *pb1, *pb2, *ph0= 0, *ph1= 0, *ph2= 0;
2930 		bool       bb= false, bh= false;
2931     Item_cond *cond_item= (Item_cond *)cond;
2932 
2933     if (x)
2934       return NULL;
2935 		else
2936 			pb0= pb1= pb2= ph0= ph1= ph2= NULL;
2937 
2938     if (trace(1))
2939       htrc("Cond: Ftype=%d name=%s\n", cond_item->functype(),
2940                                        cond_item->func_name());
2941 
2942     switch (cond_item->functype()) {
2943       case Item_func::COND_AND_FUNC: vop= OP_AND; break;
2944       case Item_func::COND_OR_FUNC:  vop= OP_OR;  break;
2945       default: return NULL;
2946       } // endswitch functype
2947 
2948     List<Item>* arglist= cond_item->argument_list();
2949     List_iterator<Item> li(*arglist);
2950     const Item *subitem;
2951 
2952     pb0= pb1= body + strlen(body);
2953     strcpy(pb0, "(");
2954     pb2= pb1 + 1;
2955 
2956 		if (havg) {
2957 			ph0= ph1= havg + strlen(havg);
2958 			strcpy(ph0, "(");
2959 			ph2= ph1 + 1;
2960 		} // endif havg
2961 
2962     for (i= 0; i < arglist->elements; i++)
2963       if ((subitem= li++)) {
2964         if (!CheckCond(g, filp, subitem)) {
2965           if (vop == OP_OR || nonul)
2966             return NULL;
2967 					else {
2968 						*pb2= 0;
2969 						if (havg) *ph2= 0;
2970 					}	// endelse
2971 
2972         } else {
2973 					if (filp->Bd) {
2974 						pb1= pb2 + strlen(pb2);
2975 						strcpy(pb1, GetValStr(vop, false));
2976 						pb2= pb1 + strlen(pb1);
2977 					} // endif Bd
2978 
2979 					if (filp->Hv) {
2980 						ph1= ph2 + strlen(ph2);
2981 						strcpy(ph1, GetValStr(vop, false));
2982 						ph2= ph1 + strlen(ph1);
2983 					} // endif Hv
2984 
2985         } // endif CheckCond
2986 
2987 				bb |= filp->Bd;
2988 				bh |= filp->Hv;
2989 				filp->Bd= filp->Hv= false;
2990       } else
2991         return NULL;
2992 
2993     if (bb)	{
2994       strcpy(pb1, ")");
2995 			filp->Bd= bb;
2996 		} else
2997 			*pb0= 0;
2998 
2999 		if (havg) {
3000 			if (bb && bh && vop == OP_OR) {
3001 				// Cannot or'ed a where clause with a having clause
3002 				bb= bh= 0;
3003 				*pb0= 0;
3004 				*ph0= 0;
3005 			} else if (bh)	{
3006 				strcpy(ph1, ")");
3007 				filp->Hv= bh;
3008 			} else
3009 				*ph0= 0;
3010 
3011 		} // endif havg
3012 
3013 		if (!bb && !bh)
3014 			return NULL;
3015 
3016   } else if (cond->type() == COND::FUNC_ITEM) {
3017     unsigned int i;
3018     bool       iscol, ishav= false, neg= false;
3019     Item_func *condf= (Item_func *)cond;
3020     Item*     *args= condf->arguments();
3021 
3022 		filp->Bd= filp->Hv= false;
3023 
3024     if (trace(1))
3025       htrc("Func type=%d argnum=%d\n", condf->functype(),
3026                                        condf->argument_count());
3027 
3028     switch (condf->functype()) {
3029       case Item_func::EQUAL_FUNC:
3030 			case Item_func::EQ_FUNC:     vop= OP_EQ;   break;
3031 			case Item_func::NE_FUNC:     vop= OP_NE;   break;
3032 			case Item_func::LT_FUNC:     vop= OP_LT;   break;
3033 			case Item_func::LE_FUNC:     vop= OP_LE;   break;
3034 			case Item_func::GE_FUNC:     vop= OP_GE;   break;
3035 			case Item_func::GT_FUNC:     vop= OP_GT;   break;
3036 #if MYSQL_VERSION_ID > 100200
3037 			case Item_func::LIKE_FUNC:
3038 				vop = OP_LIKE;
3039 			  neg= ((Item_func_like*)condf)->negated;
3040 			  break;
3041 #endif // VERSION_ID > 100200
3042 			case Item_func::ISNOTNULL_FUNC:
3043 				neg= true;
3044 				// fall through
3045 			case Item_func::ISNULL_FUNC: vop= OP_NULL; break;
3046 			case Item_func::IN_FUNC:     vop= OP_IN; /* fall through */
3047       case Item_func::BETWEEN:
3048         ismul= true;
3049         neg= ((Item_func_opt_neg *)condf)->negated;
3050         break;
3051       default: return NULL;
3052       } // endswitch functype
3053 
3054     if (condf->argument_count() < 2)
3055       return NULL;
3056     else if (ismul && tty == TYPE_AM_WMI)
3057       return NULL;        // Not supported by WQL
3058 
3059     if (x && (neg || !(vop == OP_EQ || vop == OP_IN || vop == OP_NULL)))
3060       return NULL;
3061 
3062     for (i= 0; i < condf->argument_count(); i++) {
3063       if (trace(1))
3064         htrc("Argtype(%d)=%d\n", i, args[i]->type());
3065 
3066       if (i >= 2 && !ismul) {
3067         if (trace(1))
3068           htrc("Unexpected arg for vop=%d\n", vop);
3069 
3070         continue;
3071         } // endif i
3072 
3073       if ((iscol= args[i]->type() == COND::FIELD_ITEM)) {
3074         const char *fnm;
3075         ha_field_option_struct *fop;
3076         Item_field *pField= (Item_field *)args[i];
3077 
3078 				// IN and BETWEEN clauses should be col VOP list
3079 				if (i && (x || ismul))
3080           return NULL;	// IN and BETWEEN clauses should be col VOP list
3081 				else if (pField->field->table != table)
3082 					return NULL;  // Field does not belong to this table
3083 				else if (tty != TYPE_AM_WMI && IsIndexed(pField->field))
3084 					return NULL;  // Will be handled by ReadKey
3085         else
3086           fop= GetFieldOptionStruct(pField->field);
3087 
3088         if (fop && fop->special) {
3089           if (tty == TYPE_AM_TBL && !stricmp(fop->special, "TABID"))
3090             fnm= "TABID";
3091           else if (tty == TYPE_AM_PLG)
3092             fnm= fop->special;
3093           else
3094             return NULL;
3095 
3096 				} else if (tty == TYPE_AM_TBL) {
3097 					return NULL;
3098 				} else {
3099 					bool h;
3100 
3101 					fnm= filp->Chk(pField->field->field_name.str, &h);
3102 
3103 					if (h && i && !ishav)
3104 						return NULL;			// Having should be	col VOP arg
3105 					else
3106 						ishav= h;
3107 
3108 				}	// endif's
3109 
3110         if (trace(1)) {
3111           htrc("Field index=%d\n", pField->field->field_index);
3112           htrc("Field name=%s\n", pField->field->field_name.str);
3113           htrc("Field type=%d\n", pField->field->type());
3114           htrc("Field_type=%d\n", args[i]->field_type());
3115           } // endif trace
3116 
3117         strcat((ishav ? havg : body), fnm);
3118       } else if (args[i]->type() == COND::FUNC_ITEM) {
3119         if (tty == TYPE_AM_MYSQL) {
3120           if (!CheckCond(g, filp, args[i]))
3121             return NULL;
3122 
3123         } else
3124           return NULL;
3125 
3126       } else {
3127         char    buff[256];
3128         String *res, tmp(buff, sizeof(buff), &my_charset_bin);
3129         Item_basic_constant *pval= (Item_basic_constant *)args[i];
3130         Item::Type type= args[i]->real_type();
3131 
3132         switch (type) {
3133           case COND::STRING_ITEM:
3134           case COND::INT_ITEM:
3135           case COND::REAL_ITEM:
3136           case COND::NULL_ITEM:
3137           case COND::DECIMAL_ITEM:
3138           case COND::DATE_ITEM:
3139           case COND::CACHE_ITEM:
3140             break;
3141           default:
3142             return NULL;
3143           } // endswitch type
3144 
3145         if ((res= pval->val_str(&tmp)) == NULL)
3146           return NULL;                      // To be clarified
3147 
3148         if (trace(1))
3149           htrc("Value=%.*s\n", res->length(), res->ptr());
3150 
3151         // IN and BETWEEN clauses should be col VOP list
3152         if (!i && (x || ismul))
3153           return NULL;
3154 
3155         if (!x) {
3156 					const char *p;
3157 					char *s= (ishav) ? havg : body;
3158 					uint	j, k, n;
3159 
3160           // Append the value to the filter
3161           switch (args[i]->field_type()) {
3162             case MYSQL_TYPE_TIMESTAMP:
3163             case MYSQL_TYPE_DATETIME:
3164               if (tty == TYPE_AM_ODBC) {
3165                 strcat(s, "{ts '");
3166                 strncat(s, res->ptr(), res->length());
3167 
3168                 if (res->length() < 19)
3169                   strcat(s, &"1970-01-01 00:00:00"[res->length()]);
3170 
3171                 strcat(s, "'}");
3172                 break;
3173                 } // endif ODBC
3174 
3175 							// fall through
3176             case MYSQL_TYPE_DATE:
3177               if (tty == TYPE_AM_ODBC) {
3178                 strcat(s, "{d '");
3179                 strcat(strncat(s, res->ptr(), res->length()), "'}");
3180                 break;
3181                 } // endif ODBC
3182 
3183             case MYSQL_TYPE_TIME:
3184               if (tty == TYPE_AM_ODBC) {
3185                 strcat(s, "{t '");
3186                 strcat(strncat(s, res->ptr(), res->length()), "'}");
3187                 break;
3188                 } // endif ODBC
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(uchar * buf)3678 int ha_connect::write_row(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     PDBUSER dup= PlgGetUser(g);
4378 
4379     if (xmod == MODE_ANY || xmod == MODE_ALTER) {
4380       // Pure info, not a query
4381       pure= true;
4382       xp->CheckCleanup(xmod == MODE_ANY && valid_query_id == 0);
4383       } // endif xmod
4384 
4385     // This is necessary for getting file length
4386 		if (table) {
4387 			if (SetDataPath(g, table->s->db.str)) {
4388 				my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
4389 				DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
4390 			}	// endif SetDataPath
4391 
4392 		} else
4393       DBUG_RETURN(HA_ERR_INTERNAL_ERROR);       // Should never happen
4394 
4395 		if (!(tdbp= GetTDB(g))) {
4396 			my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
4397 			DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
4398 		} // endif tdbp
4399 
4400     valid_info= false;
4401     } // endif tdbp
4402 
4403   if (!valid_info) {
4404     valid_info= CntInfo(g, tdbp, &xinfo);
4405 
4406     if (((signed)xinfo.records) < 0)
4407       DBUG_RETURN(HA_ERR_INITIALIZATION);  // Error in Cardinality
4408 
4409     } // endif valid_info
4410 
4411   if (flag & HA_STATUS_VARIABLE) {
4412     stats.records= xinfo.records;
4413     stats.deleted= 0;
4414     stats.data_file_length= xinfo.data_file_length;
4415     stats.index_file_length= 0;
4416     stats.delete_length= 0;
4417     stats.check_time= 0;
4418     stats.mean_rec_length= xinfo.mean_rec_length;
4419     } // endif HA_STATUS_VARIABLE
4420 
4421   if (flag & HA_STATUS_CONST) {
4422     // This is imported from the previous handler and must be reconsidered
4423     stats.max_data_file_length= 4294967295LL;
4424     stats.max_index_file_length= 4398046510080LL;
4425     stats.create_time= 0;
4426     data_file_name= xinfo.data_file_name;
4427     index_file_name= NULL;
4428 //  sortkey= (uint) - 1;           // Table is not sorted
4429     ref_length= sizeof(int);      // Pointer size to row
4430     table->s->db_options_in_use= 03;
4431     stats.block_size= 1024;
4432     table->s->keys_in_use.set_prefix(table->s->keys);
4433     table->s->keys_for_keyread= table->s->keys_in_use;
4434 //  table->s->keys_for_keyread.subtract(table->s->read_only_keys);
4435     table->s->db_record_offset= 0;
4436     } // endif HA_STATUS_CONST
4437 
4438   if (flag & HA_STATUS_ERRKEY) {
4439     errkey= 0;
4440     } // endif HA_STATUS_ERRKEY
4441 
4442   if (flag & HA_STATUS_TIME)
4443     stats.update_time= 0;
4444 
4445   if (flag & HA_STATUS_AUTO)
4446     stats.auto_increment_value= 1;
4447 
4448   if (tdbp && pure)
4449     CloseTable(g);        // Not used anymore
4450 
4451   DBUG_RETURN(0);
4452 } // end of info
4453 
4454 
4455 /**
4456   @brief
4457   extra() is called whenever the server wishes to send a hint to
4458   the storage engine. The myisam engine implements the most hints.
4459   ha_innodb.cc has the most exhaustive list of these hints.
4460 
4461   @note
4462   This is not yet implemented for CONNECT.
4463 
4464   @see
4465   ha_innodb.cc
4466 */
extra(enum ha_extra_function)4467 int ha_connect::extra(enum ha_extra_function /*operation*/)
4468 {
4469   DBUG_ENTER("ha_connect::extra");
4470   DBUG_RETURN(0);
4471 } // end of extra
4472 
4473 
4474 /**
4475   @brief
4476   Used to delete all rows in a table, including cases of truncate and cases where
4477   the optimizer realizes that all rows will be removed as a result of an SQL statement.
4478 
4479     @details
4480   Called from item_sum.cc by Item_func_group_concat::clear(),
4481   Item_sum_count_distinct::clear(), and Item_func_group_concat::clear().
4482   Called from sql_delete.cc by mysql_delete().
4483   Called from sql_select.cc by JOIN::reinit().
4484   Called from sql_union.cc by st_select_lex_unit::exec().
4485 
4486     @see
4487   Item_func_group_concat::clear(), Item_sum_count_distinct::clear() and
4488   Item_func_group_concat::clear() in item_sum.cc;
4489   mysql_delete() in sql_delete.cc;
4490   JOIN::reinit() in sql_select.cc and
4491   st_select_lex_unit::exec() in sql_union.cc.
4492 */
delete_all_rows()4493 int ha_connect::delete_all_rows()
4494 {
4495   int     rc= 0;
4496   PGLOBAL g= xp->g;
4497   DBUG_ENTER("ha_connect::delete_all_rows");
4498 
4499   if (tdbp && tdbp->GetUse() == USE_OPEN &&
4500       tdbp->GetAmType() != TYPE_AM_XML &&
4501       tdbp->GetFtype() != RECFM_NAF)
4502     // Close and reopen the table so it will be deleted
4503     rc= CloseTable(g);
4504 
4505   if (!(rc= OpenTable(g))) {
4506     if (CntDeleteRow(g, tdbp, true)) {
4507       htrc("%s\n", g->Message);
4508       rc= HA_ERR_INTERNAL_ERROR;
4509     } else
4510       nox= false;
4511 
4512     } // endif rc
4513 
4514   DBUG_RETURN(rc);
4515 } // end of delete_all_rows
4516 
4517 
checkPrivileges(THD * thd,TABTYPE type,PTOS options,const char * db,TABLE * table,bool quick)4518 static bool checkPrivileges(THD *thd, TABTYPE type, PTOS options,
4519                             const char *db, TABLE *table, bool quick)
4520 {
4521   switch (type) {
4522     case TAB_UNDEF:
4523 //  case TAB_CATLG:
4524     case TAB_PLG:
4525     case TAB_JCT:
4526     case TAB_DMY:
4527     case TAB_NIY:
4528       my_printf_error(ER_UNKNOWN_ERROR,
4529                       "Unsupported table type %s", MYF(0), options->type);
4530       return true;
4531 
4532     case TAB_DOS:
4533     case TAB_FIX:
4534     case TAB_BIN:
4535     case TAB_CSV:
4536     case TAB_FMT:
4537     case TAB_DBF:
4538     case TAB_XML:
4539     case TAB_INI:
4540     case TAB_VEC:
4541 		case TAB_REST:
4542     case TAB_JSON:
4543 #if defined(BSON_SUPPORT)
4544     case TAB_BSON:
4545 #endif   // BSON_SUPPORT
4546       if (options->filename && *options->filename) {
4547 				if (!quick) {
4548 					char path[FN_REFLEN], dbpath[FN_REFLEN];
4549 
4550  					strcpy(dbpath, mysql_real_data_home);
4551 
4552 					if (db)
4553 #if defined(_WIN32)
4554 						strcat(strcat(dbpath, db), "\\");
4555 #else   // !_WIN32
4556 						strcat(strcat(dbpath, db), "/");
4557 #endif  // !_WIN32
4558 
4559 					(void)fn_format(path, options->filename, dbpath, "",
4560 						MY_RELATIVE_PATH | MY_UNPACK_FILENAME);
4561 
4562 					if (!is_secure_file_path(path)) {
4563 						my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--secure-file-priv");
4564 						return true;
4565 					} // endif path
4566 
4567 				}	// endif !quick
4568 
4569 			} else
4570         return false;
4571 
4572 			// Fall through
4573 		case TAB_MYSQL:
4574 		case TAB_DIR:
4575 		case TAB_ZIP:
4576 		case TAB_OEM:
4577       if (table && table->pos_in_table_list) { // if SELECT
4578 #if MYSQL_VERSION_ID > 100200
4579 				Switch_to_definer_security_ctx backup_ctx(thd, table->pos_in_table_list);
4580 #endif // VERSION_ID > 100200
4581         return check_global_access(thd, FILE_ACL);
4582       }	else
4583         return check_global_access(thd, FILE_ACL);
4584     case TAB_ODBC:
4585 		case TAB_JDBC:
4586 		case TAB_MONGO:
4587     case TAB_MAC:
4588     case TAB_WMI:
4589 			return false;
4590     case TAB_TBL:
4591     case TAB_XCL:
4592     case TAB_PRX:
4593     case TAB_OCCUR:
4594     case TAB_PIVOT:
4595     case TAB_VIR:
4596     default:
4597 			// This is temporary until a solution is found
4598 			return false;
4599     } // endswitch type
4600 
4601   my_printf_error(ER_UNKNOWN_ERROR, "check_privileges failed", MYF(0));
4602   return true;
4603 } // end of checkPrivileges
4604 
4605 // Check whether the user has required (file) privileges
check_privileges(THD * thd,PTOS options,const char * dbn,bool quick)4606 bool ha_connect::check_privileges(THD *thd, PTOS options, const char *dbn,
4607 				  bool quick)
4608 {
4609   const char *db= (dbn && *dbn) ? dbn : NULL;
4610   TABTYPE     type=GetRealType(options);
4611 
4612   return checkPrivileges(thd, type, options, db, table, quick);
4613 } // end of check_privileges
4614 
4615 // Check that two indexes are equivalent
IsSameIndex(PIXDEF xp1,PIXDEF xp2)4616 bool ha_connect::IsSameIndex(PIXDEF xp1, PIXDEF xp2)
4617 {
4618   bool   b= true;
4619   PKPDEF kp1, kp2;
4620 
4621   if (stricmp(xp1->Name, xp2->Name))
4622     b= false;
4623   else if (xp1->Nparts  != xp2->Nparts  ||
4624            xp1->MaxSame != xp2->MaxSame ||
4625            xp1->Unique  != xp2->Unique)
4626     b= false;
4627   else for (kp1= xp1->ToKeyParts, kp2= xp2->ToKeyParts;
4628             b && (kp1 || kp2);
4629             kp1= kp1->Next, kp2= kp2->Next)
4630     if (!kp1 || !kp2)
4631       b= false;
4632     else if (stricmp(kp1->Name, kp2->Name))
4633       b= false;
4634     else if (kp1->Klen != kp2->Klen)
4635       b= false;
4636 
4637   return b;
4638 } // end of IsSameIndex
4639 
CheckMode(PGLOBAL g,THD * thd,MODE newmode,bool * chk,bool * cras)4640 MODE ha_connect::CheckMode(PGLOBAL g, THD *thd,
4641 	                         MODE newmode, bool *chk, bool *cras)
4642 {
4643 #if defined(DEVELOPMENT)
4644 	if (true) {
4645 #else
4646   if (trace(65)) {
4647 #endif
4648     LEX_STRING *query_string= thd_query_string(thd);
4649     htrc("%p check_mode: cmdtype=%d\n", this, thd_sql_command(thd));
4650     htrc("Cmd=%.*s\n", (int) query_string->length, query_string->str);
4651     } // endif trace
4652 
4653   // Next code is temporarily replaced until sql_command is set
4654   stop= false;
4655 
4656   if (newmode == MODE_WRITE) {
4657     switch (thd_sql_command(thd)) {
4658       case SQLCOM_LOCK_TABLES:
4659         locked= 2; // fall through
4660       case SQLCOM_CREATE_TABLE:
4661       case SQLCOM_INSERT:
4662       case SQLCOM_LOAD:
4663       case SQLCOM_INSERT_SELECT:
4664         newmode= MODE_INSERT;
4665         break;
4666 //    case SQLCOM_REPLACE:
4667 //    case SQLCOM_REPLACE_SELECT:
4668 //      newmode= MODE_UPDATE;               // To be checked
4669 //      break;
4670 			case SQLCOM_DELETE_MULTI:
4671 				*cras= true;
4672 			case SQLCOM_DELETE:
4673       case SQLCOM_TRUNCATE:
4674         newmode= MODE_DELETE;
4675         break;
4676       case SQLCOM_UPDATE_MULTI:
4677 				*cras= true;
4678 			case SQLCOM_UPDATE:
4679 				newmode= MODE_UPDATE;
4680         break;
4681       case SQLCOM_SELECT:
4682       case SQLCOM_OPTIMIZE:
4683         newmode= MODE_READ;
4684         break;
4685       case SQLCOM_FLUSH:
4686         locked= 0;
4687       case SQLCOM_DROP_TABLE:
4688       case SQLCOM_RENAME_TABLE:
4689         newmode= MODE_ANY;
4690         break;
4691       case SQLCOM_CREATE_VIEW:
4692       case SQLCOM_DROP_VIEW:
4693         newmode= MODE_ANY;
4694         break;
4695       case SQLCOM_ALTER_TABLE:
4696         newmode= MODE_ALTER;
4697         break;
4698       case SQLCOM_DROP_INDEX:
4699       case SQLCOM_CREATE_INDEX:
4700 //      if (!IsPartitioned()) {
4701           newmode= MODE_ANY;
4702           break;
4703 //        } // endif partitioned
4704 			case SQLCOM_REPAIR: // TODO implement it
4705 				newmode= MODE_UPDATE;
4706 				break;
4707 			default:
4708         htrc("Unsupported sql_command=%d\n", thd_sql_command(thd));
4709         strcpy(g->Message, "CONNECT Unsupported command");
4710         my_message(ER_NOT_ALLOWED_COMMAND, g->Message, MYF(0));
4711         newmode= MODE_ERROR;
4712         break;
4713       } // endswitch newmode
4714 
4715   } else if (newmode == MODE_READ) {
4716     switch (thd_sql_command(thd)) {
4717       case SQLCOM_CREATE_TABLE:
4718         *chk= true;
4719 				break;
4720 			case SQLCOM_UPDATE_MULTI:
4721 			case SQLCOM_DELETE_MULTI:
4722 				*cras= true;
4723       case SQLCOM_INSERT:
4724       case SQLCOM_LOAD:
4725       case SQLCOM_INSERT_SELECT:
4726 //    case SQLCOM_REPLACE:
4727 //    case SQLCOM_REPLACE_SELECT:
4728       case SQLCOM_DELETE:
4729       case SQLCOM_TRUNCATE:
4730       case SQLCOM_UPDATE:
4731       case SQLCOM_SELECT:
4732       case SQLCOM_OPTIMIZE:
4733       case SQLCOM_SET_OPTION:
4734         break;
4735       case SQLCOM_LOCK_TABLES:
4736         locked= 1;
4737         break;
4738       case SQLCOM_DROP_TABLE:
4739       case SQLCOM_RENAME_TABLE:
4740         newmode= MODE_ANY;
4741         break;
4742       case SQLCOM_CREATE_VIEW:
4743       case SQLCOM_DROP_VIEW:
4744 			case SQLCOM_CREATE_TRIGGER:
4745 			case SQLCOM_DROP_TRIGGER:
4746 				newmode= MODE_ANY;
4747         break;
4748       case SQLCOM_ALTER_TABLE:
4749         *chk= true;
4750         newmode= MODE_ALTER;
4751         break;
4752       case SQLCOM_DROP_INDEX:
4753       case SQLCOM_CREATE_INDEX:
4754 //      if (!IsPartitioned()) {
4755           *chk= true;
4756           newmode= MODE_ANY;
4757           break;
4758 //        } // endif partitioned
4759 
4760 			case SQLCOM_CHECK:   // TODO implement it
4761 			case SQLCOM_ANALYZE: // TODO implement it
4762 			case SQLCOM_END:	   // Met in procedures: IF(EXISTS(SELECT...
4763 				newmode= MODE_READ;
4764         break;
4765       default:
4766         htrc("Unsupported sql_command=%d\n", thd_sql_command(thd));
4767         strcpy(g->Message, "CONNECT Unsupported command");
4768         my_message(ER_NOT_ALLOWED_COMMAND, g->Message, MYF(0));
4769         newmode= MODE_ERROR;
4770         break;
4771       } // endswitch newmode
4772 
4773   } // endif's newmode
4774 
4775   if (trace(1))
4776     htrc("New mode=%d\n", newmode);
4777 
4778   return newmode;
4779 } // end of check_mode
4780 
4781 int ha_connect::start_stmt(THD *thd, thr_lock_type lock_type)
4782 {
4783   int     rc= 0;
4784   bool    chk=false, cras= false;
4785   MODE    newmode;
4786   PGLOBAL g= GetPlug(thd, xp);
4787   DBUG_ENTER("ha_connect::start_stmt");
4788 
4789   if (check_privileges(thd, GetTableOptionStruct(), table->s->db.str, true))
4790     DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
4791 
4792   // Action will depend on lock_type
4793   switch (lock_type) {
4794     case TL_WRITE_ALLOW_WRITE:
4795     case TL_WRITE_CONCURRENT_INSERT:
4796     case TL_WRITE_DELAYED:
4797     case TL_WRITE_DEFAULT:
4798     case TL_WRITE_LOW_PRIORITY:
4799     case TL_WRITE:
4800     case TL_WRITE_ONLY:
4801       newmode= MODE_WRITE;
4802       break;
4803     case TL_READ:
4804     case TL_READ_WITH_SHARED_LOCKS:
4805     case TL_READ_HIGH_PRIORITY:
4806     case TL_READ_NO_INSERT:
4807     case TL_READ_DEFAULT:
4808       newmode= MODE_READ;
4809       break;
4810     case TL_UNLOCK:
4811     default:
4812       newmode= MODE_ANY;
4813       break;
4814     } // endswitch mode
4815 
4816 	if (newmode == MODE_ANY) {
4817 		if (CloseTable(g)) {
4818 			// Make error a warning to avoid crash
4819 			push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
4820 			rc= 0;
4821 		} // endif Close
4822 
4823 		locked= 0;
4824 		xmod= MODE_ANY;              // For info commands
4825 		DBUG_RETURN(rc);
4826 	} // endif MODE_ANY
4827 
4828 	newmode= CheckMode(g, thd, newmode, &chk, &cras);
4829 
4830 	if (newmode == MODE_ERROR)
4831 		DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
4832 
4833 	DBUG_RETURN(check_stmt(g, newmode, cras));
4834 } // end of start_stmt
4835 
4836 /**
4837   @brief
4838   This create a lock on the table. If you are implementing a storage engine
4839   that can handle transacations look at ha_berkely.cc to see how you will
4840   want to go about doing this. Otherwise you should consider calling flock()
4841   here. Hint: Read the section "locking functions for mysql" in lock.cc to understand
4842   this.
4843 
4844     @details
4845   Called from lock.cc by lock_external() and unlock_external(). Also called
4846   from sql_table.cc by copy_data_between_tables().
4847 
4848     @note
4849   Following what we did in the MySQL XDB handler, we use this call to actually
4850   physically open the table. This could be reconsider when finalizing this handler
4851   design, which means we have a better understanding of what MariaDB does.
4852 
4853     @see
4854   lock.cc by lock_external() and unlock_external() in lock.cc;
4855   the section "locking functions for mysql" in lock.cc;
4856   copy_data_between_tables() in sql_table.cc.
4857 
4858 */
4859 int ha_connect::external_lock(THD *thd, int lock_type)
4860 {
4861   int     rc= 0;
4862   bool    xcheck=false, cras= false;
4863   MODE    newmode;
4864   PTOS    options= GetTableOptionStruct();
4865   PGLOBAL g= GetPlug(thd, xp);
4866   DBUG_ENTER("ha_connect::external_lock");
4867 
4868   DBUG_ASSERT(thd == current_thd);
4869 
4870   if (trace(1))
4871     htrc("external_lock: this=%p thd=%p xp=%p g=%p lock_type=%d\n",
4872             this, thd, xp, g, lock_type);
4873 
4874   if (!g)
4875     DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
4876 
4877   // Action will depend on lock_type
4878   switch (lock_type) {
4879     case F_WRLCK:
4880       newmode= MODE_WRITE;
4881       break;
4882     case F_RDLCK:
4883       newmode= MODE_READ;
4884       break;
4885     case F_UNLCK:
4886     default:
4887       newmode= MODE_ANY;
4888       break;
4889     } // endswitch mode
4890 
4891   if (newmode == MODE_ANY) {
4892     int sqlcom= thd_sql_command(thd);
4893 
4894     // This is unlocking, do it by closing the table
4895     if (xp->CheckQueryID() && sqlcom != SQLCOM_UNLOCK_TABLES
4896                            && sqlcom != SQLCOM_LOCK_TABLES
4897                            && sqlcom != SQLCOM_FLUSH
4898                            && sqlcom != SQLCOM_BEGIN
4899                            && sqlcom != SQLCOM_DROP_TABLE) {
4900       sprintf(g->Message, "external_lock: unexpected command %d", sqlcom);
4901       push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
4902       DBUG_RETURN(0);
4903     } else if (g->Xchk) {
4904       if (!tdbp) {
4905 				if (!(tdbp= GetTDB(g))) {
4906 //        DBUG_RETURN(HA_ERR_INTERNAL_ERROR);  causes assert error
4907 					push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
4908 					DBUG_RETURN(0);
4909 				} else if (!tdbp->GetDef()->Indexable()) {
4910           sprintf(g->Message, "external_lock: Table %s is not indexable", tdbp->GetName());
4911 //        DBUG_RETURN(HA_ERR_INTERNAL_ERROR);  causes assert error
4912           push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
4913           DBUG_RETURN(0);
4914         } else if (tdbp->GetDef()->Indexable() == 1) {
4915           bool    oldsep= ((PCHK)g->Xchk)->oldsep;
4916           bool    newsep= ((PCHK)g->Xchk)->newsep;
4917           PTDBDOS tdp= (PTDBDOS)tdbp;
4918 
4919           PDOSDEF ddp= (PDOSDEF)tdp->GetDef();
4920           PIXDEF  xp, xp1, xp2, drp=NULL, adp= NULL;
4921           PIXDEF  oldpix= ((PCHK)g->Xchk)->oldpix;
4922           PIXDEF  newpix= ((PCHK)g->Xchk)->newpix;
4923           PIXDEF *xlst, *xprc;
4924 
4925           ddp->SetIndx(oldpix);
4926 
4927           if (oldsep != newsep) {
4928             // All indexes have to be remade
4929             ddp->DeleteIndexFile(g, NULL);
4930             oldpix= NULL;
4931             ddp->SetIndx(NULL);
4932             SetBooleanOption("Sepindex", newsep);
4933           } else if (newsep) {
4934             // Make the list of dropped indexes
4935             xlst= &drp; xprc= &oldpix;
4936 
4937             for (xp2= oldpix; xp2; xp2= xp) {
4938               for (xp1= newpix; xp1; xp1= xp1->Next)
4939                 if (IsSameIndex(xp1, xp2))
4940                   break;        // Index not to drop
4941 
4942               xp= xp2->GetNext();
4943 
4944               if (!xp1) {
4945                 *xlst= xp2;
4946                 *xprc= xp;
4947                 *(xlst= &xp2->Next)= NULL;
4948               } else
4949                 xprc= &xp2->Next;
4950 
4951               } // endfor xp2
4952 
4953             if (drp) {
4954               // Here we erase the index files
4955               ddp->DeleteIndexFile(g, drp);
4956               } // endif xp1
4957 
4958           } else if (oldpix) {
4959             // TODO: optimize the case of just adding new indexes
4960             if (!newpix)
4961               ddp->DeleteIndexFile(g, NULL);
4962 
4963             oldpix= NULL;     // To remake all indexes
4964             ddp->SetIndx(NULL);
4965           } // endif sepindex
4966 
4967           // Make the list of new created indexes
4968           xlst= &adp; xprc= &newpix;
4969 
4970           for (xp1= newpix; xp1; xp1= xp) {
4971             for (xp2= oldpix; xp2; xp2= xp2->Next)
4972               if (IsSameIndex(xp1, xp2))
4973                 break;        // Index already made
4974 
4975             xp= xp1->Next;
4976 
4977             if (!xp2) {
4978               *xlst= xp1;
4979               *xprc= xp;
4980               *(xlst= &xp1->Next)= NULL;
4981             } else
4982               xprc= &xp1->Next;
4983 
4984             } // endfor xp1
4985 
4986           if (adp)
4987             // Here we do make the new indexes
4988             if (tdp->MakeIndex(g, adp, true) == RC_FX) {
4989               // Make it a warning to avoid crash
4990               //push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
4991               //                  0, g->Message);
4992               //rc= 0;
4993 							my_message(ER_TOO_MANY_KEYS, g->Message, MYF(0));
4994 							rc= HA_ERR_INDEX_CORRUPT;
4995 						} // endif MakeIndex
4996 
4997         } else if (tdbp->GetDef()->Indexable() == 3) {
4998           if (CheckVirtualIndex(NULL)) {
4999             // Make it a warning to avoid crash
5000             push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
5001                               0, g->Message);
5002             rc= 0;
5003             } // endif Check
5004 
5005         } // endif indexable
5006 
5007         } // endif Tdbp
5008 
5009       } // endelse Xchk
5010 
5011     if (CloseTable(g)) {
5012       // This is an error while builing index
5013       // Make it a warning to avoid crash
5014       push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
5015       rc= 0;
5016 		} // endif Close
5017 
5018     locked= 0;
5019     xmod= MODE_ANY;              // For info commands
5020     DBUG_RETURN(rc);
5021 	} else if (check_privileges(thd, options, table->s->db.str)) {
5022 		strcpy(g->Message, "This operation requires the FILE privilege");
5023 		htrc("%s\n", g->Message);
5024 		DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
5025 	} // endif check_privileges
5026 
5027 
5028   DBUG_ASSERT(table && table->s);
5029 
5030   // Table mode depends on the query type
5031   newmode= CheckMode(g, thd, newmode, &xcheck, &cras);
5032 
5033   if (newmode == MODE_ERROR)
5034     DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
5035 
5036 	DBUG_RETURN(check_stmt(g, newmode, cras));
5037 } // end of external_lock
5038 
5039 
5040 int ha_connect::check_stmt(PGLOBAL g, MODE newmode, bool cras)
5041 {
5042 	int rc= 0;
5043 	DBUG_ENTER("ha_connect::check_stmt");
5044 
5045 	// If this is the start of a new query, cleanup the previous one
5046   if (xp->CheckCleanup()) {
5047     tdbp= NULL;
5048     valid_info= false;
5049 	} // endif CheckCleanup
5050 
5051   if (cras)
5052     g->Createas= true;  // To tell external tables of a multi-table command
5053 
5054 	if (trace(1))
5055 		htrc("Calling CntCheckDB db=%s cras=%d\n", GetDBName(NULL), cras);
5056 
5057   // Set or reset the good database environment
5058   if (CntCheckDB(g, this, GetDBName(NULL))) {
5059 		htrc("%p check_stmt: %s\n", this, g->Message);
5060 		rc= HA_ERR_INTERNAL_ERROR;
5061   // This can NOT be called without open called first, but
5062   // the table can have been closed since then
5063   } else if (!tdbp || xp->CheckQuery(valid_query_id) || xmod != newmode) {
5064     if (tdbp) {
5065       // If this is called by a later query, the table may have
5066       // been already closed and the tdbp is not valid anymore.
5067       if (xp->last_query_id == valid_query_id)
5068         rc= CloseTable(g);
5069       else
5070         tdbp= NULL;
5071 
5072       } // endif tdbp
5073 
5074     xmod= newmode;
5075 
5076     // Delay open until used fields are known
5077   } // endif tdbp
5078 
5079   if (trace(1))
5080 		htrc("check_stmt: rc=%d\n", rc);
5081 
5082   DBUG_RETURN(rc);
5083 } // end of check_stmt
5084 
5085 
5086 /**
5087   @brief
5088   The idea with handler::store_lock() is: The statement decides which locks
5089   should be needed for the table. For updates/deletes/inserts we get WRITE
5090   locks, for SELECT... we get read locks.
5091 
5092     @details
5093   Before adding the lock into the table lock handler (see thr_lock.c),
5094   mysqld calls store lock with the requested locks. Store lock can now
5095   modify a write lock to a read lock (or some other lock), ignore the
5096   lock (if we don't want to use MySQL table locks at all), or add locks
5097   for many tables (like we do when we are using a MERGE handler).
5098 
5099   Berkeley DB, for example, changes all WRITE locks to TL_WRITE_ALLOW_WRITE
5100   (which signals that we are doing WRITES, but are still allowing other
5101   readers and writers).
5102 
5103   When releasing locks, store_lock() is also called. In this case one
5104   usually doesn't have to do anything.
5105 
5106   In some exceptional cases MySQL may send a request for a TL_IGNORE;
5107   This means that we are requesting the same lock as last time and this
5108   should also be ignored. (This may happen when someone does a flush
5109   table when we have opened a part of the tables, in which case mysqld
5110   closes and reopens the tables and tries to get the same locks at last
5111   time). In the future we will probably try to remove this.
5112 
5113   Called from lock.cc by get_lock_data().
5114 
5115     @note
5116   In this method one should NEVER rely on table->in_use, it may, in fact,
5117   refer to a different thread! (this happens if get_lock_data() is called
5118   from mysql_lock_abort_for_thread() function)
5119 
5120     @see
5121   get_lock_data() in lock.cc
5122 */
5123 THR_LOCK_DATA **ha_connect::store_lock(THD *,
5124                                        THR_LOCK_DATA **to,
5125                                        enum thr_lock_type lock_type)
5126 {
5127   if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
5128     lock.type=lock_type;
5129   *to++= &lock;
5130   return to;
5131 }
5132 
5133 
5134 /**
5135   Searches for a pointer to the last occurrence of  the
5136   character c in the string src.
5137   Returns true on failure, false on success.
5138 */
5139 static bool
5140 strnrchr(LEX_CSTRING *ls, const char *src, size_t length, int c)
5141 {
5142   const char *srcend, *s;
5143   for (s= srcend= src + length; s > src; s--)
5144   {
5145     if (s[-1] == c)
5146     {
5147       ls->str= s;
5148       ls->length= srcend - s;
5149       return false;
5150     }
5151   }
5152   return true;
5153 }
5154 
5155 
5156 /**
5157   Split filename into database and table name.
5158 */
5159 static bool
5160 filename_to_dbname_and_tablename(const char *filename,
5161                                  char *database, size_t database_size,
5162                                  char *table, size_t table_size)
5163 {
5164   LEX_CSTRING d, t;
5165   size_t length= strlen(filename);
5166 
5167   /* Find filename - the rightmost directory part */
5168   if (strnrchr(&t, filename, length, slash) || t.length + 1 > table_size)
5169     return true;
5170   memcpy(table, t.str, t.length);
5171   table[t.length]= '\0';
5172   if (!(length-= t.length))
5173     return true;
5174 
5175   length--; /* Skip slash */
5176 
5177   /* Find database name - the second rightmost directory part */
5178   if (strnrchr(&d, filename, length, slash) || d.length + 1 > database_size)
5179     return true;
5180   memcpy(database, d.str, d.length);
5181   database[d.length]= '\0';
5182   return false;
5183 } // end of filename_to_dbname_and_tablename
5184 
5185 /**
5186   @brief
5187   Used to delete or rename a table. By the time delete_table() has been
5188   called all opened references to this table will have been closed
5189   (and your globally shared references released) ===> too bad!!!
5190   The variable name will just be the name of the table.
5191   You will need to remove or rename any files you have created at
5192   this point.
5193 
5194     @details
5195   If you do not implement this, the default delete_table() is called from
5196   handler.cc and it will delete all files with the file extensions returned
5197   by bas_ext().
5198 
5199   Called from handler.cc by delete_table and ha_create_table(). Only used
5200   during create if the table_flag HA_DROP_BEFORE_CREATE was specified for
5201   the storage engine.
5202 
5203     @see
5204   delete_table and ha_create_table() in handler.cc
5205 */
5206 int ha_connect::delete_or_rename_table(const char *name, const char *to)
5207 {
5208   DBUG_ENTER("ha_connect::delete_or_rename_table");
5209   char db[128], tabname[128];
5210   int  rc= 0;
5211   bool ok= false;
5212   THD *thd= current_thd;
5213   int  sqlcom= thd_sql_command(thd);
5214 
5215   if (trace(1)) {
5216     if (to)
5217       htrc("rename_table: this=%p thd=%p sqlcom=%d from=%s to=%s\n",
5218               this, thd, sqlcom, name, to);
5219     else
5220       htrc("delete_table: this=%p thd=%p sqlcom=%d name=%s\n",
5221               this, thd, sqlcom, name);
5222 
5223     } // endif trace
5224 
5225   if (to && (filename_to_dbname_and_tablename(to, db, sizeof(db),
5226                                              tabname, sizeof(tabname))
5227       || (*tabname == '#' && sqlcom == SQLCOM_CREATE_INDEX)))
5228     DBUG_RETURN(0);
5229 
5230   if (filename_to_dbname_and_tablename(name, db, sizeof(db),
5231                                        tabname, sizeof(tabname))
5232       || (*tabname == '#' && sqlcom == SQLCOM_CREATE_INDEX))
5233     DBUG_RETURN(0);
5234 
5235   // If a temporary file exists, all the tests below were passed
5236   // successfully when making it, so they are not needed anymore
5237   // in particular because they sometimes cause DBUG_ASSERT crash.
5238   // Also, for partitioned tables, no test can be done because when
5239   // this function is called, the .par file is already deleted and
5240   // this causes the open_table_def function to fail.
5241   // Not having any other clues (table and table_share are NULL)
5242   // the only mean we have to test for partitioning is this:
5243   if (*tabname != '#' && !strstr(tabname, "#P#")) {
5244     // We have to retrieve the information about this table options.
5245     ha_table_option_struct *pos;
5246     char         key[MAX_DBKEY_LENGTH];
5247     uint         key_length;
5248     TABLE_SHARE *share;
5249 
5250 //  if ((p= strstr(tabname, "#P#")))   won't work, see above
5251 //    *p= 0;             // Get the main the table name
5252 
5253     key_length= tdc_create_key(key, db, tabname);
5254 
5255     // share contains the option struct that we need
5256     if (!(share= alloc_table_share(db, tabname, key, key_length)))
5257       DBUG_RETURN(rc);
5258 
5259     // Get the share info from the .frm file
5260     Dummy_error_handler error_handler;
5261     thd->push_internal_handler(&error_handler);
5262     bool got_error= open_table_def(thd, share);
5263     thd->pop_internal_handler();
5264     if (!got_error) {
5265       // Now we can work
5266       if ((pos= share->option_struct)) {
5267         if (check_privileges(thd, pos, db))
5268           rc= HA_ERR_INTERNAL_ERROR;         // ???
5269         else
5270           if (IsFileType(GetRealType(pos)) && !pos->filename)
5271             ok= true;
5272 
5273         } // endif pos
5274 
5275       } // endif open_table_def
5276 
5277     free_table_share(share);
5278   } else              // Temporary file
5279     ok= true;
5280 
5281   if (ok) {
5282     // Let the base handler do the job
5283     if (to)
5284       rc= handler::rename_table(name, to);
5285     else if ((rc= handler::delete_table(name)) == ENOENT)
5286       rc= 0;        // No files is not an error for CONNECT
5287 
5288     } // endif ok
5289 
5290   DBUG_RETURN(rc);
5291 } // end of delete_or_rename_table
5292 
5293 int ha_connect::delete_table(const char *name)
5294 {
5295   return delete_or_rename_table(name, NULL);
5296 } // end of delete_table
5297 
5298 int ha_connect::rename_table(const char *from, const char *to)
5299 {
5300   return delete_or_rename_table(from, to);
5301 } // end of rename_table
5302 
5303 /**
5304   @brief
5305   Given a starting key and an ending key, estimate the number of rows that
5306   will exist between the two keys.
5307 
5308   @details
5309   end_key may be empty, in which case determine if start_key matches any rows.
5310 
5311   Called from opt_range.cc by check_quick_keys().
5312 
5313   @see
5314   check_quick_keys() in opt_range.cc
5315 */
5316 ha_rows ha_connect::records_in_range(uint inx, key_range *min_key,
5317                                                key_range *max_key)
5318 {
5319   ha_rows rows;
5320   DBUG_ENTER("ha_connect::records_in_range");
5321 
5322   if (indexing < 0 || inx != active_index)
5323     if (index_init(inx, false))
5324       DBUG_RETURN(HA_POS_ERROR);
5325 
5326   if (trace(1))
5327     htrc("records_in_range: inx=%d indexing=%d\n", inx, indexing);
5328 
5329   if (indexing > 0) {
5330     int          nval;
5331     uint         len[2];
5332     const uchar *key[2];
5333     bool         incl[2];
5334     key_part_map kmap[2];
5335 
5336     key[0]= (min_key) ? min_key->key : NULL;
5337     key[1]= (max_key) ? max_key->key : NULL;
5338     len[0]= (min_key) ? min_key->length : 0;
5339     len[1]= (max_key) ? max_key->length : 0;
5340     incl[0]= (min_key) ? (min_key->flag == HA_READ_KEY_EXACT) : false;
5341     incl[1]= (max_key) ? (max_key->flag == HA_READ_AFTER_KEY) : false;
5342     kmap[0]= (min_key) ? min_key->keypart_map : 0;
5343     kmap[1]= (max_key) ? max_key->keypart_map : 0;
5344 
5345     if ((nval= CntIndexRange(xp->g, tdbp, key, len, incl, kmap)) < 0)
5346       rows= HA_POS_ERROR;
5347     else
5348       rows= (ha_rows)nval;
5349 
5350   } else if (indexing == 0)
5351     rows= 100000000;        // Don't use missing index
5352   else
5353     rows= HA_POS_ERROR;
5354 
5355   if (trace(1))
5356     htrc("records_in_range: rows=%llu\n", rows);
5357 
5358   DBUG_RETURN(rows);
5359 } // end of records_in_range
5360 
5361 // Used to check whether a MYSQL table is created on itself
5362 bool CheckSelf(PGLOBAL g, TABLE_SHARE *s, PCSZ host,
5363 	             PCSZ db, PCSZ tab, PCSZ src, int port)
5364 {
5365   if (src)
5366     return false;
5367   else if (host && stricmp(host, "localhost") && strcmp(host, "127.0.0.1"))
5368     return false;
5369   else if (db && stricmp(db, s->db.str))
5370     return false;
5371   else if (tab && stricmp(tab, s->table_name.str))
5372     return false;
5373   else if (port && port != (signed)GetDefaultPort())
5374     return false;
5375 
5376   strcpy(g->Message, "This MySQL table is defined on itself");
5377   return true;
5378 } // end of CheckSelf
5379 
5380 /**
5381   Convert an ISO-8859-1 column name to UTF-8
5382 */
5383 static char *encode(PGLOBAL g, const char *cnm)
5384   {
5385   char  *buf= (char*)PlugSubAlloc(g, NULL, strlen(cnm) * 3);
5386   uint   dummy_errors;
5387   uint32 len= copy_and_convert(buf, strlen(cnm) * 3,
5388                                &my_charset_utf8_general_ci,
5389                                cnm, strlen(cnm),
5390                                &my_charset_latin1,
5391                                &dummy_errors);
5392   buf[len]= '\0';
5393   return buf;
5394   } // end of encode
5395 
5396 /**
5397   Store field definition for create.
5398 
5399   @return
5400     Return 0 if ok
5401 */
5402 static bool add_field(String* sql, TABTYPE ttp, const char* field_name, int typ,
5403 	                    int len, int dec, char* key, uint tm, const char* rem,
5404 	                    char* dft, char* xtra, char* fmt, int flag, bool dbf, char v)
5405 {
5406 	char var = (len > 255) ? 'V' : v;
5407 	bool q, error = false;
5408 	const char* type = PLGtoMYSQLtype(typ, dbf, var);
5409 
5410 	error |= sql->append('`');
5411 	error |= sql->append(field_name);
5412 	error |= sql->append("` ");
5413 	error |= sql->append(type);
5414 
5415 	if (typ == TYPE_STRING ||
5416 		(len && typ != TYPE_DATE && (typ != TYPE_DOUBLE || dec >= 0))) {
5417 		error |= sql->append('(');
5418 		error |= sql->append_ulonglong(len);
5419 
5420 		if (typ == TYPE_DOUBLE) {
5421 			error |= sql->append(',');
5422 			// dec must be < len and < 31
5423 			error |= sql->append_ulonglong(MY_MIN(dec, (MY_MIN(len, 31) - 1)));
5424 		} else if (dec > 0 && !strcmp(type, "DECIMAL")) {
5425 			error |= sql->append(',');
5426 			// dec must be < len
5427 			error |= sql->append_ulonglong(MY_MIN(dec, len - 1));
5428 		} // endif dec
5429 
5430 		error |= sql->append(')');
5431 	} // endif len
5432 
5433 	if (v == 'U')
5434 		error |= sql->append(" UNSIGNED");
5435 	else if (v == 'Z')
5436 		error |= sql->append(" ZEROFILL");
5437 
5438 	if (key && *key) {
5439 		error |= sql->append(" ");
5440 		error |= sql->append(key);
5441 	} // endif key
5442 
5443 	if (tm)
5444 		error |= sql->append(STRING_WITH_LEN(" NOT NULL"), system_charset_info);
5445 
5446 	if (dft && *dft) {
5447 		error |= sql->append(" DEFAULT ");
5448 
5449 		if (typ == TYPE_DATE)
5450 			q = (strspn(dft, "0123456789 -:/") == strlen(dft));
5451 		else
5452 			q = !IsTypeNum(typ);
5453 
5454 		if (q) {
5455 			error |= sql->append("'");
5456 			error |= sql->append_for_single_quote(dft, strlen(dft));
5457 			error |= sql->append("'");
5458 		} else
5459 			error |= sql->append(dft);
5460 
5461 	} // endif dft
5462 
5463 	if (xtra && *xtra) {
5464 		error |= sql->append(" ");
5465 		error |= sql->append(xtra);
5466 	} // endif rem
5467 
5468 	if (rem && *rem) {
5469 		error |= sql->append(" COMMENT '");
5470 		error |= sql->append_for_single_quote(rem, strlen(rem));
5471 		error |= sql->append("'");
5472 	} // endif rem
5473 
5474 	if (fmt && *fmt) {
5475     switch (ttp) {
5476       case TAB_MONGO:
5477       case TAB_BSON:
5478       case TAB_JSON: error |= sql->append(" JPATH='"); break;
5479       case TAB_XML:  error |= sql->append(" XPATH='"); break;
5480       default:	     error |= sql->append(" FIELD_FORMAT='");
5481     } // endswitch ttp
5482 
5483 		error |= sql->append_for_single_quote(fmt, strlen(fmt));
5484 		error |= sql->append("'");
5485 	} // endif flag
5486 
5487 	if (flag) {
5488 		error |= sql->append(" FLAG=");
5489 		error |= sql->append_ulonglong(flag);
5490 	} // endif flag
5491 
5492 	error |= sql->append(',');
5493 	return error;
5494 } // end of add_field
5495 
5496 /**
5497   Initialise the table share with the new columns.
5498 
5499   @return
5500     Return 0 if ok
5501 */
5502 static int init_table_share(THD* thd,
5503                             TABLE_SHARE *table_s,
5504                             HA_CREATE_INFO *create_info,
5505                             String *sql)
5506 {
5507   bool oom= false;
5508   PTOS topt= table_s->option_struct;
5509 
5510   sql->length(sql->length()-1); // remove the trailing comma
5511   sql->append(')');
5512 
5513   for (ha_create_table_option *opt= connect_table_option_list;
5514        opt->name; opt++) {
5515     ulonglong   vull;
5516     const char *vstr;
5517 
5518     switch (opt->type) {
5519       case HA_OPTION_TYPE_ULL:
5520         vull= *(ulonglong*)(((char*)topt) + opt->offset);
5521 
5522         if (vull != opt->def_value) {
5523           oom|= sql->append(' ');
5524           oom|= sql->append(opt->name);
5525           oom|= sql->append('=');
5526           oom|= sql->append_ulonglong(vull);
5527           } // endif vull
5528 
5529         break;
5530       case HA_OPTION_TYPE_STRING:
5531         vstr= *(char**)(((char*)topt) + opt->offset);
5532 
5533         if (vstr) {
5534           oom|= sql->append(' ');
5535           oom|= sql->append(opt->name);
5536           oom|= sql->append("='");
5537           oom|= sql->append_for_single_quote(vstr, strlen(vstr));
5538           oom|= sql->append('\'');
5539           } // endif vstr
5540 
5541         break;
5542       case HA_OPTION_TYPE_BOOL:
5543         vull= *(bool*)(((char*)topt) + opt->offset);
5544 
5545         if (vull != opt->def_value) {
5546           oom|= sql->append(' ');
5547           oom|= sql->append(opt->name);
5548           oom|= sql->append('=');
5549           oom|= sql->append(vull ? "YES" : "NO");
5550           } // endif vull
5551 
5552         break;
5553       default: // no enums here, good :)
5554         break;
5555       } // endswitch type
5556 
5557     if (oom)
5558       return HA_ERR_OUT_OF_MEM;
5559 
5560     } // endfor opt
5561 
5562   if (create_info->connect_string.length) {
5563     oom|= sql->append(' ');
5564     oom|= sql->append("CONNECTION='");
5565     oom|= sql->append_for_single_quote(create_info->connect_string.str,
5566                                        create_info->connect_string.length);
5567     oom|= sql->append('\'');
5568 
5569     if (oom)
5570       return HA_ERR_OUT_OF_MEM;
5571 
5572     } // endif string
5573 
5574   if (create_info->default_table_charset) {
5575     oom|= sql->append(' ');
5576     oom|= sql->append("CHARSET=");
5577     oom|= sql->append(create_info->default_table_charset->csname);
5578 
5579     if (oom)
5580       return HA_ERR_OUT_OF_MEM;
5581 
5582     } // endif charset
5583 
5584   if (trace(1))
5585     htrc("s_init: %.*s\n", sql->length(), sql->ptr());
5586 
5587   return table_s->init_from_sql_statement_string(thd, true,
5588                                                  sql->ptr(), sql->length());
5589 } // end of init_table_share
5590 
5591 /**
5592   @brief
5593   connect_assisted_discovery() is called when creating a table with no columns.
5594 
5595   @details
5596   When assisted discovery is used the .frm file have not already been
5597   created. You can overwrite some definitions at this point but the
5598   main purpose of it is to define the columns for some table types.
5599 
5600   @note
5601   this function is no more called in case of CREATE .. SELECT
5602 */
5603 static int connect_assisted_discovery(handlerton *, THD* thd,
5604                                       TABLE_SHARE *table_s,
5605                                       HA_CREATE_INFO *create_info)
5606 {
5607   char     v=0;
5608 	PCSZ     fncn= "?";
5609 	PCSZ     user, fn, db, host, pwd, sep, tbl, src;
5610 	PCSZ     col, ocl, rnk, pic, fcl, skc, zfn;
5611 	char    *tab, *dsn, *shm, *dpath, *url;
5612 #if defined(_WIN32)
5613 	PCSZ     nsp= NULL, cls= NULL;
5614 #endif   // _WIN32
5615 //int      hdr, mxe;
5616 	int      port= 0, mxr= 0, rc= 0, mul= 0;
5617 //PCSZ     tabtyp= NULL;
5618 #if defined(ODBC_SUPPORT)
5619   POPARM   sop= NULL;
5620 	PCSZ     ucnc= NULL;
5621 	bool     cnc= false;
5622   int      cto= -1, qto= -1;
5623 #endif   // ODBC_SUPPORT
5624 #if defined(JAVA_SUPPORT)
5625 	PJPARM   sjp= NULL;
5626 	PCSZ     driver= NULL;
5627 #endif   // JAVA_SUPPORT
5628   uint     tm, fnc= FNC_NO, supfnc= (FNC_NO | FNC_COL);
5629   bool     bif, ok= false, dbf= false;
5630   TABTYPE  ttp= TAB_UNDEF, ttr=TAB_UNDEF;
5631   PQRYRES  qrp= NULL;
5632   PCOLRES  crp;
5633   PCONNECT xp= NULL;
5634   PGLOBAL  g= GetPlug(thd, xp);
5635 
5636   if (!g)
5637     return HA_ERR_INTERNAL_ERROR;
5638 
5639   PTOS     topt= table_s->option_struct;
5640   char     buf[1024];
5641   String   sql(buf, sizeof(buf), system_charset_info);
5642 
5643   sql.copy(STRING_WITH_LEN("CREATE TABLE whatever ("), system_charset_info);
5644 	user= host= pwd= tbl= src= col= ocl= pic= fcl= skc= rnk= zfn= NULL;
5645 	dsn= url= NULL;
5646 
5647   // Get the useful create options
5648   ttp= GetTypeID(topt->type);
5649   fn=  topt->filename;
5650   tab= (char*)topt->tabname;
5651   src= topt->srcdef;
5652   db=  topt->dbname;
5653   fncn= topt->catfunc;
5654   fnc= GetFuncID(fncn);
5655   sep= topt->separator;
5656 	mul= (int)topt->multiple;
5657 	tbl= topt->tablist;
5658   col= topt->colist;
5659 
5660   if (topt->oplist) {
5661     host= GetListOption(g, "host", topt->oplist, "localhost");
5662     user= GetListOption(g, "user", topt->oplist,
5663           ((ttp == TAB_ODBC || ttp == TAB_JDBC) ? NULL : "root"));
5664     // Default value db can come from the DBNAME=xxx option.
5665     db= GetListOption(g, "database", topt->oplist, db);
5666     col= GetListOption(g, "colist", topt->oplist, col);
5667     ocl= GetListOption(g, "occurcol", topt->oplist, NULL);
5668     pic= GetListOption(g, "pivotcol", topt->oplist, NULL);
5669     fcl= GetListOption(g, "fnccol", topt->oplist, NULL);
5670     skc= GetListOption(g, "skipcol", topt->oplist, NULL);
5671     rnk= GetListOption(g, "rankcol", topt->oplist, NULL);
5672     pwd= GetListOption(g, "password", topt->oplist);
5673 #if defined(_WIN32)
5674     nsp= GetListOption(g, "namespace", topt->oplist);
5675     cls= GetListOption(g, "class", topt->oplist);
5676 #endif   // _WIN32
5677     port= atoi(GetListOption(g, "port", topt->oplist, "0"));
5678 #if defined(ODBC_SUPPORT)
5679 //	tabtyp= GetListOption(g, "Tabtype", topt->oplist, NULL);
5680 		mxr= atoi(GetListOption(g,"maxres", topt->oplist, "0"));
5681     cto= atoi(GetListOption(g,"ConnectTimeout", topt->oplist, "-1"));
5682     qto= atoi(GetListOption(g,"QueryTimeout", topt->oplist, "-1"));
5683 
5684     if ((ucnc= GetListOption(g, "UseDSN", topt->oplist)))
5685       cnc= (!*ucnc || *ucnc == 'y' || *ucnc == 'Y' || atoi(ucnc) != 0);
5686 #endif
5687 #if defined(JAVA_SUPPORT)
5688 		driver= GetListOption(g, "Driver", topt->oplist, NULL);
5689 #endif   // JAVA_SUPPORT
5690 #if defined(PROMPT_OK)
5691     cop= atoi(GetListOption(g, "checkdsn", topt->oplist, "0"));
5692 #endif   // PROMPT_OK
5693 #if defined(ZIP_SUPPORT)
5694 		zfn= GetListOption(g, "Zipfile", topt->oplist, NULL);
5695 #endif   // ZIP_SUPPORT
5696 	} else {
5697     host= "localhost";
5698     user= ((ttp == TAB_ODBC || ttp == TAB_JDBC) ? NULL : "root");
5699   } // endif option_list
5700 
5701   if (!(shm= (char*)db))
5702     db= table_s->db.str;                   // Default value
5703 
5704 	try {
5705 		// Check table type
5706 		if (ttp == TAB_UNDEF && !topt->http) {
5707 			topt->type= (src) ? "MYSQL" : (tab) ? "PROXY" : "DOS";
5708 			ttp= GetTypeID(topt->type);
5709 			snprintf(g->Message, sizeof(g->Message), "No table_type. Was set to %s", topt->type);
5710 			push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, 0, g->Message);
5711 		} else if (ttp == TAB_NIY) {
5712 			snprintf(g->Message, sizeof(g->Message), "Unsupported table type %s", topt->type);
5713 			rc= HA_ERR_INTERNAL_ERROR;
5714 			goto err;
5715 #if defined(REST_SUPPORT)
5716 		} else if (topt->http) {
5717       if (ttp == TAB_UNDEF) {
5718         ttr= TAB_JSON;
5719         strcpy(g->Message, "No table_type. Was set to JSON");
5720         push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, 0, g->Message);
5721       } else
5722         ttr= ttp;
5723 
5724       switch (ttr) {
5725 				case TAB_JSON:
5726 #if defined(BSON_SUPPORT)
5727         case TAB_BSON:
5728 #endif   // BSON_SUPPORT
5729         case TAB_XML:
5730 				case TAB_CSV:
5731           ttp = TAB_REST;
5732 					break;
5733 				default:
5734 					break;
5735 			}	// endswitch type
5736 #endif   // REST_SUPPORT
5737 		} // endif ttp
5738 
5739     if (fn && *fn)
5740       switch (ttp) {
5741         case TAB_FMT:
5742         case TAB_DBF:
5743         case TAB_XML:
5744         case TAB_INI:
5745         case TAB_VEC:
5746         case TAB_REST:
5747         case TAB_JSON:
5748 #if defined(BSON_SUPPORT)
5749         case TAB_BSON:
5750 #endif   // BSON_SUPPORT
5751           if (checkPrivileges(thd, ttp, topt, db)) {
5752             strcpy(g->Message, "This operation requires the FILE privilege");
5753             rc= HA_ERR_INTERNAL_ERROR;
5754             goto err;
5755           } // endif check_privileges
5756 
5757           break;
5758         default:
5759           break;
5760       } // endswitch ttp
5761 
5762 		if (!tab) {
5763 			if (ttp == TAB_TBL) {
5764 				// Make tab the first table of the list
5765 				char *p;
5766 
5767 				if (!tbl) {
5768 					strcpy(g->Message, "Missing table list");
5769 					rc= HA_ERR_INTERNAL_ERROR;
5770 					goto err;
5771 				} // endif tbl
5772 
5773 				tab= PlugDup(g, tbl);
5774 
5775 				if ((p= strchr(tab, ',')))
5776 					*p= 0;
5777 
5778 				if ((p= strchr(tab, '.'))) {
5779 					*p= 0;
5780 					db= tab;
5781 					tab= p + 1;
5782 				} // endif p
5783 
5784 			} else if (ttp != TAB_ODBC || !(fnc & (FNC_TABLE | FNC_COL)))
5785 			  tab= (char*)table_s->table_name.str;   // Default value
5786 
5787 		} // endif tab
5788 
5789 		switch (ttp) {
5790 #if defined(ODBC_SUPPORT)
5791 			case TAB_ODBC:
5792 				dsn= strz(g, create_info->connect_string);
5793 
5794 				if (fnc & (FNC_DSN | FNC_DRIVER)) {
5795 					ok= true;
5796 #if defined(PROMPT_OK)
5797 				} else if (!stricmp(thd->main_security_ctx.host, "localhost")
5798 					&& cop == 1) {
5799 					if ((dsn= ODBCCheckConnection(g, dsn, cop)) != NULL) {
5800 						thd->make_lex_string(&create_info->connect_string, dsn, strlen(dsn));
5801 						ok= true;
5802 					} // endif dsn
5803 #endif   // PROMPT_OK
5804 
5805 				} else if (!dsn) {
5806 					sprintf(g->Message, "Missing %s connection string", topt->type);
5807 				} else {
5808 					// Store ODBC additional parameters
5809 					sop= (POPARM)PlugSubAlloc(g, NULL, sizeof(ODBCPARM));
5810 					sop->User= (char*)user;
5811 					sop->Pwd= (char*)pwd;
5812 					sop->Cto= cto;
5813 					sop->Qto= qto;
5814 					sop->UseCnc= cnc;
5815 					ok= true;
5816 				} // endif's
5817 
5818 				supfnc |= (FNC_TABLE | FNC_DSN | FNC_DRIVER);
5819 				break;
5820 #endif   // ODBC_SUPPORT
5821 #if defined(JAVA_SUPPORT)
5822 			case TAB_JDBC:
5823 				if (fnc & FNC_DRIVER) {
5824 					ok= true;
5825 				} else if (!(url= strz(g, create_info->connect_string))) {
5826 					strcpy(g->Message, "Missing URL");
5827 				} else {
5828 					// Store JDBC additional parameters
5829 					int      rc;
5830 					PJDBCDEF jdef= new(g) JDBCDEF();
5831 
5832 					jdef->SetName(create_info->alias.str);
5833 					sjp = (PJPARM)PlugSubAlloc(g, NULL, sizeof(JDBCPARM));
5834 					sjp->Driver = driver;
5835 					//		sjp->Properties = prop;
5836 					sjp->Fsize = 0;
5837 					sjp->Scrollable = false;
5838 
5839 					if ((rc = jdef->ParseURL(g, url, false)) == RC_OK) {
5840 						sjp->Url = url;
5841 						sjp->User = (char*)user;
5842 						sjp->Pwd = (char*)pwd;
5843 						ok = true;
5844 					} else if (rc == RC_NF) {
5845 						if (jdef->GetTabname())
5846 							tab= (char*)jdef->GetTabname();
5847 
5848 						ok= jdef->SetParms(sjp);
5849 					} // endif rc
5850 
5851 				} // endif's
5852 
5853 				supfnc |= (FNC_DRIVER | FNC_TABLE);
5854 				break;
5855 #endif   // JAVA_SUPPORT
5856 			case TAB_DBF:
5857 				dbf= true;
5858 				// fall through
5859 			case TAB_CSV:
5860 				if (!fn && fnc != FNC_NO)
5861 					sprintf(g->Message, "Missing %s file name", topt->type);
5862 				else if (sep && strlen(sep) > 1)
5863 					sprintf(g->Message, "Invalid separator %s", sep);
5864 				else
5865 					ok= true;
5866 
5867 				break;
5868 			case TAB_MYSQL:
5869 				ok= true;
5870 
5871 				if (create_info->connect_string.str &&
5872 					create_info->connect_string.length) {
5873 					PMYDEF  mydef= new(g) MYSQLDEF();
5874 
5875 					dsn= strz(g, create_info->connect_string);
5876 					mydef->SetName(create_info->alias.str);
5877 
5878 					if (!mydef->ParseURL(g, dsn, false)) {
5879 						if (mydef->GetHostname())
5880 							host= mydef->GetHostname();
5881 
5882 						if (mydef->GetUsername())
5883 							user= mydef->GetUsername();
5884 
5885 						if (mydef->GetPassword())
5886 							pwd= mydef->GetPassword();
5887 
5888 						if (mydef->GetTabschema())
5889 							db= mydef->GetTabschema();
5890 
5891 						if (mydef->GetTabname())
5892 							tab= (char*)mydef->GetTabname();
5893 
5894 						if (mydef->GetPortnumber())
5895 							port= mydef->GetPortnumber();
5896 
5897 					} else
5898 						ok= false;
5899 
5900 				} else if (!user)
5901 					user= "root";
5902 
5903 				if (ok && CheckSelf(g, table_s, host, db, tab, src, port))
5904 					ok= false;
5905 
5906 				break;
5907 #if defined(_WIN32)
5908 			case TAB_WMI:
5909 				ok= true;
5910 				break;
5911 #endif   // _WIN32
5912 			case TAB_PIVOT:
5913 				supfnc= FNC_NO;
5914 			case TAB_PRX:
5915 			case TAB_TBL:
5916 			case TAB_XCL:
5917 			case TAB_OCCUR:
5918 				if (!src && !stricmp(tab, create_info->alias.str) &&
5919 					(!db || !stricmp(db, table_s->db.str)))
5920 					sprintf(g->Message, "A %s table cannot refer to itself", topt->type);
5921 				else
5922 					ok= true;
5923 
5924 				break;
5925 			case TAB_OEM:
5926 				if (topt->module && topt->subtype)
5927 					ok= true;
5928 				else
5929 					strcpy(g->Message, "Missing OEM module or subtype");
5930 
5931 				break;
5932 #if defined(LIBXML2_SUPPORT) || defined(DOMDOC_SUPPORT)
5933 			case TAB_XML:
5934 #endif   // LIBXML2_SUPPORT  ||         DOMDOC_SUPPORT
5935 			case TAB_JSON:
5936 #if defined(BSON_SUPPORT)
5937       case TAB_BSON:
5938 #endif   // BSON_SUPPORT
5939         dsn= strz(g, create_info->connect_string);
5940 
5941 				if (!fn && !zfn && !mul && !dsn)
5942 					sprintf(g->Message, "Missing %s file name", topt->type);
5943 				else if (dsn && !topt->tabname)
5944           topt->tabname= tab;
5945 
5946 				ok= true;
5947 				break;
5948 #if defined(JAVA_SUPPORT)
5949 			case TAB_MONGO:
5950 				if (!topt->tabname)
5951 					topt->tabname= tab;
5952 
5953 				ok= true;
5954 				break;
5955 #endif   // JAVA_SUPPORT
5956 #if defined(REST_SUPPORT)
5957 			case TAB_REST:
5958 				if (!topt->http)
5959 					strcpy(g->Message, "Missing REST HTTP option");
5960 				else
5961 					ok = true;
5962 
5963 				break;
5964 #endif   // REST_SUPPORT
5965 			case TAB_VIR:
5966 				ok= true;
5967 				break;
5968 			default:
5969 				sprintf(g->Message, "Cannot get column info for table type %s", topt->type);
5970 				break;
5971 		} // endif ttp
5972 
5973 	// Check for supported catalog function
5974 		if (ok && !(supfnc & fnc)) {
5975 			sprintf(g->Message, "Unsupported catalog function %s for table type %s",
5976 				fncn, topt->type);
5977 			ok= false;
5978 		} // endif supfnc
5979 
5980 		if (src && fnc != FNC_NO) {
5981 			strcpy(g->Message, "Cannot make catalog table from srcdef");
5982 			ok= false;
5983 		} // endif src
5984 
5985 		if (ok) {
5986 			const char *cnm, *rem;
5987 			char *dft, *xtra, *key, *fmt;
5988 			int   i, len, prec, dec, typ, flg;
5989 
5990 			if (!(dpath= SetPath(g, table_s->db.str))) {
5991 				rc= HA_ERR_INTERNAL_ERROR;
5992 				goto err;
5993 			}	// endif dpath
5994 
5995 			if (src && ttp != TAB_PIVOT && ttp != TAB_ODBC && ttp != TAB_JDBC) {
5996 				qrp= SrcColumns(g, host, db, user, pwd, src, port);
5997 
5998 				if (qrp && ttp == TAB_OCCUR)
5999 					if (OcrSrcCols(g, qrp, col, ocl, rnk)) {
6000 						rc= HA_ERR_INTERNAL_ERROR;
6001 						goto err;
6002 					} // endif OcrSrcCols
6003 
6004 			} else switch (ttp) {
6005 				case TAB_DBF:
6006 					qrp= DBFColumns(g, dpath, fn, topt, fnc == FNC_COL);
6007 					break;
6008 #if defined(ODBC_SUPPORT)
6009 				case TAB_ODBC:
6010 					switch (fnc) {
6011 						case FNC_NO:
6012 						case FNC_COL:
6013 							if (src) {
6014 								qrp= ODBCSrcCols(g, dsn, (char*)src, sop);
6015 								src= NULL;     // for next tests
6016 							} else
6017 								qrp= ODBCColumns(g, dsn, shm, tab, NULL,
6018 									mxr, fnc == FNC_COL, sop);
6019 
6020 							break;
6021 						case FNC_TABLE:
6022 							qrp= ODBCTables(g, dsn, shm, tab, NULL, mxr, true, sop);
6023 							break;
6024 						case FNC_DSN:
6025 							qrp= ODBCDataSources(g, mxr, true);
6026 							break;
6027 						case FNC_DRIVER:
6028 							qrp= ODBCDrivers(g, mxr, true);
6029 							break;
6030 						default:
6031 							sprintf(g->Message, "invalid catfunc %s", fncn);
6032 							break;
6033 					} // endswitch info
6034 
6035 					break;
6036 #endif   // ODBC_SUPPORT
6037 #if defined(JAVA_SUPPORT)
6038 				case TAB_JDBC:
6039 					switch (fnc) {
6040 						case FNC_NO:
6041 						case FNC_COL:
6042 							if (src) {
6043 								qrp= JDBCSrcCols(g, (char*)src, sjp);
6044 								src= NULL;     // for next tests
6045 							} else
6046 								qrp= JDBCColumns(g, shm, tab, NULL, mxr, fnc == FNC_COL, sjp);
6047 
6048 							break;
6049 						case FNC_TABLE:
6050 //						qrp= JDBCTables(g, shm, tab, tabtyp, mxr, true, sjp);
6051 							qrp= JDBCTables(g, shm, tab, NULL, mxr, true, sjp);
6052 							break;
6053 #if 0
6054 						case FNC_DSN:
6055 							qrp= JDBCDataSources(g, mxr, true);
6056 							break;
6057 #endif // 0
6058 						case FNC_DRIVER:
6059 							qrp= JDBCDrivers(g, mxr, true);
6060 							break;
6061 						default:
6062 							sprintf(g->Message, "invalid catfunc %s", fncn);
6063 							break;
6064 					} // endswitch info
6065 
6066 					break;
6067 #endif   // JAVA_SUPPORT
6068 				case TAB_MYSQL:
6069 					qrp= MyColumns(g, thd, host, db, user, pwd, tab,
6070 						NULL, port, fnc == FNC_COL);
6071 					break;
6072 				case TAB_CSV:
6073 					qrp= CSVColumns(g, dpath, topt, fnc == FNC_COL);
6074 					break;
6075 #if defined(_WIN32)
6076 				case TAB_WMI:
6077 					qrp= WMIColumns(g, nsp, cls, fnc == FNC_COL);
6078 					break;
6079 #endif   // _WIN32
6080 				case TAB_PRX:
6081 				case TAB_TBL:
6082 				case TAB_XCL:
6083 				case TAB_OCCUR:
6084 					bif= fnc == FNC_COL;
6085 					qrp= TabColumns(g, thd, db, tab, bif);
6086 
6087 					if (!qrp && bif && fnc != FNC_COL)         // tab is a view
6088 						qrp= MyColumns(g, thd, host, db, user, pwd, tab, NULL, port, false);
6089 
6090 					if (qrp && ttp == TAB_OCCUR && fnc != FNC_COL)
6091 						if (OcrColumns(g, qrp, col, ocl, rnk)) {
6092 							rc= HA_ERR_INTERNAL_ERROR;
6093 							goto err;
6094 						} // endif OcrColumns
6095 
6096 					break;
6097 				case TAB_PIVOT:
6098 					qrp= PivotColumns(g, tab, src, pic, fcl, skc, host, db, user, pwd, port);
6099 					break;
6100 				case TAB_VIR:
6101 					qrp= VirColumns(g, fnc == FNC_COL);
6102 					break;
6103 				case TAB_JSON:
6104 #if !defined(FORCE_BSON)
6105 					qrp= JSONColumns(g, db, dsn, topt, fnc == FNC_COL);
6106 					break;
6107 #endif   // !FORCE_BSON
6108 #if defined(BSON_SUPPORT)
6109         case TAB_BSON:
6110           qrp= BSONColumns(g, db, dsn, topt, fnc == FNC_COL);
6111           break;
6112 #endif   // BSON_SUPPORT
6113 #if defined(JAVA_SUPPORT)
6114 				case TAB_MONGO:
6115 					url= strz(g, create_info->connect_string);
6116 					qrp= MGOColumns(g, db, url, topt, fnc == FNC_COL);
6117 					break;
6118 #endif   // JAVA_SUPPORT
6119 #if defined(LIBXML2_SUPPORT) || defined(DOMDOC_SUPPORT)
6120 				case TAB_XML:
6121 					qrp= XMLColumns(g, (char*)db, tab, topt, fnc == FNC_COL);
6122 					break;
6123 #endif   // LIBXML2_SUPPORT  ||         DOMDOC_SUPPORT
6124 #if defined(REST_SUPPORT)
6125 				case TAB_REST:
6126 					qrp = RESTColumns(g, topt, tab, (char *)db, fnc == FNC_COL);
6127 					break;
6128 #endif   // REST_SUPPORT
6129 				case TAB_OEM:
6130 					qrp= OEMColumns(g, topt, tab, (char*)db, fnc == FNC_COL);
6131 					break;
6132 				default:
6133 					strcpy(g->Message, "System error during assisted discovery");
6134 					break;
6135 			} // endswitch ttp
6136 
6137 			if (!qrp) {
6138 				rc= HA_ERR_INTERNAL_ERROR;
6139 				goto err;
6140 			} // endif !qrp
6141 
6142 			if (fnc != FNC_NO || src || ttp == TAB_PIVOT) {
6143 				// Catalog like table
6144 				for (crp= qrp->Colresp; !rc && crp; crp= crp->Next) {
6145 					cnm= (ttp == TAB_PIVOT) ? crp->Name : encode(g, crp->Name);
6146 					typ= crp->Type;
6147 					len= crp->Length;
6148 					dec= crp->Prec;
6149 					flg= crp->Flag;
6150 					v= (crp->Kdata->IsUnsigned()) ? 'U' : crp->Var;
6151 					tm= (crp->Kdata->IsNullable()) ? 0 : NOT_NULL_FLAG;
6152 
6153 					if (!len && typ == TYPE_STRING)
6154 						len= 256;      // STRBLK's have 0 length
6155 
6156 					// Now add the field
6157 					if (add_field(&sql, ttp, cnm, typ, len, dec, NULL, tm,
6158 						NULL, NULL, NULL, NULL, flg, dbf, v))
6159 						rc= HA_ERR_OUT_OF_MEM;
6160 				} // endfor crp
6161 
6162 			} else {
6163 				char *schem= NULL;
6164 				char *tn= NULL;
6165 
6166 				// Not a catalog table
6167 				if (!qrp->Nblin) {
6168 					if (tab)
6169 						sprintf(g->Message, "Cannot get columns from %s", tab);
6170 					else
6171 						strcpy(g->Message, "Fail to retrieve columns");
6172 
6173 					rc= HA_ERR_INTERNAL_ERROR;
6174 					goto err;
6175 				} // endif !nblin
6176 
6177 				// Restore language type
6178 				if (ttp == TAB_REST)
6179           ttp = ttr;
6180 
6181 				for (i= 0; !rc && i < qrp->Nblin; i++) {
6182 					typ= len= prec= dec= flg= 0;
6183 					tm= NOT_NULL_FLAG;
6184 					cnm= (char*)"noname";
6185 					dft= xtra= key= fmt= tn= NULL;
6186 					v= ' ';
6187 					rem= NULL;
6188 
6189 					for (crp= qrp->Colresp; crp; crp= crp->Next)
6190 						switch (crp->Fld) {
6191 							case FLD_NAME:
6192 								if (ttp == TAB_PRX ||
6193 									(ttp == TAB_CSV && topt->data_charset &&
6194 									(!stricmp(topt->data_charset, "UTF8") ||
6195 										!stricmp(topt->data_charset, "UTF-8"))))
6196 									cnm= crp->Kdata->GetCharValue(i);
6197 								else
6198 									cnm= encode(g, crp->Kdata->GetCharValue(i));
6199 
6200 								break;
6201 							case FLD_TYPE:
6202 								typ= crp->Kdata->GetIntValue(i);
6203 								v= (crp->Nulls) ? crp->Nulls[i] : 0;
6204 								break;
6205 							case FLD_TYPENAME:
6206 								tn= crp->Kdata->GetCharValue(i);
6207 								break;
6208 							case FLD_PREC:
6209 								// PREC must be always before LENGTH
6210 								len= prec= crp->Kdata->GetIntValue(i);
6211 								break;
6212 							case FLD_LENGTH:
6213 								len= crp->Kdata->GetIntValue(i);
6214 								break;
6215 							case FLD_SCALE:
6216 								dec= (!crp->Kdata->IsNull(i)) ? crp->Kdata->GetIntValue(i) : -1;
6217 								break;
6218 							case FLD_NULL:
6219 								if (crp->Kdata->GetIntValue(i))
6220 									tm= 0;               // Nullable
6221 
6222 								break;
6223 							case FLD_FLAG:
6224 								flg = crp->Kdata->GetIntValue(i);
6225 								break;
6226 							case FLD_FORMAT:
6227 								fmt= (crp->Kdata) ? crp->Kdata->GetCharValue(i) : NULL;
6228 								break;
6229 							case FLD_REM:
6230 								rem= crp->Kdata->GetCharValue(i);
6231 								break;
6232 								//          case FLD_CHARSET:
6233 															// No good because remote table is already translated
6234 								//            if (*(csn= crp->Kdata->GetCharValue(i)))
6235 								//              cs= get_charset_by_name(csn, 0);
6236 
6237 								//            break;
6238 							case FLD_DEFAULT:
6239 								dft= crp->Kdata->GetCharValue(i);
6240 								break;
6241 							case FLD_EXTRA:
6242 								xtra= crp->Kdata->GetCharValue(i);
6243 
6244 								// Auto_increment is not supported yet
6245 								if (!stricmp(xtra, "AUTO_INCREMENT"))
6246 									xtra= NULL;
6247 
6248 								break;
6249 							case FLD_KEY:
6250 								if (ttp == TAB_VIR)
6251 									key= crp->Kdata->GetCharValue(i);
6252 
6253 								break;
6254 							case FLD_SCHEM:
6255 #if defined(ODBC_SUPPORT) || defined(JAVA_SUPPORT)
6256 								if ((ttp == TAB_ODBC || ttp == TAB_JDBC) && crp->Kdata) {
6257 									if (schem && stricmp(schem, crp->Kdata->GetCharValue(i))) {
6258 										sprintf(g->Message,
6259 											"Several %s tables found, specify DBNAME", tab);
6260 										rc= HA_ERR_INTERNAL_ERROR;
6261 										goto err;
6262 									} else if (!schem)
6263 										schem= crp->Kdata->GetCharValue(i);
6264 
6265 								} // endif ttp
6266 #endif   // ODBC_SUPPORT	||				 JAVA_SUPPORT
6267 							default:
6268 								break;                 // Ignore
6269 						} // endswitch Fld
6270 
6271 #if defined(ODBC_SUPPORT)
6272 					if (ttp == TAB_ODBC) {
6273 						int  plgtyp;
6274 						bool w= false;            // Wide character type
6275 
6276 						// typ must be PLG type, not SQL type
6277 						if (!(plgtyp= TranslateSQLType(typ, dec, prec, v, w))) {
6278 							if (GetTypeConv() == TPC_SKIP) {
6279 								// Skip this column
6280 								sprintf(g->Message, "Column %s skipped (unsupported type %d)",
6281 									cnm, typ);
6282 								push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
6283 								continue;
6284 							} else {
6285 								sprintf(g->Message, "Unsupported SQL type %d", typ);
6286 								rc= HA_ERR_INTERNAL_ERROR;
6287 								goto err;
6288 							} // endif type_conv
6289 
6290 						} else
6291 							typ= plgtyp;
6292 
6293 						switch (typ) {
6294 							case TYPE_STRING:
6295 								if (w) {
6296 									sprintf(g->Message, "Column %s is wide characters", cnm);
6297 									push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, 0, g->Message);
6298 								} // endif w
6299 
6300 								break;
6301 							case TYPE_DOUBLE:
6302 								// Some data sources do not count dec in length (prec)
6303 								prec += (dec + 2);        // To be safe
6304 								break;
6305 							case TYPE_DECIM:
6306 								prec= len;
6307 								break;
6308 							default:
6309 								dec= 0;
6310 						} // endswitch typ
6311 
6312 					} else
6313 #endif   // ODBC_SUPPORT
6314 #if defined(JAVA_SUPPORT)
6315 						if (ttp == TAB_JDBC) {
6316 							int  plgtyp;
6317 
6318 							// typ must be PLG type, not SQL type
6319 							if (!(plgtyp= TranslateJDBCType(typ, tn, dec, prec, v))) {
6320 								if (GetTypeConv() == TPC_SKIP) {
6321 									// Skip this column
6322 									sprintf(g->Message, "Column %s skipped (unsupported type %d)",
6323 										cnm, typ);
6324 									push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
6325 									continue;
6326 								} else {
6327 									sprintf(g->Message, "Unsupported SQL type %d", typ);
6328 									rc= HA_ERR_INTERNAL_ERROR;
6329 									goto err;
6330 								} // endif type_conv
6331 
6332 							} else
6333 								typ= plgtyp;
6334 
6335 							switch (typ) {
6336 								case TYPE_DOUBLE:
6337 								case TYPE_DECIM:
6338 									// Some data sources do not count dec in length (prec)
6339 									prec += (dec + 2);        // To be safe
6340 									break;
6341 								default:
6342 									dec= 0;
6343 							} // endswitch typ
6344 
6345 						} else
6346 #endif   // ODBC_SUPPORT
6347 							// Make the arguments as required by add_fields
6348 							if (typ == TYPE_DOUBLE)
6349 								prec= len;
6350 
6351 						if (typ == TYPE_DATE)
6352 							prec= 0;
6353 
6354 						// Now add the field
6355 						if (add_field(&sql, ttp, cnm, typ, prec, dec, key, tm, rem, dft, xtra,
6356 								fmt, flg, dbf, v))
6357 							rc= HA_ERR_OUT_OF_MEM;
6358 				} // endfor i
6359 
6360 			} // endif fnc
6361 
6362 			if (!rc)
6363 				rc= init_table_share(thd, table_s, create_info, &sql);
6364 
6365 			//g->jump_level--;
6366 			//PopUser(xp);
6367 			//return rc;
6368 		} else {
6369 			rc= HA_ERR_UNSUPPORTED;
6370 		} // endif ok
6371 
6372 	} catch (int n) {
6373 		if (trace(1))
6374 			htrc("Exception %d: %s\n", n, g->Message);
6375 		rc= HA_ERR_INTERNAL_ERROR;
6376 	} catch (const char *msg) {
6377 		strcpy(g->Message, msg);
6378 		rc= HA_ERR_INTERNAL_ERROR;
6379 	} // end catch
6380 
6381  err:
6382   if (rc)
6383     my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6384 
6385 	PopUser(xp);
6386 	return rc;
6387 } // end of connect_assisted_discovery
6388 
6389 /**
6390   Get the database name from a qualified table name.
6391 */
6392 char *ha_connect::GetDBfromName(const char *name)
6393 {
6394   char *db, dbname[128], tbname[128];
6395 
6396   if (filename_to_dbname_and_tablename(name, dbname, sizeof(dbname),
6397                                              tbname, sizeof(tbname)))
6398     *dbname= 0;
6399 
6400   if (*dbname) {
6401     assert(xp && xp->g);
6402     db= (char*)PlugSubAlloc(xp->g, NULL, strlen(dbname + 1));
6403     strcpy(db, dbname);
6404   } else
6405     db= NULL;
6406 
6407   return db;
6408 } // end of GetDBfromName
6409 
6410 
6411 /**
6412   @brief
6413   create() is called to create a database. The variable name will have the name
6414   of the table.
6415 
6416   @details
6417   When create() is called you do not need to worry about
6418   opening the table. Also, the .frm file will have already been
6419   created so adjusting create_info is not necessary. You can overwrite
6420   the .frm file at this point if you wish to change the table
6421   definition, but there are no methods currently provided for doing
6422   so.
6423 
6424   Called from handle.cc by ha_create_table().
6425 
6426   @note
6427   Currently we do some checking on the create definitions and stop
6428   creating if an error is found. We wish we could change the table
6429   definition such as providing a default table type. However, as said
6430   above, there are no method to do so.
6431 
6432   @see
6433   ha_create_table() in handle.cc
6434 */
6435 
6436 int ha_connect::create(const char *name, TABLE *table_arg,
6437                        HA_CREATE_INFO *create_info)
6438 {
6439   int     rc= RC_OK;
6440   bool    dbf, inward;
6441   Field* *field;
6442   Field  *fp;
6443   TABTYPE type;
6444   TABLE  *st= table;                       // Probably unuseful
6445   THD    *thd= ha_thd();
6446   LEX_CSTRING cnc = table_arg->s->connect_string;
6447 #if defined(WITH_PARTITION_STORAGE_ENGINE)
6448   partition_info *part_info= table_arg->part_info;
6449 #else		// !WITH_PARTITION_STORAGE_ENGINE
6450 #define part_info 0
6451 #endif  // !WITH_PARTITION_STORAGE_ENGINE
6452   xp= GetUser(thd, xp);
6453   PGLOBAL g= xp->g;
6454 
6455   DBUG_ENTER("ha_connect::create");
6456   /*
6457     This assignment fixes test failures if some
6458     "ALTER TABLE t1 ADD KEY(a)" query exits on ER_ACCESS_DENIED_ERROR
6459     (e.g. on missing FILE_ACL). All following "CREATE TABLE" failed with
6460     "ERROR 1105: CONNECT index modification should be in-place"
6461     TODO: check with Olivier.
6462   */
6463   g->Xchk= NULL;
6464   int  sqlcom= thd_sql_command(table_arg->in_use);
6465   PTOS options= GetTableOptionStruct(table_arg->s);
6466 
6467   table= table_arg;         // Used by called functions
6468 
6469   if (trace(1))
6470     htrc("create: this=%p thd=%p xp=%p g=%p sqlcom=%d name=%s\n",
6471            this, thd, xp, g, sqlcom, GetTableName());
6472 
6473   // CONNECT engine specific table options:
6474   DBUG_ASSERT(options);
6475   type= GetTypeID(options->type);
6476 
6477   // Check table type
6478   if (type == TAB_UNDEF) {
6479     options->type= (options->srcdef)  ? "MYSQL" :
6480 #if defined(REST_SUPPORT)
6481                    (options->http) ? "JSON" :
6482 #endif   // REST_SUPPORT
6483                    (options->tabname) ? "PROXY" : "DOS";
6484     type= GetTypeID(options->type);
6485     sprintf(g->Message, "No table_type. Will be set to %s", options->type);
6486 
6487     if (sqlcom == SQLCOM_CREATE_TABLE)
6488       push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
6489 
6490   } else if (type == TAB_NIY) {
6491     sprintf(g->Message, "Unsupported table type %s", options->type);
6492     my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6493     DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
6494   } // endif ttp
6495 
6496   if (check_privileges(thd, options, GetDBfromName(name)))
6497     DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
6498 
6499   inward= IsFileType(type) && !options->filename &&
6500 		     ((type != TAB_JSON && type != TAB_BSON) || !cnc.length);
6501 
6502   if (options->data_charset) {
6503     const CHARSET_INFO *data_charset;
6504 
6505     if (!(data_charset= get_charset_by_csname(options->data_charset,
6506                                               MY_CS_PRIMARY, MYF(0)))) {
6507       my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), options->data_charset);
6508       DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
6509       } // endif charset
6510 
6511     if (type == TAB_XML && data_charset != &my_charset_utf8_general_ci) {
6512       my_printf_error(ER_UNKNOWN_ERROR,
6513                       "DATA_CHARSET='%s' is not supported for TABLE_TYPE=XML",
6514                         MYF(0), options->data_charset);
6515       DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
6516       } // endif utf8
6517 
6518     } // endif charset
6519 
6520   if (!g) {
6521     rc= HA_ERR_INTERNAL_ERROR;
6522     DBUG_RETURN(rc);
6523   } else
6524     dbf= (GetTypeID(options->type) == TAB_DBF && !options->catfunc);
6525 
6526   // Can be null in ALTER TABLE
6527   if (create_info->alias.str)
6528     // Check whether a table is defined on itself
6529     switch (type) {
6530       case TAB_PRX:
6531       case TAB_XCL:
6532       case TAB_PIVOT:
6533       case TAB_OCCUR:
6534         if (options->srcdef) {
6535           strcpy(g->Message, "Cannot check looping reference");
6536           push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
6537         } else if (options->tabname) {
6538           if (!stricmp(options->tabname, create_info->alias.str) &&
6539              (!options->dbname ||
6540               !stricmp(options->dbname, table_arg->s->db.str))) {
6541             sprintf(g->Message, "A %s table cannot refer to itself",
6542                                 options->type);
6543             my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6544             DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
6545             } // endif tab
6546 
6547         } else {
6548           strcpy(g->Message, "Missing object table name or definition");
6549           my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6550           DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
6551         } // endif tabname
6552 
6553 				// fall through
6554       case TAB_MYSQL:
6555         if (!part_info)
6556        {const char *src= options->srcdef;
6557 				PCSZ host, db, tab= options->tabname;
6558         int  port;
6559 
6560         host= GetListOption(g, "host", options->oplist, NULL);
6561         db= GetStringOption("database", NULL);
6562         port= atoi(GetListOption(g, "port", options->oplist, "0"));
6563 
6564         if (create_info->connect_string.str &&
6565             create_info->connect_string.length) {
6566           char   *dsn= strz(g, create_info->connect_string);
6567           PMYDEF  mydef= new(g) MYSQLDEF();
6568 
6569           mydef->SetName(create_info->alias.str);
6570 
6571           if (!mydef->ParseURL(g, dsn, false)) {
6572             if (mydef->GetHostname())
6573               host= mydef->GetHostname();
6574 
6575 						if (mydef->GetTabschema())
6576 							db= mydef->GetTabschema();
6577 
6578             if (mydef->GetTabname())
6579               tab= mydef->GetTabname();
6580 
6581             if (mydef->GetPortnumber())
6582               port= mydef->GetPortnumber();
6583 
6584           } else {
6585             my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6586             DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
6587           } // endif ParseURL
6588 
6589           } // endif connect_string
6590 
6591         if (CheckSelf(g, table_arg->s, host, db, tab, src, port)) {
6592           my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6593           DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
6594           } // endif CheckSelf
6595 
6596        } break;
6597       default: /* do nothing */;
6598         break;
6599      } // endswitch ttp
6600 
6601   if (type == TAB_XML) {
6602     bool dom;                  // True: MS-DOM, False libxml2
6603 		PCSZ xsup= GetListOption(g, "Xmlsup", options->oplist, "*");
6604 
6605     // Note that if no support is specified, the default is MS-DOM
6606     // on Windows and libxml2 otherwise
6607     switch (toupper(*xsup)) {
6608       case '*':
6609 #if defined(_WIN32)
6610         dom= true;
6611 #else   // !_WIN32
6612         dom= false;
6613 #endif  // !_WIN32
6614         break;
6615       case 'M':
6616       case 'D':
6617         dom= true;
6618         break;
6619       default:
6620         dom= false;
6621         break;
6622       } // endswitch xsup
6623 
6624 #if !defined(DOMDOC_SUPPORT)
6625     if (dom) {
6626       strcpy(g->Message, "MS-DOM not supported by this version");
6627       xsup= NULL;
6628       } // endif DomDoc
6629 #endif   // !DOMDOC_SUPPORT
6630 
6631 #if !defined(LIBXML2_SUPPORT)
6632     if (!dom) {
6633       strcpy(g->Message, "libxml2 not supported by this version");
6634       xsup= NULL;
6635       } // endif Libxml2
6636 #endif   // !LIBXML2_SUPPORT
6637 
6638     if (!xsup) {
6639       my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6640       rc= HA_ERR_INTERNAL_ERROR;
6641       DBUG_RETURN(rc);
6642       } // endif xsup
6643 
6644     } // endif type
6645 
6646   if (type == TAB_JSON) {
6647     int pretty= atoi(GetListOption(g, "Pretty", options->oplist, "2"));
6648 
6649     if (!options->lrecl && pretty != 2) {
6650       sprintf(g->Message, "LRECL must be specified for pretty=%d", pretty);
6651       my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6652       rc= HA_ERR_INTERNAL_ERROR;
6653       DBUG_RETURN(rc);
6654       } // endif lrecl
6655 
6656     } // endif type	JSON
6657 
6658 	if (type == TAB_CSV) {
6659 		const char *sep= options->separator;
6660 
6661 		if (sep && strlen(sep) > 1) {
6662 			sprintf(g->Message, "Invalid separator %s", sep);
6663 			my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6664 			rc= HA_ERR_INTERNAL_ERROR;
6665 			DBUG_RETURN(rc);
6666 		} // endif sep
6667 
6668 	} // endif type	CSV
6669 
6670   // Check column types
6671   for (field= table_arg->field; *field; field++) {
6672     fp= *field;
6673 
6674     if (fp->vcol_info && !fp->stored_in_db)
6675       continue;            // This is a virtual column
6676 
6677     if (fp->flags & AUTO_INCREMENT_FLAG) {
6678       strcpy(g->Message, "Auto_increment is not supported yet");
6679       my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6680       rc= HA_ERR_INTERNAL_ERROR;
6681       DBUG_RETURN(rc);
6682       } // endif flags
6683 
6684     if (fp->flags & (BLOB_FLAG | ENUM_FLAG | SET_FLAG)) {
6685       sprintf(g->Message, "Unsupported type for column %s",
6686                           fp->field_name.str);
6687       my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6688       rc= HA_ERR_INTERNAL_ERROR;
6689       DBUG_RETURN(rc);
6690       } // endif flags
6691 
6692     if (type == TAB_VIR)
6693       if (!fp->option_struct || !fp->option_struct->special) {
6694         strcpy(g->Message, "Virtual tables accept only special or virtual columns");
6695         my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6696         rc= HA_ERR_INTERNAL_ERROR;
6697         DBUG_RETURN(rc);
6698         } // endif special
6699 
6700     switch (fp->type()) {
6701       case MYSQL_TYPE_SHORT:
6702       case MYSQL_TYPE_LONG:
6703       case MYSQL_TYPE_FLOAT:
6704       case MYSQL_TYPE_DOUBLE:
6705       case MYSQL_TYPE_TIMESTAMP:
6706       case MYSQL_TYPE_DATE:
6707       case MYSQL_TYPE_TIME:
6708       case MYSQL_TYPE_DATETIME:
6709       case MYSQL_TYPE_YEAR:
6710       case MYSQL_TYPE_NEWDATE:
6711       case MYSQL_TYPE_LONGLONG:
6712       case MYSQL_TYPE_TINY:
6713       case MYSQL_TYPE_DECIMAL:
6714       case MYSQL_TYPE_NEWDECIMAL:
6715       case MYSQL_TYPE_INT24:
6716         break;                     // Ok
6717       case MYSQL_TYPE_VARCHAR:
6718       case MYSQL_TYPE_VAR_STRING:
6719       case MYSQL_TYPE_STRING:
6720 #if 0
6721         if (!fp->field_length) {
6722           sprintf(g->Message, "Unsupported 0 length for column %s",
6723                               fp->field_name.str);
6724           rc= HA_ERR_INTERNAL_ERROR;
6725           my_printf_error(ER_UNKNOWN_ERROR,
6726                           "Unsupported 0 length for column %s",
6727                           MYF(0), fp->field_name.str);
6728           DBUG_RETURN(rc);
6729           } // endif fp
6730 #endif // 0
6731         break;                     // To be checked
6732       case MYSQL_TYPE_BIT:
6733       case MYSQL_TYPE_NULL:
6734       case MYSQL_TYPE_ENUM:
6735       case MYSQL_TYPE_SET:
6736       case MYSQL_TYPE_TINY_BLOB:
6737       case MYSQL_TYPE_MEDIUM_BLOB:
6738       case MYSQL_TYPE_LONG_BLOB:
6739       case MYSQL_TYPE_BLOB:
6740       case MYSQL_TYPE_GEOMETRY:
6741       default:
6742 //      fprintf(stderr, "Unsupported type column %s\n", fp->field_name.str);
6743         sprintf(g->Message, "Unsupported type for column %s",
6744                             fp->field_name.str);
6745         rc= HA_ERR_INTERNAL_ERROR;
6746         my_printf_error(ER_UNKNOWN_ERROR, "Unsupported type for column %s",
6747                         MYF(0), fp->field_name.str);
6748         DBUG_RETURN(rc);
6749         break;
6750       } // endswitch type
6751 
6752     if ((fp)->real_maybe_null() && !IsTypeNullable(type)) {
6753       my_printf_error(ER_UNKNOWN_ERROR,
6754                       "Table type %s does not support nullable columns",
6755                       MYF(0), options->type);
6756       DBUG_RETURN(HA_ERR_UNSUPPORTED);
6757       } // endif !nullable
6758 
6759     if (dbf) {
6760       bool b= false;
6761 
6762       if ((b= fp->field_name.length > 10))
6763         sprintf(g->Message, "DBF: Column name '%s' is too long (max=10)",
6764                             fp->field_name.str);
6765       else if ((b= fp->field_length > 255))
6766         sprintf(g->Message, "DBF: Column length too big for '%s' (max=255)",
6767                             fp->field_name.str);
6768 
6769       if (b) {
6770         my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6771         rc= HA_ERR_INTERNAL_ERROR;
6772         DBUG_RETURN(rc);
6773         } // endif b
6774 
6775       } // endif dbf
6776 
6777     } // endfor field
6778 
6779   if ((sqlcom == SQLCOM_CREATE_TABLE || *GetTableName() == '#') && inward) {
6780     // The file name is not specified, create a default file in
6781     // the database directory named table_name.table_type.
6782     // (temporarily not done for XML because a void file causes
6783     // the XML parsers to report an error on the first Insert)
6784     char buf[_MAX_PATH], fn[_MAX_PATH], dbpath[_MAX_PATH], lwt[12];
6785     int  h;
6786 
6787     // Check for incompatible options
6788     if (options->sepindex) {
6789       my_message(ER_UNKNOWN_ERROR,
6790             "SEPINDEX is incompatible with unspecified file name", MYF(0));
6791       DBUG_RETURN(HA_ERR_UNSUPPORTED);
6792 		} else if (GetTypeID(options->type) == TAB_VEC) {
6793 			if (!table->s->max_rows || options->split) {
6794 				my_printf_error(ER_UNKNOWN_ERROR,
6795 					"%s tables whose file name is unspecified cannot be split",
6796 					MYF(0), options->type);
6797 				DBUG_RETURN(HA_ERR_UNSUPPORTED);
6798 			} else if (options->header == 2) {
6799 				my_printf_error(ER_UNKNOWN_ERROR,
6800 					"header=2 is not allowed for %s tables whose file name is unspecified",
6801 					MYF(0), options->type);
6802 				DBUG_RETURN(HA_ERR_UNSUPPORTED);
6803 			} // endif's
6804 
6805 		} else if (options->zipped) {
6806 			my_message(ER_UNKNOWN_ERROR,
6807 				"ZIPPED is incompatible with unspecified file name", MYF(0));
6808 			DBUG_RETURN(HA_ERR_UNSUPPORTED);
6809 		}	// endif's options
6810 
6811     // Fold type to lower case
6812     for (int i= 0; i < 12; i++)
6813       if (!options->type[i]) {
6814         lwt[i]= 0;
6815         break;
6816       } else
6817         lwt[i]= tolower(options->type[i]);
6818 
6819     if (part_info) {
6820       char *p;
6821 
6822       strcpy(dbpath, name);
6823       p= strrchr(dbpath, slash);
6824       strncpy(partname, ++p, sizeof(partname) - 1);
6825       strcat(strcat(strcpy(buf, p), "."), lwt);
6826       *p= 0;
6827     } else {
6828       strcat(strcat(strcpy(buf, GetTableName()), "."), lwt);
6829       sprintf(g->Message, "No file name. Table will use %s", buf);
6830 
6831       if (sqlcom == SQLCOM_CREATE_TABLE)
6832         push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
6833 
6834       strcat(strcat(strcpy(dbpath, "./"), table->s->db.str), "/");
6835     } // endif part_info
6836 
6837     PlugSetPath(fn, buf, dbpath);
6838 
6839     if ((h= ::open(fn, O_CREAT | O_EXCL, 0666)) == -1) {
6840       if (errno == EEXIST)
6841         sprintf(g->Message, "Default file %s already exists", fn);
6842       else
6843         sprintf(g->Message, "Error %d creating file %s", errno, fn);
6844 
6845       push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
6846     } else
6847       ::close(h);
6848 
6849     if ((type == TAB_FMT || options->readonly) && sqlcom == SQLCOM_CREATE_TABLE)
6850       push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0,
6851         "Congratulation, you just created a read-only void table!");
6852 
6853     } // endif sqlcom
6854 
6855   if (trace(1))
6856     htrc("xchk=%p createas=%d\n", g->Xchk, g->Createas);
6857 
6858 	if (options->zipped) {
6859 #if defined(ZIP_SUPPORT)
6860 		// Check whether the zip entry must be made from a file
6861 		PCSZ fn= GetListOption(g, "Load", options->oplist, NULL);
6862 
6863 		if (fn) {
6864 			char zbuf[_MAX_PATH], buf[_MAX_PATH], dbpath[_MAX_PATH];
6865 			PCSZ entry= GetListOption(g, "Entry", options->oplist, NULL);
6866 			PCSZ a= GetListOption(g, "Append", options->oplist, "NO");
6867 			bool append= *a == '1' || *a == 'Y' || *a == 'y' || !stricmp(a, "ON");
6868 			PCSZ m= GetListOption(g, "Mulentries", options->oplist, "NO");
6869 			bool mul= *m == '1' || *m == 'Y' || *m == 'y' || !stricmp(m, "ON");
6870 
6871 			strcat(strcat(strcpy(dbpath, "./"), table->s->db.str), "/");
6872 			PlugSetPath(zbuf, options->filename, dbpath);
6873 			PlugSetPath(buf, fn, dbpath);
6874 
6875 			if (ZipLoadFile(g, zbuf, buf, entry, append, mul)) {
6876 				my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6877 				DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
6878 			}	// endif LoadFile
6879 
6880 		}	// endif fn
6881 #else   // !ZIP_SUPPORT
6882 		my_message(ER_UNKNOWN_ERROR, "Option ZIP not supported", MYF(0));
6883 		DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
6884 #endif  // !ZIP_SUPPORT
6885 	}	// endif zipped
6886 
6887   // To check whether indexes have to be made or remade
6888   if (!g->Xchk) {
6889     PIXDEF xdp;
6890 
6891     // We should be in CREATE TABLE, ALTER_TABLE or CREATE INDEX
6892     if (!(sqlcom == SQLCOM_CREATE_TABLE || sqlcom == SQLCOM_ALTER_TABLE ||
6893           sqlcom == SQLCOM_CREATE_INDEX || sqlcom == SQLCOM_DROP_INDEX))
6894 //         (sqlcom == SQLCOM_CREATE_INDEX && part_info) ||
6895 //         (sqlcom == SQLCOM_DROP_INDEX && part_info)))
6896       push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0,
6897         "Unexpected command in create, please contact CONNECT team");
6898 
6899     if (part_info && !inward)
6900       strncpy(partname, decode(g, strrchr(name, '#') + 1), sizeof(partname) - 1);
6901 //    strcpy(partname, part_info->curr_part_elem->partition_name);
6902 
6903     if (g->Alchecked == 0 &&
6904         (!IsFileType(type) || FileExists(options->filename, false))) {
6905       if (part_info) {
6906         sprintf(g->Message, "Data repartition in %s is unchecked", partname);
6907         push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
6908       } else if (sqlcom == SQLCOM_ALTER_TABLE) {
6909         // This is an ALTER to CONNECT from another engine.
6910         // It cannot be accepted because the table data would be modified
6911         // except when the target file does not exist.
6912         strcpy(g->Message, "Operation denied. Table data would be modified.");
6913         my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6914         DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
6915       } // endif part_info
6916 
6917       } // endif outward
6918 
6919     // Get the index definitions
6920     if ((xdp= GetIndexInfo()) || sqlcom == SQLCOM_DROP_INDEX) {
6921       if (options->multiple) {
6922         strcpy(g->Message, "Multiple tables are not indexable");
6923         my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6924         rc= HA_ERR_UNSUPPORTED;
6925       } else if (options->compressed) {
6926         strcpy(g->Message, "Compressed tables are not indexable");
6927         my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6928         rc= HA_ERR_UNSUPPORTED;
6929       } else if (GetIndexType(type) == 1) {
6930         PDBUSER dup= PlgGetUser(g);
6931         PCATLG  cat= (dup) ? dup->Catalog : NULL;
6932 
6933 				if (SetDataPath(g, table_arg->s->db.str)) {
6934 					my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6935 					rc= HA_ERR_INTERNAL_ERROR;
6936 				} else if (cat) {
6937           if (part_info)
6938             strncpy(partname,
6939                     decode(g, strrchr(name, (inward ? slash : '#')) + 1),
6940 										sizeof(partname) - 1);
6941 
6942           if ((rc= optimize(table->in_use, NULL))) {
6943             htrc("Create rc=%d %s\n", rc, g->Message);
6944             my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6945             rc= HA_ERR_INTERNAL_ERROR;
6946           } else
6947             CloseTable(g);
6948 
6949           } // endif cat
6950 
6951       } else if (GetIndexType(type) == 3) {
6952         if (CheckVirtualIndex(table_arg->s)) {
6953           my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6954           rc= HA_ERR_UNSUPPORTED;
6955           } // endif Check
6956 
6957       } else if (!GetIndexType(type)) {
6958         sprintf(g->Message, "Table type %s is not indexable", options->type);
6959         my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
6960         rc= HA_ERR_UNSUPPORTED;
6961       } // endif index type
6962 
6963       } // endif xdp
6964 
6965   } else {
6966     // This should not happen anymore with indexing new way
6967     my_message(ER_UNKNOWN_ERROR,
6968                "CONNECT index modification should be in-place", MYF(0));
6969     DBUG_RETURN(HA_ERR_UNSUPPORTED);
6970   } // endif Xchk
6971 
6972   table= st;
6973   DBUG_RETURN(rc);
6974 } // end of create
6975 
6976 /**
6977   Used to check whether a file based outward table can be populated by
6978   an ALTER TABLE command. The conditions are:
6979   - file does not exist or is void
6980   - user has file privilege
6981 */
6982 bool ha_connect::FileExists(const char *fn, bool bf)
6983 {
6984   if (!fn || !*fn)
6985     return false;
6986   else if (IsPartitioned() && bf)
6987     return true;
6988 
6989   if (table) {
6990     const char *s;
6991 		char  tfn[_MAX_PATH], filename[_MAX_PATH], path[_MAX_PATH];
6992 		bool  b= false;
6993     int   n;
6994     struct stat info;
6995 
6996 #if defined(_WIN32)
6997     s= "\\";
6998 #else   // !_WIN32
6999     s= "/";
7000 #endif  // !_WIN32
7001     if (IsPartitioned()) {
7002       sprintf(tfn, fn, GetPartName());
7003 
7004       // This is to avoid an initialization error raised by the
7005       // test on check_table_flags made in ha_partition::open
7006       // that can fail if some partition files are empty.
7007       b= true;
7008     } else
7009       strcpy(tfn, fn);
7010 
7011     strcat(strcat(strcat(strcpy(path, "."), s), table->s->db.str), s);
7012     PlugSetPath(filename, tfn, path);
7013     n= stat(filename, &info);
7014 
7015     if (n < 0) {
7016       if (errno != ENOENT) {
7017         char buf[_MAX_PATH + 20];
7018 
7019         sprintf(buf, "Error %d for file %s", errno, filename);
7020         push_warning(table->in_use, Sql_condition::WARN_LEVEL_WARN, 0, buf);
7021         return true;
7022       } else
7023         return false;
7024 
7025     } else
7026       return (info.st_size || b) ? true : false;
7027 
7028     } // endif table
7029 
7030   return true;
7031 } // end of FileExists
7032 
7033 // Called by SameString and NoFieldOptionChange
7034 bool ha_connect::CheckString(PCSZ str1, PCSZ str2)
7035 {
7036   bool  b1= (!str1 || !*str1), b2= (!str2 || !*str2);
7037 
7038   if (b1 && b2)
7039     return true;
7040   else if ((b1 && !b2) || (!b1 && b2) || stricmp(str1, str2))
7041     return false;
7042 
7043   return true;
7044 } // end of CheckString
7045 
7046 /**
7047   check whether a string option have changed
7048   */
7049 bool ha_connect::SameString(TABLE *tab, PCSZ opn)
7050 {
7051   PCSZ str1, str2;
7052 
7053   tshp= tab->s;                 // The altered table
7054   str1= GetStringOption(opn);
7055   tshp= NULL;
7056   str2= GetStringOption(opn);
7057   return CheckString(str1, str2);
7058 } // end of SameString
7059 
7060 /**
7061   check whether a Boolean option have changed
7062   */
7063 bool ha_connect::SameBool(TABLE *tab, PCSZ opn)
7064 {
7065   bool b1, b2;
7066 
7067   tshp= tab->s;                 // The altered table
7068   b1= GetBooleanOption(opn, false);
7069   tshp= NULL;
7070   b2= GetBooleanOption(opn, false);
7071   return (b1 == b2);
7072 } // end of SameBool
7073 
7074 /**
7075   check whether an integer option have changed
7076   */
7077 bool ha_connect::SameInt(TABLE *tab, PCSZ opn)
7078 {
7079   int i1, i2;
7080 
7081   tshp= tab->s;                 // The altered table
7082   i1= GetIntegerOption(opn);
7083   tshp= NULL;
7084   i2= GetIntegerOption(opn);
7085 
7086   if (!stricmp(opn, "lrecl"))
7087     return (i1 == i2 || !i1 || !i2);
7088   else if (!stricmp(opn, "ending"))
7089     return (i1 == i2 || i1 <= 0 || i2 <= 0);
7090   else
7091     return (i1 == i2);
7092 
7093 } // end of SameInt
7094 
7095 /**
7096   check whether a field option have changed
7097   */
7098 bool ha_connect::NoFieldOptionChange(TABLE *tab)
7099 {
7100   bool rc= true;
7101   ha_field_option_struct *fop1, *fop2;
7102   Field* *fld1= table->s->field;
7103   Field* *fld2= tab->s->field;
7104 
7105   for (; rc && *fld1 && *fld2; fld1++, fld2++) {
7106     fop1= (*fld1)->option_struct;
7107     fop2= (*fld2)->option_struct;
7108 
7109     rc= (fop1->offset == fop2->offset &&
7110          fop1->fldlen == fop2->fldlen &&
7111          CheckString(fop1->dateformat, fop2->dateformat) &&
7112          CheckString(fop1->fieldformat, fop2->fieldformat) &&
7113 			   CheckString(fop1->special, fop2->special));
7114     } // endfor fld
7115 
7116   return rc;
7117 } // end of NoFieldOptionChange
7118 
7119  /**
7120     Check if a storage engine supports a particular alter table in-place
7121 
7122     @param    altered_table     TABLE object for new version of table.
7123     @param    ha_alter_info     Structure describing changes to be done
7124                                 by ALTER TABLE and holding data used
7125                                 during in-place alter.
7126 
7127     @retval   HA_ALTER_ERROR                  Unexpected error.
7128     @retval   HA_ALTER_INPLACE_NOT_SUPPORTED  Not supported, must use copy.
7129     @retval   HA_ALTER_INPLACE_EXCLUSIVE_LOCK Supported, but requires X lock.
7130     @retval   HA_ALTER_INPLACE_COPY_LOCK
7131                                               Supported, but requires SNW lock
7132                                               during main phase. Prepare phase
7133                                               requires X lock.
7134     @retval   HA_ALTER_INPLACE_SHARED_LOCK    Supported, but requires SNW lock.
7135     @retval   HA_ALTER_INPLACE_COPY_NO_LOCK
7136                                               Supported, concurrent reads/writes
7137                                               allowed. However, prepare phase
7138                                               requires X lock.
7139     @retval   HA_ALTER_INPLACE_NO_LOCK        Supported, concurrent
7140                                               reads/writes allowed.
7141 
7142     @note The default implementation uses the old in-place ALTER API
7143     to determine if the storage engine supports in-place ALTER or not.
7144 
7145     @note Called without holding thr_lock.c lock.
7146  */
7147 enum_alter_inplace_result
7148 ha_connect::check_if_supported_inplace_alter(TABLE *altered_table,
7149                                 Alter_inplace_info *ha_alter_info)
7150 {
7151   DBUG_ENTER("check_if_supported_alter");
7152 
7153   bool            idx= false, outward= false;
7154   THD            *thd= ha_thd();
7155   int             sqlcom= thd_sql_command(thd);
7156   TABTYPE         newtyp, type= TAB_UNDEF;
7157   HA_CREATE_INFO *create_info= ha_alter_info->create_info;
7158   PTOS            newopt, oldopt;
7159   xp= GetUser(thd, xp);
7160   PGLOBAL         g= xp->g;
7161 
7162   if (!g || !table) {
7163     my_message(ER_UNKNOWN_ERROR, "Cannot check ALTER operations", MYF(0));
7164     DBUG_RETURN(HA_ALTER_ERROR);
7165     } // endif Xchk
7166 
7167   newopt= altered_table->s->option_struct;
7168   oldopt= table->s->option_struct;
7169 
7170   // If this is the start of a new query, cleanup the previous one
7171   if (xp->CheckCleanup()) {
7172     tdbp= NULL;
7173     valid_info= false;
7174     } // endif CheckCleanup
7175 
7176   g->Alchecked= 1;       // Tested in create
7177   g->Xchk= NULL;
7178   type= GetRealType(oldopt);
7179   newtyp= GetRealType(newopt);
7180 
7181   // No copy algorithm for outward tables
7182   outward= (!IsFileType(type) || (oldopt->filename && *oldopt->filename));
7183 
7184   // Index operations
7185   alter_table_operations index_operations=
7186     ALTER_ADD_INDEX |
7187     ALTER_DROP_INDEX |
7188     ALTER_ADD_NON_UNIQUE_NON_PRIM_INDEX |
7189     ALTER_DROP_NON_UNIQUE_NON_PRIM_INDEX |
7190     ALTER_ADD_UNIQUE_INDEX |
7191     ALTER_DROP_UNIQUE_INDEX |
7192     ALTER_ADD_PK_INDEX |
7193     ALTER_DROP_PK_INDEX;
7194 
7195   alter_table_operations inplace_offline_operations=
7196     ALTER_COLUMN_EQUAL_PACK_LENGTH |
7197     ALTER_COLUMN_NAME |
7198     ALTER_COLUMN_DEFAULT |
7199     ALTER_CHANGE_CREATE_OPTION |
7200     ALTER_RENAME |
7201     ALTER_PARTITIONED | index_operations;
7202 
7203   if (ha_alter_info->handler_flags & index_operations ||
7204       !SameString(altered_table, "optname") ||
7205       !SameBool(altered_table, "sepindex")) {
7206     if (newopt->multiple) {
7207       strcpy(g->Message, "Multiple tables are not indexable");
7208       my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
7209       DBUG_RETURN(HA_ALTER_ERROR);
7210     } else if (newopt->compressed) {
7211       strcpy(g->Message, "Compressed tables are not indexable");
7212       my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
7213       DBUG_RETURN(HA_ALTER_ERROR);
7214     } else if (GetIndexType(type) == 1) {
7215       g->Xchk= new(g) XCHK;
7216       PCHK xcp= (PCHK)g->Xchk;
7217 
7218       xcp->oldpix= GetIndexInfo(table->s);
7219       xcp->newpix= GetIndexInfo(altered_table->s);
7220       xcp->oldsep= GetBooleanOption("sepindex", false);
7221       xcp->oldsep= xcp->SetName(g, GetStringOption("optname"));
7222       tshp= altered_table->s;
7223       xcp->newsep= GetBooleanOption("sepindex", false);
7224       xcp->newsep= xcp->SetName(g, GetStringOption("optname"));
7225       tshp= NULL;
7226 
7227       if (trace(1) && g->Xchk)
7228         htrc(
7229           "oldsep=%d newsep=%d oldopn=%s newopn=%s oldpix=%p newpix=%p\n",
7230                 xcp->oldsep, xcp->newsep,
7231                 SVP(xcp->oldopn), SVP(xcp->newopn),
7232                 xcp->oldpix, xcp->newpix);
7233 
7234       if (sqlcom == SQLCOM_ALTER_TABLE)
7235         idx= true;
7236       else
7237         DBUG_RETURN(HA_ALTER_INPLACE_EXCLUSIVE_LOCK);
7238 
7239     } else if (GetIndexType(type) == 3) {
7240       if (CheckVirtualIndex(altered_table->s)) {
7241         my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
7242         DBUG_RETURN(HA_ALTER_ERROR);
7243         } // endif Check
7244 
7245     } else if (!GetIndexType(type)) {
7246       sprintf(g->Message, "Table type %s is not indexable", oldopt->type);
7247       my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
7248       DBUG_RETURN(HA_ALTER_ERROR);
7249     } // endif index type
7250 
7251     } // endif index operation
7252 
7253   if (!SameString(altered_table, "filename")) {
7254     if (!outward) {
7255       // Conversion to outward table is only allowed for file based
7256       // tables whose file does not exist.
7257       tshp= altered_table->s;
7258 			PCSZ fn= GetStringOption("filename");
7259       tshp= NULL;
7260 
7261       if (FileExists(fn, false)) {
7262         strcpy(g->Message, "Operation denied. Table data would be lost.");
7263         my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
7264         DBUG_RETURN(HA_ALTER_ERROR);
7265       } else
7266         goto fin;
7267 
7268     } else
7269       goto fin;
7270 
7271     } // endif filename
7272 
7273   /* Is there at least one operation that requires copy algorithm? */
7274   if (ha_alter_info->handler_flags & ~inplace_offline_operations)
7275     goto fin;
7276 
7277   /*
7278     ALTER TABLE tbl_name CONVERT TO CHARACTER SET .. and
7279     ALTER TABLE table_name DEFAULT CHARSET= .. most likely
7280     change column charsets and so not supported in-place through
7281     old API.
7282 
7283     Changing of PACK_KEYS, MAX_ROWS and ROW_FORMAT options were
7284     not supported as in-place operations in old API either.
7285   */
7286   if (create_info->used_fields & (HA_CREATE_USED_CHARSET |
7287                                   HA_CREATE_USED_DEFAULT_CHARSET |
7288                                   HA_CREATE_USED_PACK_KEYS |
7289                                   HA_CREATE_USED_MAX_ROWS) ||
7290       (table->s->row_type != create_info->row_type))
7291     goto fin;
7292 
7293 #if 0
7294   uint table_changes= (ha_alter_info->handler_flags &
7295                        ALTER_COLUMN_EQUAL_PACK_LENGTH) ?
7296     IS_EQUAL_PACK_LENGTH : IS_EQUAL_YES;
7297 
7298   if (table->file->check_if_incompatible_data(create_info, table_changes)
7299       == COMPATIBLE_DATA_YES)
7300     DBUG_RETURN(HA_ALTER_INPLACE_EXCLUSIVE_LOCK);
7301 #endif // 0
7302 
7303   // This was in check_if_incompatible_data
7304   if (NoFieldOptionChange(altered_table) &&
7305       type == newtyp &&
7306       SameInt(altered_table, "lrecl") &&
7307       SameInt(altered_table, "elements") &&
7308       SameInt(altered_table, "header") &&
7309       SameInt(altered_table, "quoted") &&
7310       SameInt(altered_table, "ending") &&
7311       SameInt(altered_table, "compressed"))
7312     DBUG_RETURN(HA_ALTER_INPLACE_EXCLUSIVE_LOCK);
7313 
7314 fin:
7315   if (idx) {
7316     // Indexing is only supported inplace
7317     my_message(ER_ALTER_OPERATION_NOT_SUPPORTED,
7318       "Alter operations not supported together by CONNECT", MYF(0));
7319     DBUG_RETURN(HA_ALTER_ERROR);
7320   } else if (outward) {
7321     if (IsFileType(type))
7322       push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0,
7323         "This is an outward table, table data were not modified.");
7324 
7325     DBUG_RETURN(HA_ALTER_INPLACE_EXCLUSIVE_LOCK);
7326   } else
7327     DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
7328 
7329 } // end of check_if_supported_inplace_alter
7330 
7331 
7332 /**
7333   check_if_incompatible_data() called if ALTER TABLE can't detect otherwise
7334   if new and old definition are compatible
7335 
7336   @details If there are no other explicit signs like changed number of
7337   fields this function will be called by compare_tables()
7338   (sql/sql_tables.cc) to decide should we rewrite whole table or only .frm
7339   file.
7340 
7341   @note: This function is no more called by check_if_supported_inplace_alter
7342 */
7343 
7344 bool ha_connect::check_if_incompatible_data(HA_CREATE_INFO *, uint)
7345 {
7346   DBUG_ENTER("ha_connect::check_if_incompatible_data");
7347   // TO DO: really implement and check it.
7348   push_warning(ha_thd(), Sql_condition::WARN_LEVEL_WARN, 0,
7349       "Unexpected call to check_if_incompatible_data.");
7350   DBUG_RETURN(COMPATIBLE_DATA_NO);
7351 } // end of check_if_incompatible_data
7352 
7353 /****************************************************************************
7354  * CONNECT MRR implementation: use DS-MRR
7355    This is just copied from myisam
7356  ***************************************************************************/
7357 
7358 int ha_connect::multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
7359                                      uint n_ranges, uint mode,
7360                                      HANDLER_BUFFER *buf)
7361 {
7362   return ds_mrr.dsmrr_init(this, seq, seq_init_param, n_ranges, mode, buf);
7363 } // end of multi_range_read_init
7364 
7365 int ha_connect::multi_range_read_next(range_id_t *range_info)
7366 {
7367   return ds_mrr.dsmrr_next(range_info);
7368 } // end of multi_range_read_next
7369 
7370 ha_rows ha_connect::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
7371                                                void *seq_init_param,
7372                                                uint n_ranges, uint *bufsz,
7373                                                uint *flags, Cost_estimate *cost)
7374 {
7375   /*
7376     This call is here because there is no location where this->table would
7377     already be known.
7378     TODO: consider moving it into some per-query initialization call.
7379   */
7380   ds_mrr.init(this, table);
7381 
7382   // MMR is implemented for "local" file based tables only
7383   if (!IsFileType(GetRealType(GetTableOptionStruct())))
7384     *flags|= HA_MRR_USE_DEFAULT_IMPL;
7385 
7386   ha_rows rows= ds_mrr.dsmrr_info_const(keyno, seq, seq_init_param, n_ranges,
7387                                         bufsz, flags, cost);
7388   xp->g->Mrr= !(*flags & HA_MRR_USE_DEFAULT_IMPL);
7389   return rows;
7390 } // end of multi_range_read_info_const
7391 
7392 ha_rows ha_connect::multi_range_read_info(uint keyno, uint n_ranges, uint keys,
7393                                          uint key_parts, uint *bufsz,
7394                                          uint *flags, Cost_estimate *cost)
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(keyno, n_ranges, keys, key_parts, bufsz,
7403                                   flags, cost);
7404   xp->g->Mrr= !(*flags & HA_MRR_USE_DEFAULT_IMPL);
7405   return rows;
7406 } // end of multi_range_read_info
7407 
7408 
7409 int ha_connect::multi_range_read_explain_info(uint mrr_mode, char *str,
7410                                              size_t size)
7411 {
7412   return ds_mrr.dsmrr_explain_info(mrr_mode, str, size);
7413 } // end of multi_range_read_explain_info
7414 
7415 /* CONNECT MRR implementation ends */
7416 
7417 #if 0
7418 // Does this make sens for CONNECT?
7419 Item *ha_connect::idx_cond_push(uint keyno_arg, Item* idx_cond_arg)
7420 {
7421   pushed_idx_cond_keyno= keyno_arg;
7422   pushed_idx_cond= idx_cond_arg;
7423   in_range_check_pushed_down= TRUE;
7424   if (active_index == pushed_idx_cond_keyno)
7425     mi_set_index_cond_func(file, handler_index_cond_check, this);
7426   return NULL;
7427 }
7428 #endif // 0
7429 
7430 
7431 struct st_mysql_storage_engine connect_storage_engine=
7432 { MYSQL_HANDLERTON_INTERFACE_VERSION };
7433 
7434 /***********************************************************************/
7435 /*  CONNECT global variables definitions.                              */
7436 /***********************************************************************/
7437 #if defined(XMAP)
7438 // Using file mapping for indexes if true
7439 static MYSQL_SYSVAR_BOOL(indx_map, xmap, PLUGIN_VAR_RQCMDARG,
7440        "Using file mapping for indexes", NULL, NULL, 0);
7441 #endif   // XMAP
7442 
7443 #if defined(XMSG)
7444 static MYSQL_SYSVAR_STR(errmsg_dir_path, msg_path,
7445 //     PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
7446        PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
7447        "Path to the directory where are the message files",
7448 //     check_msg_path, update_msg_path,
7449        NULL, NULL,
7450        "../../../../storage/connect/");     // for testing
7451 #endif   // XMSG
7452 
7453 #if defined(JAVA_SUPPORT)
7454 static MYSQL_SYSVAR_STR(jvm_path, JvmPath,
7455 	PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
7456 	"Path to the directory where is the JVM lib",
7457 	//     check_jvm_path, update_jvm_path,
7458 	NULL, NULL,	NULL);
7459 
7460 static MYSQL_SYSVAR_STR(class_path, ClassPath,
7461 	PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
7462 	"Java class path",
7463 	//     check_class_path, update_class_path,
7464 	NULL, NULL, NULL);
7465 #endif   // JAVA_SUPPORT
7466 
7467 
7468 static struct st_mysql_sys_var* connect_system_variables[]= {
7469   MYSQL_SYSVAR(xtrace),
7470   MYSQL_SYSVAR(conv_size),
7471   MYSQL_SYSVAR(type_conv),
7472 #if defined(XMAP)
7473   MYSQL_SYSVAR(indx_map),
7474 #endif   // XMAP
7475   MYSQL_SYSVAR(work_size),
7476   MYSQL_SYSVAR(use_tempfile),
7477   MYSQL_SYSVAR(exact_info),
7478 #if defined(XMSG) || defined(NEWMSG)
7479   MYSQL_SYSVAR(msg_lang),
7480 #endif   // XMSG || NEWMSG
7481 #if defined(XMSG)
7482   MYSQL_SYSVAR(errmsg_dir_path),
7483 #endif   // XMSG
7484 	MYSQL_SYSVAR(json_null),
7485 	MYSQL_SYSVAR(json_all_path),
7486 	MYSQL_SYSVAR(default_depth),
7487   MYSQL_SYSVAR(default_prec),
7488   MYSQL_SYSVAR(json_grp_size),
7489 #if defined(JAVA_SUPPORT)
7490 	MYSQL_SYSVAR(jvm_path),
7491 	MYSQL_SYSVAR(class_path),
7492 	MYSQL_SYSVAR(java_wrapper),
7493 #endif   // JAVA_SUPPORT
7494 #if defined(JAVA_SUPPORT) || defined(CMGO_SUPPORT)
7495 	MYSQL_SYSVAR(enable_mongo),
7496 #endif   // JAVA_SUPPORT || CMGO_SUPPORT
7497 	MYSQL_SYSVAR(cond_push),
7498 #if defined(BSON_SUPPORT)
7499   MYSQL_SYSVAR(force_bson),
7500 #endif   // BSON_SUPPORT
7501   NULL
7502 };
7503 
7504 maria_declare_plugin(connect)
7505 {
7506   MYSQL_STORAGE_ENGINE_PLUGIN,
7507   &connect_storage_engine,
7508   "CONNECT",
7509   "Olivier Bertrand",
7510   "Management of External Data (SQL/NOSQL/MED), including Rest query results",
7511   PLUGIN_LICENSE_GPL,
7512   connect_init_func,                            /* Plugin Init */
7513   connect_done_func,                            /* Plugin Deinit */
7514   0x0107,                                       /* version number (1.07) */
7515   NULL,                                         /* status variables */
7516   connect_system_variables,                     /* system variables */
7517   "1.07.0003",                                  /* string version */
7518 	MariaDB_PLUGIN_MATURITY_STABLE                /* maturity */
7519 }
7520 maria_declare_plugin_end;
7521