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