Skip to content

Commit 936efe6

Browse files
Bug#12581895 - ndbinfo should support opening tables with more or differently named columns
- Add support for opening a table with more fields than exist in NDBINFO, all fields not sent by NDB will be set to NULL - Add check to prevent opening a table with fields which are NOT NULL - Extend NdbInfoRecAttr with isNULL()
1 parent 644cba0 commit 936efe6

File tree

5 files changed

+173
-47
lines changed

5 files changed

+173
-47
lines changed

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

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -180,20 +180,55 @@ node_id != 0
180180
1
181181
Warnings:
182182
Warning 40001 Table 'ndb$test' is defined differently in NDB, there are more columns available. The SQL to regenerate is: 'CREATE TABLE `ndbinfo`.`ndb$test` (`node_id` INT UNSIGNED, `block_number` INT UNSIGNED, `block_instance` INT UNSIGNED, `counter` INT UNSIGNED, `counter2` BIGINT UNSIGNED) ENGINE=NDBINFO'
183-
184-
## 2) Column does not exist in NDB -> error, with warning
185183
DROP TABLE ndb$test;
184+
185+
## 2) Column does not exist in NDB -> allowed, with warning, non existing
186+
## column(s) return NULL
187+
## 2a) Extra column at end
186188
CREATE TABLE ndb$test (node_id int, non_existing int) ENGINE = ndbinfo;
187-
SELECT * FROM ndb$test;
188-
ERROR HY000: Got error 40001 'Incompatible table definitions' from NDBINFO
189-
SHOW WARNINGS;
190-
Level Code Message
189+
SELECT DISTINCT node_id, non_existing FROM ndb$test;
190+
node_id non_existing
191+
1 NULL
192+
2 NULL
193+
Warnings:
191194
Error 40001 Table 'ndb$test' is defined differently in NDB, column 'non_existing' does not exist. The SQL to regenerate is: 'CREATE TABLE `ndbinfo`.`ndb$test` (`node_id` INT UNSIGNED, `block_number` INT UNSIGNED, `block_instance` INT UNSIGNED, `counter` INT UNSIGNED, `counter2` BIGINT UNSIGNED) ENGINE=NDBINFO'
192-
Error 1296 Got error 40001 'Incompatible table definitions' from NDBINFO
195+
Warning 40001 Table 'ndb$test' is defined differently in NDB, there are more columns available. The SQL to regenerate is: 'CREATE TABLE `ndbinfo`.`ndb$test` (`node_id` INT UNSIGNED, `block_number` INT UNSIGNED, `block_instance` INT UNSIGNED, `counter` INT UNSIGNED, `counter2` BIGINT UNSIGNED) ENGINE=NDBINFO'
196+
DROP TABLE ndb$test;
197+
198+
## 2b) Extra column(s) in middle
199+
CREATE TABLE ndb$test (
200+
node_id int unsigned,
201+
non_existing int unsigned,
202+
block_number int unsigned,
203+
block_instance int unsigned,
204+
counter int unsigned,
205+
counter2 bigint unsigned
206+
) ENGINE = ndbinfo;
207+
SELECT DISTINCT node_id, non_existing, block_number FROM ndb$test;
208+
node_id non_existing block_number
209+
1 NULL 249
210+
2 NULL 249
211+
Warnings:
212+
Error 40001 Table 'ndb$test' is defined differently in NDB, column 'non_existing' does not exist. The SQL to regenerate is: 'CREATE TABLE `ndbinfo`.`ndb$test` (`node_id` INT UNSIGNED, `block_number` INT UNSIGNED, `block_instance` INT UNSIGNED, `counter` INT UNSIGNED, `counter2` BIGINT UNSIGNED) ENGINE=NDBINFO'
213+
DROP TABLE ndb$test;
214+
215+
## 2c) Extra column first
216+
CREATE TABLE ndb$test (non_existing int, node_id int) ENGINE = ndbinfo;
217+
SELECT DISTINCT node_id, non_existing FROM ndb$test;
218+
node_id non_existing
219+
1 NULL
220+
2 NULL
221+
Warnings:
222+
Error 40001 Table 'ndb$test' is defined differently in NDB, column 'non_existing' does not exist. The SQL to regenerate is: 'CREATE TABLE `ndbinfo`.`ndb$test` (`node_id` INT UNSIGNED, `block_number` INT UNSIGNED, `block_instance` INT UNSIGNED, `counter` INT UNSIGNED, `counter2` BIGINT UNSIGNED) ENGINE=NDBINFO'
223+
Warning 40001 Table 'ndb$test' is defined differently in NDB, there are more columns available. The SQL to regenerate is: 'CREATE TABLE `ndbinfo`.`ndb$test` (`node_id` INT UNSIGNED, `block_number` INT UNSIGNED, `block_instance` INT UNSIGNED, `counter` INT UNSIGNED, `counter2` BIGINT UNSIGNED) ENGINE=NDBINFO'
224+
SELECT DISTINCT non_existing, node_id FROM ndb$test;
225+
non_existing node_id
226+
NULL 1
227+
NULL 2
228+
DROP TABLE ndb$test;
193229

194230
## 3) Incompatible column type -> error, with warning
195231
## 3a) int instead of bigint
196-
DROP TABLE ndb$test;
197232
CREATE TABLE ndb$test (counter2 int) ENGINE = ndbinfo;
198233
SELECT * FROM ndb$test;
199234
ERROR HY000: Got error 40001 'Incompatible table definitions' from NDBINFO
@@ -220,6 +255,26 @@ Level Code Message
220255
Error 40001 Table 'ndb$test' is defined differently in NDB, column 'node_id' is not compatible. The SQL to regenerate is: 'CREATE TABLE `ndbinfo`.`ndb$test` (`node_id` INT UNSIGNED, `block_number` INT UNSIGNED, `block_instance` INT UNSIGNED, `counter` INT UNSIGNED, `counter2` BIGINT UNSIGNED) ENGINE=NDBINFO'
221256
Error 1296 Got error 40001 'Incompatible table definitions' from NDBINFO
222257
DROP TABLE ndb$test;
258+
## 3d) column which is NOT NULL
259+
CREATE TABLE ndb$test (node_id int unsigned NOT NULL) ENGINE = ndbinfo;
260+
SELECT * FROM ndb$test;
261+
ERROR HY000: Got error 40001 'Incompatible table definitions' from NDBINFO
262+
SHOW WARNINGS;
263+
Level Code Message
264+
Error 40001 Table 'ndb$test' is defined differently in NDB, column 'node_id' is NOT NULL. The SQL to regenerate is: 'CREATE TABLE `ndbinfo`.`ndb$test` (`node_id` INT UNSIGNED, `block_number` INT UNSIGNED, `block_instance` INT UNSIGNED, `counter` INT UNSIGNED, `counter2` BIGINT UNSIGNED) ENGINE=NDBINFO'
265+
Error 1296 Got error 40001 'Incompatible table definitions' from NDBINFO
266+
DROP TABLE ndb$test;
267+
## 3e) non existing column which is NOT NULL
268+
CREATE TABLE ndb$test (
269+
block_number int unsigned,
270+
non_existing int NOT NULL) ENGINE = ndbinfo;
271+
SELECT * FROM ndb$test;
272+
ERROR HY000: Got error 40001 'Incompatible table definitions' from NDBINFO
273+
SHOW WARNINGS;
274+
Level Code Message
275+
Error 40001 Table 'ndb$test' is defined differently in NDB, column 'non_existing' is NOT NULL. The SQL to regenerate is: 'CREATE TABLE `ndbinfo`.`ndb$test` (`node_id` INT UNSIGNED, `block_number` INT UNSIGNED, `block_instance` INT UNSIGNED, `counter` INT UNSIGNED, `counter2` BIGINT UNSIGNED) ENGINE=NDBINFO'
276+
Error 1296 Got error 40001 'Incompatible table definitions' from NDBINFO
277+
DROP TABLE ndb$test;
223278

224279
## 4) Table with primary key/indexes not supported
225280
CREATE TABLE ndb$test (node_id int, block_number int PRIMARY KEY) ENGINE = ndbinfo;

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

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,17 +98,35 @@ SELECT count(*) >= 20 FROM blocks;
9898
DROP TABLE ndb$test;
9999
CREATE TABLE ndb$test (node_id int unsigned) ENGINE = ndbinfo;
100100
SELECT node_id != 0 FROM ndb$test LIMIT 1;
101-
102-
## 2) Column does not exist in NDB -> error, with warning
103101
DROP TABLE ndb$test;
102+
103+
## 2) Column does not exist in NDB -> allowed, with warning, non existing
104+
## column(s) return NULL
105+
## 2a) Extra column at end
104106
CREATE TABLE ndb$test (node_id int, non_existing int) ENGINE = ndbinfo;
105-
--error ER_GET_ERRMSG
106-
SELECT * FROM ndb$test;
107-
SHOW WARNINGS;
107+
SELECT DISTINCT node_id, non_existing FROM ndb$test;
108+
DROP TABLE ndb$test;
109+
110+
## 2b) Extra column(s) in middle
111+
CREATE TABLE ndb$test (
112+
node_id int unsigned,
113+
non_existing int unsigned,
114+
block_number int unsigned,
115+
block_instance int unsigned,
116+
counter int unsigned,
117+
counter2 bigint unsigned
118+
) ENGINE = ndbinfo;
119+
SELECT DISTINCT node_id, non_existing, block_number FROM ndb$test;
120+
DROP TABLE ndb$test;
121+
122+
## 2c) Extra column first
123+
CREATE TABLE ndb$test (non_existing int, node_id int) ENGINE = ndbinfo;
124+
SELECT DISTINCT node_id, non_existing FROM ndb$test;
125+
SELECT DISTINCT non_existing, node_id FROM ndb$test;
126+
DROP TABLE ndb$test;
108127

109128
## 3) Incompatible column type -> error, with warning
110129
## 3a) int instead of bigint
111-
DROP TABLE ndb$test;
112130
CREATE TABLE ndb$test (counter2 int) ENGINE = ndbinfo;
113131
--error ER_GET_ERRMSG
114132
SELECT * FROM ndb$test;
@@ -126,6 +144,21 @@ CREATE TABLE ndb$test (node_id varchar(255)) ENGINE = ndbinfo;
126144
SELECT * FROM ndb$test;
127145
SHOW WARNINGS;
128146
DROP TABLE ndb$test;
147+
## 3d) column which is NOT NULL
148+
CREATE TABLE ndb$test (node_id int unsigned NOT NULL) ENGINE = ndbinfo;
149+
--error ER_GET_ERRMSG
150+
SELECT * FROM ndb$test;
151+
SHOW WARNINGS;
152+
DROP TABLE ndb$test;
153+
## 3e) non existing column which is NOT NULL
154+
CREATE TABLE ndb$test (
155+
block_number int unsigned,
156+
non_existing int NOT NULL) ENGINE = ndbinfo;
157+
--error ER_GET_ERRMSG
158+
SELECT * FROM ndb$test;
159+
SHOW WARNINGS;
160+
DROP TABLE ndb$test;
161+
129162

130163
## 4) Table with primary key/indexes not supported
131164
--error ER_TOO_MANY_KEYS

sql/ha_ndbinfo.cc

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -373,21 +373,36 @@ int ha_ndbinfo::open(const char *name, int mode, uint test_if_locked)
373373
DBUG_RETURN(err2mysql(err));
374374
}
375375

376+
/*
377+
Check table def. to detect incompatible differences which should
378+
return an error. Differences which only generate a warning
379+
is checked on first use
380+
*/
376381
DBUG_PRINT("info", ("Comparing MySQL's table def against NDB"));
377382
const NdbInfo::Table* ndb_tab = m_impl.m_table;
378383
for (uint i = 0; i < table->s->fields; i++)
379384
{
380385
const Field* field = table->field[i];
381-
const NdbInfo::Column* col = ndb_tab->getColumn(field->field_name);
382-
if (!col)
386+
387+
// Check if field is NULLable
388+
if (const_cast<Field*>(field)->real_maybe_null() == false)
383389
{
384-
// The column didn't exist
390+
// Only NULLable fields supported
385391
warn_incompatible(ndb_tab, true,
386-
"column '%s' does not exist",
392+
"column '%s' is NOT NULL",
387393
field->field_name);
388394
DBUG_RETURN(ERR_INCOMPAT_TABLE_DEF);
389395
}
390396

397+
// Check if column exist in NDB
398+
const NdbInfo::Column* col = ndb_tab->getColumn(field->field_name);
399+
if (!col)
400+
{
401+
// The column didn't exist
402+
continue;
403+
}
404+
405+
// Check compatible field and column type
391406
bool compatible = false;
392407
switch(col->m_type)
393408
{
@@ -465,13 +480,30 @@ int ha_ndbinfo::rnd_init(bool scan)
465480
m_impl.m_first_use = false;
466481

467482
/*
468-
Due to different code paths in MySQL Server
469-
for prepared statement protocol, some warnings
470-
from 'handler::open' are lost and need to be
471-
deffered to first use instead
483+
Check table def. and generate warnings for incompatibilites
484+
which is allowed but should generate a warning.
485+
(Done this late due to different code paths in MySQL Server for
486+
prepared statement protocol, where warnings from 'handler::open'
487+
are lost).
472488
*/
489+
uint fields_found_in_ndb = 0;
473490
const NdbInfo::Table* ndb_tab = m_impl.m_table;
474-
if (table->s->fields < ndb_tab->columns())
491+
for (uint i = 0; i < table->s->fields; i++)
492+
{
493+
const Field* field = table->field[i];
494+
const NdbInfo::Column* col = ndb_tab->getColumn(field->field_name);
495+
if (!col)
496+
{
497+
// The column didn't exist
498+
warn_incompatible(ndb_tab, true,
499+
"column '%s' does not exist",
500+
field->field_name);
501+
continue;
502+
}
503+
fields_found_in_ndb++;
504+
}
505+
506+
if (fields_found_in_ndb < ndb_tab->columns())
475507
{
476508
// There are more columns available in NDB
477509
warn_incompatible(ndb_tab, false,
@@ -599,7 +631,7 @@ ha_ndbinfo::unpack_record(uchar *dst_row)
599631
{
600632
Field *field = table->field[i];
601633
const NdbInfoRecAttr* record = m_impl.m_columns[i];
602-
if (m_impl.m_columns[i])
634+
if (record && !record->isNULL())
603635
{
604636
field->set_notnull();
605637
field->move_field_offset(dst_offset);

storage/ndb/src/ndbapi/NdbInfoRecAttr.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,18 @@ class NdbInfoRecAttr {
4646
return m_len;
4747
}
4848

49+
bool isNULL() const {
50+
return !m_defined;
51+
}
52+
4953
protected:
5054
friend class NdbInfoScanOperation;
51-
NdbInfoRecAttr() : m_data(NULL), m_len(0) {};
55+
NdbInfoRecAttr() : m_data(NULL), m_len(0), m_defined(false) {};
5256
~NdbInfoRecAttr() {};
5357
private:
5458
const char* m_data;
5559
Uint32 m_len;
60+
bool m_defined;
5661
};
5762

5863
#endif

storage/ndb/src/ndbapi/NdbInfoScanOperation.cpp

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -418,34 +418,35 @@ NdbInfoScanOperation::execDBINFO_TRANSID_AI(const SimpleSignal * signal)
418418
m_rows_received++;
419419
DBUG_PRINT("info", ("rows received: %d", m_rows_received));
420420

421-
const Uint32* start = signal->ptr[0].p;
422-
const Uint32* end = start + signal->ptr[0].sz;
423-
424-
DBUG_PRINT("info", ("start: %p, end: %p", start, end));
425-
for (unsigned col = 0; col < m_table->columns(); col++)
421+
// Reset all recattr values before reading the new row
422+
for (unsigned i = 0; i < m_recAttrs.size(); i++)
426423
{
424+
if (m_recAttrs[i])
425+
m_recAttrs[i]->m_defined = false;
426+
}
427427

428-
// Read attribute header
429-
const AttributeHeader ah(*start);
430-
const Uint32 len = ah.getByteSize();
428+
// Read attributes from long signal section
429+
AttributeHeader* attr = (AttributeHeader*)signal->ptr[0].p;
430+
AttributeHeader* last = (AttributeHeader*)(signal->ptr[0].p +
431+
signal->ptr[0].sz);
432+
while (attr < last)
433+
{
434+
const Uint32 col = attr->getAttributeId();
435+
const Uint32 len = attr->getByteSize();
431436
DBUG_PRINT("info", ("col: %u, len: %u", col, len));
432-
433-
// Step past attribute header
434-
start += ah.getHeaderSize();
435-
436-
NdbInfoRecAttr* attr = m_recAttrs[col];
437-
if (attr)
437+
if (col < m_recAttrs.size())
438438
{
439-
// Update NdbInfoRecAttr pointer and length
440-
attr->m_data = (const char*)start;
441-
attr->m_len = len;
439+
NdbInfoRecAttr* rec_attr = m_recAttrs[col];
440+
if (rec_attr)
441+
{
442+
// Update NdbInfoRecAttr pointer, length and defined flag
443+
rec_attr->m_data = (const char*)attr->getDataPtr();
444+
rec_attr->m_len = len;
445+
rec_attr->m_defined = true;
446+
}
442447
}
443448

444-
// Step to next attribute header
445-
start += ah.getDataSize();
446-
447-
// No reading beyond end of signal size
448-
assert(start <= end);
449+
attr = attr->getNext();
449450
}
450451

451452
DBUG_RETURN(false); // Don't wait more, process this row

0 commit comments

Comments
 (0)