1 /*
2    Copyright (c) 2017, Aliyun and/or its affiliates.
3    Copyright (c) 2017, 2020, MariaDB Corporation.
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; version 2 of the License.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
17 */
18 
19 #include "mariadb.h"
20 #include "sql_list.h"
21 #include "table.h"
22 #include "sql_table.h"
23 #include "sql_sequence.h"
24 #include "ha_sequence.h"
25 #include "sql_plugin.h"
26 #include "mysql/plugin.h"
27 #include "sql_priv.h"
28 #include "sql_parse.h"
29 #include "sql_update.h"
30 #include "sql_base.h"
31 #include "log_event.h"
32 
33 #ifdef WITH_WSREP
34 #include "wsrep_trans_observer.h" /* wsrep_start_transaction() */
35 #endif
36 
37 /*
38   Table flags we should inherit and disable from the original engine.
39   We add HA_STATS_RECORDS_IS_EXACT as ha_sequence::info() will ensure
40   that records is always 1
41 */
42 
43 #define SEQUENCE_ENABLED_TABLE_FLAGS  (HA_STATS_RECORDS_IS_EXACT | \
44                                        HA_PERSISTENT_TABLE)
45 #define SEQUENCE_DISABLED_TABLE_FLAGS  (HA_CAN_SQL_HANDLER | \
46                                         HA_CAN_INSERT_DELAYED | \
47                                         HA_BINLOG_STMT_CAPABLE)
48 handlerton *sql_sequence_hton;
49 
50 /*
51   Create a sequence handler
52 */
53 
ha_sequence(handlerton * hton,TABLE_SHARE * share)54 ha_sequence::ha_sequence(handlerton *hton, TABLE_SHARE *share)
55   :handler(hton, share), write_locked(0)
56 {
57   sequence= share->sequence;
58   DBUG_ASSERT(share->sequence);
59 }
60 
61 /**
62    Destructor method must remove the underlying handler
63 */
~ha_sequence()64 ha_sequence::~ha_sequence()
65 {
66   delete file;
67 }
68 
69 /**
70    Sequence table open method
71 
72    @param name            Path to file (dbname and tablename)
73    @param mode            mode
74    @param flags           Flags how to open file
75 
76    RETURN VALUES
77    @retval 0              Success
78    @retval != 0           Failure
79 */
80 
open(const char * name,int mode,uint flags)81 int ha_sequence::open(const char *name, int mode, uint flags)
82 {
83   int error;
84   DBUG_ENTER("ha_sequence::open");
85   DBUG_ASSERT(table->s == table_share && file);
86 
87   file->table= table;
88   if (likely(!(error= file->open(name, mode, flags))))
89   {
90     /*
91       Allocate ref in table's mem_root. We can't use table's ref
92       as it's allocated by ha_ caller that allocates this.
93      */
94     ref_length= file->ref_length;
95     if (!(ref= (uchar*) alloc_root(&table->mem_root,ALIGN_SIZE(ref_length)*2)))
96     {
97       file->ha_close();
98       error=HA_ERR_OUT_OF_MEM;
99       DBUG_RETURN(error);
100     }
101     file->ref= ref;
102     file->dup_ref= dup_ref= ref+ALIGN_SIZE(file->ref_length);
103 
104     /*
105       ha_open() sets the following for us. We have to set this for the
106       underlying handler
107     */
108     file->cached_table_flags= (file->table_flags() | HA_REUSES_FILE_NAMES);
109 
110     file->reset_statistics();
111     internal_tmp_table= file->internal_tmp_table=
112       MY_TEST(flags & HA_OPEN_INTERNAL_TABLE);
113     reset_statistics();
114 
115     /*
116       Don't try to read the initial row if the call is part of CREATE, REPAIR
117       or FLUSH
118     */
119     if (!(flags & (HA_OPEN_FOR_CREATE | HA_OPEN_FOR_REPAIR |
120                    HA_OPEN_FOR_FLUSH)))
121     {
122       if (unlikely((error= table->s->sequence->read_initial_values(table))))
123         file->ha_close();
124     }
125     else if (!table->s->tmp_table)
126       table->internal_set_needs_reopen(true);
127 
128     /*
129       The following is needed to fix comparison of rows in
130       ha_update_first_row() for InnoDB
131     */
132     if (!error)
133       memcpy(table->record[1], table->s->default_values, table->s->reclength);
134   }
135   DBUG_RETURN(error);
136 }
137 
138 /*
139   Clone the sequence. Needed if table is used by range optimization
140   (Very, very unlikely)
141 */
142 
clone(const char * name,MEM_ROOT * mem_root)143 handler *ha_sequence::clone(const char *name, MEM_ROOT *mem_root)
144 {
145   ha_sequence *new_handler;
146   DBUG_ENTER("ha_sequence::clone");
147   if (!(new_handler= new (mem_root) ha_sequence(ht, table_share)))
148     DBUG_RETURN(NULL);
149 
150   /*
151     Allocate new_handler->ref here because otherwise ha_open will allocate it
152     on this->table->mem_root and we will not be able to reclaim that memory
153     when the clone handler object is destroyed.
154   */
155 
156   if (!(new_handler->ref= (uchar*) alloc_root(mem_root,
157                                               ALIGN_SIZE(ref_length)*2)))
158     goto err;
159 
160   if (new_handler->ha_open(table, name,
161                            table->db_stat,
162                            HA_OPEN_IGNORE_IF_LOCKED | HA_OPEN_NO_PSI_CALL))
163     goto err;
164 
165   /* Reuse original storage engine data for duplicate key reference */
166   new_handler->ref=        file->ref;
167   new_handler->ref_length= file->ref_length;
168   new_handler->dup_ref=    file->dup_ref;
169 
170   DBUG_RETURN((handler*) new_handler);
171 
172 err:
173   delete new_handler;
174   DBUG_RETURN(NULL);
175 }
176 
177 
178 /*
179   Map the create table to the original storage engine
180 */
181 
create(const char * name,TABLE * form,HA_CREATE_INFO * create_info)182 int ha_sequence::create(const char *name, TABLE *form,
183                         HA_CREATE_INFO *create_info)
184 {
185   DBUG_ASSERT(create_info->sequence);
186   /* Sequence tables has one and only one row */
187   create_info->max_rows= create_info->min_rows= 1;
188   return (file->create(name, form, create_info));
189 }
190 
191 /**
192   Sequence write row method.
193 
194   A sequence table has only one row. Any inserts in the table
195   will update this row.
196 
197   @retval 0     Success
198   @retval != 0  Failure
199 
200   NOTES:
201     write_locked is set if we are called from SEQUENCE::next_value
202     In this case the mutex is already locked and we should not update
203     the sequence with 'buf' as the sequence object is already up to date.
204 */
205 
write_row(const uchar * buf)206 int ha_sequence::write_row(const uchar *buf)
207 {
208   int error;
209   sequence_definition tmp_seq;
210   bool sequence_locked;
211   THD *thd= table->in_use;
212   DBUG_ENTER("ha_sequence::write_row");
213   DBUG_ASSERT(table->record[0] == buf);
214 
215   /*
216     Log to binary log even if this function has been called before
217     (The function ends by setting row_logging to 0)
218   */
219   row_logging= row_logging_init;
220   if (unlikely(sequence->initialized == SEQUENCE::SEQ_IN_PREPARE))
221   {
222     /* This calls is from ha_open() as part of create table */
223     DBUG_RETURN(file->write_row(buf));
224   }
225   if (unlikely(sequence->initialized == SEQUENCE::SEQ_IN_ALTER))
226   {
227     int error= 0;
228     /* This is called from alter table */
229     tmp_seq.read_fields(table);
230     if (tmp_seq.check_and_adjust(0))
231       DBUG_RETURN(HA_ERR_SEQUENCE_INVALID_DATA);
232     sequence->copy(&tmp_seq);
233     if (likely(!(error= file->write_row(buf))))
234       sequence->initialized= SEQUENCE::SEQ_READY_TO_USE;
235     row_logging= 0;
236     DBUG_RETURN(error);
237   }
238   if (unlikely(sequence->initialized != SEQUENCE::SEQ_READY_TO_USE))
239     DBUG_RETURN(HA_ERR_WRONG_COMMAND);
240 
241   sequence_locked= write_locked;
242   if (!write_locked)                         // If not from next_value()
243   {
244     /*
245       User tries to write a full row directly to the sequence table with
246       INSERT or LOAD DATA.
247 
248       - Get an exclusive lock for the table. This is needed to ensure that
249         we excute all full inserts (same as ALTER SEQUENCE) in same order
250         on master and slaves
251       - Check that the new row is an accurate SEQUENCE object
252     */
253     if (table->s->tmp_table == NO_TMP_TABLE &&
254         thd->mdl_context.upgrade_shared_lock(table->mdl_ticket,
255                                              MDL_EXCLUSIVE,
256                                              thd->variables.
257                                              lock_wait_timeout))
258         DBUG_RETURN(ER_LOCK_WAIT_TIMEOUT);
259 
260     tmp_seq.read_fields(table);
261     if (tmp_seq.check_and_adjust(0))
262       DBUG_RETURN(HA_ERR_SEQUENCE_INVALID_DATA);
263 
264     /*
265       Lock sequence to ensure that no one can come in between
266       while sequence, table and binary log are updated.
267     */
268     sequence->write_lock(table);
269   }
270 
271 #ifdef WITH_WSREP
272   /* We need to start Galera transaction for select NEXT VALUE FOR
273   sequence if it is not yet started. Note that ALTER is handled
274   as TOI. */
275   if (WSREP_ON && WSREP(thd) &&
276       !thd->wsrep_trx().active() &&
277       wsrep_thd_is_local(thd))
278     wsrep_start_transaction(thd, thd->wsrep_next_trx_id());
279 #endif
280 
281   if (likely(!(error= file->update_first_row(buf))))
282   {
283     Log_func *log_func= Write_rows_log_event::binlog_row_logging_function;
284     if (!sequence_locked)
285       sequence->copy(&tmp_seq);
286     rows_changed++;
287     /* We have to do the logging while we hold the sequence mutex */
288     if (row_logging)
289       error= binlog_log_row(table, 0, buf, log_func);
290   }
291 
292   /* Row is already logged, don't log it again in ha_write_row() */
293   row_logging= 0;
294   sequence->all_values_used= 0;
295   if (!sequence_locked)
296     sequence->write_unlock(table);
297   DBUG_RETURN(error);
298 }
299 
300 
301 /*
302   Inherit the sequence base table flags.
303 */
304 
table_flags() const305 handler::Table_flags ha_sequence::table_flags() const
306 {
307   DBUG_ENTER("ha_sequence::table_flags");
308   DBUG_RETURN((file->table_flags() & ~SEQUENCE_DISABLED_TABLE_FLAGS) |
309               SEQUENCE_ENABLED_TABLE_FLAGS);
310 }
311 
312 
info(uint flag)313 int ha_sequence::info(uint flag)
314 {
315   DBUG_ENTER("ha_sequence::info");
316   file->info(flag);
317   /* Inform optimizer that we have always only one record */
318   stats= file->stats;
319   stats.records= 1;
320   DBUG_RETURN(false);
321 }
322 
323 
extra(enum ha_extra_function operation)324 int ha_sequence::extra(enum ha_extra_function operation)
325 {
326   if (operation == HA_EXTRA_PREPARE_FOR_ALTER_TABLE)
327   {
328     /* In case of ALTER TABLE allow ::write_row() to copy rows */
329     sequence->initialized= SEQUENCE::SEQ_IN_ALTER;
330   }
331   return file->extra(operation);
332 }
333 
check_if_incompatible_data(HA_CREATE_INFO * create_info,uint table_changes)334 bool ha_sequence::check_if_incompatible_data(HA_CREATE_INFO *create_info,
335                                              uint table_changes)
336 {
337   /* Table definition is locked for SEQUENCE tables */
338   return(COMPATIBLE_DATA_YES);
339 }
340 
341 
external_lock(THD * thd,int lock_type)342 int ha_sequence::external_lock(THD *thd, int lock_type)
343 {
344   int error= file->external_lock(thd, lock_type);
345 
346   /*
347     Copy lock flag to satisfy DBUG_ASSERT checks in ha_* functions in
348     handler.cc when we later call it with file->ha_..()
349   */
350   if (!error)
351     file->m_lock_type= lock_type;
352   return error;
353 }
354 
355 /*
356   Squence engine error deal method
357 */
358 
print_error(int error,myf errflag)359 void ha_sequence::print_error(int error, myf errflag)
360 {
361   const char *sequence_db=   table_share->db.str;
362   const char *sequence_name= table_share->table_name.str;
363   DBUG_ENTER("ha_sequence::print_error");
364 
365   switch (error) {
366   case HA_ERR_SEQUENCE_INVALID_DATA:
367   {
368     my_error(ER_SEQUENCE_INVALID_DATA, MYF(errflag), sequence_db,
369              sequence_name);
370     DBUG_VOID_RETURN;
371   }
372   case HA_ERR_SEQUENCE_RUN_OUT:
373   {
374     my_error(ER_SEQUENCE_RUN_OUT, MYF(errflag), sequence_db, sequence_name);
375     DBUG_VOID_RETURN;
376   }
377   case HA_ERR_WRONG_COMMAND:
378     my_error(ER_ILLEGAL_HA, MYF(0), "SEQUENCE", sequence_db, sequence_name);
379     DBUG_VOID_RETURN;
380   case ER_WRONG_INSERT_INTO_SEQUENCE:
381     my_error(error, MYF(0));
382     DBUG_VOID_RETURN;
383   }
384   file->print_error(error, errflag);
385   DBUG_VOID_RETURN;
386 }
387 
388 /*****************************************************************************
389   Sequence plugin interface
390 *****************************************************************************/
391 
392 /*
393  Create an new handler
394 */
395 
sequence_create_handler(handlerton * hton,TABLE_SHARE * share,MEM_ROOT * mem_root)396 static handler *sequence_create_handler(handlerton *hton,
397                                         TABLE_SHARE *share,
398                                         MEM_ROOT *mem_root)
399 {
400   DBUG_ENTER("sequence_create_handler");
401   if (unlikely(!share))
402   {
403     /*
404       This can happen if we call get_new_handler with a non existing share
405     */
406     DBUG_RETURN(0);
407   }
408   DBUG_RETURN(new (mem_root) ha_sequence(hton, share));
409 }
410 
411 
412 /*
413   Sequence engine end.
414 
415   SYNOPSIS
416     sequence_end()
417     p                           handlerton.
418     type                        panic type.
419   RETURN VALUES
420     0           Success
421     !=0         Failure
422 */
sequence_end(handlerton * hton,ha_panic_function type)423 static int sequence_end(handlerton* hton,
424                         ha_panic_function type __attribute__((unused)))
425 {
426   DBUG_ENTER("sequence_end");
427   DBUG_RETURN(0);
428 }
429 
430 
431 /*
432   Sequence engine init.
433 
434   SYNOPSIS
435     sequence_initialize()
436 
437     @param p    handlerton.
438 
439     retval 0    Success
440     retval !=0  Failure
441 */
442 
sequence_initialize(void * p)443 static int sequence_initialize(void *p)
444 {
445   handlerton *local_sequence_hton= (handlerton *)p;
446   DBUG_ENTER("sequence_initialize");
447 
448   local_sequence_hton->db_type= DB_TYPE_SEQUENCE;
449   local_sequence_hton->create= sequence_create_handler;
450   local_sequence_hton->panic= sequence_end;
451   local_sequence_hton->flags= (HTON_NOT_USER_SELECTABLE |
452                                HTON_HIDDEN |
453                                HTON_TEMPORARY_NOT_SUPPORTED |
454                                HTON_ALTER_NOT_SUPPORTED |
455                                HTON_NO_PARTITION);
456   DBUG_RETURN(0);
457 }
458 
459 
460 static struct st_mysql_storage_engine sequence_storage_engine=
461 { MYSQL_HANDLERTON_INTERFACE_VERSION };
462 
maria_declare_plugin(sql_sequence)463 maria_declare_plugin(sql_sequence)
464 {
465   MYSQL_STORAGE_ENGINE_PLUGIN,
466   &sequence_storage_engine,
467   "SQL_SEQUENCE",
468   "jianwei.zhao @ Aliyun & Monty @ MariaDB corp",
469   "Sequence Storage Engine for CREATE SEQUENCE",
470   PLUGIN_LICENSE_GPL,
471   sequence_initialize,        /* Plugin Init */
472   NULL,                       /* Plugin Deinit */
473   0x0100,                     /* 1.0 */
474   NULL,                       /* status variables                */
475   NULL,                       /* system variables                */
476   "1.0",                      /* string version                  */
477   MariaDB_PLUGIN_MATURITY_STABLE /* maturity                     */
478 }
479 maria_declare_plugin_end;
480