Skip to content

Commit 1b175b3

Browse files
Bug#22195588
SETTING NULL TO A DYNAMIC IN-MEMORY COLUMN CORRUPTS DISK COLUMN VALUES. When a table has dynamic columns and no var sized columns in memory, along with few other columns in disk, setting null to all those dynamic columns will end up corrupting the values in the disk based columns. The var part of a tuple comprises of the variable sized columns and the dynamic columns. When a dynamic column is set to null, it is completely ignored during the shrink phase of the tuple and is not stored. So when a table doesn't have any var sized columns and the dynamic columns are set to null, no var part should be written to the tuple. And during commit, the function inspects the header_bits flag to check if VAR_PART is present. If there is no var part present, it expects the disk column content to be stored from the end of the fixed part. But during shrink phase, the length of the var part is written to the end of fix part even when there is no var part present (The length would be zero in this case). So during commit, since the varpart is not present, ndbd assumes disk column content is next to the fixed part and starts reading but since the length(0) is there, the tuple gets corrupted. This patch fixes this problem by removing the varpart length from the shrunken tuple when no varpart is present and ensures that the disk column content is written next to the end of the fix part.
1 parent cead21a commit 1b175b3

File tree

3 files changed

+300
-2
lines changed

3 files changed

+300
-2
lines changed

mysql-test/suite/ndb/r/ndb_dd_disk2memory.result

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,3 +503,165 @@ [email protected] Z001
503503
504504
505505
506+
#
507+
# Bug#22195588 : SETTING NULL TO A DYNAMIC IN-MEMORY COLUMN CORRUPTS DISK COLUMN VALUES
508+
#
509+
# Setting up tables
510+
create table t1(
511+
a int unsigned auto_increment primary key,
512+
b int,
513+
c int default null column_format dynamic storage memory,
514+
d varchar(100) default null column_format dynamic storage memory
515+
)tablespace table_space1 storage disk engine=ndbcluster;
516+
# Populate t1
517+
# Setting all the dynamic columns to NULL and varying disk parts
518+
insert into t1 () values();
519+
insert into t1 (b) values (10);
520+
# Populating values to single dynamic column and varying disk parts
521+
insert into t1 (c) values (20);
522+
insert into t1 (b, c) values (10, 20);
523+
insert into t1 (d) values (repeat('0123456789', 3));
524+
insert into t1 (d) values (repeat('0123456789', 10));
525+
insert into t1 (b, d) values (10, repeat('0123456789',3));
526+
insert into t1 (b, d) values (10, repeat('0123456789',10));
527+
# Both dynamic columns have non NULL values and disk columns varied
528+
insert into t1 (c, d) values (20, repeat('0123456789',3));
529+
insert into t1 (c, d) values (20, repeat('0123456789',10));
530+
insert into t1 (b, c, d) values (10, 20, repeat('0123456789',3));
531+
insert into t1 (b, c, d) values (10, 20, repeat('0123456789',10));
532+
# Verify that they are properly stored and retrieved
533+
select * from t1 order by a;
534+
a b c d
535+
1 NULL NULL NULL
536+
2 10 NULL NULL
537+
3 NULL 20 NULL
538+
4 10 20 NULL
539+
5 NULL NULL 012345678901234567890123456789
540+
6 NULL NULL 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
541+
7 10 NULL 012345678901234567890123456789
542+
8 10 NULL 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
543+
9 NULL 20 012345678901234567890123456789
544+
10 NULL 20 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
545+
11 10 20 012345678901234567890123456789
546+
12 10 20 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
547+
# Update some rows to verify that the varpart is reallocating properly
548+
# Updating NULL dynamic columns and assigning NON-NULL values
549+
update t1 set c = 10 where a in (1,2);
550+
select * from t1 where a in (1,2) order by a;
551+
a b c d
552+
1 NULL 10 NULL
553+
2 10 10 NULL
554+
# Updating and setting NULL to NON-NULL dynamic columns
555+
update t1 set c = null where a in (1,2);
556+
select * from t1 where a in (1,2) order by a;
557+
a b c d
558+
1 NULL NULL NULL
559+
2 10 NULL NULL
560+
# Updating NULL dynamic var columns and assigning NON-NULL values
561+
update t1 set d = repeat('0123456789', 5) where a in (1,2);
562+
select * from t1 where a in (1,2) order by a;
563+
a b c d
564+
1 NULL NULL 01234567890123456789012345678901234567890123456789
565+
2 10 NULL 01234567890123456789012345678901234567890123456789
566+
# Updating NULL to NON-NULL and vice versa in dynamic columns
567+
update t1 set c = null, d = repeat('0123456789', 10) where a in (3,4);
568+
select * from t1 where a in (3,4) order by a;
569+
a b c d
570+
3 NULL NULL 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
571+
4 10 NULL 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
572+
# Updating and setting NULL to NON-NULL dynamic var columns
573+
update t1 set c = 20, d = null where a in (5,6,7,8);
574+
select * from t1 where a in (5,6,7,8) order by a;
575+
a b c d
576+
5 NULL 20 NULL
577+
6 NULL 20 NULL
578+
7 10 20 NULL
579+
8 10 20 NULL
580+
# Update all dynamic columns to NULL
581+
update t1 set c = null, d = null where a > 8;
582+
select * from t1 where a > 8 order by a;
583+
a b c d
584+
9 NULL NULL NULL
585+
10 NULL NULL NULL
586+
11 10 NULL NULL
587+
12 10 NULL NULL
588+
# Update all dynamic columns to NON-NULL values
589+
update t1 set c = 20, d = '0123456789' where a > 8;
590+
select * from t1 where a > 8 order by a;
591+
a b c d
592+
9 NULL 20 0123456789
593+
10 NULL 20 0123456789
594+
11 10 20 0123456789
595+
12 10 20 0123456789
596+
select * from t1 order by a;
597+
a b c d
598+
1 NULL NULL 01234567890123456789012345678901234567890123456789
599+
2 10 NULL 01234567890123456789012345678901234567890123456789
600+
3 NULL NULL 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
601+
4 10 NULL 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
602+
5 NULL 20 NULL
603+
6 NULL 20 NULL
604+
7 10 20 NULL
605+
8 10 20 NULL
606+
9 NULL 20 0123456789
607+
10 NULL 20 0123456789
608+
11 10 20 0123456789
609+
12 10 20 0123456789
610+
# Now do some transactions with multiple operations
611+
# Add more data to dynamic varchar and then set to NULL
612+
# Meanwhile insert value into int column
613+
select * from t1 where a = 1;
614+
a b c d
615+
1 NULL NULL 01234567890123456789012345678901234567890123456789
616+
begin;
617+
update t1 set d = repeat('0123456789', 10) where a = 1;
618+
update t1 set b = 20 where a = 1;
619+
update t1 set d = NULL where a = 1;
620+
update t1 set c = 30 where a = 1;
621+
commit;
622+
select * from t1 where a = 1;
623+
a b c d
624+
1 20 30 NULL
625+
# Setting up next transaction - make dynamic columns NULL
626+
update t1 set c = NULL where a = 1;
627+
# Insert and later set null to dynamic columns in the same transaction
628+
select * from t1 where a = 1;
629+
a b c d
630+
1 20 NULL NULL
631+
begin;
632+
update t1 set c = 30 where a = 1;
633+
update t1 set b = 30 where a = 1;
634+
update t1 set d = repeat('0123456789', 10) where a = 1;
635+
update t1 set b = 45 where a = 1;
636+
update t1 set d = repeat('0123456789', 5) where a = 1;
637+
update t1 set c = NULL where a = 1;
638+
update t1 set d = NULL where a = 1;
639+
commit;
640+
select * from t1 where a = 1;
641+
a b c d
642+
1 45 NULL NULL
643+
# Grow the dynamic varchar and then shrink it but still having more data than when it was at start.
644+
# Insert alternatively the other dynamic column with NULL but back to a NON-NULL value at end
645+
select * from t1 where a = 12;
646+
a b c d
647+
12 10 20 0123456789
648+
begin;
649+
update t1 set c = 42 where a = 12;
650+
update t1 set d = repeat('0123456789', 5) where a = 12;
651+
update t1 set c = NULL where a = 12;
652+
update t1 set d = repeat('0123456789', 10) where a = 12;
653+
update t1 set c = 30 where a = 12;
654+
update t1 set d = repeat('0123456789', 2) where a = 12;
655+
commit;
656+
select * from t1 where a = 12;
657+
a b c d
658+
12 10 30 01234567890123456789
659+
# Cleanup
660+
drop table t1;
661+
ALTER TABLESPACE table_space1
662+
DROP DATAFILE './table_space1/datafile.dat'
663+
ENGINE = NDB;
664+
DROP TABLESPACE table_space1
665+
ENGINE = NDB;
666+
DROP LOGFILE GROUP log_group1
667+
ENGINE =NDB;

mysql-test/suite/ndb/t/ndb_dd_disk2memory.test

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,122 @@ SELECT DISTINCTROW email, shipcode FROM test.t1, test.t2
273273
WHERE test.t1.infoID=test.t2.infoID ORDER BY dateentered DESC;
274274

275275
DROP TABLE test.t1,test.t2;
276+
--enable_query_log
277+
278+
#####################################################
279+
# Testing In-Memory Dynamic Columns with Disk Columns
280+
####################################################
281+
282+
--echo #
283+
--echo # Bug#22195588 : SETTING NULL TO A DYNAMIC IN-MEMORY COLUMN CORRUPTS DISK COLUMN VALUES
284+
--echo #
285+
286+
--echo # Setting up tables
287+
create table t1(
288+
a int unsigned auto_increment primary key,
289+
b int,
290+
c int default null column_format dynamic storage memory,
291+
d varchar(100) default null column_format dynamic storage memory
292+
)tablespace table_space1 storage disk engine=ndbcluster;
293+
294+
--echo # Populate t1
295+
--echo # Setting all the dynamic columns to NULL and varying disk parts
296+
insert into t1 () values();
297+
insert into t1 (b) values (10);
298+
299+
--echo # Populating values to single dynamic column and varying disk parts
300+
insert into t1 (c) values (20);
301+
insert into t1 (b, c) values (10, 20);
302+
303+
insert into t1 (d) values (repeat('0123456789', 3));
304+
insert into t1 (d) values (repeat('0123456789', 10));
305+
insert into t1 (b, d) values (10, repeat('0123456789',3));
306+
insert into t1 (b, d) values (10, repeat('0123456789',10));
307+
308+
--echo # Both dynamic columns have non NULL values and disk columns varied
309+
insert into t1 (c, d) values (20, repeat('0123456789',3));
310+
insert into t1 (c, d) values (20, repeat('0123456789',10));
311+
insert into t1 (b, c, d) values (10, 20, repeat('0123456789',3));
312+
insert into t1 (b, c, d) values (10, 20, repeat('0123456789',10));
313+
314+
--echo # Verify that they are properly stored and retrieved
315+
select * from t1 order by a;
316+
317+
--echo # Update some rows to verify that the varpart is reallocating properly
318+
--echo # Updating NULL dynamic columns and assigning NON-NULL values
319+
update t1 set c = 10 where a in (1,2);
320+
select * from t1 where a in (1,2) order by a;
321+
322+
--echo # Updating and setting NULL to NON-NULL dynamic columns
323+
update t1 set c = null where a in (1,2);
324+
select * from t1 where a in (1,2) order by a;
325+
326+
--echo # Updating NULL dynamic var columns and assigning NON-NULL values
327+
update t1 set d = repeat('0123456789', 5) where a in (1,2);
328+
select * from t1 where a in (1,2) order by a;
329+
330+
--echo # Updating NULL to NON-NULL and vice versa in dynamic columns
331+
update t1 set c = null, d = repeat('0123456789', 10) where a in (3,4);
332+
select * from t1 where a in (3,4) order by a;
333+
334+
--echo # Updating and setting NULL to NON-NULL dynamic var columns
335+
update t1 set c = 20, d = null where a in (5,6,7,8);
336+
select * from t1 where a in (5,6,7,8) order by a;
337+
338+
--echo # Update all dynamic columns to NULL
339+
update t1 set c = null, d = null where a > 8;
340+
select * from t1 where a > 8 order by a;
341+
342+
--echo # Update all dynamic columns to NON-NULL values
343+
update t1 set c = 20, d = '0123456789' where a > 8;
344+
select * from t1 where a > 8 order by a;
345+
346+
select * from t1 order by a;
347+
348+
--echo # Now do some transactions with multiple operations
349+
350+
--echo # Add more data to dynamic varchar and then set to NULL
351+
--echo # Meanwhile insert value into int column
352+
select * from t1 where a = 1;
353+
begin;
354+
update t1 set d = repeat('0123456789', 10) where a = 1;
355+
update t1 set b = 20 where a = 1;
356+
update t1 set d = NULL where a = 1;
357+
update t1 set c = 30 where a = 1;
358+
commit;
359+
select * from t1 where a = 1;
360+
361+
--echo # Setting up next transaction - make dynamic columns NULL
362+
update t1 set c = NULL where a = 1;
363+
364+
--echo # Insert and later set null to dynamic columns in the same transaction
365+
select * from t1 where a = 1;
366+
begin;
367+
update t1 set c = 30 where a = 1;
368+
update t1 set b = 30 where a = 1;
369+
update t1 set d = repeat('0123456789', 10) where a = 1;
370+
update t1 set b = 45 where a = 1;
371+
update t1 set d = repeat('0123456789', 5) where a = 1;
372+
update t1 set c = NULL where a = 1;
373+
update t1 set d = NULL where a = 1;
374+
commit;
375+
select * from t1 where a = 1;
376+
377+
--echo # Grow the dynamic varchar and then shrink it but still having more data than when it was at start.
378+
--echo # Insert alternatively the other dynamic column with NULL but back to a NON-NULL value at end
379+
select * from t1 where a = 12;
380+
begin;
381+
update t1 set c = 42 where a = 12;
382+
update t1 set d = repeat('0123456789', 5) where a = 12;
383+
update t1 set c = NULL where a = 12;
384+
update t1 set d = repeat('0123456789', 10) where a = 12;
385+
update t1 set c = 30 where a = 12;
386+
update t1 set d = repeat('0123456789', 2) where a = 12;
387+
commit;
388+
select * from t1 where a = 12;
389+
390+
--echo # Cleanup
391+
drop table t1;
276392

277393
#################
278394
# Test Cleanup

storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
2+
Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
33
44
This program is free software; you can redistribute it and/or modify
55
it under the terms of the GNU General Public License as published by
@@ -3926,7 +3926,27 @@ Dbtup::shrink_tuple(KeyReqStruct* req_struct, Uint32 sizes[2],
39263926
Uint32 varpart_len= Uint32(dst_ptr - varstart);
39273927
vp->m_len = varpart_len;
39283928
sizes[MM] = varpart_len;
3929-
ptr->m_header_bits |= (varpart_len) ? Tuple_header::VAR_PART : 0;
3929+
if (varpart_len != 0)
3930+
{
3931+
ptr->m_header_bits |= Tuple_header::VAR_PART;
3932+
}
3933+
else if ((ptr->m_header_bits & Tuple_header::VAR_PART) == 0)
3934+
{
3935+
/*
3936+
* No varpart present.
3937+
* And this is not an update where the dynamic column is set to null.
3938+
* So skip storing the var part altogether.
3939+
*/
3940+
ndbassert(((Uint32*) vp) == ptr->get_end_of_fix_part_ptr(tabPtrP));
3941+
dst_ptr= (Uint32*)vp;
3942+
}
3943+
else
3944+
{
3945+
/*
3946+
* varpart_len is now 0, but tuple already had a varpart.
3947+
* It will be released at commit time.
3948+
*/
3949+
}
39303950

39313951
ndbassert((UintPtr(ptr) & 3) == 0);
39323952
ndbassert(varpart_len < 0x10000);

0 commit comments

Comments
 (0)