1 /* Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved.
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, version 2.0,
5   as published by the Free Software Foundation.
6 
7   This program is also distributed with certain software (including
8   but not limited to OpenSSL) that is licensed under separate terms,
9   as designated in a particular file or component or in included license
10   documentation.  The authors of MySQL hereby grant you an additional
11   permission to link the program and your derivative works with the
12   separately licensed software that they have included with MySQL.
13 
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License, version 2.0, for more details.
18 
19   You should have received a copy of the GNU General Public License
20   along with this program; if not, write to the Free Software
21   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 /**
24   @file ha_example.cc
25 
26   @brief
27   The ha_example engine is a stubbed storage engine for example purposes only;
28   it does nothing at this point. Its purpose is to provide a source
29   code illustration of how to begin writing new storage engines; see also
30   /storage/example/ha_example.h.
31 
32   @details
33   ha_example will let you create/open/delete tables, but
34   nothing further (for example, indexes are not supported nor can data
35   be stored in the table). Use this example as a template for
36   implementing the same functionality in your own storage engine. You
37   can enable the example storage engine in your build by doing the
38   following during your build process:<br> ./configure
39   --with-example-storage-engine
40 
41   Once this is done, MySQL will let you create tables with:<br>
42   CREATE TABLE \<table name\> (...) ENGINE=EXAMPLE;
43 
44   The example storage engine is set up to use table locks. It
45   implements an example "SHARE" that is inserted into a hash by table
46   name. You can use this to store information of state that any
47   example handler object will be able to see when it is using that
48   table.
49 
50   Please read the object definition in ha_example.h before reading the rest
51   of this file.
52 
53   @note
54   When you create an EXAMPLE table, the MySQL Server creates a table .frm
55   (format) file in the database directory, using the table name as the file
56   name as is customary with MySQL. No other files are created. To get an idea
57   of what occurs, here is an example select that would do a scan of an entire
58   table:
59 
60   @code
61   ha_example::store_lock
62   ha_example::external_lock
63   ha_example::info
64   ha_example::rnd_init
65   ha_example::extra
66   ha_example::rnd_next
67   ha_example::rnd_next
68   ha_example::rnd_next
69   ha_example::rnd_next
70   ha_example::rnd_next
71   ha_example::rnd_next
72   ha_example::rnd_next
73   ha_example::rnd_next
74   ha_example::rnd_next
75   ha_example::extra
76   ha_example::external_lock
77   ha_example::extra
78   ENUM HA_EXTRA_RESET        Reset database to after open
79   @endcode
80 
81   Here you see that the example storage engine has 9 rows called before
82   rnd_next signals that it has reached the end of its data. Also note that
83   the table in question was already opened; had it not been open, a call to
84   ha_example::open() would also have been necessary. Calls to
85   ha_example::extra() are hints as to what will be occurring to the request.
86 
87   A Longer Example can be found called the "Skeleton Engine" which can be
88   found on TangentOrg. It has both an engine and a full build environment
89   for building a pluggable storage engine.
90 
91   Happy coding!<br>
92     -Brian
93 */
94 
95 #include "storage/example/ha_example.h"
96 
97 #include "my_dbug.h"
98 #include "mysql/plugin.h"
99 #include "sql/sql_class.h"
100 #include "sql/sql_plugin.h"
101 #include "typelib.h"
102 
103 static handler *example_create_handler(handlerton *hton, TABLE_SHARE *table,
104                                        bool partitioned, MEM_ROOT *mem_root);
105 
106 handlerton *example_hton;
107 
108 /* Interface to mysqld, to check system tables supported by SE */
109 static bool example_is_supported_system_table(const char *db,
110                                               const char *table_name,
111                                               bool is_sql_layer_system_table);
112 
Example_share()113 Example_share::Example_share() { thr_lock_init(&lock); }
114 
example_init_func(void * p)115 static int example_init_func(void *p) {
116   DBUG_TRACE;
117 
118   example_hton = (handlerton *)p;
119   example_hton->state = SHOW_OPTION_YES;
120   example_hton->create = example_create_handler;
121   example_hton->flags = HTON_CAN_RECREATE;
122   example_hton->is_supported_system_table = example_is_supported_system_table;
123 
124   return 0;
125 }
126 
127 /**
128   @brief
129   Example of simple lock controls. The "share" it creates is a
130   structure we will pass to each example handler. Do you have to have
131   one of these? Well, you have pieces that are used for locking, and
132   they are needed to function.
133 */
134 
get_share()135 Example_share *ha_example::get_share() {
136   Example_share *tmp_share;
137 
138   DBUG_TRACE;
139 
140   lock_shared_ha_data();
141   if (!(tmp_share = static_cast<Example_share *>(get_ha_share_ptr()))) {
142     tmp_share = new Example_share;
143     if (!tmp_share) goto err;
144 
145     set_ha_share_ptr(static_cast<Handler_share *>(tmp_share));
146   }
147 err:
148   unlock_shared_ha_data();
149   return tmp_share;
150 }
151 
example_create_handler(handlerton * hton,TABLE_SHARE * table,bool,MEM_ROOT * mem_root)152 static handler *example_create_handler(handlerton *hton, TABLE_SHARE *table,
153                                        bool, MEM_ROOT *mem_root) {
154   return new (mem_root) ha_example(hton, table);
155 }
156 
ha_example(handlerton * hton,TABLE_SHARE * table_arg)157 ha_example::ha_example(handlerton *hton, TABLE_SHARE *table_arg)
158     : handler(hton, table_arg) {}
159 
160 /*
161   List of all system tables specific to the SE.
162   Array element would look like below,
163      { "<database_name>", "<system table name>" },
164   The last element MUST be,
165      { (const char*)NULL, (const char*)NULL }
166 
167   This array is optional, so every SE need not implement it.
168 */
169 static st_handler_tablename ha_example_system_tables[] = {
170     {(const char *)nullptr, (const char *)nullptr}};
171 
172 /**
173   @brief Check if the given db.tablename is a system table for this SE.
174 
175   @param db                         Database name to check.
176   @param table_name                 table name to check.
177   @param is_sql_layer_system_table  if the supplied db.table_name is a SQL
178                                     layer system table.
179 
180   @retval true   Given db.table_name is supported system table.
181   @retval false  Given db.table_name is not a supported system table.
182 */
example_is_supported_system_table(const char * db,const char * table_name,bool is_sql_layer_system_table)183 static bool example_is_supported_system_table(const char *db,
184                                               const char *table_name,
185                                               bool is_sql_layer_system_table) {
186   st_handler_tablename *systab;
187 
188   // Does this SE support "ALL" SQL layer system tables ?
189   if (is_sql_layer_system_table) return false;
190 
191   // Check if this is SE layer system tables
192   systab = ha_example_system_tables;
193   while (systab && systab->db) {
194     if (systab->db == db && strcmp(systab->tablename, table_name) == 0)
195       return true;
196     systab++;
197   }
198 
199   return false;
200 }
201 
202 /**
203   @brief
204   Used for opening tables. The name will be the name of the file.
205 
206   @details
207   A table is opened when it needs to be opened; e.g. when a request comes in
208   for a SELECT on the table (tables are not open and closed for each request,
209   they are cached).
210 
211   Called from handler.cc by handler::ha_open(). The server opens all tables by
212   calling ha_open() which then calls the handler specific open().
213 
214   @see
215   handler::ha_open() in handler.cc
216 */
217 
open(const char *,int,uint,const dd::Table *)218 int ha_example::open(const char *, int, uint, const dd::Table *) {
219   DBUG_TRACE;
220 
221   if (!(share = get_share())) return 1;
222   thr_lock_data_init(&share->lock, &lock, nullptr);
223 
224   return 0;
225 }
226 
227 /**
228   @brief
229   Closes a table.
230 
231   @details
232   Called from sql_base.cc, sql_select.cc, and table.cc. In sql_select.cc it is
233   only used to close up temporary tables or during the process where a
234   temporary table is converted over to being a myisam table.
235 
236   For sql_base.cc look at close_data_tables().
237 
238   @see
239   sql_base.cc, sql_select.cc and table.cc
240 */
241 
close(void)242 int ha_example::close(void) {
243   DBUG_TRACE;
244   return 0;
245 }
246 
247 /**
248   @brief
249   write_row() inserts a row. No extra() hint is given currently if a bulk load
250   is happening. buf() is a byte array of data. You can use the field
251   information to extract the data from the native byte array type.
252 
253   @details
254   Example of this would be:
255   @code
256   for (Field **field=table->field ; *field ; field++)
257   {
258     ...
259   }
260   @endcode
261 
262   See ha_tina.cc for an example of extracting all of the data as strings.
263   ha_berekly.cc has an example of how to store it intact by "packing" it
264   for ha_berkeley's own native storage type.
265 
266   See the note for update_row() on auto_increments. This case also applies to
267   write_row().
268 
269   Called from item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc,
270   sql_insert.cc, sql_select.cc, sql_table.cc, sql_udf.cc, and sql_update.cc.
271 
272   @see
273   item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc,
274   sql_insert.cc, sql_select.cc, sql_table.cc, sql_udf.cc and sql_update.cc
275 */
276 
write_row(uchar *)277 int ha_example::write_row(uchar *) {
278   DBUG_TRACE;
279   /*
280     Example of a successful write_row. We don't store the data
281     anywhere; they are thrown away. A real implementation will
282     probably need to do something with 'buf'. We report a success
283     here, to pretend that the insert was successful.
284   */
285   return 0;
286 }
287 
288 /**
289   @brief
290   Yes, update_row() does what you expect, it updates a row. old_data will have
291   the previous row record in it, while new_data will have the newest data in it.
292   Keep in mind that the server can do updates based on ordering if an ORDER BY
293   clause was used. Consecutive ordering is not guaranteed.
294 
295   @details
296   Currently new_data will not have an updated auto_increament record. You can
297   do this for example by doing:
298 
299   @code
300 
301   if (table->next_number_field && record == table->record[0])
302     update_auto_increment();
303 
304   @endcode
305 
306   Called from sql_select.cc, sql_acl.cc, sql_update.cc, and sql_insert.cc.
307 
308   @see
309   sql_select.cc, sql_acl.cc, sql_update.cc and sql_insert.cc
310 */
update_row(const uchar *,uchar *)311 int ha_example::update_row(const uchar *, uchar *) {
312   DBUG_TRACE;
313   return HA_ERR_WRONG_COMMAND;
314 }
315 
316 /**
317   @brief
318   This will delete a row. buf will contain a copy of the row to be deleted.
319   The server will call this right after the current row has been called (from
320   either a previous rnd_nexT() or index call).
321 
322   @details
323   If you keep a pointer to the last row or can access a primary key it will
324   make doing the deletion quite a bit easier. Keep in mind that the server does
325   not guarantee consecutive deletions. ORDER BY clauses can be used.
326 
327   Called in sql_acl.cc and sql_udf.cc to manage internal table
328   information.  Called in sql_delete.cc, sql_insert.cc, and
329   sql_select.cc. In sql_select it is used for removing duplicates
330   while in insert it is used for REPLACE calls.
331 
332   @see
333   sql_acl.cc, sql_udf.cc, sql_delete.cc, sql_insert.cc and sql_select.cc
334 */
335 
delete_row(const uchar *)336 int ha_example::delete_row(const uchar *) {
337   DBUG_TRACE;
338   return HA_ERR_WRONG_COMMAND;
339 }
340 
341 /**
342   @brief
343   Positions an index cursor to the index specified in the handle. Fetches the
344   row if available. If the key value is null, begin at the first key of the
345   index.
346 */
347 
index_read_map(uchar *,const uchar *,key_part_map,enum ha_rkey_function)348 int ha_example::index_read_map(uchar *, const uchar *, key_part_map,
349                                enum ha_rkey_function) {
350   int rc;
351   DBUG_TRACE;
352   rc = HA_ERR_WRONG_COMMAND;
353   return rc;
354 }
355 
356 /**
357   @brief
358   Used to read forward through the index.
359 */
360 
index_next(uchar *)361 int ha_example::index_next(uchar *) {
362   int rc;
363   DBUG_TRACE;
364   rc = HA_ERR_WRONG_COMMAND;
365   return rc;
366 }
367 
368 /**
369   @brief
370   Used to read backwards through the index.
371 */
372 
index_prev(uchar *)373 int ha_example::index_prev(uchar *) {
374   int rc;
375   DBUG_TRACE;
376   rc = HA_ERR_WRONG_COMMAND;
377   return rc;
378 }
379 
380 /**
381   @brief
382   index_first() asks for the first key in the index.
383 
384   @details
385   Called from opt_range.cc, opt_sum.cc, sql_handler.cc, and sql_select.cc.
386 
387   @see
388   opt_range.cc, opt_sum.cc, sql_handler.cc and sql_select.cc
389 */
index_first(uchar *)390 int ha_example::index_first(uchar *) {
391   int rc;
392   DBUG_TRACE;
393   rc = HA_ERR_WRONG_COMMAND;
394   return rc;
395 }
396 
397 /**
398   @brief
399   index_last() asks for the last key in the index.
400 
401   @details
402   Called from opt_range.cc, opt_sum.cc, sql_handler.cc, and sql_select.cc.
403 
404   @see
405   opt_range.cc, opt_sum.cc, sql_handler.cc and sql_select.cc
406 */
index_last(uchar *)407 int ha_example::index_last(uchar *) {
408   int rc;
409   DBUG_TRACE;
410   rc = HA_ERR_WRONG_COMMAND;
411   return rc;
412 }
413 
414 /**
415   @brief
416   rnd_init() is called when the system wants the storage engine to do a table
417   scan. See the example in the introduction at the top of this file to see when
418   rnd_init() is called.
419 
420   @details
421   Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc,
422   sql_table.cc, and sql_update.cc.
423 
424   @see
425   filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc and
426   sql_update.cc
427 */
rnd_init(bool)428 int ha_example::rnd_init(bool) {
429   DBUG_TRACE;
430   return 0;
431 }
432 
rnd_end()433 int ha_example::rnd_end() {
434   DBUG_TRACE;
435   return 0;
436 }
437 
438 /**
439   @brief
440   This is called for each row of the table scan. When you run out of records
441   you should return HA_ERR_END_OF_FILE. Fill buff up with the row information.
442   The Field structure for the table is the key to getting data into buf
443   in a manner that will allow the server to understand it.
444 
445   @details
446   Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc,
447   sql_table.cc, and sql_update.cc.
448 
449   @see
450   filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc and
451   sql_update.cc
452 */
rnd_next(uchar *)453 int ha_example::rnd_next(uchar *) {
454   int rc;
455   DBUG_TRACE;
456   rc = HA_ERR_END_OF_FILE;
457   return rc;
458 }
459 
460 /**
461   @brief
462   position() is called after each call to rnd_next() if the data needs
463   to be ordered. You can do something like the following to store
464   the position:
465   @code
466   my_store_ptr(ref, ref_length, current_position);
467   @endcode
468 
469   @details
470   The server uses ref to store data. ref_length in the above case is
471   the size needed to store current_position. ref is just a byte array
472   that the server will maintain. If you are using offsets to mark rows, then
473   current_position should be the offset. If it is a primary key like in
474   BDB, then it needs to be a primary key.
475 
476   Called from filesort.cc, sql_select.cc, sql_delete.cc, and sql_update.cc.
477 
478   @see
479   filesort.cc, sql_select.cc, sql_delete.cc and sql_update.cc
480 */
position(const uchar *)481 void ha_example::position(const uchar *) { DBUG_TRACE; }
482 
483 /**
484   @brief
485   This is like rnd_next, but you are given a position to use
486   to determine the row. The position will be of the type that you stored in
487   ref. You can use ha_get_ptr(pos,ref_length) to retrieve whatever key
488   or position you saved when position() was called.
489 
490   @details
491   Called from filesort.cc, records.cc, sql_insert.cc, sql_select.cc, and
492   sql_update.cc.
493 
494   @see
495   filesort.cc, records.cc, sql_insert.cc, sql_select.cc and sql_update.cc
496 */
rnd_pos(uchar *,uchar *)497 int ha_example::rnd_pos(uchar *, uchar *) {
498   int rc;
499   DBUG_TRACE;
500   rc = HA_ERR_WRONG_COMMAND;
501   return rc;
502 }
503 
504 /**
505   @brief
506   ::info() is used to return information to the optimizer. See my_base.h for
507   the complete description.
508 
509   @details
510   Currently this table handler doesn't implement most of the fields really
511   needed. SHOW also makes use of this data.
512 
513   You will probably want to have the following in your code:
514   @code
515   if (records < 2)
516     records = 2;
517   @endcode
518   The reason is that the server will optimize for cases of only a single
519   record. If, in a table scan, you don't know the number of records, it
520   will probably be better to set records to two so you can return as many
521   records as you need. Along with records, a few more variables you may wish
522   to set are:
523     records
524     deleted
525     data_file_length
526     index_file_length
527     delete_length
528     check_time
529   Take a look at the public variables in handler.h for more information.
530 
531   Called in filesort.cc, ha_heap.cc, item_sum.cc, opt_sum.cc, sql_delete.cc,
532   sql_delete.cc, sql_derived.cc, sql_select.cc, sql_select.cc, sql_select.cc,
533   sql_select.cc, sql_select.cc, sql_show.cc, sql_show.cc, sql_show.cc,
534   sql_show.cc, sql_table.cc, sql_union.cc, and sql_update.cc.
535 
536   @see
537   filesort.cc, ha_heap.cc, item_sum.cc, opt_sum.cc, sql_delete.cc,
538   sql_delete.cc, sql_derived.cc, sql_select.cc, sql_select.cc, sql_select.cc,
539   sql_select.cc, sql_select.cc, sql_show.cc, sql_show.cc, sql_show.cc,
540   sql_show.cc, sql_table.cc, sql_union.cc and sql_update.cc
541 */
info(uint)542 int ha_example::info(uint) {
543   DBUG_TRACE;
544   return 0;
545 }
546 
547 /**
548   @brief
549   extra() is called whenever the server wishes to send a hint to
550   the storage engine. The myisam engine implements the most hints.
551   ha_innodb.cc has the most exhaustive list of these hints.
552 
553     @see
554   ha_innodb.cc
555 */
extra(enum ha_extra_function)556 int ha_example::extra(enum ha_extra_function) {
557   DBUG_TRACE;
558   return 0;
559 }
560 
561 /**
562   @brief
563   Used to delete all rows in a table, including cases of truncate and cases
564   where the optimizer realizes that all rows will be removed as a result of an
565   SQL statement.
566 
567   @details
568   Called from item_sum.cc by Item_func_group_concat::clear(),
569   Item_sum_count_distinct::clear(), and Item_func_group_concat::clear().
570   Called from sql_delete.cc by mysql_delete().
571   Called from sql_select.cc by JOIN::reinit().
572   Called from sql_union.cc by st_select_lex_unit::exec().
573 
574   @see
575   Item_func_group_concat::clear(), Item_sum_count_distinct::clear() and
576   Item_func_group_concat::clear() in item_sum.cc;
577   mysql_delete() in sql_delete.cc;
578   JOIN::reinit() in sql_select.cc and
579   st_select_lex_unit::exec() in sql_union.cc.
580 */
delete_all_rows()581 int ha_example::delete_all_rows() {
582   DBUG_TRACE;
583   return HA_ERR_WRONG_COMMAND;
584 }
585 
586 /**
587   @brief
588   This create a lock on the table. If you are implementing a storage engine
589   that can handle transacations look at ha_berkely.cc to see how you will
590   want to go about doing this. Otherwise you should consider calling flock()
591   here. Hint: Read the section "locking functions for mysql" in lock.cc to
592   understand this.
593 
594   @details
595   Called from lock.cc by lock_external() and unlock_external(). Also called
596   from sql_table.cc by copy_data_between_tables().
597 
598   @see
599   lock.cc by lock_external() and unlock_external() in lock.cc;
600   the section "locking functions for mysql" in lock.cc;
601   copy_data_between_tables() in sql_table.cc.
602 */
external_lock(THD *,int)603 int ha_example::external_lock(THD *, int) {
604   DBUG_TRACE;
605   return 0;
606 }
607 
608 /**
609   @brief
610   The idea with handler::store_lock() is: The statement decides which locks
611   should be needed for the table. For updates/deletes/inserts we get WRITE
612   locks, for SELECT... we get read locks.
613 
614   @details
615   Before adding the lock into the table lock handler (see thr_lock.c),
616   mysqld calls store lock with the requested locks. Store lock can now
617   modify a write lock to a read lock (or some other lock), ignore the
618   lock (if we don't want to use MySQL table locks at all), or add locks
619   for many tables (like we do when we are using a MERGE handler).
620 
621   Berkeley DB, for example, changes all WRITE locks to TL_WRITE_ALLOW_WRITE
622   (which signals that we are doing WRITES, but are still allowing other
623   readers and writers).
624 
625   When releasing locks, store_lock() is also called. In this case one
626   usually doesn't have to do anything.
627 
628   In some exceptional cases MySQL may send a request for a TL_IGNORE;
629   This means that we are requesting the same lock as last time and this
630   should also be ignored. (This may happen when someone does a flush
631   table when we have opened a part of the tables, in which case mysqld
632   closes and reopens the tables and tries to get the same locks at last
633   time). In the future we will probably try to remove this.
634 
635   Called from lock.cc by get_lock_data().
636 
637   @note
638   In this method one should NEVER rely on table->in_use, it may, in fact,
639   refer to a different thread! (this happens if get_lock_data() is called
640   from mysql_lock_abort_for_thread() function)
641 
642   @see
643   get_lock_data() in lock.cc
644 */
store_lock(THD *,THR_LOCK_DATA ** to,enum thr_lock_type lock_type)645 THR_LOCK_DATA **ha_example::store_lock(THD *, THR_LOCK_DATA **to,
646                                        enum thr_lock_type lock_type) {
647   if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) lock.type = lock_type;
648   *to++ = &lock;
649   return to;
650 }
651 
652 /**
653   @brief
654   Used to delete a table. By the time delete_table() has been called all
655   opened references to this table will have been closed (and your globally
656   shared references released). The variable name will just be the name of
657   the table. You will need to remove any files you have created at this point.
658 
659   @details
660   If you do not implement this, the default delete_table() is called from
661   handler.cc and it will delete all files with the file extensions from
662   handlerton::file_extensions.
663 
664   Called from handler.cc by delete_table and ha_create_table(). Only used
665   during create if the table_flag HA_DROP_BEFORE_CREATE was specified for
666   the storage engine.
667 
668   @see
669   delete_table and ha_create_table() in handler.cc
670 */
delete_table(const char *,const dd::Table *)671 int ha_example::delete_table(const char *, const dd::Table *) {
672   DBUG_TRACE;
673   /* This is not implemented but we want someone to be able that it works. */
674   return 0;
675 }
676 
677 /**
678   @brief
679   Renames a table from one name to another via an alter table call.
680 
681   @details
682   If you do not implement this, the default rename_table() is called from
683   handler.cc and it will delete all files with the file extensions from
684   handlerton::file_extensions.
685 
686   Called from sql_table.cc by mysql_rename_table().
687 
688   @see
689   mysql_rename_table() in sql_table.cc
690 */
rename_table(const char *,const char *,const dd::Table *,dd::Table *)691 int ha_example::rename_table(const char *, const char *, const dd::Table *,
692                              dd::Table *) {
693   DBUG_TRACE;
694   return HA_ERR_WRONG_COMMAND;
695 }
696 
697 /**
698   @brief
699   Given a starting key and an ending key, estimate the number of rows that
700   will exist between the two keys.
701 
702   @details
703   end_key may be empty, in which case determine if start_key matches any rows.
704 
705   Called from opt_range.cc by check_quick_keys().
706 
707   @see
708   check_quick_keys() in opt_range.cc
709 */
records_in_range(uint,key_range *,key_range *)710 ha_rows ha_example::records_in_range(uint, key_range *, key_range *) {
711   DBUG_TRACE;
712   return 10;  // low number to force index usage
713 }
714 
715 static MYSQL_THDVAR_STR(last_create_thdvar, PLUGIN_VAR_MEMALLOC, nullptr,
716                         nullptr, nullptr, nullptr);
717 
718 static MYSQL_THDVAR_UINT(create_count_thdvar, 0, nullptr, nullptr, nullptr, 0,
719                          0, 1000, 0);
720 
721 /**
722   @brief
723   create() is called to create a database. The variable name will have the name
724   of the table.
725 
726   @details
727   When create() is called you do not need to worry about
728   opening the table. Also, the .frm file will have already been
729   created so adjusting create_info is not necessary. You can overwrite
730   the .frm file at this point if you wish to change the table
731   definition, but there are no methods currently provided for doing
732   so.
733 
734   Called from handle.cc by ha_create_table().
735 
736   @see
737   ha_create_table() in handle.cc
738 */
739 
create(const char * name,TABLE *,HA_CREATE_INFO *,dd::Table *)740 int ha_example::create(const char *name, TABLE *, HA_CREATE_INFO *,
741                        dd::Table *) {
742   DBUG_TRACE;
743   /*
744     This is not implemented but we want someone to be able to see that it
745     works.
746   */
747 
748   /*
749     It's just an example of THDVAR_SET() usage below.
750   */
751   THD *thd = ha_thd();
752   char *buf = (char *)my_malloc(PSI_NOT_INSTRUMENTED, SHOW_VAR_FUNC_BUFF_SIZE,
753                                 MYF(MY_FAE));
754   snprintf(buf, SHOW_VAR_FUNC_BUFF_SIZE, "Last creation '%s'", name);
755   THDVAR_SET(thd, last_create_thdvar, buf);
756   my_free(buf);
757 
758   uint count = THDVAR(thd, create_count_thdvar) + 1;
759   THDVAR_SET(thd, create_count_thdvar, &count);
760 
761   return 0;
762 }
763 
764 struct st_mysql_storage_engine example_storage_engine = {
765     MYSQL_HANDLERTON_INTERFACE_VERSION};
766 
767 static ulong srv_enum_var = 0;
768 static ulong srv_ulong_var = 0;
769 static double srv_double_var = 0;
770 static int srv_signed_int_var = 0;
771 static long srv_signed_long_var = 0;
772 static longlong srv_signed_longlong_var = 0;
773 
774 const char *enum_var_names[] = {"e1", "e2", NullS};
775 
776 TYPELIB enum_var_typelib = {array_elements(enum_var_names) - 1,
777                             "enum_var_typelib", enum_var_names, nullptr};
778 
779 static MYSQL_SYSVAR_ENUM(enum_var,                        // name
780                          srv_enum_var,                    // varname
781                          PLUGIN_VAR_RQCMDARG,             // opt
782                          "Sample ENUM system variable.",  // comment
783                          nullptr,                         // check
784                          nullptr,                         // update
785                          0,                               // def
786                          &enum_var_typelib);              // typelib
787 
788 static MYSQL_SYSVAR_ULONG(ulong_var, srv_ulong_var, PLUGIN_VAR_RQCMDARG,
789                           "0..1000", nullptr, nullptr, 8, 0, 1000, 0);
790 
791 static MYSQL_SYSVAR_DOUBLE(double_var, srv_double_var, PLUGIN_VAR_RQCMDARG,
792                            "0.500000..1000.500000", nullptr, nullptr, 8.5, 0.5,
793                            1000.5,
794                            0);  // reserved always 0
795 
796 static MYSQL_THDVAR_DOUBLE(double_thdvar, PLUGIN_VAR_RQCMDARG,
797                            "0.500000..1000.500000", nullptr, nullptr, 8.5, 0.5,
798                            1000.5, 0);
799 
800 static MYSQL_SYSVAR_INT(signed_int_var, srv_signed_int_var, PLUGIN_VAR_RQCMDARG,
801                         "INT_MIN..INT_MAX", nullptr, nullptr, -10, INT_MIN,
802                         INT_MAX, 0);
803 
804 static MYSQL_THDVAR_INT(signed_int_thdvar, PLUGIN_VAR_RQCMDARG,
805                         "INT_MIN..INT_MAX", nullptr, nullptr, -10, INT_MIN,
806                         INT_MAX, 0);
807 
808 static MYSQL_SYSVAR_LONG(signed_long_var, srv_signed_long_var,
809                          PLUGIN_VAR_RQCMDARG, "LONG_MIN..LONG_MAX", nullptr,
810                          nullptr, -10, LONG_MIN, LONG_MAX, 0);
811 
812 static MYSQL_THDVAR_LONG(signed_long_thdvar, PLUGIN_VAR_RQCMDARG,
813                          "LONG_MIN..LONG_MAX", nullptr, nullptr, -10, LONG_MIN,
814                          LONG_MAX, 0);
815 
816 static MYSQL_SYSVAR_LONGLONG(signed_longlong_var, srv_signed_longlong_var,
817                              PLUGIN_VAR_RQCMDARG, "LLONG_MIN..LLONG_MAX",
818                              nullptr, nullptr, -10, LLONG_MIN, LLONG_MAX, 0);
819 
820 static MYSQL_THDVAR_LONGLONG(signed_longlong_thdvar, PLUGIN_VAR_RQCMDARG,
821                              "LLONG_MIN..LLONG_MAX", nullptr, nullptr, -10,
822                              LLONG_MIN, LLONG_MAX, 0);
823 
824 static SYS_VAR *example_system_variables[] = {
825     MYSQL_SYSVAR(enum_var),
826     MYSQL_SYSVAR(ulong_var),
827     MYSQL_SYSVAR(double_var),
828     MYSQL_SYSVAR(double_thdvar),
829     MYSQL_SYSVAR(last_create_thdvar),
830     MYSQL_SYSVAR(create_count_thdvar),
831     MYSQL_SYSVAR(signed_int_var),
832     MYSQL_SYSVAR(signed_int_thdvar),
833     MYSQL_SYSVAR(signed_long_var),
834     MYSQL_SYSVAR(signed_long_thdvar),
835     MYSQL_SYSVAR(signed_longlong_var),
836     MYSQL_SYSVAR(signed_longlong_thdvar),
837     nullptr};
838 
839 // this is an example of SHOW_FUNC
show_func_example(MYSQL_THD,SHOW_VAR * var,char * buf)840 static int show_func_example(MYSQL_THD, SHOW_VAR *var, char *buf) {
841   var->type = SHOW_CHAR;
842   var->value = buf;  // it's of SHOW_VAR_FUNC_BUFF_SIZE bytes
843   snprintf(buf, SHOW_VAR_FUNC_BUFF_SIZE,
844            "enum_var is %lu, ulong_var is %lu, "
845            "double_var is %f, signed_int_var is %d, "
846            "signed_long_var is %ld, signed_longlong_var is %lld",
847            srv_enum_var, srv_ulong_var, srv_double_var, srv_signed_int_var,
848            srv_signed_long_var, srv_signed_longlong_var);
849   return 0;
850 }
851 
852 struct example_vars_t {
853   ulong var1;
854   double var2;
855   char var3[64];
856   bool var4;
857   bool var5;
858   ulong var6;
859 };
860 
861 example_vars_t example_vars = {100, 20.01, "three hundred", true, false, 8250};
862 
863 static SHOW_VAR show_status_example[] = {
864     {"var1", (char *)&example_vars.var1, SHOW_LONG, SHOW_SCOPE_GLOBAL},
865     {"var2", (char *)&example_vars.var2, SHOW_DOUBLE, SHOW_SCOPE_GLOBAL},
866     {nullptr, nullptr, SHOW_UNDEF,
867      SHOW_SCOPE_UNDEF}  // null terminator required
868 };
869 
870 static SHOW_VAR show_array_example[] = {
871     {"array", (char *)show_status_example, SHOW_ARRAY, SHOW_SCOPE_GLOBAL},
872     {"var3", (char *)&example_vars.var3, SHOW_CHAR, SHOW_SCOPE_GLOBAL},
873     {"var4", (char *)&example_vars.var4, SHOW_BOOL, SHOW_SCOPE_GLOBAL},
874     {nullptr, nullptr, SHOW_UNDEF, SHOW_SCOPE_UNDEF}};
875 
876 static SHOW_VAR func_status[] = {
877     {"example_func_example", (char *)show_func_example, SHOW_FUNC,
878      SHOW_SCOPE_GLOBAL},
879     {"example_status_var5", (char *)&example_vars.var5, SHOW_BOOL,
880      SHOW_SCOPE_GLOBAL},
881     {"example_status_var6", (char *)&example_vars.var6, SHOW_LONG,
882      SHOW_SCOPE_GLOBAL},
883     {"example_status", (char *)show_array_example, SHOW_ARRAY,
884      SHOW_SCOPE_GLOBAL},
885     {nullptr, nullptr, SHOW_UNDEF, SHOW_SCOPE_UNDEF}};
886 
mysql_declare_plugin(example)887 mysql_declare_plugin(example){
888     MYSQL_STORAGE_ENGINE_PLUGIN,
889     &example_storage_engine,
890     "EXAMPLE",
891     PLUGIN_AUTHOR_ORACLE,
892     "Example storage engine",
893     PLUGIN_LICENSE_GPL,
894     example_init_func, /* Plugin Init */
895     nullptr,           /* Plugin check uninstall */
896     nullptr,           /* Plugin Deinit */
897     0x0001 /* 0.1 */,
898     func_status,              /* status variables */
899     example_system_variables, /* system variables */
900     nullptr,                  /* config options */
901     0,                        /* flags */
902 } mysql_declare_plugin_end;
903