Skip to content

Commit 1ef077e

Browse files
committed
ndbinfo
- Add tests for incompatible table definitions between MySQL Server and NDB - Add detection of incompatible table definitions in open - Fix mismatch in table def for the hardcoded table "columns" in NdbApi - Add 'ha_ndbinfo::get_error_message' so that proper error messages are given in case of an error - Add some more table flags for two features that you have to 'opt-out' of - blobs and auto increment
1 parent eebcd42 commit 1ef077e

5 files changed

Lines changed: 296 additions & 8 deletions

File tree

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

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ ERROR HY000: Table 'ndb$tables' is read only
104104
DELETE FROM ndb$tables WHERE table_id > 1;
105105
ERROR HY000: Table 'ndb$tables' is read only
106106

107+
ALTER TABLE ndb$test ADD COLUMN another_col varchar(255);
108+
ERROR HY000: Table 'ndb$test' is read only
109+
107110
FLUSH TABLES;
108111
SELECT table_id FROM ndb$tables;
109112
table_id
@@ -164,3 +167,64 @@ SELECT count(*) >= 20 FROM blocks;
164167
count(*) >= 20
165168
1
166169

170+
## 1) More columns in NDB -> allowed, with warning
171+
DROP TABLE ndb$test;
172+
CREATE TABLE ndb$test (node_id int unsigned) ENGINE = ndbinfo;
173+
SELECT node_id != 0 FROM ndb$test LIMIT 1;
174+
node_id != 0
175+
1
176+
Warnings:
177+
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'
178+
179+
## 2) Column does not exist in NDB -> error, with warning
180+
DROP TABLE ndb$test;
181+
CREATE TABLE ndb$test (node_id int, non_existing int) ENGINE = ndbinfo;
182+
SELECT * FROM ndb$test;
183+
ERROR HY000: Got error 40001 'Incompatible table definitions' from NDBINFO
184+
SHOW WARNINGS;
185+
Level Code Message
186+
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'
187+
Error 1296 Got error 40001 'Incompatible table definitions' from NDBINFO
188+
189+
## 3) Incompatible column type -> error, with warning
190+
## 3a) int instead of bigint
191+
DROP TABLE ndb$test;
192+
CREATE TABLE ndb$test (counter2 int) ENGINE = ndbinfo;
193+
SELECT * FROM ndb$test;
194+
ERROR HY000: Got error 40001 'Incompatible table definitions' from NDBINFO
195+
SHOW WARNINGS;
196+
Level Code Message
197+
Error 40001 Table 'ndb$test' is defined differently in NDB, column 'counter2' 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'
198+
Error 1296 Got error 40001 'Incompatible table definitions' from NDBINFO
199+
## 3b) bigint instead of int
200+
DROP TABLE ndb$test;
201+
CREATE TABLE ndb$test (node_id bigint) ENGINE = ndbinfo;
202+
SELECT * FROM ndb$test;
203+
ERROR HY000: Got error 40001 'Incompatible table definitions' from NDBINFO
204+
SHOW WARNINGS;
205+
Level Code Message
206+
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'
207+
Error 1296 Got error 40001 'Incompatible table definitions' from NDBINFO
208+
## 3c) varchar instead of int
209+
DROP TABLE ndb$test;
210+
CREATE TABLE ndb$test (node_id varchar(255)) ENGINE = ndbinfo;
211+
SELECT * FROM ndb$test;
212+
ERROR HY000: Got error 40001 'Incompatible table definitions' from NDBINFO
213+
SHOW WARNINGS;
214+
Level Code Message
215+
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'
216+
Error 1296 Got error 40001 'Incompatible table definitions' from NDBINFO
217+
DROP TABLE ndb$test;
218+
219+
## 4) Table with primary key/indexes not supported
220+
CREATE TABLE ndb$test (node_id int, block_number int PRIMARY KEY) ENGINE = ndbinfo;
221+
ERROR 42000: Too many keys specified; max 0 keys allowed
222+
223+
## 5) Table with blobs not supported
224+
CREATE TABLE ndb$test (node_id int, block_number blob) ENGINE = ndbinfo;
225+
ERROR 42000: The used table type doesn't support BLOB/TEXT columns
226+
227+
## 6) Table with autoincrement not supported
228+
CREATE TABLE ndb$test (node_id int AUTO_INCREMENT) ENGINE = ndbinfo;
229+
ERROR 42000: The used table type doesn't support AUTO_INCREMENT columns
230+

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ DELETE FROM ndb$tables WHERE 1=0;
6161
--error ER_OPEN_AS_READONLY
6262
DELETE FROM ndb$tables WHERE table_id > 1;
6363

64+
--error ER_OPEN_AS_READONLY
65+
ALTER TABLE ndb$test ADD COLUMN another_col varchar(255);
66+
6467
FLUSH TABLES;
6568
SELECT table_id FROM ndb$tables;
6669

@@ -93,6 +96,61 @@ set @@ndbinfo_database="somethingelse";
9396
# Check that block table has been created and contain data
9497
SELECT count(*) >= 20 FROM blocks;
9598

99+
# Test incompatible table definition between NDB and MySQL Server
100+
# using the ndb$test table which originally looks like
101+
# CREATE TABLE `ndb$test` (
102+
# `node_id` int unsigned DEFAULT NULL,
103+
# `block_number` int unsigned DEFAULT NULL,
104+
# `block_instance` int unsigned DEFAULT NULL,
105+
# `counter` int unsigned DEFAULT NULL,
106+
# `counter2` bigint unsigned DEFAULT NULL
107+
# ) ENGINE=NDBINFO COMMENT='for testing'
108+
109+
## 1) More columns in NDB -> allowed, with warning
110+
DROP TABLE ndb$test;
111+
CREATE TABLE ndb$test (node_id int unsigned) ENGINE = ndbinfo;
112+
SELECT node_id != 0 FROM ndb$test LIMIT 1;
113+
114+
## 2) Column does not exist in NDB -> error, with warning
115+
DROP TABLE ndb$test;
116+
CREATE TABLE ndb$test (node_id int, non_existing int) ENGINE = ndbinfo;
117+
--error ER_GET_ERRMSG
118+
SELECT * FROM ndb$test;
119+
SHOW WARNINGS;
120+
121+
## 3) Incompatible column type -> error, with warning
122+
## 3a) int instead of bigint
123+
DROP TABLE ndb$test;
124+
CREATE TABLE ndb$test (counter2 int) ENGINE = ndbinfo;
125+
--error ER_GET_ERRMSG
126+
SELECT * FROM ndb$test;
127+
SHOW WARNINGS;
128+
## 3b) bigint instead of int
129+
DROP TABLE ndb$test;
130+
CREATE TABLE ndb$test (node_id bigint) ENGINE = ndbinfo;
131+
--error ER_GET_ERRMSG
132+
SELECT * FROM ndb$test;
133+
SHOW WARNINGS;
134+
## 3c) varchar instead of int
135+
DROP TABLE ndb$test;
136+
CREATE TABLE ndb$test (node_id varchar(255)) ENGINE = ndbinfo;
137+
--error ER_GET_ERRMSG
138+
SELECT * FROM ndb$test;
139+
SHOW WARNINGS;
140+
DROP TABLE ndb$test;
141+
142+
## 4) Table with primary key/indexes not supported
143+
--error ER_TOO_MANY_KEYS
144+
CREATE TABLE ndb$test (node_id int, block_number int PRIMARY KEY) ENGINE = ndbinfo;
145+
146+
## 5) Table with blobs not supported
147+
--error ER_TABLE_CANT_HANDLE_BLOB
148+
CREATE TABLE ndb$test (node_id int, block_number blob) ENGINE = ndbinfo;
149+
150+
## 6) Table with autoincrement not supported
151+
--error ER_TABLE_CANT_HANDLE_AUTO_INCREMENT
152+
CREATE TABLE ndb$test (node_id int AUTO_INCREMENT) ENGINE = ndbinfo;
153+
96154
if (`select @@ndbinfo_version < ((7<<16) | (1 << 8))`)
97155
{
98156
--disable_query_log

sql/ha_ndbinfo.cc

Lines changed: 169 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,36 @@ ha_ndbinfo::~ha_ndbinfo()
139139
delete &m_impl;
140140
}
141141

142+
enum ndbinfo_error_codes {
143+
ERR_INCOMPAT_TABLE_DEF = 40001
144+
};
145+
146+
struct error_message {
147+
int error;
148+
const char* message;
149+
} error_messages[] = {
150+
{ ERR_INCOMPAT_TABLE_DEF, "Incompatible table definitions" },
151+
{ HA_ERR_NO_CONNECTION, "Connection to NDB failed" },
152+
153+
{ 0, 0 }
154+
};
155+
156+
static
157+
const char* find_error_message(int error)
158+
{
159+
struct error_message* err = error_messages;
160+
while (err->error && err->message)
161+
{
162+
if (err->error == error)
163+
{
164+
assert(err->message);
165+
return err->message;
166+
}
167+
err++;
168+
}
169+
return NULL;
170+
}
171+
142172
static int err2mysql(int error)
143173
{
144174
DBUG_ENTER("err2mysql");
@@ -160,6 +190,88 @@ static int err2mysql(int error)
160190
DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
161191
}
162192

193+
bool ha_ndbinfo::get_error_message(int error, String *buf)
194+
{
195+
DBUG_ENTER("ha_ndbinfo::get_error_message");
196+
DBUG_PRINT("enter", ("error: %d", error));
197+
198+
const char* message = find_error_message(error);
199+
if (!message)
200+
DBUG_RETURN(false);
201+
202+
buf->set(message, strlen(message), &my_charset_bin);
203+
DBUG_PRINT("exit", ("message: %s", buf->ptr()));
204+
DBUG_RETURN(false);
205+
}
206+
207+
static void
208+
generate_sql(const NdbInfo::Table* ndb_tab, BaseString& sql)
209+
{
210+
sql.appfmt("'CREATE TABLE `%s`.`%s%s` (",
211+
ndbinfo_dbname, table_prefix, ndb_tab->getName());
212+
213+
const char* separator = "";
214+
for (unsigned i = 0; i < ndb_tab->columns(); i++)
215+
{
216+
const NdbInfo::Column* col = ndb_tab->getColumn(i);
217+
218+
sql.appfmt("%s", separator);
219+
separator = ", ";
220+
221+
sql.appfmt("`%s` ", col->m_name.c_str());
222+
223+
switch(col->m_type)
224+
{
225+
case NdbInfo::Column::Number:
226+
sql.appfmt("INT UNSIGNED");
227+
break;
228+
case NdbInfo::Column::Number64:
229+
sql.appfmt("BIGINT UNSIGNED");
230+
break;
231+
case NdbInfo::Column::String:
232+
sql.appfmt("VARCHAR(512)");
233+
break;
234+
default:
235+
sql.appfmt("UNKNOWN");
236+
assert(false);
237+
break;
238+
}
239+
}
240+
sql.appfmt(") ENGINE=NDBINFO'");
241+
}
242+
243+
/*
244+
Push a warning with explanation of the problem as well as the
245+
proper SQL so the user can regenerate the table definition
246+
*/
247+
248+
static void
249+
warn_incompatible(const NdbInfo::Table* ndb_tab, bool fatal,
250+
const char* format, ...)
251+
{
252+
BaseString msg;
253+
DBUG_ENTER("warn_incompatible");
254+
DBUG_PRINT("enter",("table_name: %s, fatal: %d", ndb_tab->getName(), fatal));
255+
DBUG_ASSERT(format != NULL);
256+
257+
va_list args;
258+
char explanation[128];
259+
va_start(args,format);
260+
my_vsnprintf(explanation, sizeof(explanation), format, args);
261+
va_end(args);
262+
263+
msg.assfmt("Table '%s%s' is defined differently in NDB, %s. The "
264+
"SQL to regenerate is: ",
265+
table_prefix, ndb_tab->getName(), explanation);
266+
generate_sql(ndb_tab, msg);
267+
268+
const MYSQL_ERROR::enum_warning_level level =
269+
(fatal ? MYSQL_ERROR::WARN_LEVEL_ERROR : MYSQL_ERROR::WARN_LEVEL_WARN);
270+
push_warning(current_thd, level, ERR_INCOMPAT_TABLE_DEF, msg.c_str());
271+
272+
DBUG_VOID_RETURN;
273+
}
274+
163275
int ha_ndbinfo::create(const char *name, TABLE *form,
164276
HA_CREATE_INFO *create_info)
165277
{
@@ -198,12 +310,6 @@ int ha_ndbinfo::open(const char *name, int mode, uint test_if_locked)
198310
DBUG_RETURN(0);
199311
}
200312

201-
/* Increase "ref_length" to allow a whole row to be stored in "ref" */
202-
ref_length = 0;
203-
for (uint i = 0; i < table->s->fields; i++)
204-
ref_length += table->field[i]->pack_length();
205-
DBUG_PRINT("info", ("ref_length: %u", ref_length));
206-
207313
int err = g_ndbinfo->openTable(name, &m_impl.m_table);
208314
if (err)
209315
{
@@ -212,6 +318,63 @@ int ha_ndbinfo::open(const char *name, int mode, uint test_if_locked)
212318
DBUG_RETURN(err2mysql(err));
213319
}
214320

321+
DBUG_PRINT("info", ("Comparing MySQL's table def against NDB"));
322+
const NdbInfo::Table* ndb_tab = m_impl.m_table;
323+
for (uint i = 0; i < table->s->fields; i++)
324+
{
325+
const Field* field = table->field[i];
326+
const NdbInfo::Column* col = ndb_tab->getColumn(field->field_name);
327+
if (!col)
328+
{
329+
// The column didn't exist
330+
warn_incompatible(ndb_tab, true,
331+
"column '%s' does not exist",
332+
field->field_name);
333+
DBUG_RETURN(ERR_INCOMPAT_TABLE_DEF);
334+
}
335+
336+
bool compatible = false;
337+
switch(col->m_type)
338+
{
339+
case NdbInfo::Column::Number:
340+
if (field->type() == MYSQL_TYPE_LONG)
341+
compatible = true;
342+
break;
343+
case NdbInfo::Column::Number64:
344+
if (field->type() == MYSQL_TYPE_LONGLONG)
345+
compatible = true;
346+
break;
347+
case NdbInfo::Column::String:
348+
if (field->type() == MYSQL_TYPE_VARCHAR)
349+
compatible = true;
350+
break;
351+
default:
352+
assert(false);
353+
break;
354+
}
355+
if (!compatible)
356+
{
357+
// The column type is not compatible
358+
warn_incompatible(ndb_tab, true,
359+
"column '%s' is not compatible",
360+
field->field_name);
361+
DBUG_RETURN(ERR_INCOMPAT_TABLE_DEF);
362+
}
363+
}
364+
365+
if (table->s->fields < ndb_tab->columns())
366+
{
367+
// There are more columns available in NDB
368+
warn_incompatible(ndb_tab, false,
369+
"there are more columns available");
370+
}
371+
372+
/* Increase "ref_length" to allow a whole row to be stored in "ref" */
373+
ref_length = 0;
374+
for (uint i = 0; i < table->s->fields; i++)
375+
ref_length += table->field[i]->pack_length();
376+
DBUG_PRINT("info", ("ref_length: %u", ref_length));
377+
215378
DBUG_RETURN(0);
216379
}
217380

sql/ha_ndbinfo.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ class ha_ndbinfo: public handler
3636
return null;
3737
}
3838
ulonglong table_flags() const {
39-
return HA_REC_NOT_IN_SEQ | HA_NO_TRANSACTIONS;
39+
return HA_REC_NOT_IN_SEQ | HA_NO_TRANSACTIONS |
40+
HA_NO_BLOBS | HA_NO_AUTO_INCREMENT;
4041
}
4142
ulong index_flags(uint inx, uint part, bool all_parts) const {
4243
return 0;
@@ -69,6 +70,8 @@ class ha_ndbinfo: public handler
6970
#endif
7071
}
7172

73+
bool get_error_message(int error, String *buf);
74+
7275
private:
7376
void unpack_record(uchar *dst_row);
7477

storage/ndb/src/ndbapi/NdbInfo.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ bool NdbInfo::load_hardcoded_tables(void)
7777
if (!cols.addColumn(Column("table_id", 0, Column::Number)) ||
7878
!cols.addColumn(Column("column_id", 1, Column::Number)) ||
7979
!cols.addColumn(Column("column_name", 2, Column::String)) ||
80-
!cols.addColumn(Column("column_type", 3, Column::String)) ||
80+
!cols.addColumn(Column("column_type", 3, Column::Number)) ||
8181
!cols.addColumn(Column("comment", 4, Column::String)))
8282
return false;
8383

0 commit comments

Comments
 (0)