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