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