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_sequence.h"
23 #include "ha_sequence.h"
24 #include "sql_plugin.h"
25 #include "mysql/plugin.h"
26 #include "sql_priv.h"
27 #include "sql_parse.h"
28 #include "sql_table.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();
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 /* Don't try to read the initial row the call is part of create code */
116 if (!(flags & (HA_OPEN_FOR_CREATE | HA_OPEN_FOR_REPAIR)))
117 {
118 if (unlikely((error= table->s->sequence->read_initial_values(table))))
119 file->ha_close();
120 }
121 else if (!table->s->tmp_table)
122 table->internal_set_needs_reopen(true);
123
124 /*
125 The following is needed to fix comparison of rows in
126 ha_update_first_row() for InnoDB
127 */
128 memcpy(table->record[1], table->s->default_values, table->s->reclength);
129 }
130 DBUG_RETURN(error);
131 }
132
133 /*
134 Clone the sequence. Needed if table is used by range optimization
135 (Very, very unlikely)
136 */
137
clone(const char * name,MEM_ROOT * mem_root)138 handler *ha_sequence::clone(const char *name, MEM_ROOT *mem_root)
139 {
140 ha_sequence *new_handler;
141 DBUG_ENTER("ha_sequence::clone");
142 if (!(new_handler= new (mem_root) ha_sequence(ht, table_share)))
143 DBUG_RETURN(NULL);
144
145 /*
146 Allocate new_handler->ref here because otherwise ha_open will allocate it
147 on this->table->mem_root and we will not be able to reclaim that memory
148 when the clone handler object is destroyed.
149 */
150
151 if (!(new_handler->ref= (uchar*) alloc_root(mem_root,
152 ALIGN_SIZE(ref_length)*2)))
153 goto err;
154
155 if (new_handler->ha_open(table, name,
156 table->db_stat,
157 HA_OPEN_IGNORE_IF_LOCKED | HA_OPEN_NO_PSI_CALL))
158 goto err;
159
160 /* Reuse original storage engine data for duplicate key reference */
161 new_handler->ref= file->ref;
162 new_handler->ref_length= file->ref_length;
163 new_handler->dup_ref= file->dup_ref;
164
165 DBUG_RETURN((handler*) new_handler);
166
167 err:
168 delete new_handler;
169 DBUG_RETURN(NULL);
170 }
171
172
173 /*
174 Map the create table to the original storage engine
175 */
176
create(const char * name,TABLE * form,HA_CREATE_INFO * create_info)177 int ha_sequence::create(const char *name, TABLE *form,
178 HA_CREATE_INFO *create_info)
179 {
180 DBUG_ASSERT(create_info->sequence);
181 /* Sequence tables has one and only one row */
182 create_info->max_rows= create_info->min_rows= 1;
183 return (file->create(name, form, create_info));
184 }
185
186 /**
187 Sequence write row method.
188
189 A sequence table has only one row. Any inserts in the table
190 will update this row.
191
192 @retval 0 Success
193 @retval != 0 Failure
194
195 NOTES:
196 write_locked is set if we are called from SEQUENCE::next_value
197 In this case the mutex is already locked and we should not update
198 the sequence with 'buf' as the sequence object is already up to date.
199 */
200
write_row(const uchar * buf)201 int ha_sequence::write_row(const uchar *buf)
202 {
203 int error;
204 sequence_definition tmp_seq;
205 bool sequence_locked;
206 THD *thd= table->in_use;
207 DBUG_ENTER("ha_sequence::write_row");
208 DBUG_ASSERT(table->record[0] == buf);
209
210 row_already_logged= 0;
211 if (unlikely(sequence->initialized == SEQUENCE::SEQ_IN_PREPARE))
212 {
213 /* This calls is from ha_open() as part of create table */
214 DBUG_RETURN(file->write_row(buf));
215 }
216 if (unlikely(sequence->initialized == SEQUENCE::SEQ_IN_ALTER))
217 {
218 int error= 0;
219 /* This is called from alter table */
220 tmp_seq.read_fields(table);
221 if (tmp_seq.check_and_adjust(0))
222 DBUG_RETURN(HA_ERR_SEQUENCE_INVALID_DATA);
223 sequence->copy(&tmp_seq);
224 if (likely(!(error= file->write_row(buf))))
225 sequence->initialized= SEQUENCE::SEQ_READY_TO_USE;
226 DBUG_RETURN(error);
227 }
228 if (unlikely(sequence->initialized != SEQUENCE::SEQ_READY_TO_USE))
229 DBUG_RETURN(HA_ERR_WRONG_COMMAND);
230
231 sequence_locked= write_locked;
232 if (!write_locked) // If not from next_value()
233 {
234 /*
235 User tries to write a full row directly to the sequence table with
236 INSERT or LOAD DATA.
237
238 - Get an exclusive lock for the table. This is needed to ensure that
239 we excute all full inserts (same as ALTER SEQUENCE) in same order
240 on master and slaves
241 - Check that the new row is an accurate SEQUENCE object
242 */
243 if (table->s->tmp_table == NO_TMP_TABLE &&
244 thd->mdl_context.upgrade_shared_lock(table->mdl_ticket,
245 MDL_EXCLUSIVE,
246 thd->variables.
247 lock_wait_timeout))
248 DBUG_RETURN(ER_LOCK_WAIT_TIMEOUT);
249
250 tmp_seq.read_fields(table);
251 if (tmp_seq.check_and_adjust(0))
252 DBUG_RETURN(HA_ERR_SEQUENCE_INVALID_DATA);
253
254 /*
255 Lock sequence to ensure that no one can come in between
256 while sequence, table and binary log are updated.
257 */
258 sequence->write_lock(table);
259 }
260
261 #ifdef WITH_WSREP
262 /* We need to start Galera transaction for select NEXT VALUE FOR
263 sequence if it is not yet started. Note that ALTER is handled
264 as TOI. */
265 if (WSREP_ON && WSREP(thd) &&
266 !thd->wsrep_trx().active() &&
267 wsrep_thd_is_local(thd))
268 wsrep_start_transaction(thd, thd->wsrep_next_trx_id());
269 #endif
270
271 if (likely(!(error= file->update_first_row(buf))))
272 {
273 Log_func *log_func= Write_rows_log_event::binlog_row_logging_function;
274 if (!sequence_locked)
275 sequence->copy(&tmp_seq);
276 rows_changed++;
277 /* We have to do the logging while we hold the sequence mutex */
278 error= binlog_log_row(table, 0, buf, log_func);
279 row_already_logged= 1;
280 }
281
282 sequence->all_values_used= 0;
283 if (!sequence_locked)
284 sequence->write_unlock(table);
285 DBUG_RETURN(error);
286 }
287
288
289 /*
290 Inherit the sequence base table flags.
291 */
292
table_flags() const293 handler::Table_flags ha_sequence::table_flags() const
294 {
295 DBUG_ENTER("ha_sequence::table_flags");
296 DBUG_RETURN((file->table_flags() & ~SEQUENCE_DISABLED_TABLE_FLAGS) |
297 SEQUENCE_ENABLED_TABLE_FLAGS);
298 }
299
300
info(uint flag)301 int ha_sequence::info(uint flag)
302 {
303 DBUG_ENTER("ha_sequence::info");
304 file->info(flag);
305 /* Inform optimizer that we have always only one record */
306 stats= file->stats;
307 stats.records= 1;
308 DBUG_RETURN(false);
309 }
310
311
extra(enum ha_extra_function operation)312 int ha_sequence::extra(enum ha_extra_function operation)
313 {
314 if (operation == HA_EXTRA_PREPARE_FOR_ALTER_TABLE)
315 {
316 /* In case of ALTER TABLE allow ::write_row() to copy rows */
317 sequence->initialized= SEQUENCE::SEQ_IN_ALTER;
318 }
319 return file->extra(operation);
320 }
321
check_if_incompatible_data(HA_CREATE_INFO * create_info,uint table_changes)322 bool ha_sequence::check_if_incompatible_data(HA_CREATE_INFO *create_info,
323 uint table_changes)
324 {
325 /* Table definition is locked for SEQUENCE tables */
326 return(COMPATIBLE_DATA_YES);
327 }
328
329
external_lock(THD * thd,int lock_type)330 int ha_sequence::external_lock(THD *thd, int lock_type)
331 {
332 int error= file->external_lock(thd, lock_type);
333
334 /*
335 Copy lock flag to satisfy DBUG_ASSERT checks in ha_* functions in
336 handler.cc when we later call it with file->ha_..()
337 */
338 if (!error)
339 file->m_lock_type= lock_type;
340 return error;
341 }
342
343 /*
344 Squence engine error deal method
345 */
346
print_error(int error,myf errflag)347 void ha_sequence::print_error(int error, myf errflag)
348 {
349 const char *sequence_db= table_share->db.str;
350 const char *sequence_name= table_share->table_name.str;
351 DBUG_ENTER("ha_sequence::print_error");
352
353 switch (error) {
354 case HA_ERR_SEQUENCE_INVALID_DATA:
355 {
356 my_error(ER_SEQUENCE_INVALID_DATA, MYF(errflag), sequence_db,
357 sequence_name);
358 DBUG_VOID_RETURN;
359 }
360 case HA_ERR_SEQUENCE_RUN_OUT:
361 {
362 my_error(ER_SEQUENCE_RUN_OUT, MYF(errflag), sequence_db, sequence_name);
363 DBUG_VOID_RETURN;
364 }
365 case HA_ERR_WRONG_COMMAND:
366 my_error(ER_ILLEGAL_HA, MYF(0), "SEQUENCE", sequence_db, sequence_name);
367 DBUG_VOID_RETURN;
368 case ER_WRONG_INSERT_INTO_SEQUENCE:
369 my_error(error, MYF(0));
370 DBUG_VOID_RETURN;
371 }
372 file->print_error(error, errflag);
373 DBUG_VOID_RETURN;
374 }
375
376 /*****************************************************************************
377 Sequence plugin interface
378 *****************************************************************************/
379
380 /*
381 Create an new handler
382 */
383
sequence_create_handler(handlerton * hton,TABLE_SHARE * share,MEM_ROOT * mem_root)384 static handler *sequence_create_handler(handlerton *hton,
385 TABLE_SHARE *share,
386 MEM_ROOT *mem_root)
387 {
388 DBUG_ENTER("sequence_create_handler");
389 DBUG_RETURN(new (mem_root) ha_sequence(hton, share));
390 }
391
392
393 /*
394 Sequence engine end.
395
396 SYNOPSIS
397 sequence_end()
398 p handlerton.
399 type panic type.
400 RETURN VALUES
401 0 Success
402 !=0 Failure
403 */
sequence_end(handlerton * hton,ha_panic_function type)404 static int sequence_end(handlerton* hton,
405 ha_panic_function type __attribute__((unused)))
406 {
407 DBUG_ENTER("sequence_end");
408 DBUG_RETURN(0);
409 }
410
411
412 /*
413 Sequence engine init.
414
415 SYNOPSIS
416 sequence_initialize()
417
418 @param p handlerton.
419
420 retval 0 Success
421 retval !=0 Failure
422 */
423
sequence_initialize(void * p)424 static int sequence_initialize(void *p)
425 {
426 handlerton *local_sequence_hton= (handlerton *)p;
427 DBUG_ENTER("sequence_initialize");
428
429 local_sequence_hton->state= SHOW_OPTION_YES;
430 local_sequence_hton->db_type= DB_TYPE_SEQUENCE;
431 local_sequence_hton->create= sequence_create_handler;
432 local_sequence_hton->panic= sequence_end;
433 local_sequence_hton->flags= (HTON_NOT_USER_SELECTABLE |
434 HTON_HIDDEN |
435 HTON_TEMPORARY_NOT_SUPPORTED |
436 HTON_ALTER_NOT_SUPPORTED |
437 HTON_NO_PARTITION);
438 DBUG_RETURN(0);
439 }
440
441
442 static struct st_mysql_storage_engine sequence_storage_engine=
443 { MYSQL_HANDLERTON_INTERFACE_VERSION };
444
maria_declare_plugin(sql_sequence)445 maria_declare_plugin(sql_sequence)
446 {
447 MYSQL_STORAGE_ENGINE_PLUGIN,
448 &sequence_storage_engine,
449 "SQL_SEQUENCE",
450 "jianwei.zhao @ Aliyun & Monty @ MariaDB corp",
451 "Sequence Storage Engine for CREATE SEQUENCE",
452 PLUGIN_LICENSE_GPL,
453 sequence_initialize, /* Plugin Init */
454 NULL, /* Plugin Deinit */
455 0x0100, /* 1.0 */
456 NULL, /* status variables */
457 NULL, /* system variables */
458 "1.0", /* string version */
459 MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
460 }
461 maria_declare_plugin_end;
462