Skip to content

Commit 2da5cc3

Browse files
Guilhem Bichotbjornmu
authored andcommitted
Bug#21833760 CALC_DAYNR: ASSERTION `DELSUM+(INT) Y/4-TEMP >= 0' FAILED
Virtual generated column ("vgcol") c13 depends on vgcol c11 which depends on base columns c8 and c9. A query reads c13. Thus c11, and thus c8, c9, are added to read_set. An index-only scan on c13 is used. It reads only c13 from the engine. update_generated_read_fields() sees that c11 is in read_set, and is not in the index, so it calculates it, based on an uninitialized value of c8, c9; DATE/TIME functions don't support really abnormal values of arguments, and this causes problems. If using INT columns and simpler generation expressions, we still get Valgrind warnings. Fix: if the Optimizer said "index-only scan" (table->keyread=true) it means it knows that the index provides everything; so, if keyread=true don't try to calculate anything. Note that this logic may have to be reconsidered when we fix Bug 21815348, see the @todo in code. (cherry picked from commit 283aa2f6c8eaf20ab5be19f04e900d17571db4f6) Conflicts: mysql-test/suite/gcol/r/gcol_bugfixes.result mysql-test/suite/gcol/t/gcol_bugfixes.test
1 parent 727a5c9 commit 2da5cc3

File tree

3 files changed

+77
-44
lines changed

3 files changed

+77
-44
lines changed

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,3 +477,29 @@ a b c
477477
1 2 00
478478
3 3 11
479479
DROP TABLE t;
480+
#
481+
# Bug#21833760 CALC_DAYNR: ASSERTION `DELSUM+(INT) Y/4-TEMP >= 0' FAILED.
482+
#
483+
CREATE TABLE C(
484+
c1 INT AUTO_INCREMENT,
485+
c8 DATETIME,
486+
c9 TIME,
487+
c11 TIME GENERATED ALWAYS AS(ADDTIME(c8,c9)) VIRTUAL,
488+
c13 TIME GENERATED ALWAYS AS(ADDTIME(c8,c11)) VIRTUAL,
489+
PRIMARY KEY(c1),
490+
UNIQUE KEY(c13)
491+
);
492+
INSERT INTO C (c8,c9) VALUES('1970-01-01',0),('1970-01-01',1);
493+
CREATE VIEW view_C AS SELECT * FROM C;
494+
SELECT /*+ NO_BNL(t1) */ t1.c13 FROM C AS t2 STRAIGHT_JOIN C AS t1 FORCE INDEX(c13);
495+
c13
496+
00:00:00
497+
00:00:01
498+
00:00:00
499+
00:00:01
500+
SELECT DISTINCT t1.c13 FROM C AS t1, view_C AS t2;
501+
c13
502+
00:00:00
503+
00:00:01
504+
DROP TABLE C;
505+
DROP VIEW view_C;

mysql-test/suite/gcol/t/gcol_bugfixes.test

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,3 +471,27 @@ INSERT INTO t (a, b) VALUES (1, '2'), (3, '3');
471471
# The next statement used to crash.
472472
SELECT * FROM t;
473473
DROP TABLE t;
474+
475+
--echo #
476+
--echo # Bug#21833760 CALC_DAYNR: ASSERTION `DELSUM+(INT) Y/4-TEMP >= 0' FAILED.
477+
--echo #
478+
479+
CREATE TABLE C(
480+
c1 INT AUTO_INCREMENT,
481+
c8 DATETIME,
482+
c9 TIME,
483+
c11 TIME GENERATED ALWAYS AS(ADDTIME(c8,c9)) VIRTUAL,
484+
c13 TIME GENERATED ALWAYS AS(ADDTIME(c8,c11)) VIRTUAL,
485+
PRIMARY KEY(c1),
486+
UNIQUE KEY(c13)
487+
);
488+
489+
INSERT INTO C (c8,c9) VALUES('1970-01-01',0),('1970-01-01',1);
490+
491+
CREATE VIEW view_C AS SELECT * FROM C;
492+
493+
SELECT /*+ NO_BNL(t1) */ t1.c13 FROM C AS t2 STRAIGHT_JOIN C AS t1 FORCE INDEX(c13);
494+
SELECT DISTINCT t1.c13 FROM C AS t1, view_C AS t2;
495+
496+
DROP TABLE C;
497+
DROP VIEW view_C;

sql/table.cc

Lines changed: 27 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7456,39 +7456,6 @@ bool is_simple_order(ORDER *order)
74567456
return TRUE;
74577457
}
74587458

7459-
/**
7460-
Check whether a given virtual generated column is part of a covering index.
7461-
7462-
@note that this ignores the number of key parts used by MySQL: if the index
7463-
is on (other, vgcol) and MySQL chooses to use it but only its first
7464-
key part, this function still returns true. Fortunately, if vgcol is needed
7465-
it is in read_set, and is part of the complete index, then InnoDB will read
7466-
the complete index entry (see build_template_needs_field()) and provide the
7467-
vgcol's value. So the number of key parts used by MySQL is irrelevant here.
7468-
7469-
@param table pointer of the table
7470-
@param index_no the number of index to be checked
7471-
@param vfield the pointer of checked virtual generated column
7472-
7473-
@return true if the column is covered by the given index.
7474-
*/
7475-
static bool
7476-
index_contains_this_virtual_gcol(const TABLE *table, uint index_no,
7477-
const Field *vfield)
7478-
{
7479-
DBUG_ASSERT(index_no != MAX_KEY && table->key_read);
7480-
const KEY *key= table->key_info + index_no;
7481-
7482-
for (KEY_PART_INFO *key_part= key->key_part;
7483-
key_part < key->key_part + key->user_defined_key_parts;
7484-
key_part++)
7485-
{
7486-
if (key_part->field == vfield)
7487-
return true;
7488-
}
7489-
return false;
7490-
}
7491-
74927459

74937460
/**
74947461
Repoint a table's fields from old_rec to new_rec
@@ -7508,21 +7475,42 @@ void repoint_field_to_record(TABLE *table, uchar *old_rec, uchar *new_rec)
75087475

75097476

75107477
/**
7511-
Evaluate each virtual generated column marked in read_set.
7478+
Evaluate necessary virtual generated columns.
75127479
This is used right after reading a row from the storage engine.
75137480
7514-
@note this is not necessary for stored generated fields.
7481+
@note this is not necessary for stored generated columns, as they are
7482+
provided by the storage engine.
75157483
75167484
@param buf[in,out] the buffer to store data
75177485
@param table the TABLE object
7518-
@param active_index the number of key for index scan(MAX_KEY is default)
7486+
@param active_index the number of key for index scan (MAX_KEY is default)
75197487
75207488
@return true if error.
7489+
7490+
@todo see below for potential conflict with Bug#21815348 .
75217491
*/
75227492
bool update_generated_read_fields(uchar *buf, TABLE *table, uint active_index)
75237493
{
75247494
DBUG_ENTER("update_generated_read_fields");
75257495
DBUG_ASSERT(table && table->vfield);
7496+
if (active_index != MAX_KEY && table->key_read)
7497+
{
7498+
/*
7499+
The covering index is providing all necessary columns, including
7500+
generated ones.
7501+
Note that this logic may have to be reconsidered when we fix
7502+
Bug#21815348; indeed, for that bug it could be possible to implement the
7503+
following optimization: if A is an indexed base column, and B is a
7504+
virtual generated column dependent on A, "select B from t" could choose
7505+
an index-only scan over the index of A and calculate values of B on the
7506+
fly. In that case, we would come here, however calculation of B would
7507+
still be needed.
7508+
Currently MySQL doesn't choose an index scan in that case because it
7509+
considers B as independent from A, in its index-scan decision logic.
7510+
*/
7511+
DBUG_RETURN(false);
7512+
}
7513+
75267514
int error= 0;
75277515

75287516
/*
@@ -7537,18 +7525,13 @@ bool update_generated_read_fields(uchar *buf, TABLE *table, uint active_index)
75377525
{
75387526
Field *vfield= *vfield_ptr;
75397527
DBUG_ASSERT(vfield->gcol_info && vfield->gcol_info->expr_item);
7540-
/**
7528+
/*
75417529
Only calculate those virtual generated fields that are marked in the
7542-
read_set bitmap and not filled.
7543-
@todo: Consider replacing index_contains_this_virtual_gcol with
7544-
a test on part_of_key below.
7530+
read_set bitmap.
75457531
*/
75467532
if (!vfield->stored_in_db &&
7547-
bitmap_is_set(table->read_set, vfield->field_index) &&
7548-
!(active_index != MAX_KEY && table->key_read &&
7549-
index_contains_this_virtual_gcol(table, active_index, vfield)))
7533+
bitmap_is_set(table->read_set, vfield->field_index))
75507534
{
7551-
/* Generate the actual value of the generated fields */
75527535
error= vfield->gcol_info->expr_item->save_in_field(vfield, 0);
75537536
DBUG_PRINT("info", ("field '%s' - updated", vfield->field_name));
75547537
if (error && !table->in_use->is_error())

0 commit comments

Comments
 (0)