Skip to content

Commit 56aae03

Browse files
committed
Bug#21854004: GCOLS:INNODB: FAILING ASSERTION: I < TABLE->N_DEF
When changing a generated column's expression, a check for whether the expression was the same as before was missing. If the expression is changed, storage engine may need to rebuild the column. (A virtual column generally does not need rebuilding, but if is indexed, the index will need rebuilding). Two new utility functions to check that generation expressions are equal are implemented: Field::gcol_expr_is_equal(). In fill_alter_inplace_info(), generate change commands for the storage engine according to the following: - It is possible a convert a base column to a generated column and vice versa as long as the generated column is stored. Set the handler flag ALTER_STORED_COLUMN_TYPE for this case. - If both source and target column is generated and generation expression is modified, set handler flag ALTER_STORED_COLUMN_TYPE if column is stored and ALTER_VIRTUAL_COLUMN_TYPE if column is virtual. Elaborate testing has been added: - Check modification to and from generated column. - Check modification of generated column with and without non-unique index and unique index.
1 parent 012d6f4 commit 56aae03

File tree

6 files changed

+329
-3
lines changed

6 files changed

+329
-3
lines changed

mysql-test/suite/gcol/inc/gcol_column_def_options.inc

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,3 +454,97 @@ INSERT INTO t1 (a) VALUES (NULL), (1);
454454
SELECT * FROM t1;
455455

456456
DROP TABLE t1;
457+
458+
--echo # Bug#21854004: GCOLS:INNODB: FAILING ASSERTION: I < TABLE->N_DEF
459+
CREATE TABLE t1(
460+
col1 INTEGER PRIMARY KEY,
461+
col2 INTEGER,
462+
col3 INTEGER,
463+
col4 INTEGER,
464+
vgc1 INTEGER AS (col2 + col3) VIRTUAL,
465+
sgc1 INTEGER AS (col2 - col3) STORED
466+
);
467+
468+
INSERT INTO t1(col1, col2, col3) VALUES
469+
(1, 10, 100), (2, 20, 200);
470+
471+
SELECT * FROM t1;
472+
473+
# Change expression of a virtual generated column
474+
ALTER TABLE t1 MODIFY COLUMN vgc1 INTEGER AS (col2 * col3) VIRTUAL;
475+
SELECT * FROM t1;
476+
477+
# Change expression of a stored generated column
478+
ALTER TABLE t1 MODIFY COLUMN sgc1 INTEGER AS (col2 / col3) STORED;
479+
SELECT * FROM t1;
480+
481+
ALTER TABLE t1 MODIFY COLUMN vgc1 INTEGER AS (col2 + col3) VIRTUAL;
482+
ALTER TABLE t1 MODIFY COLUMN sgc1 INTEGER AS (col2 - col3) STORED;
483+
484+
if ($support_virtual_index)
485+
{
486+
ALTER TABLE t1 ADD INDEX vgc1 (vgc1);
487+
}
488+
ALTER TABLE t1 ADD INDEX sgc1 (sgc1);
489+
490+
if ($support_virtual_index)
491+
{
492+
# Change expression of a virtual generated column, with index
493+
ALTER TABLE t1 MODIFY COLUMN vgc1 INTEGER AS (col2 * col3) VIRTUAL;
494+
SELECT * FROM t1;
495+
SELECT vgc1 FROM t1;
496+
}
497+
498+
# Change expression of a stored generated column, with index
499+
ALTER TABLE t1 MODIFY COLUMN sgc1 INTEGER AS (col2 / col3) STORED;
500+
SELECT * FROM t1;
501+
SELECT sgc1 FROM t1;
502+
503+
if ($support_virtual_index)
504+
{
505+
ALTER TABLE t1 DROP INDEX vgc1;
506+
}
507+
ALTER TABLE t1 DROP INDEX sgc1;
508+
509+
if ($support_virtual_index)
510+
{
511+
ALTER TABLE t1 MODIFY COLUMN vgc1 INTEGER AS (col2 + col3) VIRTUAL;
512+
ALTER TABLE t1 ADD UNIQUE INDEX vgc1 (vgc1);
513+
}
514+
ALTER TABLE t1 MODIFY COLUMN sgc1 INTEGER AS (col2 - col3) STORED;
515+
ALTER TABLE t1 ADD UNIQUE INDEX sgc1 (sgc1);
516+
517+
# Change expression of a virtual generated column, with unique index
518+
if ($support_virtual_index)
519+
{
520+
--error ER_DUP_ENTRY
521+
ALTER TABLE t1 MODIFY COLUMN vgc1 INTEGER AS (col2 / col3) VIRTUAL;
522+
}
523+
--error ER_DUP_ENTRY
524+
ALTER TABLE t1 MODIFY COLUMN sgc1 INTEGER AS (col2 / col3) STORED;
525+
526+
ALTER TABLE t1 MODIFY COLUMN vgc1 INTEGER AS (col2 * col3) VIRTUAL;
527+
SELECT * FROM t1;
528+
SELECT vgc1 FROM t1;
529+
530+
ALTER TABLE t1 MODIFY COLUMN sgc1 INTEGER AS (col2 * col3) STORED;
531+
SELECT * FROM t1;
532+
SELECT sgc1 FROM t1;
533+
534+
# Change virtual generated column to become stored
535+
--error ER_UNSUPPORTED_ACTION_ON_GENERATED_COLUMN
536+
ALTER TABLE t1 MODIFY COLUMN vgc1 INTEGER AS (col2 * col3) STORED;
537+
538+
# Change stored generated column to become virtual
539+
--error ER_UNSUPPORTED_ACTION_ON_GENERATED_COLUMN
540+
ALTER TABLE t1 MODIFY COLUMN sgc1 INTEGER AS (col2 / col3) VIRTUAL;
541+
542+
# Change base column to become stored generated column:
543+
ALTER TABLE t1 MODIFY COLUMN col4 INTEGER AS (col1 + col2 + col3) STORED;
544+
SELECT * FROM t1;
545+
546+
# Change stored generated column to become base column:
547+
ALTER TABLE t1 MODIFY COLUMN col4 INTEGER;
548+
SELECT * FROM t1;
549+
550+
DROP TABLE t1;

mysql-test/suite/gcol/r/gcol_column_def_options_innodb.result

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -538,8 +538,8 @@ ALTER TABLE t ADD c int(11) as (a+b), ADD f varchar(10) as ('aaa');
538538
affected rows: 0
539539
info: Records: 0 Duplicates: 0 Warnings: 0
540540
ALTER TABLE t CHANGE c c int(11) as (a), CHANGE f f varchar(10) as('bbb');
541-
affected rows: 0
542-
info: Records: 0 Duplicates: 0 Warnings: 0
541+
affected rows: 1
542+
info: Records: 1 Duplicates: 0 Warnings: 0
543543
# Change order should be ALGORITHM=INPLACE on Innodb
544544
ALTER TABLE t CHANGE c c int(11) as (a) after f;
545545
affected rows: 1
@@ -589,6 +589,96 @@ a b c
589589
NULL NULL NULL
590590
1 -1 0
591591
DROP TABLE t1;
592+
# Bug#21854004: GCOLS:INNODB: FAILING ASSERTION: I < TABLE->N_DEF
593+
CREATE TABLE t1(
594+
col1 INTEGER PRIMARY KEY,
595+
col2 INTEGER,
596+
col3 INTEGER,
597+
col4 INTEGER,
598+
vgc1 INTEGER AS (col2 + col3) VIRTUAL,
599+
sgc1 INTEGER AS (col2 - col3) STORED
600+
);
601+
INSERT INTO t1(col1, col2, col3) VALUES
602+
(1, 10, 100), (2, 20, 200);
603+
SELECT * FROM t1;
604+
col1 col2 col3 col4 vgc1 sgc1
605+
1 10 100 NULL 110 -90
606+
2 20 200 NULL 220 -180
607+
ALTER TABLE t1 MODIFY COLUMN vgc1 INTEGER AS (col2 * col3) VIRTUAL;
608+
SELECT * FROM t1;
609+
col1 col2 col3 col4 vgc1 sgc1
610+
1 10 100 NULL 1000 -90
611+
2 20 200 NULL 4000 -180
612+
ALTER TABLE t1 MODIFY COLUMN sgc1 INTEGER AS (col2 / col3) STORED;
613+
SELECT * FROM t1;
614+
col1 col2 col3 col4 vgc1 sgc1
615+
1 10 100 NULL 1000 0
616+
2 20 200 NULL 4000 0
617+
ALTER TABLE t1 MODIFY COLUMN vgc1 INTEGER AS (col2 + col3) VIRTUAL;
618+
ALTER TABLE t1 MODIFY COLUMN sgc1 INTEGER AS (col2 - col3) STORED;
619+
ALTER TABLE t1 ADD INDEX vgc1 (vgc1);
620+
ALTER TABLE t1 ADD INDEX sgc1 (sgc1);
621+
ALTER TABLE t1 MODIFY COLUMN vgc1 INTEGER AS (col2 * col3) VIRTUAL;
622+
SELECT * FROM t1;
623+
col1 col2 col3 col4 vgc1 sgc1
624+
1 10 100 NULL 1000 -90
625+
2 20 200 NULL 4000 -180
626+
SELECT vgc1 FROM t1;
627+
vgc1
628+
1000
629+
4000
630+
ALTER TABLE t1 MODIFY COLUMN sgc1 INTEGER AS (col2 / col3) STORED;
631+
SELECT * FROM t1;
632+
col1 col2 col3 col4 vgc1 sgc1
633+
1 10 100 NULL 1000 0
634+
2 20 200 NULL 4000 0
635+
SELECT sgc1 FROM t1;
636+
sgc1
637+
0
638+
0
639+
ALTER TABLE t1 DROP INDEX vgc1;
640+
ALTER TABLE t1 DROP INDEX sgc1;
641+
ALTER TABLE t1 MODIFY COLUMN vgc1 INTEGER AS (col2 + col3) VIRTUAL;
642+
ALTER TABLE t1 ADD UNIQUE INDEX vgc1 (vgc1);
643+
ALTER TABLE t1 MODIFY COLUMN sgc1 INTEGER AS (col2 - col3) STORED;
644+
ALTER TABLE t1 ADD UNIQUE INDEX sgc1 (sgc1);
645+
ALTER TABLE t1 MODIFY COLUMN vgc1 INTEGER AS (col2 / col3) VIRTUAL;
646+
ERROR 23000: Duplicate entry '0' for key 'vgc1'
647+
ALTER TABLE t1 MODIFY COLUMN sgc1 INTEGER AS (col2 / col3) STORED;
648+
ERROR 23000: Duplicate entry '0' for key 'sgc1'
649+
ALTER TABLE t1 MODIFY COLUMN vgc1 INTEGER AS (col2 * col3) VIRTUAL;
650+
SELECT * FROM t1;
651+
col1 col2 col3 col4 vgc1 sgc1
652+
1 10 100 NULL 1000 -90
653+
2 20 200 NULL 4000 -180
654+
SELECT vgc1 FROM t1;
655+
vgc1
656+
1000
657+
4000
658+
ALTER TABLE t1 MODIFY COLUMN sgc1 INTEGER AS (col2 * col3) STORED;
659+
SELECT * FROM t1;
660+
col1 col2 col3 col4 vgc1 sgc1
661+
1 10 100 NULL 1000 1000
662+
2 20 200 NULL 4000 4000
663+
SELECT sgc1 FROM t1;
664+
sgc1
665+
1000
666+
4000
667+
ALTER TABLE t1 MODIFY COLUMN vgc1 INTEGER AS (col2 * col3) STORED;
668+
ERROR HY000: 'Changing the STORED status' is not supported for generated columns.
669+
ALTER TABLE t1 MODIFY COLUMN sgc1 INTEGER AS (col2 / col3) VIRTUAL;
670+
ERROR HY000: 'Changing the STORED status' is not supported for generated columns.
671+
ALTER TABLE t1 MODIFY COLUMN col4 INTEGER AS (col1 + col2 + col3) STORED;
672+
SELECT * FROM t1;
673+
col1 col2 col3 col4 vgc1 sgc1
674+
1 10 100 111 1000 1000
675+
2 20 200 222 4000 4000
676+
ALTER TABLE t1 MODIFY COLUMN col4 INTEGER;
677+
SELECT * FROM t1;
678+
col1 col2 col3 col4 vgc1 sgc1
679+
1 10 100 111 1000 1000
680+
2 20 200 222 4000 4000
681+
DROP TABLE t1;
592682
DROP VIEW IF EXISTS v1,v2;
593683
DROP TABLE IF EXISTS t1,t2,t3;
594684
DROP PROCEDURE IF EXISTS p1;

mysql-test/suite/gcol/r/gcol_column_def_options_myisam.result

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,81 @@ a b c
542542
NULL NULL NULL
543543
1 -1 0
544544
DROP TABLE t1;
545+
# Bug#21854004: GCOLS:INNODB: FAILING ASSERTION: I < TABLE->N_DEF
546+
CREATE TABLE t1(
547+
col1 INTEGER PRIMARY KEY,
548+
col2 INTEGER,
549+
col3 INTEGER,
550+
col4 INTEGER,
551+
vgc1 INTEGER AS (col2 + col3) VIRTUAL,
552+
sgc1 INTEGER AS (col2 - col3) STORED
553+
);
554+
INSERT INTO t1(col1, col2, col3) VALUES
555+
(1, 10, 100), (2, 20, 200);
556+
SELECT * FROM t1;
557+
col1 col2 col3 col4 vgc1 sgc1
558+
1 10 100 NULL 110 -90
559+
2 20 200 NULL 220 -180
560+
ALTER TABLE t1 MODIFY COLUMN vgc1 INTEGER AS (col2 * col3) VIRTUAL;
561+
SELECT * FROM t1;
562+
col1 col2 col3 col4 vgc1 sgc1
563+
1 10 100 NULL 1000 -90
564+
2 20 200 NULL 4000 -180
565+
ALTER TABLE t1 MODIFY COLUMN sgc1 INTEGER AS (col2 / col3) STORED;
566+
SELECT * FROM t1;
567+
col1 col2 col3 col4 vgc1 sgc1
568+
1 10 100 NULL 1000 0
569+
2 20 200 NULL 4000 0
570+
ALTER TABLE t1 MODIFY COLUMN vgc1 INTEGER AS (col2 + col3) VIRTUAL;
571+
ALTER TABLE t1 MODIFY COLUMN sgc1 INTEGER AS (col2 - col3) STORED;
572+
ALTER TABLE t1 ADD INDEX sgc1 (sgc1);
573+
ALTER TABLE t1 MODIFY COLUMN sgc1 INTEGER AS (col2 / col3) STORED;
574+
SELECT * FROM t1;
575+
col1 col2 col3 col4 vgc1 sgc1
576+
1 10 100 NULL 110 0
577+
2 20 200 NULL 220 0
578+
SELECT sgc1 FROM t1;
579+
sgc1
580+
0
581+
0
582+
ALTER TABLE t1 DROP INDEX sgc1;
583+
ALTER TABLE t1 MODIFY COLUMN sgc1 INTEGER AS (col2 - col3) STORED;
584+
ALTER TABLE t1 ADD UNIQUE INDEX sgc1 (sgc1);
585+
ALTER TABLE t1 MODIFY COLUMN sgc1 INTEGER AS (col2 / col3) STORED;
586+
ERROR 23000: Duplicate entry '0' for key 'sgc1'
587+
ALTER TABLE t1 MODIFY COLUMN vgc1 INTEGER AS (col2 * col3) VIRTUAL;
588+
SELECT * FROM t1;
589+
col1 col2 col3 col4 vgc1 sgc1
590+
1 10 100 NULL 1000 -90
591+
2 20 200 NULL 4000 -180
592+
SELECT vgc1 FROM t1;
593+
vgc1
594+
1000
595+
4000
596+
ALTER TABLE t1 MODIFY COLUMN sgc1 INTEGER AS (col2 * col3) STORED;
597+
SELECT * FROM t1;
598+
col1 col2 col3 col4 vgc1 sgc1
599+
1 10 100 NULL 1000 1000
600+
2 20 200 NULL 4000 4000
601+
SELECT sgc1 FROM t1;
602+
sgc1
603+
1000
604+
4000
605+
ALTER TABLE t1 MODIFY COLUMN vgc1 INTEGER AS (col2 * col3) STORED;
606+
ERROR HY000: 'Changing the STORED status' is not supported for generated columns.
607+
ALTER TABLE t1 MODIFY COLUMN sgc1 INTEGER AS (col2 / col3) VIRTUAL;
608+
ERROR HY000: 'Changing the STORED status' is not supported for generated columns.
609+
ALTER TABLE t1 MODIFY COLUMN col4 INTEGER AS (col1 + col2 + col3) STORED;
610+
SELECT * FROM t1;
611+
col1 col2 col3 col4 vgc1 sgc1
612+
1 10 100 111 1000 1000
613+
2 20 200 222 4000 4000
614+
ALTER TABLE t1 MODIFY COLUMN col4 INTEGER;
615+
SELECT * FROM t1;
616+
col1 col2 col3 col4 vgc1 sgc1
617+
1 10 100 111 1000 1000
618+
2 20 200 222 4000 4000
619+
DROP TABLE t1;
545620
DROP VIEW IF EXISTS v1,v2;
546621
DROP TABLE IF EXISTS t1,t2,t3;
547622
DROP PROCEDURE IF EXISTS p1;

sql/field.cc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "template_utils.h" // pointer_cast
4141
#include "tztime.h" // Time_zone
4242
#include "spatial.h" // Geometry
43+
#include "sql_base.h" // is_equal
4344

4445
#include <algorithm>
4546
#include <memory> // auto_ptr
@@ -7102,6 +7103,38 @@ type_conversion_status Field_str::store(double nr)
71027103
}
71037104

71047105

7106+
/**
7107+
Check whether generated columns' expressions are the same.
7108+
7109+
@param field An existing field to compare against
7110+
7111+
@return true means the same, otherwise not.
7112+
*/
7113+
7114+
bool Field::gcol_expr_is_equal(const Field *field) const
7115+
{
7116+
DBUG_ASSERT(is_gcol() && field->is_gcol());
7117+
7118+
return gcol_info->expr_item->eq(field->gcol_info->expr_item, true);
7119+
}
7120+
7121+
7122+
/**
7123+
Check whether generated columns' expressions are the same.
7124+
7125+
@param field A new field to compare against
7126+
7127+
@return true means the same, otherwise not.
7128+
*/
7129+
7130+
bool Field::gcol_expr_is_equal(const Create_field *field) const
7131+
{
7132+
DBUG_ASSERT(is_gcol() && field->is_gcol());
7133+
7134+
return ::is_equal(&gcol_info->expr_str, &field->gcol_info->expr_str);
7135+
}
7136+
7137+
71057138
uint Field::is_equal(Create_field *new_field)
71067139
{
71077140
return (new_field->sql_type == real_type());

sql/field.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,8 @@ class Field: public Proto_field
890890
static bool type_can_have_key_part(enum_field_types);
891891
static enum_field_types field_type_merge(enum_field_types, enum_field_types);
892892
static Item_result result_merge_type(enum_field_types);
893+
bool gcol_expr_is_equal(const Field *field) const;
894+
bool gcol_expr_is_equal(const Create_field *field) const;
893895
virtual bool eq(Field *field)
894896
{
895897
return (ptr == field->ptr && m_null_ptr == field->m_null_ptr &&
@@ -1467,11 +1469,13 @@ class Field: public Proto_field
14671469
virtual uint32 max_display_length()= 0;
14681470

14691471
/**
1470-
Whether a field being created is compatible with a existing one.
1472+
Whether a field being created is type-compatible with an existing one.
14711473
14721474
Used by the ALTER TABLE code to evaluate whether the new definition
14731475
of a table is compatible with the old definition so that it can
14741476
determine if data needs to be copied over (table data change).
1477+
Constraints and generation clause (default value, generation expression)
1478+
are not checked by this function.
14751479
*/
14761480
virtual uint is_equal(Create_field *new_field);
14771481
/* convert decimal to longlong with overflow check */
@@ -4325,6 +4329,7 @@ class Create_field :public Sql_alloc
43254329
/* Used to make a clone of this object for ALTER/CREATE TABLE */
43264330
Create_field *clone(MEM_ROOT *mem_root) const
43274331
{ return new (mem_root) Create_field(*this); }
4332+
bool is_gcol() const { return gcol_info; }
43284333
bool is_virtual_gcol() const
43294334
{ return gcol_info && !gcol_info->get_field_stored(); }
43304335
void create_length_to_internal_length(void);

0 commit comments

Comments
 (0)